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": [
 | 
					    "extends": [
 | 
				
			||||||
        "airbnb-typescript/base",
 | 
					        "airbnb-typescript/base",
 | 
				
			||||||
        "prettier",
 | 
					        "prettier"
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
    "parser": "@typescript-eslint/parser",
 | 
					    "parser": "@typescript-eslint/parser",
 | 
				
			||||||
    "parserOptions": {
 | 
					    "parserOptions": {
 | 
				
			||||||
 | 
				
			|||||||
@ -17,6 +17,8 @@ const currentUser = new User({ email: 'test@mail.com' });
 | 
				
			|||||||
function getSetup() {
 | 
					function getSetup() {
 | 
				
			||||||
    const base = `/random${Math.round(Math.random() * 1000)}`;
 | 
					    const base = `/random${Math.round(Math.random() * 1000)}`;
 | 
				
			||||||
    const stores = store.createStores();
 | 
					    const stores = store.createStores();
 | 
				
			||||||
 | 
					    stores.userStore.insert(currentUser);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const config = createTestConfig({
 | 
					    const config = createTestConfig({
 | 
				
			||||||
        preHook: a => {
 | 
					        preHook: a => {
 | 
				
			||||||
            a.use((req, res, next) => {
 | 
					            a.use((req, res, next) => {
 | 
				
			||||||
@ -30,12 +32,12 @@ function getSetup() {
 | 
				
			|||||||
    const app = getApp(config, stores, services, eventBus);
 | 
					    const app = getApp(config, stores, services, eventBus);
 | 
				
			||||||
    return {
 | 
					    return {
 | 
				
			||||||
        base,
 | 
					        base,
 | 
				
			||||||
        strategyStore: stores.strategyStore,
 | 
					        userStore: stores.userStore,
 | 
				
			||||||
        request: supertest(app),
 | 
					        request: supertest(app),
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
test.only('should return current user', t => {
 | 
					test('should return current user', t => {
 | 
				
			||||||
    t.plan(1);
 | 
					    t.plan(1);
 | 
				
			||||||
    const { request, base } = getSetup();
 | 
					    const { request, base } = getSetup();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -47,13 +49,35 @@ test.only('should return current user', t => {
 | 
				
			|||||||
            t.is(res.body.user.email, currentUser.email);
 | 
					            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);
 | 
					    t.plan(0);
 | 
				
			||||||
    const { request, base } = getSetup();
 | 
					    const { request, base } = getSetup();
 | 
				
			||||||
 | 
					 | 
				
			||||||
    return request
 | 
					    return request
 | 
				
			||||||
        .get(`${base}/api/admin/user/logout`)
 | 
					        .post(`${base}/api/admin/user/change-password`)
 | 
				
			||||||
        .expect(302)
 | 
					        .send({ password: owaspPassword, confirmPassword: 'somethingelse' })
 | 
				
			||||||
        .expect('Location', `${base}/`);
 | 
					        .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';
 | 
					'use strict';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { Response } from 'express';
 | 
					import { Request, Response } from 'express';
 | 
				
			||||||
import { IAuthRequest } from '../unleash-types';
 | 
					import { IAuthRequest } from '../unleash-types';
 | 
				
			||||||
import Controller from '../controller';
 | 
					import Controller from '../controller';
 | 
				
			||||||
import { AccessService } from '../../services/access-service';
 | 
					import { AccessService } from '../../services/access-service';
 | 
				
			||||||
import { IUnleashConfig } from '../../types/option';
 | 
					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 {
 | 
					interface IChangeUserRequest {
 | 
				
			||||||
    accessService: AccessService;
 | 
					    password: string;
 | 
				
			||||||
 | 
					    confirmPassword: string;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					interface UserRequest<PARAM, QUERY, BODY, RESPONSE>
 | 
				
			||||||
 | 
					    extends Request<PARAM, QUERY, BODY, RESPONSE> {
 | 
				
			||||||
 | 
					    user: User;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class UserController extends Controller {
 | 
					class UserController extends Controller {
 | 
				
			||||||
    private accessService: AccessService;
 | 
					    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);
 | 
					        super(config);
 | 
				
			||||||
        this.accessService = accessService;
 | 
					        this.accessService = accessService;
 | 
				
			||||||
 | 
					        this.userService = userService;
 | 
				
			||||||
 | 
					        this.logger = config.getLogger('lib/routes/admin-api/user.ts');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.get('/', this.getUser);
 | 
					        this.get('/', this.getUser);
 | 
				
			||||||
        this.get('/logout', this.logout);
 | 
					        this.post('/change-password', this.updateUserPass);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async getUser(req: IAuthRequest, res: Response): Promise<void> {
 | 
					    async getUser(req: IAuthRequest, res: Response): Promise<void> {
 | 
				
			||||||
@ -36,15 +59,27 @@ class UserController extends Controller {
 | 
				
			|||||||
        return res.status(404).end();
 | 
					        return res.status(404).end();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Deprecated, use "/logout" instead.  Will be removed in v4.
 | 
					    async updateUserPass(
 | 
				
			||||||
    logout(req: IAuthRequest, res: Response): void {
 | 
					        req: UserRequest<any, any, IChangeUserRequest, any>,
 | 
				
			||||||
        if (req.session) {
 | 
					        res: Response,
 | 
				
			||||||
            req.session = null;
 | 
					    ): 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();
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
        if (req.logout) {
 | 
					            } catch (e) {
 | 
				
			||||||
            req.logout();
 | 
					                handleErrors(res, this.logger, e);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            res.status(401).end();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        res.redirect(`${this.config.server.baseUriPath}/`);
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user