mirror of
https://github.com/Unleash/unleash.git
synced 2025-08-27 13:49:10 +02: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();
|
||||||
|
}
|
||||||
|
} 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