diff --git a/docker-compose.yml b/docker-compose.yml index 81c3fcda..c0aec05e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -104,6 +104,7 @@ services: KEYCLOAK_USER: admin KEYCLOAK_PASSWORD: Pa55w0rd KEYCLOAK_IMPORT: /opt/jboss/keycloak/imports/realm-export.json -Dkeycloak.profile.feature.upload_scripts=enabled + PROXY_ADDRESS_FORWARDING: 'true' healthcheck: test: [ diff --git a/package-lock.json b/package-lock.json index 83a9f865..f1267b1f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9298,9 +9298,9 @@ } }, "tmpl": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz", - "integrity": "sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE=", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", "dev": true }, "to-fast-properties": { diff --git a/public/src/api/httpClient.ts b/public/src/api/httpClient.ts index 8882249e..c8eda47e 100644 --- a/public/src/api/httpClient.ts +++ b/public/src/api/httpClient.ts @@ -51,7 +51,7 @@ export function postSubscriptions(email: string): Promise { export function postUser(data: RegistrationUser): Promise { return makeApiRequest({ - url: ApiUrl.Users, + url: `${ApiUrl.Users}/${data.op}`, method: 'post', data }); diff --git a/public/src/api/makeApiRequest.ts b/public/src/api/makeApiRequest.ts index e1b9b8ec..ba6941b3 100644 --- a/public/src/api/makeApiRequest.ts +++ b/public/src/api/makeApiRequest.ts @@ -15,18 +15,27 @@ export function makeApiRequest( return response.data; }) .catch((error) => { - if (error.response.status === 401) { - sessionStorage.removeItem('email'); - sessionStorage.removeItem('token'); - return { - ...error, - errorText: - 'Authentication failed, please check your credentials and try again' - }; + switch (error.response.status) { + case 401: + sessionStorage.removeItem('email'); + sessionStorage.removeItem('token'); + return { + ...error, + errorText: + 'Authentication failed, please check your credentials and try again' + }; + break; + case 409: + return { + ...error, + errorText: 'User already exists' + }; + break; + default: + return { + ...error, + errorText: 'Something went wrong. Please try again later' + }; } - return { - ...error, - errorText: 'Something went wrong. Please try again later' - }; }); } diff --git a/public/src/interfaces/User.ts b/public/src/interfaces/User.ts index 9598f008..2eb339ca 100644 --- a/public/src/interfaces/User.ts +++ b/public/src/interfaces/User.ts @@ -25,4 +25,5 @@ export interface RegistrationUser { lastName: string; firstName: string; password?: string; + op?: LoginFormMode; } diff --git a/public/src/pages/auth/Login/Login.tsx b/public/src/pages/auth/Login/Login.tsx index 301806b9..1a811639 100644 --- a/public/src/pages/auth/Login/Login.tsx +++ b/public/src/pages/auth/Login/Login.tsx @@ -58,10 +58,6 @@ export const Login: FC = () => { const { value } = target as HTMLSelectElement & { value: LoginFormMode }; setForm({ ...form, op: value }); setMode(value); - switch (value as LoginFormMode) { - default: - return; - } }; const sendUser = (e: FormEvent) => { diff --git a/public/src/pages/auth/Register/Register.tsx b/public/src/pages/auth/Register/Register.tsx index 205192a9..909e7b61 100644 --- a/public/src/pages/auth/Register/Register.tsx +++ b/public/src/pages/auth/Register/Register.tsx @@ -1,6 +1,6 @@ import React, { FC, FormEvent, useState } from 'react'; import { postUser } from '../../../api/httpClient'; -import { RegistrationUser } from '../../../interfaces/User'; +import { RegistrationUser, LoginFormMode } from '../../../interfaces/User'; import AuthLayout from '../AuthLayout'; import { Link } from 'react-router-dom'; import showRegResponse from './showRegReponse'; @@ -9,7 +9,8 @@ const defaultUser: RegistrationUser = { email: '', firstName: '', lastName: '', - password: '' + password: '', + op: LoginFormMode.BASIC }; export const Register: FC = () => { @@ -17,22 +18,51 @@ export const Register: FC = () => { const { email, firstName, lastName, password } = form; const [regResponse, setRegResponse] = useState(); + const [errorText, setErrorText] = useState(); + + const [authMode, setAuthMode] = useState(LoginFormMode.BASIC); const onInput = ({ target }: { target: EventTarget | null }) => { const { name, value } = target as HTMLInputElement; setForm({ ...form, [name]: value }); }; + const onAuthModeChange = ({ target }: { target: EventTarget | null }) => { + const { value } = target as HTMLSelectElement & { value: LoginFormMode }; + setForm({ ...form, op: value }); + setAuthMode(value); + }; + const sendUser = (e: FormEvent) => { e.preventDefault(); - postUser(form).then((data) => setRegResponse(data)); + postUser(form).then((data) => { + setRegResponse(data); + setErrorText(data.errorText); + }); }; return (
+
+ + +
{ onInput={onInput} />
- + {errorText &&
{errorText}
}
{ + this.log.debug(`Called isUserExist`); + + const { access_token, token_type } = await this.generateToken(); + + const [existingUser]: unknown[] = await this.httpClient.get( + `${this.server_uri}/admin/realms/${this.realm}/users?email=${email}`, + { + headers: { + 'Content-Type': 'application/json', + Authorization: `${token_type} ${access_token}`, + }, + responseType: 'json', + }, + ); + return !!existingUser; + } + public async registerUser({ firstName, lastName, email, password, - }: RegisterUserData): Promise { + }: RegisterUserData): Promise { this.log.debug(`Called registerUser`); const { access_token, token_type } = await this.generateToken(); - await this.httpClient.post( + return this.httpClient.post( `${this.server_uri}/admin/realms/${this.realm}/users`, { firstName, diff --git a/src/users/api/CreateUserRequest.ts b/src/users/api/CreateUserRequest.ts index a6e6dbd2..37a123ea 100644 --- a/src/users/api/CreateUserRequest.ts +++ b/src/users/api/CreateUserRequest.ts @@ -4,4 +4,5 @@ import { UserDto } from './UserDto'; export class CreateUserRequest extends UserDto { @ApiProperty() password: string; + op: string; } diff --git a/src/users/api/UserDto.ts b/src/users/api/UserDto.ts index 99ffe965..50c999a4 100644 --- a/src/users/api/UserDto.ts +++ b/src/users/api/UserDto.ts @@ -1,5 +1,4 @@ import { ApiProperty } from '@nestjs/swagger'; -import { User } from '../../model/user.entity'; export class UserDto { @ApiProperty() @@ -11,11 +10,11 @@ export class UserDto { @ApiProperty() lastName: string; - public static convertToApi(user: User): UserDto { - return { - email: user.email, - firstName: user.firstName, - lastName: user.lastName, - }; + constructor( + params: { + [P in keyof UserDto]: UserDto[P]; + }, + ) { + Object.assign(this, params); } } diff --git a/src/users/users.controller.ts b/src/users/users.controller.ts index b66a9adf..8255756a 100644 --- a/src/users/users.controller.ts +++ b/src/users/users.controller.ts @@ -3,6 +3,7 @@ import { Controller, Get, Header, + HttpException, HttpStatus, InternalServerErrorException, Logger, @@ -59,7 +60,7 @@ export class UsersController { async getUser(@Param('email') email: string): Promise { try { this.logger.debug(`Find a user by email: ${email}`); - return UserDto.convertToApi(await this.usersService.findByEmail(email)); + return new UserDto(await this.usersService.findByEmail(email)); } catch (err) { throw new InternalServerErrorException({ error: err.message, @@ -150,10 +151,10 @@ export class UsersController { throw new NotFoundException('User not found in ldap'); } - return users.map(UserDto.convertToApi); + return users.map((user: User) => new UserDto(user)); } - @Post() + @Post('/basic') @ApiOperation({ description: 'creates user', }) @@ -163,9 +164,15 @@ export class UsersController { }) async createUser(@Body() user: CreateUserRequest): Promise { try { - this.logger.debug(`Create a user: ${user}`); + this.logger.debug(`Create a basic user: ${user}`); - const newUser = UserDto.convertToApi( + const userExists = await this.usersService.findByEmail(user.email); + + if (userExists) { + throw new HttpException('User already exists', 409); + } + + return new UserDto( await this.usersService.createUser( user.email, user.firstName, @@ -173,20 +180,47 @@ export class UsersController { user.password, ), ); + } catch (err) { + throw new HttpException( + err.message ?? 'Something went wrong', + err.status ?? 500, + ); + } + } + + @Post('/oidc') + @ApiOperation({ + description: 'creates user', + }) + @ApiResponse({ + type: UserDto, + status: 200, + }) + async createOIDCUser(@Body() user: CreateUserRequest): Promise { + try { + this.logger.debug(`Create a OIDC user: ${user}`); - await this.keyCloakService.registerUser({ + const userExists = await this.keyCloakService.isUserExists({ email: user.email, - firstName: user.firstName, - lastName: user.lastName, - password: user.password, }); - return newUser; + if (userExists) { + throw new HttpException('User already exists', 409); + } + + return new UserDto( + await this.keyCloakService.registerUser({ + email: user.email, + firstName: user.firstName, + lastName: user.lastName, + password: user.password, + }), + ); } catch (err) { - throw new InternalServerErrorException({ - error: err.message, - location: __filename, - }); + throw new HttpException( + err.message ?? 'Something went wrong', + err.status ?? 500, + ); } }