summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian Hofmaier2019-02-25 04:25:08 +0100
committerChristian Hofmaier2019-02-25 04:25:08 +0100
commit7b098c8b969a1d283a94391d9d8050ad0c5a4d13 (patch)
tree82d9f6edbc4503d3419a41626a986c4797478c8d
parent[webapp/datatable] improved slim mode (diff)
downloadbas-7b098c8b969a1d283a94391d9d8050ad0c5a4d13.tar.gz
bas-7b098c8b969a1d283a94391d9d8050ad0c5a4d13.tar.xz
bas-7b098c8b969a1d283a94391d9d8050ad0c5a4d13.zip
[permissionmanager] rework for blacklist system
- integrate PM into PM itself - wildcard function for hasPermission()
-rw-r--r--server/api/roles.js64
-rw-r--r--server/api/users.js4
-rw-r--r--server/lib/permissions/permissions.json35
-rw-r--r--server/lib/permissions/permissionutil.js192
-rw-r--r--server/migrations/20180726033100-create-role.js3
-rw-r--r--server/migrations/20181008151633-create-registrationhook.js2
-rw-r--r--server/migrations/20181009162133-add-registrationState-client.js2
-rw-r--r--server/migrations/20190221050800-create-role_x_group.js (renamed from server/migrations/20180809013230-create-role_x_group.js)3
-rw-r--r--server/models/iprange.js2
-rw-r--r--server/models/registrationhook.js6
-rw-r--r--server/models/role.js7
-rw-r--r--webapp/src/components/NoPermissionError.vue34
-rw-r--r--webapp/src/components/PermissionModule.vue56
-rw-r--r--webapp/src/components/PermissionModuleEdit.vue129
-rw-r--r--webapp/src/components/PermissionModuleGrantRevoke.vue14
-rw-r--r--webapp/src/components/PermissionModuleRoleList.vue11
-rw-r--r--webapp/src/components/PermissionModuleUserList.vue9
-rw-r--r--webapp/src/store/permissions.js22
18 files changed, 411 insertions, 184 deletions
diff --git a/server/api/roles.js b/server/api/roles.js
index 5e62443..3b86f50 100644
--- a/server/api/roles.js
+++ b/server/api/roles.js
@@ -10,22 +10,24 @@ var router = decorateApp(express.Router())
*
* @return: Returns the information about a role and it's permissions and groups.
*/
-router.get('/:id', (req, res) => {
- db.role.findOne({ where: { id: req.params.id }, include: ['permissions', 'groups'] }).then(role => {
- if (role) res.send(role)
- else res.status(404).end()
- })
+router.getAsync('/:id', async (req, res) => {
+ if (!await req.user.hasPermission('permissions.*')) {
+ res.status(403).end()
+ }
+ var role = await db.role.findOne({ where: { id: req.params.id }, include: ['permissions', 'groups'] })
+ if (role) res.send(role)
+ else res.status(404).end()
})
/*
* @return: Returns a list of all roles in the database.
*/
-router.get('', (req, res) => {
- db.role.findAll({
- attributes: ['id', 'name', 'descr']
- }).then(function (roles) {
- res.status(200).send(roles)
- })
+router.getAsync('', async (req, res) => {
+ if (!await req.user.hasPermission('permissions.*')) {
+ res.status(403).end()
+ }
+ var roles = await db.role.findAll({ attributes: ['id', 'name', 'descr'] })
+ res.status(200).send(roles)
})
/*
@@ -36,33 +38,37 @@ router.get('', (req, res) => {
* groups: <GROUP_IDS>,
* recursiveMode: < RECURSIVE_MODE>
*
- * Creates, updates or deletes a role. If recursiveMode is set to true, the <GROUP_IDS> are saved with childs.
+ * Creates, updates or deletes a role.
*
*/
-router.post(['', '/:id'], (req, res) => {
+router.postAsync(['', '/:id'], async (req, res) => {
+ if (!await req.user.hasPermission('permissions.editrole')) {
+ res.status(403).end()
+ }
// ?delete Delete the roles
if (req.query.delete !== undefined && req.query.delete !== 'false') {
- db.role.destroy({ where: { id: req.body.ids } }).then(function () {
- res.status(200).send('success')
- })
+ await db.role.destroy({ where: { id: req.body.ids } })
+ res.status(200).send('success')
} else {
+ var promises = []
+ var roleDb
if (req.params.id === undefined) {
// Create new role
- db.role.create({ name: req.body.name, descr: req.body.descr, recursiveGroups: req.body.recursiveMode }).then(roleDb => {
- var promises = []
- promises.push(roleDb.addPermissions(req.body.permissions))
- promises.push(roleDb.addGroups(req.body.groups))
- Promise.all(promises).then(() => { res.send({ id: req.body.id }) })
- })
+ roleDb = await db.role.create({ name: req.body.name, descr: req.body.descr })
+ promises.push(roleDb.addPermissions(req.body.permissions))
+ promises.push(roleDb.addGroups(req.body.groups, { through: { blacklist: 0 } }))
+ promises.push(roleDb.addGroups(req.body.blacklist, { through: { blacklist: 1 } }))
+ await Promise.all(promises)
+ res.send({ id: req.body.id })
} else {
// Update existing role
- db.role.findOne({ where: { id: req.body.id } }).then(roleDb => {
- var promises = []
- promises.push(roleDb.update({ name: req.body.name, descr: req.body.descr, recursiveGroups: req.body.recursiveMode }))
- promises.push(roleDb.setPermissions(req.body.permissions))
- promises.push(roleDb.setGroups(req.body.groups))
- Promise.all(promises).then(() => { res.send({ id: req.body.id }) })
- })
+ roleDb = await db.role.findOne({ where: { id: req.body.id } })
+ promises.push(roleDb.update({ name: req.body.name, descr: req.body.descr }))
+ promises.push(roleDb.setPermissions(req.body.permissions))
+ promises.push(roleDb.setGroups(req.body.groups, { through: { blacklist: 0 } }))
+ promises.push(roleDb.addGroups(req.body.blacklist, { through: { blacklist: 1 } }))
+ await Promise.all(promises)
+ res.send({ id: req.body.id })
}
}
})
diff --git a/server/api/users.js b/server/api/users.js
index 1a724ac..7963825 100644
--- a/server/api/users.js
+++ b/server/api/users.js
@@ -36,7 +36,11 @@ router.getAsync('/:id', async (req, res) => {
// ############################################################################
// ########################## POST requests #################################
+// Post request for adding roles to users.
router.postAsync('/:id/roles', async (req, res) => {
+ if (!await req.user.hasPermission('permissions.grantrevoke')) {
+ res.status(403).end()
+ }
const id = req.params.id === 'current' ? req.user.id : req.params.id
const user = await db.user.findOne({ where: { id } })
if (user) {
diff --git a/server/lib/permissions/permissions.json b/server/lib/permissions/permissions.json
index 96c971d..f574367 100644
--- a/server/lib/permissions/permissions.json
+++ b/server/lib/permissions/permissions.json
@@ -1,17 +1,22 @@
[
- {
- "name": "permissions.saverole",
- "descr": "For saving a role",
- "groupdependent": false
- },
- {
- "name": "permissions.editrole",
- "descr": "For editing a role",
- "groupdependent": false
- },
- {
- "name": "permissions.deleterole",
- "descr": "For deleting a role",
- "groupdependent": false
- }
+ {
+ "name": "superadmin",
+ "descr": "Can do anything. Is like superman.",
+ "groupdependent": false
+ },
+ {
+ "name": "permissions.viewrole",
+ "descr": "For viewing the role list.",
+ "groupdependent": false
+ },
+ {
+ "name": "permissions.editrole",
+ "descr": "For save/edit/delete roles.",
+ "groupdependent": false
+ },
+ {
+ "name": "permissions.grantrevoke",
+ "descr": "For grant/revoke roles to/from users.",
+ "groupdependent": false
+ }
] \ No newline at end of file
diff --git a/server/lib/permissions/permissionutil.js b/server/lib/permissions/permissionutil.js
index 709cd29..532af6c 100644
--- a/server/lib/permissions/permissionutil.js
+++ b/server/lib/permissions/permissionutil.js
@@ -14,15 +14,39 @@ function exportFunctions (req, res, next) {
next()
}
-async function hasPermission (userid, permissionName) {
+async function isSuperadmin (userid) {
var user = await db.user.findOne({
- where: { id: userid, '$roles.permissions.name$': permissionName },
+ where: { id: userid, '$roles.permissions.name$': 'superadmin' },
include: [{ as: 'roles', model: db.role, include: ['permissions'] }]
})
return user !== null
}
+// Check if the user has given permission.
+async function hasPermission (userid, permissionName) {
+ var superAdmin = await isSuperadmin(userid)
+ if (superAdmin) return true
+ var permission = permissionName.split('.')
+ // Wildcards
+ var user
+ if (permission[1] === '*') {
+ user = await db.user.findOne({
+ where: { id: userid, '$roles.permissions.name$': { $like: permission[0] + '%' } },
+ include: [{ as: 'roles', model: db.role, include: ['permissions'] }]
+ })
+ } else {
+ user = await db.user.findOne({
+ where: { id: userid, '$roles.permissions.name$': permissionName },
+ include: [{ as: 'roles', model: db.role, include: ['permissions'] }]
+ })
+ }
+ return user !== null
+}
+
+// Get a list of GroupIDs the user has the given permission for. 0 means for all groups.
async function getAllowedGroups (userid, permissionName) {
+ var superAdmin = await isSuperadmin(userid)
+ if (superAdmin) return [0]
var user = await db.user.findOne({
where: { id: userid, '$roles.permissions.name$': permissionName },
include: [{ as: 'roles', model: db.role, include: ['permissions', 'groups'] }]
@@ -35,12 +59,33 @@ async function getAllowedGroups (userid, permissionName) {
else {
var permGrps = []
for (let i = 0; i < user.roles.length; i++) {
- // Add groups of the roles to the result
- permGrps = permGrps.concat(user.roles[i].groups.map(g => g.id))
- if (user.roles[i].recursiveGroups) {
- // The role is flagged recursive, so add child ids to result
- var subChilds = await groupUtil.getAllChildren(user.roles[i].groups)
- permGrps = permGrps.concat(subChilds.subgroups.map(s => s.id))
+ var whitelist = []
+ var blacklist = []
+ // Fill in white- and blacklist
+ for (let j = 0; j < user.roles[i].groups.length; j++) {
+ if (user.roles[i].groups[j].role_x_group.blacklist) {
+ blacklist.push(user.roles[i].groups[j])
+ } else {
+ whitelist.push(user.roles[i].groups[j])
+ }
+ }
+
+ // Get childs of white and blacklist groups
+ var whiteSubChilds = await groupUtil.getAllChildren(whitelist)
+ var blackSubChilds = await groupUtil.getAllChildren(blacklist)
+
+ // Add all whitelist-ids to result.
+ permGrps = permGrps.concat(whitelist.map(x => x.id))
+ permGrps = permGrps.concat(whiteSubChilds.subgroups.map(s => s.id))
+
+ // Filter out blacklist-ids from the result.
+ blacklist = blacklist.map(x => x.id)
+ blacklist = blacklist.concat(blackSubChilds.subgroups.map(x => x.id))
+ for (let k = 0; k < blacklist.length; k++) {
+ var index = permGrps.indexOf(blacklist[k])
+ if (index > -1) {
+ permGrps.splice(index, 1)
+ }
}
}
// Filter result for unique entries
@@ -48,7 +93,10 @@ async function getAllowedGroups (userid, permissionName) {
}
}
+// Check if the user has a given permission for a given group.
async function hasPermissionForGroup (userid, permissionName, groupId) {
+ var superAdmin = await isSuperadmin(userid)
+ if (superAdmin) return true
var user = await db.user.findOne({
where: { id: userid, '$roles.permissions.name$': permissionName },
include: [{ as: 'roles', model: db.role, include: ['permissions', 'groups'] }]
@@ -59,19 +107,37 @@ async function hasPermissionForGroup (userid, permissionName, groupId) {
else if (!user.roles[0].permissions[0].groupdependent) return true
// User has permission, permission is groupdependent, check for group
else {
- if (user.roles.map(r => r.groups.map(g => g.id)).includes(groupId)) return true
- var permGrps = []
+ var result = false
+ var whitelist = []
+ var blacklist = []
+ // Fill in white- and blacklist
for (let i = 0; i < user.roles.length; i++) {
- if (user.roles[i].recursiveGroups) permGrps = permGrps.concat(user.roles[i].groups.map(g => g.id))
+ for (let j = 0; j < user.roles[i].groups.length; j++) {
+ if (user.roles[i].groups[j].role_x_group.blacklist) {
+ blacklist.push(user.roles[i].groups[j].id)
+ } else {
+ whitelist.push(user.roles[i].groups[j].id)
+ }
+ }
+ }
+ // Shortcut
+ if (blacklist.includes(groupId)) {
+ return false
+ }
+ // Remember it was found, check if any parent is in the blacklist tho.
+ if (whitelist.includes(groupId)) {
+ result = true
}
- permGrps = permGrps.filter(function (elem, pos, arr) { return arr.indexOf(elem) === pos })
- // get all parents of groupId and check if any parentid is in the list of groups of RECURSIVE flagged roles.
- var result = await checkParentsForIds(groupId, permGrps)
+ // Check parents for white-/blacklist entries.
+ result = await checkParents(groupId, whitelist, blacklist, result)
return result
}
}
+// Get a list of ClientIDs the user has the given permission for. 0 means for all clients.
async function getAllowedClients (userid, permissionName) {
+ var superAdmin = await isSuperadmin(userid)
+ if (superAdmin) return [0]
var user = await db.user.findOne({
where: { id: userid, '$roles.permissions.name$': permissionName },
include: [{ as: 'roles', model: db.role, include: ['permissions', { as: 'groups', model: db.group, include: ['clients'] }] }]
@@ -84,13 +150,30 @@ async function getAllowedClients (userid, permissionName) {
else {
var permClients = []
for (let i = 0; i < user.roles.length; i++) {
- if (user.roles[i].recursiveGroups) {
- // The role is flagged recursive, so add clients of childs to result
- var subChilds = await groupUtil.getAllChildren(user.roles[i].groups)
- permClients = permClients.concat(subChilds.clients.map(c => c.id))
- } else {
- for (let j = 0; j < user.roles[i].groups.length; j++) {
- permClients = permClients.concat(user.roles[i].groups[j].clients.map(c => c.id))
+ var whitelist = []
+ var blacklist = []
+ // Fill in white- and blacklist
+ for (let j = 0; j < user.roles[i].groups.length; j++) {
+ if (user.roles[i].groups[j].role_x_group.blacklist) {
+ blacklist.push(user.roles[i].groups[j])
+ } else {
+ whitelist.push(user.roles[i].groups[j])
+ }
+ }
+
+ // Get childs of white and blacklist groups
+ var whiteSubChilds = await groupUtil.getAllChildren(whitelist)
+ var blackSubChilds = await groupUtil.getAllChildren(blacklist)
+
+ // Add all whitelist-ids to result.
+ permClients = permClients.concat(whiteSubChilds.clients.map(s => s.id))
+
+ // Filter out blacklist-ids from the result.
+ blackSubChilds = blackSubChilds.clients.map(x => x.id)
+ for (let k = 0; k < blackSubChilds.length; k++) {
+ var index = permClients.indexOf(blackSubChilds[k])
+ if (index > -1) {
+ permClients.splice(index, 1)
}
}
}
@@ -99,49 +182,72 @@ async function getAllowedClients (userid, permissionName) {
}
}
+// Check if the user has a given permission for a given client.
async function hasPermissionForClient (userid, permissionName, clientId) {
+ var superAdmin = await isSuperadmin(userid)
+ if (superAdmin) return true
var user = await db.user.findOne({
where: { id: userid, '$roles.permissions.name$': permissionName },
include: [{ as: 'roles', model: db.role, include: ['permissions', { as: 'groups', model: db.group, include: ['clients'] }] }]
})
+ // User doesn't have permission
if (user === null) return false
+ // User has permission, permission is not groupdependent
else if (!user.roles[0].permissions[0].groupdependent) return true
+ // User has permission, permission is groupdependent, check for client
else {
- var permGrps = []
+ var result = false
+ var whitelist = []
+ var blacklist = []
+ // Fill in white- and blacklist
for (let i = 0; i < user.roles.length; i++) {
for (let j = 0; j < user.roles[i].groups.length; j++) {
- var groupClients = user.roles[i].groups[j].clients.map(c => c.id)
- if (groupClients.includes(clientId)) return true
+ var clients = user.roles[i].groups[j].clients.map(c => c.id)
+ if (user.roles[i].groups[j].role_x_group.blacklist) {
+ // Shortcut
+ if (clients.includes(clientId)) return false
+ blacklist.push(user.roles[i].groups[j].id)
+ } else {
+ // Remember it was found, check if any parent is in the blacklist tho.
+ if (clients.includes(clientId)) result = true
+ whitelist.push(user.roles[i].groups[j].id)
+ }
}
- if (user.roles[i].recursiveGroups) permGrps = permGrps.concat(user.roles[i].groups.map(g => g.id))
}
- permGrps = permGrps.filter(function (elem, pos, arr) { return arr.indexOf(elem) === pos })
+ // Get groups the client is assigned to
var client = await db.client.findOne({
where: { id: clientId },
include: [{ as: 'groups', model: db.group }]
})
var groupIds = client.groups.map(g => g.id)
- var result = await checkParentsForIds(groupIds, permGrps)
+ // Check parents for white-/blacklist entries.
+ result = await checkParents(groupIds, whitelist, blacklist, result)
return result
}
}
-async function checkParentsForIds (groupIds, listOfIds) {
- if (listOfIds.length === 0) return false
- if (groupIds.length === 0) return false
+// Check if parents of groupIds are in the whitelist / blacklist
+async function checkParents (groupIds, whitelist, blacklist, result) {
+ // No whitelist means the group cant be in one
+ if (whitelist.length === 0) return false
+ // No blacklist means the result can't be changed once it's true
+ if (blacklist.length === 0 && result) return true
var parentIds = []
- return db.group.findAll({ where: { id: groupIds }, include: ['parents'] }).then(groups => {
- for (let i = 0; i < groups.length; i++) {
- for (let j = 0; j < groups[i].parents.length; j++) {
- var id = groups[i].parents[j].id
- if (listOfIds.includes(id)) return true
- if (!parentIds.includes(id)) parentIds.push(id)
- }
+ var groups = await db.group.findAll({ where: { id: groupIds }, include: ['parents'] })
+ for (let i = 0; i < groups.length; i++) {
+ for (let j = 0; j < groups[i].parents.length; j++) {
+ var id = groups[i].parents[j].id
+ // blacklisted
+ if (blacklist.includes(id)) return false
+ // Remember, but a further parent can still be blacklisted, so continue.
+ if (whitelist.includes(id)) result = true
+ if (!parentIds.includes(id)) parentIds.push(id)
}
- if (parentIds.length === 0) return false
- return checkParentsForIds(parentIds, listOfIds).then(response => {
- return response
- })
- })
-} \ No newline at end of file
+ }
+ // No further parents found, result is the result.
+ if (parentIds.length === 0) return result
+ // Check next layer of parents
+ result = await checkParents(parentIds, whitelist, blacklist, result)
+ return result
+}
diff --git a/server/migrations/20180726033100-create-role.js b/server/migrations/20180726033100-create-role.js
index 20736a2..f3c0e3c 100644
--- a/server/migrations/20180726033100-create-role.js
+++ b/server/migrations/20180726033100-create-role.js
@@ -13,9 +13,6 @@ module.exports = {
},
descr: {
type: Sequelize.STRING(2048)
- },
- recursiveGroups: {
- type: Sequelize.BOOLEAN
}
})
},
diff --git a/server/migrations/20181008151633-create-registrationhook.js b/server/migrations/20181008151633-create-registrationhook.js
index 5914188..0a96f29 100644
--- a/server/migrations/20181008151633-create-registrationhook.js
+++ b/server/migrations/20181008151633-create-registrationhook.js
@@ -6,7 +6,7 @@ module.exports = {
primaryKey: true,
autoIncrement: true,
allowNull: false,
- type: Sequelize.INTEGER,
+ type: Sequelize.INTEGER
},
name: {
type: Sequelize.STRING
diff --git a/server/migrations/20181009162133-add-registrationState-client.js b/server/migrations/20181009162133-add-registrationState-client.js
index 59e90cd..edd3b17 100644
--- a/server/migrations/20181009162133-add-registrationState-client.js
+++ b/server/migrations/20181009162133-add-registrationState-client.js
@@ -13,4 +13,4 @@ module.exports = {
down: (queryInterface, Sequelize) => {
return queryInterface.removeColumn('clients', 'registrationState')
}
-} \ No newline at end of file
+}
diff --git a/server/migrations/20180809013230-create-role_x_group.js b/server/migrations/20190221050800-create-role_x_group.js
index a6dd792..e457f87 100644
--- a/server/migrations/20180809013230-create-role_x_group.js
+++ b/server/migrations/20190221050800-create-role_x_group.js
@@ -21,6 +21,9 @@ module.exports = {
model: 'groups',
key: 'id'
}
+ },
+ blacklist: {
+ type: Sequelize.BOOLEAN
}
})
},
diff --git a/server/models/iprange.js b/server/models/iprange.js
index c0975e4..0792a15 100644
--- a/server/models/iprange.js
+++ b/server/models/iprange.js
@@ -16,4 +16,4 @@ module.exports = (sequelize, DataTypes) => {
iprange.belongsTo(models.group)
}
return iprange
-} \ No newline at end of file
+}
diff --git a/server/models/registrationhook.js b/server/models/registrationhook.js
index 0d1ea6e..25fc0f0 100644
--- a/server/models/registrationhook.js
+++ b/server/models/registrationhook.js
@@ -14,9 +14,9 @@ module.exports = (sequelize, DataTypes) => {
script: {
allowNull: true,
type: DataTypes.BLOB,
- get() {
+ get () {
var blob = this.getDataValue('script')
- return blob ? blob.toString() : '';
+ return blob ? blob.toString() : ''
}
}
}, {
@@ -27,4 +27,4 @@ module.exports = (sequelize, DataTypes) => {
registrationhook.belongsToMany(models.group, { as: 'groups', through: RegistrationhookXGroup, foreignKey: 'registrationhookId', otherKey: 'groupId' })
}
return registrationhook
-} \ No newline at end of file
+}
diff --git a/server/models/role.js b/server/models/role.js
index f88c132..8588ab5 100644
--- a/server/models/role.js
+++ b/server/models/role.js
@@ -8,8 +8,7 @@ module.exports = (sequelize, DataTypes) => {
type: DataTypes.INTEGER
},
name: DataTypes.STRING,
- descr: DataTypes.STRING(2048),
- recursiveGroups: DataTypes.BOOLEAN
+ descr: DataTypes.STRING(2048)
}, {
timestamps: false
})
@@ -17,7 +16,9 @@ module.exports = (sequelize, DataTypes) => {
var RoleXPermission = sequelize.define('role_x_permission', {}, { timestamps: false, freezeTableName: true })
role.belongsToMany(models.permission, { as: 'permissions', through: RoleXPermission, foreignKey: 'roleId', otherKey: 'permissionId' })
- var RoleXGroup = sequelize.define('role_x_group', {}, { timestamps: false, freezeTableName: true })
+ var RoleXGroup = sequelize.define('role_x_group', {
+ blacklist: DataTypes.BOOLEAN
+ }, { timestamps: false, freezeTableName: true })
role.belongsToMany(models.group, { as: 'groups', through: RoleXGroup, foreignKey: 'roleId', otherKey: 'groupId' })
}
diff --git a/webapp/src/components/NoPermissionError.vue b/webapp/src/components/NoPermissionError.vue
new file mode 100644
index 0000000..031ab78
--- /dev/null
+++ b/webapp/src/components/NoPermissionError.vue
@@ -0,0 +1,34 @@
+<i18n>
+{
+ "en": {
+ "noPermission": "You don't have permission for this."
+ },
+ "de": {
+ "noPermission": "Sie haben keine Berechtigung hierfür."
+ }
+}
+</i18n>
+
+<template>
+ <div>
+ {{ $t('noPermission') }}
+ </div>
+</template>
+
+<script>
+
+export default {
+ name: 'NoPermissionError',
+ data () {
+ return {
+ }
+ },
+ methods: {
+ }
+}
+</script>
+
+<!-- Add "scoped" attribute to limit CSS to this component only -->
+<style scoped>
+
+</style>
diff --git a/webapp/src/components/PermissionModule.vue b/webapp/src/components/PermissionModule.vue
index 26d4792..b80dbaa 100644
--- a/webapp/src/components/PermissionModule.vue
+++ b/webapp/src/components/PermissionModule.vue
@@ -21,25 +21,28 @@
<v-container fill-height>
<v-layout>
<v-flex class="tabs-wrapper" xl10 offset-xl1 lg12>
- <v-card>
- <v-tabs :dark="tabsDark" :color="tabsColor" :slider-color="tabsSliderColor"
- centered
- v-model="tab"
- >
- <v-tab>{{ $t('roles') }}</v-tab>
- <v-tab>{{ $t('users') }}</v-tab>
- </v-tabs>
- </v-card>
- <v-tabs-items v-model="tab">
- <v-tab-item>
- <v-subheader>{{ $t('roles') }}</v-subheader>
- <permission-module-role-list/>
- </v-tab-item>
- <v-tab-item>
- <v-subheader>{{ $t('users') }}</v-subheader>
- <permission-module-user-list/>
- </v-tab-item>
- </v-tabs-items>
+ <v-card>
+ <v-tabs :dark="tabsDark" :color="tabsColor" :slider-color="tabsSliderColor"
+ centered
+ v-model="tab"
+ >
+ <v-tab>{{ $t('roles') }}</v-tab>
+ <v-tab>{{ $t('users') }}</v-tab>
+ </v-tabs>
+ </v-card>
+ <v-tabs-items v-model="tab" v-if="canView">
+ <v-tab-item>
+ <v-subheader>{{ $t('roles') }}</v-subheader>
+ <permission-module-role-list/>
+ </v-tab-item>
+ <v-tab-item>
+ <v-subheader>{{ $t('users') }}</v-subheader>
+ <permission-module-user-list/>
+ </v-tab-item>
+ </v-tabs-items>
+ <v-tabs-items v-model="tab" v-else>
+ <no-permission-error/>
+ </v-tabs-items>
</v-flex>
</v-layout>
@@ -82,6 +85,7 @@ import PermissionModuleRoleList from '@/components/PermissionModuleRoleList'
import PermissionModuleUserList from '@/components/PermissionModuleUserList'
import PermissionModuleGrantRevoke from '@/components/PermissionModuleGrantRevoke'
import PermissionModuleEdit from '@/components/PermissionModuleEdit'
+import NoPermissionError from '@/components/NoPermissionError'
import { mapState, mapGetters } from 'vuex'
export default {
@@ -90,12 +94,14 @@ export default {
PermissionModuleRoleList,
PermissionModuleUserList,
PermissionModuleGrantRevoke,
- PermissionModuleEdit
+ PermissionModuleEdit,
+ NoPermissionError
},
data () {
return {
- components: ['PermissionModuleRoleList', 'PermissionModuleUserList'],
- tab: 0
+ components: ['PermissionModuleRoleList', 'PermissionModuleUserList', 'NoPermissionError'],
+ tab: 0,
+ canView: false
}
},
computed: {
@@ -103,6 +109,12 @@ export default {
...mapState('permissions', ['selectedRoles', 'selectedUsers'])
},
methods: {
+ },
+ created () {
+ this.$http.get('/api/permissions/permissions.*').then(response => {
+ this.canView = response.data
+ })
+ this.$store.dispatch('permissions/loadData')
}
}
</script>
diff --git a/webapp/src/components/PermissionModuleEdit.vue b/webapp/src/components/PermissionModuleEdit.vue
index fc66e62..4f78c14 100644
--- a/webapp/src/components/PermissionModuleEdit.vue
+++ b/webapp/src/components/PermissionModuleEdit.vue
@@ -1,13 +1,13 @@
<i18n>
{
"en": {
+ "blacklist": "Blacklist",
"description": "Description",
"groupdependent": "Groupdependent",
"groups": "Groups",
"id": "ID",
"name": "Name",
"permissions": "Permissions",
- "recursiveMode": "Select Groups recursive",
"role": "Role",
"roleName": "Role Name",
"roleNameEmptyError": "Role Name can't be empty.",
@@ -17,13 +17,13 @@
"summary": "Summary"
},
"de": {
+ "blacklist": "Blacklist",
"description": "Beschreibung",
"groupdependent": "Gruppengebunden",
"groups": "Gruppen",
"id": "ID",
"name": "Name",
"permissions": "Rechte",
- "recursiveMode": "Gruppen rekursiv auswählen",
"role": "Rolle",
"roleName": "Rollenname",
"roleNameEmptyError": "Rollenname kann nicht leer sein.",
@@ -80,6 +80,15 @@
:editable="stepCompleted >= 3"
edit-icon="check"
>
+ {{ $t('blacklist') }}
+ </v-stepper-step>
+ <v-divider></v-divider>
+ <v-stepper-step
+ :complete="stepCompleted >= 5"
+ step="5"
+ :editable="stepCompleted >= 4"
+ edit-icon="check"
+ >
{{ $t('summary') }}
</v-stepper-step>
</v-stepper-header>
@@ -117,17 +126,14 @@
</v-stepper-content>
<v-stepper-content step="3" class="stepper-padding-0">
- <data-table v-model="groupsSelected" :headers="groupHeaders" :items="groups">
- <div slot="actions" slot-scope="row" style="text-align: right">
- <v-tooltip top :key="row.item" open-delay="800">
- <v-btn slot="activator" :loading="row.item.loadingSubgroups" icon @click.stop @mousedown="loadChilds(row.item)" style="margin: 0"><v-icon>device_hub</v-icon></v-btn>
- <span>{{ $t('selectChildGroups') }}</span>
- </v-tooltip>
- </div>
- </data-table>
+ <data-table v-model="whitelist" :headers="groupHeaders" :items="groups" logging/>
+ </v-stepper-content>
+
+ <v-stepper-content step="4" class="stepper-padding-0">
+ <data-table v-model="blacklist" :headers="groupHeaders" :items="blacklistGroups"/>
</v-stepper-content>
- <v-stepper-content step="4">
+ <v-stepper-content step="5">
<v-text-field
v-model="roleName"
:label="$t('roleName')"
@@ -171,7 +177,23 @@
<v-subheader inset>{{ $t('groups') }}</v-subheader>
<v-divider class="list-header-margin"></v-divider>
<RecycleScroller
- :items="groupsSelected"
+ :items="whitelist"
+ :item-height="48"
+ page-mode
+ >
+ <div slot-scope="{ item }" class="list-item">
+ <div class="list-item-header">{{ item.id }} {{ item.name }}</div>
+ <div class="list-item-subheader">{{ item.description }}</div>
+ </div>
+ </RecycleScroller>
+ </div>
+ </v-flex>
+ <v-flex xs12 sm6>
+ <div>
+ <v-subheader inset>{{ $t('blacklist') }}</v-subheader>
+ <v-divider class="list-header-margin"></v-divider>
+ <RecycleScroller
+ :items="blacklist"
:item-height="48"
page-mode
>
@@ -196,7 +218,8 @@
<v-btn color="primary" v-show="step == 1" @click.native="completeStepOne()">{{ $t('continue') }}</v-btn>
<v-btn color="primary" v-show="step == 2" @click.native="completeStepTwo()">{{ $t('continue') }}</v-btn>
<v-btn color="primary" v-show="step == 3" @click.native="completeStepThree()">{{ $t('continue') }}</v-btn>
- <v-btn class="success" v-show="step == 4" @click="submit" type="submit">{{ $t('submit') }}</v-btn>
+ <v-btn color="primary" v-show="step == 4" @click.native="completeStepFour()">{{ $t('continue') }}</v-btn>
+ <v-btn class="success" v-show="step == 5" @click="submit" type="submit">{{ $t('submit') }}</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
@@ -204,6 +227,7 @@
<script>
import DataTable from '@/components/DataTable'
+import { mapState } from 'vuex'
export default {
name: 'PermissionModuleEdit',
@@ -216,12 +240,10 @@ export default {
valid: true,
step: 1,
stepCompleted: 0,
- permissions: [],
- groups: [],
+ blacklistGroups: [],
permissionsSelected: [],
- groupsSelected: [],
- groupsSummary: [],
- recursiveSwitch: false,
+ whitelist: [],
+ blacklist: [],
permissionHeaders: [
{ text: this.$t('name'), key: 'name' },
{ text: this.$t('description'), key: 'descr' },
@@ -230,8 +252,7 @@ export default {
groupHeaders: [
{ text: this.$t('id'), key: 'id' },
{ text: this.$t('name'), key: 'name' },
- { text: this.$t('description'), key: 'description' },
- { key: 'actions', width: '60px' }
+ { text: this.$t('description'), key: 'description' }
],
roleName: '',
roleDescr: ''
@@ -241,16 +262,17 @@ export default {
submit (event) {
if (this.$refs.form.validate()) {
const filteredPermissions = this.permissionsSelected.map(x => x.id)
- const filteredGroups = this.groupsSelected.map(x => x.id)
+ const filteredGroups = this.whitelist.map(x => x.id)
+ const filteredBlacklist = this.blacklist.map(x => x.id)
this.$http.post('/api/roles' + (this.roleId === 0 ? '' : '/' + this.roleId), {
id: this.roleId,
name: this.roleName,
descr: this.roleDescr,
permissions: filteredPermissions,
groups: filteredGroups,
- recursiveMode: this.recursiveSwitch
+ blacklist: filteredBlacklist
}).then(response => {
- this.$store.commit('newSnackbar', this.$t('roleSavedSuccess'))
+ this.$snackbar({ color: 'success', text: this.$t('roleSavedSuccess'), timeout: 15000 })
this.$store.dispatch('permissions/loadRoleData')
this.$store.commit('permissions/setEdit', false)
}).catch(error => {
@@ -258,13 +280,23 @@ export default {
})
}
},
- loadRole (roleId) {
- this.$http('/api/roles/' + this.roleId).then(response => {
- this.roleName = response.data.name
- this.roleDescr = response.data.descr
- this.permissionsSelected = response.data.permissions
- this.groupsSelected = response.data.groups
- this.recursiveSwitch = response.data.recursiveGroups
+ async loadRole (roleId) {
+ const response = await this.$http('/api/roles/' + this.roleId)
+ this.roleName = response.data.name
+ this.roleDescr = response.data.descr
+ this.permissionsSelected = response.data.permissions
+
+ var blacklistPush = []
+ for (let i = 0; i < response.data.groups.length; i++) {
+ if (response.data.groups[i].role_x_group.blacklist) {
+ blacklistPush.push(response.data.groups[i])
+ } else {
+ this.whitelist.push(response.data.groups[i])
+ }
+ }
+
+ this.loadChilds().then(() => {
+ this.blacklist = this.blacklist.concat(blacklistPush)
})
},
completeStepOne () {
@@ -282,27 +314,26 @@ export default {
completeStepThree () {
this.step = 4
this.stepCompleted = Math.max(3, this.stepCompleted)
+ this.loadChilds()
},
- loadGroups () {
- this.$http('/api/groups').then(response => {
- this.groups = response.data
- })
- },
- loadPermissions () {
- this.$http('/api/permissions').then(response => {
- this.permissions = response.data
- })
+ completeStepFour () {
+ this.step = 5
+ this.stepCompleted = Math.max(4, this.stepCompleted)
},
- loadChilds (group) {
- group.loadingSubgroups = true
- this.groupsSelected.push(group)
- this.$http('/api/groups/' + group.id + '?all').then(response => {
- this.groupsSelected = this.groupsSelected.concat(response.data.subgroups)
- group.loadingSubgroups = false
+ async loadChilds () {
+ this.blacklistGroups = []
+ var promises = []
+ this.whitelist.forEach(group => {
+ promises.push(
+ this.$http('/api/groups/' + group.id + '?all').then(response => {
+ this.blacklistGroups = this.blacklistGroups.concat(response.data.subgroups)
+ }))
})
+ await Promise.all(promises)
}
},
computed: {
+ ...mapState('permissions', ['permissions', 'groups']),
edit: function () {
return this.$store.state.permissions.edit
}
@@ -310,17 +341,15 @@ export default {
watch: {
edit: function (value) {
if (value) {
- this.loadGroups()
- this.loadPermissions()
this.$refs.form.resetValidation()
this.permissionsSelected = []
- this.groupsSelected = []
+ this.whitelist = []
+ this.blacklist = []
this.step = 1
if (this.roleId !== 0) {
this.loadRole(this.roleId)
- this.stepCompleted = 4
+ this.stepCompleted = 5
} else {
- this.recursiveSwitch = true
this.roleName = ''
this.roleDescr = ''
this.stepCompleted = 0
diff --git a/webapp/src/components/PermissionModuleGrantRevoke.vue b/webapp/src/components/PermissionModuleGrantRevoke.vue
index 559fdfc..81afece 100644
--- a/webapp/src/components/PermissionModuleGrantRevoke.vue
+++ b/webapp/src/components/PermissionModuleGrantRevoke.vue
@@ -143,11 +143,15 @@ export default {
methods: {
async submit (event) {
const roleIds = this.selectedRoles.map(x => x.id)
- this.selectedUsers.forEach(user => {
- this.$http.post('/api/users/' + user.id + '/roles' + (this.grant ? '' : '/?delete'), { ids: roleIds })
- })
- this.$store.commit('newSnackbar', this.$t(this.grant ? 'roleGrantedSuccess' : 'roleRevokedSuccess'))
- this.$store.dispatch('permissions/loadData')
+ for (let i = 0; i < this.selectedUsers.length; i++) {
+ await this.$http.post('/api/users/' + this.selectedUsers[i].id + '/roles' + (this.grant ? '' : '/?delete'), { ids: roleIds })
+ }
+ if (this.grant) {
+ this.$snackbar({ color: 'success', text: this.$t('roleGrantedSuccess'), timeout: 15000 })
+ } else {
+ this.$snackbar({ color: 'success', text: this.$t('roleRevokedSuccess'), timeout: 15000 })
+ }
+ this.$store.dispatch('permissions/loadUserData')
this.$store.commit('permissions/setGrantRevoke', false)
this.$store.commit('permissions/setSelectedUsers', [])
},
diff --git a/webapp/src/components/PermissionModuleRoleList.vue b/webapp/src/components/PermissionModuleRoleList.vue
index 5478060..15f0525 100644
--- a/webapp/src/components/PermissionModuleRoleList.vue
+++ b/webapp/src/components/PermissionModuleRoleList.vue
@@ -21,12 +21,12 @@
<div>
<v-card>
<data-table :value="selectedRoles" @input="$store.commit('permissions/setSelectedRoles', $event)" :headers="headers" :items="roles">
- <div slot="action" slot-scope="row">
+ <div slot="action" slot-scope="row" v-if="canEdit">
<v-btn flat icon color="primary" @click.stop="$store.commit('permissions/editRole', row.item.id)"><v-icon>edit</v-icon></v-btn>
</div>
</data-table>
</v-card>
- <div class="text-xs-right">
+ <div class="text-xs-right" v-if="canEdit">
<v-btn color="error" flat @click="$store.commit('permissions/setDialog', true )" :disabled="selectedRoles.length === 0">
<v-icon left>remove_circle_outline</v-icon>{{ $tc('delete-role', selectedRoles.length, [selectedRoles.length]) }}
</v-btn>
@@ -53,7 +53,8 @@ export default {
{ text: this.$t('name'), key: 'name' },
{ text: this.$t('description'), key: 'descr' },
{ sortable: false, key: 'action' }
- ]
+ ],
+ canEdit: false
}
},
computed: {
@@ -62,7 +63,9 @@ export default {
methods: {
},
created () {
- this.$store.dispatch('permissions/loadRoleData')
+ this.$http.get('/api/permissions/permissions.editrole').then(response => {
+ this.canEdit = response.data
+ })
}
}
</script>
diff --git a/webapp/src/components/PermissionModuleUserList.vue b/webapp/src/components/PermissionModuleUserList.vue
index 15f003b..609ab37 100644
--- a/webapp/src/components/PermissionModuleUserList.vue
+++ b/webapp/src/components/PermissionModuleUserList.vue
@@ -31,7 +31,7 @@
</div>
</data-table>
</v-card>
- <div class="text-xs-right">
+ <div class="text-xs-right" v-if="canGrant">
<v-btn color="error" flat @click="$store.commit('permissions/grantRevoke', { show: true, grant: false } )" :disabled="selectedUsers.length === 0">
<v-icon left>remove_circle_outline</v-icon>{{ $t('revoke-role') }}
</v-btn>
@@ -57,7 +57,8 @@ export default {
{ text: this.$t('username'), key: 'username' },
{ text: this.$t('name'), key: 'name' },
{ text: this.$t('roles'), key: 'roles' }
- ]
+ ],
+ canGrant: false
}
},
computed: {
@@ -66,7 +67,9 @@ export default {
methods: {
},
created () {
- this.$store.dispatch('permissions/loadUserData')
+ this.$http.get('/api/permissions/permissions.grantrevoke').then(response => {
+ this.canGrant = response.data
+ })
}
}
</script>
diff --git a/webapp/src/store/permissions.js b/webapp/src/store/permissions.js
index b15b0d1..7ec454c 100644
--- a/webapp/src/store/permissions.js
+++ b/webapp/src/store/permissions.js
@@ -5,6 +5,8 @@ export default {
state: {
roles: [],
users: [],
+ groups: [],
+ permissions: [],
selectedRoles: [],
selectedUsers: [],
roleId: '',
@@ -23,6 +25,12 @@ export default {
setUsers (state, value) {
state.users = value
},
+ setGroups (state, value) {
+ state.groups = value
+ },
+ setPermissions (state, value) {
+ state.permissions = value
+ },
setSelectedRoles (state, value) {
state.selectedRoles = value
},
@@ -48,7 +56,7 @@ export default {
deleteSelectedRoles (context) {
// Filter selected array to get a list of ids.
const filteredArray = context.state.selectedRoles.map(x => x.id)
- axios.post('/api/roles?delete', { id: filteredArray }).then(response => {
+ axios.post('/api/roles?delete', { ids: filteredArray }).then(response => {
context.dispatch('loadData')
context.commit('setSelectedRoles', [])
})
@@ -63,9 +71,21 @@ export default {
context.commit('setUsers', response.data)
})
},
+ loadGroupData (context) {
+ axios.get('/api/groups').then(response => {
+ context.commit('setGroups', response.data)
+ })
+ },
+ loadPermissionData (context) {
+ axios.get('/api/permissions').then(response => {
+ context.commit('setPermissions', response.data)
+ })
+ },
loadData (context) {
context.dispatch('loadRoleData')
context.dispatch('loadUserData')
+ context.dispatch('loadGroupData')
+ context.dispatch('loadPermissionData')
}
}
}