Skip to content
Open
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
10 changes: 10 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,16 @@
<artifactId>spring-dotenv</artifactId>
<version>3.0.0</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
<version>3.4.5</version>
</dependency>
</dependencies>

<build>
Expand Down
53 changes: 53 additions & 0 deletions src/main/java/gtp/filesmanager/config/SecurityConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package gtp.filesmanager.config;

import gtp.filesmanager.service.UsersDetailService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
@EnableWebSecurity
public class SecurityConfig {

@Autowired
private UsersDetailService usersDetailService;

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf(config -> config.disable())
.authorizeHttpRequests(request -> request
.requestMatchers("/register","/login") //allow login and register url
.permitAll()
.anyRequest()
.authenticated()//
)
.httpBasic(Customizer.withDefaults())
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
return http.build();
}

@Bean
public AuthenticationProvider authenticationProvider(){
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setPasswordEncoder(new BCryptPasswordEncoder(12));
authProvider.setUserDetailsService(usersDetailService);

return authProvider;
}

@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration configuration) throws Exception {
return configuration.getAuthenticationManager();
}
}
47 changes: 47 additions & 0 deletions src/main/java/gtp/filesmanager/controller/UserController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package gtp.filesmanager.controller;

import gtp.filesmanager.dto.request.LoginRequest;
import gtp.filesmanager.dto.request.RegisterRequest;
import gtp.filesmanager.model.Users;
import gtp.filesmanager.service.UserServiceImpl;
import org.springframework.web.bind.annotation.*;

import java.util.logging.Logger;

@RestController
//@RequestMapping("/api/auth")
public class UserController {
private final Logger logger = Logger.getLogger(UserController.class.getName());
private final UserServiceImpl userServiceImpl;

//inject UserServiceImpl via constructor
public UserController(UserServiceImpl userServiceImpl) {
this.userServiceImpl = userServiceImpl;
}

@PostMapping(path = "/register")
protected Object registerUser(@RequestBody RegisterRequest registerRequest){
logger.info("Registering user with name: " + registerRequest.getUserName());
if(registerRequest.getUserName() == null || registerRequest.getUserEmail() == null || registerRequest.getPassword() == null){
throw new RuntimeException("Invalid request");
}
try{
return userServiceImpl.registerUser(registerRequest);
}catch (RuntimeException e){
return e.getMessage();
}
}

@PostMapping(path = "/login")
protected Object loginUser(@RequestBody LoginRequest loginRequest){
logger.info("Logging in user with name: " + loginRequest.getUserName());
if(loginRequest.getUserName() == null || loginRequest.getPassword() == null){
throw new RuntimeException("Invalid request");
}
try{
return userServiceImpl.loginUser(loginRequest);
}catch (RuntimeException e){
return e.getMessage();
}
}
}
11 changes: 11 additions & 0 deletions src/main/java/gtp/filesmanager/dto/request/LoginRequest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package gtp.filesmanager.dto.request;

import lombok.Getter;
import org.antlr.v4.runtime.misc.NotNull;

//DTO to accept when logging in
@Getter
public class LoginRequest {
private String userName;
private String password;
}
11 changes: 11 additions & 0 deletions src/main/java/gtp/filesmanager/dto/request/RegisterRequest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package gtp.filesmanager.dto.request;

import lombok.Getter;

@Getter
public class RegisterRequest {

private String userName;
private String password;
private String userEmail;
}
11 changes: 11 additions & 0 deletions src/main/java/gtp/filesmanager/dto/response/UserResponse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package gtp.filesmanager.dto.response;

import lombok.Getter;

@Getter
public class UserResponse {
private Integer Id;
private String userName;
private String userEmail;
private Boolean isActive;
}
Empty file.
53 changes: 53 additions & 0 deletions src/main/java/gtp/filesmanager/model/UserPrincipal.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package gtp.filesmanager.model;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import java.util.Collection;
import java.util.List;

public class UserPrincipal implements UserDetails {

private final Users userEntity;

public UserPrincipal(Users userEntity){
this.userEntity = userEntity;
}


@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return List.of();
}

@Override
public String getPassword() {
return userEntity.getPassword();
}

@Override
public String getUsername() {
return userEntity.getUserName();
}

@Override
public boolean isAccountNonExpired() {
return UserDetails.super.isAccountNonExpired();
}

@Override
public boolean isAccountNonLocked() {
return UserDetails.super.isAccountNonLocked();
}

@Override
public boolean isCredentialsNonExpired() {
return UserDetails.super.isCredentialsNonExpired();
}

@Override
public boolean isEnabled() {
return UserDetails.super.isEnabled();
}
}
39 changes: 39 additions & 0 deletions src/main/java/gtp/filesmanager/model/Users.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package gtp.filesmanager.model;

import com.fasterxml.jackson.annotation.JsonIgnore;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.Setter;

import java.sql.Date;

//create a user entity
@Getter
@Entity
@Table(name = "users")
public class Users {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;

@Setter
@Column(unique = true,nullable = false)
private String userName; //unique for all

@Setter
@JsonIgnore //ignores this field when serializing the object to JSON
private String password;

@Setter
@Column(unique = true,nullable = false)
private String userEmail;

@Setter
private Boolean isActive;

@Setter
private Date createdAt;

@Setter
private Date updatedAt;
}
9 changes: 9 additions & 0 deletions src/main/java/gtp/filesmanager/repository/UserRepository.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package gtp.filesmanager.repository;

import gtp.filesmanager.model.Users;
import org.springframework.data.repository.CrudRepository;

public interface UserRepository extends CrudRepository<Users, Integer> {
public Users findByUserName(String userName);
public Users findByUserEmail(String userEmail);
}
10 changes: 10 additions & 0 deletions src/main/java/gtp/filesmanager/service/UserService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package gtp.filesmanager.service;

import gtp.filesmanager.dto.request.LoginRequest;
import gtp.filesmanager.dto.request.RegisterRequest;
import gtp.filesmanager.model.Users;

public interface UserService {
abstract Users registerUser(RegisterRequest registerRequest);
abstract Users loginUser(LoginRequest loginRequest);
}
67 changes: 67 additions & 0 deletions src/main/java/gtp/filesmanager/service/UserServiceImpl.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package gtp.filesmanager.service;

import gtp.filesmanager.dto.request.LoginRequest;
import gtp.filesmanager.dto.request.RegisterRequest;
import gtp.filesmanager.model.Users;
import gtp.filesmanager.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl implements UserService { // Removed 'abstract' as this should be a concrete implementation

@Autowired
private UserRepository userRepository;

private BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(12); // Renamed from 'encoder' for clarity

@Autowired
private AuthenticationManager authManager;


@Override
public Users registerUser(RegisterRequest registerRequest) {
// Check if a user already exists
if (userRepository.findByUserName(registerRequest.getUserName()) != null) {
throw new RuntimeException("Username already exists");
}

if (userRepository.findByUserEmail(registerRequest.getUserEmail()) != null) {
throw new RuntimeException("Email already registered");
}

// Create a new user entity
Users newUser = new Users();
newUser.setUserName(registerRequest.getUserName());
newUser.setUserEmail(registerRequest.getUserEmail());
newUser.setPassword(passwordEncoder.encode(registerRequest.getPassword())); // Encode password

return userRepository.save(newUser);
}

@Override
public Users loginUser(LoginRequest loginRequest){
Users user = userRepository.findByUserName(loginRequest.getUserName());
if(user == null){
throw new RuntimeException("Invalid username");
}

//Authenticate user
Authentication authentication = authManager
.authenticate(new UsernamePasswordAuthenticationToken(
loginRequest.getUserName(), loginRequest.getPassword()
));

//check user authentication is successful
if (authentication.isAuthenticated()){
return user;
}else{
throw new RuntimeException("Invalid password");
}
}

}
24 changes: 24 additions & 0 deletions src/main/java/gtp/filesmanager/service/UsersDetailService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package gtp.filesmanager.service;

import gtp.filesmanager.model.UserPrincipal;
import gtp.filesmanager.model.Users;
import gtp.filesmanager.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
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;

@Service
public class UsersDetailService implements UserDetailsService {

@Autowired
private UserRepository userRepository;

@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Users userEntity = userRepository.findByUserName(username);

return new UserPrincipal(userEntity);
}
}
1 change: 1 addition & 0 deletions src/main/resources/application.properties
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ spring.datasource.username=${POSTGRES_USER}
spring.datasource.password=${POSTGRES_PASSWORD}
spring.datasource.driver-class-name=org.postgresql.Driver

spring.jpa.hibernate.ddl-auto=update
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.show-sql=true