users.js 8.91 KB
Newer Older
mntmn's avatar
mntmn committed
1
2
3
"use strict";

var config = require('config');
4
5
const db = require('../../models/db');
const uuidv4 = require('uuid/v4');
mntmn's avatar
mntmn committed
6
const os = require('os');
mntmn's avatar
mntmn committed
7
8
9

var mailer = require('../../helpers/mailer');
var uploader = require('../../helpers/uploader');
mntmn's avatar
mntmn committed
10
var importer = require('../../helpers/importer');
mntmn's avatar
mntmn committed
11

Martin Guether's avatar
Martin Guether committed
12
var bcrypt = require('bcryptjs');
13
var crypto = require('crypto');
mntmn's avatar
mntmn committed
14
15
16
17
18
19
var async = require('async');
var _ = require('underscore');
var fs = require('fs');
var request = require('request');
var gm = require('gm');
var validator = require('validator');
20
var URL = require('url').URL;
mntmn's avatar
mntmn committed
21
22
23

var express = require('express');
var router = express.Router();
24
var glob = require('glob');
mntmn's avatar
mntmn committed
25
26
27

router.get('/current', function(req, res, next) {
  if (req.user) {
28
29
30
31
32
33
34
35
36
    var u = _.clone(req.user.dataValues);
    delete u.password_hash;
    delete u.password_reset_token;
    delete u.confirmation_token;
    u.token = req.cookies['sdsession'];

    console.log(u);
    
    res.status(200).json(u);
mntmn's avatar
mntmn committed
37
38
39
40
41
  } else {
    res.status(401).json({"error":"user_not_found"});
  }
});

42
// create user
mntmn's avatar
mntmn committed
43
router.post('/', function(req, res) {
44
  if (!req.body["email"] || !req.body["password"]) {
mntmn's avatar
mntmn committed
45
    res.status(400).json({"error":"email or password missing"});
46
    return;
mntmn's avatar
mntmn committed
47
  }
48
49
50
51
52
  
  var email = req.body["email"].toLowerCase();
  var nickname = req.body["nickname"];
  var password = req.body["password"];
  var password_confirmation = req.body["password_confirmation"];
53
  var invite_code = req.body["invite_code"];
54
55
56
57
58
59

  if (password_confirmation != password) {
    res.status(400).json({"error":"password_confirmation"});
    return;
  }
  
60
61
62
63
64
  if (config.invite_code && invite_code != config.invite_code) {
    res.status(400).json({"error":"Invalid Invite Code."});
    return;
  }
  
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
  if (!validator.isEmail(email)) {
    res.status(400).json({"error":"email_invalid"});
    return;
  }
  
  var createUser = function() {
    bcrypt.genSalt(10, function(err, salt) {
      bcrypt.hash(password, salt, function(err, hash) {
        crypto.randomBytes(16, function(ex, buf) {
          var token = buf.toString('hex');

          var u = {
            _id: uuidv4(),
            email: email,
            account_type: "email",
            nickname: nickname,
            password_hash: hash,
            prefs_language: req.i18n.locale,
            confirmation_token: token
          };

          db.User.create(u)
            .error(err => {
              res.sendStatus(400);
            })
            .then(u => {
91
              var homeFolder = {
92
93
94
95
96
                _id: uuidv4(),
                name: req.i18n.__("home"),
                space_type: "folder",
                creator_id: u._id
              };
97
              db.Space.create(homeFolder)
98
99
100
                .error(err => {
                  res.sendStatus(400);
                })
101
102
                .then(homeFolder => {
                  u.home_folder_id = homeFolder._id;
103
104
                  u.save()
                    .then(() => {
105
106
107
108
109
110
111
                      // home folder created,
                      // auto accept pending invites
                      db.Membership.update({
                        "state": "active"
                      }, {
                        where: {
                          "email_invited": u.email,
mntmn's avatar
mntmn committed
112
                          "state": "pending"
mntmn's avatar
mntmn committed
113
114
                        }
                      });
115
                      res.status(201).json({});          
116
117
118
119
120
121
122
                    })
                    .error(err => {
                      res.status(400).json(err);
                    });
                })
            });
        });
mntmn's avatar
mntmn committed
123
      });
124
125
126
127
128
129
130
131
132
133
134
    });
  };
  
  db.User.findAll({where: {email: email}})
    .then(users => {
      if (users.length == 0) {
        createUser();
      } else {
        res.status(400).json({"error":"user_email_already_used"});
      }
    })
mntmn's avatar
mntmn committed
135
136
});

137
router.get('/current', function(req, res, next) {
mntmn's avatar
mntmn committed
138
139
140
141
142
143
144
145
  if (req.user) {
    res.status(200).json(req.user);
  } else {
    res.status(401).json({"error":"user_not_found"});
  }
});

router.put('/:id', function(req, res, next) {
146
  // TODO explicit whitelisting
mntmn's avatar
mntmn committed
147
148
149
150
151
152
  var user = req.user;
  if (user._id == req.params.id) {
    var newAttr = req.body;
    newAttr.updated_at = new Date();
    delete newAttr['_id'];

153
154
    db.User.update(newAttr, {where: {"_id": user._id}}).then(function(updatedUser) {
      res.status(200).json(newAttr);
mntmn's avatar
mntmn committed
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
    });
  } else {
    res.sendStatus(403);
  }
});

router.post('/:id/password', function(req, res, next) {
  var user = req.user;
  var old_password = req.body.old_password;
  var pass = req.body.new_password;

  if (pass.length >= 6) {
    if (user._id == req.params.id) {
      if (bcrypt.compareSync(old_password, user.password_hash)) {
        bcrypt.genSalt(10, function(err, salt) {
          bcrypt.hash(pass, salt, function(err, hash) {
            user.password_hash = hash;
172
173
            user.save().then(function() {
              res.sendStatus(204);
mntmn's avatar
mntmn committed
174
175
176
177
            });
          });
        });
      } else {
178
        res.status(403).json({"error": "Please enter the correct current password."});
mntmn's avatar
mntmn committed
179
180
      }
    } else {
181
      res.status(403).json({"error": "Access denied."});
mntmn's avatar
mntmn committed
182
183
    }
  } else {
184
    res.status(400).json({"error": "Please choose a new password with at least 6 characters."});
mntmn's avatar
mntmn committed
185
186
187
188
189
  }
});

router.delete('/:id',  (req, res, next) => {
  const user = req.user;
190
191
192
193
194
195
196
197
198
  if (user._id == req.params.id) {
    if (bcrypt.compareSync(req.query.password, user.password_hash)) {

      // TODO: this doesn't currently work.
      // all objects (indirectly) belonging to the user have
      // to be walked and deleted first.
      
      user.destroy().then(err => {
        if(err)res.status(400).json(err);
mntmn's avatar
mntmn committed
199
200
        else res.sendStatus(204);
      });
201
202
    } else {
      res.bad_request("Please enter the correct current password.");
mntmn's avatar
mntmn committed
203
    }
204
205
  } else {
    res.status(403).json({error: "Access denied."});
mntmn's avatar
mntmn committed
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
  }
});

router.put('/:user_id/confirm', (req, res) => {
  const token = req.body.token;
  const user = req.user;

  if (user.confirmation_token === token) {
    user.confirmation_token = null;
    user.confirmed_at = new Date();
    user.save(function(err, updatedUser) {
      if(err) {
        res.sendStatus(400);
      } else {
        res.status(200).json(updatedUser);
      }
    });
  } else {
    res.sendStatus(400);
  }
});

router.post('/:user_id/avatar', (req, res, next) => {
  const user = req.user;
  const filename = "u"+req.user._id+"_"+(new Date().getTime())+".jpeg"

mntmn's avatar
mntmn committed
232
233
  const localFilePath = os.tmpdir()+"/"+filename;
  const localResizedFilePath = os.tmpdir()+"/resized_"+filename;
mntmn's avatar
mntmn committed
234
235
236
237
238
239
240
241
242
243
244
  const writeStream = fs.createWriteStream(localFilePath);
  const stream = req.pipe(writeStream);

  req.on('end', function() {
    gm(localFilePath).resize(200, 200).autoOrient().write(localResizedFilePath, (err) => {
      if (err) res.status(400).json(err);
      else {
        uploader.uploadFile(filename, "image/jpeg", localResizedFilePath, (err, url) => {
          if (err) res.status(400).json(err);
          else {
            user.avatar_thumb_uri = url;
245
246
247
248
249
250
251
252
253
            user.save().then(() => {
              fs.unlink(localResizedFilePath, (err) => {
                if (err) {
                  console.error(err);
                  res.status(400).json(err);
                } else {
                  res.status(200).json(user);
                }
              });
mntmn's avatar
mntmn committed
254
255
256
257
258
259
260
261
262
263
            });
          }
        });
      }
    });
  });
});

router.post('/password_reset_requests', (req, res, next) => {
  const email = req.query.email;
264
265
266
267
268
269
270
271
272
273
274
275
  db.User.findOne({where: {"email": email}}).then((user) => {
    if (user) {
      crypto.randomBytes(16, (ex, buf) => {
        user.password_reset_token = buf.toString('hex');
        user.save().then(updatedUser => {
          mailer.sendMail(email, req.i18n.__("password_reset_subject"), req.i18n.__("password_reset_body"), {action: {
            link: config.endpoint + "/password-confirm/" + user.password_reset_token,
            name: req.i18n.__("password_reset_action")
          }});
          res.status(201).json({});
        });
      });
mntmn's avatar
mntmn committed
276
    } else {
277
      res.status(404).json({"error": "error_unknown_email"});
mntmn's avatar
mntmn committed
278
279
280
281
282
283
284
    }
  });
});

router.post('/password_reset_requests/:confirm_token/confirm', function(req, res, next) {
  var password = req.body.password;

Hirunatan's avatar
Hirunatan committed
285
  db.User
286
287
288
289
290
291
292
    .findOne({where: {"password_reset_token": req.params.confirm_token}})
    .then((user) => {
      if (user) {
        bcrypt.genSalt(10, (err, salt) => {
          bcrypt.hash(password, salt, function(err, hash) {
            user.password_hash = hash;
            user.password_token = null;
mntmn's avatar
mntmn committed
293
294
            user.save().then(function(updatedUser) {
              res.sendStatus(201);
mntmn's avatar
mntmn committed
295
296
            });
          });
297
298
299
        });
      } else {
        res.sendStatus(404);
mntmn's avatar
mntmn committed
300
301
302
303
304
305
306
307
308
309
310
311
312
      }
    });
});

router.post('/:user_id/confirm', function(req, res, next) {
  mailer.sendMail(req.user.email, req.i18n.__("confirm_subject"), req.i18n.__("confirm_body"), { action:{
    link: config.endpoint + "/confirm/" + req.user.confirmation_token,
    name: req.i18n.__("confirm_action")
  }});
  res.sendStatus(201);
});

module.exports = router;