diff --git a/src/command/rolePermissionCommand.ts b/src/command/rolePermissionCommand.ts index e86dfbd..dae6ea2 100644 --- a/src/command/rolePermissionCommand.ts +++ b/src/command/rolePermissionCommand.ts @@ -6,5 +6,11 @@ export interface CreateRolePermissionCommand { } export interface DeleteRolePermissionCommand { - id: number; + permission: PermissionString; + roleId: number; +} + +export interface UpdateRolePermissionsCommand { + roleId: number; + permissions: Array; } diff --git a/src/command/rolePermissionCommandHandler.ts b/src/command/rolePermissionCommandHandler.ts index a2fe2ac..63c803a 100644 --- a/src/command/rolePermissionCommandHandler.ts +++ b/src/command/rolePermissionCommandHandler.ts @@ -1,10 +1,58 @@ +import { EntityManager } from "typeorm"; import { dataSource } from "../data-source"; import { rolePermission } from "../entity/role_permission"; import InternalException from "../exceptions/internalException"; import RoleService from "../service/roleService"; -import { CreateRolePermissionCommand, DeleteRolePermissionCommand } from "./rolePermissionCommand"; +import { + CreateRolePermissionCommand, + DeleteRolePermissionCommand, + UpdateRolePermissionsCommand, +} from "./rolePermissionCommand"; +import PermissionHelper from "../helpers/permissionHelper"; +import RolePermissionService from "../service/rolePermissionService"; +import { role } from "../entity/role"; + +export default abstract class RolePermissionCommandHandler { + /** + * @description update role permissions + * @param UpdateRolePermissionsCommand + * @returns {Promise} + */ + static async updatePermissions(updateRolePermissions: UpdateRolePermissionsCommand): Promise { + let currentPermissions = (await RolePermissionService.getByRole(updateRolePermissions.roleId)).map( + (r) => r.permission + ); + return await dataSource.manager + .transaction(async (manager) => { + let newPermissions = PermissionHelper.getWhatToAdd(currentPermissions, updateRolePermissions.permissions); + let removePermissions = PermissionHelper.getWhatToRemove(currentPermissions, updateRolePermissions.permissions); + + for (let permission of newPermissions) { + await this.updatePermissionsAdd(manager, updateRolePermissions.roleId, permission); + } + + for (let permission of removePermissions) { + await this.updatePermissionsRemove(manager, updateRolePermissions.roleId, permission); + } + }) + .then(() => {}) + .catch((err) => { + throw new InternalException("Failed saving role permissions"); + }); + } + + private static async updatePermissionsAdd(manager: EntityManager, userId: number, permission: string): Promise { + return await manager.createQueryBuilder().relation(role, "permissions").of(userId).add(permission); + } + + private static async updatePermissionsRemove( + manager: EntityManager, + userId: number, + permission: string + ): Promise { + return await manager.createQueryBuilder().relation(role, "permissions").of(userId).remove(permission); + } -export default abstract class UserPermissionCommandHandler { /** * @description grant permission to user * @param CreateRolePermissionCommand @@ -38,7 +86,8 @@ export default abstract class UserPermissionCommandHandler { .createQueryBuilder() .delete() .from(rolePermission) - .where("permission.id = :id", { id: deletePermission.id }) + .where("roleId = :id", { id: deletePermission.roleId }) + .andWhere("permission = :permission", { permission: deletePermission.permission }) .execute() .then((res) => {}) .catch((err) => { diff --git a/src/command/userCommand.ts b/src/command/userCommand.ts index 1941645..a084115 100644 --- a/src/command/userCommand.ts +++ b/src/command/userCommand.ts @@ -5,3 +5,20 @@ export interface CreateUserCommand { lastname: string; secret: string; } + +export interface UpdateUserCommand { + id: number; + mail: string; + username: string; + firstname: string; + lastname: string; +} + +export interface UpdateUserRolesCommand { + id: number; + roleIds: Array; +} + +export interface DeleteUserCommand { + id: number; +} diff --git a/src/command/userCommandHandler.ts b/src/command/userCommandHandler.ts index 271f823..4eda082 100644 --- a/src/command/userCommandHandler.ts +++ b/src/command/userCommandHandler.ts @@ -1,7 +1,9 @@ +import { EntityManager } from "typeorm"; import { dataSource } from "../data-source"; import { user } from "../entity/user"; import InternalException from "../exceptions/internalException"; -import { CreateUserCommand } from "./userCommand"; +import { CreateUserCommand, DeleteUserCommand, UpdateUserCommand, UpdateUserRolesCommand } from "./userCommand"; +import UserService from "../service/userService"; export default abstract class UserCommandHandler { /** @@ -29,4 +31,79 @@ export default abstract class UserCommandHandler { throw new InternalException("Failed saving user"); }); } + + /** + * @description update user + * @param UpdateUserCommand + * @returns {Promise} + */ + static async update(updateUser: UpdateUserCommand): Promise { + return await dataSource + .createQueryBuilder() + .update(user) + .set({ + mail: updateUser.mail, + firstname: updateUser.firstname, + lastname: updateUser.lastname, + username: updateUser.username, + }) + .where("id := id", { id: updateUser.id }) + .execute() + .then(() => {}) + .catch((err) => { + throw new InternalException("Failed updating user"); + }); + } + + /** + * @description update user roles + * @param UpdateUserRolesCommand + * @returns {Promise} + */ + static async updateRoles(updateUserRoles: UpdateUserRolesCommand): Promise { + let currentRoles = (await UserService.getAssignedRolesByUserId(updateUserRoles.id)).map((r) => r.id); + return await dataSource.manager + .transaction(async (manager) => { + let newRoles = currentRoles.filter((r) => !updateUserRoles.roleIds.includes(r)); + let removeRoles = updateUserRoles.roleIds.filter((r) => !currentRoles.includes(r)); + + for (let role of newRoles) { + await this.updateRolesAdd(manager, updateUserRoles.id, role); + } + + for (let role of removeRoles) { + await this.updateRolesRemove(manager, updateUserRoles.id, role); + } + }) + .then(() => {}) + .catch((err) => { + throw new InternalException("Failed saving user roles"); + }); + } + + private static async updateRolesAdd(manager: EntityManager, userId: number, roleId: number): Promise { + return await manager.createQueryBuilder().relation(user, "roles").of(userId).add(roleId); + } + + private static async updateRolesRemove(manager: EntityManager, userId: number, roleId: number): Promise { + return await manager.createQueryBuilder().relation(user, "roles").of(userId).remove(roleId); + } + + /** + * @description delete user + * @param DeleteUserCommand + * @returns {Promise} + */ + static async delete(deleteUser: DeleteUserCommand): Promise { + return await dataSource + .createQueryBuilder() + .delete() + .from(user) + .where("id = :id", { id: deleteUser.id }) + .execute() + .then(() => {}) + .catch((err) => { + throw new InternalException("Failed deleting user"); + }); + } } diff --git a/src/command/userPermissionCommand.ts b/src/command/userPermissionCommand.ts index 8bd6e9d..44da15b 100644 --- a/src/command/userPermissionCommand.ts +++ b/src/command/userPermissionCommand.ts @@ -6,5 +6,11 @@ export interface CreateUserPermissionCommand { } export interface DeleteUserPermissionCommand { - id: number; + permission: PermissionString; + userId: number; +} + +export interface UpdateUserPermissionsCommand { + userId: number; + permissions: Array; } diff --git a/src/command/userPermissionCommandHandler.ts b/src/command/userPermissionCommandHandler.ts index d629677..e2379e2 100644 --- a/src/command/userPermissionCommandHandler.ts +++ b/src/command/userPermissionCommandHandler.ts @@ -1,10 +1,57 @@ +import { EntityManager } from "typeorm"; import { dataSource } from "../data-source"; +import { user } from "../entity/user"; import { userPermission } from "../entity/user_permission"; import InternalException from "../exceptions/internalException"; -import UserService from "../service/userService"; -import { CreateUserPermissionCommand, DeleteUserPermissionCommand } from "./userPermissionCommand"; +import { + CreateUserPermissionCommand, + DeleteUserPermissionCommand, + UpdateUserPermissionsCommand, +} from "./userPermissionCommand"; +import UserPermissionService from "../service/userPermissionService"; +import PermissionHelper from "../helpers/permissionHelper"; export default abstract class UserPermissionCommandHandler { + /** + * @description update user permissions + * @param UpdateUserPermissionsCommand + * @returns {Promise} + */ + static async updatePermissions(updateUserPermissions: UpdateUserPermissionsCommand): Promise { + let currentPermissions = (await UserPermissionService.getByUser(updateUserPermissions.userId)).map( + (r) => r.permission + ); + return await dataSource.manager + .transaction(async (manager) => { + let newPermissions = PermissionHelper.getWhatToAdd(currentPermissions, updateUserPermissions.permissions); + let removePermissions = PermissionHelper.getWhatToRemove(currentPermissions, updateUserPermissions.permissions); + + for (let permission of newPermissions) { + await this.updatePermissionsAdd(manager, updateUserPermissions.userId, permission); + } + + for (let permission of removePermissions) { + await this.updatePermissionsRemove(manager, updateUserPermissions.userId, permission); + } + }) + .then(() => {}) + .catch((err) => { + throw new InternalException("Failed saving user permissions"); + }); + } + + private static async updatePermissionsAdd(manager: EntityManager, userId: number, permission: string): Promise { + return await manager.createQueryBuilder().relation(user, "permissions").of(userId).add(permission); + } + + private static async updatePermissionsRemove( + manager: EntityManager, + userId: number, + permission: string + ): Promise { + return await manager.createQueryBuilder().relation(user, "permissions").of(userId).remove(permission); + } + /** * @description grant permission to user * @param CreateUserPermissionCommand @@ -17,7 +64,7 @@ export default abstract class UserPermissionCommandHandler { .into(userPermission) .values({ permission: createPermission.permission, - user: await UserService.getById(createPermission.userId), + userId: createPermission.userId, }) .execute() .then((result) => { @@ -38,7 +85,8 @@ export default abstract class UserPermissionCommandHandler { .createQueryBuilder() .delete() .from(userPermission) - .where("permission.id = :id", { id: deletePermission.id }) + .where("userId = :id", { id: deletePermission.userId }) + .andWhere("permission = :permission", { permission: deletePermission.permission }) .execute() .then((res) => {}) .catch((err) => { diff --git a/src/controller/admin/roleController.ts b/src/controller/admin/roleController.ts new file mode 100644 index 0000000..67f6f78 --- /dev/null +++ b/src/controller/admin/roleController.ts @@ -0,0 +1,121 @@ +import { Request, Response } from "express"; +import RoleService from "../../service/roleService"; +import RoleFactory from "../../factory/admin/role"; +import RolePermissionService from "../../service/rolePermissionService"; +import PermissionHelper from "../../helpers/permissionHelper"; +import { CreateRoleCommand, DeleteRoleCommand, UpdateRoleCommand } from "../../command/roleCommand"; +import RoleCommandHandler from "../../command/roleCommandHandler"; +import { UpdateRolePermissionsCommand } from "../../command/rolePermissionCommand"; +import RolePermissionCommandHandler from "../../command/rolePermissionCommandHandler"; + +/** + * @description get All roles + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function getAllRoles(req: Request, res: Response): Promise { + let roles = await RoleService.getAll(); + + res.json(RoleFactory.mapToBase(roles)); +} + +/** + * @description get role by id + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function getRoleById(req: Request, res: Response): Promise { + const id = parseInt(req.params.id); + let role = await RoleService.getById(id); + + res.json(RoleFactory.mapToSingle(role)); +} + +/** + * @description get permissions by role + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function getRolePermissions(req: Request, res: Response): Promise { + const id = parseInt(req.params.id); + let permissions = await RolePermissionService.getByRole(id); + + res.json(PermissionHelper.convertToObject(permissions.map((p) => p.permission))); +} + +/** + * @description create new role + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function createRole(req: Request, res: Response): Promise { + let role = req.body.role; + + let createRole: CreateRoleCommand = { + role: role, + }; + await RoleCommandHandler.create(createRole); + + res.sendStatus(204); +} + +/** + * @description update role data + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function updateRole(req: Request, res: Response): Promise { + const id = parseInt(req.params.id); + let role = req.body.role; + + let updateRole: UpdateRoleCommand = { + id: id, + role: role, + }; + await RoleCommandHandler.update(updateRole); + + res.sendStatus(204); +} + +/** + * @description update role assigned permission strings + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function updateRolePermissions(req: Request, res: Response): Promise { + const id = parseInt(req.params.id); + let permissions = req.body.permissions; + + let permissionStrings = PermissionHelper.convertToStringArray(permissions); + + let updateRolePermissions: UpdateRolePermissionsCommand = { + roleId: id, + permissions: permissionStrings, + }; + await RolePermissionCommandHandler.updatePermissions(updateRolePermissions); + + res.sendStatus(204); +} + +/** + * @description delete role by id + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function deleteRole(req: Request, res: Response): Promise { + const id = parseInt(req.params.id); + + let deleteRole: DeleteRoleCommand = { + id: id, + }; + await RoleCommandHandler.delete(deleteRole); + + res.sendStatus(204); +} diff --git a/src/controller/admin/userController.ts b/src/controller/admin/userController.ts new file mode 100644 index 0000000..6596c0a --- /dev/null +++ b/src/controller/admin/userController.ts @@ -0,0 +1,158 @@ +import { Request, Response } from "express"; +import UserService from "../../service/userService"; +import UserFactory from "../../factory/admin/user"; +import UserPermissionService from "../../service/userPermissionService"; +import PermissionHelper from "../../helpers/permissionHelper"; +import RoleFactory from "../../factory/admin/role"; +import { DeleteUserCommand, UpdateUserCommand, UpdateUserRolesCommand } from "../../command/userCommand"; +import UserCommandHandler from "../../command/userCommandHandler"; +import MailHelper from "../../helpers/mailHelper"; +import { CLUB_NAME } from "../../env.defaults"; +import { UpdateUserPermissionsCommand } from "../../command/userPermissionCommand"; +import UserPermissionCommandHandler from "../../command/userPermissionCommandHandler"; + +/** + * @description get All users + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function getAllUsers(req: Request, res: Response): Promise { + let users = await UserService.getAll(); + + res.json(UserFactory.mapToBase(users)); +} + +/** + * @description get user by id + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function getUserById(req: Request, res: Response): Promise { + const id = parseInt(req.params.id); + let user = await UserService.getById(id); + + res.json(UserFactory.mapToSingle(user)); +} + +/** + * @description get permissions by user + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function getUserPermissions(req: Request, res: Response): Promise { + const id = parseInt(req.params.id); + let permissions = await UserPermissionService.getByUser(id); + + res.json(PermissionHelper.convertToObject(permissions.map((p) => p.permission))); +} + +/** + * @description get assigned roles by user + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function getUserRoles(req: Request, res: Response): Promise { + const id = parseInt(req.params.id); + + let roles = await UserService.getAssignedRolesByUserId(id); + + res.json(RoleFactory.mapToBase(roles)); +} + +/** + * @description update user data + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function updateUser(req: Request, res: Response): Promise { + const id = parseInt(req.params.id); + let mail = req.body.mail; + let firstname = req.body.firstname; + let lastname = req.body.lastname; + let username = req.body.username; + + let updateUser: UpdateUserCommand = { + id: id, + mail: mail, + firstname: firstname, + lastname: lastname, + username: username, + }; + await UserCommandHandler.update(updateUser); + + res.sendStatus(204); +} + +/** + * @description update user assigned permission strings + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function updateUserPermissions(req: Request, res: Response): Promise { + const id = parseInt(req.params.id); + let permissions = req.body.permissions; + + let permissionStrings = PermissionHelper.convertToStringArray(permissions); + + let updateUserPermissions: UpdateUserPermissionsCommand = { + userId: id, + permissions: permissionStrings, + }; + await UserPermissionCommandHandler.updatePermissions(updateUserPermissions); + + res.sendStatus(204); +} + +/** + * @description update user assigned roles + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function updateUserRoles(req: Request, res: Response): Promise { + const id = parseInt(req.params.id); + let roleIds = req.body.roleIds as Array; + + let updateRoles: UpdateUserRolesCommand = { + id: id, + roleIds: roleIds, + }; + await UserCommandHandler.updateRoles(updateRoles); + + res.sendStatus(204); +} + +/** + * @description delete user by id + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function deleteUser(req: Request, res: Response): Promise { + const id = parseInt(req.params.id); + + let user = await UserService.getById(id); + + let deleteUser: DeleteUserCommand = { + id: id, + }; + await UserCommandHandler.delete(deleteUser); + + try { + // sendmail + let mailhelper = new MailHelper(); + await mailhelper.sendMail( + user.mail, + `Email Bestätigung für Mitglieder Admin-Portal von ${CLUB_NAME}`, + `Ihr Nutzerkonto des Adminportals wurde erfolgreich gelöscht.` + ); + } catch (error) {} + + res.sendStatus(204); +} diff --git a/src/controller/authController.ts b/src/controller/authController.ts index a8a4045..d2d7e02 100644 --- a/src/controller/authController.ts +++ b/src/controller/authController.ts @@ -38,7 +38,7 @@ export async function login(req: Request, res: Response): Promise { let userPermissions = await UserPermissionService.getByUser(id); let userPermissionStrings = userPermissions.map((e) => e.permission); let userRoles = await UserService.getAssignedRolesByUserId(id); - let rolePermissions = await RolePermissionService.getByRoles(userRoles.map((e) => e.id)); + let rolePermissions = userRoles.length != 0 ? await RolePermissionService.getByRoles(userRoles.map((e) => e.id)) : []; let rolePermissionStrings = rolePermissions.map((e) => e.permission); let permissionObject = PermissionHelper.convertToObject([...userPermissionStrings, ...rolePermissionStrings]); diff --git a/src/controller/inviteController.ts b/src/controller/inviteController.ts index 0a301ca..095e4a3 100644 --- a/src/controller/inviteController.ts +++ b/src/controller/inviteController.ts @@ -25,7 +25,7 @@ import UserPermissionCommandHandler from "../command/userPermissionCommandHandle * @param res {Response} Express res object * @returns {Promise<*>} */ -export async function inviteUser(req: Request, res: Response): Promise { +export async function inviteUser(req: Request, res: Response, isInvite: boolean = true): Promise { let origin = req.headers.origin; let username = req.body.username; let mail = req.body.mail; @@ -62,7 +62,7 @@ export async function inviteUser(req: Request, res: Response): Promise { await mailhelper.sendMail( mail, `Email Bestätigung für Mitglieder Admin-Portal von ${CLUB_NAME}`, - `Öffne folgenden Link: ${origin}/setup/verify?mail=${mail}&token=${token}` + `Öffne folgenden Link: ${origin}/${isInvite ? "invite" : "setup"}/verify?mail=${mail}&token=${token}` ); res.sendStatus(204); @@ -114,8 +114,6 @@ export async function finishInvite(req: Request, res: Response, grantAdmin: bool window: 2, }); - console.log(valid); - if (!valid) { throw new UnauthorizedRequestException("Token not valid or expired"); } diff --git a/src/entity/role.ts b/src/entity/role.ts index 964860b..6a4bac9 100644 --- a/src/entity/role.ts +++ b/src/entity/role.ts @@ -1,5 +1,6 @@ -import { Column, Entity, ManyToMany, PrimaryColumn } from "typeorm"; +import { Column, Entity, ManyToMany, OneToMany, PrimaryColumn } from "typeorm"; import { user } from "./user"; +import { rolePermission } from "./role_permission"; @Entity() export class role { @@ -11,4 +12,7 @@ export class role { @ManyToMany(() => user, (user) => user.roles) users: user[]; + + @OneToMany(() => rolePermission, (rolePermission) => rolePermission.role) + permissions: rolePermission[]; } diff --git a/src/entity/user.ts b/src/entity/user.ts index 3d4c05c..fd41008 100644 --- a/src/entity/user.ts +++ b/src/entity/user.ts @@ -1,5 +1,6 @@ -import { Column, Entity, JoinTable, ManyToMany, PrimaryColumn } from "typeorm"; +import { Column, Entity, JoinTable, ManyToMany, OneToMany, PrimaryColumn } from "typeorm"; import { role } from "./role"; +import { userPermission } from "./user_permission"; @Entity() export class user { @@ -26,4 +27,7 @@ export class user { name: "user_roles", }) roles: role[]; + + @OneToMany(() => userPermission, (userPermission) => userPermission.user) + permissions: userPermission[]; } diff --git a/src/factory/admin/role.ts b/src/factory/admin/role.ts new file mode 100644 index 0000000..6e1817c --- /dev/null +++ b/src/factory/admin/role.ts @@ -0,0 +1,27 @@ +import { role } from "../../entity/role"; +import PermissionHelper from "../../helpers/permissionHelper"; +import { RoleViewModel } from "../../viewmodel/admin/role.models"; + +export default abstract class RoleFactory { + /** + * @description map record to role + * @param {role} record + * @returns {roleViewModel} + */ + public static mapToSingle(record: role): RoleViewModel { + return { + id: record.id, + permissions: PermissionHelper.convertToObject(record.permissions.map((e) => e.permission)), + role: record.role, + }; + } + + /** + * @description map records to role + * @param {Array} records + * @returns {Array} + */ + public static mapToBase(records: Array): Array { + return records.map((r) => this.mapToSingle(r)); + } +} diff --git a/src/factory/admin/user.ts b/src/factory/admin/user.ts new file mode 100644 index 0000000..2f7098c --- /dev/null +++ b/src/factory/admin/user.ts @@ -0,0 +1,38 @@ +import { user } from "../../entity/user"; +import PermissionHelper from "../../helpers/permissionHelper"; +import { UserViewModel } from "../../viewmodel/admin/user.models"; +import RoleFactory from "./role"; + +export default abstract class UserFactory { + /** + * @description map record to user + * @param {user} record + * @returns {UserViewModel} + */ + public static mapToSingle(record: user): UserViewModel { + let userPermissionStrings = record.permissions.map((e) => e.permission); + let rolePermissions = record.roles.map((e) => e.permissions).flat(); + let rolePermissionStrings = rolePermissions.map((p) => p.permission); + let totalPermissions = PermissionHelper.convertToObject([...userPermissionStrings, ...rolePermissionStrings]); + + return { + id: record.id, + username: record.username, + firstname: record.firstname, + lastname: record.lastname, + mail: record.mail, + permissions: PermissionHelper.convertToObject(userPermissionStrings), + roles: RoleFactory.mapToBase(record.roles), + permissions_total: totalPermissions, + }; + } + + /** + * @description map records to user + * @param {Array} records + * @returns {Array} + */ + public static mapToBase(records: Array): Array { + return records.map((r) => this.mapToSingle(r)); + } +} diff --git a/src/helpers/permissionHelper.ts b/src/helpers/permissionHelper.ts index 2730cbe..e4af4ba 100644 --- a/src/helpers/permissionHelper.ts +++ b/src/helpers/permissionHelper.ts @@ -17,8 +17,8 @@ export default class PermissionHelper { section: PermissionSection, module?: PermissionModule ) { - if (type == "admin") return permissions.admin ?? false; - if (permissions.admin) return true; + if (type == "admin") return permissions?.admin ?? false; + if (permissions?.admin) return true; if ( (!module && permissions[section] != undefined && @@ -37,8 +37,8 @@ export default class PermissionHelper { type: PermissionType | "admin", section: PermissionSection ): boolean { - if (type == "admin") return permissions.admin ?? false; - if (permissions.admin) return true; + if (type == "admin") return permissions?.admin ?? false; + if (permissions?.admin) return true; if ( permissions[section]?.all == "*" || permissions[section]?.all?.includes(type) || @@ -130,7 +130,7 @@ export default class PermissionHelper { } static convertToStringArray(permissions: PermissionObject): Array { - if (permissions.admin) { + if (permissions?.admin) { return ["*"]; } let output: Array = []; diff --git a/src/middleware/authenticate.ts b/src/middleware/authenticate.ts index ef3713d..535a056 100644 --- a/src/middleware/authenticate.ts +++ b/src/middleware/authenticate.ts @@ -31,7 +31,7 @@ export default async function authenticate(req: Request, res: Response, next: Fu req.userId = decoded.userId; req.username = decoded.username; - req.permissions = decoded.rights; + req.permissions = decoded.permissions; next(); } diff --git a/src/routes/admin/index.ts b/src/routes/admin/index.ts new file mode 100644 index 0000000..2c24b06 --- /dev/null +++ b/src/routes/admin/index.ts @@ -0,0 +1,12 @@ +import express from "express"; +import PermissionHelper from "../../helpers/permissionHelper"; + +import role from "./role"; +import user from "./user"; + +var router = express.Router({ mergeParams: true }); + +router.use("/role", PermissionHelper.passCheckMiddleware("read", "user", "role"), role); +router.use("/user", PermissionHelper.passCheckMiddleware("read", "user", "user"), user); + +export default router; diff --git a/src/routes/admin/role.ts b/src/routes/admin/role.ts new file mode 100644 index 0000000..ac3c077 --- /dev/null +++ b/src/routes/admin/role.ts @@ -0,0 +1,59 @@ +import express, { Request, Response } from "express"; +import PermissionHelper from "../../helpers/permissionHelper"; +import { + createRole, + deleteRole, + getAllRoles, + getRoleById, + getRolePermissions, + updateRole, + updateRolePermissions, +} from "../../controller/admin/roleController"; + +var router = express.Router({ mergeParams: true }); + +router.get("/", async (req: Request, res: Response) => { + await getAllRoles(req, res); +}); + +router.get("/:id", async (req: Request, res: Response) => { + await getRoleById(req, res); +}); + +router.get("/:id/permissions", async (req: Request, res: Response) => { + await getRolePermissions(req, res); +}); + +router.post( + "/", + PermissionHelper.passCheckMiddleware("create", "user", "role"), + async (req: Request, res: Response) => { + await createRole(req, res); + } +); + +router.patch( + "/:id", + PermissionHelper.passCheckMiddleware("update", "user", "role"), + async (req: Request, res: Response) => { + await updateRole(req, res); + } +); + +router.patch( + "/:id/permissions", + PermissionHelper.passCheckMiddleware("update", "user", "role"), + async (req: Request, res: Response) => { + await updateRolePermissions(req, res); + } +); + +router.delete( + "/:id", + PermissionHelper.passCheckMiddleware("delete", "user", "role"), + async (req: Request, res: Response) => { + await deleteRole(req, res); + } +); + +export default router; diff --git a/src/routes/admin/user.ts b/src/routes/admin/user.ts new file mode 100644 index 0000000..7ce7046 --- /dev/null +++ b/src/routes/admin/user.ts @@ -0,0 +1,73 @@ +import express, { Request, Response } from "express"; +import PermissionHelper from "../../helpers/permissionHelper"; +import { + deleteUser, + getAllUsers, + getUserById, + getUserPermissions, + getUserRoles, + updateUser, + updateUserPermissions, + updateUserRoles, +} from "../../controller/admin/userController"; +import { inviteUser } from "../../controller/inviteController"; + +var router = express.Router({ mergeParams: true }); + +router.get("/", async (req: Request, res: Response) => { + await getAllUsers(req, res); +}); + +router.get("/:id", async (req: Request, res: Response) => { + await getUserById(req, res); +}); + +router.get("/:id/permissions", async (req: Request, res: Response) => { + await getUserPermissions(req, res); +}); + +router.get("/:id/roles", async (req: Request, res: Response) => { + await getUserRoles(req, res); +}); + +router.post( + "/invite", + PermissionHelper.passCheckMiddleware("create", "user", "user"), + async (req: Request, res: Response) => { + await inviteUser(req, res); + } +); + +router.patch( + "/:id", + PermissionHelper.passCheckMiddleware("update", "user", "user"), + async (req: Request, res: Response) => { + await updateUser(req, res); + } +); + +router.patch( + "/:id/permissions", + PermissionHelper.passCheckMiddleware("update", "user", "user"), + async (req: Request, res: Response) => { + await updateUserPermissions(req, res); + } +); + +router.patch( + "/:id/roles", + PermissionHelper.passCheckMiddleware("update", "user", "user"), + async (req: Request, res: Response) => { + await updateUserRoles(req, res); + } +); + +router.delete( + "/:id", + PermissionHelper.passCheckMiddleware("delete", "user", "user"), + async (req: Request, res: Response) => { + await deleteUser(req, res); + } +); + +export default router; diff --git a/src/routes/index.ts b/src/routes/index.ts index 77129c9..2dcff79 100644 --- a/src/routes/index.ts +++ b/src/routes/index.ts @@ -8,7 +8,7 @@ import errorHandler from "../middleware/errorHandler"; import setup from "./setup"; import auth from "./auth"; -import PermissionHelper from "../helpers/permissionHelper"; +import admin from "./admin/index"; export default (app: Express) => { app.set("query parser", "extended"); @@ -24,6 +24,6 @@ export default (app: Express) => { app.use("/setup", allowSetup, setup); app.use("/auth", auth); app.use(authenticate); - app.use("/secured", PermissionHelper.passCheckMiddleware("admin", "user"), (req, res) => {}); + app.use("/admin", admin); app.use(errorHandler); }; diff --git a/src/routes/setup.ts b/src/routes/setup.ts index b603488..159e04e 100644 --- a/src/routes/setup.ts +++ b/src/routes/setup.ts @@ -17,7 +17,7 @@ router.post( "/", ParamaterPassCheckHelper.requiredIncludedMiddleware(["username", "mail", "firstname", "lastname"]), async (req, res) => { - await inviteUser(req, res); + await inviteUser(req, res, false); } ); diff --git a/src/service/roleService.ts b/src/service/roleService.ts index 2316d39..65215ea 100644 --- a/src/service/roleService.ts +++ b/src/service/roleService.ts @@ -11,6 +11,7 @@ export default abstract class RoleService { return await dataSource .getRepository(role) .createQueryBuilder("role") + .leftJoinAndSelect("role.permissions", "role_permissions") .getMany() .then((res) => { return res; @@ -29,6 +30,7 @@ export default abstract class RoleService { return await dataSource .getRepository(role) .createQueryBuilder("role") + .leftJoinAndSelect("role.permissions", "role_permissions") .where("role.id = :id", { id: id }) .getOneOrFail() .then((res) => { diff --git a/src/service/userService.ts b/src/service/userService.ts index 095d1ea..8cd1968 100644 --- a/src/service/userService.ts +++ b/src/service/userService.ts @@ -4,6 +4,26 @@ import { user } from "../entity/user"; import InternalException from "../exceptions/internalException"; export default abstract class UserService { + /** + * @description get users + * @returns {Promise>} + */ + static async getAll(): Promise> { + return await dataSource + .getRepository(user) + .createQueryBuilder("user") + .leftJoinAndSelect("user.roles", "roles") + .leftJoinAndSelect("user.permissions", "permissions") + .leftJoinAndSelect("roles.permissions", "role_permissions") + .getMany() + .then((res) => { + return res; + }) + .catch((err) => { + throw new InternalException("users not found"); + }); + } + /** * @description get user by id * @param id number @@ -13,6 +33,9 @@ export default abstract class UserService { return await dataSource .getRepository(user) .createQueryBuilder("user") + .leftJoinAndSelect("user.roles", "roles") + .leftJoinAndSelect("user.permissions", "permissions") + .leftJoinAndSelect("roles.permissions", "role_permissions") .where("user.id = :id", { id: id }) .getOneOrFail() .then((res) => { @@ -93,6 +116,7 @@ export default abstract class UserService { .getRepository(user) .createQueryBuilder("user") .leftJoinAndSelect("user.roles", "roles") + .leftJoinAndSelect("roles.permissions", "role_permissions") .where("user.id = :id", { id: userId }) .getOneOrFail() .then((res) => { diff --git a/src/viewmodel/admin/role.models.ts b/src/viewmodel/admin/role.models.ts new file mode 100644 index 0000000..c810ca2 --- /dev/null +++ b/src/viewmodel/admin/role.models.ts @@ -0,0 +1,7 @@ +import { PermissionObject } from "../../type/permissionTypes"; + +export interface RoleViewModel { + id: number; + permissions: PermissionObject; + role: string; +} diff --git a/src/viewmodel/admin/user.models.ts b/src/viewmodel/admin/user.models.ts new file mode 100644 index 0000000..39ee152 --- /dev/null +++ b/src/viewmodel/admin/user.models.ts @@ -0,0 +1,13 @@ +import { PermissionObject } from "../../type/permissionTypes"; +import { RoleViewModel } from "./role.models"; + +export interface UserViewModel { + id: number; + username: string; + mail: string; + firstname: string; + lastname: string; + permissions: PermissionObject; + roles: Array; + permissions_total: PermissionObject; +}