From 0574c58f1607b5a010ddae44a28b74f7229a334e Mon Sep 17 00:00:00 2001 From: Julien Fontanet Date: Mon, 29 Feb 2016 12:49:22 +0100 Subject: [PATCH] Move ACLs management to a new mixin. --- src/xo-mixins/acls.js | 164 ++++++++++++++++++++++++++++++++++++++++++ src/xo.js | 146 ------------------------------------- 2 files changed, 164 insertions(+), 146 deletions(-) create mode 100644 src/xo-mixins/acls.js diff --git a/src/xo-mixins/acls.js b/src/xo-mixins/acls.js new file mode 100644 index 000000000..fa572283d --- /dev/null +++ b/src/xo-mixins/acls.js @@ -0,0 +1,164 @@ +import checkAuthorization from 'xo-acl-resolver' + +import { + ModelAlreadyExists +} from '../collection' +import { + Acls +} from '../models/acl' +import { + createRawObject, + forEach, + includes, + mapToArray +} from '../utils' + +// =================================================================== + +export default class { + constructor (xo) { + this._xo = xo + + this._acls = new Acls({ + connection: xo._redis, + prefix: 'xo:acl', + indexes: ['subject', 'object'] + }) + } + + async _getAclsForUser (userId) { + const subjects = (await this._xo.getUser(userId)).groups.concat(userId) + + const acls = [] + const pushAcls = (function (push) { + return function (entries) { + push.apply(acls, entries) + } + })(acls.push) + + const {_acls: collection} = this + await Promise.all(mapToArray( + subjects, + subject => collection.get({subject}).then(pushAcls) + )) + + return acls + } + + async addAcl (subjectId, objectId, action) { + try { + await this._acls.create(subjectId, objectId, action) + } catch (error) { + if (!(error instanceof ModelAlreadyExists)) { + throw error + } + } + } + + async removeAcl (subjectId, objectId, action) { + await this._acls.delete(subjectId, objectId, action) + } + + // TODO: remove when new collection. + async getAllAcls () { + return this._acls.get() + } + + async getPermissionsForUser (userId) { + const [ + acls, + permissionsByRole + ] = await Promise.all([ + this._getAclsForUser(userId), + this._getPermissionsByRole() + ]) + + const permissions = createRawObject() + for (const { action, object: objectId } of acls) { + const current = ( + permissions[objectId] || + (permissions[objectId] = createRawObject()) + ) + + const permissionsForRole = permissionsByRole[action] + if (permissionsForRole) { + for (const permission of permissionsForRole) { + current[permission] = 1 + } + } else { + current[action] = 1 + } + } + return permissions + } + + async hasPermissions (userId, permissions) { + const user = await this._xo.getUser(userId) + + // Special case for super XO administrators. + if (user.permission === 'admin') { + return true + } + + return checkAuthorization( + await this.getPermissionsForUser(userId), + id => this._xo.getObject(id), + permissions + ) + } + + // ----------------------------------------------------------------- + + async _getPermissionsByRole () { + const roles = await this.getRoles() + + const permissions = createRawObject() + for (const role of roles) { + permissions[role.id] = role.permissions + } + return permissions + } + + // TODO: delete when merged with the new collection. + async getRoles () { + return [ + { + id: 'viewer', + name: 'Viewer', + permissions: [ + 'view' + ] + }, + { + id: 'operator', + name: 'Operator', + permissions: [ + 'view', + 'operate' + ] + }, + { + id: 'admin', + name: 'Admin', + permissions: [ + 'view', + 'operate', + 'administrate' + ] + } + ] + } + + // Returns an array of roles which have a given permission. + async getRolesForPermission (permission) { + const roles = [] + + forEach(await this.getRoles(), role => { + if (includes(role.permissions, permission)) { + roles.push(role.id) + } + }) + + return roles + } +} diff --git a/src/xo.js b/src/xo.js index 0e4d7fd3d..fcd73078d 100644 --- a/src/xo.js +++ b/src/xo.js @@ -1,4 +1,3 @@ -import checkAuthorization from 'xo-acl-resolver' import includes from 'lodash.includes' import isFunction from 'lodash.isfunction' import isString from 'lodash.isstring' @@ -10,7 +9,6 @@ import {EventEmitter} from 'events' import mixins from './xo-mixins' import Connection from './connection' import LevelDbLogger from './loggers/leveldb' -import {Acls} from './models/acl' import { mixin } from './decorators' @@ -25,7 +23,6 @@ import { import { NoSuchObject } from './api-errors' -import {ModelAlreadyExists} from './collection' import Token, {Tokens} from './models/token' // =================================================================== @@ -72,11 +69,6 @@ export default class Xo extends EventEmitter { const redis = this._redis // Creates persistent collections. - this._acls = new Acls({ - connection: redis, - prefix: 'xo:acl', - indexes: ['subject', 'object'] - }) this._tokens = new Tokens({ connection: redis, prefix: 'xo:token', @@ -139,144 +131,6 @@ export default class Xo extends EventEmitter { // ----------------------------------------------------------------- - async _getAclsForUser (userId) { - const subjects = (await this.getUser(userId)).groups.concat(userId) - - const acls = [] - const pushAcls = (function (push) { - return function (entries) { - push.apply(acls, entries) - } - })(acls.push) - - const {_acls: collection} = this - await Promise.all(mapToArray( - subjects, - subject => collection.get({subject}).then(pushAcls) - )) - - return acls - } - - async addAcl (subjectId, objectId, action) { - try { - await this._acls.create(subjectId, objectId, action) - } catch (error) { - if (!(error instanceof ModelAlreadyExists)) { - throw error - } - } - } - - async removeAcl (subjectId, objectId, action) { - await this._acls.delete(subjectId, objectId, action) - } - - // TODO: remove when new collection. - async getAllAcls () { - return this._acls.get() - } - - async getPermissionsForUser (userId) { - const [ - acls, - permissionsByRole - ] = await Promise.all([ - this._getAclsForUser(userId), - this._getPermissionsByRole() - ]) - - const permissions = createRawObject() - for (const { action, object: objectId } of acls) { - const current = ( - permissions[objectId] || - (permissions[objectId] = createRawObject()) - ) - - const permissionsForRole = permissionsByRole[action] - if (permissionsForRole) { - for (const permission of permissionsForRole) { - current[permission] = 1 - } - } else { - current[action] = 1 - } - } - return permissions - } - - async hasPermissions (userId, permissions) { - const user = await this.getUser(userId) - - // Special case for super XO administrators. - if (user.permission === 'admin') { - return true - } - - return checkAuthorization( - await this.getPermissionsForUser(userId), - id => this.getObject(id), - permissions - ) - } - - // ----------------------------------------------------------------- - - async _getPermissionsByRole () { - const roles = await this.getRoles() - - const permissions = createRawObject() - for (const role of roles) { - permissions[role.id] = role.permissions - } - return permissions - } - - // TODO: delete when merged with the new collection. - async getRoles () { - return [ - { - id: 'viewer', - name: 'Viewer', - permissions: [ - 'view' - ] - }, - { - id: 'operator', - name: 'Operator', - permissions: [ - 'view', - 'operate' - ] - }, - { - id: 'admin', - name: 'Admin', - permissions: [ - 'view', - 'operate', - 'administrate' - ] - } - ] - } - - // Returns an array of roles which have a given permission. - async getRolesForPermission (permission) { - const roles = [] - - forEach(await this.getRoles(), role => { - if (includes(role.permissions, permission)) { - roles.push(role.id) - } - }) - - return roles - } - - // ----------------------------------------------------------------- - async createAuthenticationToken ({userId}) { const token = new Token({ id: await generateToken(),