From 9855a18ecb06157fc5c4fed6db259b1ca7627b7b Mon Sep 17 00:00:00 2001 From: Gabriela Aguirre Date: Thu, 9 Apr 2026 13:48:32 +0200 Subject: [PATCH 01/21] Add name, displayName correctly --- .../backendlab/team4you/user/UserEntity.java | 30 ++++++++++++++----- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/src/main/java/backendlab/team4you/user/UserEntity.java b/src/main/java/backendlab/team4you/user/UserEntity.java index da6f05d..8f1fea3 100644 --- a/src/main/java/backendlab/team4you/user/UserEntity.java +++ b/src/main/java/backendlab/team4you/user/UserEntity.java @@ -4,6 +4,7 @@ import jakarta.persistence.Entity; import jakarta.persistence.Id; import jakarta.persistence.Table; +import org.jspecify.annotations.Nullable; import org.springframework.security.web.webauthn.api.Bytes; import org.springframework.security.web.webauthn.api.PublicKeyCredentialUserEntity; @@ -18,9 +19,15 @@ public class UserEntity implements PublicKeyCredentialUserEntity { private String id; @Column(name = "name", nullable = false, unique = true) + private String name; + + @Column(name = "displayName") + private String displayName; + + @Column(name = "email") private String email; - @Column(name = "display_name") + @Column(name = "first_name") private String firstName; @Column(name = "last_name") @@ -41,10 +48,10 @@ public class UserEntity implements PublicKeyCredentialUserEntity { public UserEntity() { } - public UserEntity(Bytes id, String email, String firstName) { + public UserEntity(Bytes id, String name, String displayName) { this.id = id != null ? id.toBase64UrlString() : null; - this.email = email; - this.firstName = firstName; + this.name = name; + this.displayName = displayName; } @Override @@ -58,14 +65,21 @@ public void setId(Bytes id) { @Override public String getName() { - return this.email; + return name; + } + + public void setName(String name) { + this.name = name; } @Override - public String getDisplayName() { - return (this.firstName != null ? this.firstName : "") + " " + (this.lastName != null ? this.lastName : ""); + public @Nullable String getDisplayName() { + return displayName; + } + + public void setDisplayName(String displayName) { + this.displayName = displayName; } - public LocalDateTime getCreatedAt() { return createdAt; From 50fc9c8e3bc4ef358890d82c8da329b103cc12de Mon Sep 17 00:00:00 2001 From: Gabriela Aguirre Date: Thu, 9 Apr 2026 14:20:08 +0200 Subject: [PATCH 02/21] Create v7 migration to add column email and first_name --- src/main/java/backendlab/team4you/user/UserEntity.java | 2 +- .../db/migration/V7__add_column_email_and_firstname.sql | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 src/main/resources/db/migration/V7__add_column_email_and_firstname.sql diff --git a/src/main/java/backendlab/team4you/user/UserEntity.java b/src/main/java/backendlab/team4you/user/UserEntity.java index 8f1fea3..422dfe0 100644 --- a/src/main/java/backendlab/team4you/user/UserEntity.java +++ b/src/main/java/backendlab/team4you/user/UserEntity.java @@ -21,7 +21,7 @@ public class UserEntity implements PublicKeyCredentialUserEntity { @Column(name = "name", nullable = false, unique = true) private String name; - @Column(name = "displayName") + @Column(name = "display_name") private String displayName; @Column(name = "email") diff --git a/src/main/resources/db/migration/V7__add_column_email_and_firstname.sql b/src/main/resources/db/migration/V7__add_column_email_and_firstname.sql new file mode 100644 index 0000000..d7e528a --- /dev/null +++ b/src/main/resources/db/migration/V7__add_column_email_and_firstname.sql @@ -0,0 +1,3 @@ +ALTER TABLE user_entities + ADD COLUMN IF NOT EXISTS email VARCHAR(255), + ADD COLUMN IF NOT EXISTS first_name VARCHAR(255); From 8c4da1ebf7fa71e604e3e4d5cce7d4c1156a345c Mon Sep 17 00:00:00 2001 From: Gabriela Aguirre Date: Thu, 9 Apr 2026 16:49:10 +0200 Subject: [PATCH 03/21] Add findByName instead of findByEmail in registration controller, user repository and user service --- .../team4you/controller/RegistrationController.java | 4 ++-- .../java/backendlab/team4you/repository/UserRepository.java | 2 ++ src/main/java/backendlab/team4you/user/UserService.java | 4 ++++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/main/java/backendlab/team4you/controller/RegistrationController.java b/src/main/java/backendlab/team4you/controller/RegistrationController.java index ea7382e..f8b23f2 100644 --- a/src/main/java/backendlab/team4you/controller/RegistrationController.java +++ b/src/main/java/backendlab/team4you/controller/RegistrationController.java @@ -68,8 +68,8 @@ public String loginPage(@RequestParam(value = "registered", required = false) St public String welcome(Model model, Principal principal) { if (principal == null) return "redirect:/login"; // Säkerhetskoll - String email = principal.getName(); - UserEntity user = userService.findByEmail(email); + String name = principal.getName(); + UserEntity user = userService.findByName(name); if (user == null) { diff --git a/src/main/java/backendlab/team4you/repository/UserRepository.java b/src/main/java/backendlab/team4you/repository/UserRepository.java index b6da172..ed21df3 100644 --- a/src/main/java/backendlab/team4you/repository/UserRepository.java +++ b/src/main/java/backendlab/team4you/repository/UserRepository.java @@ -10,4 +10,6 @@ @Repository public interface UserRepository extends JpaRepository { Optional findByEmail(String email); + + Optional findByName(String name); } diff --git a/src/main/java/backendlab/team4you/user/UserService.java b/src/main/java/backendlab/team4you/user/UserService.java index 3f42fff..5df4dd5 100644 --- a/src/main/java/backendlab/team4you/user/UserService.java +++ b/src/main/java/backendlab/team4you/user/UserService.java @@ -79,6 +79,10 @@ public UserEntity findByEmail(String email) { return userRepository.findByEmail(email).orElse(null); } + public UserEntity findByName(String name){ + return userRepository.findByName(name).orElse(null); + } + @Transactional public void deleteUser(String id) { if (!userRepository.existsById(id)) { From 072a7209c6dddef93b27948e1caec7bc599586b8 Mon Sep 17 00:00:00 2001 From: Gabriela Aguirre Date: Thu, 9 Apr 2026 16:53:48 +0200 Subject: [PATCH 04/21] Fix with code rabbit --- src/main/java/backendlab/team4you/config/SecurityConfig.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/backendlab/team4you/config/SecurityConfig.java b/src/main/java/backendlab/team4you/config/SecurityConfig.java index 6d50f81..7a41a54 100644 --- a/src/main/java/backendlab/team4you/config/SecurityConfig.java +++ b/src/main/java/backendlab/team4you/config/SecurityConfig.java @@ -68,13 +68,13 @@ UserCredentialRepository userCredentialRepository(JdbcOperations jdbc) { @Bean public UserDetailsService userDetailsService(UserService userService){ return username -> { - UserEntity user = userService.findByEmail(username); + UserEntity user = userService.findByName(username); if (user == null) { throw new UsernameNotFoundException("User not found: " + username); } return User.builder() - .username(user.getEmail()) + .username(user.getName()) .password(user.getPasswordHash()) .roles(user.getRole()) .accountLocked(false) From a0fd1b95efbff7616239999c1f785fc906e6364f Mon Sep 17 00:00:00 2001 From: Gabriela Aguirre Date: Thu, 9 Apr 2026 16:56:05 +0200 Subject: [PATCH 05/21] Fix SignupController to set email, firstName and lastName when creating users via the 3-arg constructor --- .../team4you/controller/SignupController.java | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/main/java/backendlab/team4you/controller/SignupController.java b/src/main/java/backendlab/team4you/controller/SignupController.java index ea427ee..375d289 100644 --- a/src/main/java/backendlab/team4you/controller/SignupController.java +++ b/src/main/java/backendlab/team4you/controller/SignupController.java @@ -75,6 +75,10 @@ public void signup(@RequestBody SignupRequest req, HttpServletRequest request, H req.displayName ); + userEntity.setEmail(req.email); + userEntity.setFirstName(req.firstName); + userEntity.setLastName(req.lastName); + String assignedRole = req.getUsername().endsWith("@team4you.com") ? "ADMIN" : "USER"; userEntity.setRole(assignedRole); @@ -93,6 +97,9 @@ public void signup(@RequestBody SignupRequest req, HttpServletRequest request, H public static class SignupRequest { private String username; private String displayName; + private String email; + private String firstName; + private String lastName; public SignupRequest() { } @@ -112,6 +119,30 @@ public String getDisplayName() { public void setDisplayName(String displayName) { this.displayName = displayName; } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } } From 95defb793fb0c6b1af7cf68a582b7fb7b67ef143 Mon Sep 17 00:00:00 2001 From: Gabriela Aguirre Date: Thu, 9 Apr 2026 17:03:50 +0200 Subject: [PATCH 06/21] Fix findByName instead if findbyemail --- .../team4you/config/CustomAuthenticationSuccessHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/backendlab/team4you/config/CustomAuthenticationSuccessHandler.java b/src/main/java/backendlab/team4you/config/CustomAuthenticationSuccessHandler.java index ef03e35..9827d6c 100644 --- a/src/main/java/backendlab/team4you/config/CustomAuthenticationSuccessHandler.java +++ b/src/main/java/backendlab/team4you/config/CustomAuthenticationSuccessHandler.java @@ -32,7 +32,7 @@ public void onAuthenticationSuccess(HttpServletRequest request, String username = authentication.getName(); - var userEntity = userService.findByEmail(username); + var userEntity = userService.findByName(username); if (userEntity != null){ var credentials = userCredentialRepository.findByUserId(userEntity.getId()); From 36be9e73bb0e68cca843011fec555ea2cc56d13b Mon Sep 17 00:00:00 2001 From: Gabriela Aguirre Date: Thu, 9 Apr 2026 17:19:30 +0200 Subject: [PATCH 07/21] =?UTF-8?q?Change=20e-post=20for=20anv=C3=A4ndarnamn?= =?UTF-8?q?.=20Add=20dev=20user.=20Add=20new=20migration?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../team4you/Team4youApplication.java | 27 +++++++++++++++++++ .../team4you/webauthn/WebAuthnCredential.java | 4 +-- .../V7__add_column_email_and_firstname.sql | 3 +++ src/main/resources/templates/login.html | 6 ++--- 4 files changed, 35 insertions(+), 5 deletions(-) diff --git a/src/main/java/backendlab/team4you/Team4youApplication.java b/src/main/java/backendlab/team4you/Team4youApplication.java index ad5d84e..a1bc395 100644 --- a/src/main/java/backendlab/team4you/Team4youApplication.java +++ b/src/main/java/backendlab/team4you/Team4youApplication.java @@ -1,7 +1,14 @@ package backendlab.team4you; +import backendlab.team4you.repository.UserRepository; +import backendlab.team4you.user.UserEntity; +import org.springframework.boot.ApplicationRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Profile; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.web.webauthn.api.Bytes; @SpringBootApplication public class Team4youApplication { @@ -10,4 +17,24 @@ public static void main(String[] args) { SpringApplication.run(Team4youApplication.class, args); } + @Bean + @Profile("dev") + ApplicationRunner init(UserRepository repository, BCryptPasswordEncoder encoder) { + return args -> { + if (repository.count() == 0) { + + UserEntity devUser = new UserEntity( + Bytes.fromBase64("01"), + "dev", // name (username) + "Developer" // displayName + ); + + devUser.setPasswordHash(encoder.encode("123456")); + devUser.setRole("ADMIN"); + devUser.setEmail("dev@team4you.com"); + + repository.save(devUser); + } + }; + } } diff --git a/src/main/java/backendlab/team4you/webauthn/WebAuthnCredential.java b/src/main/java/backendlab/team4you/webauthn/WebAuthnCredential.java index 6dccd34..6ef9d14 100644 --- a/src/main/java/backendlab/team4you/webauthn/WebAuthnCredential.java +++ b/src/main/java/backendlab/team4you/webauthn/WebAuthnCredential.java @@ -18,10 +18,10 @@ public class WebAuthnCredential implements CredentialRecord { @Id - @Column(name = "id", length = 512) + @Column(name = "credential_id", length = 512) private String credentialId; - @Column(name = "user_entity_id", length = 255) + @Column(name = "user_entity_user_id", length = 255) private String userEntityUserId; @Column(name = "public_key", columnDefinition = "bytea") diff --git a/src/main/resources/db/migration/V7__add_column_email_and_firstname.sql b/src/main/resources/db/migration/V7__add_column_email_and_firstname.sql index d7e528a..3963b3e 100644 --- a/src/main/resources/db/migration/V7__add_column_email_and_firstname.sql +++ b/src/main/resources/db/migration/V7__add_column_email_and_firstname.sql @@ -1,3 +1,6 @@ +ALTER TABLE user_credentials RENAME COLUMN id TO credential_id; +ALTER TABLE user_credentials RENAME COLUMN user_entity_id TO user_entity_user_id; + ALTER TABLE user_entities ADD COLUMN IF NOT EXISTS email VARCHAR(255), ADD COLUMN IF NOT EXISTS first_name VARCHAR(255); diff --git a/src/main/resources/templates/login.html b/src/main/resources/templates/login.html index cf7a4f1..a693ffe 100644 --- a/src/main/resources/templates/login.html +++ b/src/main/resources/templates/login.html @@ -10,8 +10,8 @@

Logga in

- - + + @@ -27,4 +27,4 @@

Logga in

- \ No newline at end of file + From 22bd5fc8ea51e8c2f4686231be7d983574713a54 Mon Sep 17 00:00:00 2001 From: Gabriela Aguirre Date: Fri, 10 Apr 2026 15:46:33 +0200 Subject: [PATCH 08/21] Refactor flyway migration from v7 to v9 --- ...l_and_firstname.sql => V9__add_column_email_and_firstname.sql} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/main/resources/db/migration/{V7__add_column_email_and_firstname.sql => V9__add_column_email_and_firstname.sql} (100%) diff --git a/src/main/resources/db/migration/V7__add_column_email_and_firstname.sql b/src/main/resources/db/migration/V9__add_column_email_and_firstname.sql similarity index 100% rename from src/main/resources/db/migration/V7__add_column_email_and_firstname.sql rename to src/main/resources/db/migration/V9__add_column_email_and_firstname.sql From 4d750ffd7f6f9440f6821a3b7c8b5557df1e13d4 Mon Sep 17 00:00:00 2001 From: Gabriela Aguirre Date: Fri, 10 Apr 2026 16:15:57 +0200 Subject: [PATCH 09/21] Change role admin for user instead after code rabbit comment --- src/main/java/backendlab/team4you/Team4youApplication.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/backendlab/team4you/Team4youApplication.java b/src/main/java/backendlab/team4you/Team4youApplication.java index a1bc395..503f2b3 100644 --- a/src/main/java/backendlab/team4you/Team4youApplication.java +++ b/src/main/java/backendlab/team4you/Team4youApplication.java @@ -30,8 +30,8 @@ ApplicationRunner init(UserRepository repository, BCryptPasswordEncoder encoder) ); devUser.setPasswordHash(encoder.encode("123456")); - devUser.setRole("ADMIN"); - devUser.setEmail("dev@team4you.com"); + devUser.setRole("USER"); + devUser.setEmail("dev@gmail.com"); repository.save(devUser); } From 34b9d4974b0a11ae44724fe3acde55ca47331bfa Mon Sep 17 00:00:00 2001 From: Gabriela Aguirre Date: Fri, 10 Apr 2026 16:21:03 +0200 Subject: [PATCH 10/21] Fix after code rabbit --- .../team4you/controller/RegistrationController.java | 2 +- .../java/backendlab/team4you/controller/SignupController.java | 4 ++-- .../java/backendlab/team4you/dto/UserRegistrationDTO.java | 4 +++- src/main/java/backendlab/team4you/mapper/UserMapper.java | 2 +- src/main/java/backendlab/team4you/user/UserService.java | 3 ++- 5 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/main/java/backendlab/team4you/controller/RegistrationController.java b/src/main/java/backendlab/team4you/controller/RegistrationController.java index f8b23f2..2cb1c11 100644 --- a/src/main/java/backendlab/team4you/controller/RegistrationController.java +++ b/src/main/java/backendlab/team4you/controller/RegistrationController.java @@ -25,7 +25,7 @@ public RegistrationController(UserService userService){ @GetMapping("/register") public String showRegistrationForm(Model model) { - model.addAttribute("user", new UserRegistrationDTO("", "", "", "", "", "")); + model.addAttribute("user", new UserRegistrationDTO("","", "", "", "", "", "")); return "register"; } diff --git a/src/main/java/backendlab/team4you/controller/SignupController.java b/src/main/java/backendlab/team4you/controller/SignupController.java index 375d289..4de572a 100644 --- a/src/main/java/backendlab/team4you/controller/SignupController.java +++ b/src/main/java/backendlab/team4you/controller/SignupController.java @@ -79,13 +79,13 @@ public void signup(@RequestBody SignupRequest req, HttpServletRequest request, H userEntity.setFirstName(req.firstName); userEntity.setLastName(req.lastName); - String assignedRole = req.getUsername().endsWith("@team4you.com") ? "ADMIN" : "USER"; + String assignedRole = req.getEmail().endsWith("@team4you.com") ? "ADMIN" : "USER"; userEntity.setRole(assignedRole); users.save(userEntity); Authentication auth = new UsernamePasswordAuthenticationToken( - userEntity.getName(), null, List.of(new SimpleGrantedAuthority("ROLE_USER"))); + userEntity.getEmail(), null, List.of(new SimpleGrantedAuthority("ROLE_USER"))); SecurityContext context = SecurityContextHolder.createEmptyContext(); context.setAuthentication(auth); diff --git a/src/main/java/backendlab/team4you/dto/UserRegistrationDTO.java b/src/main/java/backendlab/team4you/dto/UserRegistrationDTO.java index e89a711..2fd259e 100644 --- a/src/main/java/backendlab/team4you/dto/UserRegistrationDTO.java +++ b/src/main/java/backendlab/team4you/dto/UserRegistrationDTO.java @@ -2,7 +2,9 @@ import java.time.LocalDateTime; -public record UserRegistrationDTO( String firstName, +public record UserRegistrationDTO( + String name, + String firstName, String lastName, String email, String phoneNumber, diff --git a/src/main/java/backendlab/team4you/mapper/UserMapper.java b/src/main/java/backendlab/team4you/mapper/UserMapper.java index 8971c96..3bcd862 100644 --- a/src/main/java/backendlab/team4you/mapper/UserMapper.java +++ b/src/main/java/backendlab/team4you/mapper/UserMapper.java @@ -13,6 +13,7 @@ public static UserEntity toEntity(UserRegistrationDTO dto) { if (dto == null) return null; UserEntity entity = new UserEntity(); + entity.setName(dto.name()); entity.setFirstName(dto.firstName()); entity.setLastName(dto.lastName()); entity.setEmail(dto.email()); @@ -36,4 +37,3 @@ public static UserDTO toDto(UserEntity entity) { } - diff --git a/src/main/java/backendlab/team4you/user/UserService.java b/src/main/java/backendlab/team4you/user/UserService.java index 5df4dd5..0057e47 100644 --- a/src/main/java/backendlab/team4you/user/UserService.java +++ b/src/main/java/backendlab/team4you/user/UserService.java @@ -58,10 +58,11 @@ public UserEntity update(UserEntity userEntity){ public void registerUser(UserRegistrationDTO dto) { if (userRepository.findByEmail(dto.email()).isPresent()) { - throw new RuntimeException("E-posten är redan tagen"); + throw new DuplicateEmailException("E-posten är redan tagen"); } UserEntity user = new UserEntity(); + user.setName(dto.name()); user.setFirstName(dto.firstName()); user.setLastName(dto.lastName()); user.setEmail(dto.email()); From ab05d20fc905b50064c7129da18a8218ae462613 Mon Sep 17 00:00:00 2001 From: Gabriela Aguirre Date: Fri, 10 Apr 2026 16:25:12 +0200 Subject: [PATCH 11/21] Fixed conflict --- src/main/java/backendlab/team4you/Team4youApplication.java | 2 +- .../java/backendlab/team4you/controller/SignupController.java | 1 - .../team4you/{repository => user}/UserRepository.java | 3 +-- src/main/java/backendlab/team4you/user/UserService.java | 2 -- 4 files changed, 2 insertions(+), 6 deletions(-) rename src/main/java/backendlab/team4you/{repository => user}/UserRepository.java (80%) diff --git a/src/main/java/backendlab/team4you/Team4youApplication.java b/src/main/java/backendlab/team4you/Team4youApplication.java index 503f2b3..6f9009f 100644 --- a/src/main/java/backendlab/team4you/Team4youApplication.java +++ b/src/main/java/backendlab/team4you/Team4youApplication.java @@ -1,6 +1,6 @@ package backendlab.team4you; -import backendlab.team4you.repository.UserRepository; +import backendlab.team4you.user.UserRepository; import backendlab.team4you.user.UserEntity; import org.springframework.boot.ApplicationRunner; import org.springframework.boot.SpringApplication; diff --git a/src/main/java/backendlab/team4you/controller/SignupController.java b/src/main/java/backendlab/team4you/controller/SignupController.java index 4de572a..e84aace 100644 --- a/src/main/java/backendlab/team4you/controller/SignupController.java +++ b/src/main/java/backendlab/team4you/controller/SignupController.java @@ -2,7 +2,6 @@ import backendlab.team4you.user.UserEntity; import backendlab.team4you.user.UserService; -import backendlab.team4you.repository.UserRepository; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.springframework.http.HttpStatus; diff --git a/src/main/java/backendlab/team4you/repository/UserRepository.java b/src/main/java/backendlab/team4you/user/UserRepository.java similarity index 80% rename from src/main/java/backendlab/team4you/repository/UserRepository.java rename to src/main/java/backendlab/team4you/user/UserRepository.java index ed21df3..536ff8a 100644 --- a/src/main/java/backendlab/team4you/repository/UserRepository.java +++ b/src/main/java/backendlab/team4you/user/UserRepository.java @@ -1,7 +1,6 @@ -package backendlab.team4you.repository; +package backendlab.team4you.user; -import backendlab.team4you.user.UserEntity; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; diff --git a/src/main/java/backendlab/team4you/user/UserService.java b/src/main/java/backendlab/team4you/user/UserService.java index 0057e47..69580f0 100644 --- a/src/main/java/backendlab/team4you/user/UserService.java +++ b/src/main/java/backendlab/team4you/user/UserService.java @@ -3,9 +3,7 @@ import backendlab.team4you.dto.UserRegistrationDTO; import backendlab.team4you.exceptions.DuplicateEmailException; import backendlab.team4you.exceptions.UserNotFoundException; -import backendlab.team4you.repository.UserRepository; import jakarta.transaction.Transactional; -import org.springframework.security.core.userdetails.User; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.stereotype.Service; From 6715a1cb5fb01842924af84100313688ad4f1566 Mon Sep 17 00:00:00 2001 From: Gabriela Aguirre Date: Fri, 10 Apr 2026 16:36:06 +0200 Subject: [PATCH 12/21] Use username as principal to match the new authentication contract --- .../java/backendlab/team4you/controller/SignupController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/backendlab/team4you/controller/SignupController.java b/src/main/java/backendlab/team4you/controller/SignupController.java index e84aace..b4cf816 100644 --- a/src/main/java/backendlab/team4you/controller/SignupController.java +++ b/src/main/java/backendlab/team4you/controller/SignupController.java @@ -84,7 +84,7 @@ public void signup(@RequestBody SignupRequest req, HttpServletRequest request, H users.save(userEntity); Authentication auth = new UsernamePasswordAuthenticationToken( - userEntity.getEmail(), null, List.of(new SimpleGrantedAuthority("ROLE_USER"))); + userEntity.getName(), null, List.of(new SimpleGrantedAuthority("ROLE_USER"))); SecurityContext context = SecurityContextHolder.createEmptyContext(); context.setAuthentication(auth); From b0459bf16ecb46bb305d6f15756beffd54c664d7 Mon Sep 17 00:00:00 2001 From: Gabriela Aguirre Date: Fri, 10 Apr 2026 16:38:44 +0200 Subject: [PATCH 13/21] Add validate and pre-check username uniqueness before persisting in registeruser in user service class --- src/main/java/backendlab/team4you/user/UserService.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main/java/backendlab/team4you/user/UserService.java b/src/main/java/backendlab/team4you/user/UserService.java index 69580f0..db8c5c1 100644 --- a/src/main/java/backendlab/team4you/user/UserService.java +++ b/src/main/java/backendlab/team4you/user/UserService.java @@ -55,12 +55,19 @@ public UserEntity update(UserEntity userEntity){ public void registerUser(UserRegistrationDTO dto) { + if (dto.name() == null || dto.name().isBlank()) { + throw new IllegalArgumentException("Username is required"); + } + if (userRepository.findByName(dto.name().trim()).isPresent()) { + throw new IllegalArgumentException("Username already exists"); + } + if (userRepository.findByEmail(dto.email()).isPresent()) { throw new DuplicateEmailException("E-posten är redan tagen"); } UserEntity user = new UserEntity(); - user.setName(dto.name()); + user.setName(dto.name().trim()); user.setFirstName(dto.firstName()); user.setLastName(dto.lastName()); user.setEmail(dto.email()); From a8f2da353b889d4feb1d3b88055dd3c96193dde6 Mon Sep 17 00:00:00 2001 From: Gabriela Aguirre Date: Fri, 10 Apr 2026 16:42:35 +0200 Subject: [PATCH 14/21] Add a DB-level uniqueness guard for email --- .../db/migration/V9__add_column_email_and_firstname.sql | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/resources/db/migration/V9__add_column_email_and_firstname.sql b/src/main/resources/db/migration/V9__add_column_email_and_firstname.sql index 3963b3e..6a50ee2 100644 --- a/src/main/resources/db/migration/V9__add_column_email_and_firstname.sql +++ b/src/main/resources/db/migration/V9__add_column_email_and_firstname.sql @@ -4,3 +4,7 @@ ALTER TABLE user_credentials RENAME COLUMN user_entity_id TO user_entity_user_id ALTER TABLE user_entities ADD COLUMN IF NOT EXISTS email VARCHAR(255), ADD COLUMN IF NOT EXISTS first_name VARCHAR(255); + +CREATE UNIQUE INDEX IF NOT EXISTS ux_user_entities_email + ON user_entities (email) + WHERE email IS NOT NULL; From 7e3d189c7aecb1728bc309a67d84884adfc5cc5e Mon Sep 17 00:00:00 2001 From: Gabriela Aguirre Date: Fri, 10 Apr 2026 17:03:38 +0200 Subject: [PATCH 15/21] Add static admin email --- .../java/backendlab/team4you/controller/SignupController.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/backendlab/team4you/controller/SignupController.java b/src/main/java/backendlab/team4you/controller/SignupController.java index 5239c4f..b51e9e3 100644 --- a/src/main/java/backendlab/team4you/controller/SignupController.java +++ b/src/main/java/backendlab/team4you/controller/SignupController.java @@ -32,6 +32,7 @@ public class SignupController { private final PublicKeyCredentialUserEntityRepository users; private final SecureRandom random = new SecureRandom(); private final UserService userService; + private static final String ADMIN_EMAIL = "admin@team4you.com"; public SignupController(PublicKeyCredentialUserEntityRepository users, UserService userService) { @@ -79,7 +80,7 @@ public void signup(@RequestBody SignupRequest req, HttpServletRequest request, H userEntity.setFirstName(req.firstName); userEntity.setLastName(req.lastName); - String assignedRole = req.getEmail().endsWith("@team4you.com") ? "ADMIN" : "USER"; + String assignedRole = req.getEmail().equalsIgnoreCase(ADMIN_EMAIL) ? "ADMIN" : "USER"; userEntity.setRole(assignedRole); users.save(userEntity); From 64d93b14769e09293655ff494663493e0c8bdc8a Mon Sep 17 00:00:00 2001 From: Gabriela Aguirre Date: Fri, 10 Apr 2026 18:27:37 +0200 Subject: [PATCH 16/21] Normalize findByName the same way registration does --- src/main/java/backendlab/team4you/user/UserService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/backendlab/team4you/user/UserService.java b/src/main/java/backendlab/team4you/user/UserService.java index db8c5c1..ee4a756 100644 --- a/src/main/java/backendlab/team4you/user/UserService.java +++ b/src/main/java/backendlab/team4you/user/UserService.java @@ -86,7 +86,7 @@ public UserEntity findByEmail(String email) { } public UserEntity findByName(String name){ - return userRepository.findByName(name).orElse(null); + return userRepository.findByName(name.trim()).orElse(null); } @Transactional From 86f5b9dfaa2b4b998e62540a5b8d0dff9c6143e1 Mon Sep 17 00:00:00 2001 From: Gabriela Aguirre Date: Fri, 10 Apr 2026 18:56:44 +0200 Subject: [PATCH 17/21] Refactor logic sign up with web authn to user service --- .../team4you/controller/SignupController.java | 33 ++++-------------- .../backendlab/team4you/user/UserService.java | 34 +++++++++++++++++++ 2 files changed, 41 insertions(+), 26 deletions(-) diff --git a/src/main/java/backendlab/team4you/controller/SignupController.java b/src/main/java/backendlab/team4you/controller/SignupController.java index b51e9e3..503e81b 100644 --- a/src/main/java/backendlab/team4you/controller/SignupController.java +++ b/src/main/java/backendlab/team4you/controller/SignupController.java @@ -32,7 +32,6 @@ public class SignupController { private final PublicKeyCredentialUserEntityRepository users; private final SecureRandom random = new SecureRandom(); private final UserService userService; - private static final String ADMIN_EMAIL = "admin@team4you.com"; public SignupController(PublicKeyCredentialUserEntityRepository users, UserService userService) { @@ -59,34 +58,16 @@ String signup(org.springframework.security.web.csrf.CsrfToken token, Model model @ResponseBody public void signup(@RequestBody SignupRequest req, HttpServletRequest request, HttpServletResponse response) { - if (req.username == null || req.username.isBlank()) { - throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Username is required"); - } - - if (userService.findByEmail(req.username) != null) { - throw new ResponseStatusException(HttpStatus.CONFLICT, "Username already exists"); - } - - byte[] idBytes = new byte[32]; - random.nextBytes(idBytes); - - UserEntity userEntity = new UserEntity( - new Bytes(idBytes), - req.username, - req.displayName + UserEntity userEntity = userService.registerWebAuthnUser( + req.getUsername(), + req.getDisplayName(), + req.getEmail(), + req.getFirstName(), + req.getLastName() ); - userEntity.setEmail(req.email); - userEntity.setFirstName(req.firstName); - userEntity.setLastName(req.lastName); - - String assignedRole = req.getEmail().equalsIgnoreCase(ADMIN_EMAIL) ? "ADMIN" : "USER"; - userEntity.setRole(assignedRole); - - users.save(userEntity); - Authentication auth = new UsernamePasswordAuthenticationToken( - userEntity.getName(), null, List.of(new SimpleGrantedAuthority("ROLE_USER"))); + userEntity.getName(), null, List.of(new SimpleGrantedAuthority("ROLE_" + userEntity.getRole()))); SecurityContext context = SecurityContextHolder.createEmptyContext(); context.setAuthentication(auth); diff --git a/src/main/java/backendlab/team4you/user/UserService.java b/src/main/java/backendlab/team4you/user/UserService.java index ee4a756..b33442f 100644 --- a/src/main/java/backendlab/team4you/user/UserService.java +++ b/src/main/java/backendlab/team4you/user/UserService.java @@ -4,10 +4,14 @@ import backendlab.team4you.exceptions.DuplicateEmailException; import backendlab.team4you.exceptions.UserNotFoundException; import jakarta.transaction.Transactional; +import org.springframework.http.HttpStatus; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.web.webauthn.api.Bytes; import org.springframework.stereotype.Service; +import org.springframework.web.server.ResponseStatusException; +import java.security.SecureRandom; import java.util.List; @@ -17,6 +21,7 @@ public class UserService { UserRepository userRepository; private final BCryptPasswordEncoder passwordEncoder; + private static final String ADMIN_EMAIL = "admin@team4you.com"; public UserService(UserRepository userRepository, BCryptPasswordEncoder passwordEncoder){ @@ -81,6 +86,35 @@ public void registerUser(UserRegistrationDTO dto) { userRepository.save(user); } + public UserEntity registerWebAuthnUser(String username, String displayName, String email, String firstName, String lastName){ + + String cleanName = username.trim(); + + if(userRepository.findByName(cleanName).isPresent()) + throw new ResponseStatusException(HttpStatus.CONFLICT, "Användarnamnet är redan taget"); + if(userRepository.findByEmail(email).isPresent()) + throw new ResponseStatusException(HttpStatus.CONFLICT, "E-posten är redan tagen"); + + byte[] idBytes = new byte[32]; + new SecureRandom().nextBytes(idBytes); + + UserEntity userEntity = new UserEntity( + new Bytes(idBytes), + cleanName, + displayName + ); + + userEntity.setEmail(email); + userEntity.setFirstName(firstName); + userEntity.setLastName(lastName); + + String assignedRole = email.equalsIgnoreCase(ADMIN_EMAIL) ? "ADMIN" : "USER"; + userEntity.setRole(assignedRole); + + return userRepository.save(userEntity); + + } + public UserEntity findByEmail(String email) { return userRepository.findByEmail(email).orElse(null); } From fcc43dec0f2c037917c4d5e68f867cd394232b50 Mon Sep 17 00:00:00 2001 From: Gabriela Aguirre Date: Fri, 10 Apr 2026 18:58:28 +0200 Subject: [PATCH 18/21] Removed unused imports --- .../backendlab/team4you/controller/SignupController.java | 6 ------ src/main/java/backendlab/team4you/user/UserService.java | 3 ++- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/main/java/backendlab/team4you/controller/SignupController.java b/src/main/java/backendlab/team4you/controller/SignupController.java index 503e81b..02e4187 100644 --- a/src/main/java/backendlab/team4you/controller/SignupController.java +++ b/src/main/java/backendlab/team4you/controller/SignupController.java @@ -2,10 +2,8 @@ import backendlab.team4you.user.UserEntity; import backendlab.team4you.user.UserService; -import backendlab.team4you.user.UserRepository; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; -import org.springframework.http.HttpStatus; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.authority.SimpleGrantedAuthority; @@ -13,7 +11,6 @@ import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.web.context.HttpSessionSecurityContextRepository; import org.springframework.security.web.context.SecurityContextRepository; -import org.springframework.security.web.webauthn.api.Bytes; import org.springframework.security.web.webauthn.management.PublicKeyCredentialUserEntityRepository; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; @@ -21,16 +18,13 @@ import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.ResponseBody; -import org.springframework.web.server.ResponseStatusException; -import java.security.SecureRandom; import java.util.List; @Controller public class SignupController { private final PublicKeyCredentialUserEntityRepository users; - private final SecureRandom random = new SecureRandom(); private final UserService userService; public SignupController(PublicKeyCredentialUserEntityRepository users, diff --git a/src/main/java/backendlab/team4you/user/UserService.java b/src/main/java/backendlab/team4you/user/UserService.java index b33442f..2e5cd03 100644 --- a/src/main/java/backendlab/team4you/user/UserService.java +++ b/src/main/java/backendlab/team4you/user/UserService.java @@ -22,6 +22,7 @@ public class UserService { UserRepository userRepository; private final BCryptPasswordEncoder passwordEncoder; private static final String ADMIN_EMAIL = "admin@team4you.com"; + private final SecureRandom random = new SecureRandom(); public UserService(UserRepository userRepository, BCryptPasswordEncoder passwordEncoder){ @@ -96,7 +97,7 @@ public UserEntity registerWebAuthnUser(String username, String displayName, Stri throw new ResponseStatusException(HttpStatus.CONFLICT, "E-posten är redan tagen"); byte[] idBytes = new byte[32]; - new SecureRandom().nextBytes(idBytes); + random.nextBytes(idBytes); UserEntity userEntity = new UserEntity( new Bytes(idBytes), From 04b2c9a179e28c8b31943956b8711e16ccc71d08 Mon Sep 17 00:00:00 2001 From: Gabriela Aguirre Date: Fri, 10 Apr 2026 19:36:19 +0200 Subject: [PATCH 19/21] Add null/blank validation before trimming and querying and Email-based admin assignment enables privilege escalation. --- .../java/backendlab/team4you/user/UserService.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/main/java/backendlab/team4you/user/UserService.java b/src/main/java/backendlab/team4you/user/UserService.java index 2e5cd03..701c414 100644 --- a/src/main/java/backendlab/team4you/user/UserService.java +++ b/src/main/java/backendlab/team4you/user/UserService.java @@ -89,11 +89,18 @@ public void registerUser(UserRegistrationDTO dto) { public UserEntity registerWebAuthnUser(String username, String displayName, String email, String firstName, String lastName){ + if (username == null || username.isBlank()) { + throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Username is required"); + } + if (email == null || email.isBlank()) { + throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Email is required"); + } String cleanName = username.trim(); + String cleanEmail = email.trim(); if(userRepository.findByName(cleanName).isPresent()) throw new ResponseStatusException(HttpStatus.CONFLICT, "Användarnamnet är redan taget"); - if(userRepository.findByEmail(email).isPresent()) + if(userRepository.findByEmail(cleanEmail).isPresent()) throw new ResponseStatusException(HttpStatus.CONFLICT, "E-posten är redan tagen"); byte[] idBytes = new byte[32]; @@ -109,7 +116,8 @@ public UserEntity registerWebAuthnUser(String username, String displayName, Stri userEntity.setFirstName(firstName); userEntity.setLastName(lastName); - String assignedRole = email.equalsIgnoreCase(ADMIN_EMAIL) ? "ADMIN" : "USER"; + //Every user that register themselves will automatically get the role USER assigned + String assignedRole = "USER"; userEntity.setRole(assignedRole); return userRepository.save(userEntity); From 2f2c0b8368771534e1a194971aca06464e67b49a Mon Sep 17 00:00:00 2001 From: Gabriela Aguirre Date: Fri, 10 Apr 2026 19:49:47 +0200 Subject: [PATCH 20/21] Fix with code rabbit --- .../backendlab/team4you/user/UserService.java | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/main/java/backendlab/team4you/user/UserService.java b/src/main/java/backendlab/team4you/user/UserService.java index 701c414..48b9d25 100644 --- a/src/main/java/backendlab/team4you/user/UserService.java +++ b/src/main/java/backendlab/team4you/user/UserService.java @@ -4,6 +4,7 @@ import backendlab.team4you.exceptions.DuplicateEmailException; import backendlab.team4you.exceptions.UserNotFoundException; import jakarta.transaction.Transactional; +import org.springframework.dao.DataIntegrityViolationException; import org.springframework.http.HttpStatus; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.web.webauthn.api.Bytes; @@ -21,7 +22,6 @@ public class UserService { UserRepository userRepository; private final BCryptPasswordEncoder passwordEncoder; - private static final String ADMIN_EMAIL = "admin@team4you.com"; private final SecureRandom random = new SecureRandom(); public UserService(UserRepository userRepository, BCryptPasswordEncoder passwordEncoder){ @@ -62,10 +62,10 @@ public UserEntity update(UserEntity userEntity){ public void registerUser(UserRegistrationDTO dto) { if (dto.name() == null || dto.name().isBlank()) { - throw new IllegalArgumentException("Username is required"); + throw new ResponseStatusException(HttpStatus.BAD_REQUEST,"Username is required"); } if (userRepository.findByName(dto.name().trim()).isPresent()) { - throw new IllegalArgumentException("Username already exists"); + throw new ResponseStatusException(HttpStatus.BAD_REQUEST ,"Username already exists"); } if (userRepository.findByEmail(dto.email()).isPresent()) { @@ -112,7 +112,7 @@ public UserEntity registerWebAuthnUser(String username, String displayName, Stri displayName ); - userEntity.setEmail(email); + userEntity.setEmail(cleanEmail); userEntity.setFirstName(firstName); userEntity.setLastName(lastName); @@ -120,8 +120,11 @@ public UserEntity registerWebAuthnUser(String username, String displayName, Stri String assignedRole = "USER"; userEntity.setRole(assignedRole); - return userRepository.save(userEntity); - + try { + return userRepository.save(userEntity); + } catch (DataIntegrityViolationException e) { + throw new ResponseStatusException(HttpStatus.CONFLICT, "Username or email already taken"); + } } public UserEntity findByEmail(String email) { From f4368b86b6a6d94953a9f19525298d4165822fe7 Mon Sep 17 00:00:00 2001 From: Gabriela Aguirre Date: Fri, 10 Apr 2026 19:59:32 +0200 Subject: [PATCH 21/21] Fix with code rabbit last comment --- src/main/java/backendlab/team4you/user/UserService.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/backendlab/team4you/user/UserService.java b/src/main/java/backendlab/team4you/user/UserService.java index 48b9d25..aaf6d69 100644 --- a/src/main/java/backendlab/team4you/user/UserService.java +++ b/src/main/java/backendlab/team4you/user/UserService.java @@ -68,7 +68,9 @@ public void registerUser(UserRegistrationDTO dto) { throw new ResponseStatusException(HttpStatus.BAD_REQUEST ,"Username already exists"); } - if (userRepository.findByEmail(dto.email()).isPresent()) { + String cleanEmail = dto.email() != null ? dto.email().trim() : null; + + if (cleanEmail != null && userRepository.findByEmail(cleanEmail).isPresent()) { throw new DuplicateEmailException("E-posten är redan tagen"); } @@ -76,7 +78,7 @@ public void registerUser(UserRegistrationDTO dto) { user.setName(dto.name().trim()); user.setFirstName(dto.firstName()); user.setLastName(dto.lastName()); - user.setEmail(dto.email()); + user.setEmail(cleanEmail); user.setPhoneNumber(dto.phoneNumber());