mirror of
				https://github.com/NginxProxyManager/nginx-proxy-manager.git
				synced 2025-10-30 23:33:34 +00:00 
			
		
		
		
	Added user permissions, delete user
This commit is contained in:
		| @@ -3,6 +3,7 @@ | ||||
| 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'); | ||||
| @@ -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 | ||||
|   | ||||
							
								
								
									
										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(() => { | ||||
|             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'); | ||||
|         }); | ||||
|  | ||||
| }; | ||||
|   | ||||
| @@ -5,6 +5,7 @@ | ||||
|  | ||||
| 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; | ||||
|   | ||||
							
								
								
									
										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 | ||||
|     .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); | ||||
|   | ||||
| @@ -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": { | ||||
|   | ||||
| @@ -5,6 +5,7 @@ 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 () { | ||||
| @@ -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' | ||||
|                                             }); | ||||
|                                     }); | ||||
|                             }); | ||||
|                     } | ||||
|   | ||||
| @@ -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); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|   | ||||
| @@ -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 | ||||
|      * | ||||
|   | ||||
| @@ -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); | ||||
|   | ||||
							
								
								
									
										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,26 +25,28 @@ | ||||
|                         <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' : '' %>> | ||||
|                             <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' : '' %>> | ||||
|                             <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> | ||||
|                 <% } %> | ||||
|             </div> | ||||
|         </form> | ||||
|     </div> | ||||
|     <div class="modal-footer"> | ||||
|   | ||||
| @@ -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(); | ||||
|   | ||||
							
								
								
									
										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"> | ||||
|         <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> | ||||
|   | ||||
| @@ -13,6 +13,7 @@ module.exports = Mn.View.extend({ | ||||
|  | ||||
|     ui: { | ||||
|         edit:        'a.edit-user', | ||||
|         permissions: 'a.edit-permissions', | ||||
|         password:    'a.set-password', | ||||
|         login:       'a.login', | ||||
|         delete:      'a.delete-user' | ||||
| @@ -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) { | ||||
|   | ||||
| @@ -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({ | ||||
|   | ||||
| @@ -12,7 +12,8 @@ const model = Backbone.Model.extend({ | ||||
|             nickname:    '', | ||||
|             email:       '', | ||||
|             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-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:    { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user