Sotaro KARASAWA 9 лет назад
Родитель
Сommit
e4ffcaeb16
5 измененных файлов с 163 добавлено и 0 удалено
  1. 50 0
      lib/models/user.js
  2. 15 0
      lib/routes/admin.js
  3. 2 0
      lib/routes/index.js
  4. 67 0
      lib/views/admin/users.html
  5. 29 0
      resource/js/crowi-admin.js

+ 50 - 0
lib/models/user.js

@@ -57,6 +57,19 @@ module.exports = function(crowi) {
     }
   }
 
+  function generateRandomTempPassword () {
+    var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!=-_';
+    var password = '';
+    var len = 12;
+
+    for (var i = 0; i < len; i++) {
+      var randomPoz = Math.floor(Math.random() * chars.length);
+      password += chars.substring(randomPoz, randomPoz+1);
+    }
+
+    return password;
+  }
+
   function generatePassword (password) {
     var hasher = crypto.createHash('sha256');
     hasher.update(crowi.env.PASSWORD_SEED + password);
@@ -244,6 +257,19 @@ module.exports = function(crowi) {
     return true;
   };
 
+  userSchema.statics.filterToPublicFields = function(user) {
+    var filteredUser = {};
+    var fields = USER_PUBLIC_FIELDS.split(' ');
+    for (var i = 0; i < fields.length; i++) {
+      var key = fields[i];
+      if (user[key]) {
+        filteredUser[key] = user[key];
+      }
+    }
+
+    return filteredUser;
+  };
+
   userSchema.statics.findUsers = function(options, callback) {
     var sort = options.sort || {status: 1, createdAt: 1};
 
@@ -453,6 +479,30 @@ module.exports = function(crowi) {
     });
   };
 
+  userSchema.statics.resetPasswordByRandomString = function(id) {
+    var User = this;
+
+    return new Promise(function(resolve, reject) {
+      User.findById(id, function (err, userData) {
+        if (!userData) {
+          return reject(new Error('User not found'));
+        }
+
+        // is updatable check
+        // if (userData.isUp
+        var newPassword = generateRandomTempPassword();
+        userData.setPassword(newPassword);
+        userData.save(function(err, userData) {
+          if (err) {
+            return reject(err);
+          }
+
+          resolve({user: userData, newPassword: newPassword});
+        });
+      });
+    });
+  };
+
   userSchema.statics.createUsersByInvitation = function(emailList, toSendEmail, callback) {
     var User = this
       , createdUserList = []

+ 15 - 0
lib/routes/admin.js

@@ -337,6 +337,21 @@ module.exports = function(crowi, app) {
     });
   };
 
+  // app.post('/_api/admin/users.resetPassword' , admin.api.usersResetPassword);
+  actions.user.resetPassword = function(req, res) {
+    const id = req.body.user_id;
+    const User = crowi.model('User');
+
+    User.resetPasswordByRandomString(id)
+    .then(function(data) {
+      data.user = User.filterToPublicFields(data.user);
+      return res.json(ApiResponse.success(data));
+    }).catch(function(err) {
+      debug('Error on reseting password', err);
+      return res.json(ApiResponse.error('Error'));
+    });
+  }
+
   actions.api = {};
   actions.api.appSetting = function(req, res) {
     var form = req.form.settingForm;

+ 2 - 0
lib/routes/index.js

@@ -64,6 +64,8 @@ module.exports = function(crowi, app) {
   app.post('/admin/user/:id/suspend'    , loginRequired(crowi, app) , middleware.adminRequired() , csrf, admin.user.suspend);
   app.post('/admin/user/:id/remove'     , loginRequired(crowi, app) , middleware.adminRequired() , csrf, admin.user.remove);
   app.post('/admin/user/:id/removeCompletely' , loginRequired(crowi, app) , middleware.adminRequired() , csrf, admin.user.removeCompletely);
+  // new route patterns from here:
+  app.post('/_api/admin/users.resetPassword'  , loginRequired(crowi, app) , middleware.adminRequired() , csrf, admin.user.resetPassword);
 
   app.get('/me'                       , loginRequired(crowi, app) , me.index);
   app.get('/me/password'              , loginRequired(crowi, app) , me.password);

+ 67 - 0
lib/views/admin/users.html

@@ -76,6 +76,62 @@
       </div><!-- /.modal -->
       {% endif %}
 
+      {# FIXME とりあえずクソ実装。React化はやくしたいなー(チラッチラッ #}
+      <div class="modal fade" id="admin-password-reset-modal">
+        <div class="modal-dialog">
+          <div class="modal-content">
+            <div class="modal-header">
+              <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
+              <h4 class="modal-title">パスワードを新規発行しますか?</h4>
+            </div>
+
+            <div class="modal-body">
+              <p>
+              新規発行したパスワードはこの画面を閉じると二度と表示できませんのでご注意ください。<br>
+              <span class="text-danger">新規発行したパスワードを、対象ユーザーへ連絡してください。</span>
+              </p>
+              <p>
+              Reset user: <code id="admin-password-reset-user"></code>
+              </p>
+
+              <form method="post" id="admin-users-reset-password">
+                <input type="hidden" name="user_id" value="">
+                <input type="hidden" name="_csrf" value="{{ csrf() }}">
+                <button type="submit" value="" class="btn btn-primary">
+                  実行
+                </button>
+              </form>
+
+            </div>
+
+          </div><!-- /.modal-content -->
+        </div><!-- /.modal-dialog -->
+      </div>
+      <div class="modal fade" id="admin-password-reset-modal-done">
+        <div class="modal-dialog">
+          <div class="modal-content">
+
+            <div class="modal-header">
+              <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
+              <h4 class="modal-title">Password reset!</h4>
+            </div>
+
+            <div class="modal-body">
+              <p class="alert alert-danger">Let the user know the new password below and strongly recommend to change another one immediately. </p>
+              <p>
+              Reset user: <code id="admin-password-reset-done-user"></code>
+              </p>
+              <p>
+              New passwrod: <code id="admin-password-reset-done-password"></code>
+              </p>
+            </div>
+            <div class="modal-footer">
+              <button class="btn btn-primary" data-dismiss="modal">OK</button>
+            </div>
+          </div><!-- /.modal-content -->
+        </div><!-- /.modal-dialog -->
+      </div>
+
       <h2>ユーザー一覧</h2>
 
       <table class="table table-hover table-striped table-bordered">
@@ -121,6 +177,16 @@
                   <li class="dropdown-header">編集メニュー</li>
                   <li>
                     <a href="">編集</a>
+
+                  </li>
+                  <li class="dropdown-button">
+                    <a href="#"
+                      data-user-id="{{ sUser._id.toString() }}"
+                      data-user-email="{{ sUser.email }}"
+                      data-target="#admin-password-reset-modal"
+                      data-toggle="modal" class="btn btn-block btn-default">
+                      パスワードの再発行
+                    </a>
                   </li>
                   <li class="divider"></li>
                   <li class="dropdown-header">ステータス</li>
@@ -132,6 +198,7 @@
                   </form>
                   {% endif  %}
                   {% if sUser.status == 2 %}
+
                   <form action="/admin/user/{{ sUser._id.toString() }}/suspend" method="post">
                     <input type="hidden" name="_csrf" value="{{ csrf() }}">
                     <button type="submit" class="btn btn-block btn-warning">アカウント停止</button>

+ 29 - 0
resource/js/crowi-admin.js

@@ -23,4 +23,33 @@ $(function() {
   });
 
   $('#createdUserModal').modal('show');
+
+  $('#admin-password-reset-modal').on('show.bs.modal', function(button) {
+    var data = $(button.relatedTarget);
+    var userId = data.data('user-id');
+    var email = data.data('user-email');
+
+    $('#admin-password-reset-user').text(email);
+    $('#admin-users-reset-password input[name=user_id]').val(userId);
+  });
+
+  $('form#admin-users-reset-password').on('submit', function(e) {
+    $.post('/_api/admin/users.resetPassword', $(this).serialize(), function(res) {
+      if (res.ok) {
+        // TODO Fix
+        //location.reload();
+        $('#admin-password-reset-modal').modal('hide');
+        $('#admin-password-reset-modal-done').modal('show');
+
+        $("#admin-password-reset-done-user").text(res.user.email);
+        $("#admin-password-reset-done-password").text(res.newPassword);
+        return ;
+      }
+
+      // fixme
+      alert('Failed to reset password');
+    });
+
+    return false;
+  });
 });