mirror of
				https://github.com/NginxProxyManager/nginx-proxy-manager.git
				synced 2025-10-30 23:33:34 +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. | ||||
|     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 | ||||
|   | ||||
| @@ -19,7 +19,7 @@ const internalUser = { | ||||
|      * @returns {Promise} | ||||
|      */ | ||||
|     create: (access, data) => { | ||||
|         let auth = data.auth; | ||||
|         let auth = data.auth || null; | ||||
|         delete data.auth; | ||||
|  | ||||
|         data.avatar = data.avatar || ''; | ||||
| @@ -38,21 +38,25 @@ const internalUser = { | ||||
|                     .omit(omissions()) | ||||
|                     .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 => { | ||||
|                 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  {Object}  data | ||||
|      * @param  {Integer} data.id | ||||
|      * @param  {String}  [data.email] | ||||
|      * @param  {String}  [data.name] | ||||
|      * @return {Promise} | ||||
|      */ | ||||
| @@ -337,17 +342,38 @@ const internalUser = { | ||||
|                 return user; | ||||
|             }) | ||||
|             .then(user => { | ||||
|                 // Get auth, patch if it exists | ||||
|                 return authModel | ||||
|                     .query() | ||||
|                     .where('user_id', user.id) | ||||
|                     .andWhere('type', data.type) | ||||
|                     .patch({ | ||||
|                         type:   data.type, | ||||
|                         secret: data.secret | ||||
|                     }) | ||||
|                     .then(() => { | ||||
|                         return true; | ||||
|                     .first() | ||||
|                     .then(existing_auth => { | ||||
|                         if (existing_auth) { | ||||
|                             // patch | ||||
|                             return authModel | ||||
|                                 .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 | ||||
|      * | ||||
|   | ||||
| @@ -2,59 +2,23 @@ | ||||
|     <div class="row align-items-center"> | ||||
|         <div class="col-lg order-lg-first"> | ||||
|             <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"> | ||||
|                     <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> | ||||
|                 <% if (showUsers()) { %> | ||||
|                 <li class="nav-item"> | ||||
|                     <a href="/users" class="nav-link"><i class="fe fe-users"></i> Users</a> | ||||
|                 </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> | ||||
|         </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> | ||||
|     <div><%- name %></div> | ||||
|     <div class="small text-muted"> | ||||
|         Created: Mar 19, 2018 | ||||
|         Created: <%- formatDbDate(created_on, 'Do MMMM YYYY') %> | ||||
|     </div> | ||||
| </td> | ||||
| <td> | ||||
|   | ||||
| @@ -26,7 +26,7 @@ module.exports = Mn.View.extend({ | ||||
|  | ||||
|         'click @ui.password': function (e) { | ||||
|             e.preventDefault(); | ||||
|             //Controller.showUserPasswordForm(this.model); | ||||
|             Controller.showUserPasswordForm(this.model); | ||||
|         }, | ||||
|  | ||||
|         'click @ui.delete': function (e) { | ||||
|   | ||||
| @@ -39,15 +39,21 @@ Mn.Renderer.render = function (template, data, view) { | ||||
|      * @param   {String} date | ||||
|      * @returns {String} | ||||
|      */ | ||||
|     data.shortDate = function (date) { | ||||
|         let shortdate = ''; | ||||
|  | ||||
|     data.formatDbDate = function (date, format) { | ||||
|         if (typeof date === 'number') { | ||||
|             shortdate = moment.unix(date).format('YYYY-MM-DD'); | ||||
|         } else { | ||||
|             shortdate = moment(date).format('YYYY-MM-DD'); | ||||
|             return moment.unix(date).format(format); | ||||
|         } | ||||
|  | ||||
|         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; | ||||
|     }; | ||||
|  | ||||
| @@ -56,15 +62,7 @@ Mn.Renderer.render = function (template, data, view) { | ||||
|      * @returns {String} | ||||
|      */ | ||||
|     data.shortTime = function (date) { | ||||
|         let shorttime = ''; | ||||
|  | ||||
|         if (typeof date === 'number') { | ||||
|             shorttime = moment.unix(date).format('H:mm A'); | ||||
|         } else { | ||||
|             shorttime = moment(date).format('H:mm A'); | ||||
|         } | ||||
|  | ||||
|         return shorttime; | ||||
|         return data.formatDbDate(date, 'H:mm A'); | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|   | ||||
| @@ -9,9 +9,7 @@ | ||||
|                         <input name="identity" type="email" class="form-control" placeholder="Enter email" required> | ||||
|                     </div> | ||||
|                     <div class="form-group"> | ||||
|                         <label class="form-label"> | ||||
|                             Password | ||||
|                         </label> | ||||
|                         <label class="form-label">Password</label> | ||||
|                         <input name="secret" type="password" class="form-control" placeholder="Password" required> | ||||
|                         <div class="invalid-feedback secret-error"></div> | ||||
|                     </div> | ||||
|   | ||||
		Reference in New Issue
	
	Block a user