From d2c826c7c9a8b79ff5ab6268865a26791b36c2c2 Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Thu, 3 Dec 2020 18:33:20 +0100 Subject: [PATCH 01/17] =?UTF-8?q?=F0=9F=9A=A7=20CreateUser=20model?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ref #14 --- src/models/CreateUser.ts | 53 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 src/models/CreateUser.ts diff --git a/src/models/CreateUser.ts b/src/models/CreateUser.ts new file mode 100644 index 0000000..23cd934 --- /dev/null +++ b/src/models/CreateUser.ts @@ -0,0 +1,53 @@ +import { IsInt, IsOptional, IsPhoneNumber, IsString } from 'class-validator'; +import { User } from '../models/User'; +import { getConnectionManager } from 'typeorm'; +import { UserGroupNotFoundError, UsernameOrEmailNeededError } from '../errors/CreateUserErrors'; +import { UserGroup } from './UserGroup'; + +export class CreateUser { + @IsString() + firstname: string; + @IsString() + middlename?: string; + @IsOptional() + @IsString() + username?: string; + @IsPhoneNumber("ZZ") + @IsOptional() + phone?: string; + @IsString() + password: string; + @IsString() + lastname: string; + @IsString() + email?: string; + @IsInt() + @IsOptional() + groupId?: number[] | number + + public async toUser(): Promise { + let newUser: User = new User(); + + if (this.email === undefined && this.username === undefined) { + throw new UsernameOrEmailNeededError(); + } + + if (this.groupId) { + // TODO: link user groups + // newUser.groups = await getConnectionManager().get().getRepository(UserGroup).findOne({ id: this.teamId }); + } else { + throw new UserGroupNotFoundError(); + } + + newUser.email = this.email + newUser.username = this.username + newUser.firstname = this.firstname + newUser.middlename = this.middlename + newUser.lastname = this.lastname + // TODO: hash password here or in controller/ in User model via setter? + newUser.password = this.password + + console.log(newUser) + return newUser; + } +} \ No newline at end of file From 983fa41cba3a4d537ab742a149b86d6c6a87f5bf Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Thu, 3 Dec 2020 18:33:30 +0100 Subject: [PATCH 02/17] =?UTF-8?q?=F0=9F=9A=A7=20CreateUserErrors=20model?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ref #14 --- src/errors/CreateUserErrors.ts | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 src/errors/CreateUserErrors.ts diff --git a/src/errors/CreateUserErrors.ts b/src/errors/CreateUserErrors.ts new file mode 100644 index 0000000..3b28735 --- /dev/null +++ b/src/errors/CreateUserErrors.ts @@ -0,0 +1,24 @@ +import { NotFoundError } from 'routing-controllers'; +import { IsString } from 'class-validator'; + +/** + * Error to throw when a usergroup couldn't be found. + */ +export class UserGroupNotFoundError extends NotFoundError { + @IsString() + name = "UserGroupNotFoundError" + + @IsString() + message = "User Group not found!" +} + +/** + * Error to throw when no username or email is set + */ +export class UsernameOrEmailNeededError extends NotFoundError { + @IsString() + name = "UsernameOrEmailNeededError" + + @IsString() + message = "no username or email is set!" +} From f1629440feae3a49ab17ec7d29b709ff392d6988 Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Fri, 4 Dec 2020 17:04:33 +0100 Subject: [PATCH 03/17] =?UTF-8?q?=F0=9F=9A=A7=20better=20uuid=20+=20starti?= =?UTF-8?q?ng=20hashing=20implementation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ref #14 --- package.json | 7 +++++-- src/models/CreateUser.ts | 14 ++++++++++---- src/models/User.ts | 10 ++++------ 3 files changed, 19 insertions(+), 12 deletions(-) diff --git a/package.json b/package.json index 335f5ce..afc485d 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ ], "license": "CC-BY-NC-SA-4.0", "dependencies": { + "argon2": "^0.27.0", "body-parser": "^1.19.0", "class-transformer": "^0.3.1", "class-validator": "^0.12.2", @@ -39,7 +40,8 @@ "routing-controllers-openapi": "^2.1.0", "swagger-ui-express": "^4.1.5", "typeorm": "^0.2.29", - "typeorm-routing-controllers-extensions": "^0.2.0" + "typeorm-routing-controllers-extensions": "^0.2.0", + "uuid": "^8.3.1" }, "devDependencies": { "@types/cors": "^2.8.8", @@ -49,6 +51,7 @@ "@types/multer": "^1.4.4", "@types/node": "^14.14.9", "@types/swagger-ui-express": "^4.1.2", + "@types/uuid": "^8.3.0", "dotenv-safe": "^8.2.0", "nodemon": "^2.0.6", "sqlite3": "^5.0.0", @@ -61,4 +64,4 @@ "build": "tsc", "docs": "typedoc --out docs src" } -} +} \ No newline at end of file diff --git a/src/models/CreateUser.ts b/src/models/CreateUser.ts index 23cd934..9b57318 100644 --- a/src/models/CreateUser.ts +++ b/src/models/CreateUser.ts @@ -1,8 +1,8 @@ -import { IsInt, IsOptional, IsPhoneNumber, IsString } from 'class-validator'; -import { User } from '../models/User'; -import { getConnectionManager } from 'typeorm'; +import * as argon2 from "argon2"; +import { IsInt, IsOptional, IsPhoneNumber, IsString, IsUUID } from 'class-validator'; +import * as uuid from 'uuid'; import { UserGroupNotFoundError, UsernameOrEmailNeededError } from '../errors/CreateUserErrors'; -import { UserGroup } from './UserGroup'; +import { User } from '../models/User'; export class CreateUser { @IsString() @@ -24,6 +24,8 @@ export class CreateUser { @IsInt() @IsOptional() groupId?: number[] | number + @IsUUID("5") + uuid: string; public async toUser(): Promise { let newUser: User = new User(); @@ -39,12 +41,16 @@ export class CreateUser { throw new UserGroupNotFoundError(); } + const new_uuid = uuid.v4() + newUser.email = this.email newUser.username = this.username newUser.firstname = this.firstname newUser.middlename = this.middlename newUser.lastname = this.lastname + newUser.uuid = new_uuid // TODO: hash password here or in controller/ in User model via setter? + this.password = await argon2.hash(this.password); newUser.password = this.password console.log(newUser) diff --git a/src/models/User.ts b/src/models/User.ts index 94d770e..16f8653 100644 --- a/src/models/User.ts +++ b/src/models/User.ts @@ -1,8 +1,8 @@ -import { Entity, Column, OneToMany, ManyToOne, PrimaryGeneratedColumn, Generated, Unique, JoinTable, ManyToMany } from "typeorm"; -import { IsBoolean, IsEmail, IsInt, IsNotEmpty, IsOptional, IsPhoneNumber, IsString, isUUID, } from "class-validator"; -import { UserGroup } from './UserGroup'; +import { IsBoolean, IsEmail, IsInt, IsNotEmpty, IsOptional, IsPhoneNumber, IsString, IsUUID } from "class-validator"; +import { Column, Entity, JoinTable, ManyToMany, ManyToOne, OneToMany, PrimaryGeneratedColumn } from "typeorm"; import { Permission } from './Permission'; import { UserAction } from './UserAction'; +import { UserGroup } from './UserGroup'; /** * Defines a admin user. @@ -20,9 +20,7 @@ export class User { /** * autogenerated uuid */ - @IsOptional() - @IsInt() - @Generated("uuid") + @IsUUID("5") uuid: string; /** From ae24c3394b4243065119535d24252a66cce2cdeb Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Fri, 4 Dec 2020 17:10:00 +0100 Subject: [PATCH 04/17] =?UTF-8?q?=F0=9F=93=8F=20fit=20to=20new=20structure?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/models/{ => creation}/CreateUser.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename src/models/{ => creation}/CreateUser.ts (95%) diff --git a/src/models/CreateUser.ts b/src/models/creation/CreateUser.ts similarity index 95% rename from src/models/CreateUser.ts rename to src/models/creation/CreateUser.ts index 9b57318..92f6e22 100644 --- a/src/models/CreateUser.ts +++ b/src/models/creation/CreateUser.ts @@ -1,8 +1,8 @@ import * as argon2 from "argon2"; import { IsInt, IsOptional, IsPhoneNumber, IsString, IsUUID } from 'class-validator'; import * as uuid from 'uuid'; -import { UserGroupNotFoundError, UsernameOrEmailNeededError } from '../errors/CreateUserErrors'; -import { User } from '../models/User'; +import { UserGroupNotFoundError, UsernameOrEmailNeededError } from '../../errors/CreateUserErrors'; +import { User } from '../entities/User'; export class CreateUser { @IsString() From ce2c38e1882882da13e631fdde62a79b879d13b6 Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Fri, 4 Dec 2020 17:25:15 +0100 Subject: [PATCH 05/17] =?UTF-8?q?=F0=9F=94=92argon2=20password=20hashing?= =?UTF-8?q?=20w/=20salt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ref #14 --- src/models/creation/CreateUser.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/models/creation/CreateUser.ts b/src/models/creation/CreateUser.ts index 92f6e22..28dadc1 100644 --- a/src/models/creation/CreateUser.ts +++ b/src/models/creation/CreateUser.ts @@ -49,9 +49,7 @@ export class CreateUser { newUser.middlename = this.middlename newUser.lastname = this.lastname newUser.uuid = new_uuid - // TODO: hash password here or in controller/ in User model via setter? - this.password = await argon2.hash(this.password); - newUser.password = this.password + newUser.password = await argon2.hash(this.password + new_uuid); console.log(newUser) return newUser; From a0e6424d482c54d77a887d8b91c81a6f39b51f59 Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Fri, 4 Dec 2020 17:53:28 +0100 Subject: [PATCH 06/17] =?UTF-8?q?=F0=9F=9A=A7=20better/=20more=20errors?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ref #14 --- src/errors/CreateUserErrors.ts | 24 ----------------- src/errors/UserErrors.ts | 47 ++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 24 deletions(-) delete mode 100644 src/errors/CreateUserErrors.ts create mode 100644 src/errors/UserErrors.ts diff --git a/src/errors/CreateUserErrors.ts b/src/errors/CreateUserErrors.ts deleted file mode 100644 index 3b28735..0000000 --- a/src/errors/CreateUserErrors.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { NotFoundError } from 'routing-controllers'; -import { IsString } from 'class-validator'; - -/** - * Error to throw when a usergroup couldn't be found. - */ -export class UserGroupNotFoundError extends NotFoundError { - @IsString() - name = "UserGroupNotFoundError" - - @IsString() - message = "User Group not found!" -} - -/** - * Error to throw when no username or email is set - */ -export class UsernameOrEmailNeededError extends NotFoundError { - @IsString() - name = "UsernameOrEmailNeededError" - - @IsString() - message = "no username or email is set!" -} diff --git a/src/errors/UserErrors.ts b/src/errors/UserErrors.ts new file mode 100644 index 0000000..4c9e98b --- /dev/null +++ b/src/errors/UserErrors.ts @@ -0,0 +1,47 @@ +import { IsString } from 'class-validator'; +import { NotAcceptableError, NotFoundError } from 'routing-controllers'; + +/** + * Error to throw when a usergroup couldn't be found. + */ +export class UserGroupNotFoundError extends NotFoundError { + @IsString() + name = "UserGroupNotFoundError" + + @IsString() + message = "User Group not found!" +} + +/** + * Error to throw when no username or email is set + */ +export class UsernameOrEmailNeededError extends NotFoundError { + @IsString() + name = "UsernameOrEmailNeededError" + + @IsString() + message = "no username or email is set!" +} + +/** + * Error to throw when a user couldn't be found. + */ +export class UserNotFoundError extends NotFoundError { + @IsString() + name = "UserNotFoundError" + + @IsString() + message = "User not found!" +} + +/** + * Error to throw when two users' ids don't match. + * Usually occurs when a user tries to change a user's id. + */ +export class UserIdsNotMatchingError extends NotAcceptableError { + @IsString() + name = "UserIdsNotMatchingError" + + @IsString() + message = "The id's don't match!! \n And if you wanted to change a user's id: This isn't allowed" +} \ No newline at end of file From 091b455460dfe79276305c533170f5d153d78b73 Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Fri, 4 Dec 2020 17:53:49 +0100 Subject: [PATCH 07/17] =?UTF-8?q?=F0=9F=9A=A7=20move=20to=20uuidV4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/models/creation/CreateUser.ts | 2 +- src/models/entities/User.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/models/creation/CreateUser.ts b/src/models/creation/CreateUser.ts index 28dadc1..14f0f77 100644 --- a/src/models/creation/CreateUser.ts +++ b/src/models/creation/CreateUser.ts @@ -24,7 +24,7 @@ export class CreateUser { @IsInt() @IsOptional() groupId?: number[] | number - @IsUUID("5") + @IsUUID("4") uuid: string; public async toUser(): Promise { diff --git a/src/models/entities/User.ts b/src/models/entities/User.ts index 16f8653..6fd3321 100644 --- a/src/models/entities/User.ts +++ b/src/models/entities/User.ts @@ -18,9 +18,9 @@ export class User { id: number; /** - * autogenerated uuid + * uuid */ - @IsUUID("5") + @IsUUID("4") uuid: string; /** From 1efca4733617806363e51d013590c885b9b13405 Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Fri, 4 Dec 2020 17:55:51 +0100 Subject: [PATCH 08/17] =?UTF-8?q?=F0=9F=9A=A7=20reference=20new=20Errors?= =?UTF-8?q?=20from=20CreateUser?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/models/creation/CreateUser.ts | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/models/creation/CreateUser.ts b/src/models/creation/CreateUser.ts index 14f0f77..102919a 100644 --- a/src/models/creation/CreateUser.ts +++ b/src/models/creation/CreateUser.ts @@ -1,8 +1,10 @@ import * as argon2 from "argon2"; -import { IsInt, IsOptional, IsPhoneNumber, IsString, IsUUID } from 'class-validator'; +import { IsEmail, IsOptional, IsPhoneNumber, IsString, IsUUID } from 'class-validator'; +import { getConnectionManager } from 'typeorm'; import * as uuid from 'uuid'; -import { UserGroupNotFoundError, UsernameOrEmailNeededError } from '../../errors/CreateUserErrors'; +import { UsernameOrEmailNeededError } from '../../errors/UserErrors'; import { User } from '../entities/User'; +import { UserGroup } from '../entities/UserGroup'; export class CreateUser { @IsString() @@ -19,9 +21,11 @@ export class CreateUser { password: string; @IsString() lastname: string; + @IsEmail() @IsString() email?: string; - @IsInt() + // @IsArray() + // @IsInt() @IsOptional() groupId?: number[] | number @IsUUID("4") @@ -36,9 +40,13 @@ export class CreateUser { if (this.groupId) { // TODO: link user groups - // newUser.groups = await getConnectionManager().get().getRepository(UserGroup).findOne({ id: this.teamId }); + if (Array.isArray(this.groupId)) { + + } else { + newUser.groups = await getConnectionManager().get().getRepository(UserGroup).find({ id: this.groupId }); + } } else { - throw new UserGroupNotFoundError(); + // throw new UserGroupNotFoundError(); } const new_uuid = uuid.v4() From d556e9ba190222c615b2b7567bd30fece0c08c5c Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Fri, 4 Dec 2020 17:56:09 +0100 Subject: [PATCH 09/17] =?UTF-8?q?=F0=9F=9A=A7=20UserController?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ref #14 --- src/controllers/UserController.ts | 86 +++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 src/controllers/UserController.ts diff --git a/src/controllers/UserController.ts b/src/controllers/UserController.ts new file mode 100644 index 0000000..dbc4d3a --- /dev/null +++ b/src/controllers/UserController.ts @@ -0,0 +1,86 @@ +import { Body, Delete, Get, JsonController, OnUndefined, Param, Post, Put } from 'routing-controllers'; +import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi'; +import { getConnectionManager, Repository } from 'typeorm'; +import { EntityFromBody } from 'typeorm-routing-controllers-extensions'; +import { UserGroupNotFoundError, UserIdsNotMatchingError, UserNotFoundError } from '../errors/UserErrors'; +import { CreateUser } from '../models/creation/CreateUser'; +import { User } from '../models/entities/User'; + + +@JsonController('/users') +export class UserController { + private userRepository: Repository; + + /** + * Gets the repository of this controller's model/entity. + */ + constructor() { + this.userRepository = getConnectionManager().get().getRepository(User); + } + + @Get() + @ResponseSchema(User, { isArray: true }) + @OpenAPI({ description: 'Lists all users.' }) + getAll() { + return this.userRepository.find(); + } + + @Get('/:id') + @ResponseSchema(User) + @ResponseSchema(UserNotFoundError, { statusCode: 404 }) + @OnUndefined(UserNotFoundError) + @OpenAPI({ description: 'Returns a user of a specified id (if it exists)' }) + getOne(@Param('id') id: number) { + return this.userRepository.findOne({ id: id }); + } + + @Post() + @ResponseSchema(User) + @ResponseSchema(UserGroupNotFoundError) + @OpenAPI({ description: 'Create a new user object (id will be generated automagicly).' }) + async post(@Body({ validate: true }) createUser: CreateUser) { + let user; + try { + user = await createUser.toUser(); + } catch (error) { + return error; + } + + return this.userRepository.save(user); + } + + @Put('/:id') + @ResponseSchema(User) + @ResponseSchema(UserNotFoundError, { statusCode: 404 }) + @ResponseSchema(UserIdsNotMatchingError, { statusCode: 406 }) + @OpenAPI({ description: "Update a user object (id can't be changed)." }) + async put(@Param('id') id: number, @EntityFromBody() user: User) { + let oldUser = await this.userRepository.findOne({ id: id }); + + if (!oldUser) { + throw new UserNotFoundError(); + } + + if (oldUser.id != user.id) { + throw new UserIdsNotMatchingError(); + } + + await this.userRepository.update(oldUser, user); + return user; + } + + @Delete('/:id') + @ResponseSchema(User) + @ResponseSchema(UserNotFoundError, { statusCode: 404 }) + @OpenAPI({ description: 'Delete a specified runner (if it exists).' }) + async remove(@Param('id') id: number) { + let runner = await this.userRepository.findOne({ id: id }); + + if (!runner) { + throw new UserNotFoundError(); + } + + await this.userRepository.delete(runner); + return runner; + } +} From 5b7f3ae12f3f8749aa637d18763cf5f5df8001eb Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Fri, 4 Dec 2020 18:02:28 +0100 Subject: [PATCH 10/17] =?UTF-8?q?=F0=9F=9A=A7=20CreateUser=20group=20searc?= =?UTF-8?q?h=20+=20adding?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ref #14 --- src/models/creation/CreateUser.ts | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/models/creation/CreateUser.ts b/src/models/creation/CreateUser.ts index 102919a..9595039 100644 --- a/src/models/creation/CreateUser.ts +++ b/src/models/creation/CreateUser.ts @@ -2,7 +2,7 @@ import * as argon2 from "argon2"; import { IsEmail, IsOptional, IsPhoneNumber, IsString, IsUUID } from 'class-validator'; import { getConnectionManager } from 'typeorm'; import * as uuid from 'uuid'; -import { UsernameOrEmailNeededError } from '../../errors/UserErrors'; +import { UserGroupNotFoundError, UsernameOrEmailNeededError } from '../../errors/UserErrors'; import { User } from '../entities/User'; import { UserGroup } from '../entities/UserGroup'; @@ -24,8 +24,6 @@ export class CreateUser { @IsEmail() @IsString() email?: string; - // @IsArray() - // @IsInt() @IsOptional() groupId?: number[] | number @IsUUID("4") @@ -39,14 +37,20 @@ export class CreateUser { } if (this.groupId) { - // TODO: link user groups if (Array.isArray(this.groupId)) { - + let found_groups = [] + this.groupId.forEach(async (g) => { + const foundGroup = await getConnectionManager().get().getRepository(UserGroup).find({ id: g }); + if (foundGroup) { + found_groups.push(foundGroup) + } else { + throw new UserGroupNotFoundError(); + } + }); + newUser.groups = found_groups } else { newUser.groups = await getConnectionManager().get().getRepository(UserGroup).find({ id: this.groupId }); } - } else { - // throw new UserGroupNotFoundError(); } const new_uuid = uuid.v4() From 3275b5fd8008b0c10601c33844b6d35ab39d4972 Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Fri, 4 Dec 2020 18:34:14 +0100 Subject: [PATCH 11/17] =?UTF-8?q?=F0=9F=9A=A7=20UserGroups?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ref #14 --- src/controllers/UserGroupController.ts | 86 ++++++++++++++++++++++++++ src/errors/UserGroupErrors.ts | 36 +++++++++++ src/models/creation/CreateUserGroup.ts | 28 +++++++++ src/models/entities/UserGroup.ts | 8 +-- 4 files changed, 154 insertions(+), 4 deletions(-) create mode 100644 src/controllers/UserGroupController.ts create mode 100644 src/errors/UserGroupErrors.ts create mode 100644 src/models/creation/CreateUserGroup.ts diff --git a/src/controllers/UserGroupController.ts b/src/controllers/UserGroupController.ts new file mode 100644 index 0000000..edb8658 --- /dev/null +++ b/src/controllers/UserGroupController.ts @@ -0,0 +1,86 @@ +import { Body, Delete, Get, JsonController, OnUndefined, Param, Post, Put } from 'routing-controllers'; +import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi'; +import { getConnectionManager, Repository } from 'typeorm'; +import { EntityFromBody } from 'typeorm-routing-controllers-extensions'; +import { UserGroupIdsNotMatchingError, UserGroupNotFoundError } from '../errors/UserGroupErrors'; +import { CreateUserGroup } from '../models/creation/CreateUserGroup'; +import { UserGroup } from '../models/entities/UserGroup'; + + +@JsonController('/usergroups') +export class UserController { + private userGroupsRepository: Repository; + + /** + * Gets the repository of this controller's model/entity. + */ + constructor() { + this.userGroupsRepository = getConnectionManager().get().getRepository(UserGroup); + } + + @Get() + @ResponseSchema(UserGroup, { isArray: true }) + @OpenAPI({ description: 'Lists all usergroups.' }) + getAll() { + return this.userGroupsRepository.find(); + } + + @Get('/:id') + @ResponseSchema(UserGroup) + @ResponseSchema(UserGroupNotFoundError, { statusCode: 404 }) + @OnUndefined(UserGroupNotFoundError) + @OpenAPI({ description: 'Returns a usergroup of a specified id (if it exists)' }) + getOne(@Param('id') id: number) { + return this.userGroupsRepository.findOne({ id: id }); + } + + @Post() + @ResponseSchema(UserGroup) + @ResponseSchema(UserGroupNotFoundError) + @OpenAPI({ description: 'Create a new usergroup object (id will be generated automagicly).' }) + async post(@Body({ validate: true }) createUserGroup: CreateUserGroup) { + let userGroup; + try { + userGroup = await createUserGroup.toUserGroup(); + } catch (error) { + return error; + } + + return this.userGroupsRepository.save(userGroup); + } + + @Put('/:id') + @ResponseSchema(UserGroup) + @ResponseSchema(UserGroupNotFoundError, { statusCode: 404 }) + @ResponseSchema(UserGroupIdsNotMatchingError, { statusCode: 406 }) + @OpenAPI({ description: "Update a usergroup object (id can't be changed)." }) + async put(@Param('id') id: number, @EntityFromBody() userGroup: UserGroup) { + let oldUserGroup = await this.userGroupsRepository.findOne({ id: id }); + + if (!oldUserGroup) { + throw new UserGroupNotFoundError() + } + + if (oldUserGroup.id != userGroup.id) { + throw new UserGroupIdsNotMatchingError(); + } + + await this.userGroupsRepository.update(oldUserGroup, userGroup); + return userGroup; + } + + @Delete('/:id') + @ResponseSchema(UserGroup) + @ResponseSchema(UserGroupNotFoundError, { statusCode: 404 }) + @OpenAPI({ description: 'Delete a specified usergroup (if it exists).' }) + async remove(@Param('id') id: number) { + let userGroup = await this.userGroupsRepository.findOne({ id: id }); + + if (!userGroup) { + throw new UserGroupNotFoundError(); + } + + await this.userGroupsRepository.delete(userGroup); + return userGroup; + } +} diff --git a/src/errors/UserGroupErrors.ts b/src/errors/UserGroupErrors.ts new file mode 100644 index 0000000..15495b9 --- /dev/null +++ b/src/errors/UserGroupErrors.ts @@ -0,0 +1,36 @@ +import { IsString } from 'class-validator'; +import { NotAcceptableError, NotFoundError } from 'routing-controllers'; + +/** + * Error to throw when no groupname is set + */ +export class GroupNameNeededError extends NotFoundError { + @IsString() + name = "GroupNameNeededError" + + @IsString() + message = "no groupname is set!" +} + +/** + * Error to throw when a usergroup couldn't be found. + */ +export class UserGroupNotFoundError extends NotFoundError { + @IsString() + name = "UserGroupNotFoundError" + + @IsString() + message = "User Group not found!" +} + +/** + * Error to throw when two usergroups' ids don't match. + * Usually occurs when a user tries to change a usergroups's id. + */ +export class UserGroupIdsNotMatchingError extends NotAcceptableError { + @IsString() + name = "UserGroupIdsNotMatchingError" + + @IsString() + message = "The id's don't match!! \n If you wanted to change a usergroup's id: This isn't allowed" +} \ No newline at end of file diff --git a/src/models/creation/CreateUserGroup.ts b/src/models/creation/CreateUserGroup.ts new file mode 100644 index 0000000..d55618c --- /dev/null +++ b/src/models/creation/CreateUserGroup.ts @@ -0,0 +1,28 @@ +import { IsOptional, IsString } from 'class-validator'; +import { GroupNameNeededError } from '../../errors/UserGroupErrors'; +import { UserGroup } from '../entities/UserGroup'; + +export class CreateUserGroup { + @IsOptional() + @IsString() + name: string; + @IsOptional() + @IsString() + description?: string; + + public async toUserGroup(): Promise { + let newUserGroup: UserGroup = new UserGroup(); + + if (this.name === undefined) { + throw new GroupNameNeededError(); + } + + newUserGroup.name = this.name + if (this.description) { + newUserGroup.description = this.description + } + + console.log(newUserGroup) + return newUserGroup; + } +} \ No newline at end of file diff --git a/src/models/entities/UserGroup.ts b/src/models/entities/UserGroup.ts index c3b2f0c..2156d14 100644 --- a/src/models/entities/UserGroup.ts +++ b/src/models/entities/UserGroup.ts @@ -1,17 +1,17 @@ -import { PrimaryGeneratedColumn, Column, OneToMany, Entity, ManyToOne } from "typeorm"; import { IsInt, IsNotEmpty, IsOptional, - IsString, + IsString } from "class-validator"; +import { Column, Entity, ManyToOne, PrimaryGeneratedColumn } from "typeorm"; import { Permission } from "./Permission"; /** * Defines the UserGroup interface. */ @Entity() -export abstract class UserGroup { +export class UserGroup { /** * Autogenerated unique id (primary key). */ @@ -37,7 +37,7 @@ export abstract class UserGroup { /** * The group's description */ - @Column({nullable: true}) + @Column({ nullable: true }) @IsOptional() @IsString() description?: string; From b101682e3cd23ea501ca8c6036084c0d4aef2644 Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Fri, 4 Dec 2020 18:34:24 +0100 Subject: [PATCH 12/17] CreateUser ref #14 --- src/models/creation/CreateUser.ts | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/src/models/creation/CreateUser.ts b/src/models/creation/CreateUser.ts index 9595039..b024e4c 100644 --- a/src/models/creation/CreateUser.ts +++ b/src/models/creation/CreateUser.ts @@ -37,20 +37,19 @@ export class CreateUser { } if (this.groupId) { - if (Array.isArray(this.groupId)) { - let found_groups = [] - this.groupId.forEach(async (g) => { - const foundGroup = await getConnectionManager().get().getRepository(UserGroup).find({ id: g }); - if (foundGroup) { - found_groups.push(foundGroup) - } else { - throw new UserGroupNotFoundError(); - } - }); - newUser.groups = found_groups - } else { - newUser.groups = await getConnectionManager().get().getRepository(UserGroup).find({ id: this.groupId }); + if (!Array.isArray(this.groupId)) { + this.groupId = [this.groupId] } + let found_groups = [] + this.groupId.forEach(async (g) => { + const foundGroup = await getConnectionManager().get().getRepository(UserGroup).find({ id: g }); + if (foundGroup) { + found_groups.push(foundGroup) + } else { + throw new UserGroupNotFoundError(); + } + }); + newUser.groups = found_groups } const new_uuid = uuid.v4() From a3b79ef21df1ce96f0b301097cc0122eef06bfbd Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Fri, 4 Dec 2020 18:36:46 +0100 Subject: [PATCH 13/17] Fix ref #14 --- src/controllers/UserGroupController.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controllers/UserGroupController.ts b/src/controllers/UserGroupController.ts index edb8658..fb158ed 100644 --- a/src/controllers/UserGroupController.ts +++ b/src/controllers/UserGroupController.ts @@ -8,7 +8,7 @@ import { UserGroup } from '../models/entities/UserGroup'; @JsonController('/usergroups') -export class UserController { +export class UserGroupController { private userGroupsRepository: Repository; /** From 451d0c92dddd4e56626eca8c8b9e6728b6427a23 Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Fri, 4 Dec 2020 19:02:07 +0100 Subject: [PATCH 14/17] trying to fix UserGroupNotFoundError (false/not triggering) ref #14 --- src/models/creation/CreateUser.ts | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/models/creation/CreateUser.ts b/src/models/creation/CreateUser.ts index b024e4c..1182519 100644 --- a/src/models/creation/CreateUser.ts +++ b/src/models/creation/CreateUser.ts @@ -42,14 +42,16 @@ export class CreateUser { } let found_groups = [] this.groupId.forEach(async (g) => { - const foundGroup = await getConnectionManager().get().getRepository(UserGroup).find({ id: g }); - if (foundGroup) { - found_groups.push(foundGroup) - } else { - throw new UserGroupNotFoundError(); - } + const foundGroup = await getConnectionManager().get().getRepository(UserGroup).findOne({ id: g }); + console.log(foundGroup); + found_groups.push(foundGroup) }); - newUser.groups = found_groups + console.log(found_groups); + if (found_groups.includes(undefined) || found_groups.includes(null)) { + throw new UserGroupNotFoundError(); + } else { + newUser.groups = found_groups + } } const new_uuid = uuid.v4() From d4753a02d4fa04beaf0794f1b15debb38c7827b9 Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Fri, 4 Dec 2020 19:31:10 +0100 Subject: [PATCH 15/17] =?UTF-8?q?=F0=9F=90=9E=20fixed=20UserGroupNotFoundE?= =?UTF-8?q?rror=20throwing=20=E2=9C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ref #14 --- src/models/creation/CreateUser.ts | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/src/models/creation/CreateUser.ts b/src/models/creation/CreateUser.ts index 1182519..5660227 100644 --- a/src/models/creation/CreateUser.ts +++ b/src/models/creation/CreateUser.ts @@ -40,17 +40,23 @@ export class CreateUser { if (!Array.isArray(this.groupId)) { this.groupId = [this.groupId] } - let found_groups = [] - this.groupId.forEach(async (g) => { - const foundGroup = await getConnectionManager().get().getRepository(UserGroup).findOne({ id: g }); - console.log(foundGroup); - found_groups.push(foundGroup) - }); - console.log(found_groups); - if (found_groups.includes(undefined) || found_groups.includes(null)) { + const groupIDs: number[] = this.groupId + let errors = 0 + const validateusergroups = async () => { + let foundgroups = [] + for (const g of groupIDs) { + const found = await getConnectionManager().get().getRepository(UserGroup).find({ id: g }); + if (found.length === 0) { + errors++ + } else { + foundgroups.push(found) + } + } + newUser.groups = foundgroups + } + await validateusergroups() + if (errors !== 0) { throw new UserGroupNotFoundError(); - } else { - newUser.groups = found_groups } } From a7cf86eae41991956db6b48f8c70ed0d6cec374a Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Fri, 4 Dec 2020 19:32:17 +0100 Subject: [PATCH 16/17] =?UTF-8?q?=F0=9F=9A=A7=20CreateUser=20-=20add=20gro?= =?UTF-8?q?up=20as=20object=20instead=20of=20nested=20array?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/models/creation/CreateUser.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/models/creation/CreateUser.ts b/src/models/creation/CreateUser.ts index 5660227..4bd8cd4 100644 --- a/src/models/creation/CreateUser.ts +++ b/src/models/creation/CreateUser.ts @@ -49,7 +49,7 @@ export class CreateUser { if (found.length === 0) { errors++ } else { - foundgroups.push(found) + foundgroups.push(found[0]) } } newUser.groups = foundgroups From fe46e5d667a34b2b57e0e993a414e8f88026fe79 Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Fri, 4 Dec 2020 21:29:49 +0100 Subject: [PATCH 17/17] =?UTF-8?q?=E2=9C=85=20-=20close=20#14?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit