Skip to content

Commit 12998bb

Browse files
committed
email handling and sht
1 parent c16743a commit 12998bb

File tree

10 files changed

+150
-19
lines changed

10 files changed

+150
-19
lines changed

src/main/java/com/example/usermanagement/controllers/AccountController.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,13 @@ public ResponseEntity<GeneralAccountDTO> updateMe(@RequestBody UpdateAccountDTO
6868
return new ResponseEntity<>(new GeneralAccountDTO(account), HttpStatus.OK);
6969
}
7070

71+
// with password
72+
@DeleteMapping("/me")
73+
public ResponseEntity<String> deleteMe(@RequestParam String password) {
74+
accountService.deleteMyAccount(password);
75+
return new ResponseEntity<>("Account deleted", HttpStatus.OK);
76+
}
77+
7178
@GetMapping("/{accountId}")
7279
public ResponseEntity<DetailedAccountDTO> getAccount(@PathVariable UUID accountId) {
7380
var account = new DetailedAccountDTO(accountService.getAccountById(accountId));
@@ -86,7 +93,7 @@ public ResponseEntity<List<AccountAuthoritiesEditResponse>> editAuthorities(@Req
8693
public ResponseEntity<String> verifyEmail(@RequestParam String token) {
8794
String email = emailVerificationTokenService.consumeEmailVerificationToken(token);
8895
accountService.verifyAccountEmail(email);
89-
return new ResponseEntity<>("Email verified", HttpStatus.OK);
96+
return new ResponseEntity<>(email + " verified", HttpStatus.OK);
9097
}
9198

9299
@PostMapping("/verify-email/resend")

src/main/java/com/example/usermanagement/entities/Account.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,12 @@ public class Account {
4444
@Column(nullable = false, name = "is_member", columnDefinition = "boolean default false")
4545
private Boolean isMember = false;
4646

47+
@OneToOne(mappedBy = "account", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
48+
private PasswordResetToken passwordResetToken;
49+
50+
@OneToOne(mappedBy = "account", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
51+
private EmailVerificationToken emailVerificationToken;
52+
4753
@ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
4854
@JoinTable(
4955
name = "account_roles",

src/main/java/com/example/usermanagement/entities/EmailVerificationToken.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,13 @@ public class EmailVerificationToken {
2222
@Column(nullable = false, unique = true)
2323
private String token;
2424

25-
@Column(nullable = false)
25+
@Column(nullable = false,name = "expiry_date")
2626
private Instant expiryDate;
2727

28-
@Column(nullable = false)
28+
@Column(nullable = false,name = "created_date")
2929
private Instant createdDate;
3030

31-
@Column(nullable = false)
31+
@Column(nullable = false,name = "is_used")
3232
private boolean isUsed;
3333

3434
@OneToOne(fetch = FetchType.LAZY)

src/main/java/com/example/usermanagement/events/listeners/EmailsListener.java

Lines changed: 49 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
package com.example.usermanagement.events.listeners;
22

33

4-
import com.example.usermanagement.events.publishers.emails.EmailVerificationTokenGeneratedEvent;
5-
import com.example.usermanagement.events.publishers.emails.PasswordHaveBeenResetedEvent;
6-
import com.example.usermanagement.events.publishers.emails.PasswordResetGeneratedEvent;
4+
import com.example.usermanagement.events.publishers.emails.*;
75
import com.example.usermanagement.interfaces.services.IEmailService;
86
import lombok.RequiredArgsConstructor;
97
import org.springframework.beans.factory.annotation.Value;
@@ -21,23 +19,66 @@ public class EmailsListener {
2119

2220
private final IEmailService emailService;
2321

22+
@EventListener(AccountCreatedEvent.class)
23+
public void onAccountCreatedEvent(AccountCreatedEvent event) {
24+
String email = event.getEmail();
25+
String body = "Hello, " + email + "\n" +
26+
"Your account have been created successfully." + "\n" +
27+
"If you didn't request this, please contact us.";
28+
29+
emailService.sendEmail(event.getEmail(), "Account created", body);
30+
}
31+
2432
@EventListener(EmailVerificationTokenGeneratedEvent.class)
2533
public void onEmailVerificationTokenGeneratedEvent(EmailVerificationTokenGeneratedEvent event) {
26-
String link = amwc + "/api/accounts/verify-email?token=" + event.getToken();
27-
String body = "Click here to verify your email: " + link;
34+
String link = amwc + "/auth/confirm-email-verification?token=" + event.getToken();
35+
String email = event.getEmail();
36+
String body = "Hello, " + email + "\n" +
37+
"Click here to verify your email: " + link + "\n" +
38+
"If you didn't request this, please ignore this email.";
39+
2840
emailService.sendEmail("[email protected]", "Email verification", body);
2941
}
3042

3143
@EventListener(PasswordResetGeneratedEvent.class)
3244
public void onPasswordResetGeneratedEvent(PasswordResetGeneratedEvent event) {
33-
String link = amwc + "/api/accounts/reset-password?token=" + event.getToken();
34-
String body = "Click here to reset your password: " + link;
45+
String link = amwc + "/auth/confirm-reset-password?token=" + event.getToken();
46+
String email = event.getEmail();
47+
String body = "Hello, " + email + "\n" +
48+
"Click here to reset your password: " + link + "\n" +
49+
"If you didn't request this, please ignore this email.";
50+
3551
emailService.sendEmail(event.getEmail(), "Password reset", body);
3652
}
3753

3854
@EventListener(PasswordHaveBeenResetedEvent.class)
3955
public void onPasswordHaveBeenResetedEvent(PasswordHaveBeenResetedEvent event) {
40-
String body = "Your password have been reseted";
56+
String email = event.getEmail();
57+
String body = "Hello, " + email + "\n" +
58+
"Your password have been reseted successfully." + "\n" +
59+
"If you didn't request this, please contact us.";
60+
4161
emailService.sendEmail(event.getEmail(), "Password reseted", body);
4262
}
63+
64+
@EventListener(PasswordChangedEvent.class)
65+
public void onPasswordChangedEvent(PasswordChangedEvent event) {
66+
String email = event.getEmail();
67+
String body = "Hello, " + email + "\n" +
68+
"Your password have been changed successfully." + "\n" +
69+
"If you didn't request this, please contact us.";
70+
71+
emailService.sendEmail(event.getEmail(), "Password changed", body);
72+
}
73+
74+
@EventListener(AccountDeletedEvent.class)
75+
public void onAccountDeletedEvent(AccountDeletedEvent event) {
76+
String email = event.getEmail();
77+
String body = "Hello, " + email + "\n" +
78+
"Your account have been deleted successfully." + "\n" +
79+
"If you didn't request this, please contact us.";
80+
81+
emailService.sendEmail(event.getEmail(), "Account deleted", body);
82+
}
83+
4384
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.example.usermanagement.events.publishers.emails;
2+
3+
import lombok.Getter;
4+
import org.springframework.context.ApplicationEvent;
5+
6+
@Getter
7+
public class AccountCreatedEvent extends ApplicationEvent {
8+
private final String email;
9+
10+
public AccountCreatedEvent(Object source, String email) {
11+
super(source);
12+
this.email = email;
13+
}
14+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package com.example.usermanagement.events.publishers.emails;
2+
3+
import lombok.Getter;
4+
import org.springframework.context.ApplicationEvent;
5+
6+
@Getter
7+
public class AccountDeletedEvent extends ApplicationEvent {
8+
private final String email;
9+
10+
public AccountDeletedEvent(Object source, String email) {
11+
super(source);
12+
this.email = email;
13+
}
14+
15+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.example.usermanagement.events.publishers.emails;
2+
3+
import lombok.Getter;
4+
import org.springframework.context.ApplicationEvent;
5+
6+
@Getter
7+
public class PasswordChangedEvent extends ApplicationEvent {
8+
private final String email;
9+
10+
public PasswordChangedEvent(Object source, String email) {
11+
super(source);
12+
this.email = email;
13+
}
14+
}

src/main/java/com/example/usermanagement/interfaces/services/IAccountService.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ public interface IAccountService {
2525
void verifyIdentity(boolean isVerified, Account account);
2626

2727
Account getMyAccount();
28+
void deleteMyAccount(String password);
2829
Account updateMyAccount(UpdateAccountDTO requestBody);
2930

3031
void resetPassword(String token, String newPassword);

src/main/java/com/example/usermanagement/services/AccountService.java

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,11 @@
77
import com.example.usermanagement.entities.Role;
88
import com.example.usermanagement.entities.Account;
99
import com.example.usermanagement.events.publishers.*;
10+
import com.example.usermanagement.events.publishers.emails.AccountDeletedEvent;
1011
import com.example.usermanagement.events.publishers.emails.PasswordHaveBeenResetedEvent;
11-
import com.example.usermanagement.exceptions.ForbiddenException;
12+
import com.example.usermanagement.exceptions.InputValidationException;
1213
import com.example.usermanagement.interfaces.services.IAccountService;
13-
import com.example.usermanagement.repositories.PermissionRepository;
14-
import com.example.usermanagement.repositories.RoleRepository;
15-
import com.example.usermanagement.repositories.AccountRepository;
14+
import com.example.usermanagement.repositories.*;
1615
import jakarta.persistence.EntityExistsException;
1716
import jakarta.persistence.EntityNotFoundException;
1817
import jakarta.transaction.Transactional;
@@ -36,9 +35,11 @@
3635
public class AccountService implements IAccountService {
3736

3837
private final AccountRepository accountRepository;
39-
private final PasswordEncoder passwordEncoder;
4038
private final RoleRepository roleRepository;
4139
private final PermissionRepository permissionRepository;
40+
private final EmailVerificationTokenRepository emailVerificationTokenRepository;
41+
private final PasswordResetTokenRepository passwordResetTokenRepository;
42+
private final PasswordEncoder passwordEncoder;
4243
private final ApplicationEventPublisher eventPublisher;
4344

4445
@Override
@@ -118,7 +119,7 @@ public void lockAccount(boolean lock, Account account) {
118119
public void changeMyPassword(String oldPassword, String newPassword) {
119120
Account account = getMyAccount();
120121
if (!passwordEncoder.matches(oldPassword, account.getPassword())) {
121-
throw new ForbiddenException("Old password is incorrect");
122+
throw new InputValidationException("Old password is incorrect");
122123
}
123124
// TODO: count tries and lock account if too many tries
124125

@@ -151,6 +152,38 @@ public Account getMyAccount() {
151152
return (Account) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
152153
}
153154

155+
@Override
156+
public void deleteMyAccount(String password) {
157+
Account account = getMyAccount();
158+
159+
// Validate password
160+
if (!passwordEncoder.matches(password, account.getPassword())) {
161+
throw new InputValidationException("Password is incorrect");
162+
}
163+
164+
// Clear associations
165+
account.getRoles().clear();
166+
account.getPermissions().clear();
167+
168+
// Remove and delete associated tokens
169+
if (account.getEmailVerificationToken() != null) {
170+
emailVerificationTokenRepository.delete(account.getEmailVerificationToken());
171+
account.setEmailVerificationToken(null);
172+
}
173+
174+
if (account.getPasswordResetToken() != null) {
175+
passwordResetTokenRepository.delete(account.getPasswordResetToken());
176+
account.setPasswordResetToken(null);
177+
}
178+
179+
// Delete the account
180+
accountRepository.delete(account);
181+
182+
// trigger event
183+
var event = new AccountDeletedEvent(this,account.getEmail());
184+
eventPublisher.publishEvent(event);
185+
}
186+
154187
@Override
155188
public Account updateMyAccount(UpdateAccountDTO requestBody) {
156189
Account account = getMyAccount();

src/main/java/com/example/usermanagement/services/EmailVerificationTokenService.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ public String generateEmailVerificationToken(Account account) {
5656
public String consumeEmailVerificationToken(String token) {
5757
var emailVerificationToken = emailVerificationTokenRepository.findByToken(token).orElseThrow(
5858
() -> new EntityNotFoundException("Email verification token not found"));
59-
59+
6060
if(emailVerificationToken.isExpired()) {
6161
throw new EmailVerificationTokenExpired("Email verification token has expired");
6262
}

0 commit comments

Comments
 (0)