diff --git a/kdb-bot/src/bot_api/abc/auth_service_abc.py b/kdb-bot/src/bot_api/abc/auth_service_abc.py index d7cfb9eb85..ba25ee0967 100644 --- a/kdb-bot/src/bot_api/abc/auth_service_abc.py +++ b/kdb-bot/src/bot_api/abc/auth_service_abc.py @@ -7,6 +7,7 @@ from bot_api.filter.auth_user_select_criteria import AuthUserSelectCriteria from bot_api.model.auth_user_dto import AuthUserDTO from bot_api.model.auth_user_filtered_result_dto import AuthUserFilteredResultDTO from bot_api.model.email_string_dto import EMailStringDTO +from bot_api.model.o_auth_dto import OAuthDTO from bot_api.model.reset_password_dto import ResetPasswordDTO from bot_api.model.token_dto import TokenDTO from bot_api.model.update_auth_user_dto import UpdateAuthUserDTO @@ -37,7 +38,7 @@ class AuthServiceABC(ABC): async def get_filtered_auth_users_async(self, criteria: AuthUserSelectCriteria) -> AuthUserFilteredResultDTO: pass @abstractmethod - async def get_auth_user_by_email_async(self, email: str) -> AuthUserDTO: pass + async def get_auth_user_by_email_async(self, email: str, with_password: bool = False) -> AuthUserDTO: pass @abstractmethod async def find_auth_user_by_email_async(self, email: str) -> AuthUserDTO: pass @@ -46,7 +47,10 @@ class AuthServiceABC(ABC): async def add_auth_user_async(self, user_dto: AuthUserDTO): pass @abstractmethod - async def add_auth_user_by_discord_async(self, user_dto: AuthUserDTO): pass + async def add_auth_user_by_oauth_async(self, dto: OAuthDTO): pass + + @abstractmethod + async def add_auth_user_by_discord_async(self, user_dto: AuthUserDTO) -> OAuthDTO: pass @abstractmethod async def update_user_async(self, update_user_dto: UpdateAuthUserDTO): pass diff --git a/kdb-bot/src/bot_api/config/apisettings.edrafts-lapi.json b/kdb-bot/src/bot_api/config/apisettings.edrafts-lapi.json index 9c269d8a39..f122db51e2 100644 --- a/kdb-bot/src/bot_api/config/apisettings.edrafts-lapi.json +++ b/kdb-bot/src/bot_api/config/apisettings.edrafts-lapi.json @@ -13,7 +13,8 @@ }, "DiscordAuthentication": { "ClientSecret": "V3FTb3JYVFBiVktEeHZxdWJDWW4xcnBCbXRwdmpwcy0=", - "RedirectURL": "http://localhost:5000/api/auth/discord/register", + "_RedirectURL": "http://localhost:5000/api/auth/discord/register", + "RedirectURL": "http://localhost:4200/auth/register", "Scope": [ "identify", "email" diff --git a/kdb-bot/src/bot_api/controller/auth_discord_controller.py b/kdb-bot/src/bot_api/controller/auth_discord_controller.py index 8efe4c06a2..8725dd8ee6 100644 --- a/kdb-bot/src/bot_api/controller/auth_discord_controller.py +++ b/kdb-bot/src/bot_api/controller/auth_discord_controller.py @@ -7,15 +7,17 @@ from cpl_core.mailing import EMailClientABC, EMailClientSettings from cpl_core.utils import CredentialManager from cpl_discord.service import DiscordBotServiceABC from cpl_translation import TranslatePipe -from flask import jsonify +from flask import jsonify, Response from flask import request, session from requests_oauthlib import OAuth2Session from bot_api.abc.auth_service_abc import AuthServiceABC from bot_api.api import Api from bot_api.configuration.discord_authentication_settings import DiscordAuthenticationSettings +from bot_api.json_processor import JSONProcessor from bot_api.logging.api_logger import ApiLogger from bot_api.model.auth_user_dto import AuthUserDTO +from bot_api.model.o_auth_dto import OAuthDTO from bot_api.route.route import Route from bot_data.model.auth_role_enum import AuthRoleEnum @@ -50,27 +52,26 @@ class AuthDiscordController: self._mailer = mailer self._auth_service = auth_service - @Route.get(f'{BasePath}/get-url') - async def get_url(self): - oauth = OAuth2Session(self._bot.user.id, redirect_uri=self._auth_settings.redirect_url, scope=self._auth_settings.scope) - login_url, state = oauth.authorization_url(self._auth_settings.auth_url) - session['state'] = state - # return jsonify({'loginURL': login_url}) - return 'Login with Discord' - - @Route.get(f'{BasePath}/register') - async def discord_register(self): - discord = OAuth2Session(self._bot.user.id, redirect_uri=self._auth_settings.redirect_url, state=session['state'], scope=self._auth_settings.scope) + def _get_user_from_discord_response(self) -> dict: + discord = OAuth2Session(self._bot.user.id, redirect_uri=self._auth_settings.redirect_url, state=request.args.get('state'), scope=self._auth_settings.scope) token = discord.fetch_token( self._auth_settings.token_url, client_secret=CredentialManager.decrypt(self._auth_settings.client_secret), authorization_response=request.url, ) - session['discord_token'] = token discord = OAuth2Session(self._bot.user.id, token=token) - response = discord.get('https://discordapp.com/api' + '/users/@me').json() + return discord.get('https://discordapp.com/api' + '/users/@me').json() - await self._auth_service.add_auth_user_by_discord_async(AuthUserDTO( + @Route.get(f'{BasePath}/get-url') + async def get_url(self): + oauth = OAuth2Session(self._bot.user.id, redirect_uri=self._auth_settings.redirect_url, scope=self._auth_settings.scope) + login_url, state = oauth.authorization_url(self._auth_settings.auth_url) + return jsonify({'loginUrl': login_url}) + + @Route.get(f'{BasePath}/create-user') + async def discord_create_user(self) -> Response: + response = self._get_user_from_discord_response() + result = await self._auth_service.add_auth_user_by_discord_async(AuthUserDTO( 0, response['username'], response['discriminator'], @@ -80,4 +81,10 @@ class AuthDiscordController: AuthRoleEnum.normal, response['id'] )) + return jsonify(result.to_dict()) + + @Route.post(f'{BasePath}/register') + async def discord_register(self): + dto: OAuthDTO = JSONProcessor.process(OAuthDTO, request.get_json(force=True, silent=True)) + await self._auth_service.add_auth_user_by_oauth_async(dto) return '', 200 diff --git a/kdb-bot/src/bot_api/model/auth_user_dto.py b/kdb-bot/src/bot_api/model/auth_user_dto.py index 8d42e1fad6..e5831f5fe5 100644 --- a/kdb-bot/src/bot_api/model/auth_user_dto.py +++ b/kdb-bot/src/bot_api/model/auth_user_dto.py @@ -8,14 +8,14 @@ class AuthUserDTO(DtoABC): def __init__( self, - id: int, - first_name: str, - last_name: str, - email: str, - password: str, - confirmation_id: Optional[str], - auth_role: AuthRoleEnum, - user_id: Optional[int], + id: int = None, + first_name: str = None, + last_name: str = None, + email: str = None, + password: str = None, + confirmation_id: Optional[str] = None, + auth_role: AuthRoleEnum = None, + user_id: Optional[int] = None, ): DtoABC.__init__(self) @@ -27,7 +27,7 @@ class AuthUserDTO(DtoABC): self._is_confirmed = confirmation_id is None self._auth_role = auth_role self._user_id = user_id - + @property def id(self) -> int: return self._id diff --git a/kdb-bot/src/bot_api/model/o_auth_dto.py b/kdb-bot/src/bot_api/model/o_auth_dto.py new file mode 100644 index 0000000000..72eb7bd121 --- /dev/null +++ b/kdb-bot/src/bot_api/model/o_auth_dto.py @@ -0,0 +1,44 @@ +from typing import Optional + +from bot_api.abc.dto_abc import DtoABC +from bot_api.model.auth_user_dto import AuthUserDTO +from bot_data.model.auth_role_enum import AuthRoleEnum + + +class OAuthDTO(DtoABC): + + def __init__( + self, + user: AuthUserDTO, + o_auth_id: Optional[str], + ): + DtoABC.__init__(self) + + self._user = user + self._oauth_id = o_auth_id + + @property + def user(self) -> AuthUserDTO: + return self._user + + @user.setter + def user(self, value: AuthUserDTO): + self._user = value + + @property + def oauth_id(self) -> Optional[str]: + return self._oauth_id + + @oauth_id.setter + def oauth_id(self, value: Optional[str]): + self._oauth_id = value + + def from_dict(self, values: dict): + self._user = AuthUserDTO().from_dict(values['user']) + self._oauth_id = values['oAuthId'] + + def to_dict(self) -> dict: + return { + 'user': self._user.to_dict(), + 'oAuthId': self._oauth_id + } diff --git a/kdb-bot/src/bot_api/service/auth_service.py b/kdb-bot/src/bot_api/service/auth_service.py index 99d4fad5f6..9eee110b68 100644 --- a/kdb-bot/src/bot_api/service/auth_service.py +++ b/kdb-bot/src/bot_api/service/auth_service.py @@ -25,6 +25,7 @@ from bot_api.mail.mail_thread import MailThread from bot_api.model.auth_user_dto import AuthUserDTO from bot_api.model.auth_user_filtered_result_dto import AuthUserFilteredResultDTO from bot_api.model.email_string_dto import EMailStringDTO +from bot_api.model.o_auth_dto import OAuthDTO from bot_api.model.reset_password_dto import ResetPasswordDTO from bot_api.model.token_dto import TokenDTO from bot_api.model.update_auth_user_dto import UpdateAuthUserDTO @@ -190,9 +191,10 @@ class AuthService(AuthServiceABC): users.total_count ) - async def get_auth_user_by_email_async(self, email: str) -> AuthUserDTO: + async def get_auth_user_by_email_async(self, email: str, with_password: bool = False) -> AuthUserDTO: try: - return AUT.to_dto(self._auth_users.get_auth_user_by_email(email)) + user = self._auth_users.get_auth_user_by_email(email) + return AUT.to_dto(user, password=user.password if with_password else None) except Exception as e: self._logger.error(__name__, f'AuthUser not found', e) raise ServiceException(ServiceErrorCode.InvalidData, f'User not found {email}') @@ -225,24 +227,54 @@ class AuthService(AuthServiceABC): self._logger.error(__name__, f'Cannot add user with E-Mal {user_dto.email}', e) raise ServiceException(ServiceErrorCode.UnableToAdd, "Invalid E-Mail") - async def add_auth_user_by_discord_async(self, user_dto: AuthUserDTO): + async def add_auth_user_by_oauth_async(self, dto: OAuthDTO): + db_user = self._auth_users.find_auth_user_by_email(dto.user.email) + + if db_user is None: + raise ServiceException(ServiceErrorCode.InvalidUser, 'User not found') + + if db_user.oauth_id != dto.oauth_id: + raise ServiceException(ServiceErrorCode.InvalidUser, 'Wrong OAuthId') + + db_user.first_name = dto.user.first_name + db_user.last_name = dto.user.last_name + db_user.password_salt = uuid.uuid4() + db_user.password = self._hash_sha256(dto.user.password, db_user.password_salt) + db_user.oauth_id = None + self._auth_users.update_auth_user(db_user) + self._db.save_changes() + + async def add_auth_user_by_discord_async(self, user_dto: AuthUserDTO) -> OAuthDTO: db_user = self._auth_users.find_auth_user_by_email(user_dto.email) # user exists if db_user is not None and db_user.user_id is not None: - raise ServiceException(ServiceErrorCode.InvalidUser, 'User already exists') + # raise ServiceException(ServiceErrorCode.InvalidUser, 'User already exists') + self._logger.debug(__name__, f'Discord user already exists') + return OAuthDTO(AUT.to_dto(db_user), None) # user exists but discord user id not set elif db_user is not None and db_user.user_id is None: + self._logger.debug(__name__, f'Auth user exists but not linked with discord') user = self._users.get_users_by_discord_id(user_dto.user_id).single() db_user.user_id = user.user_id - self._auth_users.update_auth_user(db_user) - self._db.save_changes() - return AUT.to_dto(db_user) + db_user.oauth_id = None - # user does not exists - await self.add_auth_user_async(user_dto) - db_user = self._auth_users.get_auth_user_by_email(user_dto.email) - return AUT.to_dto(db_user, password=db_user.password) + else: + # user does not exists + self._logger.debug(__name__, f'Auth user does not exist') + try: + user_dto.user_id = self._users.get_users_by_discord_id(user_dto.user_id).single().user_id + except Exception as e: + self._logger.error(__name__, f'User not found') + user_dto.user_id = None + + await self.add_auth_user_async(user_dto) + db_user = self._auth_users.get_auth_user_by_email(user_dto.email) + db_user.oauth_id = uuid.uuid4() + + self._auth_users.update_auth_user(db_user) + self._db.save_changes() + return OAuthDTO(AUT.to_dto(db_user), db_user.oauth_id) async def update_user_async(self, update_user_dto: UpdateAuthUserDTO): if update_user_dto is None: diff --git a/kdb-bot/src/bot_api/transformer/auth_user_transformer.py b/kdb-bot/src/bot_api/transformer/auth_user_transformer.py index 4256f39e8b..529344515e 100644 --- a/kdb-bot/src/bot_api/transformer/auth_user_transformer.py +++ b/kdb-bot/src/bot_api/transformer/auth_user_transformer.py @@ -19,6 +19,7 @@ class AuthUserTransformer(TransformerABC): None, None, None, + None, datetime.now(tz=timezone.utc), AuthRoleEnum.normal if dto.auth_role is None else AuthRoleEnum(dto.auth_role), dto.user_id, diff --git a/kdb-bot/src/bot_data/migration/api_migration.py b/kdb-bot/src/bot_data/migration/api_migration.py index 8466ec463b..d3241e6ab5 100644 --- a/kdb-bot/src/bot_data/migration/api_migration.py +++ b/kdb-bot/src/bot_data/migration/api_migration.py @@ -27,6 +27,7 @@ class ApiMigration(MigrationABC): `RefreshToken` VARCHAR(255), `ConfirmationId` VARCHAR(255) DEFAULT NULL, `ForgotPasswordId` VARCHAR(255) DEFAULT NULL, + `OAuthId` VARCHAR(255) DEFAULT NULL, `RefreshTokenExpiryTime` DATETIME(6) NOT NULL, `AuthRole` INT NOT NULL DEFAULT '0', `UserId` BIGINT DEFAULT NULL, diff --git a/kdb-bot/src/bot_data/model/auth_user.py b/kdb-bot/src/bot_data/model/auth_user.py index 58c9f982e5..c97c02d89f 100644 --- a/kdb-bot/src/bot_data/model/auth_user.py +++ b/kdb-bot/src/bot_data/model/auth_user.py @@ -19,6 +19,7 @@ class AuthUser(TableABC): refresh_token: Optional[str], confirmation_id: Optional[str], forgot_password_id: Optional[str], + oauth_id: Optional[str], refresh_token_expire_time: datetime, auth_role: AuthRoleEnum, user_id: Optional[int], @@ -34,6 +35,7 @@ class AuthUser(TableABC): self._password_salt = uuid.uuid4() if password_salt is None else password_salt self._refresh_token = refresh_token self._confirmation_id = confirmation_id + self._oauth_id = oauth_id self._forgot_password_id = forgot_password_id self._refresh_token_expire_time = refresh_token_expire_time @@ -95,23 +97,31 @@ class AuthUser(TableABC): @refresh_token.setter def refresh_token(self, value: Optional[str]): self._refresh_token = value - + @property def confirmation_id(self) -> Optional[str]: return self._confirmation_id - + @confirmation_id.setter def confirmation_id(self, value: Optional[str]): self._confirmation_id = value - + @property def forgot_password_id(self) -> Optional[str]: return self._forgot_password_id - + @forgot_password_id.setter def forgot_password_id(self, value: Optional[str]): self._forgot_password_id = value + @property + def oauth_id(self) -> Optional[str]: + return self._oauth_id + + @oauth_id.setter + def oauth_id(self, value: Optional[str]): + self._oauth_id = value + @property def refresh_token_expire_time(self) -> datetime: return self._refresh_token_expire_time @@ -183,6 +193,7 @@ class AuthUser(TableABC): `RefreshToken`, `ConfirmationId`, `ForgotPasswordId`, + `OAuthId`, `RefreshTokenExpiryTime`, `AuthRole`, `UserId`, @@ -198,6 +209,7 @@ class AuthUser(TableABC): '{"NULL" if self._refresh_token is None else self._refresh_token}', '{"NULL" if self._confirmation_id is None else self._confirmation_id}', '{"NULL" if self._forgot_password_id is None else self._forgot_password_id}', + '{"NULL" if self._oauth_id is None else self._oauth_id}', '{self._refresh_token_expire_time}', {self._auth_role_id.value}, {"NULL" if self._user_id is None else self._user_id}, @@ -218,6 +230,7 @@ class AuthUser(TableABC): `RefreshToken` = '{self._refresh_token}', `ConfirmationId` = '{"NULL" if self._confirmation_id is None else self._confirmation_id}', `ForgotPasswordId` = '{"NULL" if self._forgot_password_id is None else self._forgot_password_id}', + `OAuthId` = '{"NULL" if self._oauth_id is None else self._oauth_id}', `RefreshTokenExpiryTime` = '{self._refresh_token_expire_time}', `AuthRole` = {self._auth_role_id.value}, `UserId` = {"NULL" if self._user_id is None else self._user_id}, diff --git a/kdb-bot/src/bot_data/service/auth_user_repository_service.py b/kdb-bot/src/bot_data/service/auth_user_repository_service.py index ce8e3e4bd7..94d86bcfe8 100644 --- a/kdb-bot/src/bot_data/service/auth_user_repository_service.py +++ b/kdb-bot/src/bot_data/service/auth_user_repository_service.py @@ -37,8 +37,9 @@ class AuthUserRepositoryService(AuthUserRepositoryABC): self._get_value_from_result(result[7]), self._get_value_from_result(result[8]), self._get_value_from_result(result[9]), - AuthRoleEnum(self._get_value_from_result(result[10])), - self._get_value_from_result(result[11]), + self._get_value_from_result(result[10]), + AuthRoleEnum(self._get_value_from_result(result[11])), + self._get_value_from_result(result[12]), id=self._get_value_from_result(result[0]) ) diff --git a/kdb-web/src/app/models/auth/discord-auth-url.dto.ts b/kdb-web/src/app/models/auth/discord-auth-url.dto.ts new file mode 100644 index 0000000000..609e60cd71 --- /dev/null +++ b/kdb-web/src/app/models/auth/discord-auth-url.dto.ts @@ -0,0 +1,3 @@ +export interface DiscordAuthURL { + loginUrl: string; +} diff --git a/kdb-web/src/app/models/auth/oauth.dto.ts b/kdb-web/src/app/models/auth/oauth.dto.ts new file mode 100644 index 0000000000..a76ca52555 --- /dev/null +++ b/kdb-web/src/app/models/auth/oauth.dto.ts @@ -0,0 +1,6 @@ +import { AuthUserDTO } from "./auth-user.dto"; + +export interface OAuthDTO { + user: AuthUserDTO; + oAuthId: string; +} diff --git a/kdb-web/src/app/modules/auth/components/registration/registration.component.html b/kdb-web/src/app/modules/auth/components/registration/registration.component.html index ba7eac00c4..3433a400fd 100644 --- a/kdb-web/src/app/modules/auth/components/registration/registration.component.html +++ b/kdb-web/src/app/modules/auth/components/registration/registration.component.html @@ -1,85 +1,91 @@
-
-
\ No newline at end of file +
+ +
+
+ {{'auth.register.last_name_required' | translate}}
+
{{'auth.register.last_name_invalid' | translate}}
+
+
+ +
+ +
+
+ {{'auth.register.e_mail_required' | translate}}
+
{{'auth.register.user_already_exists' | translate}}
+
+
+ +
+ +
+
{{'auth.register.e_mails_not_match' | translate}}
+
+
+ +
+ +
+
+ {{'auth.register.password_required' | translate}}
+
+
+ +
+ + +
+
{{'auth.register.passwords_not_match' | translate}}
+
+
+ +
+ +
+ +
+
+ +
+
+ +
+
+ +
+
+ + + + + + diff --git a/kdb-web/src/app/modules/auth/components/registration/registration.component.ts b/kdb-web/src/app/modules/auth/components/registration/registration.component.ts index 3cd29c1444..09becb4454 100644 --- a/kdb-web/src/app/modules/auth/components/registration/registration.component.ts +++ b/kdb-web/src/app/modules/auth/components/registration/registration.component.ts @@ -1,19 +1,21 @@ -import { Component, OnInit } from '@angular/core'; -import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms'; -import { ActivatedRoute, Router } from '@angular/router'; -import { catchError } from 'rxjs/operators'; -import { AuthErrorMessages } from 'src/app/models/auth/auth-error-messages.enum'; -import { AuthUserDTO } from 'src/app/models/auth/auth-user.dto'; -import { AuthUserAtrErrors } from 'src/app/models/auth/auth-user-atr-errors'; -import { ErrorDTO } from 'src/app/models/error/error-dto'; -import { ServiceErrorCode } from 'src/app/models/error/service-error-code.enum'; -import { AuthService } from 'src/app/services/auth/auth.service'; -import { SpinnerService } from 'src/app/services/spinner/spinner.service'; +import { Component, OnInit } from "@angular/core"; +import { FormBuilder, FormControl, FormGroup, Validators } from "@angular/forms"; +import { ActivatedRoute, Router } from "@angular/router"; +import { catchError, finalize } from "rxjs/operators"; +import { AuthErrorMessages } from "src/app/models/auth/auth-error-messages.enum"; +import { AuthUserDTO } from "src/app/models/auth/auth-user.dto"; +import { AuthUserAtrErrors } from "src/app/models/auth/auth-user-atr-errors"; +import { ErrorDTO } from "src/app/models/error/error-dto"; +import { ServiceErrorCode } from "src/app/models/error/service-error-code.enum"; +import { AuthService } from "src/app/services/auth/auth.service"; +import { SpinnerService } from "src/app/services/spinner/spinner.service"; +import { throwError } from "rxjs"; +import { OAuthDTO } from "../../../../models/auth/oauth.dto"; @Component({ - selector: 'app-registration', - templateUrl: './registration.component.html', - styleUrls: ['./registration.component.scss'] + selector: "app-registration", + templateUrl: "./registration.component.html", + styleUrls: ["./registration.component.scss"] }) export class RegistrationComponent implements OnInit { @@ -35,6 +37,11 @@ export class RegistrationComponent implements OnInit { showEMailConfirmation = false; showEMailConfirmationError = false; + code!: string; + state!: string; + user!: AuthUserDTO; + oAuthId!: string; + constructor( private authService: AuthService, private formBuilder: FormBuilder, @@ -45,16 +52,64 @@ export class RegistrationComponent implements OnInit { this.spinnerService.showSpinner(); this.authService.isUserLoggedInAsync().then(res => { if (res) { - this.router.navigate(['/dashboard']); + this.router.navigate(["/dashboard"]); } this.spinnerService.hideSpinner(); }); } ngOnInit(): void { + this.initData(); + this.spinnerService.showSpinner(); + this.route.queryParams.pipe(catchError(err => { + this.spinnerService.hideSpinner(); + this.router.navigate(["auth", "login"]).then(() => { + }); + return throwError(() => err); + })).subscribe(params => { + if (!params["code"] || !params["state"]) { + this.spinnerService.hideSpinner(); + return; + } + + if (this.user) { + this.spinnerService.hideSpinner(); + return; + } + + this.code = params["code"]; + this.state = params["state"]; + this.authService.discordCreateUser(this.code, this.state).pipe(catchError(err => { + this.spinnerService.hideSpinner(); + this.router.navigate(["auth", "login"]).then(() => { + }); + return throwError(() => err); + })).subscribe(oAuthDTO => { + if (oAuthDTO) { + this.user = oAuthDTO.user; + this.oAuthId = oAuthDTO.oAuthId; + } + if (this.user && !this.oAuthId) { + this.router.navigate(["auth", "login"]).then(() => { + }); + return; + } + + this.router.navigate(["auth", "register"]).then(() => { + }); + this.initData(); + } + ); + this.initData(); + } + ); + } + + initData() { this.initLoginForm(); this.resetStateFlags(); this.confirmEMail(); + this.spinnerService.hideSpinner(); } resetStateFlags(): void { @@ -66,18 +121,23 @@ export class RegistrationComponent implements OnInit { } login(): void { - this.router.navigate(['/auth/login']); + this.router.navigate(["/auth/login"]); } initLoginForm(): void { this.loginForm = this.formBuilder.group({ - firstName: ['', Validators.required], - lastName: ['', Validators.required], - email: ['', [Validators.required, Validators.email]], - emailRepeat: ['', [Validators.required, Validators.email]], - password: ['', [Validators.required, Validators.minLength(8)]], - passwordRepeat: ['', [Validators.required, Validators.minLength(8)]] + firstName: [this.user ? this.user.firstName : "", Validators.required], + lastName: [this.user ? this.user.lastName : "", Validators.required], + email: [this.user ? this.user.email : "", [Validators.required, Validators.email]], + emailRepeat: [this.user ? this.user.email : "", [Validators.required, Validators.email]], + password: [this.user ? this.user.password : "", [Validators.required, Validators.minLength(8)]], + passwordRepeat: [this.user ? this.user.password : "", [Validators.required, Validators.minLength(8)]] }); + + if (this.user) { + this.loginForm.controls.email.disable(); + this.loginForm.controls.emailRepeat.disable(); + } } register(): void { @@ -100,13 +160,49 @@ export class RegistrationComponent implements OnInit { } this.spinnerService.showSpinner(); + if (this.user && this.oAuthId) { + this.registerWithOAuth(); + return; + } + + this.registerLocal(); + } + + private registerWithOAuth() { + const oAuthDTO: OAuthDTO = { + user: { + firstName: this.loginForm.value.firstName ?? this.user.firstName, + lastName: this.loginForm.value.lastName ?? this.user.lastName, + email: this.loginForm.value.email ?? this.user.email, + password: this.loginForm.value.password ?? null + }, + oAuthId: this.oAuthId + }; + this.authService.discordRegister(oAuthDTO) + .pipe(catchError(error => { + if (error.error !== null) { + const err: ErrorDTO = error.error; + + if (err.errorCode === ServiceErrorCode.InvalidUser && err.message === AuthErrorMessages.UserAlreadyExists) { + this.authUserAtrErrors.email.wrongData = true; + } + } + this.spinnerService.hideSpinner(); + throw error; + })) + .subscribe(resp => { + this.spinnerService.hideSpinner(); + this.router.navigate(["/auth/login"]); + }); + } + + private registerLocal() { const user: AuthUserDTO = { firstName: this.loginForm.value.firstName ?? null, lastName: this.loginForm.value.lastName ?? null, email: this.loginForm.value.email ?? null, password: this.loginForm.value.password ?? null }; - this.authService.register(user) .pipe(catchError(error => { if (error.error !== null) { @@ -121,24 +217,30 @@ export class RegistrationComponent implements OnInit { })) .subscribe(resp => { this.spinnerService.hideSpinner(); - this.router.navigate(['/auth/login']); + this.router.navigate(["/auth/login"]); }); } confirmEMail(): void { - const id = this.route.snapshot.params['id']; + const id = this.route.snapshot.params["id"]; if (id) { this.spinnerService.showSpinner(); this.authService.confirmEMail(id) .pipe(catchError(error => { - this.router.navigate(['/auth/login']); + this.router.navigate(["/auth/login"]); this.spinnerService.hideSpinner(); throw error; })) .subscribe(resp => { this.spinnerService.hideSpinner(); - this.router.navigate(['/auth/login']); + this.router.navigate(["/auth/login"]); }); } } + + discordLogin() { + this.authService.getDiscordAuthURL().subscribe(url => { + window.location.href = url.loginUrl; + }); + } } diff --git a/kdb-web/src/app/services/auth/auth.service.ts b/kdb-web/src/app/services/auth/auth.service.ts index b882a7ee78..a19195db55 100644 --- a/kdb-web/src/app/services/auth/auth.service.ts +++ b/kdb-web/src/app/services/auth/auth.service.ts @@ -1,20 +1,22 @@ -import { HttpClient, HttpHeaders } from '@angular/common/http'; -import { Injectable } from '@angular/core'; -import { Router } from '@angular/router'; -import { JwtHelperService } from '@auth0/angular-jwt'; -import { firstValueFrom, Observable, Subject, Subscription } from 'rxjs'; -import { catchError } from 'rxjs/operators'; -import { AdminUpdateUserDTO } from 'src/app/models/auth/admin-update-user.dto'; -import { AuthRoles } from 'src/app/models/auth/auth-roles.enum'; -import { AuthUserDTO } from 'src/app/models/auth/auth-user.dto'; -import { EMailStringDTO } from 'src/app/models/auth/email-string.dto'; -import { ResetPasswordDTO } from 'src/app/models/auth/reset-password.dto'; -import { TokenDTO } from 'src/app/models/auth/token.dto'; -import { UpdateUserDTO } from 'src/app/models/auth/update-user.dto'; -import { AuthUserSelectCriterion } from 'src/app/models/selection/auth-user/auth-user-select-criterion.dto'; -import { GetFilteredAuthUsersResultDTO } from 'src/app/models/selection/auth-user/get-filtered-auth-users-result.dto'; -import { SettingsService } from '../settings/settings.service'; -import { SpinnerService } from '../spinner/spinner.service'; +import { HttpClient, HttpHeaders } from "@angular/common/http"; +import { Injectable } from "@angular/core"; +import { Router } from "@angular/router"; +import { JwtHelperService } from "@auth0/angular-jwt"; +import { firstValueFrom, Observable, Subject, Subscription } from "rxjs"; +import { catchError } from "rxjs/operators"; +import { AdminUpdateUserDTO } from "src/app/models/auth/admin-update-user.dto"; +import { AuthRoles } from "src/app/models/auth/auth-roles.enum"; +import { AuthUserDTO } from "src/app/models/auth/auth-user.dto"; +import { EMailStringDTO } from "src/app/models/auth/email-string.dto"; +import { ResetPasswordDTO } from "src/app/models/auth/reset-password.dto"; +import { TokenDTO } from "src/app/models/auth/token.dto"; +import { UpdateUserDTO } from "src/app/models/auth/update-user.dto"; +import { AuthUserSelectCriterion } from "src/app/models/selection/auth-user/auth-user-select-criterion.dto"; +import { GetFilteredAuthUsersResultDTO } from "src/app/models/selection/auth-user/get-filtered-auth-users-result.dto"; +import { SettingsService } from "../settings/settings.service"; +import { SpinnerService } from "../spinner/spinner.service"; +import { DiscordAuthURL } from "../../models/auth/discord-auth-url.dto"; +import { OAuthDTO } from "../../models/auth/oauth.dto"; @Injectable({ providedIn: 'root' @@ -157,6 +159,39 @@ export class AuthService { }); } + /* discord auth */ + getDiscordAuthURL() { + return this.http.get(`${this.appsettings.getApiURL()}/api/auth/discord/get-url`, { + headers: new HttpHeaders({ + 'Content-Type': 'application/json' + }) + }); + } + discordCreateUser(code: string, state: string) { + return this.http.get(`${this.appsettings.getApiURL()}/api/auth/discord/create-user?code=${code}&state=${state}`, { + headers: new HttpHeaders({ + 'Content-Type': 'application/json' + }) + }); + } + + // /api/auth/discord/register?code= + discordRegister(oAuthDTO: OAuthDTO) { + return this.http.post(`${this.appsettings.getApiURL()}/api/auth/discord/register`, oAuthDTO, { + headers: new HttpHeaders({ + 'Content-Type': 'application/json' + }) + }); + } + + // discordGetUser(code: string, state: string) { + // return this.http.get(`${this.appsettings.getApiURL()}/api/auth/discord/get-user?code=${code}&state=${state}`, { + // headers: new HttpHeaders({ + // 'Content-Type': 'application/json' + // }) + // }); + // } + /* utils */ saveToken(token: TokenDTO): void { localStorage.setItem('jwt', token.token); diff --git a/kdb-web/src/assets/i18n/de.json b/kdb-web/src/assets/i18n/de.json index 9f305603c9..4593ae8f49 100644 --- a/kdb-web/src/assets/i18n/de.json +++ b/kdb-web/src/assets/i18n/de.json @@ -1,286 +1,303 @@ { - "header": { - "change_password": "Passwort ändern", - "settings": "Einstellungen", - "logout": "Ausloggen", - "header": "Krümmelmonster WI" - }, - "sidebar": { - "dashboard": "Dashboard", - "server": "Server", - "server_empty": "Kein Server ausgewählt", - "settings": "Einstellungen", - "members": "Mitglieder", - "administration": "Administration", - "config": "Konfiguration", - "auth_user_list": "Benutzer" - }, - "admin": { - "settings": { - "header": "Konfiguration", - "website": { - "header": "Webseite", - "frontend_version": "Webseite Version", - "backend_version": "Server Version", - "config_path": "Konfigurations-Dateipfad", - "frontend_base_url": "Webseite Basis-URL", - "backend_base_url": "Server Basis-URL", - "token_expire_time": "Token Ablaufzeit", - "refresh_token_expire_time": "Refresh Token Ablaufzeit" - }, - "e_mail": { - "header": "E-Mail", - "user": "Benutzer", - "host": "Host", - "port": "Port", - "transceiver": "Absender", - "e_mail_address": "E-Mail Adresse", - "e_mail": "E-Mail", - "send_e_mail": "E-Mail senden" - }, - "message": { - "error": "Fehler", - "could_not_send_mail": "E-Mail konte nicht gesendet werden!", - "connection_failed": "Verbindung fehlgeschlagen", - "connection_to_mail_failed": "Die Verbindung zum Mailserver konnte nicht hergestellt werden!", - "mail_login_failed": "Die Anmeldung am Mailserver ist fehlgeschlagen!", - "send_failed": "Senden fehlgeschlagen", - "test_mail_not_send": "Die Test E-Mail konnte nicht gesendet werden!", - "success": "Erfolg", - "send_mail": "E-Mail wurde erfolgreich gesendet" - } - }, - "auth_users": { - "header": "Benutzer", - "of": "von", - "add": "Hinzufügen", - "reset_filters": "Filter zurücksetzen", - "users": "Benutzer", - "headers": { - "users": "Benutzer", - "first_name": "Vorname", - "last_name": "Nachname", - "e_mail": "E-Mail", - "active": "Aktiv", - "role": "Rolle", - "password": "Passwort", - "actions": "Aktionen" - }, - "no_entries_found": "Keine Einträge gefunden", - "message": { - "invalid_email": "Ungültige E-Mail", - "invalid_email_d": "Die E-Mail {{email}} ist nicht gültig!", - "user_already_exists": "Benutzer existiert bereits", - "user_already_exists_d": "Der Benutzer {{email}} existiert bereits!", - "user_added": "Benutzer hinzugefügt", - "user_added_d": "Benutzer {{email}} erfolgreich hinzugefügt", - "user_change_failed": "Benutzer änderung fehlgeschlagen", - "user_change_failed_d": "Benutzer {{email}} konnte nicht geändert werden!", - "user_changed": "Benutzer geändert", - "user_changed_d": "Benutzer {{email}} erfolgreich geändert", - "cannot_delete_user": "Benutzer kann nicht gelöscht werden", - "logon_with_another_user": "Loggen Sie sich mit einem anderen Benutzer ein, um diesen Benutzer zu löschen!", - "user_delete": "Benutzer löschen", - "user_delete_q": "Sind Sie sich sicher, dass Sie {{email}} löschen möchten?", - "user_deleted": "Benutzer gelöscht", - "user_deleted_d": "Benutzer {{email}} erfolgreich gelöscht" - } - } - }, - "auth": { - "header": "Krümmelmonster WI", - "login": { - "e_mail": "E-Mail", - "password": "Passwort", - "login": "Einloggen", - "register": "Registrieren", - "forgot_password": "Passwort vergessen?", - "e_mail_required": "E-Mail benötigt", - "user_not_found": "Benutzer nicht gefunden", - "e_mail_not_confirmed": "E-Mail nicht bestätigt", - "password_required": "Passwort benötigt", - "wrong_password": "Falsches passwort" - }, - "register": { - "first_name": "Vorname", - "last_name": "Nachname", - "e_mail": "E-Mail", - "repeat_e_mail": "E-mail wiederholen", - "password": "Password", - "repeat_password": "password wiederholen", - "register": "Registrieren", - "login": "Einloggen", - "user_already_exists": "Benutzer existiert bereits", - "passwords_not_match": "Passwörter sitmmen nicht überein", - "e_mails_not_match": "E-Mails sitmmen nicht überein", - "first_name_required": "Vorname benötigt", - "last_name_required": "Nachname benötigt", - "e_mail_required": "E-Mail benötigt", - "password_required": "Passwort benötigt", - "first_name_invalid": "Vorname ungültig", - "last_name_invalid": "Nachname ungültig" - }, - "forgot_password": { - "e_mail": "E-Mail", - "send_confirmation_url": "Falls ein Benutzer mit der E-Mail gefunden wurde, wurde Betstätigungslink versendet", - "reset_password": "Passwort zurücksetzen", - "login": "Anmelden", - "register": "Registrieren", - "repeat_password": "Passwort wiederholen", - "passwords_do_not_match": "Die Passwörter stimmen nicht überein", - "password": "Passwort", - "message": { - "reset_password": "Passwort zurückgesetzt", - "reset_password_d": "Dein Passwort wurde zurückgesetzt" - } - } - }, - "view": { - "dashboard": { - "header": "Dashboard", - "of": "von", - "servers": "Server", - "server": { - "header": "Server", - "member_count": "Mitglid(er)" - }, - "filter": { - "name": "Name" - } - }, - "server": { - "header": "Server" - }, - "user-list": {}, - "change-password": { - "header": "Passwort ändern", - "wrong_password": "Falsches Passwort", - "passwords_do_not_match": "Die Passwörter stimmen nicht überein", - "password": "Passwort", - "active_password": "Aktuelles Passwort", - "new_password": "Neues Passwort", - "repeat_new_password": "Neues Passwort wiederholen", - "save": "Speichern", - "message": { - "error": "Fehler", - "password_cannot_be_changed": "Dein Passwort konnte nicht geändert werden!", - "change_password": "Passwort geändert", - "changed_password": "Dein Passwort wurde geändert" - } - }, - "user_settings": { - "header": "Einstellungen", - "first_name": "Vorname", - "last_name": "Nachname", - "e_mail": "E-Mail", - "password": "Passwort", - "e_mail_already_exists": "Die E-Mail wurde bereits vergeben", - "wrong_password": "Falsches Passwort", - "save": "Speichern", - "message": { - "user_not_found": "Benutzer nicht gefunden", - "user_not_found_d": "Der Benutzer konnte nicht gefunden werden!", - "error": "Fehler", - "could_not_change_settings": "Die Einstellungen konnten nicht geändert werden!", - "changed_settings": "Die Einstellungen wurden geändert", - "success": "Erfolg" - } - } - }, - "footer": { - "imprint": "Impressum", - "backend": "Webseite", - "frontend": "API" - }, - "dialog": { - "confirm": "Bestätigen", - "abort": "Abbrechen" - }, - "general": { - "days": "Tage", - "minutes": "Minuten" - }, - "common": { - "bool_as_string": { - "true": "Ja", - "false": "Nein" - }, + "header": { + "change_password": "Passwort ändern", + "settings": "Einstellungen", + "logout": "Ausloggen", + "header": "Krümmelmonster WI" + }, + "sidebar": { + "dashboard": "Dashboard", + "server": "Server", + "server_empty": "Kein Server ausgewählt", + "settings": "Einstellungen", + "members": "Mitglieder", + "administration": "Administration", + "config": "Konfiguration", + "auth_user_list": "Benutzer" + }, + "admin": { + "settings": { + "header": "Konfiguration", + "website": { + "header": "Webseite", + "frontend_version": "Webseite Version", + "backend_version": "Server Version", + "config_path": "Konfigurations-Dateipfad", + "frontend_base_url": "Webseite Basis-URL", + "backend_base_url": "Server Basis-URL", + "token_expire_time": "Token Ablaufzeit", + "refresh_token_expire_time": "Refresh Token Ablaufzeit" + }, + "e_mail": { + "header": "E-Mail", + "user": "Benutzer", + "host": "Host", + "port": "Port", + "transceiver": "Absender", + "e_mail_address": "E-Mail Adresse", + "e_mail": "E-Mail", + "send_e_mail": "E-Mail senden" + }, + "message": { "error": "Fehler", - "404": "404 - Der Eintrag konnte nicht gefunden werden" + "could_not_send_mail": "E-Mail konte nicht gesendet werden!", + "connection_failed": "Verbindung fehlgeschlagen", + "connection_to_mail_failed": "Die Verbindung zum Mailserver konnte nicht hergestellt werden!", + "mail_login_failed": "Die Anmeldung am Mailserver ist fehlgeschlagen!", + "send_failed": "Senden fehlgeschlagen", + "test_mail_not_send": "Die Test E-Mail konnte nicht gesendet werden!", + "success": "Erfolg", + "send_mail": "E-Mail wurde erfolgreich gesendet" + } }, - "primeng": { - "startsWith": "Startet mit", - "contains": "Enthält", - "notContains": "Enthält nicht", - "endsWith": "Ended mit", - "equals": "Gleich", - "notEquals": "Nicht gleich", - "noFilter": "Kein Filter", - "lt": "Weniger als", - "lte": "Weniger als oder gleich", - "gt": "Größer als", - "gte": "Größer als doer gleich", - "is": "Ist", - "isNot": "Ist nicht", - "before": "Vorher", - "after": "Nachher", - "clear": "Zurücksetzen", - "apply": "Anwenden", - "matchAll": "Passend zu allem", - "matchAny": "Passend zu jedem", - "addRule": "Regel hinzufügen", - "removeRule": "Regel entfernen", - "accept": "Ja", - "reject": "Nein", - "choose": "Wählen", - "upload": "Hochladen", - "cancel": "Abbrechen", - "dayNames": [ - "Sonntag", - "Montag", - "Dienstag", - "Mittwoch", - "Donnerstag", - "Freitag", - "Samstag" - ], - "dayNamesShort": ["Son", "Mon", "Die", "Mit", "Don", "Fre", "Sam"], - "dayNamesMin": ["So", "Mo", "Di", "Mi", "Do", "Fr", "Sa"], - "monthNames": [ - "Januar", - "Februar", - "März", - "April", - "Mai", - "Juni", - "Juli", - "August", - "September", - "Oktober", - "November", - "Dezember" - ], - "monthNamesShort": [ - "Jan", - "Feb", - "Mär", - "Apr", - "Mai", - "Jun", - "Jul", - "Aug", - "Sep", - "Okt", - "Nov", - "Dez" - ], - "today": "Heute", - "weekHeader": "Wk", - "weak": "Woche", - "medium": "Mittel", - "strong": "Stark", - "passwordPrompt": "Passwort eingeben", - "emptyMessage": "Keine Ergebnisse gefunden", - "emptyFilterMessage": "Keine Ergebnisse gefunden" + "auth_users": { + "header": "Benutzer", + "of": "von", + "add": "Hinzufügen", + "reset_filters": "Filter zurücksetzen", + "users": "Benutzer", + "headers": { + "users": "Benutzer", + "first_name": "Vorname", + "last_name": "Nachname", + "e_mail": "E-Mail", + "active": "Aktiv", + "role": "Rolle", + "password": "Passwort", + "actions": "Aktionen" + }, + "no_entries_found": "Keine Einträge gefunden", + "message": { + "invalid_email": "Ungültige E-Mail", + "invalid_email_d": "Die E-Mail {{email}} ist nicht gültig!", + "user_already_exists": "Benutzer existiert bereits", + "user_already_exists_d": "Der Benutzer {{email}} existiert bereits!", + "user_added": "Benutzer hinzugefügt", + "user_added_d": "Benutzer {{email}} erfolgreich hinzugefügt", + "user_change_failed": "Benutzer änderung fehlgeschlagen", + "user_change_failed_d": "Benutzer {{email}} konnte nicht geändert werden!", + "user_changed": "Benutzer geändert", + "user_changed_d": "Benutzer {{email}} erfolgreich geändert", + "cannot_delete_user": "Benutzer kann nicht gelöscht werden", + "logon_with_another_user": "Loggen Sie sich mit einem anderen Benutzer ein, um diesen Benutzer zu löschen!", + "user_delete": "Benutzer löschen", + "user_delete_q": "Sind Sie sich sicher, dass Sie {{email}} löschen möchten?", + "user_deleted": "Benutzer gelöscht", + "user_deleted_d": "Benutzer {{email}} erfolgreich gelöscht" + } } + }, + "auth": { + "header": "Krümmelmonster WI", + "login": { + "e_mail": "E-Mail", + "password": "Passwort", + "login": "Einloggen", + "register": "Registrieren", + "forgot_password": "Passwort vergessen?", + "e_mail_required": "E-Mail benötigt", + "user_not_found": "Benutzer nicht gefunden", + "e_mail_not_confirmed": "E-Mail nicht bestätigt", + "password_required": "Passwort benötigt", + "wrong_password": "Falsches passwort" + }, + "register": { + "first_name": "Vorname", + "last_name": "Nachname", + "e_mail": "E-Mail", + "repeat_e_mail": "E-mail wiederholen", + "password": "Password", + "repeat_password": "Password wiederholen", + "register": "Registrieren", + "register_with_discord": "Mit Discord Registrieren", + "login": "Einloggen", + "user_already_exists": "Benutzer existiert bereits", + "passwords_not_match": "Passwörter sitmmen nicht überein", + "e_mails_not_match": "E-Mails sitmmen nicht überein", + "first_name_required": "Vorname benötigt", + "last_name_required": "Nachname benötigt", + "e_mail_required": "E-Mail benötigt", + "password_required": "Passwort benötigt", + "first_name_invalid": "Vorname ungültig", + "last_name_invalid": "Nachname ungültig" + }, + "forgot_password": { + "e_mail": "E-Mail", + "send_confirmation_url": "Falls ein Benutzer mit der E-Mail gefunden wurde, wurde Betstätigungslink versendet", + "reset_password": "Passwort zurücksetzen", + "login": "Anmelden", + "register": "Registrieren", + "repeat_password": "Passwort wiederholen", + "passwords_do_not_match": "Die Passwörter stimmen nicht überein", + "password": "Passwort", + "message": { + "reset_password": "Passwort zurückgesetzt", + "reset_password_d": "Dein Passwort wurde zurückgesetzt" + } + } + }, + "view": { + "dashboard": { + "header": "Dashboard", + "of": "von", + "servers": "Server", + "server": { + "header": "Server", + "member_count": "Mitglid(er)" + }, + "filter": { + "name": "Name" + } + }, + "server": { + "header": "Server" + }, + "user-list": {}, + "change-password": { + "header": "Passwort ändern", + "wrong_password": "Falsches Passwort", + "passwords_do_not_match": "Die Passwörter stimmen nicht überein", + "password": "Passwort", + "active_password": "Aktuelles Passwort", + "new_password": "Neues Passwort", + "repeat_new_password": "Neues Passwort wiederholen", + "save": "Speichern", + "message": { + "error": "Fehler", + "password_cannot_be_changed": "Dein Passwort konnte nicht geändert werden!", + "change_password": "Passwort geändert", + "changed_password": "Dein Passwort wurde geändert" + } + }, + "user_settings": { + "header": "Einstellungen", + "first_name": "Vorname", + "last_name": "Nachname", + "e_mail": "E-Mail", + "password": "Passwort", + "e_mail_already_exists": "Die E-Mail wurde bereits vergeben", + "wrong_password": "Falsches Passwort", + "save": "Speichern", + "message": { + "user_not_found": "Benutzer nicht gefunden", + "user_not_found_d": "Der Benutzer konnte nicht gefunden werden!", + "error": "Fehler", + "could_not_change_settings": "Die Einstellungen konnten nicht geändert werden!", + "changed_settings": "Die Einstellungen wurden geändert", + "success": "Erfolg" + } + } + }, + "footer": { + "imprint": "Impressum", + "backend": "Webseite", + "frontend": "API" + }, + "dialog": { + "confirm": "Bestätigen", + "abort": "Abbrechen" + }, + "general": { + "days": "Tage", + "minutes": "Minuten" + }, + "common": { + "bool_as_string": { + "true": "Ja", + "false": "Nein" + }, + "error": "Fehler", + "404": "404 - Der Eintrag konnte nicht gefunden werden" + }, + "primeng": { + "startsWith": "Startet mit", + "contains": "Enthält", + "notContains": "Enthält nicht", + "endsWith": "Ended mit", + "equals": "Gleich", + "notEquals": "Nicht gleich", + "noFilter": "Kein Filter", + "lt": "Weniger als", + "lte": "Weniger als oder gleich", + "gt": "Größer als", + "gte": "Größer als doer gleich", + "is": "Ist", + "isNot": "Ist nicht", + "before": "Vorher", + "after": "Nachher", + "clear": "Zurücksetzen", + "apply": "Anwenden", + "matchAll": "Passend zu allem", + "matchAny": "Passend zu jedem", + "addRule": "Regel hinzufügen", + "removeRule": "Regel entfernen", + "accept": "Ja", + "reject": "Nein", + "choose": "Wählen", + "upload": "Hochladen", + "cancel": "Abbrechen", + "dayNames": [ + "Sonntag", + "Montag", + "Dienstag", + "Mittwoch", + "Donnerstag", + "Freitag", + "Samstag" + ], + "dayNamesShort": [ + "Son", + "Mon", + "Die", + "Mit", + "Don", + "Fre", + "Sam" + ], + "dayNamesMin": [ + "So", + "Mo", + "Di", + "Mi", + "Do", + "Fr", + "Sa" + ], + "monthNames": [ + "Januar", + "Februar", + "März", + "April", + "Mai", + "Juni", + "Juli", + "August", + "September", + "Oktober", + "November", + "Dezember" + ], + "monthNamesShort": [ + "Jan", + "Feb", + "Mär", + "Apr", + "Mai", + "Jun", + "Jul", + "Aug", + "Sep", + "Okt", + "Nov", + "Dez" + ], + "today": "Heute", + "weekHeader": "Wk", + "weak": "Woche", + "medium": "Mittel", + "strong": "Stark", + "passwordPrompt": "Passwort eingeben", + "emptyMessage": "Keine Ergebnisse gefunden", + "emptyFilterMessage": "Keine Ergebnisse gefunden" + } } diff --git a/kdb-web/src/styles.scss b/kdb-web/src/styles.scss index 9f56ea05fb..8407ecb44b 100644 --- a/kdb-web/src/styles.scss +++ b/kdb-web/src/styles.scss @@ -421,7 +421,7 @@ footer { } .register-form-wrapper { - height: 550px; + height: 600px; } .register-confirm-form-wrapper {