Tighten user linking

This commit is contained in:
2026-03-29 21:59:43 -06:00
parent 909026143d
commit 14ca0d8809
3 changed files with 68 additions and 5 deletions

View File

@@ -52,6 +52,8 @@ public class CustomerService {
@Transactional @Transactional
public CustomerResponse createCustomer(CustomerRequest request) { public CustomerResponse createCustomer(CustomerRequest request) {
ensureEmailAvailable(request.getEmail(), null);
Customer customer = new Customer(); Customer customer = new Customer();
customer.setFirstName(request.getFirstName()); customer.setFirstName(request.getFirstName());
customer.setLastName(request.getLastName()); customer.setLastName(request.getLastName());
@@ -78,6 +80,8 @@ public class CustomerService {
Customer customer = customerRepository.findById(id) Customer customer = customerRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("Customer not found with id: " + id)); .orElseThrow(() -> new ResourceNotFoundException("Customer not found with id: " + id));
ensureEmailAvailable(request.getEmail(), customer.getUserId());
customer.setFirstName(request.getFirstName()); customer.setFirstName(request.getFirstName());
customer.setLastName(request.getLastName()); customer.setLastName(request.getLastName());
customer.setEmail(request.getEmail()); customer.setEmail(request.getEmail());
@@ -142,4 +146,16 @@ public class CustomerService {
private String generatePhone(Customer customer) { private String generatePhone(Customer customer) {
return String.format("200-000-%04d", customer.getCustomerId()); return String.format("200-000-%04d", customer.getCustomerId());
} }
private void ensureEmailAvailable(String email, Long currentUserId) {
if (email == null || email.isBlank()) {
return;
}
userRepository.findByEmail(email).ifPresent(existing -> {
if (currentUserId == null || !existing.getId().equals(currentUserId)) {
throw new ResponseStatusException(CONFLICT, "Email already exists");
}
});
}
} }

View File

@@ -2,7 +2,10 @@ INSERT INTO users (username, password, email, fullName, phone, role, active, tok
SELECT SELECT
CONCAT('customer_', c.customerId) AS username, CONCAT('customer_', c.customerId) AS username,
'$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq' AS password, '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq' AS password,
c.email, CASE
WHEN EXISTS (SELECT 1 FROM users u WHERE u.email = c.email) THEN CONCAT('customer_', c.customerId, '@petshop.local')
ELSE c.email
END AS email,
CONCAT(c.firstName, ' ', c.lastName) AS fullName, CONCAT(c.firstName, ' ', c.lastName) AS fullName,
CONCAT('200-000-', LPAD(c.customerId, 4, '0')) AS phone, CONCAT('200-000-', LPAD(c.customerId, 4, '0')) AS phone,
'CUSTOMER' AS role, 'CUSTOMER' AS role,
@@ -14,14 +17,16 @@ WHERE c.user_id IS NULL
SELECT 1 SELECT 1
FROM users u FROM users u
WHERE u.username = CONCAT('customer_', c.customerId) WHERE u.username = CONCAT('customer_', c.customerId)
OR u.email = c.email
); );
INSERT INTO users (username, password, email, fullName, phone, role, active, tokenVersion) INSERT INTO users (username, password, email, fullName, phone, role, active, tokenVersion)
SELECT SELECT
CONCAT('employee_', e.employeeId) AS username, CONCAT('employee_', e.employeeId) AS username,
'$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq' AS password, '$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq' AS password,
e.email, CASE
WHEN EXISTS (SELECT 1 FROM users u WHERE u.email = e.email) THEN CONCAT('employee_', e.employeeId, '@petshop.local')
ELSE e.email
END AS email,
CONCAT(e.firstName, ' ', e.lastName) AS fullName, CONCAT(e.firstName, ' ', e.lastName) AS fullName,
CONCAT('300-000-', LPAD(e.employeeId, 4, '0')) AS phone, CONCAT('300-000-', LPAD(e.employeeId, 4, '0')) AS phone,
CASE CASE
@@ -36,7 +41,6 @@ WHERE e.user_id IS NULL
SELECT 1 SELECT 1
FROM users u FROM users u
WHERE u.username = CONCAT('employee_', e.employeeId) WHERE u.username = CONCAT('employee_', e.employeeId)
OR u.email = e.email
); );
UPDATE customer c UPDATE customer c

View File

@@ -98,9 +98,52 @@ class CustomerServiceTest {
existing.setEmail("pat@example.com"); existing.setEmail("pat@example.com");
existing.setRole(User.Role.STAFF); existing.setRole(User.Role.STAFF);
when(customerRepository.save(any(Customer.class))).thenAnswer(invocation -> invocation.getArgument(0));
when(userRepository.findByEmail("pat@example.com")).thenReturn(Optional.of(existing)); when(userRepository.findByEmail("pat@example.com")).thenReturn(Optional.of(existing));
assertThrows(ResponseStatusException.class, () -> customerService.createCustomer(request)); assertThrows(ResponseStatusException.class, () -> customerService.createCustomer(request));
} }
@Test
void createCustomerRejectsExistingCustomerEmail() {
CustomerRequest request = new CustomerRequest();
request.setFirstName("Pat");
request.setLastName("Owner");
request.setEmail("pat@example.com");
User existing = new User();
existing.setId(22L);
existing.setUsername("customer1");
existing.setEmail("pat@example.com");
existing.setRole(User.Role.CUSTOMER);
when(userRepository.findByEmail("pat@example.com")).thenReturn(Optional.of(existing));
assertThrows(ResponseStatusException.class, () -> customerService.createCustomer(request));
}
@Test
void updateCustomerRejectsExistingEmailFromOtherUser() {
Customer customer = new Customer();
customer.setCustomerId(7L);
customer.setUserId(11L);
customer.setFirstName("Pat");
customer.setLastName("Owner");
customer.setEmail("old@example.com");
CustomerRequest request = new CustomerRequest();
request.setFirstName("Pat");
request.setLastName("Owner");
request.setEmail("pat@example.com");
User existing = new User();
existing.setId(22L);
existing.setUsername("customer2");
existing.setEmail("pat@example.com");
existing.setRole(User.Role.CUSTOMER);
when(customerRepository.findById(7L)).thenReturn(Optional.of(customer));
when(userRepository.findByEmail("pat@example.com")).thenReturn(Optional.of(existing));
assertThrows(ResponseStatusException.class, () -> customerService.updateCustomer(7L, request));
}
} }