Преглед изворни кода

Set password functionality

Jamie Curnow пре 7 година
родитељ
комит
493bb77169

+ 5 - 1
src/backend/app.js

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

+ 46 - 20
src/backend/internal/user.js

@@ -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;
             });
     },
 

+ 13 - 0
src/frontend/js/app/controller.js

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

+ 9 - 45
src/frontend/js/app/ui/menu/main.ejs

@@ -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">
-                    <a href="/" class="nav-link"><i class="fe fe-home"></i> Home</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>
+                    <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="../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>
+                        <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 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>
+                    <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="../docs/index.html" class="nav-link"><i class="fe fe-file-text"></i> Documentation</a>
+                    <a href="/users" class="nav-link"><i class="fe fe-users"></i> Users</a>
                 </li>
+                <% } %>
             </ul>
         </div>
     </div>

+ 30 - 0
src/frontend/js/app/user/password.ejs

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

+ 63 - 0
src/frontend/js/app/user/password.js

@@ -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)
+        };
+    }
+});

+ 1 - 1
src/frontend/js/app/users/list/item.ejs

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

+ 1 - 1
src/frontend/js/app/users/list/item.js

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

+ 13 - 15
src/frontend/js/lib/marionette.js

@@ -39,32 +39,30 @@ 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().format('YYYY-MM-DD') === shortdate ? 'Today' : shortdate;
+        return moment(date).format(format);
     };
 
     /**
      * @param   {String} date
      * @returns {String}
      */
-    data.shortTime = function (date) {
-        let shorttime = '';
+    data.shortDate = function (date) {
+        let shortdate = data.formatDbDate(date, 'YYYY-MM-DD');
 
-        if (typeof date === 'number') {
-            shorttime = moment.unix(date).format('H:mm A');
-        } else {
-            shorttime = moment(date).format('H:mm A');
-        }
+        return moment().format('YYYY-MM-DD') === shortdate ? 'Today' : shortdate;
+    };
 
-        return shorttime;
+    /**
+     * @param   {String} date
+     * @returns {String}
+     */
+    data.shortTime = function (date) {
+        return data.formatDbDate(date, 'H:mm A');
     };
 
     /**

+ 1 - 3
src/frontend/js/login/ui/login.ejs

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