mirror of
https://github.com/xiaoxinpro/nginx-proxy-manager-zh.git
synced 2025-01-22 21:08:13 -05:00
Added user permissions, delete user
This commit is contained in:
parent
4a59ef9925
commit
30924a6922
@ -1,11 +1,12 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
const error = require('../lib/error');
|
const error = require('../lib/error');
|
||||||
const userModel = require('../models/user');
|
const userModel = require('../models/user');
|
||||||
const authModel = require('../models/auth');
|
const userPermissionModel = require('../models/user_permission');
|
||||||
const gravatar = require('gravatar');
|
const authModel = require('../models/auth');
|
||||||
const internalToken = require('./token');
|
const gravatar = require('gravatar');
|
||||||
|
const internalToken = require('./token');
|
||||||
|
|
||||||
function omissions () {
|
function omissions () {
|
||||||
return ['is_deleted'];
|
return ['is_deleted'];
|
||||||
@ -56,7 +57,23 @@ const internalUser = {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.then(user => {
|
.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()
|
.query()
|
||||||
.where('is_deleted', 0)
|
.where('is_deleted', 0)
|
||||||
.andWhere('id', data.id)
|
.andWhere('id', data.id)
|
||||||
|
.allowEager('[permissions]')
|
||||||
.first();
|
.first();
|
||||||
|
|
||||||
// Custom omissions
|
// 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 {Access} access
|
||||||
* @param {Object} data
|
* @param {Object} data
|
||||||
|
7
src/backend/lib/access/users-permissions.json
Normal file
7
src/backend/lib/access/users-permissions.json
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"$ref": "roles#/definitions/admin"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -43,6 +43,124 @@ exports.up = function (knex/*, Promise*/) {
|
|||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
logger.info('[' + migrate_name + '] user Table created');
|
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');
|
||||||
});
|
});
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -3,8 +3,9 @@
|
|||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const db = require('../db');
|
const db = require('../db');
|
||||||
const Model = require('objection').Model;
|
const Model = require('objection').Model;
|
||||||
|
const UserPermission = require('./user_permission');
|
||||||
|
|
||||||
Model.knex(db);
|
Model.knex(db);
|
||||||
|
|
||||||
@ -30,6 +31,22 @@ class User extends Model {
|
|||||||
return ['roles'];
|
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;
|
module.exports = User;
|
||||||
|
30
src/backend/models/user_permission.js
Normal file
30
src/backend/models/user_permission.js
Normal 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;
|
@ -183,12 +183,12 @@ router
|
|||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specific user service settings
|
* Specific user permissions
|
||||||
*
|
*
|
||||||
* /api/users/123/services
|
* /api/users/123/permissions
|
||||||
*/
|
*/
|
||||||
router
|
router
|
||||||
.route('/:user_id/services')
|
.route('/:user_id/permissions')
|
||||||
.options((req, res) => {
|
.options((req, res) => {
|
||||||
res.sendStatus(204);
|
res.sendStatus(204);
|
||||||
})
|
})
|
||||||
@ -196,18 +196,18 @@ router
|
|||||||
.all(userIdFromMe)
|
.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)
|
apiValidator({$ref: 'endpoints/users#/links/5/schema'}, req.body)
|
||||||
.then(payload => {
|
.then(payload => {
|
||||||
payload.id = req.params.user_id;
|
payload.id = req.params.user_id;
|
||||||
return internalUser.setServiceSettings(res.locals.access, payload);
|
return internalUser.setPermissions(res.locals.access, payload);
|
||||||
})
|
})
|
||||||
.then(result => {
|
.then(result => {
|
||||||
res.status(200)
|
res.status(201)
|
||||||
.send(result);
|
.send(result);
|
||||||
})
|
})
|
||||||
.catch(next);
|
.catch(next);
|
||||||
|
@ -206,6 +206,49 @@
|
|||||||
"targetSchema": {
|
"targetSchema": {
|
||||||
"type": "boolean"
|
"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": {
|
"properties": {
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const NodeRSA = require('node-rsa');
|
const NodeRSA = require('node-rsa');
|
||||||
const config = require('config');
|
const config = require('config');
|
||||||
const logger = require('./logger').global;
|
const logger = require('./logger').global;
|
||||||
const userModel = require('./models/user');
|
const userModel = require('./models/user');
|
||||||
const authModel = require('./models/auth');
|
const userPermissionModel = require('./models/user_permission');
|
||||||
|
const authModel = require('./models/auth');
|
||||||
|
|
||||||
module.exports = function () {
|
module.exports = function () {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
@ -54,7 +55,7 @@ module.exports = function () {
|
|||||||
.select(userModel.raw('COUNT(`id`) as `count`'))
|
.select(userModel.raw('COUNT(`id`) as `count`'))
|
||||||
.where('is_deleted', 0)
|
.where('is_deleted', 0)
|
||||||
.first('count')
|
.first('count')
|
||||||
.then((row) => {
|
.then(row => {
|
||||||
if (!row.count) {
|
if (!row.count) {
|
||||||
// Create a new user and set password
|
// Create a new user and set password
|
||||||
logger.info('Creating a new user: admin@example.com with password: changeme');
|
logger.info('Creating a new user: admin@example.com with password: changeme');
|
||||||
@ -79,6 +80,19 @@ module.exports = function () {
|
|||||||
type: 'password',
|
type: 'password',
|
||||||
secret: 'changeme',
|
secret: 'changeme',
|
||||||
meta: {}
|
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'
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -224,6 +224,16 @@ module.exports = {
|
|||||||
*/
|
*/
|
||||||
loginAs: function (id) {
|
loginAs: function (id) {
|
||||||
return fetch('post', 'users/' + id + '/login');
|
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);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -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
|
* 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
|
* Error
|
||||||
*
|
*
|
||||||
|
@ -110,7 +110,7 @@ const App = Mn.Application.extend({
|
|||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
bootstrap: function () {
|
bootstrap: function () {
|
||||||
return Api.Users.getById('me')
|
return Api.Users.getById('me', ['permissions'])
|
||||||
.then(response => {
|
.then(response => {
|
||||||
Cache.User.set(response);
|
Cache.User.set(response);
|
||||||
Tokens.setCurrentName(response.nickname || response.name);
|
Tokens.setCurrentName(response.nickname || response.name);
|
||||||
|
19
src/frontend/js/app/user/delete.ejs
Normal file
19
src/frontend/js/app/user/delete.ejs
Normal 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"> </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>
|
38
src/frontend/js/app/user/delete.js
Normal file
38
src/frontend/js/app/user/delete.js
Normal 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');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
@ -25,25 +25,27 @@
|
|||||||
<div class="invalid-feedback secret-error"></div>
|
<div class="invalid-feedback secret-error"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<% if (!isSelf()) { %>
|
|
||||||
<div class="col-sm-12 col-md-12">
|
<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-group">
|
||||||
<div class="form-label">Switches</div>
|
<label class="custom-switch">
|
||||||
<div class="custom-switches-stacked">
|
<input type="checkbox" class="custom-switch-input" name="is_admin" value="1"<%- isAdmin() ? ' checked' : '' %><%- isSelf() ? ' disabled' : '' %>>
|
||||||
<label class="custom-switch">
|
<span class="custom-switch-indicator"></span>
|
||||||
<input type="checkbox" class="custom-switch-input" name="is_admin" value="1"<%- isAdmin() ? ' checked' : '' %>>
|
<span class="custom-switch-description">Administrator</span>
|
||||||
<span class="custom-switch-indicator"></span>
|
</label>
|
||||||
<span class="custom-switch-description">Administrator</span>
|
</div>
|
||||||
</label>
|
</div>
|
||||||
<label class="custom-switch">
|
<div class="col-sm-6 col-md-6">
|
||||||
<input type="checkbox" class="custom-switch-input" name="is_disabled" value="1"<%- is_disabled ? ' checked' : '' %>>
|
<div class="form-group">
|
||||||
<span class="custom-switch-indicator"></span>
|
<label class="custom-switch">
|
||||||
<span class="custom-switch-description">Disabled</span>
|
<input type="checkbox" class="custom-switch-input" name="is_disabled" value="1"<%- is_disabled ? ' checked' : '' %><%- isSelf() ? ' disabled' : '' %>>
|
||||||
</label>
|
<span class="custom-switch-indicator"></span>
|
||||||
</div>
|
<span class="custom-switch-description">Disabled</span>
|
||||||
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<% } %>
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
@ -58,7 +58,12 @@ module.exports = Mn.View.extend({
|
|||||||
}
|
}
|
||||||
|
|
||||||
view.model.set(result);
|
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 => {
|
.catch(err => {
|
||||||
this.ui.error.text(err.message).show();
|
this.ui.error.text(err.message).show();
|
||||||
|
140
src/frontend/js/app/user/permissions.ejs
Normal file
140
src/frontend/js/app/user/permissions.ejs
Normal 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"> </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>
|
99
src/frontend/js/app/user/permissions.js
Normal file
99
src/frontend/js/app/user/permissions.js
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
@ -19,8 +19,9 @@
|
|||||||
<div class="item-action dropdown">
|
<div class="item-action dropdown">
|
||||||
<a href="#" data-toggle="dropdown" class="icon"><i class="fe fe-more-vertical"></i></a>
|
<a href="#" data-toggle="dropdown" class="icon"><i class="fe fe-more-vertical"></i></a>
|
||||||
<div class="dropdown-menu dropdown-menu-right">
|
<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="edit-user dropdown-item"><i class="dropdown-icon fe fe-edit"></i> Edit Details</a>
|
||||||
<a href="#" class="set-password dropdown-item"><i class="dropdown-icon fe fe-shield"></i> Set Password</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()) { %>
|
<% if (!isSelf()) { %>
|
||||||
<a href="#" class="login dropdown-item"><i class="dropdown-icon fe fe-log-in"></i> Sign in as User</a>
|
<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>
|
<div class="dropdown-divider"></div>
|
||||||
|
@ -12,10 +12,11 @@ module.exports = Mn.View.extend({
|
|||||||
tagName: 'tr',
|
tagName: 'tr',
|
||||||
|
|
||||||
ui: {
|
ui: {
|
||||||
edit: 'a.edit-user',
|
edit: 'a.edit-user',
|
||||||
password: 'a.set-password',
|
permissions: 'a.edit-permissions',
|
||||||
login: 'a.login',
|
password: 'a.set-password',
|
||||||
delete: 'a.delete-user'
|
login: 'a.login',
|
||||||
|
delete: 'a.delete-user'
|
||||||
},
|
},
|
||||||
|
|
||||||
events: {
|
events: {
|
||||||
@ -24,6 +25,11 @@ module.exports = Mn.View.extend({
|
|||||||
Controller.showUserForm(this.model);
|
Controller.showUserForm(this.model);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
'click @ui.permissions': function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
Controller.showUserPermissions(this.model);
|
||||||
|
},
|
||||||
|
|
||||||
'click @ui.password': function (e) {
|
'click @ui.password': function (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
Controller.showUserPasswordForm(this.model);
|
Controller.showUserPasswordForm(this.model);
|
||||||
@ -31,7 +37,7 @@ module.exports = Mn.View.extend({
|
|||||||
|
|
||||||
'click @ui.delete': function (e) {
|
'click @ui.delete': function (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
//Controller.showUserDeleteConfirm(this.model);
|
Controller.showUserDeleteConfirm(this.model);
|
||||||
},
|
},
|
||||||
|
|
||||||
'click @ui.login': function (e) {
|
'click @ui.login': function (e) {
|
||||||
|
@ -31,7 +31,7 @@ module.exports = Mn.View.extend({
|
|||||||
onRender: function () {
|
onRender: function () {
|
||||||
let view = this;
|
let view = this;
|
||||||
|
|
||||||
Api.Users.getAll()
|
Api.Users.getAll(['permissions'])
|
||||||
.then(response => {
|
.then(response => {
|
||||||
if (!view.isDestroyed() && response && response.length) {
|
if (!view.isDestroyed() && response && response.length) {
|
||||||
view.showChildView('list_region', new ListView({
|
view.showChildView('list_region', new ListView({
|
||||||
|
@ -12,7 +12,8 @@ const model = Backbone.Model.extend({
|
|||||||
nickname: '',
|
nickname: '',
|
||||||
email: '',
|
email: '',
|
||||||
is_disabled: false,
|
is_disabled: false,
|
||||||
roles: []
|
roles: [],
|
||||||
|
permissions: null
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -24,7 +24,8 @@ module.exports = {
|
|||||||
'vector-map': 'tabler-ui/dist/assets/js/vendors/jquery-jvectormap-2.0.3.min',
|
'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-de': 'tabler-ui/dist/assets/js/vendors/jquery-jvectormap-de-merc',
|
||||||
'vector-map-world': 'tabler-ui/dist/assets/js/vendors/jquery-jvectormap-world-mill',
|
'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: {
|
module: {
|
||||||
|
Loading…
Reference in New Issue
Block a user