-
Notifications
You must be signed in to change notification settings - Fork 175
feat: Add supplemental signals configuration for Akamai integration #1310
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| { | ||
| "akamai_enabled": true | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -525,3 +525,5 @@ riskAssessment: | |
| # new_device: | ||
| # remember_for: 30 | ||
|
|
||
| supplementalSignals: | ||
| akamai_enabled: true | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| import path from 'path'; | ||
| import { existsMustBeDir, isFile, dumpJSON, loadJSON } from '../../../utils'; | ||
| import { DirectoryHandler } from '.'; | ||
| import DirectoryContext from '..'; | ||
| import { ParsedAsset } from '../../../types'; | ||
|
|
||
| type ParsedSupplementalSignals = ParsedAsset<'supplementalSignals', { akamai_enabled?: boolean }>; | ||
|
|
||
| function parse(context: DirectoryContext): ParsedSupplementalSignals { | ||
| const baseFolder = path.join(context.filePath); | ||
| if (!existsMustBeDir(baseFolder)) return { supplementalSignals: null }; // Skip | ||
|
|
||
| const supplementalSignalsFile = path.join(baseFolder, 'supplemental-signals.json'); | ||
|
|
||
| if (!isFile(supplementalSignalsFile)) { | ||
| return { supplementalSignals: null }; | ||
| } | ||
|
|
||
| const supplementalSignals = loadJSON(supplementalSignalsFile, { | ||
| mappings: context.mappings, | ||
| disableKeywordReplacement: context.disableKeywordReplacement, | ||
| }); | ||
|
|
||
| return { | ||
| supplementalSignals, | ||
| }; | ||
| } | ||
|
|
||
| async function dump(context: DirectoryContext): Promise<void> { | ||
| const { supplementalSignals } = context.assets; | ||
|
|
||
| if (!supplementalSignals) return; // Skip, nothing to dump | ||
|
|
||
| const supplementalSignalsFile = path.join(context.filePath, 'supplemental-signals.json'); | ||
| dumpJSON(supplementalSignalsFile, supplementalSignals); | ||
| } | ||
|
|
||
| export default { | ||
| parse, | ||
| dump, | ||
| } as DirectoryHandler<ParsedSupplementalSignals>; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| import { YAMLHandler } from '.'; | ||
| import YAMLContext from '..'; | ||
| import { ParsedAsset } from '../../../types'; | ||
|
|
||
| type ParsedSupplementalSignals = ParsedAsset<'supplementalSignals', { akamai_enabled?: boolean }>; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use SDK interface |
||
|
|
||
| async function parseAndDump(context: YAMLContext): Promise<ParsedSupplementalSignals> { | ||
| const { supplementalSignals } = context.assets; | ||
|
|
||
| if (!supplementalSignals) return { supplementalSignals: null }; | ||
|
|
||
| return { | ||
| supplementalSignals, | ||
| }; | ||
| } | ||
|
|
||
| const supplementalSignalsHandler: YAMLHandler<ParsedSupplementalSignals> = { | ||
| parse: parseAndDump, | ||
| dump: parseAndDump, | ||
| }; | ||
|
|
||
| export default supplementalSignalsHandler; | ||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,65 @@ | ||||||
| import { Management, ManagementError } from 'auth0'; | ||||||
| import DefaultHandler, { order } from './default'; | ||||||
| import { Asset, Assets } from '../../../types'; | ||||||
| import log from '../../../logger'; | ||||||
|
|
||||||
| export const schema = { | ||||||
| type: 'object', | ||||||
| properties: { | ||||||
| akamai_enabled: { | ||||||
| type: 'boolean', | ||||||
| description: 'Enable Akamai supplemental signals integration', | ||||||
| }, | ||||||
| }, | ||||||
| additionalProperties: false, | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. better to remove it: additionalProperties: false, |
||||||
| }; | ||||||
|
|
||||||
| export type SupplementalSignals = Management.GetSupplementalSignalsResponseContent; | ||||||
|
|
||||||
| export default class SupplementalSignalsHandler extends DefaultHandler { | ||||||
| existing: SupplementalSignals; | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|
|
||||||
| constructor(options: DefaultHandler) { | ||||||
| super({ | ||||||
| ...options, | ||||||
| type: 'supplementalSignals', | ||||||
| }); | ||||||
| } | ||||||
|
|
||||||
| async getType(): Promise<Asset> { | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| try { | ||||||
| const supplementalSignals = await this.client.supplementalSignals.get(); | ||||||
| this.existing = supplementalSignals; | ||||||
| return supplementalSignals; | ||||||
| } catch (err) { | ||||||
| if (err instanceof ManagementError && err.statusCode === 403) { | ||||||
| log.debug( | ||||||
| 'Supplemental Signals feature is not available for this tenant. Please verify `scope` or contact Auth0 support to enable this feature.' | ||||||
| ); | ||||||
| return {}; | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| } | ||||||
| throw err; | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| async validate(assets: Assets): Promise<void> { | ||||||
| const { supplementalSignals } = assets; | ||||||
|
|
||||||
| if (!supplementalSignals) return; | ||||||
| } | ||||||
|
Comment on lines
+45
to
+49
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we need this validation? this is not needed |
||||||
|
|
||||||
| @order('100') | ||||||
| async processChanges(assets: Assets): Promise<void> { | ||||||
| const { supplementalSignals } = assets; | ||||||
|
|
||||||
| if (!supplementalSignals) return; | ||||||
|
|
||||||
| if (supplementalSignals && Object.keys(supplementalSignals).length > 0) { | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| await this.client.supplementalSignals.patch( | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we need 403 here? |
||||||
| supplementalSignals as Management.UpdateSupplementalSignalsRequestContent | ||||||
| ); | ||||||
| this.updated += 1; | ||||||
| this.didUpdate(supplementalSignals); | ||||||
| } | ||||||
| } | ||||||
| } | ||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -137,6 +137,7 @@ export type Assets = Partial<{ | |
| rulesConfigs: Asset[] | null; | ||
| tenant: Tenant | null; | ||
| triggers: Asset[] | null; | ||
| supplementalSignals: Management.GetSupplementalSignalsResponseContent | null; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. keep |
||
| // non-resource types | ||
| exclude?: { | ||
| [key: string]: string[]; | ||
|
|
@@ -204,7 +205,8 @@ export type AssetTypes = | |
| | 'networkACLs' | ||
| | 'userAttributeProfiles' | ||
| | 'connectionProfiles' | ||
| | 'tokenExchangeProfiles'; | ||
| | 'tokenExchangeProfiles' | ||
| | 'supplementalSignals'; | ||
|
|
||
| export type KeywordMappings = { [key: string]: (string | number)[] | string | number }; | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,88 @@ | ||
| import path from 'path'; | ||
| import { expect } from 'chai'; | ||
|
|
||
| import Context from '../../../src/context/directory'; | ||
| import { testDataDir, createDir, mockMgmtClient, cleanThenMkdir } from '../../utils'; | ||
| import handler from '../../../src/context/directory/handlers/supplementalSignals'; | ||
| import { loadJSON } from '../../../src/utils'; | ||
|
|
||
| describe('#directory context supplementalSignals', () => { | ||
| it('should process supplementalSignals', async () => { | ||
| const supplementalSignalsTest = { | ||
| 'supplemental-signals.json': `{ | ||
| "akamai_enabled": true | ||
| }`, | ||
| }; | ||
|
|
||
| const supplementalSignalsTarget = { | ||
| akamai_enabled: true, | ||
| }; | ||
|
|
||
| createDir(path.join(testDataDir, 'directory'), { | ||
| supplementalSignals1: supplementalSignalsTest, | ||
| }); | ||
|
|
||
| const config = { | ||
| AUTH0_INPUT_FILE: path.join(testDataDir, 'directory', 'supplementalSignals1'), | ||
| }; | ||
| const context = new Context(config, mockMgmtClient()); | ||
| await context.loadAssetsFromLocal(); | ||
|
|
||
| expect(context.assets.supplementalSignals).to.deep.equal(supplementalSignalsTarget); | ||
| }); | ||
|
|
||
| it('should process supplementalSignals with keyword replacement', async () => { | ||
| const supplementalSignalsTest = { | ||
| 'supplemental-signals.json': `{ | ||
| "akamai_enabled": ##AKAMAI_ENABLED## | ||
| }`, | ||
| }; | ||
|
|
||
| const supplementalSignalsTarget = { | ||
| akamai_enabled: false, | ||
| }; | ||
|
|
||
| createDir(path.join(testDataDir, 'directory'), { | ||
| supplementalSignals2: supplementalSignalsTest, | ||
| }); | ||
|
|
||
| const config = { | ||
| AUTH0_INPUT_FILE: path.join(testDataDir, 'directory', 'supplementalSignals2'), | ||
| AUTH0_KEYWORD_REPLACE_MAPPINGS: { AKAMAI_ENABLED: false }, | ||
| }; | ||
| const context = new Context(config, mockMgmtClient()); | ||
| await context.loadAssetsFromLocal(); | ||
|
|
||
| expect(context.assets.supplementalSignals).to.deep.equal(supplementalSignalsTarget); | ||
| }); | ||
|
|
||
| it('should dump supplementalSignals', async () => { | ||
| const dir = path.join(testDataDir, 'directory', 'supplementalSignalsDump'); | ||
| cleanThenMkdir(dir); | ||
| const context = new Context({ AUTH0_INPUT_FILE: dir }, mockMgmtClient()); | ||
|
|
||
| context.assets.supplementalSignals = { | ||
| akamai_enabled: true, | ||
| }; | ||
|
|
||
| await handler.dump(context); | ||
| const dumped = loadJSON(path.join(dir, 'supplemental-signals.json')); | ||
|
|
||
| expect(dumped).to.deep.equal(context.assets.supplementalSignals); | ||
| }); | ||
|
|
||
| it('should dump supplementalSignals with false value', async () => { | ||
| const dir = path.join(testDataDir, 'directory', 'supplementalSignalsDumpFalse'); | ||
| cleanThenMkdir(dir); | ||
| const context = new Context({ AUTH0_INPUT_FILE: dir }, mockMgmtClient()); | ||
|
|
||
| context.assets.supplementalSignals = { | ||
| akamai_enabled: false, | ||
| }; | ||
|
|
||
| await handler.dump(context); | ||
| const dumped = loadJSON(path.join(dir, 'supplemental-signals.json')); | ||
|
|
||
| expect(dumped).to.deep.equal(context.assets.supplementalSignals); | ||
| }); | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use SDK interface