Skip to content
This repository has been archived by the owner on Jul 31, 2021. It is now read-only.

Commit

Permalink
Merge pull request #99 from pmalouin/mfa-351
Browse files Browse the repository at this point in the history
[MFA-351] fix(guardian): support for older installations
  • Loading branch information
Chris Geihsler authored Jul 7, 2020
2 parents 8dbcc8a + c8aafe1 commit 493b13f
Show file tree
Hide file tree
Showing 9 changed files with 261 additions and 41 deletions.
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "auth0-source-control-extension-tools",
"version": "4.1.0",
"version": "4.1.1",
"description": "Supporting tools for the Source Control extensions",
"main": "lib/index.js",
"scripts": {
Expand Down
32 changes: 28 additions & 4 deletions src/auth0/handlers/guardianPhoneFactorMessageTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,24 @@ export const schema = {
}
}
},
required: [ 'message_types' ],
additionalProperties: false
};

const isFeatureUnavailableError = (err) => {
if (err.statusCode === 404) {
// Older Management API version where the endpoint is not available.
return true;
}
if (err.statusCode === 403
&& err.originalError
&& err.originalError.response
&& err.originalError.response.body
&& err.originalError.response.body.errorCode === 'voice_mfa_not_allowed') {
// Recent Management API version, but with feature explicitly disabled.
return true;
}
return false;
};

export default class GuardianPhoneMessageTypesHandler extends DefaultHandler {
constructor(options) {
Expand All @@ -28,11 +42,21 @@ export default class GuardianPhoneMessageTypesHandler extends DefaultHandler {
async getType() {
// in case client version does not support the operation
if (!this.client.guardian || typeof this.client.guardian.getPhoneFactorMessageTypes !== 'function') {
return null;
return {};
}

if (this.existing) return this.existing;
this.existing = await this.client.guardian.getPhoneFactorMessageTypes();

try {
this.existing = await this.client.guardian.getPhoneFactorMessageTypes();
} catch (e) {
if (isFeatureUnavailableError(e)) {
// Gracefully skip processing this configuration value.
return {};
}
throw e;
}

return this.existing;
}

Expand All @@ -41,7 +65,7 @@ export default class GuardianPhoneMessageTypesHandler extends DefaultHandler {
const { guardianPhoneFactorMessageTypes } = assets;

// Do nothing if not set
if (!guardianPhoneFactorMessageTypes) return;
if (!guardianPhoneFactorMessageTypes || !guardianPhoneFactorMessageTypes.message_types) return;

const params = {};
const data = guardianPhoneFactorMessageTypes;
Expand Down
32 changes: 28 additions & 4 deletions src/auth0/handlers/guardianPhoneFactorSelectedProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,24 @@ export const schema = {
enum: constants.GUARDIAN_PHONE_PROVIDERS
}
},
required: [ 'provider' ],
additionalProperties: false
};

const isFeatureUnavailableError = (err) => {
if (err.statusCode === 404) {
// Older Management API version where the endpoint is not available.
return true;
}
if (err.statusCode === 403
&& err.originalError
&& err.originalError.response
&& err.originalError.response.body
&& err.originalError.response.body.errorCode === 'hooks_not_allowed') {
// Recent Management API version, but with feature explicitly disabled.
return true;
}
return false;
};

export default class GuardianPhoneSelectedProviderHandler extends DefaultHandler {
constructor(options) {
Expand All @@ -25,11 +39,21 @@ export default class GuardianPhoneSelectedProviderHandler extends DefaultHandler
async getType() {
// in case client version does not support the operation
if (!this.client.guardian || typeof this.client.guardian.getPhoneFactorSelectedProvider !== 'function') {
return null;
return {};
}

if (this.existing) return this.existing;
this.existing = await this.client.guardian.getPhoneFactorSelectedProvider();

try {
this.existing = await this.client.guardian.getPhoneFactorSelectedProvider();
} catch (e) {
if (isFeatureUnavailableError(e)) {
// Gracefully skip processing this configuration value.
return {};
}
throw e;
}

return this.existing;
}

Expand All @@ -38,7 +62,7 @@ export default class GuardianPhoneSelectedProviderHandler extends DefaultHandler
const { guardianPhoneFactorSelectedProvider } = assets;

// Do nothing if not set
if (!guardianPhoneFactorSelectedProvider) return;
if (!guardianPhoneFactorSelectedProvider || !guardianPhoneFactorSelectedProvider.provider) return;

const params = {};
const data = guardianPhoneFactorSelectedProvider;
Expand Down
26 changes: 15 additions & 11 deletions src/auth0/handlers/guardianPolicies.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,19 @@ import DefaultHandler from './default';
import constants from '../../constants';

export const schema = {
type: 'array',
items: {
type: 'string',
enum: constants.GUARDIAN_POLICIES
type: 'object',
properties: {
policies: {
type: 'array',
items: {
type: 'string',
enum: constants.GUARDIAN_POLICIES
}
}
},
minLength: 0,
maxLength: 1
additionalProperties: false
};


export default class GuardianPoliciesHandler extends DefaultHandler {
constructor(options) {
super({
Expand All @@ -23,11 +26,12 @@ export default class GuardianPoliciesHandler extends DefaultHandler {
async getType() {
// in case client version does not support the operation
if (!this.client.guardian || typeof this.client.guardian.getPolicies !== 'function') {
return null;
return {};
}

if (this.existing) return this.existing;
this.existing = await this.client.guardian.getPolicies();
const policies = await this.client.guardian.getPolicies();
this.existing = { policies };
return this.existing;
}

Expand All @@ -36,10 +40,10 @@ export default class GuardianPoliciesHandler extends DefaultHandler {
const { guardianPolicies } = assets;

// Do nothing if not set
if (!guardianPolicies) return;
if (!guardianPolicies || !guardianPolicies.policies) return;

const params = {};
const data = guardianPolicies;
const data = guardianPolicies.policies;
await this.client.guardian.updatePolicies(params, data);
this.updated += 1;
this.didUpdate(guardianPolicies);
Expand Down
84 changes: 82 additions & 2 deletions tests/auth0/handlers/guardianPhoneFactorMessageTypes.tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,68 @@ describe('#guardianPhoneFactorMessageTypes handler', () => {

const handler = new guardianPhoneFactorMessageTypes.default({ client: auth0 });
const data = await handler.getType();
expect(data).to.deep.equal(null);
expect(data).to.deep.equal({});
});

it('should support when endpoint does not exist (older installations)', async () => {
const auth0 = {
guardian: {
getPhoneFactorMessageTypes: () => {
const err = new Error('Not Found');
err.name = 'Not Found';
err.statusCode = 404;
err.requestInfo = {
method: 'get',
url: 'https://example.auth0.com/api/v2/guardian/factors/phone/message-types'
};
err.originalError = new Error('Not Found');
err.originalError.status = 404;
err.originalError.response = {
body: {
statusCode: 404,
error: 'Not Found',
message: 'Not Found'
}
};
return Promise.reject(err);
}
}
};

const handler = new guardianPhoneFactorMessageTypes.default({ client: auth0 });
const data = await handler.getType();
expect(data).to.deep.equal({});
});

it('should support when endpoint is disabled for tenant', async () => {
const auth0 = {
guardian: {
getPhoneFactorMessageTypes: () => {
const err = new Error('This endpoint is disabled for your tenant.');
err.name = 'Forbidden';
err.statusCode = 403;
err.requestInfo = {
method: 'get',
url: 'https://example.auth0.com/api/v2/guardian/factors/phone/message-types'
};
err.originalError = new Error('Forbidden');
err.originalError.status = 403;
err.originalError.response = {
body: {
statusCode: 403,
error: 'Forbidden',
message: 'This endpoint is disabled for your tenant.',
errorCode: 'voice_mfa_not_allowed'
}
};
return Promise.reject(err);
}
}
};

const handler = new guardianPhoneFactorMessageTypes.default({ client: auth0 });
const data = await handler.getType();
expect(data).to.deep.equal({});
});

it('should get guardian phone factor message types', async () => {
Expand All @@ -26,6 +87,25 @@ describe('#guardianPhoneFactorMessageTypes handler', () => {
const data = await handler.getType();
expect(data).to.deep.equal({ message_types: [ 'sms', 'voice' ] });
});

it('should throw an error for all other failed requests', async () => {
const auth0 = {
guardian: {
getPhoneFactorMessageTypes: () => {
const error = new Error('Bad request');
error.statusCode = 500;
throw error;
}
}
};

const handler = new guardianPhoneFactorMessageTypes.default({ client: auth0 });
try {
await handler.getType();
} catch (error) {
expect(error).to.be.an.instanceOf(Error);
}
});
});

describe('#processChanges', () => {
Expand Down Expand Up @@ -61,7 +141,7 @@ describe('#guardianPhoneFactorMessageTypes handler', () => {
const stageFn = Object.getPrototypeOf(handler).processChanges;

await stageFn.apply(handler, [
{ guardianPhoneFactorMessageTypes: null }
{ guardianPhoneFactorMessageTypes: {} }
]);
});
});
Expand Down
84 changes: 82 additions & 2 deletions tests/auth0/handlers/guardianPhoneFactorSelectedProvider.tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,68 @@ describe('#guardianPhoneFactorSelectedProvider handler', () => {

const handler = new guardianPhoneFactorSelectedProvider.default({ client: auth0 });
const data = await handler.getType();
expect(data).to.deep.equal(null);
expect(data).to.deep.equal({});
});

it('should support when endpoint does not exist (older installations)', async () => {
const auth0 = {
guardian: {
getPhoneFactorSelectedProvider: () => {
const err = new Error('Not Found');
err.name = 'Not Found';
err.statusCode = 404;
err.requestInfo = {
method: 'get',
url: 'https://example.auth0.com/api/v2/guardian/factors/sms/selected-provider'
};
err.originalError = new Error('Not Found');
err.originalError.status = 404;
err.originalError.response = {
body: {
statusCode: 404,
error: 'Not Found',
message: 'Not Found'
}
};
return Promise.reject(err);
}
}
};

const handler = new guardianPhoneFactorSelectedProvider.default({ client: auth0 });
const data = await handler.getType();
expect(data).to.deep.equal({});
});

it('should support when endpoint is disabled for tenant', async () => {
const auth0 = {
guardian: {
getPhoneFactorSelectedProvider: () => {
const err = new Error('This endpoint is disabled for your tenant.');
err.name = 'Forbidden';
err.statusCode = 403;
err.requestInfo = {
method: 'get',
url: 'https://example.auth0.com/api/v2/guardian/factors/sms/selected-provider'
};
err.originalError = new Error('Forbidden');
err.originalError.status = 403;
err.originalError.response = {
body: {
statusCode: 403,
error: 'Forbidden',
message: 'This endpoint is disabled for your tenant.',
errorCode: 'hooks_not_allowed'
}
};
return Promise.reject(err);
}
}
};

const handler = new guardianPhoneFactorSelectedProvider.default({ client: auth0 });
const data = await handler.getType();
expect(data).to.deep.equal({});
});

it('should get guardian phone factor selected provider', async () => {
Expand All @@ -26,6 +87,25 @@ describe('#guardianPhoneFactorSelectedProvider handler', () => {
const data = await handler.getType();
expect(data).to.deep.equal({ provider: 'twilio' });
});

it('should throw an error for all other failed requests', async () => {
const auth0 = {
guardian: {
getPhoneFactorSelectedProvider: () => {
const error = new Error('Bad request');
error.statusCode = 500;
throw error;
}
}
};

const handler = new guardianPhoneFactorSelectedProvider.default({ client: auth0 });
try {
await handler.getType();
} catch (error) {
expect(error).to.be.an.instanceOf(Error);
}
});
});

describe('#processChanges', () => {
Expand Down Expand Up @@ -61,7 +141,7 @@ describe('#guardianPhoneFactorSelectedProvider handler', () => {
const stageFn = Object.getPrototypeOf(handler).processChanges;

await stageFn.apply(handler, [
{ guardianPhoneFactorSelectedProvider: null }
{ guardianPhoneFactorSelectedProvider: {} }
]);
});
});
Expand Down
Loading

0 comments on commit 493b13f

Please sign in to comment.