diff --git a/src/oauth2views/account_create.ejs b/src/oauth2views/account_create.ejs new file mode 100644 index 000000000..794328932 --- /dev/null +++ b/src/oauth2views/account_create.ejs @@ -0,0 +1,48 @@ +<% include header %> + + + + + +
+
+

Welcome to <%= cloudronName %>.

+
+ +
+
+
+

Create your account by providing an email address.

+
+ + + +
+ +
+ Must be a valid email address + {{{ error }}} +
+ +
+ +
+
+
+

You have received an email invitation to this Cloudron to finish the signup.

+
+
+
+ +<% include footer %> diff --git a/src/routes/oauth2.js b/src/routes/oauth2.js index 882f14f89..b6c67bd25 100644 --- a/src/routes/oauth2.js +++ b/src/routes/oauth2.js @@ -11,6 +11,7 @@ var appdb = require('../appdb'), DatabaseError = require('../databaseerror'), debug = require('debug')('box:routes/oauth2'), eventlog = require('../eventlog.js'), + generatePassword = require('../password.js').generate, hat = require('hat'), HttpError = require('connect-lastmile').HttpError, middleware = require('../middleware/index.js'), @@ -357,6 +358,77 @@ function accountSetup(req, res, next) { }); } +// -> POST /api/v1/session/account/setup +function accountSetup(req, res, next) { + assert.strictEqual(typeof req.body, 'object'); + + if (typeof req.body.resetToken !== 'string') return next(new HttpError(400, 'Missing resetToken')); + if (typeof req.body.password !== 'string') return next(new HttpError(400, 'Missing password')); + if (typeof req.body.username !== 'string') return next(new HttpError(400, 'Missing username')); + if (typeof req.body.displayName !== 'string') return next(new HttpError(400, 'Missing displayName')); + + debug('accountSetup: with token %s.', req.body.resetToken); + + user.getByResetToken(req.body.resetToken, function (error, userObject) { + if (error) return sendError(req, res, 'Invalid Reset Token'); + + var data = _.pick(req.body, 'username', 'displayName'); + user.update(userObject.id, data, auditSource(req), function (error) { + if (error && error.reason === UserError.ALREADY_EXISTS) return renderAccountSetupSite(res, req, userObject, 'Username already exists'); + if (error && error.reason === UserError.BAD_FIELD) return renderAccountSetupSite(res, req, userObject, error.message); + if (error && error.reason === UserError.NOT_FOUND) return renderAccountSetupSite(res, req, userObject, 'No such user'); + if (error) return next(new HttpError(500, error)); + + userObject.username = req.body.username; + userObject.displayName = req.body.displayName; + + // setPassword clears the resetToken + user.setPassword(userObject.id, req.body.password, function (error, result) { + if (error && error.reason === UserError.BAD_FIELD) return renderAccountSetupSite(res, req, userObject, error.message); + + if (error) return next(new HttpError(500, error)); + + res.redirect(util.format('%s?accessToken=%s&expiresAt=%s', config.adminOrigin(), result.token, result.expiresAt)); + }); + }); + }); +} + +function renderAccountCreateSite(res, req, error, success) { + renderTemplate(res, 'account_create', { + error: error, + success: !!success, + csrf: req.csrfToken(), + title: 'Account Create' + }); +} + +// -> GET /api/v1/session/account/create.html +function accountCreateSite(req, res) { + renderAccountCreateSite(res, req, '', ''); +} + +// -> POST /api/v1/session/account/create +function accountCreate(req, res, next) { + assert.strictEqual(typeof req.body, 'object'); + + if (typeof req.body.email !== 'string') return next(new HttpError(400, 'Missing email')); + + debug('accountCreate: with email %s.', req.body.email); + + var ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress || null; + var auditSource = { ip: ip, username: req.body.email, userId: null }; + + user.create('', generatePassword(), req.body.email, '', auditSource, { sendInvite: true }, function (error, result) { + if (error && error.reason === UserError.ALREADY_EXISTS) return renderAccountCreateSite(res, req, 'User with this email address already exists'); + if (error) return sendError(req, res, 'Internal Error'); + + debug('accountCreate: success for email %s now with id %s', req.body.remail, result.id); + + renderAccountCreateSite(res, req, '', true); + }); +} + // -> GET /api/v1/session/password/reset.html function passwordResetSite(req, res, next) { if (!req.query.reset_token) return next(new HttpError(400, 'Missing reset_token')); @@ -555,6 +627,8 @@ exports = module.exports = { passwordReset: passwordReset, accountSetupSite: accountSetupSite, accountSetup: accountSetup, + accountCreateSite: accountCreateSite, + accountCreate: accountCreate, authorization: authorization, token: token, validateRequestedScopes: validateRequestedScopes, diff --git a/src/server.js b/src/server.js index d7b9b32c7..b3c457516 100644 --- a/src/server.js +++ b/src/server.js @@ -148,6 +148,8 @@ function initializeExpressSync() { router.post('/api/v1/session/password/reset', csrf, routes.oauth2.passwordReset); router.get ('/api/v1/session/account/setup.html', csrf, routes.oauth2.accountSetupSite); router.post('/api/v1/session/account/setup', csrf, routes.oauth2.accountSetup); + router.get ('/api/v1/session/account/create.html', csrf, routes.oauth2.accountCreateSite); + router.post('/api/v1/session/account/create', csrf, routes.oauth2.accountCreate); // oauth2 routes router.get ('/api/v1/oauth/dialog/authorize', routes.oauth2.authorization);