Sotaro KARASAWA 10 лет назад
Родитель
Сommit
9f97b1ba33

+ 2 - 1
lib/form/index.js

@@ -4,7 +4,8 @@ exports.invited = require('./invited');
 exports.revision = require('./revision');
 exports.me = {
   user: require('./me/user'),
-  password: require('./me/password')
+  password: require('./me/password'),
+  apiToken: require('./me/apiToken'),
 };
 exports.admin = {
   app: require('./admin/app'),

+ 9 - 0
lib/form/me/apiToken.js

@@ -0,0 +1,9 @@
+'use strict';
+
+var form = require('express-form')
+  , field = form.field;
+
+module.exports = form(
+  field('apiTokenForm.confirm').required()
+);
+

+ 24 - 0
lib/models/user.js

@@ -25,6 +25,7 @@ module.exports = function(crowi) {
     username: { type: String },
     email: { type: String, required: true },
     password: String,
+    apiToken: String,
     status: { type: Number, required: true, default: STATUS_ACTIVE },
     createdAt: { type: Date, default: Date.now },
     admin: { type: Boolean, default: 0 }
@@ -58,6 +59,13 @@ module.exports = function(crowi) {
     return hasher.digest('hex');
   }
 
+  function generateApiToken (user) {
+    var hasher = crypto.createHash('sha256');
+    hasher.update((new Date).getTime() + user._id);
+
+    return hasher.digest('base64');
+  }
+
   userSchema.methods.isPasswordSet = function() {
     if (this.password) {
       return true;
@@ -96,6 +104,22 @@ module.exports = function(crowi) {
     });
   };
 
+  userSchema.methods.updateApiToken = function(callback) {
+    var self = this;
+
+    self.apiToken = generateApiToken(this);
+    return new Promise(function(resolve, reject) {
+      self.save(function(err, userData) {
+        if (err) {
+          return reject(err);
+        } else {
+          return resolve(userData);
+        }
+      });
+
+    });
+  };
+
   userSchema.methods.updateImage = function(image, callback) {
     this.image = image;
     this.save(function(err, userData) {

+ 2 - 0
lib/routes/index.js

@@ -52,8 +52,10 @@ module.exports = function(crowi, app) {
 
   app.get('/me'                       , loginRequired(crowi, app) , me.index);
   app.get('/me/password'              , loginRequired(crowi, app) , me.password);
+  app.get('/me/apiToken'              , loginRequired(crowi, app) , me.apiToken);
   app.post('/me'                      , form.me.user              , loginRequired(crowi, app) , me.index);
   app.post('/me/password'             , form.me.password          , loginRequired(crowi, app) , me.password);
+  app.post('/me/apiToken'             , form.me.apiToken          , loginRequired(crowi, app) , me.apiToken);
   app.post('/me/picture/delete'       , loginRequired(crowi, app) , me.deletePicture);
   app.post('/me/auth/facebook'        , loginRequired(crowi, app) , me.authFacebook);
   app.post('/me/auth/google'          , loginRequired(crowi, app) , me.authGoogle);

+ 22 - 0
lib/routes/me.js

@@ -165,6 +165,28 @@ module.exports = function(crowi, app) {
     }
   };
 
+  actions.apiToken = function(req, res) {
+    var apiTokenForm = req.body.apiTokenForm;
+    var userData = req.user;
+
+    if (req.method == 'POST' && req.form.isValid) {
+      userData.updateApiToken()
+      .then(function(userData) {
+          req.flash('successMessage', 'API Token を更新しました');
+          return res.redirect('/me/apiToken');
+      })
+      .catch(function(err) {
+          //req.flash('successMessage',);
+          req.form.errors.push('API Token の更新に失敗しました');
+          return res.render('me/api_token', {
+          });
+      });
+    } else {
+      return res.render('me/api_token', {
+      });
+    }
+  };
+
   actions.updates = function(req, res) {
     res.render('me/update', {
     });

+ 83 - 0
lib/views/me/api_token.html

@@ -0,0 +1,83 @@
+{% extends '../layout/2column.html' %}
+
+
+{% block html_title %}APIの設定 · {{ path }}{% endblock %}
+
+{% block content_head %}
+<header  id="page-header">
+  <h1 class="title" id="">ユーザー設定</h1>
+</header>
+{% endblock %}
+
+{% block content_main %}
+<div class="content-main">
+
+  <ul class="nav nav-tabs">
+    <li><a href="/me"><i class="fa fa-gears"></i> ユーザー情報</a></li>
+    <li><a href="/me/password"><i class="fa fa-key"></i> パスワード設定</a></li>
+    <li class="active"><a href="/me/apiToken"><i class="fa fa-rocket"></i> API設定</a></li>
+  </ul>
+
+  <div class="tab-content">
+
+  {% set message = req.flash('successMessage') %}
+  {% if message.length %}
+  <div class="alert alert-success">
+    {{ message }}
+  </div>
+  {% endif %}
+
+  {% if req.form.errors.length > 0 %}
+  <div class="alert alert-danger">
+    <ul>
+    {% for error in req.form.errors %}
+      <li>{{ error }}</li>
+    {% endfor %}
+    </ul>
+  </div>
+  {% endif %}
+
+  <div id="form-box">
+
+    <form action="/me/apiToken" method="post" class="form-horizontal" role="form">
+    <fieldset>
+      <legend>API Token 設定</legend>
+      <div class="form-group {% if not user.password %}has-error{% endif %}">
+        <label for="" class="col-xs-2 control-label">現在のAPI Token</label>
+        <div class="col-xs-6">
+          {% if user.apiToken %}
+            <input class="form-control" type="text" value="{{ user.apiToken }}">
+          {% else %}
+          <p class="form-control-static">
+            API Token が設定されていません。更新するボタンから発行してください。
+          </p>
+          {% endif %}
+        </div>
+      </div>
+
+      <div class="form-group">
+        <div class="col-xs-offset-2 col-xs-10">
+
+          <p class="alert alert-warning">
+          API Token を更新すると、自動的に新しい Token が生成されます。<br>
+          現在の Token を利用している処理は動かなくなります。
+          </p>
+
+          <button type="submit" value="1" name="apiTokenForm[confirm]" class="btn btn-primary">API Tokenを更新する</button>
+        </div>
+      </div>
+
+    </fieldset>
+    </form>
+  </div>
+
+
+  </div>
+</div>
+{% endblock content_main %}
+
+{% block content_footer %}
+{% endblock %}
+
+{% block footer %}
+{% endblock %}

+ 1 - 0
lib/views/me/index.html

@@ -14,6 +14,7 @@
   <ul class="nav nav-tabs">
     <li class="active"><a href="/me"><i class="fa fa-gears"></i> ユーザー情報</a></li>
     <li><a href="/me/password"><i class="fa fa-key"></i> パスワード設定</a></li>
+    <li><a href="/me/apiToken"><i class="fa fa-rocket"></i> API設定</a></li>
   </ul>
 
   <div class="tab-content">

+ 1 - 0
lib/views/me/password.html

@@ -14,6 +14,7 @@
   <ul class="nav nav-tabs">
     <li><a href="/me"><i class="fa fa-gears"></i> ユーザー情報</a></li>
     <li class="active"><a href="/me/password"><i class="fa fa-key"></i> パスワード設定</a></li>
+    <li><a href="/me/apiToken"><i class="fa fa-rocket"></i> API設定</a></li>
   </ul>
 
   <div class="tab-content">