Added tester React frontend, changed DTOs and User and UserRepository to fit around specialize USER key

This commit is contained in:
2025-01-04 13:37:11 -05:00
parent fdbbcc8386
commit 3eba8eed73
33 changed files with 6839 additions and 10 deletions

View File

@@ -0,0 +1,15 @@
package com.inoct.NoctuAuthenticator;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
public class JWTUtils {
public String generateToken(String username, String email, String source) {
return null;
}
}

View File

@@ -0,0 +1,46 @@
package com.inoct.NoctuAuthenticator.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.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
@Configuration
public class SecurityConfig {
@Bean
//TODO Make sure it is rate-limited.
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/login", "/register", "/oauth/**").permitAll() // Public authentication endpoints
.anyRequest().authenticated() // Secure other endpoints
)
.csrf(csrf -> csrf.disable())
.cors(cors -> cors.configurationSource(corsConfigurationSource())); // Enable CORS
return http.build();
}
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.addAllowedOrigin("http://localhost:3000"); // Frontend URL
corsConfiguration.addAllowedHeader("*"); // Allow all headers
corsConfiguration.addAllowedMethod("*"); // Allow all HTTP methods
corsConfiguration.setAllowCredentials(true); // Allow cookies/auth headers
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", corsConfiguration); // Apply to all endpoints
return source;
}
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}

View File

@@ -0,0 +1,59 @@
package com.inoct.NoctuAuthenticator.controller;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.web.bind.annotation.*;
import com.inoct.NoctuAuthenticator.dto.UserDTO;
import com.inoct.NoctuAuthenticator.entity.User;
import com.inoct.NoctuAuthenticator.service.UserService;
@RestController
@RequestMapping("/")
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/login")
public ResponseEntity<UserDTO> loginUser(@RequestBody String username, @RequestBody String password) {
UserDTO user = userService.authenticateUser(username, password);
return user != null ? ResponseEntity.ok(user) : ResponseEntity.notFound().build();
}
@GetMapping("/oauth")
public ResponseEntity<UserDTO> oauthLogin(@RequestBody String username, @RequestBody String password) {
UserDTO user = userService.authenticateUser(username, password);
return user != null ? ResponseEntity.ok(user) : ResponseEntity.notFound().build();
}
@PostMapping("/register")
public ResponseEntity<User> registerUser(@RequestBody UserDTO userDTO) {
System.out.println(userDTO);
User user = userService.createUser(userDTO);
return user != null ? ResponseEntity.ok(user) : ResponseEntity.notFound().build();
}
@PutMapping("/{id}")
public ResponseEntity<UserDTO> updateUser(@PathVariable Long id, @RequestBody User userDetails) {
UserDTO updatedUser = userService.updateUser(id, userDetails);
return updatedUser != null ? ResponseEntity.ok(updatedUser) : ResponseEntity.notFound().build();
}
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
userService.deleteUser(id);
return ResponseEntity.noContent().build();
}
@GetMapping("/welcome")
public String welcome(@AuthenticationPrincipal OAuth2User user) {
Map<String, Object> attributes = user.getAttributes();
return "Welcome " + attributes.get("name") + "!";
}
}

View File

@@ -0,0 +1,28 @@
package com.inoct.NoctuAuthenticator.dto;
import java.time.LocalDateTime;
import jakarta.validation.constraints.NotNull;
public class OauthDataDTO {
private Long id;
private Long userId;
@NotNull
private String provider;
@NotNull
private String providerUserId;
@NotNull
private String accessToken;
@NotNull
private String refreshToken;
LocalDateTime expiresAt;
@NotNull
private String [] scopes;
}

View File

@@ -0,0 +1,42 @@
package com.inoct.NoctuAuthenticator.dto;
import java.util.List;
import com.inoct.NoctuAuthenticator.validation.ValidateUser;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
@ValidateUser
public class UserDTO {
private Long id;
@NotNull
private String username;
@NotNull
private String firstName;
@NotNull
private String lastName;
private String email;
private List<String> roles;
private String password;
@NotNull
private boolean oauth;
private OauthDataDTO oauthData;
}

View File

@@ -0,0 +1,65 @@
package com.inoct.NoctuAuthenticator.entity;
import java.time.LocalDateTime;
import java.util.UUID;
import org.hibernate.annotations.UpdateTimestamp;
import org.springframework.data.annotation.CreatedDate;
import jakarta.persistence.*;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Entity
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Setter
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
@Builder
public class OauthData {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@EqualsAndHashCode.Include
private Long id;
@EqualsAndHashCode.Include
@Transient
private final UUID tempId = UUID.randomUUID();
@OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
@JoinColumn(name = "user_id")
@NotNull
private User user;
@NotNull
private String provider;
@NotNull
private String providerUserId;
@NotNull
private String accessToken;
@NotNull
private String refreshToken;
LocalDateTime expiresAt;
@NotNull
private String [] scopes;
@CreatedDate
@Column(updatable = false)
LocalDateTime createdDateTime;
@UpdateTimestamp
LocalDateTime updateTimestamp;
}

View File

@@ -0,0 +1,80 @@
package com.inoct.NoctuAuthenticator.entity;
import java.time.LocalDate;
import org.hibernate.annotations.UpdateTimestamp;
import org.hibernate.validator.constraints.UniqueElements;
import org.springframework.data.annotation.CreatedDate;
import com.inoct.NoctuAuthenticator.validation.ValidateUser;
import java.util.List;
import java.util.UUID;
import jakarta.persistence.*;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.EqualsAndHashCode.Include;
@Entity
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
@ValidateUser
@Table(name = "users",
uniqueConstraints = {
@UniqueConstraint(columnNames = "username"),
@UniqueConstraint(columnNames = "email")
})
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Include
private Long id;
@Include
@NotNull
private String username;
@Transient
@Include
private final UUID tempId = UUID.randomUUID();
@NotNull
private String firstName;
@NotNull
private String lastName;
@Include
private String email;
private String password;
private List<String> roles;
@OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
OauthData oauthData;
@NotNull
private boolean oauth;
@CreatedDate
@Column(updatable = false)
private LocalDate createdDate;
@UpdateTimestamp
private LocalDate updatedDate;
}

View File

@@ -0,0 +1,11 @@
package com.inoct.NoctuAuthenticator.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import com.inoct.NoctuAuthenticator.entity.User;
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
User findByUsername(String username);
User findByEmail(String email);
}

View File

@@ -0,0 +1,27 @@
package com.inoct.NoctuAuthenticator.service;
import com.inoct.NoctuAuthenticator.dto.UserDTO;
import com.inoct.NoctuAuthenticator.entity.User;
import org.springframework.stereotype.Service;
import java.util.List;
public interface UserService {
List<UserDTO> findAllUsers();
UserDTO authenticateUser(String userName, String password);
UserDTO getUserById(Long id);
User createUser(UserDTO user);
void deleteUser(Long id);
UserDTO findUserByUsername(String username);
UserDTO findUserByEmail(String email);
UserDTO updateUser(Long id, User userDetails);
}

View File

@@ -0,0 +1,91 @@
package com.inoct.NoctuAuthenticator.service.impl;
import com.inoct.NoctuAuthenticator.dto.UserDTO;
import com.inoct.NoctuAuthenticator.entity.User;
import com.inoct.NoctuAuthenticator.repository.UserRepository;
import com.inoct.NoctuAuthenticator.service.UserService;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserRepository userRepository;
@Override
public User createUser(UserDTO userDTO) {
User user = mapFromDTO(userDTO);
return userRepository.save(user);
}
@Override
public void deleteUser(Long id) {
// TODO Auto-generated method stub
}
@Override
public List<UserDTO> findAllUsers() {
// TODO Auto-generated method stub
return null;
}
@Override
public UserDTO findUserByEmail(String email) {
// TODO Auto-generated method stub
return null;
}
@Override
public UserDTO getUserById(Long id) {
// TODO Auto-generated method stub
return null;
}
@Override
public UserDTO findUserByUsername(String username) {
// TODO Auto-generated method stub
return null;
}
@Override
public UserDTO updateUser(Long id, User userDetails) {
// TODO Auto-generated method stub
return null;
}
@Override
public UserDTO authenticateUser(String userName, String password) {
// TODO Auto-generated method stub
return null;
}
private UserDTO mapToDTO(User user) {
UserDTO userDTO = UserDTO.builder()
.id(user.getId())
.username(user.getUsername())
.firstName(user.getFirstName())
.lastName(user.getLastName())
.email(user.getEmail())
.roles(user.getRoles())
.oauth(user.isOauth())
.build();
return userDTO;
}
private User mapFromDTO(UserDTO userDTO) {
User user = User.builder()
.username(userDTO.getUsername())
.firstName(userDTO.getFirstName())
.lastName(userDTO.getLastName())
.email(userDTO.getEmail())
.roles(userDTO.getRoles())
.oauth(userDTO.isOauth())
.build();
return user;
}
}

View File

@@ -0,0 +1,20 @@
package com.inoct.NoctuAuthenticator.validation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import jakarta.validation.Constraint;
import jakarta.validation.Payload;
@Constraint(validatedBy = ValidateUserValidator.class)
@Target({ElementType.TYPE}) // Apply this annotation at the class level
@Retention(RetentionPolicy.RUNTIME)
public @interface ValidateUser {
String message() default "Invalid user details";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}

View File

@@ -0,0 +1,34 @@
package com.inoct.NoctuAuthenticator.validation;
import com.inoct.NoctuAuthenticator.entity.User;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
public class ValidateUserValidator implements ConstraintValidator<ValidateUser, User> {
@Override
public boolean isValid(User user, ConstraintValidatorContext context) {
if (user == null) {
return true; // Validation for null objects is handled separately
}
// Example validation logic:
// Ensure the username is not null and at least 3 characters long
if (user.getUsername() == null || user.getUsername().length() < 3) {
context.buildConstraintViolationWithTemplate("Username must be at least 3 characters long")
.addPropertyNode("username")
.addConstraintViolation();
return false;
}
// Ensure the email follows a specific pattern (simplified example)
if (user.getEmail() != null && !user.getEmail().contains("@")) {
context.buildConstraintViolationWithTemplate("Invalid email format")
.addPropertyNode("email")
.addConstraintViolation();
return false;
}
return true; // Valid user
}
}

View File

@@ -9,7 +9,28 @@ spring.datasource.username=${POSTGRES_USER}
spring.datasource.password=${POSTGRES_PASSWORD}
spring.datasource.driver-class-name=org.postgresql.Driver
#JPA configuration
sprint.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect
spring.jpa.hibernate.ddl-auto=create-drop
# Redis configuration
spring.redis.host=${REDIS_HOST}
spring.redis.port=${REDIS_PORT}
#spring.redis.password=${REDIS_PASSWORD}
#Google Oauth2.0 configuration
# OAuth2 Client Configuration for Google
spring.security.oauth2.client.registration.google.client-id=${GOOGLE_CLIENT_ID}
spring.security.oauth2.client.registration.google.client-secret=${GOOGLE_CLIENT_SECRET}
spring.security.oauth2.client.registration.google.redirect-uri=http://localhost:8080/login/oauth2/code/google
spring.security.oauth2.client.registration.google.scope=email,profile
spring.security.oauth2.client.registration.google.client-authentication-method=post
spring.security.oauth2.client.registration.google.authorization-grant-type=authorization_code
# Provider-specific configuration (optional)
spring.security.oauth2.client.provider.google.authorization-uri=https://accounts.google.com/o/oauth2/v2/auth
spring.security.oauth2.client.provider.google.token-uri=https://oauth2.googleapis.com/token
spring.security.oauth2.client.provider.google.user-info-uri=https://openidconnect.googleapis.com/v1/userinfo
spring.security.oauth2.client.provider.google.user-name-attribute=sub