Confirm and password emails sending

This commit is contained in:
2025-03-22 23:47:17 -04:00
parent b6fb636ddd
commit 01fca599ac
7 changed files with 131 additions and 17 deletions

View File

@@ -1,7 +1,7 @@
CREATE TABLE raid_builder.raid_instance_person_character_xref(
raid_instance_person_character_xref_id uuid PRIMARY KEY,
raid_instance_id uuid REFERENCES raid_builder.raid_instance(raid_instance_id) NOT NULL,
person_character_id uuid REFERENCES raid_builder.person_character(person_character_id) NOT NULL,
person_character_id uuid REFERENCES raid_builder.person_character(person_character_id),
group_number int NOT NULL,
position_number int NOT NULL,

12
pom.xml
View File

@@ -140,6 +140,18 @@
<artifactId>disruptor</artifactId>
<version>4.0.0</version>
</dependency>
<!--! Email -->
<dependency>
<groupId>org.eclipse.angus</groupId>
<artifactId>angus-mail</artifactId>
<version>2.0.3</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.18.0</version>
</dependency>
</dependencies>
<build>

View File

@@ -46,8 +46,8 @@ public class SecurityConfig{
auth.requestMatchers(HttpMethod.OPTIONS).permitAll()
.requestMatchers("/icons/**").permitAll()
.requestMatchers("/auth/refresh", "/auth/test").permitAll() //Permit refresh tokens
.requestMatchers(HttpMethod.POST, "/auth/signup", "/auth/confirm").permitAll() //Permit signup operations
.requestMatchers("/auth/forgot", "/auth/forgot/*").permitAll() //Permit forgot password operations
.requestMatchers(HttpMethod.POST, "/auth/signup", "/auth/confirm/*").permitAll() //Permit signup operations
.requestMatchers(HttpMethod.POST, "/auth/forgot", "/auth/forgot/*").permitAll() //Permit forgot password operations
.anyRequest().authenticated();
})
.oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults()))

View File

@@ -27,7 +27,9 @@ import com.mattrixwv.raidbuilder.entity.Account;
import com.mattrixwv.raidbuilder.service.AccountService;
import com.mattrixwv.raidbuilder.util.DatabaseTypeUtil.AccountPermissionType;
import com.mattrixwv.raidbuilder.util.DatabaseTypeUtil.AccountStatus;
import com.mattrixwv.raidbuilder.util.EmailUtil;
import jakarta.mail.internet.InternetAddress;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
@@ -44,6 +46,8 @@ public class AuthenticationController{
private final PasswordEncoder passwordEncoder;
private final TokenService tokenService;
private final AccountService accountService;
//Email
private final EmailUtil emailUtil;
@GetMapping("/token")
@@ -121,8 +125,6 @@ public class AuthenticationController{
returnNode.put("accountId", account.getAccountId().toString());
returnNode.put("status", "success");
//TODO: Send email
log.info("Successfully created account: {}", account.getAccountId());
}
else{
@@ -136,10 +138,9 @@ public class AuthenticationController{
return returnNode;
}
@PostMapping("/confirm")
@PostMapping("/confirm/{confirmToken}")
@AccountAuthorization(permissions = {})
public ObjectNode confirm(@RequestBody ObjectNode confirmNode){
UUID confirmToken = UUID.fromString(confirmNode.get("confirmToken").asText());
public ObjectNode confirm(@PathVariable("confirmToken") UUID confirmToken){
log.info("Confirming account with token {}", confirmToken);
@@ -164,14 +165,14 @@ public class AuthenticationController{
@PostMapping("/forgot")
@AccountAuthorization(permissions = {})
public ObjectNode forgot(@RequestParam("loginId") String loginId){
log.info("Setting up user that forgot their password: {}", loginId);
public ObjectNode forgot(@RequestParam("username") String username){
log.info("Setting up user that forgot their password: {}", username);
ObjectNode returnNode = mapper.createObjectNode();
//Verify the account exists
Account account = accountService.getByUsername(loginId);
Account account = accountService.getByUsername(username);
if(account != null){
//Setup token
UUID token = UUID.randomUUID();
@@ -181,24 +182,29 @@ public class AuthenticationController{
account = accountService.updateAccount(account);
//TODO: Send email
try{
emailUtil.sendPasswordResetEmail(new InternetAddress(account.getEmail()), token);
}
catch(Exception error){
log.error("Error sending email", error);
throw new RuntimeException("Error sending reset email", error);
}
returnNode.put("status", "success");
}
else{
ArrayNode errorsNode = mapper.createArrayNode();
errorsNode.add("Could not find account with login " + loginId);
errorsNode.add("Could not find account with login " + username);
returnNode.set("errors", errorsNode);
}
return returnNode;
}
@PostMapping("/forgot/reset")
@PostMapping("/forgot/{forgotToken}")
@AccountAuthorization(permissions = {})
public ObjectNode setNewPasswordForgot(@RequestBody ObjectNode forgotNode){
UUID forgotToken = UUID.fromString(forgotNode.get("forgotToken").asText());
String newPassword = forgotNode.get("password").asText();
public ObjectNode setNewPasswordForgot(@PathVariable("forgotToken") UUID forgotToken, @RequestBody ObjectNode passwordNode){
String newPassword = passwordNode.get("password").asText();
log.info("Confirming user reset password (forget) with token {}", forgotToken);

View File

@@ -23,11 +23,15 @@ import com.mattrixwv.raidbuilder.repository.account.AccountRepository;
import com.mattrixwv.raidbuilder.util.DatabaseTypeUtil.AccountPermissionType;
import com.mattrixwv.raidbuilder.util.DatabaseTypeUtil.AccountStatus;
import com.mattrixwv.raidbuilder.util.DatabaseTypeUtil.TutorialStatus;
import com.mattrixwv.raidbuilder.util.EmailUtil;
import com.mattrixwv.raidbuilder.util.UserPrincipal;
import jakarta.mail.internet.InternetAddress;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Service
@Transactional(rollbackFor = Exception.class)
@RequiredArgsConstructor
@@ -39,6 +43,8 @@ public class AccountService implements UserDetailsService{
private final AccountPermissionService accountPermissionService;
private final GamePermissionService gamePermissionService;
private final RaidGroupPermissionService raidGroupPermissionService;
//Emails
private final EmailUtil emailUtil;
//Fields
@Value("${jwt.refreshTokenDuration}")
private Duration refreshTokenDuration;
@@ -71,6 +77,16 @@ public class AccountService implements UserDetailsService{
accountTutorialStatus.setInstanceTutorialStatus(TutorialStatus.NOT_COMPLETED);
accountTutorialStatus = accountTutorialStatusService.createAccountTutorialStatus(accountTutorialStatus);
//Send confirmation email
try{
emailUtil.sendConfirmationEmail(new InternetAddress(account.getEmail()), account.getRefreshToken());
}
catch(Exception error){
log.error(error.getMessage(), error);
throw new RuntimeException("Could not send confirmation email", error);
}
//Return the new account
return account;
}

View File

@@ -0,0 +1,74 @@
package com.mattrixwv.raidbuilder.util;
import java.util.Properties;
import java.util.UUID;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import jakarta.mail.Message;
import jakarta.mail.MessagingException;
import jakarta.mail.Session;
import jakarta.mail.Transport;
import jakarta.mail.internet.InternetAddress;
import jakarta.mail.internet.MimeMessage;
@Component
public class EmailUtil{
@Value("${email.username}")
private String username;
@Value("${email.password}")
private String password;
@Value("${email.from}")
private InternetAddress fromAddress;
public Session createSession(){
Properties props = new Properties();
props.put("mail.smtp.host", "smtp.gmail.com");
props.put("mail.smtp.port", "465");
props.put("mail.smtp.auth", "true");
props.put("mail.smtp.socketFactory.port", "465");
props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
return Session.getInstance(props, new jakarta.mail.Authenticator(){
@Override
protected jakarta.mail.PasswordAuthentication getPasswordAuthentication(){
return new jakarta.mail.PasswordAuthentication(username, password);
}
});
}
public void sendConfirmationEmail(InternetAddress toAddress, UUID confirmationToken) throws MessagingException{
sendMessage(
createSession(),
toAddress,
fromAddress,
"Raid Builder Confirmation",
"<p>This is the confirmation email for your Raid Builder Account.<br/><a href=https://raidbuilder.mattrixwv.com/confirm/" + confirmationToken.toString() + ">Click Here</a> to confirm your account.</p>"
);
}
public void sendPasswordResetEmail(InternetAddress toAddress, UUID resetToken) throws MessagingException{
sendMessage(
createSession(),
toAddress,
fromAddress,
"Raid Builder Password Reset",
"<p>This is the password reset email for your Raid Builder Account.<br/><a href=https://raidBuilder.mattrixwv.com/forgotPassword/" + resetToken.toString() + ">Click Here</a> to reset your password.</p>"
);
}
public static void sendMessage(Session session, InternetAddress toAddress, InternetAddress fromAddress, String subject, String body) throws MessagingException{
MimeMessage message = new MimeMessage(session);
message.setFrom(fromAddress);
message.setRecipient(Message.RecipientType.TO, toAddress);
message.setSubject(subject);
message.setText(body, "utf-8", "html");
Transport.send(message);
}
}

View File

@@ -23,3 +23,9 @@ jwt.refreshTokenDuration=30d
#Files
uploadFileDirectory=../raidBuilderIcons
spring.servlet.multipart.max-file-size=10MB
#Email
email.username=
email.password=
email.from=