Skip to content

Commit

Permalink
Merge pull request #590 from auth0/prevent-updating-migration-flags
Browse files Browse the repository at this point in the history
Prevent the updating of migration flags
  • Loading branch information
willvedd authored Jun 27, 2022
2 parents 852bbc8 + 5486177 commit 57944fc
Show file tree
Hide file tree
Showing 3 changed files with 211 additions and 8 deletions.
73 changes: 69 additions & 4 deletions src/tools/auth0/handlers/tenant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,23 @@ import ValidationError from '../../validationError';
import DefaultHandler, { order } from './default';
import { supportedPages, pageNameMap } from './pages';
import { convertJsonToString } from '../../utils';
import { Asset, Assets } from '../../../types';
import { Asset, Assets, Language } from '../../../types';

export const schema = {
type: 'object',
};

export type Tenant = Asset & { enabled_locales: Language[]; flags: { [key: string]: boolean } };

const blockPageKeys = [
...Object.keys(pageNameMap),
...Object.values(pageNameMap),
...supportedPages,
];

export default class TenantHandler extends DefaultHandler {
existing: Tenant;

constructor(options: DefaultHandler) {
super({
...options,
Expand All @@ -25,6 +29,9 @@ export default class TenantHandler extends DefaultHandler {

async getType(): Promise<Asset> {
const tenant = await this.client.tenant.getSettings();

this.existing = tenant;

blockPageKeys.forEach((key) => {
if (tenant[key]) delete tenant[key];
});
Expand Down Expand Up @@ -53,10 +60,68 @@ export default class TenantHandler extends DefaultHandler {
async processChanges(assets: Assets): Promise<void> {
const { tenant } = assets;

if (tenant && Object.keys(tenant).length > 0) {
await this.client.tenant.updateSettings(tenant);
// Do nothing if not set
if (!tenant) return;

const existingTenant = this.existing || (await this.getType());

const updatedTenant = {
...tenant,
flags: sanitizeMigrationFlags({
existingFlags: existingTenant.flags,
proposedFlags: tenant.flags,
}),
};

if (updatedTenant && Object.keys(updatedTenant).length > 0) {
await this.client.tenant.updateSettings(updatedTenant);
this.updated += 1;
this.didUpdate(tenant);
this.didUpdate(updatedTenant);
}
}
}

export const sanitizeMigrationFlags = ({
existingFlags = {},
proposedFlags = {},
}: {
existingFlags: Tenant['flags'];
proposedFlags: Tenant['flags'];
}): Tenant['flags'] => {
/*
Tenants can only update migration flags that are already configured.
If moving configuration from one tenant to another, there may be instances
where different migration flags exist and cause an error on update. This
function removes any migration flags that aren't already present on the target
tenant. See: https://github.com/auth0/auth0-deploy-cli/issues/374
*/

const tenantMigrationFlags = [
'disable_clickjack_protection_headers',
'enable_mgmt_api_v1',
'trust_azure_adfs_email_verified_connection_property',
'include_email_in_reset_pwd_redirect',
'include_email_in_verify_email_redirect',
];

return Object.keys(proposedFlags).reduce(
(acc: Tenant['flags'], proposedKey: string): Tenant['flags'] => {
const isMigrationFlag = tenantMigrationFlags.includes(proposedKey);
if (!isMigrationFlag)
return {
...acc,
[proposedKey]: proposedFlags[proposedKey],
};

const keyCurrentlyExists = existingFlags[proposedKey] !== undefined;
if (keyCurrentlyExists)
return {
...acc,
[proposedKey]: proposedFlags[proposedKey],
};

return acc;
},
{}
);
};
8 changes: 4 additions & 4 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
PromptsCustomText,
PromptSettings,
} from './tools/auth0/handlers/prompts';

import { Tenant } from './tools/auth0/handlers/tenant';
import { Theme } from './tools/auth0/handlers/themes';

type SharedPaginationParams = {
Expand Down Expand Up @@ -145,8 +145,8 @@ export type BaseAuth0APIClient = {
getAll: () => Promise<Asset[]>;
};
tenant: APIClientBaseFunctions & {
getSettings: () => Promise<Asset & { enabled_locales: Language[] }>;
updateSettings: (arg0: Asset) => Promise<void>;
getSettings: () => Promise<Tenant>;
updateSettings: (arg0: Partial<Tenant>) => Promise<Tenant>;
};
triggers: APIClientBaseFunctions & {
getTriggerBindings: () => Promise<Asset>;
Expand Down Expand Up @@ -230,7 +230,7 @@ export type Assets = Partial<{
roles: Asset[] | null;
rules: Asset[] | null;
rulesConfigs: Asset[] | null;
tenant: Asset | null;
tenant: Tenant | null;
triggers: Asset[] | null;
//non-resource types
exclude?: {
Expand Down
138 changes: 138 additions & 0 deletions test/tools/auth0/handlers/tenant.tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ describe('#tenant handler', () => {
it('should update tenant settings', async () => {
const auth0 = {
tenant: {
getSettings: () => ({
friendly_name: 'Test',
default_directory: 'users',
}),
updateSettings: (data) => {
expect(data).to.be.an('object');
expect(data.sandbox_version).to.equal('4');
Expand All @@ -51,5 +55,139 @@ describe('#tenant handler', () => {

await stageFn.apply(handler, [{ tenant: { sandbox_version: '4' } }]);
});

it("should remove migration flags if don't exist on target tenant", async () => {
const mockFlags = {
trust_azure_adfs_email_verified_connection_property: true, // Migration flag
'some-flag-1': true,
'some-flag-2': false,
};

const auth0 = {
tenant: {
getSettings: async () => {
const flags = { ...mockFlags };
delete flags.trust_azure_adfs_email_verified_connection_property;
return Promise.resolve({
friendly_name: 'Test',
default_directory: 'users',
flags,
});
},
updateSettings: (data) => {
expect(data).to.be.an('object');
expect(data.flags).to.deep.equal(
(() => {
const flags = { ...mockFlags };
delete flags.trust_azure_adfs_email_verified_connection_property;
return flags;
})()
);
return Promise.resolve(data);
},
},
};

const handler = new tenant.default({ client: auth0 });
const { processChanges } = Object.getPrototypeOf(handler);

await processChanges.apply(handler, [{ tenant: { flags: mockFlags } }]);
});
});

describe('#sanitizeMigrationFlags function', () => {
it('should not alter flags if existing and proposed are identical', () => {
const flags = {
trust_azure_adfs_email_verified_connection_property: true,
'some-flag-1': false,
'some-flag-2': true,
};

const result = tenant.sanitizeMigrationFlags({
existingFlags: flags,
proposedFlags: flags,
});

expect(result).to.deep.equal(flags);
});

it("should not remove migration flag if proposed but doesn't exist currently", () => {
const existingFlags = {
'some-flag-1': false,
'some-flag-2': true,
};

const proposedFlags = {
trust_azure_adfs_email_verified_connection_property: true, // Migration flag
'some-flag-1': true,
'some-flag-2': true,
};

const result = tenant.sanitizeMigrationFlags({
existingFlags,
proposedFlags,
});

const expectedFlags = (() => {
const expected = proposedFlags;
delete expected.trust_azure_adfs_email_verified_connection_property;
return expected;
})();

expect(result).to.deep.equal(expectedFlags);
});

it('allow alterations to migration flags if they currently exist', () => {
const existingFlags = {
'some-flag-1': false,
'some-flag-2': true,
trust_azure_adfs_email_verified_connection_property: false, // Migration flag
};

const proposedFlags = {
trust_azure_adfs_email_verified_connection_property: true, // Migration flag
'some-flag-1': true,
'some-flag-2': false,
};

const result = tenant.sanitizeMigrationFlags({ existingFlags, proposedFlags });

expect(result).to.deep.equal(proposedFlags);
});

it('should allow alterations of non-migration flags even if they do not currently exist', () => {
const existingFlags = {
trust_azure_adfs_email_verified_connection_property: true,
};

const proposedFlags = {
trust_azure_adfs_email_verified_connection_property: true,
'some-flag-1': false, // Doesn't currently exist
'some-flag-2': true, // Doesn't currently exist
'some-flag-3': true, // Doesn't currently exist
};

const result = tenant.sanitizeMigrationFlags({ existingFlags, proposedFlags });

expect(result).to.deep.equal(proposedFlags);
});

it('should not throw if allow empty flag objects passed', () => {
const mockFlags = {
trust_azure_adfs_email_verified_connection_property: true,
'some-flag-1': false, // Doesn't currently exist
'some-flag-2': true, // Doesn't currently exist
};

expect(() =>
tenant.sanitizeMigrationFlags({ existingFlags: {}, proposedFlags: {} })
).to.not.throw();
expect(() =>
tenant.sanitizeMigrationFlags({ existingFlags: mockFlags, proposedFlags: {} })
).to.not.throw();
expect(() =>
tenant.sanitizeMigrationFlags({ existingFlags: {}, proposedFlags: mockFlags })
).to.not.throw();
});
});
});

0 comments on commit 57944fc

Please sign in to comment.