summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--server/api/permissions.js52
-rw-r--r--server/api/user.js33
-rw-r--r--server/app.js1
-rw-r--r--server/lib/permissions.js38
-rw-r--r--server/migrations/20180726033100-create-role.js22
-rw-r--r--server/migrations/20180726033400-create-user_x_role.js30
-rw-r--r--server/migrations/20180726171200-create-permission.js25
-rw-r--r--server/migrations/20180726173100-create-role_x_permission.js30
-rw-r--r--server/migrations/20180804234000-create-role_x_group.js30
-rw-r--r--server/models/permission.js21
-rw-r--r--server/models/role.js7
-rw-r--r--server/models/user.js4
-rw-r--r--server/models/user_x_role.js17
-rw-r--r--webapp/package-lock.json8
-rw-r--r--webapp/src/components/PermissionModule.vue90
-rw-r--r--webapp/src/components/PermissionModuleEdit.vue273
-rw-r--r--webapp/src/components/PermissionModuleGrantRevoke.vue197
-rw-r--r--webapp/src/components/PermissionModuleRoleList.vue91
-rw-r--r--webapp/src/components/PermissionModuleUserList.vue88
-rw-r--r--webapp/src/store/index.js4
-rw-r--r--webapp/src/store/permissions.js81
21 files changed, 1088 insertions, 54 deletions
diff --git a/server/api/permissions.js b/server/api/permissions.js
index 52cd110..d55e7d9 100644
--- a/server/api/permissions.js
+++ b/server/api/permissions.js
@@ -1,28 +1,42 @@
/* global __appdir */
-var path = require('path')
-var db = require(path.join(__appdir, 'lib', 'sequelize'))
+var path = require('path');
+var db = require(path.join(__appdir, 'lib', 'sequelize'));
module.exports = {
// Return ID, Description and Name of a given RoleID
- getRoleById: function (req, res) {
+ getRoleById: function(req, res) {
var roleid = req.params.roleid
- db.role.findById(roleid).then(robeDb => {
- var role = { }
- role.id = robeDb.id
- role.descr = robeDb.descr
- role.name = robeDb.name
+ db.role.findById(roleid, {
+ attributes: ['id', 'name', 'descr']
+ // include: [{model: db.permission, as: 'permissions', attributes: ['id', 'name', 'descr', 'groupdependent'] }]
+ }).then(role => {
res.status(200).send(role)
})
},
- // Return all RoleIDs associated to a given UserID
- getRolesByUserid: function (req, res) {
- // var userid = req.query.userid;
- // the usersxroles (and rolesxpermissions) models first have to get created
- /* db.usersxroles.findAndCountAll({ where: { id: userid }, attributes: ['roleid'] }).then(roles_db => {
- var result = { };
- result.count = roles_db.count;
- result.roles = roles_db.rows;
- res.status(200).send(result);
- }); */
+
+ getRoleList: function(req, res) {
+ db.role.findAll({
+ attributes: ['id', 'name', 'descr']
+ }).then(function (roles) {
+ res.status(200).send(roles)
+ })
+ },
+
+ getPermissionList: function(req, res) {
+ db.permission.findAll().then(function (permissions) {
+ res.status(200).send(permissions)
+ })
+ },
+
+ deleteRoles: function(req, res) {
+ const roleIds = req.body.id
+
+ db.role.destroy({ where: { id: roleIds } }).then(function() {
+ res.status(200).send('success')
+ })
+ },
+
+ saveRole: function(req, res) {
+
}
-}
+} \ No newline at end of file
diff --git a/server/api/user.js b/server/api/user.js
index 0565d58..9aba1fc 100644
--- a/server/api/user.js
+++ b/server/api/user.js
@@ -26,5 +26,38 @@ module.exports = {
user.name = userDb.name
res.status(200).send(user)
})
+ },
+
+ getUserList: function(req, res) {
+ db.user.findAll({
+ attributes: ['id', 'username', 'name'],
+ include: [{model: db.role, as: 'roles', attributes: ['name'] }]
+ }).then(function (users) {
+ res.status(200).send(users)
+ })
+ },
+
+ grantRoles: function(req, res) {
+ const roleIds = req.body.roleIds
+ const userIds = req.body.userIds
+
+ db.user.findAll({ where: { id: userIds } }).then(users => {
+ users.forEach(user => {
+ user.addRoles(roleIds)
+ })
+ res.status(200).send('success')
+ })
+ },
+
+ revokeRoles: function(req, res) {
+ const roleIds = req.body.roleIds
+ const userIds = req.body.userIds
+
+ db.user.findAll({ where: { id: userIds } }).then(users => {
+ users.forEach(user => {
+ user.removeRoles(roleIds)
+ })
+ res.status(200).send('success')
+ })
}
}
diff --git a/server/app.js b/server/app.js
index 8a18b58..d98c0ce 100644
--- a/server/app.js
+++ b/server/app.js
@@ -11,6 +11,7 @@ var app = express()
global.__appdir = __dirname
var tftp = require('./lib/tftp')
+var permissionsDB = require ('./lib/permissions')
// ############################################################################
// ########################### setup middleware ###############################
diff --git a/server/lib/permissions.js b/server/lib/permissions.js
new file mode 100644
index 0000000..431e051
--- /dev/null
+++ b/server/lib/permissions.js
@@ -0,0 +1,38 @@
+/* global __appdir */
+const path = require('path')
+var permissions = require(path.join(__appdir, 'config', 'permissions'))
+var db = require(path.join(__appdir, 'lib', 'sequelize'))
+
+updatePermissionDatabase()
+
+function updatePermissionDatabase () {
+ var permissionNames = []
+
+ // Insert / Update Entries in Database
+ permissions.forEach(function(permission) {
+ permissionNames.push(permission.name)
+ upsert(db.permission, { name: permission.name, descr: permission.descr, groupdependent: permission.groupdependent }, { name: permission.name })
+ })
+
+ // Delete Entries from Database
+ db.permission.destroy(
+ { where: { $not: { name: permissionNames } } }
+ )
+}
+
+// Update or Insert function
+function upsert (model, newItem, where) {
+ return model
+ .findOne({where: where})
+ .then(function (foundItem) {
+ if (!foundItem) {
+ return model
+ .create(newItem)
+ .then(function (item) { return {item: item, created: true} })
+ }
+
+ return model
+ .update(newItem, {where: where})
+ .then(function (item) { return {item: item, created: false} })
+ })
+} \ No newline at end of file
diff --git a/server/migrations/20180726033100-create-role.js b/server/migrations/20180726033100-create-role.js
new file mode 100644
index 0000000..c930148
--- /dev/null
+++ b/server/migrations/20180726033100-create-role.js
@@ -0,0 +1,22 @@
+'use strict'
+module.exports = {
+ up: (queryInterface, Sequelize) => {
+ return queryInterface.createTable('roles', {
+ id: {
+ allowNull: false,
+ autoIncrement: true,
+ primaryKey: true,
+ type: Sequelize.INTEGER
+ },
+ name: {
+ type: Sequelize.STRING
+ },
+ descr: {
+ type: Sequelize.STRING
+ }
+ })
+ },
+ down: (queryInterface, Sequelize) => {
+ return queryInterface.dropTable('roles')
+ }
+}
diff --git a/server/migrations/20180726033400-create-user_x_role.js b/server/migrations/20180726033400-create-user_x_role.js
new file mode 100644
index 0000000..d82bc9a
--- /dev/null
+++ b/server/migrations/20180726033400-create-user_x_role.js
@@ -0,0 +1,30 @@
+'use strict'
+module.exports = {
+ up: (queryInterface, Sequelize) => {
+ return queryInterface.createTable('user_x_role', {
+ userId: {
+ primaryKey: true,
+ allowNull: false,
+ type: Sequelize.INTEGER,
+ onDelete: "cascade",
+ references: {
+ model: 'users',
+ key: 'id'
+ }
+ },
+ roleId: {
+ primaryKey: true,
+ allowNull: false,
+ type: Sequelize.INTEGER,
+ onDelete: "cascade",
+ references: {
+ model: 'roles',
+ key: 'id'
+ }
+ }
+ })
+ },
+ down: (queryInterface, Sequelize) => {
+ return queryInterface.dropTable('user_x_role')
+ }
+} \ No newline at end of file
diff --git a/server/migrations/20180726171200-create-permission.js b/server/migrations/20180726171200-create-permission.js
new file mode 100644
index 0000000..822e47c
--- /dev/null
+++ b/server/migrations/20180726171200-create-permission.js
@@ -0,0 +1,25 @@
+'use strict'
+module.exports = {
+ up: (queryInterface, Sequelize) => {
+ return queryInterface.createTable('permissions', {
+ id: {
+ allowNull: false,
+ autoIncrement: true,
+ primaryKey: true,
+ type: Sequelize.INTEGER
+ },
+ name: {
+ type: Sequelize.STRING
+ },
+ descr: {
+ type: Sequelize.STRING
+ },
+ groupdependent: {
+ type: Sequelize.BOOLEAN
+ }
+ })
+ },
+ down: (queryInterface, Sequelize) => {
+ return queryInterface.dropTable('permissions')
+ }
+}
diff --git a/server/migrations/20180726173100-create-role_x_permission.js b/server/migrations/20180726173100-create-role_x_permission.js
new file mode 100644
index 0000000..edfcb8e
--- /dev/null
+++ b/server/migrations/20180726173100-create-role_x_permission.js
@@ -0,0 +1,30 @@
+'use strict'
+module.exports = {
+ up: (queryInterface, Sequelize) => {
+ return queryInterface.createTable('role_x_permission', {
+ roleId: {
+ primaryKey: true,
+ allowNull: false,
+ type: Sequelize.INTEGER,
+ onDelete: "cascade",
+ references: {
+ model: 'roles',
+ key: 'id'
+ }
+ },
+ permissionId: {
+ primaryKey: true,
+ allowNull: false,
+ type: Sequelize.INTEGER,
+ onDelete: "cascade",
+ references: {
+ model: 'permissions',
+ key: 'id'
+ }
+ }
+ })
+ },
+ down: (queryInterface, Sequelize) => {
+ return queryInterface.dropTable('role_x_permission')
+ }
+} \ No newline at end of file
diff --git a/server/migrations/20180804234000-create-role_x_group.js b/server/migrations/20180804234000-create-role_x_group.js
new file mode 100644
index 0000000..d9024be
--- /dev/null
+++ b/server/migrations/20180804234000-create-role_x_group.js
@@ -0,0 +1,30 @@
+'use strict'
+module.exports = {
+ up: (queryInterface, Sequelize) => {
+ return queryInterface.createTable('role_x_group', {
+ roleId: {
+ primaryKey: true,
+ allowNull: false,
+ type: Sequelize.INTEGER,
+ onDelete: "cascade",
+ references: {
+ model: 'roles',
+ key: 'id'
+ }
+ },
+ groupId: {
+ primaryKey: true,
+ allowNull: false,
+ type: Sequelize.INTEGER,
+ onDelete: "cascade",
+ references: {
+ model: 'groups',
+ key: 'id'
+ }
+ }
+ })
+ },
+ down: (queryInterface, Sequelize) => {
+ return queryInterface.dropTable('role_x_group')
+ }
+} \ No newline at end of file
diff --git a/server/models/permission.js b/server/models/permission.js
new file mode 100644
index 0000000..a1bd5d3
--- /dev/null
+++ b/server/models/permission.js
@@ -0,0 +1,21 @@
+'use strict'
+module.exports = (sequelize, DataTypes) => {
+ var permission = sequelize.define('permission', {
+ id: {
+ allowNull: false,
+ autoIncrement: true,
+ primaryKey: true,
+ type: DataTypes.INTEGER
+ },
+ name: DataTypes.STRING,
+ descr: DataTypes.STRING,
+ groupdependent: DataTypes.BOOLEAN
+ }, {
+ timestamps: false
+ })
+ permission.associate = function (models) {
+
+ }
+
+ return permission
+}
diff --git a/server/models/role.js b/server/models/role.js
index 60fba53..2ce1ad1 100644
--- a/server/models/role.js
+++ b/server/models/role.js
@@ -13,7 +13,12 @@ module.exports = (sequelize, DataTypes) => {
timestamps: false
})
role.associate = function (models) {
- // associations can be defined here
+ 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 })
+ role.belongsToMany(models.group, { as: 'groups', through: RoleXGroup, foreignKey: 'roleId', otherKey: 'groupId'})
}
+
return role
}
diff --git a/server/models/user.js b/server/models/user.js
index 63e87bd..7ef8afc 100644
--- a/server/models/user.js
+++ b/server/models/user.js
@@ -15,7 +15,9 @@ module.exports = (sequelize, DataTypes) => {
timestamps: false
})
user.associate = function (models) {
- // associations can be defined here
+ var UserXRole = sequelize.define('user_x_role', {}, { timestamps: false, freezeTableName: true })
+ user.belongsToMany(models.role, { as: 'roles', through: UserXRole, foreignKey: 'userId', otherKey: 'roleId'})
}
+
return user
}
diff --git a/server/models/user_x_role.js b/server/models/user_x_role.js
deleted file mode 100644
index 96d5883..0000000
--- a/server/models/user_x_role.js
+++ /dev/null
@@ -1,17 +0,0 @@
-'use strict'
-module.exports = (sequelize, DataTypes) => {
- var user_x_role = sequelize.define('user_x_role', {},
- {
- timestamps: false
- })
-
- user_x_role.associate = function (models) {
- // associations can be defined here
- // sequelize.role.belongsToMany(sequelize.user, {through: 'user_x_role', foreignKey: 'userid'});
- // sequelize.user.hasMany(sequelize.role, {
- // through: 'user_x_role',
- // foreignKey: 'roleid'
- // });
- }
- return user_x_role
-}
diff --git a/webapp/package-lock.json b/webapp/package-lock.json
index 2fac179..91e680d 100644
--- a/webapp/package-lock.json
+++ b/webapp/package-lock.json
@@ -11201,6 +11201,14 @@
"rollup-plugin-node-resolve": "^2.0.0"
}
},
+ "vue2-hammer": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/vue2-hammer/-/vue2-hammer-1.0.6.tgz",
+ "integrity": "sha512-MfRDkMdQoEng/BRe7moIDbiMgcPqgclVD3WqNfnQ6JlKtTOyMV/X5Z8/IEySa8QGHDVDOp9fI3WmwEoKxnZQ3g==",
+ "requires": {
+ "hammerjs": "^2.0.8"
+ }
+ },
"vuetify": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/vuetify/-/vuetify-1.1.1.tgz",
diff --git a/webapp/src/components/PermissionModule.vue b/webapp/src/components/PermissionModule.vue
index c63f962..00796b5 100644
--- a/webapp/src/components/PermissionModule.vue
+++ b/webapp/src/components/PermissionModule.vue
@@ -3,44 +3,104 @@
"en": {
"roles": "Roles",
"users": "Users",
- "createRole": "Create Role",
- "assignRole": "Assign Role",
- "revokeRole": "Revoke Role"
+ "delete-are-you-sure": "Are you sure you want to delete this role? | Are you sure you want to delete those roles?",
+ "delete-permission": "Delete {0} role | Delete {0} roles"
},
"de": {
"roles": "Rollen",
"users": "Nutzer",
- "createRole": "Rolle erstellen",
- "assignRole": "Rolle zuweisen",
- "revokeROle": "Rolleentziehen"
+ "delete-are-you-sure": "Sind sie sicher, dass sie diese Rolle Löschen wollen? | Sind sie sicher, dass sie diese Rollen Löschen wollen?",
+ "delete-permission": "Delete {0} role | Delete {0} roles"
}
}
</i18n>
<template>
- <v-container>
+ <v-container fill-height>
<v-layout>
- <v-flex md10 offset-md1 sm10 offset-sm1>
- <v-btn color="primary" @click="createRole">{{ $t('createRole') }}</v-btn>
- <v-btn class="right" color="success">{{ $t('assignRole') }}</v-btn>
- <v-btn class="right" color="warning">{{ $t('revokeRole') }}</v-btn>
+ <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-flex>
</v-layout>
+
+ <!--- Put Dialogs here (delete Dialog, edit Dialog, revoke Dialog, grant Dialog -->
+ <v-dialog
+ :value="$store.state.permissions.dialog"
+ @input="$store.commit('permissions/setDialog', $event )"
+ max-width="500px"
+ scrollable
+ >
+ <v-card>
+ <v-card-title primary-title class="elevation-3">
+ <div>
+ <div class="headline">{{ $tc('delete-role', selectedRoles.length, [selectedRoles.length]) }}</div>
+ </div>
+ </v-card-title>
+ <v-card-text>
+ {{ $tc('delete-are-you-sure', selectedRoles.length) }}
+ <template v-for="item in selectedRoles">
+ <div class="grey--text" :key="item.id">[{{ item.id }}] {{ item.name }} ({{ item.descr }})</div>
+ </template>
+ </v-card-text>
+ <v-divider></v-divider>
+ <v-card-actions>
+ <v-spacer></v-spacer>
+ <v-btn flat="flat" @click="$store.commit('permissions/setDialog', false )">{{ $t('cancel') }}</v-btn>
+ <v-btn color="error" @click="$store.commit('permissions/setDialog', false ); $store.dispatch('permissions/deleteSelectedRoles')">{{ $t('delete') }}</v-btn>
+ </v-card-actions>
+ </v-card>
+ </v-dialog>
+
+ <permission-module-grant-revoke :grant="$store.state.permissions.grant"/>
+ <permission-module-edit :roleId="$store.state.permissions.roleId"/>
+
</v-container>
</template>
<script>
+import PermissionModuleRoleList from '@/components/PermissionModuleRoleList'
+import PermissionModuleUserList from '@/components/PermissionModuleUserList'
+import PermissionModuleGrantRevoke from '@/components/PermissionModuleGrantRevoke'
+import PermissionModuleEdit from '@/components/PermissionModuleEdit'
+import { mapState, mapGetters } from 'vuex'
export default {
- name: 'AccountPage',
+ name: 'PermissionModule',
+ components: {
+ PermissionModuleRoleList,
+ PermissionModuleUserList,
+ PermissionModuleGrantRevoke,
+ PermissionModuleEdit
+ },
data () {
return {
+ components: ['PermissionModuleRoleList', 'PermissionModuleUserList'],
+ tab: ''
}
},
+ computed: {
+ ...mapGetters(['tabsDark', 'tabsColor', 'tabsSliderColor']),
+ ...mapState('permissions', ['selectedRoles', 'selectedUsers'])
+ },
methods: {
- createRole: function (event) {
- window.open('https://bas.stfu-kthx.net:8000/#/dashboard/permission/createRole', '_self')
- }
}
}
</script>
diff --git a/webapp/src/components/PermissionModuleEdit.vue b/webapp/src/components/PermissionModuleEdit.vue
new file mode 100644
index 0000000..5e892de
--- /dev/null
+++ b/webapp/src/components/PermissionModuleEdit.vue
@@ -0,0 +1,273 @@
+<i18n>
+{
+ "en": {
+ },
+ "de": {
+ }
+}
+</i18n>
+
+<template>
+ <v-dialog
+ :value="$store.state.permissions.edit"
+ @input="$store.commit('permissions/setEdit', $event)"
+ max-width="700px"
+ scrollable
+ >
+ <v-card>
+ <v-card-title style="padding: 0px">
+ <v-stepper v-model="step" horizontal style="width: 100%; background: transparent;" class="elevation-3">
+ <v-stepper-header>
+ <v-stepper-step
+ :complete="stepCompleted >= 1"
+ step="1"
+ :editable="stepCompleted >= 1"
+ edit-icon="check"
+ >
+ {{ $t('role') }}
+ </v-stepper-step>
+ <v-divider></v-divider>
+ <v-stepper-step
+ :complete="stepCompleted >= 2"
+ step="2"
+ :editable="stepCompleted >= 2"
+ edit-icon="check"
+ >
+ {{ $t('permissions') }}
+ </v-stepper-step>
+ <v-divider></v-divider>
+ <v-stepper-step
+ :complete="stepCompleted >= 3"
+ step="3"
+ :editable="stepCompleted >= 3"
+ edit-icon="check"
+ >
+ {{ $t('groups') }}
+ </v-stepper-step>
+ <v-divider></v-divider>
+ <v-stepper-step
+ :complete="stepCompleted >= 4"
+ step="4"
+ :editable="stepCompleted >= 4"
+ edit-icon="check"
+ >
+ {{ $t('confirm') }}
+ </v-stepper-step>
+ </v-stepper-header>
+ </v-stepper>
+ </v-card-title>
+ <v-card-text style="height: 500px;">
+ <v-form v-model="valid" ref="form" @submit.prevent="submit" lazy-validation>
+ <v-stepper v-model="step" horizontal style="width: 100%; background: transparent" class="elevation-0">
+ <v-stepper-items>
+
+ <v-stepper-content step="1">
+ <p> 2 Input Boxen (Name, Description) </p>
+ <v-text-field
+ v-model="roleName"
+ :label="$t('roleName')"
+ :rules="[() => !!roleName || $t('roleNameEmptyError')]"
+ ref="roleName"
+ prepend-icon="assignment_ind"
+ ></v-text-field>
+ </v-stepper-content>
+
+ <v-stepper-content step="2">
+ <v-data-table
+ class="group-table"
+ :headers="permissionHeaders"
+ :items="permissions"
+ item-key="id"
+ hide-actions
+ select-all
+ :value="permissionsSelected"
+ @input="setPermissionsSelected($event)"
+ >
+ <template slot="items" slot-scope="props">
+ <tr @click="props.selected = !props.selected">
+ <td>
+ <v-checkbox
+ color="primary"
+ v-model="props.selected"
+ hide-details
+ @click.native.stop
+ ></v-checkbox>
+ </td>
+ <td hidden>{{ props.item.id }}</td>
+ <td>{{ props.item.name }}</td>
+ <td>{{ props.item.descr }}</td>
+ <td>{{ props.item.groupdependent }}</td>
+ </tr>
+ </template>
+ </v-data-table>
+ </v-stepper-content>
+
+ <v-stepper-content step="3">
+ <p> Data-Table aller Gruppen</p>
+ </v-stepper-content>
+
+ <v-stepper-content step="4">
+ {{roleName}}
+ {{roleDescr}}
+ <v-layout row wrap>
+ <v-flex>
+ <v-list two-line subheader>
+ <v-subheader inset>{{ $t('permissions') }}</v-subheader>
+ <v-divider></v-divider>
+ <v-list-tile
+ v-for="permission in permissionsSelected"
+ :key="permission.id"
+ >
+ <v-list-tile-content>
+ <v-list-tile-title>{{ permission.name }}</v-list-tile-title>
+ <v-list-tile-sub-title>{{ permission.descr }}</v-list-tile-sub-title>
+ </v-list-tile-content>
+ </v-list-tile>
+ </v-list>
+ </v-flex>
+ <v-flex>
+ <v-list two-line subheader>
+ <v-subheader inset>{{ $t('groups') }}</v-subheader>
+ <v-divider></v-divider>
+ <v-list-tile
+ v-for="group in groupsSelected"
+ :key="group.id"
+ >
+ <v-list-tile-content>
+ <v-list-tile-title>{{ group.name }}</v-list-tile-title>
+ </v-list-tile-content>
+ </v-list-tile>
+ </v-list>
+ </v-flex>
+ </v-layout>
+ </v-stepper-content>
+
+ </v-stepper-items>
+ </v-stepper>
+ </v-form>
+ </v-card-text>
+ <v-divider></v-divider>
+ <v-card-actions>
+ <v-flex xl10 offset-xl1 lg12 text-xs-right>
+ <v-btn flat @click.native="$store.commit('permissions/setEdit', false )">{{ $t('cancel') }}</v-btn>
+ <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-flex>
+ </v-card-actions>
+ </v-card>
+ </v-dialog>
+</template>
+
+<script>
+import { mapState } from 'vuex'
+
+export default {
+ name: 'PermissionModuleEdit',
+ props: ['roleId'],
+ data () {
+ return {
+ valid: true,
+ step: 1,
+ stepCompleted: 0,
+ permissionsSelected: [],
+ groupsSelected: [],
+ permissionHeaders: [
+ { text: 'Name', value: 'name' },
+ { text: 'Description', value: 'descr' },
+ { text: 'Groupdependent', value: 'groupdependent' }
+ ],
+ groupHeaders: [
+ { text: 'ID', value: 'id' },
+ { text: 'Name', value: 'name' }
+ ],
+ roleName: '',
+ roleDescr: ''
+ }
+ },
+ methods: {
+ submit (event) {
+ if (this.$refs.form.validate()) {
+ const filteredPermissions = this.permissionsSelected.map(x => x.id)
+ const filteredGroups = this.groupsSelected.map(x => x.id)
+ this.$http.post('/api/permissions/saveRole', {
+ id: this.roleId,
+ name: this.roleName,
+ descr: this.roleDescr,
+ permissions: filteredPermissions,
+ groups: filteredGroups
+ }).then(response => {
+ // TODO: Add backend saved successfull msg.
+ console.log('TODO: Implement snackbar and print role saved successfully msg.')
+ this.$store.dispatch('permissions/loadData')
+ this.$store.commit('permissions/setEdit', false)
+ }).catch(error => {
+ console.log(error)
+ // if (error.response.data.status === '') {
+ // }
+ // this.$refs.form.validate()
+ })
+ }
+ },
+ loadRole (roleId) {
+ this.$http('/api/permissions/getRoleById?id=' + this.roleId).then(response => {
+ this.roleName = response.data.name
+ this.roleDescr = response.data.descr
+ // this.permissionsSelected = response.data.permissions.id
+ // this.groupsSelected = response.data.groups.id
+ })
+ },
+ completeStepOne () {
+ if (this.roleName !== '') {
+ this.step = 2
+ this.stepCompleted = Math.max(1, this.stepCompleted)
+ } else {
+ this.$refs.form.validate()
+ }
+ },
+ completeStepTwo () {
+ this.step = 3
+ this.stepCompleted = Math.max(2, this.stepCompleted)
+ },
+ completeStepThree () {
+ this.step = 4
+ this.stepCompleted = Math.max(3, this.stepCompleted)
+ },
+ setPermissionsSelected (value) {
+ this.permissionsSelected = value
+ }
+ },
+ computed: {
+ ...mapState('permissions', ['permissions']),
+ edit: function () {
+ return this.$store.state.permissions.edit
+ }
+ },
+ watch: {
+ edit: function (value) {
+ if (value) {
+ this.$refs.form.reset()
+ if (this.roleId !== 0) {
+ this.loadRole(this.roleId)
+ } else {
+ this.roleName = ''
+ this.roleDescr = ''
+ this.permissionsSelected = []
+ this.groupsSelected = []
+ }
+ this.step = 1
+ this.stepCompleted = 0
+ }
+ }
+ },
+ created () {
+ this.$store.dispatch('permissions/loadPermissionData')
+ }
+}
+</script>
+
+<!-- Add "scoped" attribute to limit CSS to this component only -->
+<style scoped>
+
+</style>
diff --git a/webapp/src/components/PermissionModuleGrantRevoke.vue b/webapp/src/components/PermissionModuleGrantRevoke.vue
new file mode 100644
index 0000000..c999e1b
--- /dev/null
+++ b/webapp/src/components/PermissionModuleGrantRevoke.vue
@@ -0,0 +1,197 @@
+<i18n>
+{
+ "en": {
+ },
+ "de": {
+ }
+}
+</i18n>
+
+<template>
+ <v-dialog
+ :value="$store.state.permissions.grantRevoke"
+ @input="$store.commit('permissions/setGrantRevoke', $event)"
+ max-width="700px"
+ scrollable
+ >
+ <v-card>
+ <v-card-title style="padding: 0px">
+ <v-stepper v-model="step" horizontal style="width: 100%; background: transparent;" class="elevation-3">
+ <v-stepper-header>
+ <v-stepper-step
+ :complete="stepCompleted >= 1"
+ step="1"
+ :editable="stepCompleted >= 1"
+ edit-icon="check"
+ >
+ {{ $t('select_roles') }}
+ </v-stepper-step>
+ <v-divider></v-divider>
+ <v-stepper-step
+ :complete="stepCompleted >= 2"
+ step="2"
+ :editable="stepCompleted >= 2"
+ edit-icon="check"
+ >
+ {{ $t('confirm_selection') }}
+ </v-stepper-step>
+ </v-stepper-header>
+ </v-stepper>
+ </v-card-title>
+ <v-card-text style="height: 500px;">
+ <v-form v-model="valid" ref="form" @submit.prevent="submit" lazy-validation>
+ <v-stepper v-model="step" horizontal style="width: 100%; background: transparent" class="elevation-0">
+ <v-stepper-items>
+ <v-stepper-content step="1">
+ <v-data-table
+ class="group-table"
+ :headers="roleHeaders"
+ :items="roles"
+ item-key="id"
+ hide-actions
+ select-all
+ :value="rolesSelected"
+ @input="setRolesSelected($event)"
+ >
+ <template slot="items" slot-scope="props">
+ <tr @click="props.selected = !props.selected">
+ <td>
+ <v-checkbox
+ color="primary"
+ v-model="props.selected"
+ hide-details
+ @click.native.stop
+ ></v-checkbox>
+ </td>
+ <td>{{ props.item.id }}</td>
+ <td>{{ props.item.name }}</td>
+ <td>{{ props.item.descr }}</td>
+ </tr>
+ </template>
+ </v-data-table>
+ </v-stepper-content>
+ <v-stepper-content step="2">
+ <!-- List of selected Users and selected Roles here -->
+ <v-layout row wrap>
+ <v-flex xs12 class="list-content">
+ <v-list two-line subheader>
+ <v-subheader inset>{{ $t('roles') }}</v-subheader>
+ <v-divider></v-divider>
+ <v-list-tile
+ v-for="role in rolesSelected"
+ :key="role.id"
+ >
+ <v-list-tile-content>
+ <v-list-tile-title>{{ role.name }}</v-list-tile-title>
+ <v-list-tile-sub-title>{{ role.descr }}</v-list-tile-sub-title>
+ </v-list-tile-content>
+ </v-list-tile>
+ </v-list>
+ </v-flex>
+ <v-flex xs12 class="list-content">
+ <v-list>
+ <v-subheader inset>{{ $t('users') }}</v-subheader>
+ <v-divider></v-divider>
+ <v-list-tile
+ v-for="user in selectedUsers"
+ :key="user.username"
+ >
+ <v-list-tile-content>
+ <v-list-tile-title>{{ user.username }}</v-list-tile-title>
+ <v-list-tile-sub-title>{{ user.name }}</v-list-tile-sub-title>
+ </v-list-tile-content>
+ </v-list-tile>
+ </v-list>
+ </v-flex>
+ </v-layout>
+ </v-stepper-content>
+ </v-stepper-items>
+ </v-stepper>
+ </v-form>
+ </v-card-text>
+ <v-divider></v-divider>
+ <v-card-actions>
+ <v-flex xl10 offset-xl1 lg12 text-xs-right>
+ <v-btn flat @click.native="$store.commit('permissions/setGrantRevoke', false )">{{ $t('cancel') }}</v-btn>
+ <v-btn color="primary" v-show="step == 1" @click.native="completeStepOne()">{{ $t('continue') }}</v-btn>
+ <v-btn type="submit" @click="submit" v-show="step == 2" v-if="grant" class="success">{{ $t('grant') }}</v-btn>
+ <v-btn type="submit" @click="submit" v-show="step == 2" v-else class="error">{{ $t('revoke') }}</v-btn>
+ </v-flex>
+ </v-card-actions>
+ </v-card>
+ </v-dialog>
+</template>
+
+<script>
+import { mapState } from 'vuex'
+
+export default {
+ name: 'PermissionModuleGrantRevoke',
+ props: ['grant'],
+ data () {
+ return {
+ valid: true,
+ step: 1,
+ stepCompleted: 0,
+ rolesSelected: [],
+ roleHeaders: [
+ { text: 'ID', value: 'id' },
+ { text: 'Name', value: 'name' },
+ { text: 'Description', value: 'descr' }
+ ]
+ }
+ },
+ methods: {
+ submit (event) {
+ const filteredRoles = this.rolesSelected.map(x => x.id)
+ const filteredUsers = this.selectedUsers.map(x => x.id)
+ if (this.grant) {
+ this.$http.post('/api/user/grantRoles', {
+ userIds: filteredUsers,
+ roleIds: filteredRoles
+ }).then(response => {
+ console.log('TODO: Implement snackbar and print roles granted successfully msg.')
+ this.$store.dispatch('permissions/loadData')
+ this.$store.commit('permissions/setGrantRevoke', false)
+ this.$store.commit('permissions/setSelectedUsers', [])
+ }).catch(error => {
+ console.log(error)
+ })
+ } else {
+ this.$http.post('/api/user/revokeRoles', {
+ userIds: filteredUsers,
+ roleIds: filteredRoles
+ }).then(response => {
+ console.log('TODO: Implement snackbar and print roles revoked successfully msg.')
+ this.$store.dispatch('permissions/loadData')
+ this.$store.commit('permissions/setGrantRevoke', false)
+ this.$store.commit('permissions/setSelectedUsers', [])
+ }).catch(error => {
+ console.log(error)
+ })
+ }
+ },
+ completeStepOne () {
+ this.step = 2
+ this.stepCompleted = Math.max(1, this.stepCompleted)
+ },
+ setRolesSelected (value) {
+ this.rolesSelected = value
+ }
+ },
+ computed: {
+ ...mapState('permissions', ['roles', 'selectedUsers'])
+ },
+ beforeMount () {
+ },
+ watch: {
+ }
+}
+</script>
+
+<!-- Add "scoped" attribute to limit CSS to this component only -->
+<style scoped>
+.list-content {
+ margin-bottom: 10px;
+}
+</style>
diff --git a/webapp/src/components/PermissionModuleRoleList.vue b/webapp/src/components/PermissionModuleRoleList.vue
new file mode 100644
index 0000000..5e3632a
--- /dev/null
+++ b/webapp/src/components/PermissionModuleRoleList.vue
@@ -0,0 +1,91 @@
+<i18n>
+{
+ "en": {
+ "create-role": "Create Role",
+ "delete-role": "Delete {0} Role | Delete {0} Roles"
+ },
+ "de": {
+ "create-role": "Rolle erstellen",
+ "delete-role": "Lösche {0} Rolle | Lösche {0} Rollen"
+ }
+}
+</i18n>
+
+<template>
+ <div>
+ <v-card>
+ <v-data-table
+ class="group-table"
+ :headers="headers"
+ :items="roles"
+ item-key="id"
+ hide-actions
+ select-all
+ :value="selectedRoles"
+ @input="$store.commit('permissions/setSelectedRoles', $event)"
+ >
+ <template slot="items" slot-scope="props">
+ <tr>
+ <td>
+ <v-checkbox
+ color="primary"
+ v-model="props.selected"
+ hide-details
+ @click.native.stop
+ ></v-checkbox>
+ </td>
+ <td>{{ props.item.id }}</td>
+ <td>{{ props.item.name }}</td>
+ <td>{{ props.item.descr }}</td>
+ <td>
+ <v-layout>
+ <v-btn flat icon color="primary" @click.stop="$store.commit('permissions/editRole', props.item.id)">
+ <v-icon>edit</v-icon>
+ </v-btn>
+ </v-layout>
+ </td>
+ </tr>
+ </template>
+ </v-data-table>
+ </v-card>
+ <div class="text-xs-right">
+ <v-btn color="error" flat @click="$store.commit('permissions/setDialog', true )">
+ <v-icon left>remove_circle_outline</v-icon>{{ $tc('delete-role', selectedRoles.length, [selectedRoles.length]) }}
+ </v-btn>
+ <v-btn color="success" flat @click="$store.commit('permissions/editRole', 0)">
+ <v-icon left>add_circle_outline</v-icon>{{ $t('create-role') }}
+ </v-btn>
+ </div>
+ </div>
+</template>
+
+<script>
+import { mapState } from 'vuex'
+
+export default {
+ name: 'PermissionModuleRoleList',
+ data () {
+ return {
+ headers: [
+ { text: 'ID', value: 'id' },
+ { text: 'Name', value: 'name' },
+ { text: 'Description', value: 'descr' },
+ { sortable: false }
+ ]
+ }
+ },
+ computed: {
+ ...mapState('permissions', ['selectedRoles', 'roles'])
+ },
+ methods: {
+ },
+ created () {
+ this.$store.dispatch('permissions/loadRoleData')
+ }
+}
+</script>
+
+<!-- Add "scoped" attribute to limit CSS to this component only -->
+<style scoped>
+
+</style>
diff --git a/webapp/src/components/PermissionModuleUserList.vue b/webapp/src/components/PermissionModuleUserList.vue
new file mode 100644
index 0000000..074a11f
--- /dev/null
+++ b/webapp/src/components/PermissionModuleUserList.vue
@@ -0,0 +1,88 @@
+<i18n>
+{
+ "en": {
+ "grant-role": "Grant Role",
+ "revoke-role": "Revoke Role"
+ },
+ "de": {
+ "grant-role": "Rolle zuweisen",
+ "revoke-role": "Rolle entziehen"
+ }
+}
+</i18n>
+
+<template>
+ <div>
+ <v-card>
+ <v-data-table
+ class="group-table"
+ :headers="headers"
+ :items="users"
+ item-key="id"
+ hide-actions
+ select-all
+ :value="selectedUsers"
+ @input="$store.commit('permissions/setSelectedUsers', $event)"
+ >
+ <template slot="items" slot-scope="props">
+ <tr>
+ <td>
+ <v-checkbox
+ color="primary"
+ v-model="props.selected"
+ hide-details
+ @click.native.stop
+ ></v-checkbox>
+ </td>
+ <td hidden>{{ props.item.id }}</td>
+ <td>{{ props.item.username }}</td>
+ <td>{{ props.item.name }}</td>
+ <td>
+ <template v-for="role in props.item.roles">
+ {{ role.name }}
+ </template>
+ </td>
+ </tr>
+ </template>
+ </v-data-table>
+ </v-card>
+ <div class="text-xs-right">
+ <v-btn color="error" flat @click="$store.commit('permissions/grantRevoke', { show: true, grant: false } )">
+ <v-icon left>remove_circle_outline</v-icon>{{ $t('revoke-role') }}
+ </v-btn>
+ <v-btn color="success" flat @click="$store.commit('permissions/grantRevoke', { show: true, grant: true } )">
+ <v-icon left>add_circle_outline</v-icon>{{ $t('grant-role') }}
+ </v-btn>
+ </div>
+ </div>
+</template>
+
+<script>
+import { mapState } from 'vuex'
+
+export default {
+ name: 'PermissionModuleUserList',
+ data () {
+ return {
+ headers: [
+ { text: 'Username', value: 'username' },
+ { text: 'Name', value: 'name' },
+ { text: 'Roles', value: 'id' }
+ ]
+ }
+ },
+ computed: {
+ ...mapState('permissions', ['selectedUsers', 'users'])
+ },
+ methods: {
+ },
+ created () {
+ this.$store.dispatch('permissions/loadUserData')
+ }
+}
+</script>
+
+<!-- Add "scoped" attribute to limit CSS to this component only -->
+<style scoped>
+
+</style>
diff --git a/webapp/src/store/index.js b/webapp/src/store/index.js
index afaa1b4..b351957 100644
--- a/webapp/src/store/index.js
+++ b/webapp/src/store/index.js
@@ -3,12 +3,14 @@ import Vuex from 'vuex'
import globalStore from '@/store/global'
import groups from '@/store/groups'
import backends from '@/store/backends'
+import permissions from '@/store/permissions'
Vue.use(Vuex)
globalStore.modules = {
groups,
- backends
+ backends,
+ permissions
}
export default new Vuex.Store(globalStore)
diff --git a/webapp/src/store/permissions.js b/webapp/src/store/permissions.js
new file mode 100644
index 0000000..a8d09ec
--- /dev/null
+++ b/webapp/src/store/permissions.js
@@ -0,0 +1,81 @@
+import axios from 'axios'
+
+export default {
+ namespaced: true,
+ state: {
+ roles: [],
+ users: [],
+ permissions: [],
+ selectedRoles: [],
+ selectedUsers: [],
+ roleId: '',
+ dialog: false,
+ grantRevoke: false,
+ grant: false,
+ edit: false
+ },
+ mutations: {
+ setDialog (state, value) {
+ state.dialog = value
+ },
+ setRoles (state, value) {
+ state.roles = value
+ },
+ setUsers (state, value) {
+ state.users = value
+ },
+ setPermissions (state, value) {
+ state.permissions = value
+ },
+ setSelectedRoles (state, value) {
+ state.selectedRoles = value
+ },
+ setSelectedUsers (state, value) {
+ state.selectedUsers = value
+ },
+ editRole (state, value) {
+ state.roleId = value
+ state.edit = true
+ },
+ setEdit (state, value) {
+ state.edit = value
+ },
+ grantRevoke (state, value) {
+ state.grantRevoke = value.show
+ state.grant = value.grant
+ },
+ setGrantRevoke (state, value) {
+ state.grantRevoke = value
+ }
+ },
+ actions: {
+ deleteSelectedRoles (context) {
+ // Filter selected array to get a list of ids.
+ const filteredArray = context.state.selectedRoles.map(x => x.id)
+ axios.post('/api/permissions/deleteRoles', { id: filteredArray }).then(response => {
+ context.dispatch('loadData')
+ context.commit('setSelectedRoles', [])
+ })
+ },
+ loadRoleData (context) {
+ axios.get('/api/permissions/getRoleList').then(response => {
+ context.commit('setRoles', response.data)
+ })
+ },
+ loadUserData (context) {
+ axios.get('/api/user/getUserList').then(response => {
+ context.commit('setUsers', response.data)
+ })
+ },
+ loadPermissionData (context) {
+ axios.get('/api/permissions/getPermissionList').then(response => {
+ context.commit('setPermissions', response.data)
+ })
+ },
+ loadData (context) {
+ context.dispatch('loadPermissionData')
+ context.dispatch('loadRoleData')
+ context.dispatch('loadUserData')
+ }
+ }
+}