diff --git a/staff/marti-herms/project/V-HUB/com/errors.js b/staff/marti-herms/project/V-HUB/com/errors.js index d4e54a279..18989e4e9 100644 --- a/staff/marti-herms/project/V-HUB/com/errors.js +++ b/staff/marti-herms/project/V-HUB/com/errors.js @@ -22,10 +22,28 @@ class DuplicityError extends Error { } } +class NotFoundError extends Error { + constructor(message) { + super(message) + + this.name = this.constructor.name + } +} + +class CredentialsError extends Error { + constructor(message) { + super(message) + + this.name = this.constructor.name + } +} + const errors = { ValidationError, SystemError, - DuplicityError + DuplicityError, + NotFoundError, + CredentialsError } export default errors \ No newline at end of file diff --git a/staff/marti-herms/project/V-HUB/core/logic/authenticateUser.js b/staff/marti-herms/project/V-HUB/core/logic/authenticateUser.js index e69de29bb..c2150e945 100644 --- a/staff/marti-herms/project/V-HUB/core/logic/authenticateUser.js +++ b/staff/marti-herms/project/V-HUB/core/logic/authenticateUser.js @@ -0,0 +1,26 @@ +import bcrypt from 'bcryptjs' + +import { User } from '../data/models.js' + +import { validate, errors } from 'com' + +const { NotFoundError, CredentialsError, SystemError } = errors + +export default (username, password) => { + validate.username(username) + validate.password(password) + + return User.findOne({ username }).lean() + .catch(error => { throw new SystemError(error.message) }) + .then(user => { + if (!user) throw new NotFoundError('user not found') + + return bcrypt.compare(password, user.password) + .catch(error => { throw new SystemError(error.message) }) + .then(match => { + if (!match) throw new CredentialsError('wrong password') + + return user._id.toString() + }) + }) +} \ No newline at end of file diff --git a/staff/marti-herms/project/V-HUB/core/logic/authenticateUser.spec.js b/staff/marti-herms/project/V-HUB/core/logic/authenticateUser.spec.js new file mode 100644 index 000000000..33fb82ca3 --- /dev/null +++ b/staff/marti-herms/project/V-HUB/core/logic/authenticateUser.spec.js @@ -0,0 +1,125 @@ +import 'dotenv/config' +import mongoose from 'mongoose' +import { expect } from 'chai' +import bcrypt from 'bcryptjs' + +import authenticateUser from './authenticateUser.js' +import { User } from '../data/models.js' + +import { errors } from 'com' + +const { NotFoundError, CredentialsError, ValidationError } = errors + +describe('authenticateUser', () => { + before(() => mongoose.connect(process.env.MONGODB_URI)) + + beforeEach(() => User.deleteMany()) + + it('succeeds on existing user and correct password', () => + bcrypt.hash('123123123', 8) + .then(hash => { + return User.create({ username: 'monoloco', email: 'mono@loco.com', password: hash, role: false }) + .then(user => { + return authenticateUser('monoloco', '123123123') + .then(id => expect(id).to.equal(user.id)) + }) + }) + ) + + it('fails on non-existing user', () => { + let error + + authenticateUser('monoloco', '123123123') + .catch(_error => error = _error) + .finally(() => { + expect(error).to.be.instanceOf(NotFoundError) + expect(error.message).to.equal('user not found') + }) + }) + + + it('fails on wrong password', () => { + let error + + return bcrypt.hash('123123123', 8) + .then(hash => + User.create({ name: 'Mono', surname: 'Loco', email: 'mono@loco.com', username: 'monoloco', password: hash }) + ) + .then(() => authenticateUser('monoloco', '111111111')) + .catch(_error => error = _error) + .finally(() => { + expect(error).to.be.instanceOf(CredentialsError) + expect(error.message).to.equal('wrong password') + }) + + }) + + it('fails on non-string username', () => { + let error + + try { + authenticateUser(123, '123123123') + } catch (_error) { + error = _error + } finally { + expect(error).to.be.instanceOf(ValidationError) + expect(error.message).to.equal('username is not a string') + } + }) + + it('fails on invalid username', () => { + let error + + try { + authenticateUser('', '123123123') + } catch (_error) { + error = _error + } finally { + expect(error).to.be.instanceOf(ValidationError) + expect(error.message).to.equal('invalid username') + } + }) + + it('fails on non-string password', () => { + let error + + try { + authenticateUser('monoloco', 123) + } catch (_error) { + error = _error + } finally { + expect(error).to.be.instanceOf(ValidationError) + expect(error.message).to.equal('password is not a string') + } + }) + + it('fails on password short', () => { + let error + + try { + authenticateUser('monoloco', '132') + } catch (_error) { + error = _error + } finally { + expect(error).to.be.instanceOf(ValidationError) + expect(error.message).to.equal('password length is lower than 8 characters') + } + }) + + it('fails on password with spaces', () => { + let error + + try { + authenticateUser('monoloco', '123 123132') + } catch (_error) { + error = _error + } finally { + expect(error).to.be.instanceOf(ValidationError) + expect(error.message).to.equal('password has empty spaces') + } + }) + + afterEach(() => User.deleteMany()) + + after(() => mongoose.disconnect()) +}) \ No newline at end of file diff --git a/staff/marti-herms/project/V-HUB/core/logic/authenticateUser.test.js b/staff/marti-herms/project/V-HUB/core/logic/authenticateUser.test.js new file mode 100644 index 000000000..52024c2b2 --- /dev/null +++ b/staff/marti-herms/project/V-HUB/core/logic/authenticateUser.test.js @@ -0,0 +1,16 @@ +import 'dotenv/config' +import bcrypt from 'bcryptjs' + +import authenticateUser from './authenticateUser.js' + +import { User } from '../data/models.js' + +import mongoose from 'mongoose' + +mongoose.connect(process.env.MONGODB_URI) + .then(() => bcrypt.hash('123123123', 8)) + .then(hash => User.create({ username: 'eden', email: 'marti@herms.com', password: hash })) + .then(() => authenticateUser('eden', '123123123')) + .then(userId => console.log('user authenticated', userId)) + .catch(error => console.error(error)) + .finally(() => mongoose.disconnect()) \ No newline at end of file