Skip to content

Commit 06e5006

Browse files
authored
Merge pull request #1332 from navikt/feature/mottaksadresse-v2
Feature/mottaksadresse v2
2 parents fc4c208 + 722cbb2 commit 06e5006

33 files changed

+836
-18
lines changed

.nais/bygger/nais.yaml

+11-3
Original file line numberDiff line numberDiff line change
@@ -52,23 +52,29 @@ spec:
5252
value: http://formio-api
5353
- name: FORMIO_PROJECT_NAME
5454
value: jvcemxwcpghcqjn
55+
- name: FORMS_API_URL
56+
value: http://forms-api.fyllut-sendinn
5557
{{#each environmentVars}}
5658
- name: {{this.name}}
5759
value: {{this.value}}
5860
{{/each}}
61+
{{#each azure.ad_groups}}
62+
- name: "FORMS_API_AD_GROUP_{{this.type}}"
63+
value: "{{this.id}}"
64+
{{/each }}
5965
envFrom:
6066
{{#each secrets as |secret|}}
6167
- secret: {{secret}}
6268
{{/each}}
6369
azure:
6470
application:
6571
enabled: true
66-
tenant: nav.no
6772
allowAllUsers: false
6873
claims:
6974
groups:
70-
- id: 1d12af59-d953-4f85-9f65-d8cbf6672deb
71-
- id: 0c0e4023-5fd3-4cfe-8b40-3b98645bb08f
75+
{{#each azure.ad_groups}}
76+
- id: "{{this.id}}"
77+
{{/each}}
7278
extra:
7379
- NAVident
7480
sidecar:
@@ -79,6 +85,8 @@ spec:
7985
accessPolicy:
8086
outbound:
8187
rules:
88+
- application: forms-api
89+
namespace: fyllut-sendinn
8290
{{#each accessPolicy.outbound.rules}}
8391
- application: {{this.application}}
8492
namespace: {{this.namespace}}

.nais/bygger/preprod-alt.yaml

+6
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,9 @@ environmentVars:
3535
value: http://formio-enterprise-server
3636
- name: FORMIO_PROJECT_NAME_PROD
3737
value: jvcemxwcpghcqjn
38+
azure:
39+
ad_groups:
40+
- id: b7012a89-90b9-4215-b7dc-988b929216e9
41+
type: USER
42+
- id: 5398ed9e-bb41-43f5-9434-120b0116953c
43+
type: ADMIN

.nais/bygger/preprod.yaml

+6
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,9 @@ environmentVars:
3838
value: http://formio-enterprise-server
3939
- name: FORMIO_PROJECT_NAME_PROD
4040
value: jvcemxwcpghcqjn
41+
azure:
42+
ad_groups:
43+
- id: b7012a89-90b9-4215-b7dc-988b929216e9
44+
type: USER
45+
- id: 5398ed9e-bb41-43f5-9434-120b0116953c
46+
type: ADMIN

.nais/bygger/prod.yaml

+6
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,9 @@ secrets:
2727
- bygger-pusher
2828
- github-app-installation
2929
environmentVars: []
30+
azure:
31+
ad_groups:
32+
- id: 1d12af59-d953-4f85-9f65-d8cbf6672deb
33+
type: USER
34+
- id: 0c0e4023-5fd3-4cfe-8b40-3b98645bb08f
35+
type: ADMIN

README.md

+12
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,18 @@ til url til den lokale instansen av innsending-api i miljøvariabelen `SEND_INN_
9898

9999
SEND_INN_HOST=http://127.0.0.1:9064
100100

101+
## Kjøre Bygger lokalt med integrasjon mot forms-api
102+
103+
For kontinuerlig utvikling mot forms-api er det best å hente ned og kjøre https://github.com/navikt/forms-api lokalt.
104+
Sett miljøvariabelen `FORMS_API_URL` i byggeren sin `.env`-fil til riktig port på localhost. F.eks:
105+
106+
FORMS_API_URL=http://localhost:8082
107+
108+
Alternativt kan du bruke [azure-token-generator](https://azure-token-generator.intern.dev.nav.no/api/obo?aud=dev-gcp:fyllut-sendinn:forms-api) (krever trygdeetaten-bruker) til å generere et midlertidig access token for å nå forms-api i preprod. Merk at tokenet kun er gyldig en begrenset periode. Legg til følgende miljøvariabler for å få tilgang.
109+
110+
FORMS_API_URL=https://forms-api.intern.dev.nav.no
111+
FORMS_API_ACCESS_TOKEN=<access-token> // Bruk access_token fra responsen til azure-token-generator
112+
101113
## Teste publisering av skjema på lokal maskin
102114

103115
Byggeren er konfigurert med default-verdier lokalt som sørger for at eventuelle publiseringer blir gjort mot en

packages/bygger-backend/src/config/development.ts

+9
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import {
22
AzureConfig,
33
FormioConfig,
4+
FormsApiConfig,
45
FyllutConfig,
56
GithubAppConfig,
67
PublishRepoConfig,
@@ -38,6 +39,14 @@ export const devFyllut: Partial<FyllutConfig> = {
3839
baseUrl: 'https://skjemadelingslenke.ekstern.dev.nav.no/fyllut',
3940
};
4041

42+
export const devFormsApi: FormsApiConfig = {
43+
url: 'https://forms-api.intern.dev.nav.no',
44+
adGroups: {
45+
user: 'b7012a89-90b9-4215-b7dc-988b929216e9',
46+
admin: '5398ed9e-bb41-43f5-9434-120b0116953c',
47+
},
48+
};
49+
4150
export const devPusher: Partial<PusherConfig> = {
4251
cluster: 'eu',
4352
};

packages/bygger-backend/src/config/index.ts

+13-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
devAzure,
55
devEnabledFeatures,
66
devFormio,
7+
devFormsApi,
78
devFyllut,
89
devGithub,
910
devGithubApp,
@@ -36,6 +37,8 @@ const optionalEnv = (name: string): string | undefined => {
3637
};
3738

3839
const naisClusterName = env('NAIS_CLUSTER_NAME') as 'dev-gcp' | 'prod-gcp' | undefined;
40+
const isProduction = nodeEnv === 'production';
41+
const isDevelopment = nodeEnv === 'development';
3942

4043
const config: ConfigType = {
4144
azure: {
@@ -88,6 +91,14 @@ const config: ConfigType = {
8891
baseUrl: env('FYLLUT_BASE_URL', devFyllut.baseUrl),
8992
skjemadelingslenkeUrl: 'https://skjemadelingslenke.ekstern.dev.nav.no/fyllut',
9093
},
94+
formsApi: {
95+
url: env('FORMS_API_URL', devFormsApi.url),
96+
adGroups: {
97+
user: env('FORMS_API_AD_GROUP_USER', devFormsApi.adGroups.user),
98+
admin: env('FORMS_API_AD_GROUP_ADMIN', devFormsApi.adGroups.admin),
99+
},
100+
devToken: isDevelopment ? optionalEnv('FORMS_API_ACCESS_TOKEN') : undefined,
101+
},
91102
pusher: {
92103
cluster: env('PUSHER_CLUSTER', devPusher.cluster),
93104
key: env('PUSHER_KEY'),
@@ -96,8 +107,8 @@ const config: ConfigType = {
96107
},
97108
nodeEnv,
98109
port: parseInt(process.env.PORT || '8080'),
99-
isProduction: nodeEnv === 'production',
100-
isDevelopment: nodeEnv === 'development',
110+
isProduction,
111+
isDevelopment,
101112
featureToggles: featureUtils.toFeatureToggles(env('ENABLED_FEATURES', devEnabledFeatures)),
102113
naisClusterName,
103114
frontendLoggerConfig: configUtils.loadJsonFromEnv('BYGGER_FRONTEND_LOGCONFIG'),

packages/bygger-backend/src/config/types.d.ts

+10
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,15 @@ export type FyllutConfig = {
3434
skjemadelingslenkeUrl: string;
3535
};
3636

37+
export type FormsApiConfig = {
38+
url: string;
39+
adGroups: {
40+
user: string;
41+
admin: string;
42+
};
43+
devToken?: string;
44+
};
45+
3746
export type PusherConfig = {
3847
cluster: string;
3948
key: string;
@@ -58,6 +67,7 @@ export type ConfigType = {
5867
formio: FormioConfig;
5968
prodFormio?: Pick<FormioConfig, 'apiService' | 'projectName'>;
6069
fyllut: FyllutConfig;
70+
formsApi: FormsApiConfig;
6171
pusher: PusherConfig;
6272
githubApp: GithubAppConfig;
6373
gitSha: string;

packages/bygger-backend/src/middleware/authHandler.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import config from '../config';
66
import { logger } from '../logging/logger';
77
import { AzureAdTokenPayload, User } from '../types/custom';
88
import { getDevUser } from '../util/devUser';
9-
import { adGroups } from './azureAd';
109

1110
function toExpiredDateString(exp?: number) {
1211
if (exp) {
@@ -62,12 +61,13 @@ const authHandler = async (req: Request, res: Response, next: NextFunction) => {
6261
return res.sendStatus(401);
6362
}
6463

64+
const { adGroups } = config.formsApi;
6565
logger.info(`Validation of jwt token succeeded (expires ${toExpiredDateString(tokenPayload.exp)})`);
6666
req.getUser = () => ({
6767
name: tokenPayload.name,
6868
preferredUsername: tokenPayload.preferred_username,
6969
NAVident: tokenPayload.NAVident,
70-
isAdmin: tokenPayload.groups?.includes(adGroups.ADMINISTRATOR) || false,
70+
isAdmin: tokenPayload.groups?.includes(adGroups.admin) || false,
7171
});
7272
}
7373
next();

packages/bygger-backend/src/middleware/azureAd.ts

+1-6
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,6 @@
11
import { RequestHandler } from 'express';
22
import { UnauthorizedError } from '../routers/api/helpers/errors';
33

4-
const adGroups = {
5-
USER: '1d12af59-d953-4f85-9f65-d8cbf6672deb',
6-
ADMINISTRATOR: '0c0e4023-5fd3-4cfe-8b40-3b98645bb08f',
7-
};
8-
94
const adHandlers: RequestHandlers = {
105
isAdmin: (req, res, next) => {
116
if (req.getUser().isAdmin) {
@@ -20,4 +15,4 @@ type RequestHandlers = {
2015
[key: string]: RequestHandler;
2116
};
2217

23-
export { adGroups, adHandlers };
18+
export { adHandlers };
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import appConfig from '../../../config';
2+
import authorizedPublisher from './authorizedPublisher';
3+
import azureOnBehalfOfTokenHandler from './azureOnBehalfOfTokenHandler';
4+
5+
const { naisClusterName, formsApi } = appConfig;
6+
7+
const authHandlers = {
8+
authorizedPublisher,
9+
formsApiAuthHandler: azureOnBehalfOfTokenHandler(`${naisClusterName}.fyllut-sendinn.forms-api`, formsApi.devToken),
10+
};
11+
12+
export default authHandlers;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { NextFunction, Request, Response } from 'express';
2+
import qs from 'qs';
3+
import config from '../../../config';
4+
import { fetchWithErrorHandling } from '../../../fetchUtils';
5+
import { logger } from '../../../logging/logger';
6+
import { UnauthorizedError } from './errors';
7+
8+
const { azure, isDevelopment } = config;
9+
10+
const azureOnBehalfOfTokenHandler =
11+
(scope: string, devToken?: string) => async (req: Request, res: Response, next: NextFunction) => {
12+
const accessToken = req.get('Authorization')?.replace('Bearer ', '');
13+
14+
if (isDevelopment) {
15+
if (devToken) {
16+
req.headers.AzureAccessToken = devToken;
17+
logger.info('Using pre-generated access token to fetch');
18+
} else {
19+
logger.info(`Skipping Azure access token fetch (scope='${scope}')`);
20+
}
21+
return next();
22+
}
23+
24+
try {
25+
const response = await fetchWithErrorHandling(azure.openidTokenEndpoint, {
26+
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
27+
method: 'POST',
28+
body: qs.stringify({
29+
assertion: accessToken,
30+
client_id: azure.clientId,
31+
client_secret: azure.clientSecret,
32+
grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer',
33+
requested_token_use: 'on_behalf_of',
34+
scope: `api://${scope}/.default`,
35+
}),
36+
});
37+
req.headers.AzureAccessToken = (response.data as any).access_token;
38+
next();
39+
} catch (error: any) {
40+
if (error.http_response_body) {
41+
next(new UnauthorizedError(`Access token failed with: ${JSON.stringify(error.http_response_body)}`));
42+
} else {
43+
next(new UnauthorizedError(`Access token failed with: ${JSON.stringify(error)}`));
44+
}
45+
46+
next(error);
47+
}
48+
};
49+
50+
export default azureOnBehalfOfTokenHandler;

packages/bygger-backend/src/routers/api/index.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import formDiff from './formDiff';
99
import formsRouter from './forms';
1010
import globalTranslationsRouter from './global-translations';
1111
import apiErrorHandler from './helpers/apiErrorHandler';
12-
import authorizedPublisher from './helpers/authorizedPublisher';
12+
import authHandlers from './helpers/authHandlers';
1313
import importRouter from './import';
1414
import log from './log';
1515
import migrate from './migrate';
@@ -19,11 +19,13 @@ import publishForm from './publish-form';
1919
import publishForms from './publish-forms';
2020
import publishResource from './publish-resource';
2121
import publishedForms from './published-forms';
22+
import recipientsRouter from './recipients';
2223
import reportsRouter from './reports';
2324
import temakoder from './temakoder';
2425
import unpublishForm from './unpublish-form';
2526

2627
const apiRouter = express.Router();
28+
const { authorizedPublisher, formsApiAuthHandler } = authHandlers;
2729

2830
apiRouter.get('/config', config);
2931
apiRouter.put('/publish/:formPath', authorizedPublisher, deprecatedPublishForm);
@@ -42,6 +44,7 @@ apiRouter.get('/migrate/preview/:formPath', migratePreview);
4244
apiRouter.post('/migrate/update', authorizedPublisher, migrateUpdate);
4345
apiRouter.get('/form/:formPath/diff', formDiff);
4446
apiRouter.use('/forms', formsRouter);
47+
apiRouter.use('/recipients', formsApiAuthHandler, recipientsRouter);
4548
apiRouter.use('/import', importRouter);
4649
apiRouter.use('/global-translations', authorizedPublisher, globalTranslationsRouter);
4750
apiRouter.post('/log/:level', rateLimiter(60000, 60), log);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { Router } from 'express';
2+
import recipients from './recipients';
3+
4+
const recipientsRouter = Router();
5+
6+
recipientsRouter.get('/', recipients.getAll);
7+
recipientsRouter.get('/:recipientId', recipients.get);
8+
recipientsRouter.post('/', recipients.post);
9+
recipientsRouter.put('/:recipientId', recipients.put);
10+
recipientsRouter.delete('/:recipientId', recipients.deleteRecipient);
11+
12+
export default recipientsRouter;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import { RequestHandler } from 'express';
2+
import { recipientService } from '../../../services';
3+
4+
const getAll: RequestHandler = async (req, res, next) => {
5+
try {
6+
const allRecipients = await recipientService.getAll();
7+
res.json(allRecipients);
8+
} catch (error) {
9+
next(error);
10+
}
11+
};
12+
13+
const get: RequestHandler = async (req, res, next) => {
14+
try {
15+
const { recipientId } = req.params;
16+
const recipient = await recipientService.get(recipientId);
17+
res.json(recipient);
18+
} catch (error) {
19+
next(error);
20+
}
21+
};
22+
23+
const post: RequestHandler = async (req, res, next) => {
24+
const accessToken = req.headers.AzureAccessToken as string;
25+
try {
26+
const recipient = await recipientService.post(req.body, accessToken);
27+
res.status(201).json(recipient);
28+
} catch (error) {
29+
next(error);
30+
}
31+
};
32+
33+
const put: RequestHandler = async (req, res, next) => {
34+
const { recipientId } = req.params;
35+
const accessToken = req.headers.AzureAccessToken as string;
36+
try {
37+
const recipient = await recipientService.put(recipientId, req.body, accessToken);
38+
res.json(recipient);
39+
} catch (error) {
40+
next(error);
41+
}
42+
};
43+
44+
const deleteRecipient: RequestHandler = async (req, res, next) => {
45+
const { recipientId } = req.params;
46+
const accessToken = req.headers.AzureAccessToken as string;
47+
try {
48+
await recipientService.delete(recipientId, accessToken);
49+
res.status(204).send();
50+
} catch (error) {
51+
next(error);
52+
}
53+
};
54+
55+
const recipients = {
56+
getAll,
57+
get,
58+
post,
59+
put,
60+
deleteRecipient,
61+
};
62+
export default recipients;

0 commit comments

Comments
 (0)