mirror of
				https://github.com/NginxProxyManager/nginx-proxy-manager.git
				synced 2025-10-31 07:43:33 +00:00 
			
		
		
		
	Set password functionality
This commit is contained in:
		| @@ -84,7 +84,11 @@ app.use(function (err, req, res, next) { | |||||||
|  |  | ||||||
|     // Not every error is worth logging - but this is good for now until it gets annoying. |     // Not every error is worth logging - but this is good for now until it gets annoying. | ||||||
|     if (typeof err.stack !== 'undefined' && err.stack) { |     if (typeof err.stack !== 'undefined' && err.stack) { | ||||||
|         log.warn(err.stack); |         if (process.env.NODE_ENV === 'development') { | ||||||
|  |             log.warn(err.stack); | ||||||
|  |         } else { | ||||||
|  |             log.warn(err.message); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     res |     res | ||||||
|   | |||||||
| @@ -19,7 +19,7 @@ const internalUser = { | |||||||
|      * @returns {Promise} |      * @returns {Promise} | ||||||
|      */ |      */ | ||||||
|     create: (access, data) => { |     create: (access, data) => { | ||||||
|         let auth = data.auth; |         let auth = data.auth || null; | ||||||
|         delete data.auth; |         delete data.auth; | ||||||
|  |  | ||||||
|         data.avatar = data.avatar || ''; |         data.avatar = data.avatar || ''; | ||||||
| @@ -38,21 +38,25 @@ const internalUser = { | |||||||
|                     .omit(omissions()) |                     .omit(omissions()) | ||||||
|                     .insertAndFetch(data); |                     .insertAndFetch(data); | ||||||
|             }) |             }) | ||||||
|  |             .then(user => { | ||||||
|  |                 if (auth) { | ||||||
|  |                     return authModel | ||||||
|  |                         .query() | ||||||
|  |                         .insert({ | ||||||
|  |                             user_id: user.id, | ||||||
|  |                             type:    auth.type, | ||||||
|  |                             secret:  auth.secret, | ||||||
|  |                             meta:    {} | ||||||
|  |                         }) | ||||||
|  |                         .then(() => { | ||||||
|  |                             return user; | ||||||
|  |                         }); | ||||||
|  |                 } else { | ||||||
|  |                     return user; | ||||||
|  |                 } | ||||||
|  |             }) | ||||||
|             .then(user => { |             .then(user => { | ||||||
|                 return internalUser.get(access, {id: user.id}); |                 return internalUser.get(access, {id: user.id}); | ||||||
|                 /* |  | ||||||
|                 return authModel |  | ||||||
|                     .query() |  | ||||||
|                     .insert({ |  | ||||||
|                         user_id: user.id, |  | ||||||
|                         type:    auth.type, |  | ||||||
|                         secret:  auth.secret, |  | ||||||
|                         meta:    {} |  | ||||||
|                     }) |  | ||||||
|                     .then(() => { |  | ||||||
|                         return internalUser.get(access, {id: user.id}); |  | ||||||
|                     }); |  | ||||||
|                     */ |  | ||||||
|             }); |             }); | ||||||
|     }, |     }, | ||||||
|  |  | ||||||
| @@ -60,6 +64,7 @@ const internalUser = { | |||||||
|      * @param  {Access}  access |      * @param  {Access}  access | ||||||
|      * @param  {Object}  data |      * @param  {Object}  data | ||||||
|      * @param  {Integer} data.id |      * @param  {Integer} data.id | ||||||
|  |      * @param  {String}  [data.email] | ||||||
|      * @param  {String}  [data.name] |      * @param  {String}  [data.name] | ||||||
|      * @return {Promise} |      * @return {Promise} | ||||||
|      */ |      */ | ||||||
| @@ -337,17 +342,38 @@ const internalUser = { | |||||||
|                 return user; |                 return user; | ||||||
|             }) |             }) | ||||||
|             .then(user => { |             .then(user => { | ||||||
|  |                 // Get auth, patch if it exists | ||||||
|                 return authModel |                 return authModel | ||||||
|                     .query() |                     .query() | ||||||
|                     .where('user_id', user.id) |                     .where('user_id', user.id) | ||||||
|                     .andWhere('type', data.type) |                     .andWhere('type', data.type) | ||||||
|                     .patch({ |                     .first() | ||||||
|                         type:   data.type, |                     .then(existing_auth => { | ||||||
|                         secret: data.secret |                         if (existing_auth) { | ||||||
|                     }) |                             // patch | ||||||
|                     .then(() => { |                             return authModel | ||||||
|                         return true; |                                 .query() | ||||||
|  |                                 .where('user_id', user.id) | ||||||
|  |                                 .andWhere('type', data.type) | ||||||
|  |                                 .patch({ | ||||||
|  |                                     type:   data.type, // This is required for the model to encrypt on save | ||||||
|  |                                     secret: data.secret | ||||||
|  |                                 }); | ||||||
|  |                         } else { | ||||||
|  |                             // insert | ||||||
|  |                             return authModel | ||||||
|  |                                 .query() | ||||||
|  |                                 .insert({ | ||||||
|  |                                     user_id: user.id, | ||||||
|  |                                     type:    data.type, | ||||||
|  |                                     secret:  data.secret, | ||||||
|  |                                     meta:    {} | ||||||
|  |                                 }); | ||||||
|  |                         } | ||||||
|                     }); |                     }); | ||||||
|  |             }) | ||||||
|  |             .then(() => { | ||||||
|  |                 return true; | ||||||
|             }); |             }); | ||||||
|     }, |     }, | ||||||
|  |  | ||||||
|   | |||||||
| @@ -52,6 +52,19 @@ module.exports = { | |||||||
|         } |         } | ||||||
|     }, |     }, | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * User Password Form | ||||||
|  |      * | ||||||
|  |      * @param model | ||||||
|  |      */ | ||||||
|  |     showUserPasswordForm: function (model) { | ||||||
|  |         if (Cache.User.isAdmin() || model.get('id') === Cache.User.get('id')) { | ||||||
|  |             require(['./main', './user/password'], function (App, View) { | ||||||
|  |                 App.UI.showModalDialog(new View({model: model})); | ||||||
|  |             }); | ||||||
|  |         } | ||||||
|  |     }, | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Error |      * Error | ||||||
|      * |      * | ||||||
|   | |||||||
| @@ -2,59 +2,23 @@ | |||||||
|     <div class="row align-items-center"> |     <div class="row align-items-center"> | ||||||
|         <div class="col-lg order-lg-first"> |         <div class="col-lg order-lg-first"> | ||||||
|             <ul class="nav nav-tabs border-0 flex-column flex-lg-row"> |             <ul class="nav nav-tabs border-0 flex-column flex-lg-row"> | ||||||
|  |                 <li class="nav-item dropdown"> | ||||||
|  |                     <a href="#" class="nav-link" data-toggle="dropdown"><i class="fe fe-monitor"></i> Hosts</a> | ||||||
|  |                     <div class="dropdown-menu dropdown-menu-arrow"> | ||||||
|  |                         <a href="/nginx/proxy" class="dropdown-item ">Proxy Hosts</a> | ||||||
|  |                         <a href="/nginx/redirection" class="dropdown-item ">Redirections</a> | ||||||
|  |                         <a href="/nginx/stream" class="dropdown-item ">Streams</a> | ||||||
|  |                         <a href="/nginx/404" class="dropdown-item ">404 Hosts</a> | ||||||
|  |                     </div> | ||||||
|  |                 </li> | ||||||
|                 <li class="nav-item"> |                 <li class="nav-item"> | ||||||
|                     <a href="/" class="nav-link"><i class="fe fe-home"></i> Home</a> |                     <a href="/nginx/access" class="nav-link"><i class="fe fe-lock"></i> Access Lists</a> | ||||||
|                 </li> |                 </li> | ||||||
|                 <% if (showUsers()) { %> |                 <% if (showUsers()) { %> | ||||||
|                 <li class="nav-item"> |                 <li class="nav-item"> | ||||||
|                     <a href="/users" class="nav-link"><i class="fe fe-users"></i> Users</a> |                     <a href="/users" class="nav-link"><i class="fe fe-users"></i> Users</a> | ||||||
|                 </li> |                 </li> | ||||||
|                 <% } %> |                 <% } %> | ||||||
|                 <li class="nav-item"> |  | ||||||
|                     <a href="#" class="nav-link" data-toggle="dropdown"><i class="fe fe-box"></i> Interface</a> |  | ||||||
|                     <div class="dropdown-menu dropdown-menu-arrow"> |  | ||||||
|                         <a href="../cards.html" class="dropdown-item ">Cards design</a> |  | ||||||
|                         <a href="../charts.html" class="dropdown-item ">Charts</a> |  | ||||||
|                         <a href="../pricing-cards.html" class="dropdown-item ">Pricing cards</a> |  | ||||||
|                     </div> |  | ||||||
|                 </li> |  | ||||||
|                 <li class="nav-item dropdown"> |  | ||||||
|                     <a href="#" class="nav-link" data-toggle="dropdown"><i class="fe fe-calendar"></i> Components</a> |  | ||||||
|                     <div class="dropdown-menu dropdown-menu-arrow"> |  | ||||||
|                         <a href="../maps.html" class="dropdown-item ">Maps</a> |  | ||||||
|                         <a href="../icons.html" class="dropdown-item ">Icons</a> |  | ||||||
|                         <a href="../store.html" class="dropdown-item ">Store</a> |  | ||||||
|                         <a href="../blog.html" class="dropdown-item ">Blog</a> |  | ||||||
|                         <a href="../carousel.html" class="dropdown-item ">Carousel</a> |  | ||||||
|                     </div> |  | ||||||
|                 </li> |  | ||||||
|                 <li class="nav-item dropdown"> |  | ||||||
|                     <a href="#" class="nav-link" data-toggle="dropdown"><i class="fe fe-file"></i> Pages</a> |  | ||||||
|                     <div class="dropdown-menu dropdown-menu-arrow"> |  | ||||||
|                         <a href="../profile.html" class="dropdown-item ">Profile</a> |  | ||||||
|                         <a href="../login.html" class="dropdown-item ">Login</a> |  | ||||||
|                         <a href="../register.html" class="dropdown-item ">Register</a> |  | ||||||
|                         <a href="../forgot-password.html" class="dropdown-item ">Forgot password</a> |  | ||||||
|                         <a href="../400.html" class="dropdown-item ">400 error</a> |  | ||||||
|                         <a href="../401.html" class="dropdown-item ">401 error</a> |  | ||||||
|                         <a href="../403.html" class="dropdown-item ">403 error</a> |  | ||||||
|                         <a href="../404.html" class="dropdown-item ">404 error</a> |  | ||||||
|                         <a href="../500.html" class="dropdown-item ">500 error</a> |  | ||||||
|                         <a href="../503.html" class="dropdown-item ">503 error</a> |  | ||||||
|                         <a href="../email.html" class="dropdown-item ">Email</a> |  | ||||||
|                         <a href="../empty.html" class="dropdown-item ">Empty page</a> |  | ||||||
|                         <a href="../rtl.html" class="dropdown-item ">RTL mode</a> |  | ||||||
|                     </div> |  | ||||||
|                 </li> |  | ||||||
|                 <li class="nav-item dropdown"> |  | ||||||
|                     <a href="../form-elements.html" class="nav-link"><i class="fe fe-check-square"></i> Forms</a> |  | ||||||
|                 </li> |  | ||||||
|                 <li class="nav-item"> |  | ||||||
|                     <a href="../gallery.html" class="nav-link"><i class="fe fe-image"></i> Gallery</a> |  | ||||||
|                 </li> |  | ||||||
|                 <li class="nav-item"> |  | ||||||
|                     <a href="../docs/index.html" class="nav-link"><i class="fe fe-file-text"></i> Documentation</a> |  | ||||||
|                 </li> |  | ||||||
|             </ul> |             </ul> | ||||||
|         </div> |         </div> | ||||||
|     </div> |     </div> | ||||||
|   | |||||||
							
								
								
									
										30
									
								
								src/frontend/js/app/user/password.ejs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								src/frontend/js/app/user/password.ejs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,30 @@ | |||||||
|  | <div class="modal-content"> | ||||||
|  |     <div class="modal-header"> | ||||||
|  |         <h5 class="modal-title">Change Password <%- isSelf() ? '' : 'for' + name %></h5> | ||||||
|  |         <button type="button" class="close cancel" aria-label="Close" data-dismiss="modal"> </button> | ||||||
|  |     </div> | ||||||
|  |     <div class="modal-body"> | ||||||
|  |         <form> | ||||||
|  |             <% if (isSelf()) { %> | ||||||
|  |             <div class="form-group"> | ||||||
|  |                 <label class="form-label">Current Password</label> | ||||||
|  |                 <input type="password" name="current_password" class="form-control" placeholder="" minlength="8" required> | ||||||
|  |             </div> | ||||||
|  |             <% } %> | ||||||
|  |  | ||||||
|  |             <div class="form-group"> | ||||||
|  |                 <label class="form-label">New Password</label> | ||||||
|  |                 <input type="password" name="new_password1" class="form-control" placeholder="" minlength="8" required> | ||||||
|  |                 <div class="invalid-feedback secret-error"></div> | ||||||
|  |             </div> | ||||||
|  |             <div class="form-group"> | ||||||
|  |                 <label class="form-label">Confirm Password</label> | ||||||
|  |                 <input type="password" name="new_password2" class="form-control" placeholder="" minlength="8" required> | ||||||
|  |             </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> | ||||||
							
								
								
									
										63
									
								
								src/frontend/js/app/user/password.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								src/frontend/js/app/user/password.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,63 @@ | |||||||
|  | 'use strict'; | ||||||
|  |  | ||||||
|  | const Mn         = require('backbone.marionette'); | ||||||
|  | const template   = require('./password.ejs'); | ||||||
|  | const Controller = require('../controller'); | ||||||
|  | const Api        = require('../api'); | ||||||
|  | const App        = require('../main'); | ||||||
|  | const Cache      = require('../cache'); | ||||||
|  |  | ||||||
|  | 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(); | ||||||
|  |             this.ui.error.hide(); | ||||||
|  |             let form = this.ui.form.serializeJSON(); | ||||||
|  |  | ||||||
|  |             if (form.new_password1 !== form.new_password2) { | ||||||
|  |                 this.ui.error.text('Passwords do not match!').show(); | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             let data = { | ||||||
|  |                 type:    'password', | ||||||
|  |                 current: form.current_password, | ||||||
|  |                 secret:  form.new_password1 | ||||||
|  |             }; | ||||||
|  |  | ||||||
|  |             this.ui.buttons.prop('disabled', true).addClass('btn-disabled'); | ||||||
|  |             Api.Users.setPassword(this.model.get('id'), data) | ||||||
|  |                 .then(() => { | ||||||
|  |                     App.UI.closeModal(); | ||||||
|  |                     Controller.showUsers(); | ||||||
|  |                 }) | ||||||
|  |                 .catch(err => { | ||||||
|  |                     this.ui.error.text(err.message).show(); | ||||||
|  |                     this.ui.buttons.prop('disabled', false).removeClass('btn-disabled'); | ||||||
|  |                 }); | ||||||
|  |         } | ||||||
|  |     }, | ||||||
|  |  | ||||||
|  |     isSelf: function () { | ||||||
|  |         return Cache.User.get('id') === this.model.get('id'); | ||||||
|  |     }, | ||||||
|  |  | ||||||
|  |     templateContext: function () { | ||||||
|  |         return { | ||||||
|  |             isSelf: this.isSelf.bind(this) | ||||||
|  |         }; | ||||||
|  |     } | ||||||
|  | }); | ||||||
| @@ -6,7 +6,7 @@ | |||||||
| <td> | <td> | ||||||
|     <div><%- name %></div> |     <div><%- name %></div> | ||||||
|     <div class="small text-muted"> |     <div class="small text-muted"> | ||||||
|         Created: Mar 19, 2018 |         Created: <%- formatDbDate(created_on, 'Do MMMM YYYY') %> | ||||||
|     </div> |     </div> | ||||||
| </td> | </td> | ||||||
| <td> | <td> | ||||||
|   | |||||||
| @@ -26,7 +26,7 @@ module.exports = Mn.View.extend({ | |||||||
|  |  | ||||||
|         'click @ui.password': function (e) { |         'click @ui.password': function (e) { | ||||||
|             e.preventDefault(); |             e.preventDefault(); | ||||||
|             //Controller.showUserPasswordForm(this.model); |             Controller.showUserPasswordForm(this.model); | ||||||
|         }, |         }, | ||||||
|  |  | ||||||
|         'click @ui.delete': function (e) { |         'click @ui.delete': function (e) { | ||||||
|   | |||||||
| @@ -39,15 +39,21 @@ Mn.Renderer.render = function (template, data, view) { | |||||||
|      * @param   {String} date |      * @param   {String} date | ||||||
|      * @returns {String} |      * @returns {String} | ||||||
|      */ |      */ | ||||||
|     data.shortDate = function (date) { |     data.formatDbDate = function (date, format) { | ||||||
|         let shortdate = ''; |  | ||||||
|  |  | ||||||
|         if (typeof date === 'number') { |         if (typeof date === 'number') { | ||||||
|             shortdate = moment.unix(date).format('YYYY-MM-DD'); |             return moment.unix(date).format(format); | ||||||
|         } else { |  | ||||||
|             shortdate = moment(date).format('YYYY-MM-DD'); |  | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         return moment(date).format(format); | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * @param   {String} date | ||||||
|  |      * @returns {String} | ||||||
|  |      */ | ||||||
|  |     data.shortDate = function (date) { | ||||||
|  |         let shortdate = data.formatDbDate(date, 'YYYY-MM-DD'); | ||||||
|  |  | ||||||
|         return moment().format('YYYY-MM-DD') === shortdate ? 'Today' : shortdate; |         return moment().format('YYYY-MM-DD') === shortdate ? 'Today' : shortdate; | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
| @@ -56,15 +62,7 @@ Mn.Renderer.render = function (template, data, view) { | |||||||
|      * @returns {String} |      * @returns {String} | ||||||
|      */ |      */ | ||||||
|     data.shortTime = function (date) { |     data.shortTime = function (date) { | ||||||
|         let shorttime = ''; |         return data.formatDbDate(date, 'H:mm A'); | ||||||
|  |  | ||||||
|         if (typeof date === 'number') { |  | ||||||
|             shorttime = moment.unix(date).format('H:mm A'); |  | ||||||
|         } else { |  | ||||||
|             shorttime = moment(date).format('H:mm A'); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         return shorttime; |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|   | |||||||
| @@ -9,9 +9,7 @@ | |||||||
|                         <input name="identity" type="email" class="form-control" placeholder="Enter email" required> |                         <input name="identity" type="email" class="form-control" placeholder="Enter email" required> | ||||||
|                     </div> |                     </div> | ||||||
|                     <div class="form-group"> |                     <div class="form-group"> | ||||||
|                         <label class="form-label"> |                         <label class="form-label">Password</label> | ||||||
|                             Password |  | ||||||
|                         </label> |  | ||||||
|                         <input name="secret" type="password" class="form-control" placeholder="Password" required> |                         <input name="secret" type="password" class="form-control" placeholder="Password" required> | ||||||
|                         <div class="invalid-feedback secret-error"></div> |                         <div class="invalid-feedback secret-error"></div> | ||||||
|                     </div> |                     </div> | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user