Skip to content

Commit

Permalink
email handling and sht
Browse files Browse the repository at this point in the history
  • Loading branch information
Wassim-Rached committed Oct 31, 2024
1 parent c16743a commit 12998bb
Show file tree
Hide file tree
Showing 10 changed files with 150 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,13 @@ public ResponseEntity<GeneralAccountDTO> updateMe(@RequestBody UpdateAccountDTO
return new ResponseEntity<>(new GeneralAccountDTO(account), HttpStatus.OK);
}

// with password
@DeleteMapping("/me")
public ResponseEntity<String> deleteMe(@RequestParam String password) {
accountService.deleteMyAccount(password);
return new ResponseEntity<>("Account deleted", HttpStatus.OK);
}

@GetMapping("/{accountId}")
public ResponseEntity<DetailedAccountDTO> getAccount(@PathVariable UUID accountId) {
var account = new DetailedAccountDTO(accountService.getAccountById(accountId));
Expand All @@ -86,7 +93,7 @@ public ResponseEntity<List<AccountAuthoritiesEditResponse>> editAuthorities(@Req
public ResponseEntity<String> verifyEmail(@RequestParam String token) {
String email = emailVerificationTokenService.consumeEmailVerificationToken(token);
accountService.verifyAccountEmail(email);
return new ResponseEntity<>("Email verified", HttpStatus.OK);
return new ResponseEntity<>(email + " verified", HttpStatus.OK);
}

@PostMapping("/verify-email/resend")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,12 @@ public class Account {
@Column(nullable = false, name = "is_member", columnDefinition = "boolean default false")
private Boolean isMember = false;

@OneToOne(mappedBy = "account", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private PasswordResetToken passwordResetToken;

@OneToOne(mappedBy = "account", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private EmailVerificationToken emailVerificationToken;

@ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
@JoinTable(
name = "account_roles",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,13 @@ public class EmailVerificationToken {
@Column(nullable = false, unique = true)
private String token;

@Column(nullable = false)
@Column(nullable = false,name = "expiry_date")
private Instant expiryDate;

@Column(nullable = false)
@Column(nullable = false,name = "created_date")
private Instant createdDate;

@Column(nullable = false)
@Column(nullable = false,name = "is_used")
private boolean isUsed;

@OneToOne(fetch = FetchType.LAZY)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
package com.example.usermanagement.events.listeners;


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

private final IEmailService emailService;

@EventListener(AccountCreatedEvent.class)
public void onAccountCreatedEvent(AccountCreatedEvent event) {
String email = event.getEmail();
String body = "Hello, " + email + "\n" +
"Your account have been created successfully." + "\n" +
"If you didn't request this, please contact us.";

emailService.sendEmail(event.getEmail(), "Account created", body);
}

@EventListener(EmailVerificationTokenGeneratedEvent.class)
public void onEmailVerificationTokenGeneratedEvent(EmailVerificationTokenGeneratedEvent event) {
String link = amwc + "/api/accounts/verify-email?token=" + event.getToken();
String body = "Click here to verify your email: " + link;
String link = amwc + "/auth/confirm-email-verification?token=" + event.getToken();
String email = event.getEmail();
String body = "Hello, " + email + "\n" +
"Click here to verify your email: " + link + "\n" +
"If you didn't request this, please ignore this email.";

emailService.sendEmail("wa55death405@gmail.com", "Email verification", body);
}

@EventListener(PasswordResetGeneratedEvent.class)
public void onPasswordResetGeneratedEvent(PasswordResetGeneratedEvent event) {
String link = amwc + "/api/accounts/reset-password?token=" + event.getToken();
String body = "Click here to reset your password: " + link;
String link = amwc + "/auth/confirm-reset-password?token=" + event.getToken();
String email = event.getEmail();
String body = "Hello, " + email + "\n" +
"Click here to reset your password: " + link + "\n" +
"If you didn't request this, please ignore this email.";

emailService.sendEmail(event.getEmail(), "Password reset", body);
}

@EventListener(PasswordHaveBeenResetedEvent.class)
public void onPasswordHaveBeenResetedEvent(PasswordHaveBeenResetedEvent event) {
String body = "Your password have been reseted";
String email = event.getEmail();
String body = "Hello, " + email + "\n" +
"Your password have been reseted successfully." + "\n" +
"If you didn't request this, please contact us.";

emailService.sendEmail(event.getEmail(), "Password reseted", body);
}

@EventListener(PasswordChangedEvent.class)
public void onPasswordChangedEvent(PasswordChangedEvent event) {
String email = event.getEmail();
String body = "Hello, " + email + "\n" +
"Your password have been changed successfully." + "\n" +
"If you didn't request this, please contact us.";

emailService.sendEmail(event.getEmail(), "Password changed", body);
}

@EventListener(AccountDeletedEvent.class)
public void onAccountDeletedEvent(AccountDeletedEvent event) {
String email = event.getEmail();
String body = "Hello, " + email + "\n" +
"Your account have been deleted successfully." + "\n" +
"If you didn't request this, please contact us.";

emailService.sendEmail(event.getEmail(), "Account deleted", body);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.example.usermanagement.events.publishers.emails;

import lombok.Getter;
import org.springframework.context.ApplicationEvent;

@Getter
public class AccountCreatedEvent extends ApplicationEvent {
private final String email;

public AccountCreatedEvent(Object source, String email) {
super(source);
this.email = email;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.example.usermanagement.events.publishers.emails;

import lombok.Getter;
import org.springframework.context.ApplicationEvent;

@Getter
public class AccountDeletedEvent extends ApplicationEvent {
private final String email;

public AccountDeletedEvent(Object source, String email) {
super(source);
this.email = email;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.example.usermanagement.events.publishers.emails;

import lombok.Getter;
import org.springframework.context.ApplicationEvent;

@Getter
public class PasswordChangedEvent extends ApplicationEvent {
private final String email;

public PasswordChangedEvent(Object source, String email) {
super(source);
this.email = email;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ public interface IAccountService {
void verifyIdentity(boolean isVerified, Account account);

Account getMyAccount();
void deleteMyAccount(String password);
Account updateMyAccount(UpdateAccountDTO requestBody);

void resetPassword(String token, String newPassword);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,11 @@
import com.example.usermanagement.entities.Role;
import com.example.usermanagement.entities.Account;
import com.example.usermanagement.events.publishers.*;
import com.example.usermanagement.events.publishers.emails.AccountDeletedEvent;
import com.example.usermanagement.events.publishers.emails.PasswordHaveBeenResetedEvent;
import com.example.usermanagement.exceptions.ForbiddenException;
import com.example.usermanagement.exceptions.InputValidationException;
import com.example.usermanagement.interfaces.services.IAccountService;
import com.example.usermanagement.repositories.PermissionRepository;
import com.example.usermanagement.repositories.RoleRepository;
import com.example.usermanagement.repositories.AccountRepository;
import com.example.usermanagement.repositories.*;
import jakarta.persistence.EntityExistsException;
import jakarta.persistence.EntityNotFoundException;
import jakarta.transaction.Transactional;
Expand All @@ -36,9 +35,11 @@
public class AccountService implements IAccountService {

private final AccountRepository accountRepository;
private final PasswordEncoder passwordEncoder;
private final RoleRepository roleRepository;
private final PermissionRepository permissionRepository;
private final EmailVerificationTokenRepository emailVerificationTokenRepository;
private final PasswordResetTokenRepository passwordResetTokenRepository;
private final PasswordEncoder passwordEncoder;
private final ApplicationEventPublisher eventPublisher;

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

Expand Down Expand Up @@ -151,6 +152,38 @@ public Account getMyAccount() {
return (Account) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
}

@Override
public void deleteMyAccount(String password) {
Account account = getMyAccount();

// Validate password
if (!passwordEncoder.matches(password, account.getPassword())) {
throw new InputValidationException("Password is incorrect");
}

// Clear associations
account.getRoles().clear();
account.getPermissions().clear();

// Remove and delete associated tokens
if (account.getEmailVerificationToken() != null) {
emailVerificationTokenRepository.delete(account.getEmailVerificationToken());
account.setEmailVerificationToken(null);
}

if (account.getPasswordResetToken() != null) {
passwordResetTokenRepository.delete(account.getPasswordResetToken());
account.setPasswordResetToken(null);
}

// Delete the account
accountRepository.delete(account);

// trigger event
var event = new AccountDeletedEvent(this,account.getEmail());
eventPublisher.publishEvent(event);
}

@Override
public Account updateMyAccount(UpdateAccountDTO requestBody) {
Account account = getMyAccount();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public String generateEmailVerificationToken(Account account) {
public String consumeEmailVerificationToken(String token) {
var emailVerificationToken = emailVerificationTokenRepository.findByToken(token).orElseThrow(
() -> new EntityNotFoundException("Email verification token not found"));

if(emailVerificationToken.isExpired()) {
throw new EmailVerificationTokenExpired("Email verification token has expired");
}
Expand Down

0 comments on commit 12998bb

Please sign in to comment.