Updated/Introduced: 1. Spring security (currently disabled seen in SecurityConfig.java) 2. Renamed Coloumn names to be more aligned with best practices 3. Return DTO instead of User Object to request
This commit is contained in:
parent
10f417e961
commit
c37139287f
|
@ -19,10 +19,11 @@ repositories {
|
|||
|
||||
dependencies {
|
||||
implementation 'org.springframework.boot:spring-boot-starter-web'
|
||||
implementation 'org.springframework.boot:spring-boot-starter-security'
|
||||
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
|
||||
// Argon2 password hashing
|
||||
implementation 'org.springframework.security:spring-security-crypto:6.3.3'
|
||||
implementation 'org.bouncycastle:bcprov-jdk15on:1.70'
|
||||
implementation 'org.bouncycastle:bcprov-jdk18on:1.78.1'
|
||||
|
||||
// Dotenv manager
|
||||
implementation 'io.github.cdimascio:java-dotenv:5.2.2'
|
||||
|
@ -30,8 +31,14 @@ dependencies {
|
|||
// Minio S3 Storage
|
||||
implementation 'io.minio:minio:8.5.12'
|
||||
|
||||
// JWT token libary (jjwt)
|
||||
implementation 'io.jsonwebtoken:jjwt-api:0.12.6'
|
||||
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.12.6'
|
||||
runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.12.6' // or 'io.jsonwebtoken:jjwt-gson:0.12.6' for gson
|
||||
|
||||
runtimeOnly 'org.postgresql:postgresql'
|
||||
testImplementation 'org.springframework.boot:spring-boot-starter-test'
|
||||
testImplementation 'org.springframework.security:spring-security-test'
|
||||
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
package com.mixel.docusphere.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
public class SecurityConfig {
|
||||
|
||||
@Bean
|
||||
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.csrf(csrf -> csrf.disable()) // Disable CSRF protection
|
||||
.authorizeHttpRequests(auth -> auth
|
||||
.anyRequest().permitAll() // Allow all requests without authentication
|
||||
);
|
||||
|
||||
return http.build();
|
||||
}
|
||||
}
|
|
@ -23,24 +23,24 @@ public class UserController {
|
|||
}
|
||||
|
||||
@GetMapping
|
||||
public List<User> getAllUsers() {
|
||||
public List<UserDTO> getAllUsers() {
|
||||
return userService.findAll();
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
public ResponseEntity<User> getUserById(@PathVariable UUID id) {
|
||||
Optional<User> user = userService.findById(id);
|
||||
public ResponseEntity<UserDTO> getUserById(@PathVariable UUID id) {
|
||||
Optional<UserDTO> user = userService.findById(id);
|
||||
return user.map(ResponseEntity::ok).orElseGet(() -> ResponseEntity.notFound().build());
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
public User createUser(@RequestBody UserDTO userDTO) {
|
||||
public UserDTO createUser(@RequestBody UserDTO userDTO) {
|
||||
return userService.save(userDTO);
|
||||
}
|
||||
|
||||
@PutMapping("/{id}")
|
||||
public ResponseEntity<User> updateUser(@PathVariable UUID id, @RequestBody UserDTO userDTO) {
|
||||
Optional<User> updatedUser = userService.update(id, userDTO);
|
||||
public ResponseEntity<UserDTO> updateUser(@PathVariable UUID id, @RequestBody UserDTO userDTO) {
|
||||
Optional<UserDTO> updatedUser = userService.update(id, userDTO);
|
||||
return updatedUser.map(ResponseEntity::ok).orElseGet(() -> ResponseEntity.notFound().build());
|
||||
}
|
||||
|
||||
|
|
|
@ -1,10 +1,14 @@
|
|||
package com.mixel.docusphere.dto;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
public class UserDTO {
|
||||
private String username;
|
||||
private String name;
|
||||
private String email;
|
||||
private String password;
|
||||
private String password; // Only Include for creation and update
|
||||
private LocalDateTime createdAt;
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
// Getters and Setters
|
||||
public String getUsername() {
|
||||
|
@ -38,4 +42,20 @@ public class UserDTO {
|
|||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public LocalDateTime getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(LocalDateTime createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
|
||||
public LocalDateTime getUpdatedAt() {
|
||||
return updatedAt;
|
||||
}
|
||||
|
||||
public void setUpdatedAt(LocalDateTime updatedAt) {
|
||||
this.updatedAt = updatedAt;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,44 +1,45 @@
|
|||
package com.mixel.docusphere.entity;
|
||||
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import jakarta.persistence.*;
|
||||
import org.hibernate.annotations.CreationTimestamp;
|
||||
import org.hibernate.annotations.UpdateTimestamp;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.UUID;
|
||||
|
||||
@Entity
|
||||
@Table(name = "Users")
|
||||
public class User {
|
||||
public class User implements UserDetails {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.UUID)
|
||||
@Column(name = "UserID", updatable = false, nullable = false)
|
||||
@Column(name = "user_id", updatable = false, nullable = false)
|
||||
private UUID userId;
|
||||
|
||||
@Column(name = "Username", nullable = false, unique = true)
|
||||
@Column(name = "username", nullable = false, unique = true)
|
||||
private String username;
|
||||
|
||||
@Column(name = "Name", nullable = false)
|
||||
@Column(name = "name", nullable = false)
|
||||
private String name;
|
||||
|
||||
@Column(name = "Email", nullable = false, unique = true)
|
||||
@Column(name = "email", nullable = false, unique = true)
|
||||
private String email;
|
||||
|
||||
@JsonIgnore
|
||||
@Column(name = "PasswordHash", nullable = false)
|
||||
@Column(name = "password_hash", nullable = false)
|
||||
private String passwordHash;
|
||||
|
||||
@JsonIgnore
|
||||
@Column(name = "PasswordSalt", nullable = false)
|
||||
private String passwordSalt;
|
||||
|
||||
@CreationTimestamp
|
||||
@Column(name = "CreatedAt", nullable = false, updatable = false)
|
||||
@Column(name = "created_at", nullable = false, updatable = false)
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
@UpdateTimestamp
|
||||
@Column(name = "UpdatedAt", nullable = false)
|
||||
@Column(name = "updated_at", nullable = false)
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
@PrePersist
|
||||
|
@ -93,14 +94,6 @@ public class User {
|
|||
this.passwordHash = passwordHash;
|
||||
}
|
||||
|
||||
public String getPasswordSalt() {
|
||||
return passwordSalt;
|
||||
}
|
||||
|
||||
public void setPasswordSalt(String passwordSalt) {
|
||||
this.passwordSalt = passwordSalt;
|
||||
}
|
||||
|
||||
public LocalDateTime getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
@ -116,4 +109,38 @@ public class User {
|
|||
public void setUpdatedAt(LocalDateTime updatedAt) {
|
||||
this.updatedAt = updatedAt;
|
||||
}
|
||||
|
||||
// UserDetails interface methods
|
||||
@Override
|
||||
public Collection<? extends GrantedAuthority> getAuthorities() {
|
||||
// Return the authorities granted to the user
|
||||
return Collections.emptyList(); // Implement this based on your requirements
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
@Override
|
||||
public String getPassword() {
|
||||
return passwordHash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAccountNonExpired() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAccountNonLocked() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCredentialsNonExpired() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package com.mixel.docusphere.mapper;
|
||||
|
||||
import com.mixel.docusphere.dto.UserDTO;
|
||||
import com.mixel.docusphere.entity.User;
|
||||
|
||||
public class UserMapper {
|
||||
public static UserDTO toDTO(User user) {
|
||||
UserDTO userDTO = new UserDTO();
|
||||
userDTO.setUsername(user.getUsername());
|
||||
userDTO.setName(user.getName());
|
||||
userDTO.setEmail(user.getEmail());
|
||||
userDTO.setCreatedAt(user.getCreatedAt());
|
||||
userDTO.setUpdatedAt(user.getUpdatedAt());
|
||||
|
||||
return userDTO;
|
||||
}
|
||||
|
||||
public static User toEntity(UserDTO userDTO) {
|
||||
User user = new User();
|
||||
user.setUsername(userDTO.getUsername());
|
||||
user.setName(userDTO.getName());
|
||||
user.setEmail(userDTO.getEmail());
|
||||
|
||||
return user;
|
||||
}
|
||||
}
|
|
@ -2,14 +2,15 @@ package com.mixel.docusphere.service;
|
|||
|
||||
import com.mixel.docusphere.dto.UserDTO;
|
||||
import com.mixel.docusphere.entity.User;
|
||||
import com.mixel.docusphere.mapper.UserMapper;
|
||||
import com.mixel.docusphere.repository.UserRepository;
|
||||
import org.springframework.security.crypto.argon2.Argon2PasswordEncoder;
|
||||
import org.springframework.security.crypto.keygen.KeyGenerators;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
public class UserService {
|
||||
|
@ -24,25 +25,24 @@ public class UserService {
|
|||
this.userRepository = userRepository;
|
||||
}
|
||||
|
||||
public List<User> findAll() {
|
||||
return userRepository.findAll();
|
||||
public List<UserDTO> findAll() {
|
||||
return userRepository.findAll().stream()
|
||||
.map(UserMapper::toDTO)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public Optional<User> findById(UUID id) {
|
||||
return userRepository.findById(id);
|
||||
public Optional<UserDTO> findById(UUID id) {
|
||||
return userRepository.findById(id)
|
||||
.map(UserMapper::toDTO);
|
||||
}
|
||||
|
||||
public User save(UserDTO userDTO) {
|
||||
User user = new User();
|
||||
user.setUsername(userDTO.getUsername());
|
||||
user.setName(userDTO.getName());
|
||||
user.setEmail(userDTO.getEmail());
|
||||
|
||||
public UserDTO save(UserDTO userDTO) {
|
||||
User user = UserMapper.toEntity(userDTO);
|
||||
isPasswordAlreadySet(userDTO, user);
|
||||
return userRepository.save(user);
|
||||
return UserMapper.toDTO(userRepository.save(user));
|
||||
}
|
||||
|
||||
public Optional<User> update(UUID id, UserDTO userDTO){
|
||||
public Optional<UserDTO> update(UUID id, UserDTO userDTO){
|
||||
Optional<User> userOptional = userRepository.findById(id);
|
||||
if (userOptional.isPresent()) {
|
||||
User user = userOptional.get();
|
||||
|
@ -51,17 +51,14 @@ public class UserService {
|
|||
user.setEmail(userDTO.getEmail());
|
||||
|
||||
isPasswordAlreadySet(userDTO, user);
|
||||
return Optional.of(userRepository.save(user));
|
||||
return Optional.of(UserMapper.toDTO(userRepository.save(user)));
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
private void isPasswordAlreadySet(UserDTO userDTO, User user) {
|
||||
if (userDTO.getPassword() != null && !userDTO.getPassword().isEmpty()) {
|
||||
final String salt = KeyGenerators.string().generateKey();
|
||||
user.setPasswordSalt(salt);
|
||||
final String saltedPassword = salt + userDTO.getPassword();
|
||||
user.setPasswordHash(passwordEncoder.encode(saltedPassword));
|
||||
user.setPasswordHash(passwordEncoder.encode(userDTO.getPassword()));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue