Resend confirmation mail
Some checks failed
Deploy staging on push / pre-build (push) Successful in 1s
Deploy staging on push / build-bot (push) Failing after 22s
Deploy staging on push / build-web (push) Failing after 21s
Deploy staging on push / deploy (push) Has been skipped

This commit is contained in:
Sven Heidemann 2024-01-26 15:32:01 +01:00
parent a24fefd3e2
commit 60d81ce18b
9 changed files with 77 additions and 43 deletions

View File

@ -2,22 +2,22 @@
"api": { "api": {
"api": { "api": {
"test_mail": { "test_mail": {
"message": "Dies ist eine Test-Mail vom Kruemelmonster Web Interface\r\nGesendet von {}-{}", "message": "Dies ist eine Test-Mail vom Krümelmonster Web Interface\r\nGesendet von {}-{}",
"subject": "Krümelmonster Web Interface Test-Mail" "subject": "Krümelmonster Web Interface Test-Mail"
} }
}, },
"auth": { "auth": {
"confirmation": { "confirmation": {
"message": "Öffne den Link, um die E-Mail zu bestätigen:\n{}auth/register/{}", "message": "Öffne den Link, um die E-Mail zu bestätigen:\r\n{}auth/register/{}",
"subject": "E-Mail für {} {} bestätigen" "subject": "E-Mail für {} {} bestätigen"
}, },
"forgot_password": { "forgot_password": {
"message": "Öffne den Link, um das Passwort zu ändern:\n{}auth/forgot-password/{}", "message": "Öffne den Link, um das Passwort zu ändern:\r\n{}auth/forgot-password/{}",
"subject": "Passwort für {} {} zurücksetzen" "subject": "Passwort für {} {} zurücksetzen"
} }
}, },
"mail": { "mail": {
"automatic_mail": "\n\nDies ist eine automatische E-Mail.\nGesendet von {}-{}@{}" "automatic_mail": "\r\n\r\nDies ist eine automatische E-Mail.\r\nGesendet von {}-{}@{}"
} }
}, },
"common": { "common": {

View File

@ -114,3 +114,7 @@ class AuthServiceABC(ABC):
@abstractmethod @abstractmethod
async def reset_password_async(self, rp_dto: ResetPasswordDTO): async def reset_password_async(self, rp_dto: ResetPasswordDTO):
pass pass
@abstractmethod
async def resend_confirmation_email_by_mail(self, mail: str):
pass

View File

@ -84,6 +84,11 @@ class AuthController:
self._auth_service.add_auth_user(dto) self._auth_service.add_auth_user(dto)
return "", 200 return "", 200
@Route.post(f"{BasePath}/resend-confirmation-email-by-mail/<mail>")
async def resend_confirmation_email_by_user_id(self, mail: str):
await self._auth_service.resend_confirmation_email_by_mail(mail)
return "", 200
@Route.post(f"{BasePath}/register-by-id/<id>") @Route.post(f"{BasePath}/register-by-id/<id>")
async def register_id(self, id: str): async def register_id(self, id: str):
result = await self._auth_service.confirm_email_async(id) result = await self._auth_service.confirm_email_async(id)

View File

@ -16,6 +16,7 @@ from bot_api.api import Api
from bot_api.configuration.discord_authentication_settings import ( from bot_api.configuration.discord_authentication_settings import (
DiscordAuthenticationSettings, DiscordAuthenticationSettings,
) )
from bot_api.exception.service_exception import ServiceException
from bot_api.logging.api_logger import ApiLogger from bot_api.logging.api_logger import ApiLogger
from bot_api.model.auth_user_dto import AuthUserDTO from bot_api.model.auth_user_dto import AuthUserDTO
from bot_api.route.route import Route from bot_api.route.route import Route
@ -90,5 +91,10 @@ class AuthDiscordController:
AuthRoleEnum.normal, AuthRoleEnum.normal,
) )
result = await self._auth_service.login_discord_async(dto, response["id"]) try:
return jsonify(result.to_dict()) result = await self._auth_service.login_discord_async(dto, response["id"])
return jsonify(result.to_dict())
except ServiceException as e:
r = jsonify({"email": dto.email})
r.status_code = 403
return r

View File

@ -1,6 +1,5 @@
import hashlib import hashlib
import re import re
import textwrap
import uuid import uuid
from datetime import datetime, timedelta, timezone from datetime import datetime, timedelta, timezone
from threading import Thread from threading import Thread
@ -172,11 +171,7 @@ class AuthService(AuthServiceABC):
mail.add_header("Content-Transfer-Encoding: quoted-printable") mail.add_header("Content-Transfer-Encoding: quoted-printable")
mail.add_receiver(str(email)) mail.add_receiver(str(email))
mail.subject = subject mail.subject = subject
mail.body = textwrap.dedent( mail.body = f"{message}\r\n{self._t.transform('api.mail.automatic_mail').format(self._environment.application_name, self._environment.environment_name, self._environment.host_name)}"
f"""{message}
{self._t.transform('api.mail.automatic_mail').format(self._environment.application_name, self._environment.environment_name, self._environment.host_name)}
"""
)
thr = Thread(target=self._mailer.send_mail, args=[mail]) thr = Thread(target=self._mailer.send_mail, args=[mail])
thr.start() thr.start()
@ -599,3 +594,12 @@ class AuthService(AuthServiceABC):
user.forgot_password_id = None user.forgot_password_id = None
self._auth_users.update_auth_user(user) self._auth_users.update_auth_user(user)
self._db.save_changes() self._db.save_changes()
async def resend_confirmation_email_by_mail(self, mail: str):
user = self._auth_users.find_auth_user_by_email(mail)
if user is None:
raise ServiceException(ServiceErrorCode.InvalidUser, f"User not found")
if user.confirmation_id is None:
raise ServiceException(ServiceErrorCode.DataAlreadyExists, f"User already confirmed")
self._send_confirmation_id_to_user(user)

View File

@ -69,6 +69,17 @@ export class LoginComponent implements OnInit {
this.spinnerService.hideSpinner(); this.spinnerService.hideSpinner();
} }
openConfirmationMailInfo(email: string) {
this.confirmDialog.confirmDialog(
this.translate.instant(
"auth.login.message.confirm_email"),
this.translate.instant(
"auth.login.message.confirm_email_d",
{ email: email }
)
);
}
checkDiscordLogin() { checkDiscordLogin() {
this.route.queryParams.pipe(catchError(err => { this.route.queryParams.pipe(catchError(err => {
this.spinnerService.hideSpinner(); this.spinnerService.hideSpinner();
@ -87,19 +98,31 @@ export class LoginComponent implements OnInit {
this.spinnerService.hideSpinner(); this.spinnerService.hideSpinner();
this.router.navigate(["auth", "login"]).then(() => { this.router.navigate(["auth", "login"]).then(() => {
}); });
this.state = ""; this.confirmDialog.confirmDialog(
this.code = ""; this.translate.instant(
"auth.login.message.confirm_email"),
this.translate.instant(
"auth.login.message.confirm_email_resend"
),
() => {
this.authService.resendConfirmationMailByMail(err.error.email).subscribe(mail => {
this.openConfirmationMailInfo(err.error.email);
}
);
this.state = "";
this.code = "";
},
() => {
this.state = "";
this.code = "";
}
);
return throwError(() => err); return throwError(() => err);
})).subscribe(token => { })).subscribe(token => {
if (token.firstLogin) { if (token.firstLogin) {
this.confirmDialog.confirmDialog( this.openConfirmationMailInfo(this.authService.getEMailFromDecodedToken(this.authService.getDecodedToken(token)) ?? "");
this.translate.instant(
"auth.login.message.confirm_email"),
this.translate.instant(
"auth.login.message.confirm_email_d",
{ email: this.authService.getEMailFromDecodedToken(this.authService.getDecodedToken(token)) }
)
);
} }
this.authService.saveToken(token); this.authService.saveToken(token);

View File

@ -2,7 +2,7 @@ import { HttpClient, HttpHeaders } from "@angular/common/http";
import { Injectable } from "@angular/core"; import { Injectable } from "@angular/core";
import { Router } from "@angular/router"; import { Router } from "@angular/router";
import { JwtHelperService } from "@auth0/angular-jwt"; import { JwtHelperService } from "@auth0/angular-jwt";
import { BehaviorSubject, firstValueFrom, Observable, Subject, Subscription, throwError } from "rxjs"; import { BehaviorSubject, firstValueFrom, Observable, Subscription, throwError } from "rxjs";
import { catchError } from "rxjs/operators"; import { catchError } from "rxjs/operators";
import { AdminUpdateUserDTO } from "src/app/models/auth/admin-update-user.dto"; import { AdminUpdateUserDTO } from "src/app/models/auth/admin-update-user.dto";
import { AuthRoles } from "src/app/models/auth/auth-roles.enum"; import { AuthRoles } from "src/app/models/auth/auth-roles.enum";
@ -16,7 +16,6 @@ import { GetFilteredAuthUsersResultDTO } from "src/app/models/selection/auth-use
import { SettingsService } from "../settings/settings.service"; import { SettingsService } from "../settings/settings.service";
import { SpinnerService } from "../spinner/spinner.service"; import { SpinnerService } from "../spinner/spinner.service";
import { DiscordAuthURL } from "../../models/auth/discord-auth-url.dto"; import { DiscordAuthURL } from "../../models/auth/discord-auth-url.dto";
import { OAuthDTO } from "../../models/auth/oauth.dto";
@Injectable({ @Injectable({
providedIn: "root" providedIn: "root"
@ -108,6 +107,14 @@ export class AuthService {
}); });
} }
resendConfirmationMailByMail(email: string): Observable<unknown> {
return this.http.post(`${this.appsettings.getApiURL()}/api/auth/resend-confirmation-email-by-mail/${email}`, {
headers: new HttpHeaders({
"Content-Type": "application/json"
})
});
}
getEMailFromforgotPasswordId(id: string): Observable<EMailStringDTO> { getEMailFromforgotPasswordId(id: string): Observable<EMailStringDTO> {
return this.http.post<EMailStringDTO>(`${this.appsettings.getApiURL()}/api/auth/confirm-forgot-password/${id}`, { return this.http.post<EMailStringDTO>(`${this.appsettings.getApiURL()}/api/auth/confirm-forgot-password/${id}`, {
headers: new HttpHeaders({ headers: new HttpHeaders({
@ -173,23 +180,6 @@ export class AuthService {
}); });
} }
// /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<AuthUserDTO>(`${this.appsettings.getApiURL()}/api/auth/discord/get-user?code=${code}&state=${state}`, {
// headers: new HttpHeaders({
// 'Content-Type': 'application/json'
// })
// });
// }
/* utils */ /* utils */
saveToken(token: TokenDTO): void { saveToken(token: TokenDTO): void {
localStorage.setItem("jwt", token.token); localStorage.setItem("jwt", token.token);

View File

@ -93,7 +93,8 @@
"login_with_discord": "Mit Discord einloggen", "login_with_discord": "Mit Discord einloggen",
"message": { "message": {
"confirm_email": "E-Mail Bestätigen", "confirm_email": "E-Mail Bestätigen",
"confirm_email_d": "Sie müssen die E-Mail {{email}} Bestätigen, in dem Sie den Link öffnen, welchen wir Ihnen zugesendet haben." "confirm_email_d": "Sie müssen die E-Mail {{email}} Bestätigen, in dem Sie den Link öffnen, welchen wir Ihnen zugesendet haben.",
"confirm_email_resend": "Sollen wir die Bestätigungsmail neu verschicken?"
}, },
"password": "Passwort", "password": "Passwort",
"password_required": "Passwort benötigt", "password_required": "Passwort benötigt",

View File

@ -93,7 +93,8 @@
"login_with_discord": "Login with discord", "login_with_discord": "Login with discord",
"message": { "message": {
"confirm_email": "Confirm E-Mail", "confirm_email": "Confirm E-Mail",
"confirm_email_d": "You have to confirm your email {{email}} Please confirm your account through the link in the email." "confirm_email_d": "You have to confirm your email {{email}} Please confirm your account through the link in the email.",
"confirm_email_resend": "Should we resend the confirmation email?"
}, },
"password": "Password", "password": "Password",
"password_required": "Passwort required", "password_required": "Passwort required",