From bb2e9e48340520b5ba22153c2a76b5900a4d4128 Mon Sep 17 00:00:00 2001
From: Jan-Marlon Leibl
Date: Wed, 14 May 2025 12:12:33 +0200
Subject: [PATCH 001/193] feat(auth): emit closeDialog on successful
registration
---
frontend/src/app/feature/auth/register/register.component.ts | 1 +
1 file changed, 1 insertion(+)
diff --git a/frontend/src/app/feature/auth/register/register.component.ts b/frontend/src/app/feature/auth/register/register.component.ts
index 7a3e381..60f289b 100644
--- a/frontend/src/app/feature/auth/register/register.component.ts
+++ b/frontend/src/app/feature/auth/register/register.component.ts
@@ -64,6 +64,7 @@ export class RegisterComponent {
})
.subscribe({
next: () => {
+ this.closeDialog.emit();
this.router.navigate(['/home']);
},
error: () => {
From 59aa8319818efa633211f210fbf1a6cc66768631 Mon Sep 17 00:00:00 2001
From: csimonis
Date: Thu, 15 May 2025 10:02:41 +0200
Subject: [PATCH 002/193] feat(auth): add email verification functionality
---
.../casino/security/service/AuthService.java | 2 +-
.../casino/security/service/EmailService.java | 18 +++
.../java/de/szut/casino/user/UserEntity.java | 7 +-
.../java/de/szut/casino/user/UserService.java | 4 +-
.../resources/templates/email/deposit.html | 2 +-
.../resources/templates/email/verify.html | 149 ++++++++++++++++++
.../resources/templates/email/welcome.html | 2 +-
7 files changed, 179 insertions(+), 5 deletions(-)
create mode 100644 backend/src/main/resources/templates/email/verify.html
diff --git a/backend/src/main/java/de/szut/casino/security/service/AuthService.java b/backend/src/main/java/de/szut/casino/security/service/AuthService.java
index ed2b70c..c4d324d 100644
--- a/backend/src/main/java/de/szut/casino/security/service/AuthService.java
+++ b/backend/src/main/java/de/szut/casino/security/service/AuthService.java
@@ -47,7 +47,7 @@ public class AuthService {
public GetUserDto register(CreateUserDto signUpRequest) throws MessagingException, IOException {
UserEntity user = userService.createUser(signUpRequest);
- this.emailService.sendRegistrationEmail(user);
+ this.emailService.sendEmailVerificationEmail(user);
return new GetUserDto(
user.getId(),
diff --git a/backend/src/main/java/de/szut/casino/security/service/EmailService.java b/backend/src/main/java/de/szut/casino/security/service/EmailService.java
index 861a0c2..348fafd 100644
--- a/backend/src/main/java/de/szut/casino/security/service/EmailService.java
+++ b/backend/src/main/java/de/szut/casino/security/service/EmailService.java
@@ -35,6 +35,24 @@ public class EmailService {
}
}
+ public void sendEmailVerificationEmail(UserEntity user) throws IOException, MessagingException {
+ String template = loadTemplate("email/verify.html");
+ String htmlContent = template
+ .replace("${username}", user.getUsername())
+ .replace("${feUrl}", feUrl)
+ .replace("${token}", user.getVerificationToken());
+
+ MimeMessage message = mailSender.createMimeMessage();
+ MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8");
+
+ helper.setFrom(mailConfig.fromAddress);
+ helper.setTo(user.getEmailAddress());
+ helper.setSubject("E-Mail Bestätigung");
+ helper.setText(htmlContent, true);
+
+ mailSender.send(message);
+ }
+
public void sendRegistrationEmail(UserEntity user) throws IOException, MessagingException {
String template = loadTemplate("email/welcome.html");
String htmlContent = template
diff --git a/backend/src/main/java/de/szut/casino/user/UserEntity.java b/backend/src/main/java/de/szut/casino/user/UserEntity.java
index 270d178..161ce52 100644
--- a/backend/src/main/java/de/szut/casino/user/UserEntity.java
+++ b/backend/src/main/java/de/szut/casino/user/UserEntity.java
@@ -30,11 +30,16 @@ public class UserEntity {
@Column(precision = 19, scale = 2)
private BigDecimal balance;
- public UserEntity(String email, String username, String password, BigDecimal balance) {
+ private Boolean emailVerified = false;
+
+ private String verificationToken;
+
+ public UserEntity(String email, String username, String password, BigDecimal balance, String verificationToken) {
this.email = email;
this.username = username;
this.password = password;
this.balance = balance;
+ this.verificationToken = verificationToken;
}
public void addBalance(BigDecimal amountToAdd) {
diff --git a/backend/src/main/java/de/szut/casino/user/UserService.java b/backend/src/main/java/de/szut/casino/user/UserService.java
index 25fabef..6855619 100644
--- a/backend/src/main/java/de/szut/casino/user/UserService.java
+++ b/backend/src/main/java/de/szut/casino/user/UserService.java
@@ -2,6 +2,7 @@ package de.szut.casino.user;
import de.szut.casino.user.dto.CreateUserDto;
import jakarta.persistence.EntityExistsException;
+import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.password.PasswordEncoder;
@@ -31,7 +32,8 @@ public class UserService {
createUserDto.getEmail(),
createUserDto.getUsername(),
passwordEncoder.encode(createUserDto.getPassword()),
- BigDecimal.valueOf(100) // Starting balance
+ BigDecimal.valueOf(100),
+ RandomStringUtils.randomAlphanumeric(64)
);
return userRepository.save(user);
diff --git a/backend/src/main/resources/templates/email/deposit.html b/backend/src/main/resources/templates/email/deposit.html
index 632d7ab..be54677 100644
--- a/backend/src/main/resources/templates/email/deposit.html
+++ b/backend/src/main/resources/templates/email/deposit.html
@@ -128,7 +128,7 @@
Ihr Trustworthy Casino Team
diff --git a/backend/src/main/resources/templates/email/verify.html b/backend/src/main/resources/templates/email/verify.html
new file mode 100644
index 0000000..008236d
--- /dev/null
+++ b/backend/src/main/resources/templates/email/verify.html
@@ -0,0 +1,149 @@
+
+
+
+
+
+ E-Mail-Verifizierung - Trustworthy Casino©
+
+
+
+
+
+
+
Hallo ${username} ,
+
+
vielen Dank für Ihre Registrierung bei Trustworthy Casino. Um Ihr Konto zu aktivieren und Zugang zu allen Funktionen zu erhalten, bestätigen Sie bitte Ihre E-Mail-Adresse.
+
+
+
+
Klicken Sie auf den folgenden Button, um Ihre E-Mail-Adresse zu bestätigen:
+
+
+
+
+
Hinweis: Der Bestätigungscode könnte nur 24 Stunden gültig sein und kann vielleicht auch nur einmal verwendet werden.
+
+
+
+
+
Nach der Bestätigung Ihrer E-Mail-Adresse können Sie sofort mit dem Spielen beginnen und alle Vorteile Ihres Kontos nutzen.
+
+
Bei Fragen stehen wir Ihnen jederzeit zur Verfügung.
+
+
Mit freundlichen Grüßen,
+ Ihr Trustworthy Casino Team
+
+
+
+
+
\ No newline at end of file
diff --git a/backend/src/main/resources/templates/email/welcome.html b/backend/src/main/resources/templates/email/welcome.html
index 2a10134..ed43938 100644
--- a/backend/src/main/resources/templates/email/welcome.html
+++ b/backend/src/main/resources/templates/email/welcome.html
@@ -120,7 +120,7 @@
Ihr Trustworthy Casino Team
From 21209524be632ba86e8d2c19351dd80bbfc5f435 Mon Sep 17 00:00:00 2001
From: Phan Huy Tran
Date: Thu, 15 May 2025 10:27:43 +0200
Subject: [PATCH 003/193] feat: implement coinflip api
---
.../de/szut/casino/coinflip/CoinSide.java | 6 +++
.../casino/coinflip/CoinflipController.java | 45 +++++++++++++++++++
.../de/szut/casino/coinflip/CoinflipDto.java | 20 +++++++++
.../szut/casino/coinflip/CoinflipResult.java | 15 +++++++
.../szut/casino/coinflip/CoinflipService.java | 34 ++++++++++++++
.../casino/shared/service/BalanceService.java | 2 +-
6 files changed, 121 insertions(+), 1 deletion(-)
create mode 100644 backend/src/main/java/de/szut/casino/coinflip/CoinSide.java
create mode 100644 backend/src/main/java/de/szut/casino/coinflip/CoinflipController.java
create mode 100644 backend/src/main/java/de/szut/casino/coinflip/CoinflipDto.java
create mode 100644 backend/src/main/java/de/szut/casino/coinflip/CoinflipResult.java
create mode 100644 backend/src/main/java/de/szut/casino/coinflip/CoinflipService.java
diff --git a/backend/src/main/java/de/szut/casino/coinflip/CoinSide.java b/backend/src/main/java/de/szut/casino/coinflip/CoinSide.java
new file mode 100644
index 0000000..f369cb4
--- /dev/null
+++ b/backend/src/main/java/de/szut/casino/coinflip/CoinSide.java
@@ -0,0 +1,6 @@
+package de.szut.casino.coinflip;
+
+public enum CoinSide {
+ HEAD,
+ TAILS;
+}
diff --git a/backend/src/main/java/de/szut/casino/coinflip/CoinflipController.java b/backend/src/main/java/de/szut/casino/coinflip/CoinflipController.java
new file mode 100644
index 0000000..0a526f1
--- /dev/null
+++ b/backend/src/main/java/de/szut/casino/coinflip/CoinflipController.java
@@ -0,0 +1,45 @@
+package de.szut.casino.coinflip;
+
+import de.szut.casino.exceptionHandling.exceptions.InsufficientFundsException;
+import de.szut.casino.exceptionHandling.exceptions.UserNotFoundException;
+import de.szut.casino.shared.service.BalanceService;
+import de.szut.casino.user.UserEntity;
+import de.szut.casino.user.UserService;
+import jakarta.validation.Valid;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.Optional;
+
+@RestController
+public class CoinflipController {
+ private final UserService userService;
+ private final BalanceService balanceService;
+ private final CoinflipService coinflipService;
+
+ public CoinflipController(UserService userService, BalanceService balanceService, CoinflipService coinflipService) {
+ this.userService = userService;
+ this.balanceService = balanceService;
+ this.coinflipService = coinflipService;
+ }
+
+
+ @PostMapping("/coinflip")
+ public ResponseEntity coinFlip(@RequestBody @Valid CoinflipDto coinflipDto) {
+ Optional optionalUser = userService.getCurrentUser();
+
+ if (optionalUser.isEmpty()) {
+ throw new UserNotFoundException();
+ }
+
+ UserEntity user = optionalUser.get();
+
+ if (!this.balanceService.hasFunds(user, coinflipDto)) {
+ throw new InsufficientFundsException();
+ }
+
+ return ResponseEntity.ok(coinflipService.play(user, coinflipDto));
+ }
+}
diff --git a/backend/src/main/java/de/szut/casino/coinflip/CoinflipDto.java b/backend/src/main/java/de/szut/casino/coinflip/CoinflipDto.java
new file mode 100644
index 0000000..8d5a47b
--- /dev/null
+++ b/backend/src/main/java/de/szut/casino/coinflip/CoinflipDto.java
@@ -0,0 +1,20 @@
+package de.szut.casino.coinflip;
+
+import de.szut.casino.shared.dto.BetDto;
+import jakarta.validation.constraints.NotNull;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.math.BigDecimal;
+
+@Getter
+@Setter
+public class CoinflipDto extends BetDto {
+ @NotNull(message = "chosen side cannot be null")
+ private CoinSide coinSide;
+
+ public CoinflipDto(BigDecimal betAmount, CoinSide coinSide) {
+ super(betAmount);
+ this.coinSide = coinSide;
+ }
+}
diff --git a/backend/src/main/java/de/szut/casino/coinflip/CoinflipResult.java b/backend/src/main/java/de/szut/casino/coinflip/CoinflipResult.java
new file mode 100644
index 0000000..71ead05
--- /dev/null
+++ b/backend/src/main/java/de/szut/casino/coinflip/CoinflipResult.java
@@ -0,0 +1,15 @@
+package de.szut.casino.coinflip;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.math.BigDecimal;
+
+@AllArgsConstructor
+@Setter
+@Getter
+public class CoinflipResult {
+ private boolean isWin;
+ private BigDecimal payout;
+}
diff --git a/backend/src/main/java/de/szut/casino/coinflip/CoinflipService.java b/backend/src/main/java/de/szut/casino/coinflip/CoinflipService.java
new file mode 100644
index 0000000..4cdc3e1
--- /dev/null
+++ b/backend/src/main/java/de/szut/casino/coinflip/CoinflipService.java
@@ -0,0 +1,34 @@
+package de.szut.casino.coinflip;
+
+import de.szut.casino.shared.service.BalanceService;
+import de.szut.casino.user.UserEntity;
+import org.springframework.stereotype.Service;
+
+import java.math.BigDecimal;
+import java.util.Random;
+
+@Service
+public class CoinflipService {
+ private final Random random = new Random();
+ private final BalanceService balanceService;
+
+ public CoinflipService(BalanceService balanceService) {
+ this.balanceService = balanceService;
+ }
+
+ public CoinflipResult play(UserEntity user, CoinflipDto coinflipDto) {
+ this.balanceService.subtractFunds(user, coinflipDto.getBetAmount());
+
+ CoinflipResult coinflipResult = new CoinflipResult(false, BigDecimal.ZERO);
+ CoinSide coinSide = this.random.nextBoolean() ? CoinSide.HEAD : CoinSide.TAILS;
+ if (coinSide == coinflipDto.getCoinSide()) {
+ coinflipResult.setWin(true);
+
+ BigDecimal payout = coinflipDto.getBetAmount().multiply(BigDecimal.TWO);
+ this.balanceService.addFunds(user, payout);
+ coinflipResult.setPayout(payout);
+ }
+
+ return coinflipResult;
+ }
+}
diff --git a/backend/src/main/java/de/szut/casino/shared/service/BalanceService.java b/backend/src/main/java/de/szut/casino/shared/service/BalanceService.java
index 40e6caa..048840b 100644
--- a/backend/src/main/java/de/szut/casino/shared/service/BalanceService.java
+++ b/backend/src/main/java/de/szut/casino/shared/service/BalanceService.java
@@ -9,7 +9,7 @@ import java.math.BigDecimal;
@Service
public class BalanceService {
- private UserRepository userRepository;
+ private final UserRepository userRepository;
public BalanceService(UserRepository userRepository) {
this.userRepository = userRepository;
From d2225decc19c942e6d03a9083dc9a15b33b60e3c Mon Sep 17 00:00:00 2001
From: csimonis
Date: Thu, 15 May 2025 10:49:24 +0200
Subject: [PATCH 004/193] feat(auth): add email verification feature and
handler
---
.../GlobalExceptionHandler.java | 7 +++++
.../exceptions/EmailNotVerifiedException.java | 9 ++++++
.../szut/casino/security/AuthController.java | 19 +++++++++---
.../casino/security/service/AuthService.java | 24 +++++++++++++-
.../de/szut/casino/user/UserRepository.java | 7 +++++
.../java/de/szut/casino/user/UserService.java | 18 +++++++++++
.../resources/templates/email/verify.html | 2 +-
frontend/src/app/app.routes.ts | 4 +++
.../auth/register/register.component.ts | 19 ------------
.../verify-email/verify-email.component.css | 0
.../verify-email/verify-email.component.html | 1 +
.../verify-email/verify-email.component.ts | 31 +++++++++++++++++++
.../app/feature/landing/landing.component.ts | 6 +++-
frontend/src/app/service/auth.service.ts | 4 +++
14 files changed, 124 insertions(+), 27 deletions(-)
create mode 100644 backend/src/main/java/de/szut/casino/exceptionHandling/exceptions/EmailNotVerifiedException.java
create mode 100644 frontend/src/app/feature/auth/verify-email/verify-email.component.css
create mode 100644 frontend/src/app/feature/auth/verify-email/verify-email.component.html
create mode 100644 frontend/src/app/feature/auth/verify-email/verify-email.component.ts
diff --git a/backend/src/main/java/de/szut/casino/exceptionHandling/GlobalExceptionHandler.java b/backend/src/main/java/de/szut/casino/exceptionHandling/GlobalExceptionHandler.java
index 573abb8..65b3b4b 100644
--- a/backend/src/main/java/de/szut/casino/exceptionHandling/GlobalExceptionHandler.java
+++ b/backend/src/main/java/de/szut/casino/exceptionHandling/GlobalExceptionHandler.java
@@ -1,5 +1,6 @@
package de.szut.casino.exceptionHandling;
+import de.szut.casino.exceptionHandling.exceptions.EmailNotVerifiedException;
import de.szut.casino.exceptionHandling.exceptions.InsufficientFundsException;
import de.szut.casino.exceptionHandling.exceptions.UserNotFoundException;
import jakarta.persistence.EntityExistsException;
@@ -31,4 +32,10 @@ public class GlobalExceptionHandler {
ErrorDetails errorDetails = new ErrorDetails(new Date(), ex.getMessage(), request.getDescription(false));
return new ResponseEntity<>(errorDetails, HttpStatus.CONFLICT);
}
+
+ @ExceptionHandler(EmailNotVerifiedException.class)
+ public ResponseEntity> handleEmailNotVerifiedException(EmailNotVerifiedException ex, WebRequest request) {
+ ErrorDetails errorDetails = new ErrorDetails(new Date(), ex.getMessage(), request.getDescription(false));
+ return new ResponseEntity<>(errorDetails, HttpStatus.BAD_REQUEST);
+ }
}
diff --git a/backend/src/main/java/de/szut/casino/exceptionHandling/exceptions/EmailNotVerifiedException.java b/backend/src/main/java/de/szut/casino/exceptionHandling/exceptions/EmailNotVerifiedException.java
new file mode 100644
index 0000000..ea08367
--- /dev/null
+++ b/backend/src/main/java/de/szut/casino/exceptionHandling/exceptions/EmailNotVerifiedException.java
@@ -0,0 +1,9 @@
+package de.szut.casino.exceptionHandling.exceptions;
+
+import de.szut.casino.security.service.EmailService;
+
+public class EmailNotVerifiedException extends Exception {
+ public EmailNotVerifiedException() {
+ super("Email not verified");
+ }
+}
diff --git a/backend/src/main/java/de/szut/casino/security/AuthController.java b/backend/src/main/java/de/szut/casino/security/AuthController.java
index 6d99625..7862ae7 100644
--- a/backend/src/main/java/de/szut/casino/security/AuthController.java
+++ b/backend/src/main/java/de/szut/casino/security/AuthController.java
@@ -1,5 +1,7 @@
package de.szut.casino.security;
+import de.szut.casino.exceptionHandling.ErrorDetails;
+import de.szut.casino.exceptionHandling.exceptions.EmailNotVerifiedException;
import de.szut.casino.security.dto.AuthResponseDto;
import de.szut.casino.security.dto.LoginRequestDto;
import de.szut.casino.security.service.AuthService;
@@ -9,12 +11,10 @@ import jakarta.mail.MessagingException;
import jakarta.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.bind.annotation.*;
import java.io.IOException;
+import java.util.Date;
@RestController
@RequestMapping("/auth")
@@ -24,7 +24,7 @@ public class AuthController {
private AuthService authService;
@PostMapping("/login")
- public ResponseEntity authenticateUser(@Valid @RequestBody LoginRequestDto loginRequest) {
+ public ResponseEntity authenticateUser(@Valid @RequestBody LoginRequestDto loginRequest) throws EmailNotVerifiedException {
AuthResponseDto response = authService.login(loginRequest);
return ResponseEntity.ok(response);
}
@@ -34,4 +34,13 @@ public class AuthController {
GetUserDto response = authService.register(signUpRequest);
return ResponseEntity.ok(response);
}
+
+ @PostMapping("/verify")
+ public ResponseEntity verifyEmail(@RequestParam("token") String token) {
+ if (authService.verifyEmail(token)) {
+ return ResponseEntity.badRequest().build();
+ }
+
+ return ResponseEntity.ok().build();
+ }
}
diff --git a/backend/src/main/java/de/szut/casino/security/service/AuthService.java b/backend/src/main/java/de/szut/casino/security/service/AuthService.java
index c4d324d..714b7f3 100644
--- a/backend/src/main/java/de/szut/casino/security/service/AuthService.java
+++ b/backend/src/main/java/de/szut/casino/security/service/AuthService.java
@@ -1,5 +1,6 @@
package de.szut.casino.security.service;
+import de.szut.casino.exceptionHandling.exceptions.EmailNotVerifiedException;
import de.szut.casino.security.dto.AuthResponseDto;
import de.szut.casino.security.dto.LoginRequestDto;
import de.szut.casino.security.jwt.JwtUtils;
@@ -16,6 +17,7 @@ import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import java.io.IOException;
+import java.util.Optional;
@Service
public class AuthService {
@@ -32,7 +34,11 @@ public class AuthService {
@Autowired
private EmailService emailService;
- public AuthResponseDto login(LoginRequestDto loginRequest) {
+ public AuthResponseDto login(LoginRequestDto loginRequest) throws EmailNotVerifiedException {
+ if (!userService.isVerified(loginRequest.getUsernameOrEmail())) {
+ throw new EmailNotVerifiedException();
+ }
+
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
loginRequest.getUsernameOrEmail(),
@@ -56,4 +62,20 @@ public class AuthService {
user.getBalance()
);
}
+
+ public Boolean verifyEmail(String token) {
+ Optional optionalUser = userService.getUserByVerificationToken(token);
+
+ if(!optionalUser.isPresent()) {
+ return false;
+ }
+
+ UserEntity user = optionalUser.get();
+
+ user.setEmailVerified(true);
+ user.setVerificationToken(null);
+ userService.saveUser(user);
+
+ return true;
+ }
}
diff --git a/backend/src/main/java/de/szut/casino/user/UserRepository.java b/backend/src/main/java/de/szut/casino/user/UserRepository.java
index 863e744..9eafd54 100644
--- a/backend/src/main/java/de/szut/casino/user/UserRepository.java
+++ b/backend/src/main/java/de/szut/casino/user/UserRepository.java
@@ -1,6 +1,7 @@
package de.szut.casino.user;
import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Service;
import java.util.Optional;
@@ -14,4 +15,10 @@ public interface UserRepository extends JpaRepository {
boolean existsByUsername(String username);
boolean existsByEmail(String email);
+
+ @Query("SELECT u FROM UserEntity u WHERE u.verificationToken = ?1")
+ Optional findOneByVerificationToken(String token);
+
+ @Query("SELECT u FROM UserEntity u WHERE u.username = ?1 OR u.email = ?1")
+ Optional findOneByUsernameOrEmail(String usernameOrEmail);
}
diff --git a/backend/src/main/java/de/szut/casino/user/UserService.java b/backend/src/main/java/de/szut/casino/user/UserService.java
index 6855619..9113864 100644
--- a/backend/src/main/java/de/szut/casino/user/UserService.java
+++ b/backend/src/main/java/de/szut/casino/user/UserService.java
@@ -44,4 +44,22 @@ public class UserService {
return userRepository.findByUsername(username);
}
+
+ public Optional getUserByVerificationToken(String token) {
+ return this.userRepository.findOneByVerificationToken(token);
+ }
+
+ public void saveUser(UserEntity user) {
+ userRepository.save(user);
+ }
+
+ public boolean isVerified(String usernameOrEmail) {
+ Optional optionalUser = userRepository.findOneByUsernameOrEmail(usernameOrEmail);
+
+ if (!optionalUser.isPresent()) {
+ return false;
+ }
+
+ return optionalUser.get().getEmailVerified();
+ }
}
diff --git a/backend/src/main/resources/templates/email/verify.html b/backend/src/main/resources/templates/email/verify.html
index 008236d..b7dc2a6 100644
--- a/backend/src/main/resources/templates/email/verify.html
+++ b/backend/src/main/resources/templates/email/verify.html
@@ -123,7 +123,7 @@
Klicken Sie auf den folgenden Button, um Ihre E-Mail-Adresse zu bestätigen:
diff --git a/frontend/src/app/app.routes.ts b/frontend/src/app/app.routes.ts
index 3792038..48c1e8e 100644
--- a/frontend/src/app/app.routes.ts
+++ b/frontend/src/app/app.routes.ts
@@ -12,6 +12,10 @@ export const routes: Routes = [
loadComponent: () => import('./feature/home/home.component'),
canActivate: [authGuard],
},
+ {
+ path: 'verify',
+ loadComponent: () => import('./feature/auth/verify-email/verify-email.component').then(m => m.VerifyEmailComponent),
+ },
{
path: 'game/blackjack',
loadComponent: () => import('./feature/game/blackjack/blackjack.component'),
diff --git a/frontend/src/app/feature/auth/register/register.component.ts b/frontend/src/app/feature/auth/register/register.component.ts
index 60f289b..a421184 100644
--- a/frontend/src/app/feature/auth/register/register.component.ts
+++ b/frontend/src/app/feature/auth/register/register.component.ts
@@ -56,25 +56,6 @@ export class RegisterComponent {
};
this.authService.register(registerRequest).subscribe({
- next: () => {
- this.authService
- .login({
- usernameOrEmail: registerRequest.email,
- password: registerRequest.password,
- })
- .subscribe({
- next: () => {
- this.closeDialog.emit();
- this.router.navigate(['/home']);
- },
- error: () => {
- this.isLoading.set(false);
- this.errorMessage.set(
- 'Registration successful but failed to login automatically. Please log in manually.'
- );
- },
- });
- },
error: (err: HttpErrorResponse) => {
this.isLoading.set(false);
diff --git a/frontend/src/app/feature/auth/verify-email/verify-email.component.css b/frontend/src/app/feature/auth/verify-email/verify-email.component.css
new file mode 100644
index 0000000..e69de29
diff --git a/frontend/src/app/feature/auth/verify-email/verify-email.component.html b/frontend/src/app/feature/auth/verify-email/verify-email.component.html
new file mode 100644
index 0000000..c39c8c0
--- /dev/null
+++ b/frontend/src/app/feature/auth/verify-email/verify-email.component.html
@@ -0,0 +1 @@
+
verify-email works!
diff --git a/frontend/src/app/feature/auth/verify-email/verify-email.component.ts b/frontend/src/app/feature/auth/verify-email/verify-email.component.ts
new file mode 100644
index 0000000..c67e1ff
--- /dev/null
+++ b/frontend/src/app/feature/auth/verify-email/verify-email.component.ts
@@ -0,0 +1,31 @@
+import { Component, inject, OnInit } from '@angular/core';
+import { ActivatedRoute, Router } from '@angular/router';
+import { AuthService } from '@service/auth.service';
+
+@Component({
+ selector: 'app-verify-email',
+ imports: [],
+ templateUrl: './verify-email.component.html',
+ styleUrl: './verify-email.component.css'
+})
+export class VerifyEmailComponent implements OnInit{
+ route: ActivatedRoute = inject(ActivatedRoute);
+ router: Router = inject(Router);
+ authService: AuthService = inject(AuthService);
+
+ ngOnInit(): void {
+ const token = this.route.snapshot.queryParamMap.get('token');
+
+ if (!token) {
+ this.router.navigate(['']);
+ console.log('no token');
+ return;
+ }
+
+ this.authService.verifyEmail(token).subscribe(() => {
+ this.router.navigate([''], {
+ queryParams: { login: true },
+ });
+ })
+ }
+}
diff --git a/frontend/src/app/feature/landing/landing.component.ts b/frontend/src/app/feature/landing/landing.component.ts
index d4d6078..5cfe0c5 100644
--- a/frontend/src/app/feature/landing/landing.component.ts
+++ b/frontend/src/app/feature/landing/landing.component.ts
@@ -7,7 +7,7 @@ import {
signal,
} from '@angular/core';
import { NgFor } from '@angular/common';
-import { RouterLink } from '@angular/router';
+import { ActivatedRoute, RouterLink } from '@angular/router';
import { AuthService } from '@service/auth.service';
import { LoginComponent } from '../auth/login/login.component';
import { RegisterComponent } from '../auth/register/register.component';
@@ -23,12 +23,16 @@ export class LandingComponent implements OnInit, OnDestroy {
currentSlide = 0;
private autoplayInterval: ReturnType
| undefined;
authService: AuthService = inject(AuthService);
+ route: ActivatedRoute = inject(ActivatedRoute);
showLogin = signal(false);
showRegister = signal(false);
ngOnInit() {
this.startAutoplay();
document.body.style.overflow = 'auto';
+ if (this.route.snapshot.queryParamMap.get('login') === 'true') {
+ this.showLoginForm();
+ }
}
ngOnDestroy() {
diff --git a/frontend/src/app/service/auth.service.ts b/frontend/src/app/service/auth.service.ts
index 1066008..1133958 100644
--- a/frontend/src/app/service/auth.service.ts
+++ b/frontend/src/app/service/auth.service.ts
@@ -74,6 +74,10 @@ export class AuthService {
});
}
+ public verifyEmail(token: string): Observable {
+ return this.http.post(`${this.authUrl}/verify?token=${token}`, null);
+ }
+
private setToken(token: string): void {
localStorage.setItem(TOKEN_KEY, token);
}
From db9fe842599b9a46d0770c346b4f7c23021033cc Mon Sep 17 00:00:00 2001
From: csimonis
Date: Thu, 15 May 2025 10:51:29 +0200
Subject: [PATCH 005/193] fix: change status code for EmailNotVerifiedException
---
.../szut/casino/exceptionHandling/GlobalExceptionHandler.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/backend/src/main/java/de/szut/casino/exceptionHandling/GlobalExceptionHandler.java b/backend/src/main/java/de/szut/casino/exceptionHandling/GlobalExceptionHandler.java
index 65b3b4b..9c185a8 100644
--- a/backend/src/main/java/de/szut/casino/exceptionHandling/GlobalExceptionHandler.java
+++ b/backend/src/main/java/de/szut/casino/exceptionHandling/GlobalExceptionHandler.java
@@ -36,6 +36,6 @@ public class GlobalExceptionHandler {
@ExceptionHandler(EmailNotVerifiedException.class)
public ResponseEntity> handleEmailNotVerifiedException(EmailNotVerifiedException ex, WebRequest request) {
ErrorDetails errorDetails = new ErrorDetails(new Date(), ex.getMessage(), request.getDescription(false));
- return new ResponseEntity<>(errorDetails, HttpStatus.BAD_REQUEST);
+ return new ResponseEntity<>(errorDetails, HttpStatus.UNAUTHORIZED);
}
}
From 0963dbae06cdc0f534fe07fad33c046b54bf596b Mon Sep 17 00:00:00 2001
From: csimonis
Date: Thu, 15 May 2025 10:57:14 +0200
Subject: [PATCH 006/193] refactor(verify-email): remove unused CSS file and
update template
---
.../app/feature/auth/verify-email/verify-email.component.css | 0
.../app/feature/auth/verify-email/verify-email.component.html | 2 +-
.../src/app/feature/auth/verify-email/verify-email.component.ts | 1 -
3 files changed, 1 insertion(+), 2 deletions(-)
delete mode 100644 frontend/src/app/feature/auth/verify-email/verify-email.component.css
diff --git a/frontend/src/app/feature/auth/verify-email/verify-email.component.css b/frontend/src/app/feature/auth/verify-email/verify-email.component.css
deleted file mode 100644
index e69de29..0000000
diff --git a/frontend/src/app/feature/auth/verify-email/verify-email.component.html b/frontend/src/app/feature/auth/verify-email/verify-email.component.html
index c39c8c0..d7bc11c 100644
--- a/frontend/src/app/feature/auth/verify-email/verify-email.component.html
+++ b/frontend/src/app/feature/auth/verify-email/verify-email.component.html
@@ -1 +1 @@
-verify-email works!
+Verifying...
diff --git a/frontend/src/app/feature/auth/verify-email/verify-email.component.ts b/frontend/src/app/feature/auth/verify-email/verify-email.component.ts
index c67e1ff..26f0317 100644
--- a/frontend/src/app/feature/auth/verify-email/verify-email.component.ts
+++ b/frontend/src/app/feature/auth/verify-email/verify-email.component.ts
@@ -6,7 +6,6 @@ import { AuthService } from '@service/auth.service';
selector: 'app-verify-email',
imports: [],
templateUrl: './verify-email.component.html',
- styleUrl: './verify-email.component.css'
})
export class VerifyEmailComponent implements OnInit{
route: ActivatedRoute = inject(ActivatedRoute);
From decf2e21a393bca68f9aeffc16c79a77e2ee0a15 Mon Sep 17 00:00:00 2001
From: csimonis
Date: Thu, 15 May 2025 10:58:53 +0200
Subject: [PATCH 007/193] feat(email): add welcome email sending on
verification success
---
.../src/main/java/de/szut/casino/security/AuthController.java | 2 +-
.../main/java/de/szut/casino/security/service/AuthService.java | 3 ++-
.../java/de/szut/casino/security/service/EmailService.java | 3 +--
3 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/backend/src/main/java/de/szut/casino/security/AuthController.java b/backend/src/main/java/de/szut/casino/security/AuthController.java
index 7862ae7..f833d78 100644
--- a/backend/src/main/java/de/szut/casino/security/AuthController.java
+++ b/backend/src/main/java/de/szut/casino/security/AuthController.java
@@ -36,7 +36,7 @@ public class AuthController {
}
@PostMapping("/verify")
- public ResponseEntity verifyEmail(@RequestParam("token") String token) {
+ public ResponseEntity verifyEmail(@RequestParam("token") String token) throws MessagingException, IOException {
if (authService.verifyEmail(token)) {
return ResponseEntity.badRequest().build();
}
diff --git a/backend/src/main/java/de/szut/casino/security/service/AuthService.java b/backend/src/main/java/de/szut/casino/security/service/AuthService.java
index 714b7f3..f51ff83 100644
--- a/backend/src/main/java/de/szut/casino/security/service/AuthService.java
+++ b/backend/src/main/java/de/szut/casino/security/service/AuthService.java
@@ -63,7 +63,7 @@ public class AuthService {
);
}
- public Boolean verifyEmail(String token) {
+ public Boolean verifyEmail(String token) throws MessagingException, IOException {
Optional optionalUser = userService.getUserByVerificationToken(token);
if(!optionalUser.isPresent()) {
@@ -75,6 +75,7 @@ public class AuthService {
user.setEmailVerified(true);
user.setVerificationToken(null);
userService.saveUser(user);
+ this.emailService.sendWelcomeEmail(user);
return true;
}
diff --git a/backend/src/main/java/de/szut/casino/security/service/EmailService.java b/backend/src/main/java/de/szut/casino/security/service/EmailService.java
index 348fafd..4d83262 100644
--- a/backend/src/main/java/de/szut/casino/security/service/EmailService.java
+++ b/backend/src/main/java/de/szut/casino/security/service/EmailService.java
@@ -15,7 +15,6 @@ import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.nio.charset.StandardCharsets;
-import java.util.List;
@Service
public class EmailService {
@@ -53,7 +52,7 @@ public class EmailService {
mailSender.send(message);
}
- public void sendRegistrationEmail(UserEntity user) throws IOException, MessagingException {
+ public void sendWelcomeEmail(UserEntity user) throws IOException, MessagingException {
String template = loadTemplate("email/welcome.html");
String htmlContent = template
.replace("${username}", user.getUsername())
From 51984318e6df018e041e9b08fb20c32e2a994b7c Mon Sep 17 00:00:00 2001
From: csimonis
Date: Thu, 15 May 2025 11:01:47 +0200
Subject: [PATCH 008/193] refactor(auth.service): change observable type to
unknown
---
frontend/src/app/service/auth.service.ts | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/frontend/src/app/service/auth.service.ts b/frontend/src/app/service/auth.service.ts
index 1133958..657067f 100644
--- a/frontend/src/app/service/auth.service.ts
+++ b/frontend/src/app/service/auth.service.ts
@@ -74,8 +74,8 @@ export class AuthService {
});
}
- public verifyEmail(token: string): Observable {
- return this.http.post(`${this.authUrl}/verify?token=${token}`, null);
+ public verifyEmail(token: string): Observable {
+ return this.http.post(`${this.authUrl}/verify?token=${token}`, null);
}
private setToken(token: string): void {
From 2f21408e3da6db9a692286ec7e9ca3e5a16b5f95 Mon Sep 17 00:00:00 2001
From: csimonis
Date: Thu, 15 May 2025 11:03:50 +0200
Subject: [PATCH 009/193] style: format code for better readability
---
frontend/src/app/app.routes.ts | 5 ++++-
.../app/feature/auth/verify-email/verify-email.component.ts | 4 ++--
2 files changed, 6 insertions(+), 3 deletions(-)
diff --git a/frontend/src/app/app.routes.ts b/frontend/src/app/app.routes.ts
index 48c1e8e..5c57416 100644
--- a/frontend/src/app/app.routes.ts
+++ b/frontend/src/app/app.routes.ts
@@ -14,7 +14,10 @@ export const routes: Routes = [
},
{
path: 'verify',
- loadComponent: () => import('./feature/auth/verify-email/verify-email.component').then(m => m.VerifyEmailComponent),
+ loadComponent: () =>
+ import('./feature/auth/verify-email/verify-email.component').then(
+ (m) => m.VerifyEmailComponent
+ ),
},
{
path: 'game/blackjack',
diff --git a/frontend/src/app/feature/auth/verify-email/verify-email.component.ts b/frontend/src/app/feature/auth/verify-email/verify-email.component.ts
index 26f0317..5f2814a 100644
--- a/frontend/src/app/feature/auth/verify-email/verify-email.component.ts
+++ b/frontend/src/app/feature/auth/verify-email/verify-email.component.ts
@@ -7,7 +7,7 @@ import { AuthService } from '@service/auth.service';
imports: [],
templateUrl: './verify-email.component.html',
})
-export class VerifyEmailComponent implements OnInit{
+export class VerifyEmailComponent implements OnInit {
route: ActivatedRoute = inject(ActivatedRoute);
router: Router = inject(Router);
authService: AuthService = inject(AuthService);
@@ -25,6 +25,6 @@ export class VerifyEmailComponent implements OnInit{
this.router.navigate([''], {
queryParams: { login: true },
});
- })
+ });
}
}
From 47e04567a92495f036db46725837fc20806af42b Mon Sep 17 00:00:00 2001
From: jank
Date: Thu, 15 May 2025 11:07:00 +0200
Subject: [PATCH 010/193] perf: Update bun jobs
---
.gitea/workflows/ci.yml | 26 +++++++++-----------------
1 file changed, 9 insertions(+), 17 deletions(-)
diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml
index 7742416..e277f53 100644
--- a/.gitea/workflows/ci.yml
+++ b/.gitea/workflows/ci.yml
@@ -113,17 +113,15 @@ jobs:
push: false
eslint:
- runs-on: ubuntu-latest
+ runs-on: docker
+ container:
+ image: git.kjan.de/actions/runner-bun:latest
name: eslint
needs: changed_files
if: ${{ needs.changed_files.outputs.frontend == 'true' || needs.changed_files.outputs.workflow == 'true' }}
- container:
- image: catthehacker/ubuntu:act-latest
steps:
- name: Checkout Code
uses: actions/checkout@v4
- - name: Install bun
- uses: oven-sh/setup-bun@v2
- uses: actions/cache@v4
working-directory: ./frontend
with:
@@ -142,17 +140,15 @@ jobs:
bun run lint
oxlint:
- runs-on: ubuntu-latest
+ runs-on: docker
name: oxlint
needs: changed_files
if: ${{ needs.changed_files.outputs.frontend == 'true' || needs.changed_files.outputs.workflow == 'true' }}
container:
- image: catthehacker/ubuntu:act-latest
+ image: git.kjan.de/actions/runner-bun:latest
steps:
- name: Checkout Code
uses: actions/checkout@v4
- - name: Install bun
- uses: oven-sh/setup-bun@v2
- uses: actions/cache@v4
working-directory: ./frontend
with:
@@ -171,17 +167,15 @@ jobs:
bun run oxlint
prettier:
- runs-on: ubuntu-latest
+ runs-on: docker
name: prettier
needs: changed_files
if: ${{ needs.changed_files.outputs.frontend == 'true' || needs.changed_files.outputs.workflow == 'true' }}
container:
- image: catthehacker/ubuntu:act-latest
+ image: git.kjan.de/actions/runner-bun:latest
steps:
- name: Checkout Code
uses: actions/checkout@v4
- - name: Install bun
- uses: oven-sh/setup-bun@v2
- uses: actions/cache@v4
working-directory: ./frontend
with:
@@ -200,17 +194,15 @@ jobs:
bun run format:check
test-build:
- runs-on: ubuntu-latest
+ runs-on: docker
name: test-build
needs: changed_files
if: ${{ needs.changed_files.outputs.frontend == 'true' || needs.changed_files.outputs.workflow == 'true' }}
container:
- image: catthehacker/ubuntu:act-latest
+ image: git.kjan.de/actions/runner-bun:latest
steps:
- name: Checkout Code
uses: actions/checkout@v4
- - name: Install bun
- uses: oven-sh/setup-bun@v2
- uses: actions/cache@v4
working-directory: ./frontend
with:
From d7f2e72a15e4c85d420c442a0791ba7d11d6d36c Mon Sep 17 00:00:00 2001
From: Phan Huy Tran
Date: Thu, 15 May 2025 11:44:56 +0200
Subject: [PATCH 011/193] feat: add coinside result
---
.../src/main/java/de/szut/casino/coinflip/CoinflipResult.java | 1 +
.../src/main/java/de/szut/casino/coinflip/CoinflipService.java | 2 +-
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/backend/src/main/java/de/szut/casino/coinflip/CoinflipResult.java b/backend/src/main/java/de/szut/casino/coinflip/CoinflipResult.java
index 71ead05..4c8fbdf 100644
--- a/backend/src/main/java/de/szut/casino/coinflip/CoinflipResult.java
+++ b/backend/src/main/java/de/szut/casino/coinflip/CoinflipResult.java
@@ -12,4 +12,5 @@ import java.math.BigDecimal;
public class CoinflipResult {
private boolean isWin;
private BigDecimal payout;
+ private CoinSide coinSide;
}
diff --git a/backend/src/main/java/de/szut/casino/coinflip/CoinflipService.java b/backend/src/main/java/de/szut/casino/coinflip/CoinflipService.java
index 4cdc3e1..c706ffa 100644
--- a/backend/src/main/java/de/szut/casino/coinflip/CoinflipService.java
+++ b/backend/src/main/java/de/szut/casino/coinflip/CoinflipService.java
@@ -19,8 +19,8 @@ public class CoinflipService {
public CoinflipResult play(UserEntity user, CoinflipDto coinflipDto) {
this.balanceService.subtractFunds(user, coinflipDto.getBetAmount());
- CoinflipResult coinflipResult = new CoinflipResult(false, BigDecimal.ZERO);
CoinSide coinSide = this.random.nextBoolean() ? CoinSide.HEAD : CoinSide.TAILS;
+ CoinflipResult coinflipResult = new CoinflipResult(false, BigDecimal.ZERO, coinSide);
if (coinSide == coinflipDto.getCoinSide()) {
coinflipResult.setWin(true);
From 9827f81230b17c5cee598eb51f7624536edfd19e Mon Sep 17 00:00:00 2001
From: csimonis
Date: Thu, 15 May 2025 11:19:46 +0200
Subject: [PATCH 012/193] wip: stuff
---
.../szut/casino/security/AuthController.java | 6 +
.../casino/security/service/AuthService.java | 17 +-
.../casino/security/service/EmailService.java | 17 ++
.../java/de/szut/casino/user/UserService.java | 4 +
.../templates/email/recover-password.html | 160 ++++++++++++++++++
5 files changed, 202 insertions(+), 2 deletions(-)
create mode 100644 backend/src/main/resources/templates/email/recover-password.html
diff --git a/backend/src/main/java/de/szut/casino/security/AuthController.java b/backend/src/main/java/de/szut/casino/security/AuthController.java
index f833d78..5d8717e 100644
--- a/backend/src/main/java/de/szut/casino/security/AuthController.java
+++ b/backend/src/main/java/de/szut/casino/security/AuthController.java
@@ -43,4 +43,10 @@ public class AuthController {
return ResponseEntity.ok().build();
}
+
+ @PostMapping("/recover-password")
+ public ResponseEntity recoverPassword(@RequestParam("email") String email) throws MessagingException, IOException {
+ authService.recoverPassword(email);
+ return ResponseEntity.ok().build();
+ }
}
diff --git a/backend/src/main/java/de/szut/casino/security/service/AuthService.java b/backend/src/main/java/de/szut/casino/security/service/AuthService.java
index f51ff83..380b5e4 100644
--- a/backend/src/main/java/de/szut/casino/security/service/AuthService.java
+++ b/backend/src/main/java/de/szut/casino/security/service/AuthService.java
@@ -16,6 +16,7 @@ import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
+import javax.swing.text.html.Option;
import java.io.IOException;
import java.util.Optional;
@@ -46,7 +47,7 @@ public class AuthService {
SecurityContextHolder.getContext().setAuthentication(authentication);
String jwt = jwtUtils.generateToken(authentication);
-
+
return new AuthResponseDto(jwt);
}
@@ -66,7 +67,7 @@ public class AuthService {
public Boolean verifyEmail(String token) throws MessagingException, IOException {
Optional optionalUser = userService.getUserByVerificationToken(token);
- if(!optionalUser.isPresent()) {
+ if (!optionalUser.isPresent()) {
return false;
}
@@ -79,4 +80,16 @@ public class AuthService {
return true;
}
+
+ public void recoverPassword(String email) {
+ Optional optionalUser = userService.getUserByEmail(email);
+
+ if (optionalUser.isPresent()) {
+ UserEntity user = optionalUser.get();
+ String token = jwtUtils.generateToken(user.getUsername());
+ user.setVerificationToken(token);
+ userService.saveUser(user);
+ this.emailService.sendPasswordRecoveryEmail(user);
+ }
+ }
}
diff --git a/backend/src/main/java/de/szut/casino/security/service/EmailService.java b/backend/src/main/java/de/szut/casino/security/service/EmailService.java
index 4d83262..b7a05b9 100644
--- a/backend/src/main/java/de/szut/casino/security/service/EmailService.java
+++ b/backend/src/main/java/de/szut/casino/security/service/EmailService.java
@@ -87,6 +87,23 @@ public class EmailService {
mailSender.send(message);
}
+ public void sendPasswordRecoveryEmail(UserEntity user) throws IOException, MessagingException {
+ String template = loadTemplate("email/recover-password.html");
+ String htmlContent = template
+ .replace("${username}", user.getUsername())
+ .replace("${feUrl}", feUrl);
+
+ MimeMessage message = mailSender.createMimeMessage();
+ MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8");
+
+ helper.setFrom(mailConfig.fromAddress);
+ helper.setTo(user.getEmailAddress());
+ helper.setSubject("Zurücksetzen ihres Passworts");
+ helper.setText(htmlContent, true);
+
+ mailSender.send(message);
+ }
+
private String loadTemplate(String templatePath) throws IOException {
ClassPathResource resource = new ClassPathResource("templates/" + templatePath);
try (Reader reader = new InputStreamReader(resource.getInputStream(), StandardCharsets.UTF_8)) {
diff --git a/backend/src/main/java/de/szut/casino/user/UserService.java b/backend/src/main/java/de/szut/casino/user/UserService.java
index 9113864..a04b02e 100644
--- a/backend/src/main/java/de/szut/casino/user/UserService.java
+++ b/backend/src/main/java/de/szut/casino/user/UserService.java
@@ -62,4 +62,8 @@ public class UserService {
return optionalUser.get().getEmailVerified();
}
+
+ public Optional getUserByEmail(String email) {
+ return userRepository.findByEmail(email);
+ }
}
diff --git a/backend/src/main/resources/templates/email/recover-password.html b/backend/src/main/resources/templates/email/recover-password.html
new file mode 100644
index 0000000..6d4e1a1
--- /dev/null
+++ b/backend/src/main/resources/templates/email/recover-password.html
@@ -0,0 +1,160 @@
+
+
+
+
+
+ Passwort zurücksetzen - Trustworthy Casino©
+
+
+
+
+
+
+
Hallo ${username} ,
+
+
wir haben eine Anfrage zum Zurücksetzen Ihres Passworts für Ihr Trustworthy Casino Konto erhalten. Um Ihr Passwort zurückzusetzen, klicken Sie bitte auf den folgenden Button:
+
+
+
+
Alternativ können Sie auch diesen Code verwenden:
+
+
${resetCode}
+
+
+
Hinweis: Dieser Link und Code sind aus Sicherheitsgründen nur 60 Minuten gültig.
+
+
+
+
+
+
Falls Sie diese Anfrage nicht gestellt haben, ignorieren Sie diese E-Mail bitte. In diesem Fall empfehlen wir Ihnen, Ihr Passwort zu ändern und unseren Kundenservice zu kontaktieren, um die Sicherheit Ihres Kontos zu gewährleisten.
+
+
+
+
+
Bei Fragen steht Ihnen unser Support-Team jederzeit zur Verfügung.
+
+
Mit freundlichen Grüßen,
+ Ihr Trustworthy Casino Team
+
+
+
+
+
\ No newline at end of file
From c8f2d16f076d04d64fbb8cbc2761a10bb1153d5e Mon Sep 17 00:00:00 2001
From: csimonis
Date: Thu, 15 May 2025 12:15:33 +0200
Subject: [PATCH 013/193] feat(auth): add password reset functionality and DTO
---
.../szut/casino/security/AuthController.java | 7 ++++++
.../casino/security/dto/ResetPasswordDto.java | 15 +++++++++++++
.../casino/security/service/AuthService.java | 22 ++++++++++++++++---
.../casino/security/service/EmailService.java | 1 +
.../java/de/szut/casino/user/UserEntity.java | 2 ++
.../de/szut/casino/user/UserRepository.java | 3 +++
.../java/de/szut/casino/user/UserService.java | 4 ++++
.../templates/email/recover-password.html | 12 ++++------
8 files changed, 55 insertions(+), 11 deletions(-)
create mode 100644 backend/src/main/java/de/szut/casino/security/dto/ResetPasswordDto.java
diff --git a/backend/src/main/java/de/szut/casino/security/AuthController.java b/backend/src/main/java/de/szut/casino/security/AuthController.java
index 5d8717e..d22de30 100644
--- a/backend/src/main/java/de/szut/casino/security/AuthController.java
+++ b/backend/src/main/java/de/szut/casino/security/AuthController.java
@@ -4,6 +4,7 @@ import de.szut.casino.exceptionHandling.ErrorDetails;
import de.szut.casino.exceptionHandling.exceptions.EmailNotVerifiedException;
import de.szut.casino.security.dto.AuthResponseDto;
import de.szut.casino.security.dto.LoginRequestDto;
+import de.szut.casino.security.dto.ResetPasswordDto;
import de.szut.casino.security.service.AuthService;
import de.szut.casino.user.dto.CreateUserDto;
import de.szut.casino.user.dto.GetUserDto;
@@ -49,4 +50,10 @@ public class AuthController {
authService.recoverPassword(email);
return ResponseEntity.ok().build();
}
+
+ @PostMapping("/reset-password")
+ public ResponseEntity resetPassword(@Valid @RequestBody ResetPasswordDto passwordDto) throws MessagingException, IOException {
+ authService.resetPassword(passwordDto);
+ return ResponseEntity.ok().build();
+ }
}
diff --git a/backend/src/main/java/de/szut/casino/security/dto/ResetPasswordDto.java b/backend/src/main/java/de/szut/casino/security/dto/ResetPasswordDto.java
new file mode 100644
index 0000000..192d928
--- /dev/null
+++ b/backend/src/main/java/de/szut/casino/security/dto/ResetPasswordDto.java
@@ -0,0 +1,15 @@
+package de.szut.casino.security.dto;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@AllArgsConstructor
+@NoArgsConstructor
+@Getter
+@Setter
+public class ResetPasswordDto {
+ private String token;
+ private String password;
+}
diff --git a/backend/src/main/java/de/szut/casino/security/service/AuthService.java b/backend/src/main/java/de/szut/casino/security/service/AuthService.java
index 380b5e4..959a55a 100644
--- a/backend/src/main/java/de/szut/casino/security/service/AuthService.java
+++ b/backend/src/main/java/de/szut/casino/security/service/AuthService.java
@@ -3,17 +3,20 @@ package de.szut.casino.security.service;
import de.szut.casino.exceptionHandling.exceptions.EmailNotVerifiedException;
import de.szut.casino.security.dto.AuthResponseDto;
import de.szut.casino.security.dto.LoginRequestDto;
+import de.szut.casino.security.dto.ResetPasswordDto;
import de.szut.casino.security.jwt.JwtUtils;
import de.szut.casino.user.UserEntity;
import de.szut.casino.user.UserService;
import de.szut.casino.user.dto.CreateUserDto;
import de.szut.casino.user.dto.GetUserDto;
import jakarta.mail.MessagingException;
+import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import javax.swing.text.html.Option;
@@ -35,6 +38,9 @@ public class AuthService {
@Autowired
private EmailService emailService;
+ @Autowired
+ private PasswordEncoder passwordEncoder;
+
public AuthResponseDto login(LoginRequestDto loginRequest) throws EmailNotVerifiedException {
if (!userService.isVerified(loginRequest.getUsernameOrEmail())) {
throw new EmailNotVerifiedException();
@@ -81,15 +87,25 @@ public class AuthService {
return true;
}
- public void recoverPassword(String email) {
+ public void recoverPassword(String email) throws MessagingException, IOException {
Optional optionalUser = userService.getUserByEmail(email);
if (optionalUser.isPresent()) {
UserEntity user = optionalUser.get();
- String token = jwtUtils.generateToken(user.getUsername());
- user.setVerificationToken(token);
+ user.setPasswordResetToken(RandomStringUtils.randomAlphanumeric(64));
userService.saveUser(user);
this.emailService.sendPasswordRecoveryEmail(user);
}
}
+
+ public void resetPassword(ResetPasswordDto passwordDto) {
+ Optional optionalUser = userService.getUserByPasswordResetToken(passwordDto.getToken());
+
+ if (optionalUser.isPresent()) {
+ UserEntity user = optionalUser.get();
+ user.setPassword(passwordEncoder.encode(passwordDto.getPassword()));
+ user.setPasswordResetToken(null);
+ userService.saveUser(user);
+ }
+ }
}
diff --git a/backend/src/main/java/de/szut/casino/security/service/EmailService.java b/backend/src/main/java/de/szut/casino/security/service/EmailService.java
index b7a05b9..f276a3c 100644
--- a/backend/src/main/java/de/szut/casino/security/service/EmailService.java
+++ b/backend/src/main/java/de/szut/casino/security/service/EmailService.java
@@ -91,6 +91,7 @@ public class EmailService {
String template = loadTemplate("email/recover-password.html");
String htmlContent = template
.replace("${username}", user.getUsername())
+ .replace("${resetToken}", user.getPasswordResetToken())
.replace("${feUrl}", feUrl);
MimeMessage message = mailSender.createMimeMessage();
diff --git a/backend/src/main/java/de/szut/casino/user/UserEntity.java b/backend/src/main/java/de/szut/casino/user/UserEntity.java
index 161ce52..2190867 100644
--- a/backend/src/main/java/de/szut/casino/user/UserEntity.java
+++ b/backend/src/main/java/de/szut/casino/user/UserEntity.java
@@ -34,6 +34,8 @@ public class UserEntity {
private String verificationToken;
+ private String passwordResetToken;
+
public UserEntity(String email, String username, String password, BigDecimal balance, String verificationToken) {
this.email = email;
this.username = username;
diff --git a/backend/src/main/java/de/szut/casino/user/UserRepository.java b/backend/src/main/java/de/szut/casino/user/UserRepository.java
index 9eafd54..29790f5 100644
--- a/backend/src/main/java/de/szut/casino/user/UserRepository.java
+++ b/backend/src/main/java/de/szut/casino/user/UserRepository.java
@@ -21,4 +21,7 @@ public interface UserRepository extends JpaRepository {
@Query("SELECT u FROM UserEntity u WHERE u.username = ?1 OR u.email = ?1")
Optional findOneByUsernameOrEmail(String usernameOrEmail);
+
+ @Query("SELECT u FROM UserEntity u WHERE u.passwordResetToken = ?1")
+ Optional findOneByPasswordResetToken(String token);
}
diff --git a/backend/src/main/java/de/szut/casino/user/UserService.java b/backend/src/main/java/de/szut/casino/user/UserService.java
index a04b02e..baa2eab 100644
--- a/backend/src/main/java/de/szut/casino/user/UserService.java
+++ b/backend/src/main/java/de/szut/casino/user/UserService.java
@@ -66,4 +66,8 @@ public class UserService {
public Optional getUserByEmail(String email) {
return userRepository.findByEmail(email);
}
+
+ public Optional getUserByPasswordResetToken(String token) {
+ return this.userRepository.findOneByPasswordResetToken(token);
+ }
}
diff --git a/backend/src/main/resources/templates/email/recover-password.html b/backend/src/main/resources/templates/email/recover-password.html
index 6d4e1a1..cf666d1 100644
--- a/backend/src/main/resources/templates/email/recover-password.html
+++ b/backend/src/main/resources/templates/email/recover-password.html
@@ -129,12 +129,8 @@
Passwort zurücksetzen
- Alternativ können Sie auch diesen Code verwenden:
-
- ${resetCode}
-
-
Hinweis: Dieser Link und Code sind aus Sicherheitsgründen nur 60 Minuten gültig.
+
Hinweis: Dieser Link und Code sind aus Sicherheitsgründen vielleicht nur 60 Minuten gültig.
@@ -145,15 +141,15 @@
- Bei Fragen steht Ihnen unser Support-Team jederzeit zur Verfügung.
+ Bei Fragen steht Ihnen unser Support-Team nicht zur Verfügung.
Mit freundlichen Grüßen,
Ihr Trustworthy Casino Team