Added user permissions, delete user

This commit is contained in:
Jamie Curnow 2018-07-05 08:27:25 +10:00
parent 4a59ef9925
commit 30924a6922
22 changed files with 690 additions and 51 deletions

View File

@ -1,11 +1,12 @@
'use strict';
const _ = require('lodash');
const error = require('../lib/error');
const userModel = require('../models/user');
const authModel = require('../models/auth');
const gravatar = require('gravatar');
const internalToken = require('./token');
const _ = require('lodash');
const error = require('../lib/error');
const userModel = require('../models/user');
const userPermissionModel = require('../models/user_permission');
const authModel = require('../models/auth');
const gravatar = require('gravatar');
const internalToken = require('./token');
function omissions () {
return ['is_deleted'];
@ -56,7 +57,23 @@ const internalUser = {
}
})
.then(user => {
return internalUser.get(access, {id: user.id});
// Create permissions row as well
let is_admin = data.roles.indexOf('admin') !== -1;
return userPermissionModel
.query()
.insert({
user_id: user.id,
visibility: is_admin ? 'all' : 'user',
proxy_hosts: 'manage',
redirection_hosts: 'manage',
dead_hosts: 'manage',
streams: 'manage',
access_lists: 'manage'
})
.then(() => {
return internalUser.get(access, {id: user.id, expand: ['permissions']});
});
});
},
@ -145,6 +162,7 @@ const internalUser = {
.query()
.where('is_deleted', 0)
.andWhere('id', data.id)
.allowEager('[permissions]')
.first();
// Custom omissions
@ -377,6 +395,50 @@ const internalUser = {
});
},
/**
* @param {Access} access
* @param {Object} data
* @return {Promise}
*/
setPermissions: (access, data) => {
return access.can('users:permissions', data.id)
.then(() => {
return internalUser.get(access, {id: data.id});
})
.then(user => {
if (user.id !== data.id) {
// Sanity check that something crazy hasn't happened
throw new error.InternalValidationError('User could not be updated, IDs do not match: ' + user.id + ' !== ' + data.id);
}
return user;
})
.then(user => {
// Get perms row, patch if it exists
return userPermissionModel
.query()
.where('user_id', user.id)
.first()
.then(existing_auth => {
if (existing_auth) {
// patch
return userPermissionModel
.query()
.where('user_id', user.id)
.patchAndFetchById(existing_auth.id, _.assign({user_id: user.id}, data));
} else {
// insert
return userPermissionModel
.query()
.insertAndFetch(_.assign({user_id: user.id}, data));
}
})
.then(permissions => {
return true;
});
});
},
/**
* @param {Access} access
* @param {Object} data

View File

@ -0,0 +1,7 @@
{
"anyOf": [
{
"$ref": "roles#/definitions/admin"
}
]
}

View File

@ -43,6 +43,124 @@ exports.up = function (knex/*, Promise*/) {
})
.then(() => {
logger.info('[' + migrate_name + '] user Table created');
return knex.schema.createTable('user_permission', table => {
table.increments().primary();
table.dateTime('created_on').notNull();
table.dateTime('modified_on').notNull();
table.integer('user_id').notNull().unsigned();
table.string('visibility').notNull();
table.string('proxy_hosts').notNull();
table.string('redirection_hosts').notNull();
table.string('dead_hosts').notNull();
table.string('streams').notNull();
table.string('access_lists').notNull();
table.unique('user_id');
});
})
.then(() => {
logger.info('[' + migrate_name + '] user_permission Table created');
return knex.schema.createTable('proxy_host', table => {
table.increments().primary();
table.dateTime('created_on').notNull();
table.dateTime('modified_on').notNull();
table.integer('owner_user_id').notNull().unsigned();
table.integer('is_deleted').notNull().unsigned().defaultTo(0);
table.string('domain_name').notNull();
table.string('forward_ip').notNull();
table.integer('forward_port').notNull().unsigned();
table.integer('access_list_id').notNull().unsigned().defaultTo(0);
table.integer('ssl_enabled').notNull().unsigned().defaultTo(0);
table.string('ssl_provider').notNull().defaultTo('');
table.integer('ssl_forced').notNull().unsigned().defaultTo(0);
table.integer('caching_enabled').notNull().unsigned().defaultTo(0);
table.integer('block_exploits').notNull().unsigned().defaultTo(0);
table.json('meta').notNull();
table.unique(['domain_name', 'is_deleted']);
});
})
.then(() => {
logger.info('[' + migrate_name + '] proxy_host Table created');
return knex.schema.createTable('redirection_host', table => {
table.increments().primary();
table.dateTime('created_on').notNull();
table.dateTime('modified_on').notNull();
table.integer('owner_user_id').notNull().unsigned();
table.integer('is_deleted').notNull().unsigned().defaultTo(0);
table.string('domain_name').notNull();
table.string('forward_domain_name').notNull();
table.integer('preserve_path').notNull().unsigned().defaultTo(0);
table.integer('ssl_enabled').notNull().unsigned().defaultTo(0);
table.string('ssl_provider').notNull().defaultTo('');
table.integer('block_exploits').notNull().unsigned().defaultTo(0);
table.json('meta').notNull();
table.unique(['domain_name', 'is_deleted']);
});
})
.then(() => {
logger.info('[' + migrate_name + '] redirection_host Table created');
return knex.schema.createTable('dead_host', table => {
table.increments().primary();
table.dateTime('created_on').notNull();
table.dateTime('modified_on').notNull();
table.integer('owner_user_id').notNull().unsigned();
table.integer('is_deleted').notNull().unsigned().defaultTo(0);
table.string('domain_name').notNull();
table.integer('ssl_enabled').notNull().unsigned().defaultTo(0);
table.string('ssl_provider').notNull().defaultTo('');
table.json('meta').notNull();
table.unique(['domain_name', 'is_deleted']);
});
})
.then(() => {
logger.info('[' + migrate_name + '] dead_host Table created');
return knex.schema.createTable('stream', table => {
table.increments().primary();
table.dateTime('created_on').notNull();
table.dateTime('modified_on').notNull();
table.integer('owner_user_id').notNull().unsigned();
table.integer('is_deleted').notNull().unsigned().defaultTo(0);
table.integer('incoming_port').notNull().unsigned();
table.string('forward_ip').notNull();
table.integer('forwarding_port').notNull().unsigned();
table.integer('tcp_forwarding').notNull().unsigned().defaultTo(0);
table.integer('udp_forwarding').notNull().unsigned().defaultTo(0);
table.json('meta').notNull();
table.unique(['incoming_port', 'is_deleted']);
});
})
.then(() => {
logger.info('[' + migrate_name + '] stream Table created');
return knex.schema.createTable('access_list', table => {
table.increments().primary();
table.dateTime('created_on').notNull();
table.dateTime('modified_on').notNull();
table.integer('owner_user_id').notNull().unsigned();
table.integer('is_deleted').notNull().unsigned().defaultTo(0);
table.string('name').notNull();
table.json('meta').notNull();
});
})
.then(() => {
logger.info('[' + migrate_name + '] access_list Table created');
return knex.schema.createTable('access_list_auth', table => {
table.increments().primary();
table.dateTime('created_on').notNull();
table.dateTime('modified_on').notNull();
table.integer('access_list_id').notNull().unsigned();
table.string('username').notNull();
table.string('password').notNull();
table.json('meta').notNull();
});
})
.then(() => {
logger.info('[' + migrate_name + '] access_list_auth Table created');
});
};

View File

@ -3,8 +3,9 @@
'use strict';
const db = require('../db');
const Model = require('objection').Model;
const db = require('../db');
const Model = require('objection').Model;
const UserPermission = require('./user_permission');
Model.knex(db);
@ -30,6 +31,22 @@ class User extends Model {
return ['roles'];
}
static get relationMappings () {
return {
permissions: {
relation: Model.HasOneRelation,
modelClass: UserPermission,
join: {
from: 'user.id',
to: 'user_permission.user_id'
},
modify: function (qb) {
qb.omit(['id', 'created_on', 'modified_on', 'user_id']);
}
}
};
}
}
module.exports = User;

View File

@ -0,0 +1,30 @@
// Objection Docs:
// http://vincit.github.io/objection.js/
'use strict';
const db = require('../db');
const Model = require('objection').Model;
Model.knex(db);
class UserPermission extends Model {
$beforeInsert () {
this.created_on = Model.raw('NOW()');
this.modified_on = Model.raw('NOW()');
}
$beforeUpdate () {
this.modified_on = Model.raw('NOW()');
}
static get name () {
return 'UserPermission';
}
static get tableName () {
return 'user_permission';
}
}
module.exports = UserPermission;

View File

@ -183,12 +183,12 @@ router
});
/**
* Specific user service settings
* Specific user permissions
*
* /api/users/123/services
* /api/users/123/permissions
*/
router
.route('/:user_id/services')
.route('/:user_id/permissions')
.options((req, res) => {
res.sendStatus(204);
})
@ -196,18 +196,18 @@ router
.all(userIdFromMe)
/**
* POST /api/users/123/services
* PUT /api/users/123/permissions
*
* Sets Service Settings for a user
* Set some or all permissions for a user
*/
.post((req, res, next) => {
.put((req, res, next) => {
apiValidator({$ref: 'endpoints/users#/links/5/schema'}, req.body)
.then(payload => {
payload.id = req.params.user_id;
return internalUser.setServiceSettings(res.locals.access, payload);
return internalUser.setPermissions(res.locals.access, payload);
})
.then(result => {
res.status(200)
res.status(201)
.send(result);
})
.catch(next);

View File

@ -206,6 +206,49 @@
"targetSchema": {
"type": "boolean"
}
},
{
"title": "Set Permissions",
"description": "Sets Permissions for a User",
"href": "/users/{definitions.identity.example}/permissions",
"access": "private",
"method": "PUT",
"rel": "update",
"http_header": {
"$ref": "../examples.json#/definitions/auth_header"
},
"schema": {
"type": "object",
"properties": {
"visibility": {
"type": "string",
"pattern": "^(all|user)$"
},
"access_lists": {
"type": "string",
"pattern": "^(hidden|view|manage)$"
},
"dead_hosts": {
"type": "string",
"pattern": "^(hidden|view|manage)$"
},
"proxy_hosts": {
"type": "string",
"pattern": "^(hidden|view|manage)$"
},
"redirection_hosts": {
"type": "string",
"pattern": "^(hidden|view|manage)$"
},
"streams": {
"type": "string",
"pattern": "^(hidden|view|manage)$"
}
}
},
"targetSchema": {
"type": "boolean"
}
}
],
"properties": {

View File

@ -1,11 +1,12 @@
'use strict';
const fs = require('fs');
const NodeRSA = require('node-rsa');
const config = require('config');
const logger = require('./logger').global;
const userModel = require('./models/user');
const authModel = require('./models/auth');
const fs = require('fs');
const NodeRSA = require('node-rsa');
const config = require('config');
const logger = require('./logger').global;
const userModel = require('./models/user');
const userPermissionModel = require('./models/user_permission');
const authModel = require('./models/auth');
module.exports = function () {
return new Promise((resolve, reject) => {
@ -54,7 +55,7 @@ module.exports = function () {
.select(userModel.raw('COUNT(`id`) as `count`'))
.where('is_deleted', 0)
.first('count')
.then((row) => {
.then(row => {
if (!row.count) {
// Create a new user and set password
logger.info('Creating a new user: admin@example.com with password: changeme');
@ -79,6 +80,19 @@ module.exports = function () {
type: 'password',
secret: 'changeme',
meta: {}
})
.then(() => {
return userPermissionModel
.query()
.insert({
user_id: user.id,
visibility: 'all',
proxy_hosts: 'manage',
redirection_hosts: 'manage',
dead_hosts: 'manage',
streams: 'manage',
access_lists: 'manage'
});
});
});
}

View File

@ -224,6 +224,16 @@ module.exports = {
*/
loginAs: function (id) {
return fetch('post', 'users/' + id + '/login');
},
/**
*
* @param {Integer} id
* @param {Object} perms
* @returns {Promise}
*/
setPermissions: function (id, perms) {
return fetch('put', 'users/' + id + '/permissions', perms);
}
},

View File

@ -52,6 +52,19 @@ module.exports = {
}
},
/**
* User Permissions Form
*
* @param model
*/
showUserPermissions: function (model) {
if (Cache.User.isAdmin()) {
require(['./main', './user/permissions'], function (App, View) {
App.UI.showModalDialog(new View({model: model}));
});
}
},
/**
* User Password Form
*
@ -65,6 +78,19 @@ module.exports = {
}
},
/**
* User Delete Confirm
*
* @param model
*/
showUserDeleteConfirm: function (model) {
if (Cache.User.isAdmin() && model.get('id') !== Cache.User.get('id')) {
require(['./main', './user/delete'], function (App, View) {
App.UI.showModalDialog(new View({model: model}));
});
}
},
/**
* Error
*

View File

@ -110,7 +110,7 @@ const App = Mn.Application.extend({
* @returns {Promise}
*/
bootstrap: function () {
return Api.Users.getById('me')
return Api.Users.getById('me', ['permissions'])
.then(response => {
Cache.User.set(response);
Tokens.setCurrentName(response.nickname || response.name);

View File

@ -0,0 +1,19 @@
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Delete <%- name %></h5>
<button type="button" class="close cancel" aria-label="Close" data-dismiss="modal">&nbsp;</button>
</div>
<div class="modal-body">
<form>
<div class="row">
<div class="col-sm-12 col-md-12">
Are you sure you want to delete <strong><%- name %></strong>?
</div>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary cancel" data-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-danger save">Yes I'm Sure</button>
</div>
</div>

View File

@ -0,0 +1,38 @@
'use strict';
const Mn = require('backbone.marionette');
const template = require('./delete.ejs');
const Controller = require('../controller');
const Api = require('../api');
const App = require('../main');
require('jquery-serializejson');
module.exports = Mn.View.extend({
template: template,
className: 'modal-dialog',
ui: {
form: 'form',
buttons: '.modal-footer button',
cancel: 'button.cancel',
save: 'button.save'
},
events: {
'click @ui.save': function (e) {
e.preventDefault();
Api.Users.delete(this.model.get('id'))
.then(() => {
Controller.showUsers();
App.UI.closeModal();
})
.catch(err => {
alert(err.message);
this.ui.buttons.prop('disabled', false).removeClass('btn-disabled');
});
}
}
});

View File

@ -25,25 +25,27 @@
<div class="invalid-feedback secret-error"></div>
</div>
</div>
<% if (!isSelf()) { %>
<div class="col-sm-12 col-md-12">
<div class="form-label">Roles</div>
</div>
<div class="col-sm-6 col-md-6">
<div class="form-group">
<div class="form-label">Switches</div>
<div class="custom-switches-stacked">
<label class="custom-switch">
<input type="checkbox" class="custom-switch-input" name="is_admin" value="1"<%- isAdmin() ? ' checked' : '' %>>
<span class="custom-switch-indicator"></span>
<span class="custom-switch-description">Administrator</span>
</label>
<label class="custom-switch">
<input type="checkbox" class="custom-switch-input" name="is_disabled" value="1"<%- is_disabled ? ' checked' : '' %>>
<span class="custom-switch-indicator"></span>
<span class="custom-switch-description">Disabled</span>
</label>
</div>
<label class="custom-switch">
<input type="checkbox" class="custom-switch-input" name="is_admin" value="1"<%- isAdmin() ? ' checked' : '' %><%- isSelf() ? ' disabled' : '' %>>
<span class="custom-switch-indicator"></span>
<span class="custom-switch-description">Administrator</span>
</label>
</div>
</div>
<div class="col-sm-6 col-md-6">
<div class="form-group">
<label class="custom-switch">
<input type="checkbox" class="custom-switch-input" name="is_disabled" value="1"<%- is_disabled ? ' checked' : '' %><%- isSelf() ? ' disabled' : '' %>>
<span class="custom-switch-indicator"></span>
<span class="custom-switch-description">Disabled</span>
</label>
</div>
</div>
<% } %>
</div>
</form>
</div>

View File

@ -58,7 +58,12 @@ module.exports = Mn.View.extend({
}
view.model.set(result);
App.UI.closeModal();
App.UI.closeModal(function () {
if (method === Api.Users.create) {
// Show permissions dialog immediately
Controller.showUserPermissions(view.model);
}
});
})
.catch(err => {
this.ui.error.text(err.message).show();

View File

@ -0,0 +1,140 @@
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Permissions for <%- name %></h5>
<button type="button" class="close cancel" aria-label="Close" data-dismiss="modal">&nbsp;</button>
</div>
<div class="modal-body">
<form>
<div class="row">
<div class="col-sm-12 col-md-12">
<% if (isAdmin()) { %>
<div class="alert alert-icon alert-secondary" role="alert">
<i class="fe fe-alert-triangle mr-2" aria-hidden="true"></i>
This user is an Administrator and some items cannot be altered
</div>
<% } %>
<div class="form-group">
<label class="form-label">Item Visibility</label>
<div class="selectgroup w-100">
<label class="selectgroup-item">
<input type="radio" name="visibility" value="user" class="selectgroup-input"<%- getPerm('visibility') !== 'all' ? ' checked' : '' %>>
<span class="selectgroup-button">Created Items Only</span>
</label>
<label class="selectgroup-item">
<input type="radio" name="visibility" value="all" class="selectgroup-input"<%- getPerm('visibility') === 'all' ? ' checked' : '' %>>
<span class="selectgroup-button">All Items</span>
</label>
</div>
</div>
</div>
<div class="col-sm-12 col-md-12">
<div class="form-group">
<label class="form-label">Proxy Hosts</label>
<div class="selectgroup w-100">
<label class="selectgroup-item">
<input type="radio" name="proxy_hosts" value="manage" class="selectgroup-input" <%- getPermProps('proxy_hosts', 'manage', true) %>>
<span class="selectgroup-button">Manage</span>
</label>
<label class="selectgroup-item">
<input type="radio" name="proxy_hosts" value="view" class="selectgroup-input" <%- getPermProps('proxy_hosts', 'view') %>>
<span class="selectgroup-button">View Only</span>
</label>
<label class="selectgroup-item">
<input type="radio" name="proxy_hosts" value="hidden" class="selectgroup-input" <%- getPermProps('proxy_hosts', 'hidden') %>>
<span class="selectgroup-button">Hidden</span>
</label>
</div>
</div>
</div>
<div class="col-sm-12 col-md-12">
<div class="form-group">
<label class="form-label">Redirection Hosts</label>
<div class="selectgroup w-100">
<label class="selectgroup-item">
<input type="radio" name="redirection_hosts" value="manage" class="selectgroup-input" <%- getPermProps('redirection_hosts', 'manage', true) %>>
<span class="selectgroup-button">Manage</span>
</label>
<label class="selectgroup-item">
<input type="radio" name="redirection_hosts" value="view" class="selectgroup-input" <%- getPermProps('redirection_hosts', 'view') %>>
<span class="selectgroup-button">View Only</span>
</label>
<label class="selectgroup-item">
<input type="radio" name="redirection_hosts" value="hidden" class="selectgroup-input" <%- getPermProps('redirection_hosts', 'hidden') %>>
<span class="selectgroup-button">Hidden</span>
</label>
</div>
</div>
</div>
<div class="col-sm-12 col-md-12">
<div class="form-group">
<label class="form-label">404 Hosts</label>
<div class="selectgroup w-100">
<label class="selectgroup-item">
<input type="radio" name="dead_hosts" value="manage" class="selectgroup-input" <%- getPermProps('dead_hosts', 'manage', true) %>>
<span class="selectgroup-button">Manage</span>
</label>
<label class="selectgroup-item">
<input type="radio" name="dead_hosts" value="view" class="selectgroup-input" <%- getPermProps('dead_hosts', 'view') %>>
<span class="selectgroup-button">View Only</span>
</label>
<label class="selectgroup-item">
<input type="radio" name="dead_hosts" value="hidden" class="selectgroup-input" <%- getPermProps('dead_hosts', 'hidden') %>>
<span class="selectgroup-button">Hidden</span>
</label>
</div>
</div>
</div>
<div class="col-sm-12 col-md-12">
<div class="form-group">
<label class="form-label">Streams</label>
<div class="selectgroup w-100">
<label class="selectgroup-item">
<input type="radio" name="streams" value="manage" class="selectgroup-input" <%- getPermProps('streams', 'manage', true) %>>
<span class="selectgroup-button">Manage</span>
</label>
<label class="selectgroup-item">
<input type="radio" name="streams" value="view" class="selectgroup-input" <%- getPermProps('streams', 'view') %>>
<span class="selectgroup-button">View Only</span>
</label>
<label class="selectgroup-item">
<input type="radio" name="streams" value="hidden" class="selectgroup-input" <%- getPermProps('streams', 'hidden') %>>
<span class="selectgroup-button">Hidden</span>
</label>
</div>
</div>
</div>
<div class="col-sm-12 col-md-12">
<div class="form-group">
<label class="form-label">Access Lists</label>
<div class="selectgroup w-100">
<label class="selectgroup-item">
<input type="radio" name="access_lists" value="manage" class="selectgroup-input" <%- getPermProps('access_lists', 'manage', true) %>>
<span class="selectgroup-button">Manage</span>
</label>
<label class="selectgroup-item">
<input type="radio" name="access_lists" value="view" class="selectgroup-input" <%- getPermProps('access_lists', 'view') %>>
<span class="selectgroup-button">View Only</span>
</label>
<label class="selectgroup-item">
<input type="radio" name="access_lists" value="hidden" class="selectgroup-input" <%- getPermProps('access_lists', 'hidden') %>>
<span class="selectgroup-button">Hidden</span>
</label>
</div>
</div>
</div>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary cancel" data-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-teal save">Save</button>
</div>
</div>

View File

@ -0,0 +1,99 @@
'use strict';
const Mn = require('backbone.marionette');
const template = require('./permissions.ejs');
const Controller = require('../controller');
const Cache = require('../cache');
const Api = require('../api');
const App = require('../main');
const UserModel = require('../../models/user');
require('jquery-serializejson');
module.exports = Mn.View.extend({
template: template,
className: 'modal-dialog',
ui: {
form: 'form',
buttons: '.modal-footer button',
cancel: 'button.cancel',
save: 'button.save',
error: '.secret-error'
},
events: {
'click @ui.save': function (e) {
e.preventDefault();
let view = this;
let data = this.ui.form.serializeJSON();
// Manipulate
if (view.model.isAdmin()) {
// Force some attributes for admin
data = _.assign({}, data, {
access_lists: 'manage',
dead_hosts: 'manage',
proxy_hosts: 'manage',
redirection_hosts: 'manage',
streams: 'manage'
});
}
this.ui.buttons.prop('disabled', true).addClass('btn-disabled');
Api.Users.setPermissions(view.model.get('id'), data)
.then(() => {
if (view.model.get('id') === Cache.User.get('id')) {
Cache.User.set({permissions: data});
}
view.model.set({permissions: data});
App.UI.closeModal();
})
.catch(err => {
this.ui.error.text(err.message).show();
this.ui.buttons.prop('disabled', false).removeClass('btn-disabled');
});
}
},
templateContext: function () {
let perms = this.model.get('permissions');
let is_admin = this.model.isAdmin();
return {
getPerm: function (key) {
if (perms !== null && typeof perms[key] !== 'undefined') {
return perms[key];
}
return null;
},
getPermProps: function (key, item, forced_admin) {
if (forced_admin && is_admin) {
return 'checked disabled';
} else if (is_admin) {
return 'disabled';
} else if (perms !== null && typeof perms[key] !== 'undefined' && perms[key] === item) {
return 'checked';
}
return '';
},
isAdmin: function () {
return is_admin;
}
};
},
initialize: function (options) {
if (typeof options.model === 'undefined' || !options.model) {
this.model = new UserModel.Model();
}
}
});

View File

@ -19,8 +19,9 @@
<div class="item-action dropdown">
<a href="#" data-toggle="dropdown" class="icon"><i class="fe fe-more-vertical"></i></a>
<div class="dropdown-menu dropdown-menu-right">
<a href="#" class="edit-user dropdown-item"><i class="dropdown-icon fe fe-edit"></i> Edit User</a>
<a href="#" class="set-password dropdown-item"><i class="dropdown-icon fe fe-shield"></i> Set Password</a>
<a href="#" class="edit-user dropdown-item"><i class="dropdown-icon fe fe-edit"></i> Edit Details</a>
<a href="#" class="edit-permissions dropdown-item"><i class="dropdown-icon fe fe-shield"></i> Edit Permissions</a>
<a href="#" class="set-password dropdown-item"><i class="dropdown-icon fe fe-lock"></i> Set Password</a>
<% if (!isSelf()) { %>
<a href="#" class="login dropdown-item"><i class="dropdown-icon fe fe-log-in"></i> Sign in as User</a>
<div class="dropdown-divider"></div>

View File

@ -12,10 +12,11 @@ module.exports = Mn.View.extend({
tagName: 'tr',
ui: {
edit: 'a.edit-user',
password: 'a.set-password',
login: 'a.login',
delete: 'a.delete-user'
edit: 'a.edit-user',
permissions: 'a.edit-permissions',
password: 'a.set-password',
login: 'a.login',
delete: 'a.delete-user'
},
events: {
@ -24,6 +25,11 @@ module.exports = Mn.View.extend({
Controller.showUserForm(this.model);
},
'click @ui.permissions': function (e) {
e.preventDefault();
Controller.showUserPermissions(this.model);
},
'click @ui.password': function (e) {
e.preventDefault();
Controller.showUserPasswordForm(this.model);
@ -31,7 +37,7 @@ module.exports = Mn.View.extend({
'click @ui.delete': function (e) {
e.preventDefault();
//Controller.showUserDeleteConfirm(this.model);
Controller.showUserDeleteConfirm(this.model);
},
'click @ui.login': function (e) {

View File

@ -31,7 +31,7 @@ module.exports = Mn.View.extend({
onRender: function () {
let view = this;
Api.Users.getAll()
Api.Users.getAll(['permissions'])
.then(response => {
if (!view.isDestroyed() && response && response.length) {
view.showChildView('list_region', new ListView({

View File

@ -12,7 +12,8 @@ const model = Backbone.Model.extend({
nickname: '',
email: '',
is_disabled: false,
roles: []
roles: [],
permissions: null
};
},

View File

@ -24,7 +24,8 @@ module.exports = {
'vector-map': 'tabler-ui/dist/assets/js/vendors/jquery-jvectormap-2.0.3.min',
'vector-map-de': 'tabler-ui/dist/assets/js/vendors/jquery-jvectormap-de-merc',
'vector-map-world': 'tabler-ui/dist/assets/js/vendors/jquery-jvectormap-world-mill',
'circle-progress': 'tabler-ui/dist/assets/js/vendors/circle-progress.min'
'circle-progress': 'tabler-ui/dist/assets/js/vendors/circle-progress.min',
'c3': 'tabler-ui/dist/assets/js/vendors/chart.bundle.min'
}
},
module: {