Skip to content
This repository was archived by the owner on Jul 6, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,24 +1,15 @@
package com.zenfulcode.commercify.commercify.api.requests;

import com.zenfulcode.commercify.commercify.dto.AddressDTO;

import java.util.UUID;
import com.zenfulcode.commercify.commercify.dto.UserDTO;

public record RegisterUserRequest(
String email,
String password,
String firstName,
String lastName,
Boolean isGuest,
AddressDTO defaultAddress) {
// Set a secure default password
public RegisterUserRequest {
if (password == null || password.isBlank()) {
password = UUID.randomUUID().toString();
}

if (isGuest == null) {
isGuest = false;
}
public UserDTO toUserDTO() {
return new UserDTO(null, email, firstName, lastName, null, defaultAddress, null);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@


import com.zenfulcode.commercify.commercify.OrderStatus;
import com.zenfulcode.commercify.commercify.dto.AddressDTO;
import com.zenfulcode.commercify.commercify.dto.OrderDTO;
import com.zenfulcode.commercify.commercify.dto.OrderDetailsDTO;
import com.zenfulcode.commercify.commercify.viewmodel.OrderLineViewModel;
Expand All @@ -12,6 +13,9 @@
public record GetOrderResponse(
Long id,
Long userId,
String customerName,
String customerEmail,
AddressDTO shippingAddress,
OrderStatus orderStatus,
String currency,
Double totalAmount,
Expand All @@ -24,6 +28,9 @@ public static GetOrderResponse from(OrderDetailsDTO orderDetails) {
return new GetOrderResponse(
order.getId(),
order.getUserId(),
orderDetails.getCustomerDetails().getFirstName() + " " + orderDetails.getCustomerDetails().getLastName(),
orderDetails.getCustomerDetails().getEmail(),
orderDetails.getShippingAddress(),
order.getOrderStatus(),
order.getCurrency(),
order.getTotalAmount(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,7 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti
.requestMatchers(
"/api/v1/auth/**",
"/api/v1/products/active",
"/api/v1/products/{id}",
"/api/v1/orders",
"/api/v1/payments/mobilepay/create",
"/api/v1/payments/stripe/create").permitAll()
"/api/v1/products/{id}").permitAll()
.anyRequest().authenticated()
)
.sessionManagement(smc -> smc.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;

@Slf4j
Expand All @@ -25,13 +24,6 @@ public class AuthenticationController {
public ResponseEntity<AuthResponse> register(@RequestBody RegisterUserRequest registerRequest) {
try {
UserDTO user = authenticationService.registerUser(registerRequest);

if (registerRequest.isGuest()) {
UserDTO authenticated = authenticationService.authenticate(new LoginUserRequest(registerRequest.email(), registerRequest.password()));
String jwt = jwtService.generateToken(authenticated);
return ResponseEntity.ok(AuthResponse.UserAuthenticated(authenticated, jwt, jwtService.getExpirationTime()));
}

return ResponseEntity.ok(AuthResponse.UserAuthenticated(user, "", 0));
} catch (RuntimeException e) {
log.error("Error registering user: {}", e.getMessage());
Expand All @@ -51,8 +43,23 @@ public ResponseEntity<AuthResponse> login(@RequestBody LoginUserRequest loginReq
}

@GetMapping("/me")
@PreAuthorize("hasRole('USER')")
public ResponseEntity<UserDTO> getAuthenticatedUser(@RequestHeader("Authorization") String authHeader) {
return ResponseEntity.ok(authenticationService.getAuthenticatedUser(authHeader));
try {
return ResponseEntity.ok(authenticationService.getAuthenticatedUser(authHeader));
} catch (Exception e) {
return ResponseEntity.badRequest().body(null);
}
}

@PostMapping("/guest")
public ResponseEntity<AuthResponse> registerGuest() {
try {
UserDTO user = authenticationService.registerGuest();
String jwt = jwtService.generateToken(user);
return ResponseEntity.ok(AuthResponse.UserAuthenticated(user, jwt, jwtService.getExpirationTime()));
} catch (RuntimeException e) {
log.error("Error registering guest: {}", e.getMessage());
return ResponseEntity.badRequest().body(AuthResponse.AuthenticationFailed(e.getMessage()));
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public class OrderController {
"id", "userId", "status", "currency", "totalAmount", "createdAt", "updatedAt"
);

@PreAuthorize("hasRole('USER') and #userId == authentication.principal.id")
@PreAuthorize("#userId == authentication.principal.id")
@PostMapping("/{userId}")
public ResponseEntity<?> createOrder(@PathVariable Long userId, @RequestBody CreateOrderRequest orderRequest) {
try {
Expand Down Expand Up @@ -86,7 +86,7 @@ public ResponseEntity<?> createOrder(@RequestBody CreateOrderRequest orderReques
}
}

@PreAuthorize("hasRole('USER') and #userId == authentication.principal.id or hasRole('ADMIN')")
@PreAuthorize("#userId == authentication.principal.id or hasRole('ADMIN')")
@GetMapping("/user/{userId}")
public ResponseEntity<?> getOrdersByUserId(
@PathVariable Long userId,
Expand Down Expand Up @@ -143,7 +143,7 @@ public ResponseEntity<?> getAllOrders(
}
}

@PreAuthorize("hasRole('USER') and @orderService.isOrderOwnedByUser(#orderId, authentication.principal.id) or hasRole('ADMIN')")
@PreAuthorize("@orderService.isOrderOwnedByUser(#orderId, authentication.principal.id) or hasRole('ADMIN')")
@GetMapping("/{orderId}")
public ResponseEntity<GetOrderResponse> getOrderById(@PathVariable Long orderId) {
try {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.zenfulcode.commercify.commercify.controller;


import com.zenfulcode.commercify.commercify.api.requests.RegisterUserRequest;
import com.zenfulcode.commercify.commercify.dto.AddressDTO;
import com.zenfulcode.commercify.commercify.dto.UserDTO;
import com.zenfulcode.commercify.commercify.service.UserManagementService;
Expand Down Expand Up @@ -50,6 +51,20 @@ public ResponseEntity<UserDTO> updateUser(@PathVariable Long id, @RequestBody Us
return ResponseEntity.ok(userManagementService.updateUser(id, userDTO));
}

@PutMapping("/{id}/register")
@PreAuthorize("hasRole('ADMIN') or #id == authentication.principal.id")
public ResponseEntity<UserDTO> registerGuest(@PathVariable Long id, @RequestBody RegisterUserRequest request) {
System.out.println("Registering guest");
System.out.println(request);

try {
return ResponseEntity.ok(userManagementService.updateGuest(id, request));
} catch (Exception e) {
System.out.println("error: " + e.getMessage());
return ResponseEntity.badRequest().build();
}
}

@DeleteMapping("/{id}")
@PreAuthorize("hasRole('ADMIN')")
public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,7 @@
import org.springframework.security.core.userdetails.UserDetails;

import java.time.Instant;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.*;
import java.util.stream.Collectors;

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

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

public void addRole(String role) {
if (roles == null) {
roles = new ArrayList<>();
}

if (!roles.contains(role.toUpperCase())) {
roles.add(role.toUpperCase());
}
}

public void removeRole(String role) {
if (roles == null) {
return;
}

roles.remove(role.toUpperCase());
}

@Override
public String getUsername() {
return email;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.UUID;

@Service
@AllArgsConstructor
Expand Down Expand Up @@ -66,6 +68,32 @@ public UserDTO registerUser(RegisterUserRequest registerRequest) {
return mapper.apply(savedUser);
}

public UserDTO registerGuest() {
String firstName = "Guest";
String lastName = String.valueOf(new Date().toInstant().toEpochMilli());
String email = firstName + lastName + "@commercify.app";
String password = UUID.randomUUID().toString();

UserEntity user = UserEntity.builder()
.firstName(firstName)
.lastName(lastName)
.email(email)
.password(passwordEncoder.encode(password))
.roles(List.of("GUEST"))
.emailConfirmed(true)
.build();
UserEntity savedUser = userRepository.save(user);

authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
email,
password
)
);

return mapper.apply(savedUser);
}

public UserDTO authenticate(LoginUserRequest login) {
authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.zenfulcode.commercify.commercify.service;


import com.zenfulcode.commercify.commercify.api.requests.RegisterUserRequest;
import com.zenfulcode.commercify.commercify.dto.AddressDTO;
import com.zenfulcode.commercify.commercify.dto.UserDTO;
import com.zenfulcode.commercify.commercify.dto.mapper.AddressMapper;
Expand All @@ -9,19 +10,24 @@
import com.zenfulcode.commercify.commercify.entity.UserEntity;
import com.zenfulcode.commercify.commercify.repository.UserRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Optional;

@Service
@RequiredArgsConstructor
@Slf4j
public class UserManagementService {
private final UserRepository userRepository;
private final UserMapper mapper;
private final AddressMapper addressMapper;
private final BCryptPasswordEncoder passwordEncoder;

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

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

Optional<UserEntity> existing = userRepository.findByEmail(userDTO.getEmail());

if (existing.isPresent() && !existing.get().getId().equals(id)) { // Add check for same user
throw new RuntimeException("User with email " + userDTO.getEmail() + " already exists");
}

user.setFirstName(userDTO.getFirstName());
user.setLastName(userDTO.getLastName());
user.setEmail(userDTO.getEmail());

UserEntity updatedUser = userRepository.save(user);
return mapper.apply(updatedUser);
return mapper.apply(userRepository.save(user));
}

@Transactional
public UserDTO updateGuest(Long id, RegisterUserRequest request) {
try {
updateUser(id, request.toUserDTO());

UserEntity user = userRepository.findById(id)
.orElseThrow(() -> new RuntimeException("User not found"));

user.setPassword(passwordEncoder.encode(request.password()));
user.removeRole("GUEST");
user.addRole("USER");

UserEntity updatedUser = userRepository.save(user);
return mapper.apply(updatedUser);
} catch (RuntimeException e) {
// Log the error
log.error("Failed to update guest user: {}", e.getMessage(), e);
throw e; // Re-throw the exception instead of swallowing it
}
}

@Transactional
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,6 @@ void setUp() {
"password123",
"John",
"Doe",
false,
shippingAddress
);

Expand Down Expand Up @@ -127,9 +126,7 @@ void registerUser_Success() {
void registerUser_NoPasswordProvided_ShouldSetDefaultPassword() {
// Arrange
RegisterUserRequest request = new RegisterUserRequest(
"test@example.com", "", "Test", "User",
false,
null);
"test@example.com", "", "Test", "User", null);

when(userRepository.findByEmail(anyString())).thenReturn(Optional.empty());
when(passwordEncoder.encode(anyString())).thenReturn("encodedPassword");
Expand Down
Loading