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 #76 from zxan1285/hooks-secrets
Browse files Browse the repository at this point in the history
Hooks secrets handling
  • Loading branch information
lzychowski authored Dec 16, 2019
2 parents 64ee34e + 58cfda2 commit 09f0c6d
Show file tree
Hide file tree
Showing 5 changed files with 246 additions and 207 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.0.0-dev.4",
"version": "4.0.0-dev.5",
"description": "Supporting tools for the Source Control extensions",
"main": "lib/index.js",
"scripts": {
Expand Down
113 changes: 79 additions & 34 deletions src/auth0/handlers/hooks.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import DefaultHandler from './default';
import constants from '../../constants';

const ALLOWED_TRIGGER_IDS = [ 'credentials-exchange', 'pre-user-registration', 'post-user-registration' ];

Expand All @@ -13,7 +14,7 @@ export const schema = {
type: 'object',
default: [],
properties: {
code: {
script: {
type: 'string',
description: 'A script that contains the hook\'s code',
default: ''
Expand All @@ -23,7 +24,7 @@ export const schema = {
description: 'The name of the hook. Can only contain alphanumeric characters, spaces and \'-\'. Can neither start nor end with \'-\' or spaces',
pattern: '^[^-\\s][a-zA-Z0-9-\\s]+[^-\\s]$'
},
active: {
enabled: {
type: 'boolean',
description: 'true if the hook is active, false otherwise',
default: false
Expand All @@ -44,10 +45,22 @@ export const schema = {
description: 'List of key-value pairs of NPM dependencies available to the hook.'
}
},
required: [ 'code', 'name', 'triggerId' ]
required: [ 'script', 'name', 'triggerId' ]
}
};

const getCertainHook = (hooks, name, triggerId) => {
let result = null;

hooks.forEach((hook) => {
if (hook.name === name && hook.triggerId === triggerId) {
result = hook;
}
});

return result;
};

const getActive = (hooks) => {
const result = {};

Expand All @@ -71,8 +84,58 @@ export default class HooksHandler extends DefaultHandler {
return super.objString({ name: hook.name, triggerId: hook.triggerId });
}

async getType() {
if (this.existing) {
async processSecrets(hooks) {
const allHooks = await this.getType(true);
const changes = {
create: [],
update: [],
del: []
};

hooks.forEach((hook) => {
const current = getCertainHook(allHooks, hook.name, hook.triggerId);
if (current) { // if the hook was deleted we don't care about its secrets
const oldSecrets = current.secrets || {};
const newSecrets = hook.secrets || {};
const create = {};
const update = {};
const del = [];

Object.keys(newSecrets).forEach((key) => {
if (!oldSecrets[key]) {
create[key] = newSecrets[key];
} else if (newSecrets[key] !== constants.HOOKS_HIDDEN_SECRET_VALUE) {
update[key] = newSecrets[key];
}
});

Object.keys(oldSecrets).forEach((key) => {
if (!newSecrets[key]) {
del.push(key);
}
});

if (Object.keys(create).length) changes.create.push({ hookId: current.id, secrets: create });
if (Object.keys(update).length) changes.update.push({ hookId: current.id, secrets: update });
if (del.length) changes.del.push({ hookId: current.id, secrets: del });
}
});

await Promise.all(changes.del.map(async (data) => {
await this.client.hooks.removeSecrets({ id: data.hookId }, data.secrets);
}));

await Promise.all(changes.update.map(async (data) => {
await this.client.hooks.updateSecrets({ id: data.hookId }, data.secrets);
}));

await Promise.all(changes.create.map(async (data) => {
await this.client.hooks.addSecrets({ id: data.hookId }, data.secrets);
}));
}

async getType(reload) {
if (this.existing && !reload) {
return this.existing;
}

Expand All @@ -84,7 +147,9 @@ export default class HooksHandler extends DefaultHandler {
const hooks = await this.client.hooks.getAll();

// hooks.getAll does not return code and secrets, we have to fetch hooks one-by-one
this.existing = await Promise.all(hooks.map(hook => this.client.hooks.get({ id: hook.id })));
this.existing = await Promise.all(hooks.map(hook => this.client.hooks.get({ id: hook.id })
.then(hookWithCode => this.client.hooks.getSecrets({ id: hook.id })
.then(secrets => ({ ...hookWithCode, secrets })))));

return this.existing;
}
Expand All @@ -94,36 +159,14 @@ export default class HooksHandler extends DefaultHandler {
del, update, create, conflicts
} = await super.calcChanges(assets);

// If ALLOW_DELETE is set to TRUE, the app will remove all hooks that are not in the assets
if (this.config('AUTH0_ALLOW_DELETE') === 'true' || this.config('AUTH0_ALLOW_DELETE') === true) {
return {
del,
update,
create,
conflicts
};
}

// Otherwise we have to make sure that existing hooks will be deactivated
const active = getActive([ ...create, ...update, ...conflicts ]);
const filtered = del.filter((hook) => {
const activeOfType = active[hook.triggerId] && active[hook.triggerId][0] && active[hook.triggerId][0].name;

// deactivating the existing hook only if we have another active hook in the same category
if (hook.active && activeOfType && hook.name !== activeOfType) {
hook.active = false;
update.push(hook);
return false;
}

return true;
});
// strip secrets before hooks creating/updating, secrets have to be handled separately
const stripSecrets = list => list.map(item => ({ ...item, secrets: undefined }));

return {
del: filtered,
update,
create,
conflicts
del,
update: stripSecrets(update),
create: stripSecrets(create),
conflicts: stripSecrets(conflicts)
};
}

Expand Down Expand Up @@ -161,5 +204,7 @@ export default class HooksHandler extends DefaultHandler {
update: changes.update,
conflicts: changes.conflicts
});

await this.processSecrets(hooks);
}
}
1 change: 1 addition & 0 deletions src/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ constants.RULES_STAGES = [
];

constants.DEFAULT_RULE_STAGE = constants.RULES_STAGES[0]; // eslint-disable-line
constants.HOOKS_HIDDEN_SECRET_VALUE = '_VALUE_NOT_SHOWN_';

constants.HOOKS_DIRECTORY = 'hooks';

Expand Down
Loading

0 comments on commit 09f0c6d

Please sign in to comment.