diff --git a/backend/src/main/java/online/wesal/wesal/config/SecurityConfig.java b/backend/src/main/java/online/wesal/wesal/config/SecurityConfig.java index e812afb..c5f860a 100644 --- a/backend/src/main/java/online/wesal/wesal/config/SecurityConfig.java +++ b/backend/src/main/java/online/wesal/wesal/config/SecurityConfig.java @@ -68,6 +68,7 @@ public class SecurityConfig { .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .authorizeHttpRequests(auth -> auth .requestMatchers("/", "/login", "/swagger-ui/**", "/v3/api-docs/**").permitAll() + .requestMatchers("/admin/**").hasRole("ADMIN") .anyRequest().authenticated() ) .authenticationProvider(authenticationProvider()) diff --git a/backend/src/main/java/online/wesal/wesal/controller/AuthController.java b/backend/src/main/java/online/wesal/wesal/controller/AuthController.java index e7934a8..632d7a8 100644 --- a/backend/src/main/java/online/wesal/wesal/controller/AuthController.java +++ b/backend/src/main/java/online/wesal/wesal/controller/AuthController.java @@ -3,6 +3,7 @@ package online.wesal.wesal.controller; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; +import online.wesal.wesal.dto.CreateUserRequest; import online.wesal.wesal.dto.LoginRequest; import online.wesal.wesal.dto.LoginResponse; import online.wesal.wesal.dto.UsernameSelectionRequest; @@ -65,4 +66,15 @@ public class AuthController { return ResponseEntity.badRequest().build(); } } + + @PostMapping("/admin/createUser") + @Operation(summary = "Create new user (Admin only)", description = "Creates a new user - requires admin privileges") + public ResponseEntity createUser(@Valid @RequestBody CreateUserRequest request) { + try { + User user = userService.createUser(request.getEmail(), request.getPassword(), request.getDisplayName()); + return ResponseEntity.ok(user); + } catch (RuntimeException e) { + return ResponseEntity.badRequest().body(Map.of("error", e.getMessage())); + } + } } \ No newline at end of file diff --git a/backend/src/main/java/online/wesal/wesal/dto/CreateUserRequest.java b/backend/src/main/java/online/wesal/wesal/dto/CreateUserRequest.java new file mode 100644 index 0000000..579aa49 --- /dev/null +++ b/backend/src/main/java/online/wesal/wesal/dto/CreateUserRequest.java @@ -0,0 +1,51 @@ +package online.wesal.wesal.dto; + +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; + +public class CreateUserRequest { + + @Email + @NotBlank + private String email; + + @NotBlank + @Size(min = 8) + private String password; + + @NotBlank + private String displayName; + + public CreateUserRequest() {} + + public CreateUserRequest(String email, String password, String displayName) { + this.email = email; + this.password = password; + this.displayName = displayName; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getDisplayName() { + return displayName; + } + + public void setDisplayName(String displayName) { + this.displayName = displayName; + } +} \ No newline at end of file diff --git a/backend/src/main/java/online/wesal/wesal/entity/User.java b/backend/src/main/java/online/wesal/wesal/entity/User.java index 61131bb..75f8ee4 100644 --- a/backend/src/main/java/online/wesal/wesal/entity/User.java +++ b/backend/src/main/java/online/wesal/wesal/entity/User.java @@ -37,6 +37,9 @@ public class User { @Column(nullable = false) private boolean activated = false; + @Column(nullable = false) + private String role = "USER"; + public User() {} public User(String email, String password, String displayName) { @@ -108,4 +111,12 @@ public class User { public void setActivated(boolean activated) { this.activated = activated; } + + public String getRole() { + return role; + } + + public void setRole(String role) { + this.role = role; + } } \ No newline at end of file diff --git a/backend/src/main/java/online/wesal/wesal/service/UserDetailsServiceImpl.java b/backend/src/main/java/online/wesal/wesal/service/UserDetailsServiceImpl.java index 513f9d0..ce92452 100644 --- a/backend/src/main/java/online/wesal/wesal/service/UserDetailsServiceImpl.java +++ b/backend/src/main/java/online/wesal/wesal/service/UserDetailsServiceImpl.java @@ -3,12 +3,15 @@ package online.wesal.wesal.service; import online.wesal.wesal.entity.User; import online.wesal.wesal.repository.UserRepository; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Service; import java.util.ArrayList; +import java.util.Collection; @Service public class UserDetailsServiceImpl implements UserDetailsService { @@ -21,10 +24,13 @@ public class UserDetailsServiceImpl implements UserDetailsService { User user = userRepository.findByEmail(email) .orElseThrow(() -> new UsernameNotFoundException("User not found: " + email)); + Collection authorities = new ArrayList<>(); + authorities.add(new SimpleGrantedAuthority("ROLE_" + user.getRole())); + return new org.springframework.security.core.userdetails.User( user.getEmail(), user.getPassword(), - new ArrayList<>() + authorities ); } } \ No newline at end of file diff --git a/backend/src/main/java/online/wesal/wesal/service/UserService.java b/backend/src/main/java/online/wesal/wesal/service/UserService.java index 7015dad..9a0e790 100644 --- a/backend/src/main/java/online/wesal/wesal/service/UserService.java +++ b/backend/src/main/java/online/wesal/wesal/service/UserService.java @@ -5,6 +5,7 @@ import online.wesal.wesal.repository.UserRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; @Service @@ -13,6 +14,9 @@ public class UserService { @Autowired private UserRepository userRepository; + @Autowired + private PasswordEncoder passwordEncoder; + public User getCurrentUser() { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); String email = authentication.getName(); @@ -33,4 +37,22 @@ public class UserService { user.setUsername(username); return userRepository.save(user); } + + public boolean isCurrentUserAdmin() { + User user = getCurrentUser(); + return "ADMIN".equals(user.getRole()); + } + + public User createUser(String email, String password, String displayName) { + if (!isCurrentUserAdmin()) { + throw new RuntimeException("Access denied: Admin privileges required"); + } + + if (userRepository.existsByEmail(email)) { + throw new RuntimeException("User with this email already exists"); + } + + User user = new User(email, passwordEncoder.encode(password), displayName); + return userRepository.save(user); + } } \ No newline at end of file