Rename old UserService to AuthService, LoginRoutes to AuthRoutes
This commit is contained in:
parent
0a1eabadd9
commit
5cf54d0be9
@ -20,7 +20,7 @@ export interface IAPIDef {
|
||||
'/auth/logout': {
|
||||
'get': {}
|
||||
}
|
||||
'/users/password': {
|
||||
'/auth/password': {
|
||||
'post': {
|
||||
body: {
|
||||
oldPassword: string
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import {IUserService} from '../services'
|
||||
import {IAuthService} from '../services'
|
||||
import {ITeamService} from '../team'
|
||||
import {IUserPermissions} from '../user'
|
||||
|
||||
export interface IServices {
|
||||
userService: IUserService
|
||||
authService: IAuthService
|
||||
teamService: ITeamService
|
||||
userPermissions: IUserPermissions
|
||||
}
|
||||
|
||||
@ -26,12 +26,12 @@ export const configureServer: ServerConfigurator = (config, database) => {
|
||||
const logger = loggerFactory.getLogger('api')
|
||||
|
||||
const services: IServices = {
|
||||
userService: new Services.UserService(database),
|
||||
authService: new Services.AuthService(database),
|
||||
teamService: new Team.TeamService(database),
|
||||
userPermissions: new User.UserPermissions(database),
|
||||
}
|
||||
|
||||
const authenticator = new Middleware.Authenticator(services.userService)
|
||||
const authenticator = new Middleware.Authenticator(services.authService)
|
||||
const transactionManager = database.transactionManager
|
||||
|
||||
const createTransactionalRouter = <T extends IRoutes>() =>
|
||||
@ -73,13 +73,13 @@ export const configureServer: ServerConfigurator = (config, database) => {
|
||||
api: {
|
||||
path: '/api',
|
||||
handle: [
|
||||
new routes.LoginRoutes(
|
||||
services.userService,
|
||||
new routes.AuthRoutes(
|
||||
services.authService,
|
||||
authenticator,
|
||||
createTransactionalRouter(),
|
||||
).handle,
|
||||
new routes.UserRoutes(
|
||||
services.userService,
|
||||
services.authService,
|
||||
createTransactionalRouter(),
|
||||
).handle,
|
||||
new Team.TeamRoutes(
|
||||
|
||||
@ -2,11 +2,11 @@ import express, {Application} from 'express'
|
||||
import request from 'supertest'
|
||||
import {Authenticator} from './Authenticator'
|
||||
import {ICredentials} from '@rondo.dev/common'
|
||||
import {IUserService} from '../services'
|
||||
import {IAuthService} from '../services'
|
||||
import {handlePromise} from './handlePromise'
|
||||
import {urlencoded} from 'body-parser'
|
||||
|
||||
describe('passport.promise', () => {
|
||||
describe('Authenticator', () => {
|
||||
|
||||
let app: Application
|
||||
let loginMiddleware: any
|
||||
@ -18,7 +18,7 @@ describe('passport.promise', () => {
|
||||
firstName: 'test',
|
||||
lastName: 'test',
|
||||
}
|
||||
const userService = new (class implements IUserService {
|
||||
const authService = new (class implements IAuthService {
|
||||
async createUser() {
|
||||
return {id: 1, ...userInfo}
|
||||
}
|
||||
@ -40,7 +40,7 @@ describe('passport.promise', () => {
|
||||
return undefined
|
||||
}
|
||||
})()
|
||||
const authenticator = new Authenticator(userService)
|
||||
const authenticator = new Authenticator(authService)
|
||||
|
||||
app.use(urlencoded({ extended: false }))
|
||||
app.use(authenticator.handle)
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import {Authenticator as A, Passport} from 'passport'
|
||||
import {IUserService} from '../services'
|
||||
import {IAuthService} from '../services'
|
||||
import {Strategy as LocalStrategy} from 'passport-local'
|
||||
import {THandler} from './THandler'
|
||||
import {IMiddleware} from './IMiddleware'
|
||||
@ -9,7 +9,7 @@ export class Authenticator implements IMiddleware {
|
||||
protected readonly passport: A
|
||||
readonly handle: THandler[]
|
||||
|
||||
constructor(protected readonly userService: IUserService) {
|
||||
constructor(protected readonly authService: IAuthService) {
|
||||
this.passport = new Passport() as any
|
||||
|
||||
this.configurePassport()
|
||||
@ -45,7 +45,7 @@ export class Authenticator implements IMiddleware {
|
||||
protected deserializeUser =
|
||||
// TODO parametrize user type
|
||||
(userId: number, done: (err?: Error, user?: any) => void) => {
|
||||
this.userService.findOne(userId)
|
||||
this.authService.findOne(userId)
|
||||
.then(user => done(undefined, user))
|
||||
.catch(done)
|
||||
}
|
||||
@ -67,7 +67,7 @@ export class Authenticator implements IMiddleware {
|
||||
password: string,
|
||||
done: (err?: Error, user?: any) => void,
|
||||
) => {
|
||||
this.userService.validateCredentials({ username, password })
|
||||
this.authService.validateCredentials({ username, password })
|
||||
.then(user => done(undefined, user))
|
||||
.catch(done)
|
||||
}
|
||||
|
||||
78
packages/server/src/routes/AuthRoutes.test.ts
Normal file
78
packages/server/src/routes/AuthRoutes.test.ts
Normal file
@ -0,0 +1,78 @@
|
||||
import {test} from '../test'
|
||||
|
||||
describe('/auth', () => {
|
||||
|
||||
test.withDatabase()
|
||||
|
||||
describe('/register', () => {
|
||||
it('should create a new user account', async () => {
|
||||
await test.registerAccount()
|
||||
})
|
||||
})
|
||||
|
||||
describe('/login', () => {
|
||||
|
||||
beforeEach(async () => {
|
||||
await test.registerAccount()
|
||||
})
|
||||
|
||||
it('should log in the newly created user', async () => {
|
||||
await test.login()
|
||||
})
|
||||
})
|
||||
|
||||
describe('/auth/password', () => {
|
||||
|
||||
const t = test.request('/api')
|
||||
beforeEach(async () => {
|
||||
const session = await test.registerAccount()
|
||||
const token = session.token
|
||||
const cookie = session.cookie
|
||||
t.setHeaders({cookie, 'x-csrf-token': token})
|
||||
})
|
||||
|
||||
it('should prevent access when user not logged in', async () => {
|
||||
const {cookie, token} = await test.getCsrf()
|
||||
await t
|
||||
.setHeaders({'cookie': cookie, 'x-csrf-token': token})
|
||||
.post('/auth/password')
|
||||
.expect(401)
|
||||
})
|
||||
|
||||
describe('POST /users/password', () => {
|
||||
it('changes user password when passwords match', async () => {
|
||||
await t
|
||||
.post('/auth/password')
|
||||
.send({ oldPassword: test.password, newPassword: 'newPass' })
|
||||
.expect(200)
|
||||
|
||||
await test.login(test.username, 'newPass')
|
||||
})
|
||||
|
||||
it('returns 400 when passwords do not match', async () => {
|
||||
await t
|
||||
.post('/auth/password')
|
||||
.send({ oldPassword: 'invalid-password', newPassword: 'newPass' })
|
||||
.expect(400)
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('/logout', () => {
|
||||
|
||||
let cookie!: string
|
||||
beforeEach(async () => {
|
||||
await test.registerAccount()
|
||||
cookie = (await test.login()).cookie
|
||||
})
|
||||
|
||||
it('should log out the user', async () => {
|
||||
await test.request('/api')
|
||||
.get('/auth/logout')
|
||||
.set('cookie', cookie)
|
||||
.expect(200)
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
@ -1,12 +1,13 @@
|
||||
import {AsyncRouter} from '../router'
|
||||
import {BaseRoute} from './BaseRoute'
|
||||
import {IAPIDef} from '@rondo.dev/common'
|
||||
import {IUserService} from '../services'
|
||||
import {IAuthService} from '../services'
|
||||
import {Authenticator} from '../middleware'
|
||||
import {ensureLoggedInApi} from '../middleware'
|
||||
|
||||
export class LoginRoutes extends BaseRoute<IAPIDef> {
|
||||
export class AuthRoutes extends BaseRoute<IAPIDef> {
|
||||
constructor(
|
||||
protected readonly userService: IUserService,
|
||||
protected readonly authService: IAuthService,
|
||||
protected readonly authenticator: Authenticator,
|
||||
protected readonly t: AsyncRouter<IAPIDef>,
|
||||
) {
|
||||
@ -15,7 +16,7 @@ export class LoginRoutes extends BaseRoute<IAPIDef> {
|
||||
|
||||
setup(t: AsyncRouter<IAPIDef>) {
|
||||
t.post('/auth/register', async (req, res) => {
|
||||
const user = await this.userService.createUser({
|
||||
const user = await this.authService.createUser({
|
||||
username: req.body.username,
|
||||
password: req.body.password,
|
||||
firstName: req.body.firstName,
|
||||
@ -37,6 +38,14 @@ export class LoginRoutes extends BaseRoute<IAPIDef> {
|
||||
return user
|
||||
})
|
||||
|
||||
t.post('/auth/password', [ensureLoggedInApi], async req => {
|
||||
await this.authService.changePassword({
|
||||
userId: req.user!.id,
|
||||
oldPassword: req.body.oldPassword,
|
||||
newPassword: req.body.newPassword,
|
||||
})
|
||||
})
|
||||
|
||||
t.get('/auth/logout', async (req, res) => {
|
||||
req.logout()
|
||||
})
|
||||
@ -1,40 +0,0 @@
|
||||
import {test} from '../test'
|
||||
|
||||
describe('login', () => {
|
||||
|
||||
test.withDatabase()
|
||||
|
||||
describe('/register', () => {
|
||||
it('should create a new user account', async () => {
|
||||
await test.registerAccount()
|
||||
})
|
||||
})
|
||||
|
||||
describe('/login', () => {
|
||||
|
||||
beforeEach(async () => {
|
||||
await test.registerAccount()
|
||||
})
|
||||
|
||||
it('should log in the newly created user', async () => {
|
||||
await test.login()
|
||||
})
|
||||
})
|
||||
|
||||
describe('/logout', () => {
|
||||
|
||||
let cookie!: string
|
||||
beforeEach(async () => {
|
||||
await test.registerAccount()
|
||||
cookie = (await test.login()).cookie
|
||||
})
|
||||
|
||||
it('should log out the user', async () => {
|
||||
await test.request('/api')
|
||||
.get('/auth/logout')
|
||||
.set('cookie', cookie)
|
||||
.expect(200)
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
@ -15,31 +15,6 @@ describe('user', () => {
|
||||
t.setHeaders({ cookie, 'x-csrf-token': token })
|
||||
})
|
||||
|
||||
it('should prevent access when user not logged in', async () => {
|
||||
await t
|
||||
.setHeaders({ token })
|
||||
.get(`/users/password`)
|
||||
.expect(401)
|
||||
})
|
||||
|
||||
describe('POST /users/password', () => {
|
||||
it('changes user password when passwords match', async () => {
|
||||
await t
|
||||
.post('/users/password')
|
||||
.send({ oldPassword: test.password, newPassword: 'newPass' })
|
||||
.expect(200)
|
||||
|
||||
await test.login(test.username, 'newPass')
|
||||
})
|
||||
|
||||
it('returns 400 when passwords do not match', async () => {
|
||||
await t
|
||||
.post('/users/password')
|
||||
.send({ oldPassword: 'invalid-password', newPassword: 'newPass' })
|
||||
.expect(400)
|
||||
})
|
||||
})
|
||||
|
||||
describe('GET /users/profile', () => {
|
||||
it('fetches user profile', async () => {
|
||||
t.setHeaders({ cookie })
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
import {AsyncRouter} from '../router'
|
||||
import {BaseRoute} from './BaseRoute'
|
||||
import {IAPIDef} from '@rondo.dev/common'
|
||||
import {IUserService} from '../services'
|
||||
import {IAuthService} from '../services'
|
||||
import {ensureLoggedInApi} from '../middleware'
|
||||
|
||||
export class UserRoutes extends BaseRoute<IAPIDef> {
|
||||
constructor(
|
||||
protected readonly userService: IUserService,
|
||||
protected readonly userService: IAuthService,
|
||||
protected readonly t: AsyncRouter<IAPIDef>,
|
||||
) {
|
||||
super(t)
|
||||
@ -15,14 +15,6 @@ export class UserRoutes extends BaseRoute<IAPIDef> {
|
||||
setup(t: AsyncRouter<IAPIDef>) {
|
||||
t.use('/users', ensureLoggedInApi)
|
||||
|
||||
t.post('/users/password', async req => {
|
||||
await this.userService.changePassword({
|
||||
userId: req.user!.id,
|
||||
oldPassword: req.body.oldPassword,
|
||||
newPassword: req.body.newPassword,
|
||||
})
|
||||
})
|
||||
|
||||
t.get('/users/emails/:email', async req => {
|
||||
return this.userService.findUserByEmail(req.params.email)
|
||||
})
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
export * from './BaseRoute'
|
||||
export * from './LoginRoutes'
|
||||
export * from './UserRoutes'
|
||||
export * from './application'
|
||||
export * from './AuthRoutes'
|
||||
export * from './BaseRoute'
|
||||
export * from './UserRoutes'
|
||||
|
||||
@ -1,17 +1,17 @@
|
||||
import {test} from '../test'
|
||||
import {UserService} from './UserService'
|
||||
import {AuthService} from './AuthService'
|
||||
|
||||
describe('UserService', () => {
|
||||
describe('AuthService', () => {
|
||||
|
||||
test.withDatabase()
|
||||
|
||||
const username = test.username
|
||||
const password = '1234567890'
|
||||
|
||||
const userService = new UserService(test.bootstrap.database)
|
||||
const authService = new AuthService(test.bootstrap.database)
|
||||
|
||||
async function createUser(u = username, p = password) {
|
||||
return userService.createUser({
|
||||
return authService.createUser({
|
||||
username: u,
|
||||
password: p,
|
||||
firstName: 'test',
|
||||
@ -23,7 +23,7 @@ describe('UserService', () => {
|
||||
it('creates a new user with bcrypted password', async () => {
|
||||
const result = await createUser()
|
||||
expect(result.id).toBeTruthy()
|
||||
const user = await userService.findOne(result.id)
|
||||
const user = await authService.findOne(result.id)
|
||||
expect(user).toBeTruthy()
|
||||
expect(user).not.toHaveProperty('password')
|
||||
})
|
||||
@ -48,7 +48,7 @@ describe('UserService', () => {
|
||||
describe('findUserByMail', () => {
|
||||
it('returns user without password', async () => {
|
||||
await createUser()
|
||||
const user = await userService.findUserByEmail(username)
|
||||
const user = await authService.findUserByEmail(username)
|
||||
expect(user).toBeTruthy()
|
||||
expect(user).not.toHaveProperty('password')
|
||||
})
|
||||
@ -57,7 +57,7 @@ describe('UserService', () => {
|
||||
describe('getUserEmails', () => {
|
||||
it('returns user emails', async () => {
|
||||
const {id} = await createUser()
|
||||
const emails = await userService.findUserEmails(id)
|
||||
const emails = await authService.findUserEmails(id)
|
||||
expect(emails).toEqual([{
|
||||
id: jasmine.any(Number),
|
||||
userId: id,
|
||||
@ -71,24 +71,24 @@ describe('UserService', () => {
|
||||
describe('validateCredentials', () => {
|
||||
it('returns user when password is valid', async () => {
|
||||
await createUser()
|
||||
expect(await userService.validateCredentials({ username, password }))
|
||||
expect(await authService.validateCredentials({ username, password }))
|
||||
.toBeTruthy()
|
||||
})
|
||||
|
||||
it('returns undefined when no user', async () => {
|
||||
expect(await userService.validateCredentials({ username, password }))
|
||||
expect(await authService.validateCredentials({ username, password }))
|
||||
.toBe(undefined)
|
||||
})
|
||||
|
||||
it('returns undefined when password is invalid', async () => {
|
||||
await createUser()
|
||||
expect(await userService.validateCredentials({ username, password: 't' }))
|
||||
expect(await authService.validateCredentials({ username, password: 't' }))
|
||||
.toBe(undefined)
|
||||
})
|
||||
|
||||
it('does not return a password', async () => {
|
||||
await createUser()
|
||||
const user = await userService
|
||||
const user = await authService
|
||||
.validateCredentials({ username, password })
|
||||
expect(user).not.toHaveProperty('password')
|
||||
})
|
||||
@ -2,7 +2,7 @@ import createError from 'http-errors'
|
||||
import {BaseService} from './BaseService'
|
||||
import {IDatabase} from '../database/IDatabase'
|
||||
import {ICredentials, INewUser, IUser, trim} from '@rondo.dev/common'
|
||||
import {IUserService} from './IUserService'
|
||||
import {IAuthService} from './IAuthService'
|
||||
import {UserEmail} from '../entities/UserEmail'
|
||||
import {User} from '../entities/User'
|
||||
import {compare, hash} from 'bcrypt'
|
||||
@ -12,7 +12,7 @@ import {Validator} from '../validator'
|
||||
const SALT_ROUNDS = 10
|
||||
const MIN_PASSWORD_LENGTH = 10
|
||||
|
||||
export class UserService implements IUserService {
|
||||
export class AuthService implements IAuthService {
|
||||
constructor(protected readonly db: IDatabase) {}
|
||||
|
||||
async createUser(payload: INewUser): Promise<IUser> {
|
||||
@ -1,6 +1,6 @@
|
||||
import {ICredentials, INewUser, IUser} from '@rondo.dev/common'
|
||||
|
||||
export interface IUserService {
|
||||
export interface IAuthService {
|
||||
createUser(credentials: INewUser): Promise<IUser>
|
||||
changePassword(params: {
|
||||
userId: number,
|
||||
@ -1,3 +1,3 @@
|
||||
export * from './BaseService'
|
||||
export * from './IUserService'
|
||||
export * from './UserService'
|
||||
export * from './IAuthService'
|
||||
export * from './AuthService'
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user