src/auth0/handlers/roles.js
import DefaultHandler, { order } from './default';
import { calcChanges } from '../../utils';
import log from '../../logger';
export const schema = {
type: 'array',
items: {
type: 'object',
properties: {
name: { type: 'string' },
id: { type: 'string' },
description: { type: 'string' },
permissions: {
type: 'array',
items: {
type: 'object',
properties: {
permission_name: { type: 'string' },
resource_server_identifier: { type: 'string' }
}
}
}
},
required: [ 'name' ]
}
};
export default class RoleHandler extends DefaultHandler {
constructor(config) {
super({
...config,
type: 'roles',
id: 'id',
identifiers: [ 'name' ]
});
}
async createRole(data) {
const role = { ...data };
delete role.permissions;
const created = await this.client.roles.create(role);
if (typeof data.permissions !== 'undefined' && data.permissions.length > 0) {
await this.client.roles.permissions.create({ id: created.id }, { permissions: data.permissions });
}
return created;
}
async createRoles(creates) {
await this.client.pool.addEachTask({
data: creates || [],
generator: (item) => this.createRole(item).then((data) => {
this.didCreate(data);
this.created += 1;
}).catch((err) => {
throw new Error(`Problem creating ${this.type} ${this.objString(item)}\n${err}`);
})
}).promise();
}
async deleteRole(data) {
await this.client.roles.delete({ id: data.id });
}
async deleteRoles(dels) {
if (this.config('AUTH0_ALLOW_DELETE') === 'true' || this.config('AUTH0_ALLOW_DELETE') === true) {
await this.client.pool.addEachTask({
data: dels || [],
generator: (item) => this.deleteRole(item).then(() => {
this.didDelete(item);
this.deleted += 1;
}).catch((err) => {
throw new Error(`Problem deleting ${this.type} ${this.objString(item)}\n${err}`);
})
}).promise();
} else {
log.warn(`Detected the following roles should be deleted. Doing so may be destructive.\nYou can enable deletes by setting 'AUTH0_ALLOW_DELETE' to true in the config
\n${dels.map((i) => this.objString(i)).join('\n')}`);
}
}
async updateRole(data, roles) {
const existingRole = await roles.find((roleDataForUpdate) => roleDataForUpdate.name === data.name);
const params = { id: data.id };
const newPermissions = data.permissions;
delete data.permissions;
delete data.id;
await this.client.roles.update(params, data);
if (typeof existingRole.permissions !== 'undefined' && existingRole.permissions.length > 0) {
await this.client.roles.permissions.delete(params, { permissions: existingRole.permissions });
}
if (typeof newPermissions !== 'undefined' && newPermissions.length > 0) {
await this.client.roles.permissions.create(params, { permissions: newPermissions });
}
return params;
}
async updateRoles(updates, roles) {
await this.client.pool.addEachTask({
data: updates || [],
generator: (item) => this.updateRole(item, roles).then((data) => {
this.didUpdate(data);
this.updated += 1;
}).catch((err) => {
throw new Error(`Problem updating ${this.type} ${this.objString(item)}\n${err}`);
})
}).promise();
}
async getType() {
if (this.existing) {
return this.existing;
}
// in case client version does not support roles
if (!this.client.roles || typeof this.client.roles.getAll !== 'function') {
return [];
}
try {
const roles = await this.client.roles.getAll({ paginate: true });
for (let index = 0; index < roles.length; index++) {
const permissions = await this.client.roles.permissions.getAll({ paginate: true, id: roles[index].id });
const strippedPerms = await Promise.all(permissions.map(async (permission) => {
delete permission.resource_server_name;
delete permission.description;
return permission;
}));
roles[index].permissions = strippedPerms;
}
this.existing = roles;
return this.existing;
} catch (err) {
if (err.statusCode === 404 || err.statusCode === 501) {
return [];
}
throw err;
}
}
@order('60')
async processChanges(assets) {
const { roles } = assets;
// Do nothing if not set
if (!roles) return;
// Gets roles from destination tenant
const existing = await this.getType();
const changes = calcChanges(roles, existing, [ 'id', 'name' ]);
log.debug(`Start processChanges for roles [delete:${changes.del.length}] [update:${changes.update.length}], [create:${changes.create.length}]`);
const myChanges = [ { del: changes.del }, { create: changes.create }, { update: changes.update } ];
await Promise.all(myChanges.map(async (change) => {
switch (true) {
case change.del && change.del.length > 0:
await this.deleteRoles(change.del);
break;
case change.create && change.create.length > 0:
await this.createRoles(changes.create);
break;
case change.update && change.update.length > 0:
await this.updateRoles(change.update, existing);
break;
default:
break;
}
}));
}
}