2016-03-22 22:09:22 -07:00
|
|
|
|
import { randomString } from '../cryptoUtils';
|
|
|
|
|
|
import { inflate } from '../triggers';
|
2016-02-27 10:51:12 -05:00
|
|
|
|
import AdaptableController from './AdaptableController';
|
2016-03-22 22:09:22 -07:00
|
|
|
|
import MailAdapter from '../Adapters/Email/MailAdapter';
|
|
|
|
|
|
import rest from '../rest';
|
2016-02-25 19:04:27 -05:00
|
|
|
|
|
|
|
|
|
|
var DatabaseAdapter = require('../DatabaseAdapter');
|
2016-02-27 14:46:29 -05:00
|
|
|
|
var RestWrite = require('../RestWrite');
|
2016-02-27 20:01:12 -05:00
|
|
|
|
var RestQuery = require('../RestQuery');
|
2016-02-27 14:46:29 -05:00
|
|
|
|
var hash = require('../password').hash;
|
|
|
|
|
|
var Auth = require('../Auth');
|
2016-02-25 19:04:27 -05:00
|
|
|
|
|
2016-02-27 10:51:12 -05:00
|
|
|
|
export class UserController extends AdaptableController {
|
|
|
|
|
|
|
|
|
|
|
|
constructor(adapter, appId, options = {}) {
|
|
|
|
|
|
super(adapter, appId, options);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
validateAdapter(adapter) {
|
|
|
|
|
|
// Allow no adapter
|
|
|
|
|
|
if (!adapter && !this.shouldVerifyEmails) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
super.validateAdapter(adapter);
|
|
|
|
|
|
}
|
2016-03-02 20:59:25 -08:00
|
|
|
|
|
2016-02-27 10:51:12 -05:00
|
|
|
|
expectedAdapterType() {
|
|
|
|
|
|
return MailAdapter;
|
2016-02-25 19:04:27 -05:00
|
|
|
|
}
|
2016-03-02 20:59:25 -08:00
|
|
|
|
|
2016-02-27 10:51:12 -05:00
|
|
|
|
get shouldVerifyEmails() {
|
|
|
|
|
|
return this.options.verifyUserEmails;
|
|
|
|
|
|
}
|
2016-03-02 20:59:25 -08:00
|
|
|
|
|
2016-02-27 10:51:12 -05:00
|
|
|
|
setEmailVerifyToken(user) {
|
|
|
|
|
|
if (this.shouldVerifyEmails) {
|
|
|
|
|
|
user._email_verify_token = randomString(25);
|
|
|
|
|
|
user.emailVerified = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2016-03-02 20:59:25 -08:00
|
|
|
|
|
2016-02-27 15:24:45 -05:00
|
|
|
|
verifyEmail(username, token) {
|
2016-03-02 00:04:29 -08:00
|
|
|
|
if (!this.shouldVerifyEmails) {
|
2016-02-27 10:51:12 -05:00
|
|
|
|
// Trying to verify email when not enabled
|
2016-03-02 00:04:29 -08:00
|
|
|
|
// TODO: Better error here.
|
|
|
|
|
|
return Promise.reject();
|
|
|
|
|
|
}
|
2016-04-14 19:24:56 -04:00
|
|
|
|
let database = this.config.database.WithoutValidation();
|
|
|
|
|
|
return database.update('_User', {
|
|
|
|
|
|
username: username,
|
|
|
|
|
|
_email_verify_token: token
|
|
|
|
|
|
}, {emailVerified: true}).then(document => {
|
|
|
|
|
|
if (!document) {
|
|
|
|
|
|
return Promise.reject();
|
|
|
|
|
|
}
|
|
|
|
|
|
return Promise.resolve(document);
|
|
|
|
|
|
});
|
2016-02-27 10:51:12 -05:00
|
|
|
|
}
|
2016-03-02 20:59:25 -08:00
|
|
|
|
|
2016-02-27 15:24:45 -05:00
|
|
|
|
checkResetTokenValidity(username, token) {
|
2016-04-14 19:24:56 -04:00
|
|
|
|
let database = this.config.database.WithoutValidation();
|
|
|
|
|
|
return database.find('_User', {
|
|
|
|
|
|
username: username,
|
|
|
|
|
|
_perishable_token: token
|
|
|
|
|
|
}, {limit: 1}).then(results => {
|
|
|
|
|
|
if (results.length != 1) {
|
|
|
|
|
|
return Promise.reject();
|
|
|
|
|
|
}
|
|
|
|
|
|
return results[0];
|
|
|
|
|
|
});
|
2016-02-27 10:51:12 -05:00
|
|
|
|
}
|
2016-03-02 20:59:25 -08:00
|
|
|
|
|
2016-02-27 20:01:12 -05:00
|
|
|
|
getUserIfNeeded(user) {
|
|
|
|
|
|
if (user.username && user.email) {
|
|
|
|
|
|
return Promise.resolve(user);
|
|
|
|
|
|
}
|
|
|
|
|
|
var where = {};
|
|
|
|
|
|
if (user.username) {
|
|
|
|
|
|
where.username = user.username;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (user.email) {
|
|
|
|
|
|
where.email = user.email;
|
|
|
|
|
|
}
|
2016-03-02 20:59:25 -08:00
|
|
|
|
|
2016-02-27 20:01:12 -05:00
|
|
|
|
var query = new RestQuery(this.config, Auth.master(this.config), '_User', where);
|
|
|
|
|
|
return query.execute().then(function(result){
|
|
|
|
|
|
if (result.results.length != 1) {
|
|
|
|
|
|
return Promise.reject();
|
|
|
|
|
|
}
|
|
|
|
|
|
return result.results[0];
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
2016-03-02 20:59:25 -08:00
|
|
|
|
|
2016-02-27 15:24:45 -05:00
|
|
|
|
sendVerificationEmail(user) {
|
2016-02-27 10:51:12 -05:00
|
|
|
|
if (!this.shouldVerifyEmails) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2016-03-29 17:48:54 +02:00
|
|
|
|
const token = encodeURIComponent(user._email_verify_token);
|
2016-02-27 20:01:12 -05:00
|
|
|
|
// We may need to fetch the user in case of update email
|
|
|
|
|
|
this.getUserIfNeeded(user).then((user) => {
|
|
|
|
|
|
const username = encodeURIComponent(user.username);
|
|
|
|
|
|
let link = `${this.config.verifyEmailURL}?token=${token}&username=${username}`;
|
|
|
|
|
|
let options = {
|
|
|
|
|
|
appName: this.config.appName,
|
|
|
|
|
|
link: link,
|
|
|
|
|
|
user: inflate('_User', user),
|
|
|
|
|
|
};
|
|
|
|
|
|
if (this.adapter.sendVerificationEmail) {
|
|
|
|
|
|
this.adapter.sendVerificationEmail(options);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
this.adapter.sendMail(this.defaultVerificationEmail(options));
|
|
|
|
|
|
}
|
2016-02-25 19:04:27 -05:00
|
|
|
|
});
|
2016-02-27 10:51:12 -05:00
|
|
|
|
}
|
2016-03-02 20:59:25 -08:00
|
|
|
|
|
2016-02-27 15:24:45 -05:00
|
|
|
|
setPasswordResetToken(email) {
|
2016-03-02 00:04:29 -08:00
|
|
|
|
let token = randomString(25);
|
2016-04-14 19:24:56 -04:00
|
|
|
|
let database = this.config.database.WithoutValidation();
|
|
|
|
|
|
return database.update('_User', {email: email}, {_perishable_token: token});
|
2016-02-27 14:46:29 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
2016-02-27 15:24:45 -05:00
|
|
|
|
sendPasswordResetEmail(email) {
|
2016-02-27 10:51:12 -05:00
|
|
|
|
if (!this.adapter) {
|
2016-02-27 14:46:29 -05:00
|
|
|
|
throw "Trying to send a reset password but no adapter is set";
|
|
|
|
|
|
// TODO: No adapter?
|
2016-02-27 10:51:12 -05:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
2016-03-02 20:59:25 -08:00
|
|
|
|
|
2016-02-27 14:46:29 -05:00
|
|
|
|
return this.setPasswordResetToken(email).then((user) => {
|
|
|
|
|
|
|
|
|
|
|
|
const token = encodeURIComponent(user._perishable_token);
|
2016-03-02 20:59:25 -08:00
|
|
|
|
const username = encodeURIComponent(user.username);
|
2016-02-27 15:24:45 -05:00
|
|
|
|
let link = `${this.config.requestResetPasswordURL}?token=${token}&username=${username}`
|
2016-02-28 00:15:59 -05:00
|
|
|
|
|
2016-02-27 20:01:12 -05:00
|
|
|
|
let options = {
|
2016-02-28 00:15:59 -05:00
|
|
|
|
appName: this.config.appName,
|
|
|
|
|
|
link: link,
|
|
|
|
|
|
user: inflate('_User', user),
|
|
|
|
|
|
};
|
2016-03-02 20:59:25 -08:00
|
|
|
|
|
2016-02-27 20:01:12 -05:00
|
|
|
|
if (this.adapter.sendPasswordResetEmail) {
|
|
|
|
|
|
this.adapter.sendPasswordResetEmail(options);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
this.adapter.sendMail(this.defaultResetPasswordEmail(options));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2016-02-27 14:46:29 -05:00
|
|
|
|
return Promise.resolve(user);
|
2016-02-27 10:51:12 -05:00
|
|
|
|
});
|
|
|
|
|
|
}
|
2016-03-02 20:59:25 -08:00
|
|
|
|
|
2016-02-27 15:24:45 -05:00
|
|
|
|
updatePassword(username, token, password, config) {
|
2016-03-22 19:08:25 +08:00
|
|
|
|
return this.checkResetTokenValidity(username, token).then((user) => {
|
2016-04-14 19:24:56 -04:00
|
|
|
|
return updateUserPassword(user.objectId, password, this.config);
|
2016-03-22 20:26:38 +08:00
|
|
|
|
}).then(() => {
|
|
|
|
|
|
// clear reset password token
|
2016-04-14 19:24:56 -04:00
|
|
|
|
return this.config.database.WithoutValidation().update('_User', { username }, {
|
|
|
|
|
|
_perishable_token: {__op: 'Delete'}
|
2016-03-22 20:26:38 +08:00
|
|
|
|
});
|
|
|
|
|
|
});
|
2016-02-27 14:46:29 -05:00
|
|
|
|
}
|
2016-03-02 20:59:25 -08:00
|
|
|
|
|
2016-02-27 20:01:12 -05:00
|
|
|
|
defaultVerificationEmail({link, user, appName, }) {
|
|
|
|
|
|
let text = "Hi,\n\n" +
|
2016-03-22 22:09:22 -07:00
|
|
|
|
"You are being asked to confirm the e-mail address " + user.get("email") + " with " + appName + "\n\n" +
|
2016-02-27 20:01:12 -05:00
|
|
|
|
"" +
|
|
|
|
|
|
"Click here to confirm it:\n" + link;
|
|
|
|
|
|
let to = user.get("email");
|
|
|
|
|
|
let subject = 'Please verify your e-mail for ' + appName;
|
|
|
|
|
|
return { text, to, subject };
|
|
|
|
|
|
}
|
2016-03-02 20:59:25 -08:00
|
|
|
|
|
2016-02-27 20:01:12 -05:00
|
|
|
|
defaultResetPasswordEmail({link, user, appName, }) {
|
2016-03-02 20:59:25 -08:00
|
|
|
|
let text = "Hi,\n\n" +
|
2016-02-27 20:01:12 -05:00
|
|
|
|
"You requested to reset your password for " + appName + ".\n\n" +
|
|
|
|
|
|
"" +
|
|
|
|
|
|
"Click here to reset it:\n" + link;
|
|
|
|
|
|
let to = user.get("email");
|
|
|
|
|
|
let subject = 'Password Reset for ' + appName;
|
|
|
|
|
|
return { text, to, subject };
|
|
|
|
|
|
}
|
2016-02-25 19:04:27 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
2016-02-27 14:46:29 -05:00
|
|
|
|
// Mark this private
|
2016-03-22 19:08:25 +08:00
|
|
|
|
function updateUserPassword(userId, password, config) {
|
|
|
|
|
|
return rest.update(config, Auth.master(config), '_User', userId, {
|
2016-03-22 20:26:38 +08:00
|
|
|
|
password: password
|
2016-03-22 19:08:25 +08:00
|
|
|
|
});
|
2016-02-27 14:46:29 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
2016-02-25 19:04:27 -05:00
|
|
|
|
export default UserController;
|