Skip to content
This repository was archived by the owner on Jul 6, 2025. It is now read-only.

Commit 87ff823

Browse files
authored
Merge pull request #85 from Zenfulcode/84-unauthenticated-order-fix
Reworking order flow with as a guest user
2 parents 9899ba3 + df72e67 commit 87ff823

10 files changed

Lines changed: 131 additions & 42 deletions

File tree

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,15 @@
11
package com.zenfulcode.commercify.commercify.api.requests;
22

33
import com.zenfulcode.commercify.commercify.dto.AddressDTO;
4-
5-
import java.util.UUID;
4+
import com.zenfulcode.commercify.commercify.dto.UserDTO;
65

76
public record RegisterUserRequest(
87
String email,
98
String password,
109
String firstName,
1110
String lastName,
12-
Boolean isGuest,
1311
AddressDTO defaultAddress) {
14-
// Set a secure default password
15-
public RegisterUserRequest {
16-
if (password == null || password.isBlank()) {
17-
password = UUID.randomUUID().toString();
18-
}
19-
20-
if (isGuest == null) {
21-
isGuest = false;
22-
}
12+
public UserDTO toUserDTO() {
13+
return new UserDTO(null, email, firstName, lastName, null, defaultAddress, null);
2314
}
2415
}

src/main/java/com/zenfulcode/commercify/commercify/api/responses/orders/GetOrderResponse.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33

44
import com.zenfulcode.commercify.commercify.OrderStatus;
5+
import com.zenfulcode.commercify.commercify.dto.AddressDTO;
56
import com.zenfulcode.commercify.commercify.dto.OrderDTO;
67
import com.zenfulcode.commercify.commercify.dto.OrderDetailsDTO;
78
import com.zenfulcode.commercify.commercify.viewmodel.OrderLineViewModel;
@@ -12,6 +13,9 @@
1213
public record GetOrderResponse(
1314
Long id,
1415
Long userId,
16+
String customerName,
17+
String customerEmail,
18+
AddressDTO shippingAddress,
1519
OrderStatus orderStatus,
1620
String currency,
1721
Double totalAmount,
@@ -24,6 +28,9 @@ public static GetOrderResponse from(OrderDetailsDTO orderDetails) {
2428
return new GetOrderResponse(
2529
order.getId(),
2630
order.getUserId(),
31+
orderDetails.getCustomerDetails().getFirstName() + " " + orderDetails.getCustomerDetails().getLastName(),
32+
orderDetails.getCustomerDetails().getEmail(),
33+
orderDetails.getShippingAddress(),
2734
order.getOrderStatus(),
2835
order.getCurrency(),
2936
order.getTotalAmount(),

src/main/java/com/zenfulcode/commercify/commercify/config/SecurityConfig.java

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,7 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti
3535
.requestMatchers(
3636
"/api/v1/auth/**",
3737
"/api/v1/products/active",
38-
"/api/v1/products/{id}",
39-
"/api/v1/orders",
40-
"/api/v1/payments/mobilepay/create",
41-
"/api/v1/payments/stripe/create").permitAll()
38+
"/api/v1/products/{id}").permitAll()
4239
.anyRequest().authenticated()
4340
)
4441
.sessionManagement(smc -> smc.sessionCreationPolicy(SessionCreationPolicy.STATELESS))

src/main/java/com/zenfulcode/commercify/commercify/controller/AuthenticationController.java

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
import lombok.RequiredArgsConstructor;
1111
import lombok.extern.slf4j.Slf4j;
1212
import org.springframework.http.ResponseEntity;
13-
import org.springframework.security.access.prepost.PreAuthorize;
1413
import org.springframework.web.bind.annotation.*;
1514

1615
@Slf4j
@@ -25,13 +24,6 @@ public class AuthenticationController {
2524
public ResponseEntity<AuthResponse> register(@RequestBody RegisterUserRequest registerRequest) {
2625
try {
2726
UserDTO user = authenticationService.registerUser(registerRequest);
28-
29-
if (registerRequest.isGuest()) {
30-
UserDTO authenticated = authenticationService.authenticate(new LoginUserRequest(registerRequest.email(), registerRequest.password()));
31-
String jwt = jwtService.generateToken(authenticated);
32-
return ResponseEntity.ok(AuthResponse.UserAuthenticated(authenticated, jwt, jwtService.getExpirationTime()));
33-
}
34-
3527
return ResponseEntity.ok(AuthResponse.UserAuthenticated(user, "", 0));
3628
} catch (RuntimeException e) {
3729
log.error("Error registering user: {}", e.getMessage());
@@ -51,8 +43,23 @@ public ResponseEntity<AuthResponse> login(@RequestBody LoginUserRequest loginReq
5143
}
5244

5345
@GetMapping("/me")
54-
@PreAuthorize("hasRole('USER')")
5546
public ResponseEntity<UserDTO> getAuthenticatedUser(@RequestHeader("Authorization") String authHeader) {
56-
return ResponseEntity.ok(authenticationService.getAuthenticatedUser(authHeader));
47+
try {
48+
return ResponseEntity.ok(authenticationService.getAuthenticatedUser(authHeader));
49+
} catch (Exception e) {
50+
return ResponseEntity.badRequest().body(null);
51+
}
52+
}
53+
54+
@PostMapping("/guest")
55+
public ResponseEntity<AuthResponse> registerGuest() {
56+
try {
57+
UserDTO user = authenticationService.registerGuest();
58+
String jwt = jwtService.generateToken(user);
59+
return ResponseEntity.ok(AuthResponse.UserAuthenticated(user, jwt, jwtService.getExpirationTime()));
60+
} catch (RuntimeException e) {
61+
log.error("Error registering guest: {}", e.getMessage());
62+
return ResponseEntity.badRequest().body(AuthResponse.AuthenticationFailed(e.getMessage()));
63+
}
5764
}
58-
}
65+
}

src/main/java/com/zenfulcode/commercify/commercify/controller/OrderController.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ public class OrderController {
3737
"id", "userId", "status", "currency", "totalAmount", "createdAt", "updatedAt"
3838
);
3939

40-
@PreAuthorize("hasRole('USER') and #userId == authentication.principal.id")
40+
@PreAuthorize("#userId == authentication.principal.id")
4141
@PostMapping("/{userId}")
4242
public ResponseEntity<?> createOrder(@PathVariable Long userId, @RequestBody CreateOrderRequest orderRequest) {
4343
try {
@@ -86,7 +86,7 @@ public ResponseEntity<?> createOrder(@RequestBody CreateOrderRequest orderReques
8686
}
8787
}
8888

89-
@PreAuthorize("hasRole('USER') and #userId == authentication.principal.id or hasRole('ADMIN')")
89+
@PreAuthorize("#userId == authentication.principal.id or hasRole('ADMIN')")
9090
@GetMapping("/user/{userId}")
9191
public ResponseEntity<?> getOrdersByUserId(
9292
@PathVariable Long userId,
@@ -143,7 +143,7 @@ public ResponseEntity<?> getAllOrders(
143143
}
144144
}
145145

146-
@PreAuthorize("hasRole('USER') and @orderService.isOrderOwnedByUser(#orderId, authentication.principal.id) or hasRole('ADMIN')")
146+
@PreAuthorize("@orderService.isOrderOwnedByUser(#orderId, authentication.principal.id) or hasRole('ADMIN')")
147147
@GetMapping("/{orderId}")
148148
public ResponseEntity<GetOrderResponse> getOrderById(@PathVariable Long orderId) {
149149
try {

src/main/java/com/zenfulcode/commercify/commercify/controller/UserManagementController.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.zenfulcode.commercify.commercify.controller;
22

33

4+
import com.zenfulcode.commercify.commercify.api.requests.RegisterUserRequest;
45
import com.zenfulcode.commercify.commercify.dto.AddressDTO;
56
import com.zenfulcode.commercify.commercify.dto.UserDTO;
67
import com.zenfulcode.commercify.commercify.service.UserManagementService;
@@ -50,6 +51,20 @@ public ResponseEntity<UserDTO> updateUser(@PathVariable Long id, @RequestBody Us
5051
return ResponseEntity.ok(userManagementService.updateUser(id, userDTO));
5152
}
5253

54+
@PutMapping("/{id}/register")
55+
@PreAuthorize("hasRole('ADMIN') or #id == authentication.principal.id")
56+
public ResponseEntity<UserDTO> registerGuest(@PathVariable Long id, @RequestBody RegisterUserRequest request) {
57+
System.out.println("Registering guest");
58+
System.out.println(request);
59+
60+
try {
61+
return ResponseEntity.ok(userManagementService.updateGuest(id, request));
62+
} catch (Exception e) {
63+
System.out.println("error: " + e.getMessage());
64+
return ResponseEntity.badRequest().build();
65+
}
66+
}
67+
5368
@DeleteMapping("/{id}")
5469
@PreAuthorize("hasRole('ADMIN')")
5570
public ResponseEntity<Void> deleteUser(@PathVariable Long id) {

src/main/java/com/zenfulcode/commercify/commercify/entity/UserEntity.java

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,7 @@
99
import org.springframework.security.core.userdetails.UserDetails;
1010

1111
import java.time.Instant;
12-
import java.util.Collection;
13-
import java.util.HashSet;
14-
import java.util.List;
15-
import java.util.Set;
12+
import java.util.*;
1613
import java.util.stream.Collectors;
1714

1815
@Table(name = "users")
@@ -48,7 +45,7 @@ public class UserEntity implements UserDetails {
4845
@ElementCollection(fetch = FetchType.EAGER)
4946
@CollectionTable(name = "user_roles", joinColumns = @JoinColumn(name = "id"))
5047
@Column(name = "role")
51-
private List<String> roles;
48+
private List<String> roles = new ArrayList<>();
5249

5350
@Column(nullable = false, name = "email_confirmed")
5451
private Boolean emailConfirmed = false;
@@ -72,6 +69,24 @@ public Collection<? extends GrantedAuthority> getAuthorities() {
7269
.collect(Collectors.toList());
7370
}
7471

72+
public void addRole(String role) {
73+
if (roles == null) {
74+
roles = new ArrayList<>();
75+
}
76+
77+
if (!roles.contains(role.toUpperCase())) {
78+
roles.add(role.toUpperCase());
79+
}
80+
}
81+
82+
public void removeRole(String role) {
83+
if (roles == null) {
84+
return;
85+
}
86+
87+
roles.remove(role.toUpperCase());
88+
}
89+
7590
@Override
7691
public String getUsername() {
7792
return email;

src/main/java/com/zenfulcode/commercify/commercify/service/AuthenticationService.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,10 @@
1616
import org.springframework.stereotype.Service;
1717
import org.springframework.transaction.annotation.Transactional;
1818

19+
import java.util.Date;
1920
import java.util.List;
2021
import java.util.Optional;
22+
import java.util.UUID;
2123

2224
@Service
2325
@AllArgsConstructor
@@ -66,6 +68,32 @@ public UserDTO registerUser(RegisterUserRequest registerRequest) {
6668
return mapper.apply(savedUser);
6769
}
6870

71+
public UserDTO registerGuest() {
72+
String firstName = "Guest";
73+
String lastName = String.valueOf(new Date().toInstant().toEpochMilli());
74+
String email = firstName + lastName + "@commercify.app";
75+
String password = UUID.randomUUID().toString();
76+
77+
UserEntity user = UserEntity.builder()
78+
.firstName(firstName)
79+
.lastName(lastName)
80+
.email(email)
81+
.password(passwordEncoder.encode(password))
82+
.roles(List.of("GUEST"))
83+
.emailConfirmed(true)
84+
.build();
85+
UserEntity savedUser = userRepository.save(user);
86+
87+
authenticationManager.authenticate(
88+
new UsernamePasswordAuthenticationToken(
89+
email,
90+
password
91+
)
92+
);
93+
94+
return mapper.apply(savedUser);
95+
}
96+
6997
public UserDTO authenticate(LoginUserRequest login) {
7098
authenticationManager.authenticate(
7199
new UsernamePasswordAuthenticationToken(

src/main/java/com/zenfulcode/commercify/commercify/service/UserManagementService.java

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.zenfulcode.commercify.commercify.service;
22

33

4+
import com.zenfulcode.commercify.commercify.api.requests.RegisterUserRequest;
45
import com.zenfulcode.commercify.commercify.dto.AddressDTO;
56
import com.zenfulcode.commercify.commercify.dto.UserDTO;
67
import com.zenfulcode.commercify.commercify.dto.mapper.AddressMapper;
@@ -9,19 +10,24 @@
910
import com.zenfulcode.commercify.commercify.entity.UserEntity;
1011
import com.zenfulcode.commercify.commercify.repository.UserRepository;
1112
import lombok.RequiredArgsConstructor;
13+
import lombok.extern.slf4j.Slf4j;
1214
import org.springframework.data.domain.Page;
1315
import org.springframework.data.domain.Pageable;
16+
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
1417
import org.springframework.stereotype.Service;
1518
import org.springframework.transaction.annotation.Transactional;
1619

1720
import java.util.ArrayList;
21+
import java.util.Optional;
1822

1923
@Service
2024
@RequiredArgsConstructor
25+
@Slf4j
2126
public class UserManagementService {
2227
private final UserRepository userRepository;
2328
private final UserMapper mapper;
2429
private final AddressMapper addressMapper;
30+
private final BCryptPasswordEncoder passwordEncoder;
2531

2632
@Transactional(readOnly = true)
2733
public UserDTO getUserById(Long id) {
@@ -36,16 +42,42 @@ public Page<UserDTO> getAllUsers(Pageable pageable) {
3642
}
3743

3844
@Transactional
39-
public UserDTO updateUser(Long id, UserDTO userDTO) {
45+
public UserDTO updateUser(Long id, UserDTO userDTO) throws RuntimeException { // Explicitly declare throws
4046
UserEntity user = userRepository.findById(id)
4147
.orElseThrow(() -> new RuntimeException("User not found"));
4248

49+
Optional<UserEntity> existing = userRepository.findByEmail(userDTO.getEmail());
50+
51+
if (existing.isPresent() && !existing.get().getId().equals(id)) { // Add check for same user
52+
throw new RuntimeException("User with email " + userDTO.getEmail() + " already exists");
53+
}
54+
4355
user.setFirstName(userDTO.getFirstName());
4456
user.setLastName(userDTO.getLastName());
4557
user.setEmail(userDTO.getEmail());
4658

47-
UserEntity updatedUser = userRepository.save(user);
48-
return mapper.apply(updatedUser);
59+
return mapper.apply(userRepository.save(user));
60+
}
61+
62+
@Transactional
63+
public UserDTO updateGuest(Long id, RegisterUserRequest request) {
64+
try {
65+
updateUser(id, request.toUserDTO());
66+
67+
UserEntity user = userRepository.findById(id)
68+
.orElseThrow(() -> new RuntimeException("User not found"));
69+
70+
user.setPassword(passwordEncoder.encode(request.password()));
71+
user.removeRole("GUEST");
72+
user.addRole("USER");
73+
74+
UserEntity updatedUser = userRepository.save(user);
75+
return mapper.apply(updatedUser);
76+
} catch (RuntimeException e) {
77+
// Log the error
78+
log.error("Failed to update guest user: {}", e.getMessage(), e);
79+
throw e; // Re-throw the exception instead of swallowing it
80+
}
4981
}
5082

5183
@Transactional

src/test/java/com/zenfulcode/commercify/commercify/service/AuthenticationServiceTest.java

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,6 @@ void setUp() {
7777
"password123",
7878
"John",
7979
"Doe",
80-
false,
8180
shippingAddress
8281
);
8382

@@ -127,9 +126,7 @@ void registerUser_Success() {
127126
void registerUser_NoPasswordProvided_ShouldSetDefaultPassword() {
128127
// Arrange
129128
RegisterUserRequest request = new RegisterUserRequest(
130-
"test@example.com", "", "Test", "User",
131-
false,
132-
null);
129+
"test@example.com", "", "Test", "User", null);
133130

134131
when(userRepository.findByEmail(anyString())).thenReturn(Optional.empty());
135132
when(passwordEncoder.encode(anyString())).thenReturn("encodedPassword");

0 commit comments

Comments
 (0)