diff --git a/src/context/directory/index.ts b/src/context/directory/index.ts index 51cd700f7..bd004d027 100644 --- a/src/context/directory/index.ts +++ b/src/context/directory/index.ts @@ -3,11 +3,11 @@ import { loadFileAndReplaceKeywords, Auth0 } from '../../tools'; import cleanAssets from '../../readonly'; import log from '../../logger'; -import handlers from './handlers'; +import handlers, { DirectoryHandler } from './handlers'; import { isDirectory, isFile, stripIdentifiers, toConfigFn } from '../../utils'; -import { Assets, Auth0APIClient, Config } from '../../types' +import { Assets, Auth0APIClient, Config, AssetTypes } from '../../types' type KeywordMappings = { [key: string]: (string | number)[] | string | number } @@ -78,14 +78,17 @@ export default class DirectoryContext { // Copy clients to be used by handlers which require converting client_id to the name // Must copy as the client_id will be stripped if AUTH0_EXPORT_IDENTIFIERS is false //@ts-ignore because assets haven't been typed yet TODO: type assets - this.assets.clientsOrig = [...this.assets.clients]; + this.assets.clientsOrig = [...this.assets.clients || []]; // Optionally Strip identifiers if (!this.config.AUTH0_EXPORT_IDENTIFIERS) { this.assets = stripIdentifiers(auth0, this.assets); } - await Promise.all(Object.entries(handlers).map(async ([name, handler]) => { + await Promise.all(Object.entries(handlers).filter(([handlerName]: [AssetTypes, DirectoryHandler]) => { + const excludedAssetTypes = this.config.AUTH0_EXCLUDED || [] + return !excludedAssetTypes.includes(handlerName) + }).map(async ([name, handler]) => { try { await handler.dump(this); } catch (err) { diff --git a/src/context/index.ts b/src/context/index.ts index b66204ffb..4ddeac45d 100644 --- a/src/context/index.ts +++ b/src/context/index.ts @@ -17,6 +17,7 @@ const nonPrimitiveProps: (keyof Config)[] = [ 'AUTH0_EXCLUDED_CONNECTIONS', 'AUTH0_EXCLUDED_RESOURCE_SERVERS', 'AUTH0_EXCLUDED_DEFAULTS', + 'AUTH0_EXCLUDED', 'EXCLUDED_PROPS', 'INCLUDED_PROPS' ]; diff --git a/src/context/yaml/index.ts b/src/context/yaml/index.ts index fcc7d8391..4bcabe289 100644 --- a/src/context/yaml/index.ts +++ b/src/context/yaml/index.ts @@ -7,9 +7,9 @@ import log from '../../logger'; import { isFile, toConfigFn, stripIdentifiers, formatResults, recordsSorter } from '../../utils'; -import handlers from './handlers'; +import handlers, { YAMLHandler } from './handlers'; import cleanAssets from '../../readonly'; -import { Assets, Config, Auth0APIClient } from '../../types' +import { Assets, Config, Auth0APIClient, AssetTypes } from '../../types' type KeywordMappings = { [key: string]: (string | number)[] | string | number } @@ -101,7 +101,10 @@ export default class YAMLContext { throw new Error(`Problem loading tenant data from Auth0 ${err}${extraMessage}`); } - await Promise.all(Object.entries(handlers).map(async ([name, handler]) => { + await Promise.all(Object.entries(handlers).filter(([handlerName]: [AssetTypes, YAMLHandler]) => { + const excludedAssetTypes = this.config.AUTH0_EXCLUDED || [] + return !excludedAssetTypes.includes(handlerName) + }).map(async ([name, handler]) => { try { const data = await handler.dump(this); if (data) { diff --git a/src/tools/auth0/index.ts b/src/tools/auth0/index.ts index 3d698634b..379ebbd84 100644 --- a/src/tools/auth0/index.ts +++ b/src/tools/auth0/index.ts @@ -39,11 +39,13 @@ export default class Auth0 { this.config = config; this.assets = assets; - this.handlers = Object.values(handlers).map((h) => { - //@ts-ignore because prompts don't appear to have been universally implemented yet - const handler = new h.default({ client: this.client, config: this.config }); - return handler - }); + this.handlers = Object.values(handlers).map((handler) => { + //@ts-ignore because class expects `type` property but gets directly injected into class constructors + return new handler.default({ client: this.client, config: this.config }); + }).filter((handler) => { + const excludedAssetTypes = config('AUTH0_EXCLUDED') || [] + return !excludedAssetTypes.includes(handler.type) + }) } async runStage(stage: Stage): Promise { diff --git a/src/types.ts b/src/types.ts index a0778f456..833bf0075 100644 --- a/src/types.ts +++ b/src/types.ts @@ -115,18 +115,13 @@ export type Config = { AUTH0_CLIENT_SECRET: string AUTH0_INPUT_FILE: string AUTH0_ALLOW_DELETE: boolean + AUTH0_EXCLUDED: AssetTypes[] EXTENSION_SECRET: string AUTH0_ACCESS_TOKEN?: string AUTH0_BASE_PATH?: string AUTH0_AUDIENCE?: string AUTH0_API_MAX_RETRIES?: number AUTH0_KEYWORD_REPLACE_MAPPINGS?: { [key: string]: string[] | string } - AUTH0_EXCLUDED_RULES?: string[] - AUTH0_EXCLUDED_CLIENTS?: string[] - AUTH0_EXCLUDED_DATABASES?: string[] - AUTH0_EXCLUDED_CONNECTIONS?: string[] - AUTH0_EXCLUDED_RESOURCE_SERVERS?: string[] - AUTH0_EXCLUDED_DEFAULTS?: string[] AUTH0_EXPORT_IDENTIFIERS?: boolean AUTH0_CONNECTIONS_DIRECTORY?: string EXCLUDED_PROPS?: { @@ -134,6 +129,13 @@ export type Config = { } INCLUDED_PROPS?: {} AUTH0_IGNORE_UNAVAILABLE_MIGRATIONS?: boolean + // Eventually deprecate: + AUTH0_EXCLUDED_RULES?: string[] + AUTH0_EXCLUDED_CLIENTS?: string[] + AUTH0_EXCLUDED_DATABASES?: string[] + AUTH0_EXCLUDED_CONNECTIONS?: string[] + AUTH0_EXCLUDED_RESOURCE_SERVERS?: string[] + AUTH0_EXCLUDED_DEFAULTS?: string[] }// TODO: replace with a more accurate representation of the Config type export type Asset = { [key: string]: any } diff --git a/test/tools/auth0/index.test.ts b/test/tools/auth0/index.test.ts new file mode 100644 index 000000000..699584d37 --- /dev/null +++ b/test/tools/auth0/index.test.ts @@ -0,0 +1,34 @@ +import { expect } from 'chai'; +import Auth0 from '../../../src/tools/auth0' +import { Auth0APIClient, Assets } from '../../../src/types' + +const mockEmptyClient = {} as Auth0APIClient +const mockEmptyAssets = {} as Assets + +describe("#Auth0 class", () => { + + describe("#resource exclusion", () => { + it('should exclude handlers listed in AUTH0_EXCLUDED from Auth0 class', () => { + + const auth0WithoutExclusions = new Auth0(mockEmptyClient, mockEmptyAssets, () => []); + + const AUTH0_EXCLUDED = ['rules', 'organizations', 'connections'] + const auth0WithExclusions = new Auth0(mockEmptyClient, mockEmptyAssets, () => AUTH0_EXCLUDED); + + expect(auth0WithoutExclusions.handlers.length).to.equal(auth0WithExclusions.handlers.length + AUTH0_EXCLUDED.length) // Number of handlers is reduced by number of exclusions + + const areAllExcludedHandlersAbsent = auth0WithExclusions.handlers.some((handler) => { + return AUTH0_EXCLUDED.includes(handler.type) + }) + + expect(areAllExcludedHandlersAbsent).to.be.false; + }) + + it('should not exclude any handlers if AUTH0_EXCLUDED is undefined', () => { + const AUTH0_EXCLUDED = undefined + const auth0 = new Auth0(mockEmptyClient, mockEmptyAssets, () => AUTH0_EXCLUDED); + + expect(auth0.handlers.length).to.be.greaterThan(0) + }) + }) +}) \ No newline at end of file diff --git a/test/tools/auth0/validator.tests.js b/test/tools/auth0/validator.tests.js index dd43ff9a6..364b88f63 100644 --- a/test/tools/auth0/validator.tests.js +++ b/test/tools/auth0/validator.tests.js @@ -2,6 +2,8 @@ import { expect } from 'chai'; import Auth0 from '../../../src/tools/auth0'; import constants from '../../../src/tools/constants'; +const mockConfigFn = () => { }; + describe('#schema validation tests', () => { const client = { rules: { @@ -33,13 +35,13 @@ describe('#schema validation tests', () => { }; const checkPassed = (data, done) => { - const auth0 = new Auth0(client, data, {}); + const auth0 = new Auth0(client, data, mockConfigFn); auth0.validate().then(passedCb(done), failedCb(done)); }; const checkRequired = (field, data, done) => { - const auth0 = new Auth0({}, data, {}); + const auth0 = new Auth0({}, data, mockConfigFn); auth0 .validate() @@ -50,7 +52,7 @@ describe('#schema validation tests', () => { }; const checkEnum = (data, done) => { - const auth0 = new Auth0({}, data, {}); + const auth0 = new Auth0({}, data, mockConfigFn); auth0 .validate() @@ -61,7 +63,7 @@ describe('#schema validation tests', () => { }; const checkTypeError = (field, expectedType, data, done) => { - const auth0 = new Auth0({}, data, {}); + const auth0 = new Auth0({}, data, mockConfigFn); auth0 .validate() @@ -77,7 +79,7 @@ describe('#schema validation tests', () => { anything: 'anything' } ]; - const auth0 = new Auth0({}, { branding: data }, {}); + const auth0 = new Auth0({}, { branding: data }, mockConfigFn); auth0.validate().then(failedCb(done), passedCb(done, 'should be object')); }); @@ -125,7 +127,7 @@ describe('#schema validation tests', () => { audience: 'audience' } ]; - const auth0 = new Auth0({}, { clientGrants: data }, {}); + const auth0 = new Auth0({}, { clientGrants: data }, mockConfigFn); auth0.validate().then(failedCb(done), passedCb(done, 'should be array')); }); @@ -155,7 +157,7 @@ describe('#schema validation tests', () => { name: '' } ]; - const auth0 = new Auth0({}, { clients: data }, {}); + const auth0 = new Auth0({}, { clients: data }, mockConfigFn); auth0 .validate() @@ -235,7 +237,7 @@ describe('#schema validation tests', () => { anything: 'anything' } ]; - const auth0 = new Auth0({}, { emailProvider: data }, {}); + const auth0 = new Auth0({}, { emailProvider: data }, mockConfigFn); auth0.validate().then(failedCb(done), passedCb(done, 'should be object')); }); @@ -490,7 +492,7 @@ describe('#schema validation tests', () => { anything: 'anything' } ]; - const auth0 = new Auth0({}, { prompts: data }, {}); + const auth0 = new Auth0({}, { prompts: data }, mockConfigFn); auth0.validate().then(failedCb(done), passedCb(done, 'should be object')); }); @@ -545,7 +547,7 @@ describe('#schema validation tests', () => { name: '-rule-' } ]; - const auth0 = new Auth0({}, { rules: data }, {}); + const auth0 = new Auth0({}, { rules: data }, mockConfigFn); auth0 .validate() @@ -595,7 +597,7 @@ describe('#schema validation tests', () => { value: 'value' } ]; - const auth0 = new Auth0({}, { rulesConfigs: data }, {}); + const auth0 = new Auth0({}, { rulesConfigs: data }, mockConfigFn); auth0 .validate() @@ -626,7 +628,7 @@ describe('#schema validation tests', () => { name: '-hook-' } ]; - const auth0 = new Auth0({}, { hooks: data }, {}); + const auth0 = new Auth0({}, { hooks: data }, mockConfigFn); auth0 .validate() @@ -668,7 +670,7 @@ describe('#schema validation tests', () => { anything: 'anything' } ]; - const auth0 = new Auth0({}, { tenant: data }, {}); + const auth0 = new Auth0({}, { tenant: data }, mockConfigFn); auth0.validate().then(failedCb(done), passedCb(done, 'should be object')); }); @@ -686,7 +688,7 @@ describe('#schema validation tests', () => { it('should fail validation if migrations is not an object', (done) => { const data = ''; - const auth0 = new Auth0({}, { migrations: data }, {}); + const auth0 = new Auth0({}, { migrations: data }, mockConfigFn); auth0.validate().then(failedCb(done), passedCb(done, 'should be object')); }); @@ -696,7 +698,7 @@ describe('#schema validation tests', () => { migration_flag: 'string' }; - const auth0 = new Auth0({}, { migrations: data }, {}); + const auth0 = new Auth0({}, { migrations: data }, mockConfigFn); auth0.validate().then(failedCb(done), passedCb(done, 'should be boolean')); }); diff --git a/test/utils.test.js b/test/utils.test.js index b68b5011a..40b59e29a 100644 --- a/test/utils.test.js +++ b/test/utils.test.js @@ -22,6 +22,8 @@ import { toConfigFn } from '../src/utils'; +const mockConfigFn = () => { }; + describe('#utils', function() { it('should check if directory exist', () => { const dirExist = path.join(testDataDir, 'utils', 'isdir'); @@ -95,7 +97,7 @@ describe('#utils', function() { rulesConfigs: [ { key: 'test', value: 'test' } ] }; - const auth0 = new Auth0(mockMgmtClient(), {}, {}); + const auth0 = new Auth0(mockMgmtClient(), {}, mockConfigFn); expect(stripIdentifiers(auth0, assets)).to.deep.equal({ clients: [