diff --git a/src/main/java/com/mattrixwv/raidbuilder/config/SecurityConfig.java b/src/main/java/com/mattrixwv/raidbuilder/config/SecurityConfig.java
index b924272..7403ea1 100644
--- a/src/main/java/com/mattrixwv/raidbuilder/config/SecurityConfig.java
+++ b/src/main/java/com/mattrixwv/raidbuilder/config/SecurityConfig.java
@@ -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()))
diff --git a/src/main/java/com/mattrixwv/raidbuilder/controller/AuthenticationController.java b/src/main/java/com/mattrixwv/raidbuilder/controller/AuthenticationController.java
index c7034f0..1feccb9 100644
--- a/src/main/java/com/mattrixwv/raidbuilder/controller/AuthenticationController.java
+++ b/src/main/java/com/mattrixwv/raidbuilder/controller/AuthenticationController.java
@@ -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);
diff --git a/src/main/java/com/mattrixwv/raidbuilder/service/AccountService.java b/src/main/java/com/mattrixwv/raidbuilder/service/AccountService.java
index 034311a..b413274 100644
--- a/src/main/java/com/mattrixwv/raidbuilder/service/AccountService.java
+++ b/src/main/java/com/mattrixwv/raidbuilder/service/AccountService.java
@@ -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;
}
diff --git a/src/main/java/com/mattrixwv/raidbuilder/util/EmailUtil.java b/src/main/java/com/mattrixwv/raidbuilder/util/EmailUtil.java
new file mode 100644
index 0000000..21f73d6
--- /dev/null
+++ b/src/main/java/com/mattrixwv/raidbuilder/util/EmailUtil.java
@@ -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",
+ "This is the confirmation email for your Raid Builder Account.
Click Here to confirm your account.
"
+ );
+ }
+
+ public void sendPasswordResetEmail(InternetAddress toAddress, UUID resetToken) throws MessagingException{
+ sendMessage(
+ createSession(),
+ toAddress,
+ fromAddress,
+ "Raid Builder Password Reset",
+ "This is the password reset email for your Raid Builder Account.
Click Here to reset your password.
"
+ );
+ }
+
+
+ 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);
+ }
+}
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index 653ab35..3375097 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -23,3 +23,9 @@ jwt.refreshTokenDuration=30d
#Files
uploadFileDirectory=../raidBuilderIcons
spring.servlet.multipart.max-file-size=10MB
+
+
+#Email
+email.username=
+email.password=
+email.from=