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 {
|
dependencies {
|
||||||
implementation 'org.springframework.boot:spring-boot-starter-web'
|
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'
|
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
|
||||||
// Argon2 password hashing
|
// Argon2 password hashing
|
||||||
implementation 'org.springframework.security:spring-security-crypto:6.3.3'
|
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
|
// Dotenv manager
|
||||||
implementation 'io.github.cdimascio:java-dotenv:5.2.2'
|
implementation 'io.github.cdimascio:java-dotenv:5.2.2'
|
||||||
|
@ -30,8 +31,14 @@ dependencies {
|
||||||
// Minio S3 Storage
|
// Minio S3 Storage
|
||||||
implementation 'io.minio:minio:8.5.12'
|
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'
|
runtimeOnly 'org.postgresql:postgresql'
|
||||||
testImplementation 'org.springframework.boot:spring-boot-starter-test'
|
testImplementation 'org.springframework.boot:spring-boot-starter-test'
|
||||||
|
testImplementation 'org.springframework.security:spring-security-test'
|
||||||
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
|
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
|
@GetMapping
|
||||||
public List<User> getAllUsers() {
|
public List<UserDTO> getAllUsers() {
|
||||||
return userService.findAll();
|
return userService.findAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/{id}")
|
@GetMapping("/{id}")
|
||||||
public ResponseEntity<User> getUserById(@PathVariable UUID id) {
|
public ResponseEntity<UserDTO> getUserById(@PathVariable UUID id) {
|
||||||
Optional<User> user = userService.findById(id);
|
Optional<UserDTO> user = userService.findById(id);
|
||||||
return user.map(ResponseEntity::ok).orElseGet(() -> ResponseEntity.notFound().build());
|
return user.map(ResponseEntity::ok).orElseGet(() -> ResponseEntity.notFound().build());
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping
|
@PostMapping
|
||||||
public User createUser(@RequestBody UserDTO userDTO) {
|
public UserDTO createUser(@RequestBody UserDTO userDTO) {
|
||||||
return userService.save(userDTO);
|
return userService.save(userDTO);
|
||||||
}
|
}
|
||||||
|
|
||||||
@PutMapping("/{id}")
|
@PutMapping("/{id}")
|
||||||
public ResponseEntity<User> updateUser(@PathVariable UUID id, @RequestBody UserDTO userDTO) {
|
public ResponseEntity<UserDTO> updateUser(@PathVariable UUID id, @RequestBody UserDTO userDTO) {
|
||||||
Optional<User> updatedUser = userService.update(id, userDTO);
|
Optional<UserDTO> updatedUser = userService.update(id, userDTO);
|
||||||
return updatedUser.map(ResponseEntity::ok).orElseGet(() -> ResponseEntity.notFound().build());
|
return updatedUser.map(ResponseEntity::ok).orElseGet(() -> ResponseEntity.notFound().build());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,14 @@
|
||||||
package com.mixel.docusphere.dto;
|
package com.mixel.docusphere.dto;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
public class UserDTO {
|
public class UserDTO {
|
||||||
private String username;
|
private String username;
|
||||||
private String name;
|
private String name;
|
||||||
private String email;
|
private String email;
|
||||||
private String password;
|
private String password; // Only Include for creation and update
|
||||||
|
private LocalDateTime createdAt;
|
||||||
|
private LocalDateTime updatedAt;
|
||||||
|
|
||||||
// Getters and Setters
|
// Getters and Setters
|
||||||
public String getUsername() {
|
public String getUsername() {
|
||||||
|
@ -38,4 +42,20 @@ public class UserDTO {
|
||||||
public void setPassword(String password) {
|
public void setPassword(String password) {
|
||||||
this.password = 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;
|
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 com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
import jakarta.persistence.*;
|
import jakarta.persistence.*;
|
||||||
import org.hibernate.annotations.CreationTimestamp;
|
import org.hibernate.annotations.CreationTimestamp;
|
||||||
import org.hibernate.annotations.UpdateTimestamp;
|
import org.hibernate.annotations.UpdateTimestamp;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "Users")
|
@Table(name = "Users")
|
||||||
public class User {
|
public class User implements UserDetails {
|
||||||
@Id
|
@Id
|
||||||
@GeneratedValue(strategy = GenerationType.UUID)
|
@GeneratedValue(strategy = GenerationType.UUID)
|
||||||
@Column(name = "UserID", updatable = false, nullable = false)
|
@Column(name = "user_id", updatable = false, nullable = false)
|
||||||
private UUID userId;
|
private UUID userId;
|
||||||
|
|
||||||
@Column(name = "Username", nullable = false, unique = true)
|
@Column(name = "username", nullable = false, unique = true)
|
||||||
private String username;
|
private String username;
|
||||||
|
|
||||||
@Column(name = "Name", nullable = false)
|
@Column(name = "name", nullable = false)
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
@Column(name = "Email", nullable = false, unique = true)
|
@Column(name = "email", nullable = false, unique = true)
|
||||||
private String email;
|
private String email;
|
||||||
|
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
@Column(name = "PasswordHash", nullable = false)
|
@Column(name = "password_hash", nullable = false)
|
||||||
private String passwordHash;
|
private String passwordHash;
|
||||||
|
|
||||||
@JsonIgnore
|
|
||||||
@Column(name = "PasswordSalt", nullable = false)
|
|
||||||
private String passwordSalt;
|
|
||||||
|
|
||||||
@CreationTimestamp
|
@CreationTimestamp
|
||||||
@Column(name = "CreatedAt", nullable = false, updatable = false)
|
@Column(name = "created_at", nullable = false, updatable = false)
|
||||||
private LocalDateTime createdAt;
|
private LocalDateTime createdAt;
|
||||||
|
|
||||||
@UpdateTimestamp
|
@UpdateTimestamp
|
||||||
@Column(name = "UpdatedAt", nullable = false)
|
@Column(name = "updated_at", nullable = false)
|
||||||
private LocalDateTime updatedAt;
|
private LocalDateTime updatedAt;
|
||||||
|
|
||||||
@PrePersist
|
@PrePersist
|
||||||
|
@ -93,14 +94,6 @@ public class User {
|
||||||
this.passwordHash = passwordHash;
|
this.passwordHash = passwordHash;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getPasswordSalt() {
|
|
||||||
return passwordSalt;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPasswordSalt(String passwordSalt) {
|
|
||||||
this.passwordSalt = passwordSalt;
|
|
||||||
}
|
|
||||||
|
|
||||||
public LocalDateTime getCreatedAt() {
|
public LocalDateTime getCreatedAt() {
|
||||||
return createdAt;
|
return createdAt;
|
||||||
}
|
}
|
||||||
|
@ -116,4 +109,38 @@ public class User {
|
||||||
public void setUpdatedAt(LocalDateTime updatedAt) {
|
public void setUpdatedAt(LocalDateTime updatedAt) {
|
||||||
this.updatedAt = 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.dto.UserDTO;
|
||||||
import com.mixel.docusphere.entity.User;
|
import com.mixel.docusphere.entity.User;
|
||||||
|
import com.mixel.docusphere.mapper.UserMapper;
|
||||||
import com.mixel.docusphere.repository.UserRepository;
|
import com.mixel.docusphere.repository.UserRepository;
|
||||||
import org.springframework.security.crypto.argon2.Argon2PasswordEncoder;
|
import org.springframework.security.crypto.argon2.Argon2PasswordEncoder;
|
||||||
import org.springframework.security.crypto.keygen.KeyGenerators;
|
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class UserService {
|
public class UserService {
|
||||||
|
@ -24,25 +25,24 @@ public class UserService {
|
||||||
this.userRepository = userRepository;
|
this.userRepository = userRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<User> findAll() {
|
public List<UserDTO> findAll() {
|
||||||
return userRepository.findAll();
|
return userRepository.findAll().stream()
|
||||||
|
.map(UserMapper::toDTO)
|
||||||
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
public Optional<User> findById(UUID id) {
|
public Optional<UserDTO> findById(UUID id) {
|
||||||
return userRepository.findById(id);
|
return userRepository.findById(id)
|
||||||
|
.map(UserMapper::toDTO);
|
||||||
}
|
}
|
||||||
|
|
||||||
public User save(UserDTO userDTO) {
|
public UserDTO save(UserDTO userDTO) {
|
||||||
User user = new User();
|
User user = UserMapper.toEntity(userDTO);
|
||||||
user.setUsername(userDTO.getUsername());
|
|
||||||
user.setName(userDTO.getName());
|
|
||||||
user.setEmail(userDTO.getEmail());
|
|
||||||
|
|
||||||
isPasswordAlreadySet(userDTO, user);
|
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);
|
Optional<User> userOptional = userRepository.findById(id);
|
||||||
if (userOptional.isPresent()) {
|
if (userOptional.isPresent()) {
|
||||||
User user = userOptional.get();
|
User user = userOptional.get();
|
||||||
|
@ -51,17 +51,14 @@ public class UserService {
|
||||||
user.setEmail(userDTO.getEmail());
|
user.setEmail(userDTO.getEmail());
|
||||||
|
|
||||||
isPasswordAlreadySet(userDTO, user);
|
isPasswordAlreadySet(userDTO, user);
|
||||||
return Optional.of(userRepository.save(user));
|
return Optional.of(UserMapper.toDTO(userRepository.save(user)));
|
||||||
}
|
}
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void isPasswordAlreadySet(UserDTO userDTO, User user) {
|
private void isPasswordAlreadySet(UserDTO userDTO, User user) {
|
||||||
if (userDTO.getPassword() != null && !userDTO.getPassword().isEmpty()) {
|
if (userDTO.getPassword() != null && !userDTO.getPassword().isEmpty()) {
|
||||||
final String salt = KeyGenerators.string().generateKey();
|
user.setPasswordHash(passwordEncoder.encode(userDTO.getPassword()));
|
||||||
user.setPasswordSalt(salt);
|
|
||||||
final String saltedPassword = salt + userDTO.getPassword();
|
|
||||||
user.setPasswordHash(passwordEncoder.encode(saltedPassword));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue