2016-02-27 10:51:12 -05:00
|
|
|
import { randomString } from '../cryptoUtils';
|
|
|
|
|
import { inflate } from '../triggers';
|
|
|
|
|
import AdaptableController from './AdaptableController';
|
|
|
|
|
import MailAdapter from '../Adapters/Email/MailAdapter';
|
2016-02-25 19:04:27 -05:00
|
|
|
|
|
|
|
|
var DatabaseAdapter = require('../DatabaseAdapter');
|
2016-02-27 14:46:29 -05:00
|
|
|
var RestWrite = require('../RestWrite');
|
|
|
|
|
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-02-25 19:04:27 -05:00
|
|
|
|
2016-02-27 10:51:12 -05:00
|
|
|
expectedAdapterType() {
|
|
|
|
|
return MailAdapter;
|
2016-02-25 19:04:27 -05:00
|
|
|
}
|
|
|
|
|
|
2016-02-27 10:51:12 -05:00
|
|
|
get shouldVerifyEmails() {
|
|
|
|
|
return this.options.verifyUserEmails;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
setEmailVerifyToken(user) {
|
|
|
|
|
if (this.shouldVerifyEmails) {
|
|
|
|
|
user._email_verify_token = randomString(25);
|
|
|
|
|
user.emailVerified = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2016-02-27 14:46:29 -05:00
|
|
|
verifyEmail(username, token, config = this.config) {
|
2016-02-27 10:51:12 -05:00
|
|
|
|
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
|
|
|
|
|
|
// Trying to verify email when not enabled
|
|
|
|
|
if (!this.shouldVerifyEmails) {
|
|
|
|
|
reject();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-27 14:46:29 -05:00
|
|
|
var database = config.database;
|
2016-02-27 10:51:12 -05:00
|
|
|
|
|
|
|
|
database.collection('_User').then(coll => {
|
|
|
|
|
// Need direct database access because verification token is not a parse field
|
|
|
|
|
return coll.findAndModify({
|
|
|
|
|
username: username,
|
|
|
|
|
_email_verify_token: token,
|
|
|
|
|
}, null, {$set: {emailVerified: true}}, (err, doc) => {
|
|
|
|
|
if (err || !doc.value) {
|
|
|
|
|
reject();
|
|
|
|
|
} else {
|
|
|
|
|
resolve();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-27 14:46:29 -05:00
|
|
|
checkResetTokenValidity(username, token, config = this.config) {
|
2016-02-25 19:04:27 -05:00
|
|
|
return new Promise((resolve, reject) => {
|
2016-02-27 14:46:29 -05:00
|
|
|
return config.database.collection('_User').then(coll => {
|
2016-02-27 10:51:12 -05:00
|
|
|
return coll.findOne({
|
|
|
|
|
username: username,
|
2016-02-27 14:46:29 -05:00
|
|
|
_perishable_token: token,
|
2016-02-27 10:51:12 -05:00
|
|
|
}, (err, doc) => {
|
2016-02-27 14:46:29 -05:00
|
|
|
if (err || !doc) {
|
|
|
|
|
reject(err);
|
2016-02-27 10:51:12 -05:00
|
|
|
} else {
|
2016-02-27 14:46:29 -05:00
|
|
|
resolve(doc);
|
2016-02-27 10:51:12 -05:00
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-27 14:46:29 -05:00
|
|
|
|
2016-02-27 10:51:12 -05:00
|
|
|
sendVerificationEmail(user, config = this.config) {
|
|
|
|
|
if (!this.shouldVerifyEmails) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const token = encodeURIComponent(user._email_verify_token);
|
|
|
|
|
const username = encodeURIComponent(user.username);
|
|
|
|
|
|
|
|
|
|
let link = `${config.verifyEmailURL}?token=${token}&username=${username}`;
|
|
|
|
|
this.adapter.sendVerificationEmail({
|
|
|
|
|
appName: config.appName,
|
|
|
|
|
link: link,
|
|
|
|
|
user: inflate('_User', user),
|
2016-02-25 19:04:27 -05:00
|
|
|
});
|
2016-02-27 10:51:12 -05:00
|
|
|
}
|
|
|
|
|
|
2016-02-27 14:46:29 -05:00
|
|
|
setPasswordResetToken(email, config = this.config) {
|
|
|
|
|
var database = config.database;
|
|
|
|
|
var token = randomString(25);
|
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
|
return database.collection('_User').then(coll => {
|
|
|
|
|
// Need direct database access because verification token is not a parse field
|
|
|
|
|
return coll.findAndModify({
|
|
|
|
|
email: email,
|
|
|
|
|
}, null, {$set: {_perishable_token: token}}, (err, doc) => {
|
|
|
|
|
if (err || !doc.value) {
|
|
|
|
|
console.error(err);
|
|
|
|
|
reject(err);
|
|
|
|
|
} else {
|
|
|
|
|
doc.value._perishable_token = token;
|
|
|
|
|
resolve(doc.value);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sendPasswordResetEmail(email, config = this.config) {
|
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-02-25 19:04:27 -05:00
|
|
|
|
2016-02-27 14:46:29 -05:00
|
|
|
return this.setPasswordResetToken(email).then((user) => {
|
|
|
|
|
|
|
|
|
|
const token = encodeURIComponent(user._perishable_token);
|
|
|
|
|
const username = encodeURIComponent(user.username);
|
|
|
|
|
let link = `${config.requestResetPasswordURL}?token=${token}&username=${username}`
|
|
|
|
|
this.adapter.sendPasswordResetEmail({
|
|
|
|
|
appName: config.appName,
|
|
|
|
|
link: link,
|
|
|
|
|
user: inflate('_User', user),
|
|
|
|
|
});
|
|
|
|
|
return Promise.resolve(user);
|
|
|
|
|
}, (err) => {
|
|
|
|
|
return Promise.reject(err);
|
2016-02-27 10:51:12 -05:00
|
|
|
});
|
|
|
|
|
}
|
2016-02-27 14:46:29 -05:00
|
|
|
|
|
|
|
|
updatePassword(username, token, password, config = this.config) {
|
|
|
|
|
return this.checkResetTokenValidity(username, token, config).then(() => {
|
|
|
|
|
return updateUserPassword(username, token, password, config);
|
|
|
|
|
});
|
|
|
|
|
}
|
2016-02-27 10:51:12 -05:00
|
|
|
|
|
|
|
|
sendMail(options) {
|
|
|
|
|
this.adapter.sendMail(options);
|
2016-02-25 19:04:27 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-27 14:46:29 -05:00
|
|
|
// Mark this private
|
|
|
|
|
function updateUserPassword(username, token, password, config) {
|
|
|
|
|
var write = new RestWrite(config, Auth.master(config), '_User', {
|
|
|
|
|
username: username,
|
|
|
|
|
_perishable_token: token
|
|
|
|
|
}, {password: password, _perishable_token: null }, undefined);
|
|
|
|
|
return write.execute();
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-25 19:04:27 -05:00
|
|
|
export default UserController;
|