mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-17 22:57:21 +01:00
try moving all the logic to the model
This commit is contained in:
@@ -1,6 +1,8 @@
|
|||||||
{
|
{
|
||||||
"missingAuthHeaders": "Missing authentication headers.",
|
"missingAuthHeaders": "Missing authentication headers.",
|
||||||
"missingUsernameEmail": "Missing username or email.",
|
"missingUsernameEmail": "Missing username or email.",
|
||||||
|
"missingEmail": "Missing email.",
|
||||||
|
"missingUsername": "Missing username.",
|
||||||
"missingPassword": "Missing password.",
|
"missingPassword": "Missing password.",
|
||||||
"invalidEmail": "Invalid email address.",
|
"invalidEmail": "Invalid email address.",
|
||||||
"emailTaken": "Email already taken.",
|
"emailTaken": "Email already taken.",
|
||||||
|
|||||||
@@ -20,45 +20,11 @@ let api = {};
|
|||||||
* @apiParam {String} passwordConfirmation Password confirmation
|
* @apiParam {String} passwordConfirmation Password confirmation
|
||||||
*
|
*
|
||||||
* @apiSuccess {Object} user The user object
|
* @apiSuccess {Object} user The user object
|
||||||
*
|
|
||||||
* @apiUse NotAuthorized
|
|
||||||
*/
|
*/
|
||||||
api.registerLocal = {
|
api.registerLocal = {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
url: '/user/register/local',
|
url: '/user/register/local',
|
||||||
handler (req, res, next) {
|
handler (req, res, next) {
|
||||||
req.checkBody({
|
|
||||||
username: {
|
|
||||||
notEmpty: true,
|
|
||||||
errorMessage: res.t('missingEmail'),
|
|
||||||
},
|
|
||||||
email: {
|
|
||||||
notEmpty: true,
|
|
||||||
isEmail: true,
|
|
||||||
errorMessage: res.t('invalidEmail'),
|
|
||||||
},
|
|
||||||
password: {
|
|
||||||
notEmpty: true,
|
|
||||||
errorMessage: res.t('missingPassword'),
|
|
||||||
},
|
|
||||||
passwordConfirmation: {
|
|
||||||
notEmpty: true,
|
|
||||||
equals: {
|
|
||||||
options: [req.body.password],
|
|
||||||
},
|
|
||||||
errorMessage: res.t('passwordConfirmationMatch'),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
let validationErrors = req.validationErrors();
|
|
||||||
|
|
||||||
if (validationErrors) return next(validationErrors);
|
|
||||||
|
|
||||||
req.sanitizeBody('username').trim();
|
|
||||||
req.sanitizeBody('email').trim();
|
|
||||||
req.sanitizeBody('password').trim();
|
|
||||||
req.sanitizeBody('passwordConfirmation').trim();
|
|
||||||
|
|
||||||
let email = req.body.email.toLowerCase();
|
let email = req.body.email.toLowerCase();
|
||||||
let username = req.body.username;
|
let username = req.body.username;
|
||||||
// Get the lowercase version of username to check that we do not have duplicates
|
// Get the lowercase version of username to check that we do not have duplicates
|
||||||
@@ -71,22 +37,22 @@ api.registerLocal = {
|
|||||||
{'auth.local.lowerCaseUsername': lowerCaseUsername},
|
{'auth.local.lowerCaseUsername': lowerCaseUsername},
|
||||||
]}, {'auth.local': 1})
|
]}, {'auth.local': 1})
|
||||||
.exec()
|
.exec()
|
||||||
.then((results) => {
|
.then((user) => {
|
||||||
if (results[0]) {
|
if (user) {
|
||||||
if (email === results[0].auth.local.email) return next(new NotAuthorized(res.t('emailTaken')));
|
if (email === user.auth.local.email) return next(new NotAuthorized(res.t('emailTaken')));
|
||||||
// Check that the lowercase username isn't already used
|
// Check that the lowercase username isn't already used
|
||||||
if (lowerCaseUsername === results[0].auth.local.lowerCaseUsername) return next(new NotAuthorized(res.t('usernameTaken')));
|
if (lowerCaseUsername === user.auth.local.lowerCaseUsername) return next(new NotAuthorized(res.t('usernameTaken')));
|
||||||
}
|
}
|
||||||
|
|
||||||
let salt = passwordUtils.makeSalt();
|
|
||||||
let newUser = new User({
|
let newUser = new User({
|
||||||
auth: {
|
auth: {
|
||||||
local: {
|
local: {
|
||||||
username,
|
username,
|
||||||
lowerCaseUsername, // Store the lowercase version of the username
|
lowerCaseUsername, // Store the lowercase version of the username
|
||||||
email, // Store email as lowercase
|
email, // Store email as lowercase
|
||||||
salt,
|
salt: passwordUtils.makeSalt(),
|
||||||
hashed_password: passwordUtils.encrypt(req.body.password, salt), // eslint-disable-line camelcase
|
password: req.body.password,
|
||||||
|
passwordConfirmation: req.body.passwordConfirmation,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
preferences: {
|
preferences: {
|
||||||
@@ -131,7 +97,6 @@ api.registerLocal = {
|
|||||||
* @apiSuccess {String} _id The user's unique identifier
|
* @apiSuccess {String} _id The user's unique identifier
|
||||||
* @apiSuccess {String} apiToken The user's api token that must be used to authenticate requests.
|
* @apiSuccess {String} apiToken The user's api token that must be used to authenticate requests.
|
||||||
*
|
*
|
||||||
* @apiUse NotAuthorized
|
|
||||||
*/
|
*/
|
||||||
api.loginLocal = {
|
api.loginLocal = {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
// User schema and model
|
// User schema and model
|
||||||
import mongoose from 'mongoose';
|
import mongoose from 'mongoose';
|
||||||
import shared from '../../../common';
|
import shared from '../../../common';
|
||||||
|
import passwordUtils from '../libs/api-v3/password';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
|
import validator from 'validator';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import TaskSchemas from './task';
|
import TaskSchemas from './task';
|
||||||
// import {model as Challenge} from './challenge';
|
// import {model as Challenge} from './challenge';
|
||||||
@@ -26,12 +28,29 @@ export let schema = new Schema({
|
|||||||
blocked: Boolean,
|
blocked: Boolean,
|
||||||
facebook: Schema.Types.Mixed, // TODO validate
|
facebook: Schema.Types.Mixed, // TODO validate
|
||||||
local: {
|
local: {
|
||||||
email: String,
|
email: {
|
||||||
hashed_password: String, // eslint-disable-line camelcase
|
type: String,
|
||||||
salt: String,
|
trim: true,
|
||||||
username: String,
|
lowercase: true,
|
||||||
|
validate: [validator.isEmail, shared.i18n.t('invalidEmail')], // TODO translate error messages here, use preferences.language?
|
||||||
|
},
|
||||||
|
username: {
|
||||||
|
type: String,
|
||||||
|
trim: true,
|
||||||
|
},
|
||||||
// Store a lowercase version of username to check for duplicates
|
// Store a lowercase version of username to check for duplicates
|
||||||
lowerCaseUsername: String,
|
lowerCaseUsername: String,
|
||||||
|
hashed_password: String, // eslint-disable-line camelcase
|
||||||
|
salt: String,
|
||||||
|
// password and passwordConfirmation are not stored in the database, used only for validation
|
||||||
|
password: {
|
||||||
|
type: String,
|
||||||
|
trim: true,
|
||||||
|
},
|
||||||
|
passwordConfirmation: {
|
||||||
|
type: String,
|
||||||
|
trim: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
timestamps: {
|
timestamps: {
|
||||||
created: {type: Date, default: Date.now},
|
created: {type: Date, default: Date.now},
|
||||||
@@ -208,6 +227,7 @@ export let schema = new Schema({
|
|||||||
party: Schema.Types.Mixed, // TODO dictionary
|
party: Schema.Types.Mixed, // TODO dictionary
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// TODO we're storing too many fields here, find a way to reduce them
|
||||||
items: {
|
items: {
|
||||||
gear: {
|
gear: {
|
||||||
owned: _.transform(shared.content.gear.flat, (m, v) => {
|
owned: _.transform(shared.content.gear.flat, (m, v) => {
|
||||||
@@ -328,6 +348,7 @@ export let schema = new Schema({
|
|||||||
orderAscending: {type: String, default: 'ascending'},
|
orderAscending: {type: String, default: 'ascending'},
|
||||||
quest: {
|
quest: {
|
||||||
key: String,
|
key: String,
|
||||||
|
// TODO why are we storing quest progress here too and not only on party object?
|
||||||
progress: {
|
progress: {
|
||||||
up: {type: Number, default: 0},
|
up: {type: Number, default: 0},
|
||||||
down: {type: Number, default: 0},
|
down: {type: Number, default: 0},
|
||||||
@@ -589,6 +610,37 @@ function _setProfileName (user) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
schema.pre('save', function postSaveUser (next) {
|
schema.pre('save', function postSaveUser (next) {
|
||||||
|
// Validate the auth path (doesn't work with schema.path('auth').validate)
|
||||||
|
if (!this.auth.facebook.id) {
|
||||||
|
if (!this.auth.local.email) {
|
||||||
|
this.invalidate('auth.local.email', shared.i18n.t('missingEmail'));
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.auth.local.email) {
|
||||||
|
this.invalidate('auth.local.username', shared.i18n.t('missingUsername'));
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate password and password confirmation and create hashed version
|
||||||
|
if (this.isModified('auth.local.password') || this.isNew() && !this.auth.facebook.id) {
|
||||||
|
if (!this.auth.local.password) {
|
||||||
|
this.invalidate('auth.local.password', shared.i18n.t('missingPassword'));
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.auth.local.password !== this.auth.local.passwordConfirmation) {
|
||||||
|
this.invalidate('auth.local.passwordConfirmation', shared.i18n.t('passwordConfirmationMatch'));
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.hashed_password = passwordUtils.encrypt(this.auth.local.password, this.auth.local.salt); // eslint-disable-line camelcase
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do not store password and passwordConfirmation
|
||||||
|
this.auth.local.password = this.local.auth.passwordConfirmation = undefined;
|
||||||
|
|
||||||
// Populate new users with default content
|
// Populate new users with default content
|
||||||
if (this.isNew) {
|
if (this.isNew) {
|
||||||
_populateDefaultsForNewUser(this);
|
_populateDefaultsForNewUser(this);
|
||||||
|
|||||||
Reference in New Issue
Block a user