mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01:00 
			
		
		
		
	feat: Add change-password endpoint to user-controller (#800)
fixes: #801 Co-authored-by: Fredrik Oseberg <fredrik.no@gmail.com>
This commit is contained in:
		
							parent
							
								
									185091174f
								
							
						
					
					
						commit
						18f66ef732
					
				| @ -4,7 +4,7 @@ | ||||
|     }, | ||||
|     "extends": [ | ||||
|         "airbnb-typescript/base", | ||||
|         "prettier", | ||||
|         "prettier" | ||||
|     ], | ||||
|     "parser": "@typescript-eslint/parser", | ||||
|     "parserOptions": { | ||||
|  | ||||
| @ -17,6 +17,8 @@ const currentUser = new User({ email: 'test@mail.com' }); | ||||
| function getSetup() { | ||||
|     const base = `/random${Math.round(Math.random() * 1000)}`; | ||||
|     const stores = store.createStores(); | ||||
|     stores.userStore.insert(currentUser); | ||||
| 
 | ||||
|     const config = createTestConfig({ | ||||
|         preHook: a => { | ||||
|             a.use((req, res, next) => { | ||||
| @ -30,12 +32,12 @@ function getSetup() { | ||||
|     const app = getApp(config, stores, services, eventBus); | ||||
|     return { | ||||
|         base, | ||||
|         strategyStore: stores.strategyStore, | ||||
|         userStore: stores.userStore, | ||||
|         request: supertest(app), | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| test.only('should return current user', t => { | ||||
| test('should return current user', t => { | ||||
|     t.plan(1); | ||||
|     const { request, base } = getSetup(); | ||||
| 
 | ||||
| @ -47,13 +49,35 @@ test.only('should return current user', t => { | ||||
|             t.is(res.body.user.email, currentUser.email); | ||||
|         }); | ||||
| }); | ||||
| const owaspPassword = 't7GTx&$Y9pcsnxRv6'; | ||||
| 
 | ||||
| test('should logout and redirect', t => { | ||||
| test('should allow user to change password', async t => { | ||||
|     t.plan(2); | ||||
|     const { request, base, userStore } = getSetup(); | ||||
|     const before = await userStore.get(currentUser); | ||||
|     t.falsy(before.passwordHash); | ||||
|     await request | ||||
|         .post(`${base}/api/admin/user/change-password`) | ||||
|         .send({ password: owaspPassword, confirmPassword: owaspPassword }) | ||||
|         .expect(200); | ||||
|     const updated = await userStore.get(currentUser); | ||||
|     t.truthy(updated.passwordHash); | ||||
| }); | ||||
| 
 | ||||
| test('should deny if password and confirmPassword are not equal', async t => { | ||||
|     t.plan(0); | ||||
|     const { request, base } = getSetup(); | ||||
| 
 | ||||
|     return request | ||||
|         .get(`${base}/api/admin/user/logout`) | ||||
|         .expect(302) | ||||
|         .expect('Location', `${base}/`); | ||||
|         .post(`${base}/api/admin/user/change-password`) | ||||
|         .send({ password: owaspPassword, confirmPassword: 'somethingelse' }) | ||||
|         .expect(400); | ||||
| }); | ||||
| 
 | ||||
| test('should deny if password does not fulfill owasp criteria', async t => { | ||||
|     t.plan(0); | ||||
|     const { request, base } = getSetup(); | ||||
|     return request | ||||
|         .post(`${base}/api/admin/user/change-password`) | ||||
|         .send({ password: 'hunter123', confirmPassword: 'hunter123' }) | ||||
|         .expect(400); | ||||
| }); | ||||
|  | ||||
| @ -1,24 +1,47 @@ | ||||
| 'use strict'; | ||||
| 
 | ||||
| import { Response } from 'express'; | ||||
| import { Request, Response } from 'express'; | ||||
| import { IAuthRequest } from '../unleash-types'; | ||||
| import Controller from '../controller'; | ||||
| import { AccessService } from '../../services/access-service'; | ||||
| import { IUnleashConfig } from '../../types/option'; | ||||
| import { IUnleashServices } from '../../types/services'; | ||||
| import UserService from '../../services/user-service'; | ||||
| import User from '../../user'; | ||||
| import { Logger } from '../../logger'; | ||||
| import { handleErrors } from './util'; | ||||
| 
 | ||||
| interface IService { | ||||
|     accessService: AccessService; | ||||
| interface IChangeUserRequest { | ||||
|     password: string; | ||||
|     confirmPassword: string; | ||||
| } | ||||
| 
 | ||||
| interface UserRequest<PARAM, QUERY, BODY, RESPONSE> | ||||
|     extends Request<PARAM, QUERY, BODY, RESPONSE> { | ||||
|     user: User; | ||||
| } | ||||
| 
 | ||||
| class UserController extends Controller { | ||||
|     private accessService: AccessService; | ||||
| 
 | ||||
|     constructor(config: IUnleashConfig, { accessService }: IService) { | ||||
|     private userService: UserService; | ||||
| 
 | ||||
|     private logger: Logger; | ||||
| 
 | ||||
|     constructor( | ||||
|         config: IUnleashConfig, | ||||
|         { | ||||
|             accessService, | ||||
|             userService, | ||||
|         }: Pick<IUnleashServices, 'accessService' | 'userService'>, | ||||
|     ) { | ||||
|         super(config); | ||||
|         this.accessService = accessService; | ||||
|         this.userService = userService; | ||||
|         this.logger = config.getLogger('lib/routes/admin-api/user.ts'); | ||||
| 
 | ||||
|         this.get('/', this.getUser); | ||||
|         this.get('/logout', this.logout); | ||||
|         this.post('/change-password', this.updateUserPass); | ||||
|     } | ||||
| 
 | ||||
|     async getUser(req: IAuthRequest, res: Response): Promise<void> { | ||||
| @ -36,15 +59,27 @@ class UserController extends Controller { | ||||
|         return res.status(404).end(); | ||||
|     } | ||||
| 
 | ||||
|     // Deprecated, use "/logout" instead.  Will be removed in v4.
 | ||||
|     logout(req: IAuthRequest, res: Response): void { | ||||
|         if (req.session) { | ||||
|             req.session = null; | ||||
|     async updateUserPass( | ||||
|         req: UserRequest<any, any, IChangeUserRequest, any>, | ||||
|         res: Response, | ||||
|     ): Promise<void> { | ||||
|         const { user } = req; | ||||
|         if (user) { | ||||
|             const { password, confirmPassword } = req.body; | ||||
|             try { | ||||
|                 if (password === confirmPassword) { | ||||
|                     this.userService.validatePassword(password); | ||||
|                     await this.userService.changePassword(user.id, password); | ||||
|                     res.status(200).end(); | ||||
|                 } else { | ||||
|                     res.status(400).end(); | ||||
|                 } | ||||
|             } catch (e) { | ||||
|                 handleErrors(res, this.logger, e); | ||||
|             } | ||||
|         } else { | ||||
|             res.status(401).end(); | ||||
|         } | ||||
|         if (req.logout) { | ||||
|             req.logout(); | ||||
|         } | ||||
|         res.redirect(`${this.config.server.baseUriPath}/`); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user