Commit 1dd8eaf3 authored by Sacha Brants's avatar Sacha Brants
Browse files

Merge branch 'slb/dm3/audience-check' into 'master'

feat: added reporting audience check & audience token exchange

See merge request !189
parents 6b18d6d1 138764af
Pipeline #63825 failed with stages
in 18 minutes and 37 seconds
......@@ -48,4 +48,4 @@ RUN apk --no-cache add --virtual native-deps g++ gcc libgcc libstdc++ linux-head
&& npm install --production --quiet \
&& apk del native-deps
ENTRYPOINT ["node", "./dist/server/server-start.js"]
\ No newline at end of file
ENTRYPOINT ["node", "./dist/server/server-start.js", "--trace-warnings", "--trace-uncaught"]
\ No newline at end of file
......@@ -68,6 +68,7 @@ export interface IAuthProvider {
convertToImpersonationTokenModel(credential: any): ImpersonationTokenModel;
getClientID(): string;
getClientSecret(): string;
exchangeCredentialAudience(credential: string, audience: string): Promise<string>;
}
export abstract class AbstractAuthProvider implements IAuthProvider {
......@@ -75,6 +76,7 @@ export abstract class AbstractAuthProvider implements IAuthProvider {
public abstract convertToImpersonationTokenModel(credential: any): ImpersonationTokenModel;
public abstract getClientID(): string;
public abstract getClientSecret(): string;
public abstract exchangeCredentialAudience(credential: string, audience: string): Promise<string>;
}
export class AuthProviderFactory extends AuthProviderFactoryBuilder {
......
......@@ -41,4 +41,9 @@ export class GenericAuthProvider extends AbstractAuthProvider {
'The required feature is not supported, the credential auth provider has not been found.'));
}
public exchangeCredentialAudience(credential: string, audience: string): Promise<string> {
throw (Error.make(Error.Status.NOT_IMPLEMENTED,
'The required feature is not supported, the credential auth provider has not been found.'));
}
}
\ No newline at end of file
......@@ -52,6 +52,9 @@ export interface ConfigModel {
CORRELATION_ID?: string;
SERVICE_AUTH_PROVIDER?: string;
SERVICE_AUTH_PROVIDER_CREDENTIAL?: string;
ENABLE_SDMS_ID_AUDIENCE_CHECK?: boolean;
ENABLE_DE_TOKEN_EXCHANGE?: boolean;
DES_TARGET_AUDIENCE?: string;
FEATURE_FLAG_AUTHORIZATION: boolean;
FEATURE_FLAG_LEGALTAG: boolean;
FEATURE_FLAG_SEISMICMETA_STORAGE: boolean;
......@@ -167,11 +170,21 @@ export abstract class Config implements IConfig {
// The C++ SDK mainly requires a fix on how behave on mutable calls.
public static SKIP_WRITE_LOCK_CHECK_ON_MUTABLE_OPERATIONS = true;
// Access policy of a subproject can either be uniform or dataset
public static UNIFORM_ACCESS_POLICY = 'uniform';
public static DATASET_ACCESS_POLICY = 'dataset';
// Audience Check Reporting
// This will be temporary used instead of the more generic JWKS PROXY.
// We want a non-fail check that report only the missing audience
// slb requirement - to support client migration in september 2021
// This will be removed and replace in october 2021 with the generic JWKS PROXY
public static ENABLE_SDMS_ID_AUDIENCE_CHECK = false;
// Enable token exchange to include DE target audience
public static ENABLE_DE_TOKEN_EXCHANGE = false;
public static DES_TARGET_AUDIENCE = undefined;
public static setCloudProvider(cloudProvider: string) {
Config.CLOUDPROVIDER = cloudProvider;
if (Config.CLOUDPROVIDER === undefined) {
......@@ -238,6 +251,10 @@ export abstract class Config implements IConfig {
Config.SERVICE_AUTH_PROVIDER = model.SERVICE_AUTH_PROVIDER || 'generic';
Config.SERVICE_AUTH_PROVIDER_CREDENTIAL = model.SERVICE_AUTH_PROVIDER_CREDENTIAL || undefined;
Config.ENABLE_SDMS_ID_AUDIENCE_CHECK = model.ENABLE_SDMS_ID_AUDIENCE_CHECK || false;
Config.ENABLE_DE_TOKEN_EXCHANGE = model.ENABLE_DE_TOKEN_EXCHANGE || false;
Config.DES_TARGET_AUDIENCE = model.DES_TARGET_AUDIENCE;
Config.checkRequiredConfig(Config.CLOUDPROVIDER, 'CLOUDPROVIDER');
Config.checkRequiredConfig(Config.SERVICE_ENV, 'SERVICE_ENV');
Config.checkRequiredConfig(Config.IMP_SERVICE_ACCOUNT_SIGNER, 'IMP_SERVICE_ACCOUNT_SIGNER');
......
......@@ -105,6 +105,11 @@ export class AzureConfig extends Config {
JWT_ENABLE_FEATURE: process.env.JWT_ENABLE_FEATURE ? process.env.JWT_ENABLE_FEATURE === 'true' : false,
TENANT_JOURNAL_ON_DATA_PARTITION: true,
CORRELATION_ID: 'correlation-id',
ENABLE_SDMS_ID_AUDIENCE_CHECK: process.env.ENABLE_SDMS_ID_AUDIENCE_CHECK !== undefined ?
process.env.ENABLE_SDMS_ID_AUDIENCE_CHECK === 'true' : false,
ENABLE_DE_TOKEN_EXCHANGE: process.env.ENABLE_DE_TOKEN_EXCHANGE !== undefined ?
process.env.ENABLE_DE_TOKEN_EXCHANGE === 'true' : false,
DES_TARGET_AUDIENCE: process.env.DES_TARGET_AUDIENCE,
FEATURE_FLAG_AUTHORIZATION: process.env.FEATURE_FLAG_AUTHORIZATION !== undefined ?
process.env.FEATURE_FLAG_AUTHORIZATION !== 'false' : true,
FEATURE_FLAG_LEGALTAG: process.env.FEATURE_FLAG_LEGALTAG !== undefined ?
......
......@@ -131,6 +131,11 @@ export class ConfigGoogle extends Config {
CORRELATION_ID: 'correlation-id',
SERVICE_AUTH_PROVIDER: ConfigGoogle.SERVICE_AUTH_PROVIDER,
SERVICE_AUTH_PROVIDER_CREDENTIAL: ConfigGoogle.SERVICE_AUTH_PROVIDER_CREDENTIAL,
ENABLE_SDMS_ID_AUDIENCE_CHECK: process.env.ENABLE_SDMS_ID_AUDIENCE_CHECK !== undefined ?
process.env.ENABLE_SDMS_ID_AUDIENCE_CHECK === 'true' : false,
ENABLE_DE_TOKEN_EXCHANGE: process.env.ENABLE_DE_TOKEN_EXCHANGE !== undefined ?
process.env.ENABLE_DE_TOKEN_EXCHANGE === 'true' : false,
DES_TARGET_AUDIENCE: process.env.DES_TARGET_AUDIENCE,
TENANT_JOURNAL_ON_DATA_PARTITION: false,
FEATURE_FLAG_AUTHORIZATION: process.env.FEATURE_FLAG_AUTHORIZATION !== undefined ?
process.env.FEATURE_FLAG_AUTHORIZATION !== 'false' : true,
......
......@@ -19,7 +19,8 @@ import express from 'express';
import jwtProxy, { JwtProxyOptions } from 'jwtproxy';
import { Config, LoggerFactory } from '../cloud';
import { ServiceRouter } from '../services';
import { Error, Feature, FeatureFlags, Response } from '../shared';
import { Error, Feature, FeatureFlags, Response, Utils } from '../shared';
import { AuthProviderFactory } from '../auth';
import { v4 as uuidv4 } from 'uuid';
import fs from 'fs';
......@@ -102,11 +103,35 @@ export class Server {
this.app.use('/seistore-svc/api/v3/swagger-ui.html',swaggerUi.serve, swaggerUi.setup(swaggerDocument,{
customCss: '.swagger-ui .topbar { display: none }'
}));
this.app.use((req: express.Request, res: express.Response, next: express.NextFunction) => {
this.app.use(async (req: express.Request, res: express.Response, next: express.NextFunction) => {
// Audience Check Reporting
// This will be temporary used instead of the more generic JWKS PROXY.
// We want a non-fail check that report only the missing audience
// slb requirement - to support client migration in september 2021
// This will be removed and replace in october 2021 with the generic JWKS PROXY
if(Config.ENABLE_SDMS_ID_AUDIENCE_CHECK) {
if(req.headers.authorization) {
const audience = Utils.getAudienceFromPayload(req.headers.authorization);
const sdmsID = AuthProviderFactory.build(Config.SERVICE_AUTH_PROVIDER).getClientID();
if((Array.isArray(audience) && audience.indexOf(sdmsID) === -1) || (audience !== sdmsID)) {
if(audience.indexOf(sdmsID) === -1) {
LoggerFactory.build(Config.CLOUDPROVIDER).info('[audience] ' +
JSON.stringify(Utils.getPayloadFromStringToken(req.headers.authorization)));
}
}
}
}
// not required anymore - to verify
if (req.get('slb-on-behalf-of') !== undefined) {
req.headers.authorization = req.get('slb-on-behalf-of');
// If required, exchange the caller credentials to include the DE target audience
if(Config.ENABLE_DE_TOKEN_EXCHANGE) {
if(Config.DES_TARGET_AUDIENCE) {
if(req.headers.authorization) {
req.headers.authorization = await AuthProviderFactory.build(
Config.SERVICE_AUTH_PROVIDER).exchangeCredentialAudience(
req.headers.authorization, Config.DES_TARGET_AUDIENCE);
}
}
}
// ensure the authorization header is passed/
......@@ -160,7 +185,7 @@ export class Server {
audience: Config.JWT_AUDIENCE
}
// adding middleware to intercept and valiate jwt
// adding middleware to intercept and validate jwt
this.app.use(jwtProxy(jwtValidateOptions));
this.app.use(ServiceRouter);
......
......@@ -43,7 +43,7 @@ export class Utils {
return Number(this.getPayloadFromStringToken(base64JwtPayload).exp);
}
public static getAudienceFromPayload(base64JwtPayload: string): string {
public static getAudienceFromPayload(base64JwtPayload: string): string | string[] {
return this.getPayloadFromStringToken(base64JwtPayload).aud;
}
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment