diff --git a/src/command/userCommand.ts b/src/command/userCommand.ts index b61b578..a084115 100644 --- a/src/command/userCommand.ts +++ b/src/command/userCommand.ts @@ -4,7 +4,6 @@ export interface CreateUserCommand { firstname: string; lastname: string; secret: string; - isOwner: boolean; } export interface UpdateUserCommand { @@ -15,11 +14,6 @@ export interface UpdateUserCommand { lastname: string; } -export interface TransferUserOwnerCommand { - fromId: number; - toId: number; -} - export interface UpdateUserRolesCommand { id: number; roleIds: Array; diff --git a/src/command/userCommandHandler.ts b/src/command/userCommandHandler.ts index a88979b..79834e2 100644 --- a/src/command/userCommandHandler.ts +++ b/src/command/userCommandHandler.ts @@ -2,13 +2,7 @@ import { EntityManager } from "typeorm"; import { dataSource } from "../data-source"; import { user } from "../entity/user"; import InternalException from "../exceptions/internalException"; -import { - CreateUserCommand, - DeleteUserCommand, - TransferUserOwnerCommand, - UpdateUserCommand, - UpdateUserRolesCommand, -} from "./userCommand"; +import { CreateUserCommand, DeleteUserCommand, UpdateUserCommand, UpdateUserRolesCommand } from "./userCommand"; import UserService from "../service/userService"; export default abstract class UserCommandHandler { @@ -28,7 +22,6 @@ export default abstract class UserCommandHandler { firstname: createUser.firstname, lastname: createUser.lastname, secret: createUser.secret, - isOwner: createUser.isOwner, }) .execute() .then((result) => { @@ -96,38 +89,6 @@ export default abstract class UserCommandHandler { return await manager.createQueryBuilder().relation(user, "roles").of(userId).remove(roleId); } - /** - * @description transfer ownership - * @param TransferUserOwnerCommand - * @returns {Promise} - */ - static async transferOwnership(transferOwnership: TransferUserOwnerCommand): Promise { - return await dataSource.manager - .transaction(async (manager) => { - manager - .createQueryBuilder() - .update(user) - .set({ - isOwner: false, - }) - .where("id = :id", { id: transferOwnership.fromId }) - .execute(); - - manager - .createQueryBuilder() - .update(user) - .set({ - isOwner: true, - }) - .where("id = :id", { id: transferOwnership.toId }) - .execute(); - }) - .then(() => {}) - .catch((err) => { - throw new InternalException("Failed transfering ownership", err); - }); - } - /** * @description delete user * @param DeleteUserCommand diff --git a/src/controller/authController.ts b/src/controller/authController.ts index f68aab2..cbd1a1d 100644 --- a/src/controller/authController.ts +++ b/src/controller/authController.ts @@ -22,7 +22,7 @@ export async function login(req: Request, res: Response): Promise { let username = req.body.username; let totp = req.body.totp; - let { id, secret, mail, firstname, lastname, isOwner } = await UserService.getByUsername(username); + let { id, secret, mail, firstname, lastname } = await UserService.getByUsername(username); let valid = speakeasy.totp.verify({ secret: secret, @@ -48,7 +48,6 @@ export async function login(req: Request, res: Response): Promise { username: username, firstname: firstname, lastname: lastname, - isOwner: isOwner, permissions: permissionObject, }; @@ -106,7 +105,7 @@ export async function refresh(req: Request, res: Response): Promise { throw new UnauthorizedRequestException("user not identified with token and refresh"); } - let { id, username, mail, firstname, lastname, isOwner } = await UserService.getById(tokenUserId); + let { id, username, mail, firstname, lastname } = await UserService.getById(tokenUserId); let permissions = await UserPermissionService.getByUser(id); let permissionStrings = permissions.map((e) => e.permission); @@ -118,7 +117,6 @@ export async function refresh(req: Request, res: Response): Promise { username: username, firstname: firstname, lastname: lastname, - isOwner: isOwner, permissions: permissionObject, }; diff --git a/src/controller/inviteController.ts b/src/controller/inviteController.ts index 0b2b207..f9be5b3 100644 --- a/src/controller/inviteController.ts +++ b/src/controller/inviteController.ts @@ -124,17 +124,23 @@ export async function finishInvite(req: Request, res: Response, grantAdmin: bool lastname: lastname, mail: mail, secret: secret, - isOwner: grantAdmin, }; let id = await UserCommandHandler.create(createUser); + if (grantAdmin) { + let createPermission: CreateUserPermissionCommand = { + permission: "*", + userId: id, + }; + await UserPermissionCommandHandler.create(createPermission); + } + let jwtData: JWTToken = { userId: id, mail: mail, username: username, firstname: firstname, lastname: lastname, - isOwner: grantAdmin, permissions: { ...(grantAdmin ? { admin: true } : {}), }, diff --git a/src/controller/userController.ts b/src/controller/userController.ts deleted file mode 100644 index ebea67a..0000000 --- a/src/controller/userController.ts +++ /dev/null @@ -1,121 +0,0 @@ -import { Request, Response } from "express"; -import speakeasy from "speakeasy"; -import QRCode from "qrcode"; -import InternalException from "../exceptions/internalException"; -import { CLUB_NAME } from "../env.defaults"; -import UserService from "../service/userService"; -import UserFactory from "../factory/admin/user"; -import { TransferUserOwnerCommand, UpdateUserCommand } from "../command/userCommand"; -import UserCommandHandler from "../command/userCommandHandler"; -import ForbiddenRequestException from "../exceptions/forbiddenRequestException"; - -/** - * @description get my by id - * @param req {Request} Express req object - * @param res {Response} Express res object - * @returns {Promise<*>} - */ -export async function getMeById(req: Request, res: Response): Promise { - const id = parseInt(req.userId); - let user = await UserService.getById(id); - - res.json(UserFactory.mapToSingle(user)); -} - -/** - * @description get my totp - * @param req {Request} Express req object - * @param res {Response} Express res object - * @returns {Promise<*>} - */ -export async function getMyTotp(req: Request, res: Response): Promise { - const userId = parseInt(req.userId); - - let { secret } = await UserService.getById(userId); - - const url = `otpauth://totp/Mitgliederverwaltung ${CLUB_NAME}?secret=${secret}`; - - QRCode.toDataURL(url) - .then((result) => { - res.json({ - dataUrl: result, - otp: secret, - }); - }) - .catch((err) => { - throw new InternalException("QRCode not created", err); - }); -} - -/** - * @description verify my totp - * @param req {Request} Express req object - * @param res {Response} Express res object - * @returns {Promise<*>} - */ -export async function verifyMyTotp(req: Request, res: Response): Promise { - const userId = parseInt(req.userId); - let totp = req.body.totp; - - let { secret } = await UserService.getById(userId); - let valid = speakeasy.totp.verify({ - secret: secret, - encoding: "base32", - token: totp, - window: 2, - }); - - if (!valid) { - throw new InternalException("Token not valid or expired"); - } - res.sendStatus(204); -} - -/** - * @description transferOwnership - * @param req {Request} Express req object - * @param res {Response} Express res object - * @returns {Promise<*>} - */ -export async function transferOwnership(req: Request, res: Response): Promise { - const userId = parseInt(req.userId); - let toId = req.body.toId; - - let { isOwner } = await UserService.getById(userId); - if (!isOwner) { - throw new ForbiddenRequestException("Action only allowed to owner."); - } - - let transfer: TransferUserOwnerCommand = { - toId: toId, - fromId: userId, - }; - await UserCommandHandler.transferOwnership(transfer); - - res.sendStatus(204); -} - -/** - * @description update my data - * @param req {Request} Express req object - * @param res {Response} Express res object - * @returns {Promise<*>} - */ -export async function updateMe(req: Request, res: Response): Promise { - const id = parseInt(req.userId); - 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); -} diff --git a/src/data-source.ts b/src/data-source.ts index 3801455..a38def0 100644 --- a/src/data-source.ts +++ b/src/data-source.ts @@ -29,7 +29,6 @@ import { memberQualifications } from "./entity/memberQualifications"; import { membership } from "./entity/membership"; import { Memberdata1726301836849 } from "./migrations/1726301836849-memberdata"; import { CommunicationFields1727439800630 } from "./migrations/1727439800630-communicationFields"; -import { Ownership1728313041449 } from "./migrations/1728313041449-ownership"; import { protocol } from "./entity/protocol"; import { protocolAgenda } from "./entity/protocolAgenda"; import { protocolDecision } from "./entity/protocolDecision"; @@ -87,7 +86,6 @@ const dataSource = new DataSource({ MemberBaseData1725435669492, Memberdata1726301836849, CommunicationFields1727439800630, - Ownership1728313041449, Protocol1729347911107, Calendar1729947763295, ], diff --git a/src/entity/user.ts b/src/entity/user.ts index 09435cd..fcb31c6 100644 --- a/src/entity/user.ts +++ b/src/entity/user.ts @@ -22,9 +22,6 @@ export class user { @Column({ type: "varchar", length: 255 }) secret: string; - @Column({ type: "boolean", default: false }) - isOwner: boolean; - @ManyToMany(() => role, (role) => role.users, { nullable: false, onDelete: "CASCADE", diff --git a/src/factory/admin/user.ts b/src/factory/admin/user.ts index ca8524f..2f7098c 100644 --- a/src/factory/admin/user.ts +++ b/src/factory/admin/user.ts @@ -21,7 +21,6 @@ export default abstract class UserFactory { firstname: record.firstname, lastname: record.lastname, mail: record.mail, - isOwner: record.isOwner, permissions: PermissionHelper.convertToObject(userPermissionStrings), roles: RoleFactory.mapToBase(record.roles), permissions_total: totalPermissions, diff --git a/src/helpers/permissionHelper.ts b/src/helpers/permissionHelper.ts index 63edffa..2eb4def 100644 --- a/src/helpers/permissionHelper.ts +++ b/src/helpers/permissionHelper.ts @@ -55,9 +55,8 @@ export default class PermissionHelper { ): (req: Request, res: Response, next: Function) => void { return (req: Request, res: Response, next: Function) => { const permissions = req.permissions; - const isOwner = req.isOwner; - if (isOwner || this.can(permissions, requiredPermissions, section, module)) { + if (this.can(permissions, requiredPermissions, section, module)) { next(); } else { throw new ForbiddenRequestException( @@ -75,9 +74,8 @@ export default class PermissionHelper { ): (req: Request, res: Response, next: Function) => void { return (req: Request, res: Response, next: Function) => { const permissions = req.permissions; - const isOwner = req.isOwner; - if (isOwner || this.canSection(permissions, requiredPermissions, section)) { + if (this.canSection(permissions, requiredPermissions, section)) { next(); } else { throw new ForbiddenRequestException( diff --git a/src/index.ts b/src/index.ts index 6fd0f99..d551a1e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -9,7 +9,6 @@ declare global { export interface Request { userId: string; username: string; - isOwner: boolean; permissions: PermissionObject; } } diff --git a/src/middleware/authenticate.ts b/src/middleware/authenticate.ts index cfa1f56..a62c689 100644 --- a/src/middleware/authenticate.ts +++ b/src/middleware/authenticate.ts @@ -31,7 +31,6 @@ export default async function authenticate(req: Request, res: Response, next: Fu req.userId = decoded.userId; req.username = decoded.username; - req.isOwner = decoded.isOwner; req.permissions = decoded.permissions; next(); diff --git a/src/migrations/1728313041449-ownership.ts b/src/migrations/1728313041449-ownership.ts deleted file mode 100644 index bf744a2..0000000 --- a/src/migrations/1728313041449-ownership.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { MigrationInterface, QueryRunner, TableColumn } from "typeorm"; - -export class Ownership1728313041449 implements MigrationInterface { - name = "Ownership1728313041449"; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.addColumn( - "user", - new TableColumn({ - name: "isOwner", - type: "tinyint", - default: 0, - isNullable: false, - }) - ); - - await queryRunner.manager - .createQueryBuilder() - .update("user") - .set({ isOwner: 1 }) - .where((qb) => { - const subQuery = queryRunner.manager - .createQueryBuilder() - .select("1") - .from("user_permission", "up") - .where("user.id = up.userId") - .andWhere("up.permission = '*'") - .getQuery(); - return `EXISTS (${subQuery})`; - }) - .execute(); - - await queryRunner.manager.createQueryBuilder().delete().from("user_permission").where("permission = '*'").execute(); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.manager - .createQueryBuilder() - .insert() - .into("user_permission") - .values( - await queryRunner.manager - .createQueryBuilder() - .select("user.id", "userId") - .addSelect("'*'", "permission") - .from("user", "user") - .where("user.isOwner = 1") - .execute() - ) - .execute(); - - await queryRunner.manager.createQueryBuilder().update("user").set({ isOwner: 0 }).where("isOwner = 1").execute(); - - await queryRunner.dropColumn("user", "isOwner"); - } -} diff --git a/src/routes/index.ts b/src/routes/index.ts index b9281d0..070508c 100644 --- a/src/routes/index.ts +++ b/src/routes/index.ts @@ -10,7 +10,6 @@ import publicAvailable from "./public"; import setup from "./setup"; import auth from "./auth"; import admin from "./admin/index"; -import user from "./user"; export default (app: Express) => { app.set("query parser", "extended"); @@ -28,6 +27,5 @@ export default (app: Express) => { app.use("/auth", auth); app.use(authenticate); app.use("/admin", admin); - app.use("/user", user); app.use(errorHandler); }; diff --git a/src/routes/user.ts b/src/routes/user.ts deleted file mode 100644 index d196e16..0000000 --- a/src/routes/user.ts +++ /dev/null @@ -1,26 +0,0 @@ -import express from "express"; -import { getMeById, getMyTotp, transferOwnership, updateMe, verifyMyTotp } from "../controller/userController"; - -var router = express.Router({ mergeParams: true }); - -router.get("/me", async (req, res) => { - await getMeById(req, res); -}); - -router.get("/totp", async (req, res) => { - await getMyTotp(req, res); -}); - -router.post("/verify", async (req, res) => { - await verifyMyTotp(req, res); -}); - -router.put("/transferOwner", async (req, res) => { - await transferOwnership(req, res); -}); - -router.patch("/me", async (req, res) => { - await updateMe(req, res); -}); - -export default router; diff --git a/src/type/jwtTypes.ts b/src/type/jwtTypes.ts index 1d65d38..83e9a44 100644 --- a/src/type/jwtTypes.ts +++ b/src/type/jwtTypes.ts @@ -1,7 +1,7 @@ import { PermissionObject } from "./permissionTypes"; export type JWTData = { - [key: string]: string | number | boolean | PermissionObject; + [key: string]: string | number | PermissionObject; }; export type JWTToken = { @@ -10,7 +10,6 @@ export type JWTToken = { username: string; firstname: string; lastname: string; - isOwner: boolean; permissions: PermissionObject; } & JWTData; diff --git a/src/viewmodel/admin/user.models.ts b/src/viewmodel/admin/user.models.ts index df6eaa3..39ee152 100644 --- a/src/viewmodel/admin/user.models.ts +++ b/src/viewmodel/admin/user.models.ts @@ -7,7 +7,6 @@ export interface UserViewModel { mail: string; firstname: string; lastname: string; - isOwner: boolean; permissions: PermissionObject; roles: Array; permissions_total: PermissionObject;