Skip to content

Commit

Permalink
Merge branch 'master' into validate-for-unreplaced-keywords
Browse files Browse the repository at this point in the history
  • Loading branch information
willvedd authored Jun 27, 2022
2 parents 7113409 + d3fd080 commit 7992add
Show file tree
Hide file tree
Showing 5 changed files with 240 additions and 9 deletions.
2 changes: 1 addition & 1 deletion src/context/yaml/handlers/pages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ async function dump(context: YAMLContext): Promise<ParsedPages> {
// Dump html to file
const htmlFile = path.join(pagesFolder, `${page.name}.html`);
log.info(`Writing ${htmlFile}`);
fs.writeFileSync(htmlFile, page.html);
fs.writeFileSync(htmlFile, page.html || '');
return {
...page,
html: `./pages/${page.name}.html`,
Expand Down
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
28 changes: 28 additions & 0 deletions test/context/yaml/pages.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,34 @@ describe('#YAML context pages', () => {
);
});

it('should not throw if page HTML is not defined', async () => {
// See: https://github.com/auth0/auth0-deploy-cli/issues/365
const dir = path.join(testDataDir, 'yaml', 'pagesDump');
cleanThenMkdir(dir);
const context = new Context(
{ AUTH0_INPUT_FILE: path.join(dir, 'tennat.yaml') },
mockMgmtClient()
);

context.assets.pages = [
{ html: undefined, name: 'login' }, // HTML property is not defined here
];

const dumped = await handler.dump(context);
expect(dumped).to.deep.equal({
pages: [
{
html: './pages/login.html',
name: 'login',
},
],
});

const pagesFolder = path.join(dir, 'pages');
expect(fs.readFileSync(path.join(pagesFolder, 'login.html'), 'utf8')).to.deep.equal('');
expect(fs.readdirSync(pagesFolder).length).to.equal(1);
});

it('should dump error_page with html undefined', async () => {
const dir = path.join(testDataDir, 'yaml', 'pagesDump');
cleanThenMkdir(dir);
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 7992add

Please sign in to comment.