1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-06-23 01:16:27 +02:00
unleash.unleash/src/lib/routes/admin-api/user/user.ts
Ivar Conradi Østhus f00eac0881
fix: User audit events (create, update, delete) should include rootRole. (#5399)
Audit events for USER_CREATE, USER_UPDATE and USER_DELETE did not
include the users rootRole.


![image](https://github.com/Unleash/unleash/assets/158948/fcbc1407-e4f0-438f-86cf-7073205cd8c2)

---------

Co-authored-by: Gastón Fournier <gaston@getunleash.io>
2023-11-24 16:06:37 +01:00

210 lines
7.0 KiB
TypeScript

import { Response } from 'express';
import { IAuthRequest } from '../../unleash-types';
import Controller from '../../controller';
import { AccessService } from '../../../services/access-service';
import { IAuthType, IUnleashConfig } from '../../../types/option';
import { IUnleashServices } from '../../../types/services';
import UserService from '../../../services/user-service';
import UserFeedbackService from '../../../services/user-feedback-service';
import UserSplashService from '../../../services/user-splash-service';
import { ADMIN, NONE } from '../../../types/permissions';
import { OpenApiService } from '../../../services/openapi-service';
import { createRequestSchema } from '../../../openapi/util/create-request-schema';
import { createResponseSchema } from '../../../openapi/util/create-response-schema';
import { meSchema, MeSchema } from '../../../openapi/spec/me-schema';
import { serializeDates } from '../../../types/serialize-dates';
import { IUserPermission } from '../../../types/stores/access-store';
import { PasswordSchema } from '../../../openapi/spec/password-schema';
import {
emptyResponse,
getStandardResponses,
} from '../../../openapi/util/standard-responses';
import {
profileSchema,
ProfileSchema,
} from '../../../openapi/spec/profile-schema';
import ProjectService from '../../../services/project-service';
class UserController extends Controller {
private accessService: AccessService;
private userService: UserService;
private userFeedbackService: UserFeedbackService;
private userSplashService: UserSplashService;
private openApiService: OpenApiService;
private projectService: ProjectService;
constructor(
config: IUnleashConfig,
{
accessService,
userService,
userFeedbackService,
userSplashService,
openApiService,
projectService,
}: Pick<
IUnleashServices,
| 'accessService'
| 'userService'
| 'userFeedbackService'
| 'userSplashService'
| 'openApiService'
| 'projectService'
>,
) {
super(config);
this.accessService = accessService;
this.userService = userService;
this.userFeedbackService = userFeedbackService;
this.userSplashService = userSplashService;
this.openApiService = openApiService;
this.projectService = projectService;
this.route({
method: 'get',
path: '',
handler: this.getMe,
permission: NONE,
middleware: [
openApiService.validPath({
tags: ['Users'],
operationId: 'getMe',
summary: 'Get your own user details',
description:
'Detailed information about the current user, user permissions and user feedback',
responses: {
200: createResponseSchema('meSchema'),
...getStandardResponses(401),
},
}),
],
});
this.route({
method: 'get',
path: '/profile',
handler: this.getProfile,
permission: NONE,
middleware: [
openApiService.validPath({
tags: ['Users'],
operationId: 'getProfile',
summary: 'Get your own user profile',
description:
'Detailed information about the current user root role and project membership',
responses: {
200: createResponseSchema('profileSchema'),
...getStandardResponses(401),
},
}),
],
});
this.route({
method: 'post',
path: '/change-password',
handler: this.changeMyPassword,
permission: NONE,
middleware: [
openApiService.validPath({
tags: ['Users'],
operationId: 'changeMyPassword',
summary: 'Change your own password',
description:
'Requires specifying old password and confirming new password',
requestBody: createRequestSchema('passwordSchema'),
responses: {
200: emptyResponse,
400: {
description: 'Old and new password do not match',
},
401: {
description:
'Old password is incorrect or user is not authenticated',
},
},
}),
],
});
}
async getMe(req: IAuthRequest, res: Response<MeSchema>): Promise<void> {
res.setHeader('cache-control', 'no-store');
const { user } = req;
let permissions: IUserPermission[];
if (this.config.authentication.type === IAuthType.NONE) {
permissions = [{ permission: ADMIN }];
} else {
permissions = await this.accessService.getPermissionsForUser(user);
}
const feedback = await this.userFeedbackService.getAllUserFeedback(
user,
);
const splash = await this.userSplashService.getAllUserSplashes(user);
const responseData: MeSchema = {
user: serializeDates(user),
permissions,
feedback: serializeDates(feedback),
splash,
};
this.openApiService.respondWithValidation(
200,
res,
meSchema.$id,
responseData,
);
}
async getProfile(
req: IAuthRequest,
res: Response<ProfileSchema>,
): Promise<void> {
const { user } = req;
const projects = await this.projectService.getProjectsByUser(user.id);
const rootRole = await this.accessService.getRootRoleForUser(user.id);
const responseData: ProfileSchema = {
projects,
rootRole,
features: [],
};
this.openApiService.respondWithValidation(
200,
res,
profileSchema.$id,
responseData,
);
}
async changeMyPassword(
req: IAuthRequest<unknown, unknown, PasswordSchema>,
res: Response,
): Promise<void> {
const { user } = req;
const { password, confirmPassword, oldPassword } = req.body;
if (password === confirmPassword && oldPassword != null) {
this.userService.validatePassword(password);
await this.userService.changePasswordWithVerification(
user.id,
password,
oldPassword,
);
res.status(200).end();
} else {
res.status(400).end();
}
}
}
module.exports = UserController;
export default UserController;