Move ACLs management to a new mixin.

This commit is contained in:
Julien Fontanet 2016-02-29 12:49:22 +01:00
parent 31e3117190
commit 0574c58f16
2 changed files with 164 additions and 146 deletions

164
src/xo-mixins/acls.js Normal file
View File

@ -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
}
}

146
src/xo.js
View File

@ -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(),