Set password functionality

This commit is contained in:
Jamie Curnow 2018-06-25 16:56:13 +10:00
parent 446921111e
commit 493bb77169
10 changed files with 183 additions and 87 deletions

View File

@ -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) {
if (process.env.NODE_ENV === 'development') {
log.warn(err.stack); log.warn(err.stack);
} else {
log.warn(err.message);
}
} }
res res

View File

@ -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 || '';
@ -39,8 +39,7 @@ const internalUser = {
.insertAndFetch(data); .insertAndFetch(data);
}) })
.then(user => { .then(user => {
return internalUser.get(access, {id: user.id}); if (auth) {
/*
return authModel return authModel
.query() .query()
.insert({ .insert({
@ -50,9 +49,14 @@ const internalUser = {
meta: {} meta: {}
}) })
.then(() => { .then(() => {
return internalUser.get(access, {id: user.id}); return user;
}); });
*/ } else {
return user;
}
})
.then(user => {
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,18 +342,39 @@ const internalUser = {
return user; return user;
}) })
.then(user => { .then(user => {
// Get auth, patch if it exists
return authModel
.query()
.where('user_id', user.id)
.andWhere('type', data.type)
.first()
.then(existing_auth => {
if (existing_auth) {
// patch
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({ .patch({
type: data.type, type: data.type, // This is required for the model to encrypt on save
secret: data.secret secret: data.secret
});
} else {
// insert
return authModel
.query()
.insert({
user_id: user.id,
type: data.type,
secret: data.secret,
meta: {}
});
}
});
}) })
.then(() => { .then(() => {
return true; return true;
}); });
});
}, },
/** /**

View File

@ -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
* *

View File

@ -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>

View 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">&nbsp;</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>

View 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)
};
}
});

View File

@ -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>

View File

@ -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) {

View File

@ -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;
}; };
/** /**

View File

@ -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>