Merge pull request #1804 from richardpringle/master
Add case-sensitive email option for User model
This commit is contained in:
commit
6d040a98ae
|
@ -62,6 +62,7 @@ var debug = require('debug')('loopback:user');
|
||||||
* @property {String} settings.realmDelimiter When set a realm is required.
|
* @property {String} settings.realmDelimiter When set a realm is required.
|
||||||
* @property {Number} settings.resetPasswordTokenTTL Time to live for password reset `AccessToken`. Default is `900` (15 minutes).
|
* @property {Number} settings.resetPasswordTokenTTL Time to live for password reset `AccessToken`. Default is `900` (15 minutes).
|
||||||
* @property {Number} settings.saltWorkFactor The `bcrypt` salt work factor. Default is `10`.
|
* @property {Number} settings.saltWorkFactor The `bcrypt` salt work factor. Default is `10`.
|
||||||
|
* @property {Boolean} settings.caseSensitiveEmail Enable case sensitive email.
|
||||||
*
|
*
|
||||||
* @class User
|
* @class User
|
||||||
* @inherits {PersistedModel}
|
* @inherits {PersistedModel}
|
||||||
|
@ -577,6 +578,14 @@ module.exports = function(User) {
|
||||||
this.settings.maxTTL = this.settings.maxTTL || DEFAULT_MAX_TTL;
|
this.settings.maxTTL = this.settings.maxTTL || DEFAULT_MAX_TTL;
|
||||||
this.settings.ttl = this.settings.ttl || DEFAULT_TTL;
|
this.settings.ttl = this.settings.ttl || DEFAULT_TTL;
|
||||||
|
|
||||||
|
UserModel.setter.email = function(value) {
|
||||||
|
if (!UserModel.settings.caseSensitiveEmail) {
|
||||||
|
this.$email = value.toLowerCase();
|
||||||
|
} else {
|
||||||
|
this.$email = value;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
UserModel.setter.password = function(plain) {
|
UserModel.setter.password = function(plain) {
|
||||||
if (typeof plain !== 'string') {
|
if (typeof plain !== 'string') {
|
||||||
return;
|
return;
|
||||||
|
@ -590,6 +599,14 @@ module.exports = function(User) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Access token to normalize email credentials
|
||||||
|
UserModel.observe('access', function normalizeEmailCase(ctx, next) {
|
||||||
|
if (!ctx.Model.settings.caseSensitiveEmail && ctx.query.where && ctx.query.where.email) {
|
||||||
|
ctx.query.where.email = ctx.query.where.email.toLowerCase();
|
||||||
|
}
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
|
||||||
// Make sure emailVerified is not set by creation
|
// Make sure emailVerified is not set by creation
|
||||||
UserModel.beforeRemote('create', function(ctx, user, next) {
|
UserModel.beforeRemote('create', function(ctx, user, next) {
|
||||||
var body = ctx.req.body;
|
var body = ctx.req.body;
|
||||||
|
|
|
@ -29,6 +29,9 @@
|
||||||
"created": "date",
|
"created": "date",
|
||||||
"lastUpdated": "date"
|
"lastUpdated": "date"
|
||||||
},
|
},
|
||||||
|
"options": {
|
||||||
|
"caseSensitiveEmail": true
|
||||||
|
},
|
||||||
"hidden": ["password"],
|
"hidden": ["password"],
|
||||||
"acls": [
|
"acls": [
|
||||||
{
|
{
|
||||||
|
|
|
@ -15,6 +15,7 @@ describe('User', function() {
|
||||||
var validCredentialsEmailVerifiedOverREST = {email: 'foo2@bar.com', password: 'bar2', emailVerified: true};
|
var validCredentialsEmailVerifiedOverREST = {email: 'foo2@bar.com', password: 'bar2', emailVerified: true};
|
||||||
var validCredentialsWithTTL = {email: 'foo@bar.com', password: 'bar', ttl: 3600};
|
var validCredentialsWithTTL = {email: 'foo@bar.com', password: 'bar', ttl: 3600};
|
||||||
var validCredentialsWithTTLAndScope = {email: 'foo@bar.com', password: 'bar', ttl: 3600, scope: 'all'};
|
var validCredentialsWithTTLAndScope = {email: 'foo@bar.com', password: 'bar', ttl: 3600, scope: 'all'};
|
||||||
|
var validMixedCaseEmailCredentials = {email: 'Foo@bar.com', password: 'bar'};
|
||||||
var invalidCredentials = {email: 'foo1@bar.com', password: 'invalid'};
|
var invalidCredentials = {email: 'foo1@bar.com', password: 'invalid'};
|
||||||
var incompleteCredentials = {password: 'bar1'};
|
var incompleteCredentials = {password: 'bar1'};
|
||||||
|
|
||||||
|
@ -67,6 +68,26 @@ describe('User', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Create a new user (email case-sensitivity off)', function(done) {
|
||||||
|
User.settings.caseSensitiveEmail = false;
|
||||||
|
User.create({email: 'F@b.com', password: 'bar'}, function(err, user) {
|
||||||
|
if (err) return done(err);
|
||||||
|
assert(user.id);
|
||||||
|
assert.equal(user.email, user.email.toLowerCase());
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Create a new user (email case-sensitive)', function(done) {
|
||||||
|
User.create({email: 'F@b.com', password: 'bar'}, function(err, user) {
|
||||||
|
if (err) return done(err);
|
||||||
|
assert(user.id);
|
||||||
|
assert(user.email);
|
||||||
|
assert.notEqual(user.email, user.email.toLowerCase());
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('credentials/challenges are object types', function(done) {
|
it('credentials/challenges are object types', function(done) {
|
||||||
User.create({email: 'f1@b.com', password: 'bar1',
|
User.create({email: 'f1@b.com', password: 'bar1',
|
||||||
credentials: {cert: 'xxxxx', key: '111'},
|
credentials: {cert: 'xxxxx', key: '111'},
|
||||||
|
@ -124,6 +145,27 @@ describe('User', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Requires a unique email (email case-sensitivity off)', function(done) {
|
||||||
|
User.settings.caseSensitiveEmail = false;
|
||||||
|
User.create({email: 'A@b.com', password: 'foobar'}, function(err) {
|
||||||
|
if (err) return done(err);
|
||||||
|
User.create({email: 'a@b.com', password: 'batbaz'}, function(err) {
|
||||||
|
assert(err, 'should error because the email is not unique!');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Requires a unique email (email case-sensitive)', function(done) {
|
||||||
|
User.create({email: 'A@b.com', password: 'foobar'}, function(err, user1) {
|
||||||
|
User.create({email: 'a@b.com', password: 'batbaz'}, function(err, user2) {
|
||||||
|
if (err) return done(err);
|
||||||
|
assert.notEqual(user1.email, user2.email);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('Requires a unique username', function(done) {
|
it('Requires a unique username', function(done) {
|
||||||
User.create({email: 'a@b.com', username: 'abc', password: 'foobar'}, function() {
|
User.create({email: 'a@b.com', username: 'abc', password: 'foobar'}, function() {
|
||||||
User.create({email: 'b@b.com', username: 'abc', password: 'batbaz'}, function(err) {
|
User.create({email: 'b@b.com', username: 'abc', password: 'batbaz'}, function(err) {
|
||||||
|
@ -212,6 +254,25 @@ describe('User', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('Access-hook for queries with email NOT case-sensitive', function() {
|
||||||
|
it('Should not throw an error if the query does not contain {where: }', function(done) {
|
||||||
|
User.find({}, function(err) {
|
||||||
|
if (err) done(err);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should be able to find lowercase email with mixed-case email query', function(done) {
|
||||||
|
User.settings.caseSensitiveEmail = false;
|
||||||
|
User.find({where:{email: validMixedCaseEmailCredentials.email}}, function(err, result) {
|
||||||
|
if (err) done(err);
|
||||||
|
assert(result[0], 'The query did not find the user');
|
||||||
|
assert.equal(result[0].email, validCredentialsEmail);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('User.login', function() {
|
describe('User.login', function() {
|
||||||
it('Login a user by providing credentials', function(done) {
|
it('Login a user by providing credentials', function(done) {
|
||||||
User.login(validCredentials, function(err, accessToken) {
|
User.login(validCredentials, function(err, accessToken) {
|
||||||
|
@ -223,6 +284,23 @@ describe('User', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Login a user by providing email credentials (email case-sensitivity off)', function(done) {
|
||||||
|
User.settings.caseSensitiveEmail = false;
|
||||||
|
User.login(validMixedCaseEmailCredentials, function(err, accessToken) {
|
||||||
|
assert(accessToken.userId);
|
||||||
|
assert(accessToken.id);
|
||||||
|
assert.equal(accessToken.id.length, 64);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Try to login with invalid email case', function(done) {
|
||||||
|
User.login(validMixedCaseEmailCredentials, function(err, accessToken) {
|
||||||
|
assert(err);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('Login a user by providing credentials with TTL', function(done) {
|
it('Login a user by providing credentials with TTL', function(done) {
|
||||||
User.login(validCredentialsWithTTL, function(err, accessToken) {
|
User.login(validCredentialsWithTTL, function(err, accessToken) {
|
||||||
assert(accessToken.userId);
|
assert(accessToken.userId);
|
||||||
|
@ -477,7 +555,6 @@ describe('User', function() {
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
function assertGoodToken(accessToken) {
|
function assertGoodToken(accessToken) {
|
||||||
|
|
Loading…
Reference in New Issue