diff --git a/pom.xml b/pom.xml
index ba1eac8..f4f7a06 100644
--- a/pom.xml
+++ b/pom.xml
@@ -12,7 +12,7 @@
anyame-backend
0.0.1-SNAPSHOT
anyame-backend
- User service for anyame backend
+ User service for anyame
@@ -50,6 +50,10 @@
org.springframework.boot
spring-boot-starter-validation
+
+ org.springframework.security
+ spring-security-oauth2-authorization-server
+
io.jsonwebtoken
diff --git a/src/main/java/com/backend/user/service/anyame/component/AuthorizationProperties.java b/src/main/java/com/backend/user/service/anyame/component/AuthorizationProperties.java
deleted file mode 100644
index 0fdd990..0000000
--- a/src/main/java/com/backend/user/service/anyame/component/AuthorizationProperties.java
+++ /dev/null
@@ -1,104 +0,0 @@
-package com.backend.user.service.anyame.component;
-
-import org.springframework.boot.context.properties.ConfigurationProperties;
-import org.springframework.stereotype.Component;
-
-import java.time.Duration;
-import java.util.List;
-import java.util.Optional;
-
-@Component
-@ConfigurationProperties(prefix = "authorization")
-public class AuthorizationProperties {
-
- private List roles;
- private String hierarchy;
- private String defaultRole;
-
- public List getRoles() {
- return roles;
- }
-
- public String getHierarchy() {
- return hierarchy;
- }
-
- public String getDefaultRole() {
- return defaultRole;
- }
-
- public AuthorizationProperties setRoles(List roles) {
- this.roles = roles;
- return this;
- }
-
- public AuthorizationProperties setHierarchy(String hierarchy) {
- this.hierarchy = hierarchy;
- return this;
- }
-
- public AuthorizationProperties setDefaultRole(String defaultRole) {
- this.defaultRole = defaultRole;
- return this;
- }
-
- public Optional getAccessExpiry(String roleName) {
- return roles.stream()
- .filter(role -> role.getName().equals(roleName))
- .map(RoleConfig::getAccessExpiry)
- .findFirst();
- }
-
- public Optional getRefreshExpiry(String roleName) {
- return roles.stream()
- .filter(role -> role.getName().equals(roleName))
- .map(RoleConfig::getRefreshExpiry)
- .findFirst();
- }
-
- public static class RoleConfig {
-
- private String name;
- private Duration accessExpiry;
- private Duration refreshExpiry;
- private List privileges;
-
- public String getName() {
- return name;
- }
-
- public Duration getAccessExpiry() {
- return accessExpiry;
- }
-
- public Duration getRefreshExpiry() {
- return refreshExpiry;
- }
-
- public List getPrivileges() {
- return privileges;
- }
-
- public RoleConfig setName(String name) {
- this.name = name;
- return this;
- }
-
- public RoleConfig setAccessExpiry(Duration accessExpiry) {
- this.accessExpiry = accessExpiry;
- return this;
- }
-
- public RoleConfig setRefreshExpiry(Duration refreshExpiry) {
- this.refreshExpiry = refreshExpiry;
- return this;
- }
-
- public RoleConfig setPrivileges(List privileges) {
- this.privileges = privileges;
- return this;
- }
-
- }
-
-}
diff --git a/src/main/java/com/backend/user/service/anyame/component/AuthorizationServerProperties.java b/src/main/java/com/backend/user/service/anyame/component/AuthorizationServerProperties.java
new file mode 100644
index 0000000..b43b4a5
--- /dev/null
+++ b/src/main/java/com/backend/user/service/anyame/component/AuthorizationServerProperties.java
@@ -0,0 +1,31 @@
+package com.backend.user.service.anyame.component;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+@ConfigurationProperties(prefix = "spring.security.oauth2.authorizationserver")
+public class AuthorizationServerProperties {
+
+ private String issuerUrl;
+ private String introspectionEndpoint;
+
+ public String getIssuerUrl() {
+ return issuerUrl;
+ }
+
+ public AuthorizationServerProperties setIssuerUrl(String issuerUrl) {
+ this.issuerUrl = issuerUrl;
+ return this;
+ }
+
+ public String getIntrospectionEndpoint() {
+ return introspectionEndpoint;
+ }
+
+ public AuthorizationServerProperties setIntrospectionEndpoint(String introspectionEndpoint) {
+ this.introspectionEndpoint = introspectionEndpoint;
+ return this;
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/backend/user/service/anyame/component/DefaultRoleProvider.java b/src/main/java/com/backend/user/service/anyame/component/DefaultRoleProvider.java
deleted file mode 100644
index 11ee60c..0000000
--- a/src/main/java/com/backend/user/service/anyame/component/DefaultRoleProvider.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package com.backend.user.service.anyame.component;
-
-import com.backend.user.service.anyame.entity.Role;
-import com.backend.user.service.anyame.repository.RoleRepository;
-import jakarta.annotation.PostConstruct;
-import org.springframework.stereotype.Component;
-
-@Component
-public class DefaultRoleProvider {
- private final AuthorizationProperties authorizationProperties;
- private final RoleRepository repository;
- private Role defaultRole;
-
- public DefaultRoleProvider(AuthorizationProperties authorizationProperties, RoleRepository repository) {
- this.authorizationProperties = authorizationProperties;
- this.repository = repository;
- }
-
- @PostConstruct
- public void init() {
- defaultRole = repository.findByName(authorizationProperties.getDefaultRole()).orElseThrow();
- }
-
- public Role getDefaultRole() {
- return defaultRole;
- }
-
-}
diff --git a/src/main/java/com/backend/user/service/anyame/component/JWTSecretProvider.java b/src/main/java/com/backend/user/service/anyame/component/JWTSecretProvider.java
deleted file mode 100644
index a44afd0..0000000
--- a/src/main/java/com/backend/user/service/anyame/component/JWTSecretProvider.java
+++ /dev/null
@@ -1,27 +0,0 @@
-package com.backend.user.service.anyame.component;
-
-import io.jsonwebtoken.security.Keys;
-import jakarta.annotation.PostConstruct;
-import javax.crypto.SecretKey;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.stereotype.Component;
-
-import java.nio.charset.StandardCharsets;
-
-@Component
-public class JWTSecretProvider {
-
- @Value("${jwt.secret}")
- private String jwtSecret;
- private SecretKey secretKey;
-
- @PostConstruct
- public void init() {
- secretKey = Keys.hmacShaKeyFor(jwtSecret.getBytes(StandardCharsets.UTF_8));
- }
-
- public SecretKey getSecretKey() {
- return secretKey;
- }
-
-}
diff --git a/src/main/java/com/backend/user/service/anyame/component/SetupDataLoader.java b/src/main/java/com/backend/user/service/anyame/component/SetupDataLoader.java
deleted file mode 100644
index 1e727bb..0000000
--- a/src/main/java/com/backend/user/service/anyame/component/SetupDataLoader.java
+++ /dev/null
@@ -1,64 +0,0 @@
-package com.backend.user.service.anyame.component;
-
-import com.backend.user.service.anyame.entity.Privilege;
-import com.backend.user.service.anyame.entity.Role;
-import com.backend.user.service.anyame.repository.PrivilegeRepository;
-import com.backend.user.service.anyame.repository.RoleRepository;
-import org.springframework.context.ApplicationListener;
-import org.springframework.context.event.ContextRefreshedEvent;
-import org.springframework.stereotype.Component;
-
-import java.util.Collection;
-import java.util.List;
-import java.util.Optional;
-
-@Component
-public class SetupDataLoader implements
- ApplicationListener {
-
- boolean alreadySetup = false;
- private final AuthorizationProperties authorizationProperties;
- private final RoleRepository roleRepository;
- private final PrivilegeRepository privilegeRepository;
-
- public SetupDataLoader(AuthorizationProperties authorizationProperties,
- RoleRepository roleRepository,
- PrivilegeRepository privilegeRepository) {
- this.authorizationProperties = authorizationProperties;
- this.roleRepository = roleRepository;
- this.privilegeRepository = privilegeRepository;
- }
-
- @Override
- public void onApplicationEvent(ContextRefreshedEvent event) {
- if (alreadySetup)
- return;
- authorizationProperties.getRoles().forEach(roleConfig -> {
- List privileges = roleConfig.getPrivileges()
- .stream()
- .map(this::createPrivilegeIfNotFound)
- .toList();
- createRoleIfNotFound(roleConfig.getName(), privileges);
- });
- }
-
- Privilege createPrivilegeIfNotFound(String name) {
- Optional privilegeOptional = privilegeRepository.findByName(name);
- if (privilegeOptional.isEmpty()) {
- privilegeOptional = Optional.of(new Privilege(name));
- privilegeRepository.save(privilegeOptional.get());
- }
- return privilegeOptional.get();
- }
-
- Role createRoleIfNotFound(String name, Collection privileges) {
- Optional roleOptional = roleRepository.findByName(name);
- if (roleOptional.isEmpty()) {
- roleOptional = Optional.of(new Role(name));
- roleOptional.get().setPrivileges(privileges);
- roleRepository.save(roleOptional.get());
- }
- return roleOptional.get();
- }
-
-}
\ No newline at end of file
diff --git a/src/main/java/com/backend/user/service/anyame/config/AuthorizationServerConfig.java b/src/main/java/com/backend/user/service/anyame/config/AuthorizationServerConfig.java
new file mode 100644
index 0000000..c2444e1
--- /dev/null
+++ b/src/main/java/com/backend/user/service/anyame/config/AuthorizationServerConfig.java
@@ -0,0 +1,105 @@
+package com.backend.user.service.anyame.config;
+
+import com.backend.user.service.anyame.component.AuthorizationServerProperties;
+import com.nimbusds.jose.jwk.source.JWKSource;
+import com.nimbusds.jose.proc.SecurityContext;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.annotation.Order;
+import org.springframework.http.MediaType;
+import org.springframework.security.config.Customizer;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.oauth2.core.AuthorizationGrantType;
+import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
+import org.springframework.security.oauth2.core.oidc.OidcScopes;
+import org.springframework.security.oauth2.jwt.JwtDecoder;
+import org.springframework.security.oauth2.server.authorization.client.InMemoryRegisteredClientRepository;
+import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
+import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
+import org.springframework.security.oauth2.server.authorization.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;
+import org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers.OAuth2AuthorizationServerConfigurer;
+import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;
+import org.springframework.security.oauth2.server.authorization.settings.ClientSettings;
+import org.springframework.security.oauth2.server.authorization.settings.OAuth2TokenFormat;
+import org.springframework.security.oauth2.server.authorization.settings.TokenSettings;
+import org.springframework.security.web.SecurityFilterChain;
+import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
+import org.springframework.security.web.util.matcher.MediaTypeRequestMatcher;
+import org.springframework.web.cors.CorsConfigurationSource;
+
+import java.time.Duration;
+import java.time.temporal.ChronoUnit;
+import java.util.UUID;
+
+@Configuration(proxyBeanMethods = false)
+public class AuthorizationServerConfig {
+ private final AuthorizationServerProperties authorizationServerProperties;
+
+ public AuthorizationServerConfig(AuthorizationServerProperties authorizationServerProperties) {
+ this.authorizationServerProperties = authorizationServerProperties;
+ }
+
+ @Bean
+ @Order(1)
+ public SecurityFilterChain authServerSecurityFilterChain(HttpSecurity http, @Qualifier("corsConfigurationSource") CorsConfigurationSource configurationSource) throws Exception {
+ OAuth2AuthorizationServerConfigurer authorizationServerConfigurer =
+ OAuth2AuthorizationServerConfigurer.authorizationServer();
+ return http
+ .cors(c -> c.configurationSource(configurationSource))
+ .securityMatcher(authorizationServerConfigurer.getEndpointsMatcher())
+ .with(authorizationServerConfigurer, (authorizationServer) ->
+ authorizationServer
+ .oidc(Customizer.withDefaults())
+ )
+ .authorizeHttpRequests((authorize) ->
+ authorize
+ .anyRequest().authenticated()
+ )
+ .exceptionHandling((exceptions) -> exceptions
+ .defaultAuthenticationEntryPointFor(
+ new LoginUrlAuthenticationEntryPoint("/login"),
+ new MediaTypeRequestMatcher(MediaType.ALL)
+ )
+ )
+ .build();
+ }
+
+ @Bean
+ public RegisteredClientRepository registeredClientRepository() {
+ RegisteredClient oidcClient = RegisteredClient.withId(UUID.randomUUID().toString())
+ .clientId("oidc-client")
+ .clientSecret("{noop}secret")
+ .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
+ .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
+ .authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
+ .redirectUri("http://localhost:5173/code")
+ .postLogoutRedirectUri("http://localhost:5173/")
+ .scope(OidcScopes.OPENID)
+ .scope(OidcScopes.PROFILE)
+ .clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build())
+ .tokenSettings(TokenSettings.builder()
+ .accessTokenFormat(OAuth2TokenFormat.REFERENCE)
+ .accessTokenTimeToLive(Duration.of(30, ChronoUnit.MINUTES))
+ .refreshTokenTimeToLive(Duration.of(120, ChronoUnit.MINUTES))
+ .reuseRefreshTokens(false)
+ .authorizationCodeTimeToLive(Duration.of(30, ChronoUnit.SECONDS))
+ .build())
+ .build();
+
+ return new InMemoryRegisteredClientRepository(oidcClient);
+ }
+
+ @Bean
+ public JwtDecoder jwtDecoder(JWKSource jwkSource) {
+ return OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource);
+ }
+
+ @Bean
+ public AuthorizationServerSettings authorizationServerSettings() {
+ return AuthorizationServerSettings.builder()
+ .issuer(authorizationServerProperties.getIssuerUrl())
+ .tokenIntrospectionEndpoint(authorizationServerProperties.getIntrospectionEndpoint())
+ .build();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/backend/user/service/anyame/config/SecurityConfig.java b/src/main/java/com/backend/user/service/anyame/config/SecurityConfig.java
index 6049794..23cbf0d 100644
--- a/src/main/java/com/backend/user/service/anyame/config/SecurityConfig.java
+++ b/src/main/java/com/backend/user/service/anyame/config/SecurityConfig.java
@@ -1,51 +1,42 @@
package com.backend.user.service.anyame.config;
-import com.backend.user.service.anyame.component.AuthorizationProperties;
-import org.passay.CharacterRule;
-import org.passay.EnglishCharacterData;
-import org.passay.LengthRule;
-import org.passay.PasswordValidator;
-import org.passay.WhitespaceRule;
+import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
-import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
-import org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl;
+import org.springframework.core.annotation.Order;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
-import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
-import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
-import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.security.core.userdetails.User;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
+import org.springframework.web.cors.CorsConfigurationSource;
-import java.util.Arrays;
+import static org.springframework.security.config.Customizer.withDefaults;
-@Configuration
+@Configuration(proxyBeanMethods = false)
public class SecurityConfig {
@Bean
- public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
- return http.csrf(AbstractHttpConfigurer::disable).authorizeHttpRequests(c -> c.anyRequest().permitAll()).build();
+ @Order(2)
+ public SecurityFilterChain filterChain(HttpSecurity http, @Qualifier("corsConfigurationSource") CorsConfigurationSource configurationSource) throws Exception {
+ return http
+ .cors(c -> c.configurationSource(configurationSource))
+ .authorizeHttpRequests(c ->
+ c.anyRequest().authenticated()
+ )
+ .formLogin(withDefaults())
+ .build();
}
@Bean
- public PasswordEncoder passwordEncoder() {
- return new BCryptPasswordEncoder();
+ public UserDetailsService users() {
+ UserDetails user = User.builder()
+ .username("admin")
+ .password("{noop}password")
+ .roles("USER")
+ .build();
+ return new InMemoryUserDetailsManager(user);
}
- @Bean
- public PasswordValidator passwordValidator() {
- return new PasswordValidator(Arrays.asList(
- new LengthRule(8, 64),
- new CharacterRule(EnglishCharacterData.UpperCase, 1),
- new CharacterRule(EnglishCharacterData.LowerCase, 1),
- new CharacterRule(EnglishCharacterData.Digit, 1),
- new CharacterRule(EnglishCharacterData.Special, 1),
- new WhitespaceRule()
- ));
- }
-
- @Bean
- public RoleHierarchy roleHierarchy(AuthorizationProperties authorizationProperties) {
- return RoleHierarchyImpl.fromHierarchy(authorizationProperties.getHierarchy());
- }
-
-}
+}
\ No newline at end of file
diff --git a/src/main/java/com/backend/user/service/anyame/config/WebConfig.java b/src/main/java/com/backend/user/service/anyame/config/WebConfig.java
new file mode 100644
index 0000000..c8e94ea
--- /dev/null
+++ b/src/main/java/com/backend/user/service/anyame/config/WebConfig.java
@@ -0,0 +1,38 @@
+package com.backend.user.service.anyame.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.cors.CorsConfiguration;
+import org.springframework.web.cors.CorsConfigurationSource;
+import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
+import org.springframework.web.servlet.config.annotation.CorsRegistry;
+import org.springframework.web.servlet.config.annotation.EnableWebMvc;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+import java.util.List;
+
+@Configuration
+@EnableWebMvc
+public class WebConfig implements WebMvcConfigurer {
+ @Override
+ public void addCorsMappings(CorsRegistry registry) {
+ registry.addMapping("/**")
+ .allowedOrigins("http://localhost:5173")
+ .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
+ .allowedHeaders("*")
+ .allowCredentials(true);
+ }
+
+ @Bean(name = "corsConfigurationSource")
+ public CorsConfigurationSource corsConfigurationSource() {
+ CorsConfiguration config = new CorsConfiguration();
+ config.setAllowedOrigins(List.of("http://localhost:5173"));
+ config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "OPTIONS"));
+ config.setAllowedHeaders(List.of("*"));
+ config.setAllowCredentials(true);
+
+ UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
+ source.registerCorsConfiguration("/**", config);
+ return source;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/backend/user/service/anyame/controller/AuthController.java b/src/main/java/com/backend/user/service/anyame/controller/AuthController.java
deleted file mode 100644
index 6453b89..0000000
--- a/src/main/java/com/backend/user/service/anyame/controller/AuthController.java
+++ /dev/null
@@ -1,58 +0,0 @@
-package com.backend.user.service.anyame.controller;
-
-import com.backend.user.service.anyame.dto.AuthResponse;
-import com.backend.user.service.anyame.dto.LoginRequest;
-import com.backend.user.service.anyame.dto.RegisterRequest;
-import com.backend.user.service.anyame.exception.UserAlreadyExistsException;
-import com.backend.user.service.anyame.exception.InvalidCredentialsException;
-import com.backend.user.service.anyame.exception.InvalidRoleException;
-import com.backend.user.service.anyame.exception.NoExpiryDurationException;
-import com.backend.user.service.anyame.exception.UnsafePasswordException;
-import com.backend.user.service.anyame.service.AuthService;
-import org.springframework.http.HttpStatus;
-import org.springframework.http.ResponseEntity;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
-import org.springframework.web.server.ResponseStatusException;
-
-@RestController
-@RequestMapping("/api/auth")
-public class AuthController {
-
- private final AuthService authService;
-
- public AuthController(AuthService authService) {
- this.authService = authService;
- }
-
- @PostMapping("/register")
- public ResponseEntity register(@RequestBody RegisterRequest request) {
- try {
- return ResponseEntity.ok(authService.register(request));
- } catch (UnsafePasswordException e) {
- throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "unsafe password, length 8-64, 1 upper case, 1 lower case, 1 digit, 1 special symbol. No whitespaces in password.");
- } catch (UserAlreadyExistsException e) {
- throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "user already registered");
- } catch (NoExpiryDurationException e) {
- throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "no expiry duration found, invalid server config");
- } catch (InvalidRoleException e) {
- throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "invalid role defined: " + e.getInvalidRole());
- }
- }
-
- @PostMapping("/login")
- public ResponseEntity login(@RequestBody LoginRequest request) {
- try {
- return ResponseEntity.ok(authService.login(request));
- } catch (InvalidCredentialsException e) {
- throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "invalid credentials", e);
- } catch (NoExpiryDurationException e) {
- throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "no expiry duration found, invalid server config");
- } catch (InvalidRoleException e) {
- throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "invalid role defined: " + e.getInvalidRole());
- }
- }
-
-}
diff --git a/src/main/java/com/backend/user/service/anyame/controller/HelloController.java b/src/main/java/com/backend/user/service/anyame/controller/HelloController.java
new file mode 100644
index 0000000..8b5ffaa
--- /dev/null
+++ b/src/main/java/com/backend/user/service/anyame/controller/HelloController.java
@@ -0,0 +1,14 @@
+package com.backend.user.service.anyame.controller;
+
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@RequestMapping("/api")
+public class HelloController {
+ @GetMapping("/hello")
+ public String hello() {
+ return "Hello World";
+ }
+}
diff --git a/src/main/java/com/backend/user/service/anyame/dto/AuthResponse.java b/src/main/java/com/backend/user/service/anyame/dto/AuthResponse.java
deleted file mode 100644
index fceee93..0000000
--- a/src/main/java/com/backend/user/service/anyame/dto/AuthResponse.java
+++ /dev/null
@@ -1,6 +0,0 @@
-package com.backend.user.service.anyame.dto;
-
-import java.util.List;
-
-public record AuthResponse(String accessToken, String refreshToken, long id, String email, String name, List roles) {
-}
\ No newline at end of file
diff --git a/src/main/java/com/backend/user/service/anyame/dto/LoginRequest.java b/src/main/java/com/backend/user/service/anyame/dto/LoginRequest.java
deleted file mode 100644
index 4c509e6..0000000
--- a/src/main/java/com/backend/user/service/anyame/dto/LoginRequest.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package com.backend.user.service.anyame.dto;
-
-import jakarta.validation.constraints.Email;
-import jakarta.validation.constraints.NotBlank;
-import jakarta.validation.constraints.NotNull;
-
-public record LoginRequest(
- @NotBlank(message = "email must not be blank")
- @Email(message = "email must be valid")
- String email,
- @NotNull(message = "password must not be null, but can be blank")
- String password) {
-
-}
diff --git a/src/main/java/com/backend/user/service/anyame/dto/RegisterRequest.java b/src/main/java/com/backend/user/service/anyame/dto/RegisterRequest.java
deleted file mode 100644
index 86c65b3..0000000
--- a/src/main/java/com/backend/user/service/anyame/dto/RegisterRequest.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package com.backend.user.service.anyame.dto;
-
-import jakarta.validation.constraints.Email;
-import jakarta.validation.constraints.NotBlank;
-import jakarta.validation.constraints.NotNull;
-import jakarta.validation.constraints.Size;
-
-public record RegisterRequest(
- @NotBlank(message = "name must not be blank")
- @Size(min = 3, max = 16, message = "name min length is 3, max is 16")
- String name,
- @NotBlank(message = "email must not be blank")
- @Email(message = "email must be valid")
- String email,
- @NotNull(message = "password must not be null, but can be blank")
- String password) {
-}
\ No newline at end of file
diff --git a/src/main/java/com/backend/user/service/anyame/entity/Privilege.java b/src/main/java/com/backend/user/service/anyame/entity/Privilege.java
deleted file mode 100644
index 686c895..0000000
--- a/src/main/java/com/backend/user/service/anyame/entity/Privilege.java
+++ /dev/null
@@ -1,40 +0,0 @@
-package com.backend.user.service.anyame.entity;
-
-import jakarta.persistence.Entity;
-import jakarta.persistence.GeneratedValue;
-import jakarta.persistence.GenerationType;
-import jakarta.persistence.Id;
-import jakarta.persistence.ManyToMany;
-
-import java.util.Collection;
-
-@Entity
-public class Privilege {
-
- @Id
- @GeneratedValue(strategy = GenerationType.AUTO)
- private Long id;
- private String name;
- @ManyToMany(mappedBy = "privileges")
- private Collection roles;
-
- protected Privilege() {
- }
-
- public Privilege(String name) {
- this.name = name;
- }
-
- public Long getId() {
- return id;
- }
-
- public String getName() {
- return name;
- }
-
- public Collection getRoles() {
- return roles;
- }
-
-}
\ No newline at end of file
diff --git a/src/main/java/com/backend/user/service/anyame/entity/Role.java b/src/main/java/com/backend/user/service/anyame/entity/Role.java
deleted file mode 100644
index 8779eb8..0000000
--- a/src/main/java/com/backend/user/service/anyame/entity/Role.java
+++ /dev/null
@@ -1,61 +0,0 @@
-package com.backend.user.service.anyame.entity;
-
-import jakarta.persistence.Entity;
-import jakarta.persistence.GeneratedValue;
-import jakarta.persistence.GenerationType;
-import jakarta.persistence.Id;
-import jakarta.persistence.JoinColumn;
-import jakarta.persistence.JoinTable;
-import jakarta.persistence.ManyToMany;
-
-import java.util.Collection;
-
-@Entity
-public class Role {
-
- @Id
- @GeneratedValue(strategy = GenerationType.AUTO)
- private Long id;
-
- private String name;
- @ManyToMany(mappedBy = "roles")
- private Collection users;
-
- @ManyToMany
- @JoinTable(
- name = "roles_privileges",
- joinColumns = @JoinColumn(
- name = "role_id", referencedColumnName = "id"),
- inverseJoinColumns = @JoinColumn(
- name = "privilege_id", referencedColumnName = "id"))
- private Collection privileges;
-
- protected Role() {
- }
-
- public Role(String name) {
- this.name = name;
- }
-
- public Long getId() {
- return id;
- }
-
- public String getName() {
- return name;
- }
-
- public Collection getUsers() {
- return users;
- }
-
- public Collection getPrivileges() {
- return privileges;
- }
-
- public Role setPrivileges(Collection privileges) {
- this.privileges = privileges;
- return this;
- }
-
-}
\ No newline at end of file
diff --git a/src/main/java/com/backend/user/service/anyame/entity/User.java b/src/main/java/com/backend/user/service/anyame/entity/User.java
deleted file mode 100644
index dd41ca9..0000000
--- a/src/main/java/com/backend/user/service/anyame/entity/User.java
+++ /dev/null
@@ -1,91 +0,0 @@
-package com.backend.user.service.anyame.entity;
-
-import jakarta.persistence.Column;
-import jakarta.persistence.Entity;
-import jakarta.persistence.GeneratedValue;
-import jakarta.persistence.GenerationType;
-import jakarta.persistence.Id;
-import jakarta.persistence.JoinColumn;
-import jakarta.persistence.JoinTable;
-import jakarta.persistence.ManyToMany;
-import jakarta.persistence.Table;
-
-import java.util.Collection;
-
-@Entity
-@Table(name = "users")
-public class User {
-
- @Id
- @GeneratedValue(strategy = GenerationType.IDENTITY)
- private Long id;
- private String name;
- @Column(unique = true, nullable = false)
- private String email;
- private String password;
- private boolean enabled = true;
-
- @ManyToMany
- @JoinTable(
- name = "users_roles",
- joinColumns = @JoinColumn(
- name = "user_id", referencedColumnName = "id"),
- inverseJoinColumns = @JoinColumn(
- name = "role_id", referencedColumnName = "id"))
- private Collection roles;
-
- protected User() {
- }
-
- public User(String name, String email, String password) {
- this.name = name;
- this.email = email;
- this.password = password;
- }
-
- public User(String name, String email) {
- this.name = name;
- this.email = email;
- }
-
- public Long getId() {
- return id;
- }
-
- public String getName() {
- return name;
- }
-
- public String getEmail() {
- return email;
- }
-
- public String getPassword() {
- return password;
- }
-
- public User setPassword(String password) {
- this.password = password;
- return this;
- }
-
- public boolean isEnabled() {
- return enabled;
- }
-
- public User setEnabled(boolean enabled) {
- this.enabled = enabled;
- return this;
- }
-
- public Collection getRoles() {
- return roles;
- }
-
- public User setRoles(Collection roles) {
- this.roles = roles;
- return this;
- }
-
-}
-
diff --git a/src/main/java/com/backend/user/service/anyame/exception/InvalidCredentialsException.java b/src/main/java/com/backend/user/service/anyame/exception/InvalidCredentialsException.java
deleted file mode 100644
index d56e7da..0000000
--- a/src/main/java/com/backend/user/service/anyame/exception/InvalidCredentialsException.java
+++ /dev/null
@@ -1,9 +0,0 @@
-package com.backend.user.service.anyame.exception;
-
-public class InvalidCredentialsException extends Exception {
-
- public InvalidCredentialsException() {
- super("invalid credentials");
- }
-
-}
diff --git a/src/main/java/com/backend/user/service/anyame/exception/InvalidRoleException.java b/src/main/java/com/backend/user/service/anyame/exception/InvalidRoleException.java
deleted file mode 100644
index 153e343..0000000
--- a/src/main/java/com/backend/user/service/anyame/exception/InvalidRoleException.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package com.backend.user.service.anyame.exception;
-
-public class InvalidRoleException extends Exception {
- private final String invalidRole;
-
- public InvalidRoleException(String invalidRole) {
- this.invalidRole = invalidRole;
- }
-
- public InvalidRoleException(String message, String invalidRole) {
- super(message);
- this.invalidRole = invalidRole;
- }
-
- public String getInvalidRole() {
- return invalidRole;
- }
-
-}
diff --git a/src/main/java/com/backend/user/service/anyame/exception/NoExpiryDurationException.java b/src/main/java/com/backend/user/service/anyame/exception/NoExpiryDurationException.java
deleted file mode 100644
index f159331..0000000
--- a/src/main/java/com/backend/user/service/anyame/exception/NoExpiryDurationException.java
+++ /dev/null
@@ -1,5 +0,0 @@
-package com.backend.user.service.anyame.exception;
-
-public class NoExpiryDurationException extends Exception {
-
-}
diff --git a/src/main/java/com/backend/user/service/anyame/exception/UnsafePasswordException.java b/src/main/java/com/backend/user/service/anyame/exception/UnsafePasswordException.java
deleted file mode 100644
index 394286c..0000000
--- a/src/main/java/com/backend/user/service/anyame/exception/UnsafePasswordException.java
+++ /dev/null
@@ -1,9 +0,0 @@
-package com.backend.user.service.anyame.exception;
-
-public class UnsafePasswordException extends Exception {
-
- public UnsafePasswordException(String message) {
- super(message);
- }
-
-}
diff --git a/src/main/java/com/backend/user/service/anyame/exception/UserAlreadyExistsException.java b/src/main/java/com/backend/user/service/anyame/exception/UserAlreadyExistsException.java
deleted file mode 100644
index fd83018..0000000
--- a/src/main/java/com/backend/user/service/anyame/exception/UserAlreadyExistsException.java
+++ /dev/null
@@ -1,9 +0,0 @@
-package com.backend.user.service.anyame.exception;
-
-public class UserAlreadyExistsException extends Exception {
-
- public UserAlreadyExistsException() {
- super("user already exists");
- }
-
-}
diff --git a/src/main/java/com/backend/user/service/anyame/repository/PrivilegeRepository.java b/src/main/java/com/backend/user/service/anyame/repository/PrivilegeRepository.java
deleted file mode 100644
index 4f8a585..0000000
--- a/src/main/java/com/backend/user/service/anyame/repository/PrivilegeRepository.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package com.backend.user.service.anyame.repository;
-
-import com.backend.user.service.anyame.entity.Privilege;
-import org.springframework.data.jpa.repository.JpaRepository;
-
-import java.util.Optional;
-
-public interface PrivilegeRepository extends JpaRepository {
-
- Optional findByName(String name);
-
-}
diff --git a/src/main/java/com/backend/user/service/anyame/repository/RoleRepository.java b/src/main/java/com/backend/user/service/anyame/repository/RoleRepository.java
deleted file mode 100644
index 4204795..0000000
--- a/src/main/java/com/backend/user/service/anyame/repository/RoleRepository.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package com.backend.user.service.anyame.repository;
-
-import com.backend.user.service.anyame.entity.Role;
-import org.springframework.data.jpa.repository.JpaRepository;
-
-import java.util.Optional;
-
-public interface RoleRepository extends JpaRepository {
-
- Optional findByName(String roleName);
-
-}
diff --git a/src/main/java/com/backend/user/service/anyame/repository/UserRepository.java b/src/main/java/com/backend/user/service/anyame/repository/UserRepository.java
deleted file mode 100644
index 0663e18..0000000
--- a/src/main/java/com/backend/user/service/anyame/repository/UserRepository.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package com.backend.user.service.anyame.repository;
-
-import com.backend.user.service.anyame.entity.User;
-import org.springframework.data.jpa.repository.JpaRepository;
-
-import java.util.Optional;
-
-public interface UserRepository extends JpaRepository {
-
- Optional findByEmail(String email);
-
- Optional findByEmailOrName(String email, String name);
-
-}
diff --git a/src/main/java/com/backend/user/service/anyame/service/AuthService.java b/src/main/java/com/backend/user/service/anyame/service/AuthService.java
deleted file mode 100644
index 8195f97..0000000
--- a/src/main/java/com/backend/user/service/anyame/service/AuthService.java
+++ /dev/null
@@ -1,80 +0,0 @@
-package com.backend.user.service.anyame.service;
-
-import com.backend.user.service.anyame.component.DefaultRoleProvider;
-import com.backend.user.service.anyame.dto.AuthResponse;
-import com.backend.user.service.anyame.dto.LoginRequest;
-import com.backend.user.service.anyame.dto.RegisterRequest;
-import com.backend.user.service.anyame.entity.Role;
-import com.backend.user.service.anyame.entity.User;
-import com.backend.user.service.anyame.exception.UserAlreadyExistsException;
-import com.backend.user.service.anyame.exception.InvalidCredentialsException;
-import com.backend.user.service.anyame.exception.InvalidRoleException;
-import com.backend.user.service.anyame.exception.NoExpiryDurationException;
-import com.backend.user.service.anyame.exception.UnsafePasswordException;
-import com.backend.user.service.anyame.repository.UserRepository;
-import org.springframework.security.crypto.password.PasswordEncoder;
-import org.springframework.stereotype.Service;
-
-import java.util.Collections;
-import java.util.List;
-
-@Service
-public class AuthService {
-
- private final UserRepository userRepository;
- private final PasswordEncoder passwordEncoder;
- private final JwtService jwtService;
- private final PasswordValidatorService passwordValidator;
- private final DefaultRoleProvider defaultRoleProvider;
-
- public AuthService(UserRepository userRepository,
- PasswordEncoder passwordEncoder,
- JwtService jwtService,
- PasswordValidatorService passwordValidator,
- DefaultRoleProvider defaultRoleProvider) {
- this.userRepository = userRepository;
- this.passwordEncoder = passwordEncoder;
- this.jwtService = jwtService;
- this.passwordValidator = passwordValidator;
- this.defaultRoleProvider = defaultRoleProvider;
- }
-
- public AuthResponse register(
- RegisterRequest request) throws UnsafePasswordException, UserAlreadyExistsException, NoExpiryDurationException, InvalidRoleException {
- if (userRepository.findByEmailOrName(request.email(), request.name()).isPresent()) {
- throw new UserAlreadyExistsException();
- }
-
- User user = new User(request.name(), request.email());
- user.setRoles(Collections.singleton(defaultRoleProvider.getDefaultRole()));
-
- if (request.password() != null && !request.password().isBlank()) {
- if (!passwordValidator.validate(request.password())) {
- throw new UnsafePasswordException("unsafe password");
- }
- user.setPassword(passwordEncoder.encode(request.password()));
- }
- user = userRepository.save(user);
-
- return generateAuthResponse(user);
- }
-
- public AuthResponse login(LoginRequest request) throws InvalidCredentialsException, NoExpiryDurationException, InvalidRoleException {
- User user = userRepository.findByEmail(request.email())
- .orElseThrow(() -> new RuntimeException("Invalid credentials"));
-
- if (user.getPassword() == null || !passwordEncoder.matches(request.password(), user.getPassword())) {
- throw new InvalidCredentialsException();
- }
-
- return generateAuthResponse(user);
- }
-
- private AuthResponse generateAuthResponse(User user) throws NoExpiryDurationException, InvalidRoleException {
- String accessToken = jwtService.generateAccessToken(user);
- String refreshToken = jwtService.generateRefreshToken(user);
- List roles = user.getRoles().stream().map(Role::getName).toList();
- return new AuthResponse(accessToken, refreshToken, user.getId(), user.getEmail(), user.getName(), roles);
- }
-
-}
diff --git a/src/main/java/com/backend/user/service/anyame/service/AuthorityResolver.java b/src/main/java/com/backend/user/service/anyame/service/AuthorityResolver.java
deleted file mode 100644
index 7679150..0000000
--- a/src/main/java/com/backend/user/service/anyame/service/AuthorityResolver.java
+++ /dev/null
@@ -1,58 +0,0 @@
-package com.backend.user.service.anyame.service;
-
-import com.backend.user.service.anyame.entity.Privilege;
-import com.backend.user.service.anyame.entity.Role;
-import com.backend.user.service.anyame.entity.User;
-import com.backend.user.service.anyame.exception.InvalidRoleException;
-import com.backend.user.service.anyame.repository.RoleRepository;
-import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
-import org.springframework.security.core.GrantedAuthority;
-import org.springframework.security.core.authority.SimpleGrantedAuthority;
-import org.springframework.stereotype.Service;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-import java.util.stream.Collectors;
-
-@Service
-public class AuthorityResolver {
- private final RoleRepository roleRepository;
- private final RoleHierarchy roleHierarchy;
-
- public AuthorityResolver(RoleRepository roleRepository, RoleHierarchy roleHierarchy) {
- this.roleRepository = roleRepository;
- this.roleHierarchy = roleHierarchy;
- }
-
- public List getAuthorities(User user) throws InvalidRoleException {
- List grantedAuthorities = user.getRoles().stream()
- .map(role -> new SimpleGrantedAuthority(role.getName()))
- .collect(Collectors.toList());
-
- Collection extends GrantedAuthority> reachableAuthorities =
- roleHierarchy.getReachableGrantedAuthorities(grantedAuthorities);
-
- Set privileges = new HashSet<>();
- for (GrantedAuthority authority : reachableAuthorities) {
- String roleName = authority.getAuthority();
- Role role = roleRepository.findByName(roleName)
- .orElseThrow(() -> new InvalidRoleException(roleName));
- if (role != null && role.getPrivileges() != null) {
- privileges.addAll(role.getPrivileges().stream()
- .map(Privilege::getName)
- .collect(Collectors.toSet()));
- }
- }
-
- Set allAuthorities = reachableAuthorities.stream()
- .map(GrantedAuthority::getAuthority)
- .collect(Collectors.toSet());
- allAuthorities.addAll(privileges);
-
- return new ArrayList<>(allAuthorities);
- }
-
-}
diff --git a/src/main/java/com/backend/user/service/anyame/service/JwtService.java b/src/main/java/com/backend/user/service/anyame/service/JwtService.java
deleted file mode 100644
index f601764..0000000
--- a/src/main/java/com/backend/user/service/anyame/service/JwtService.java
+++ /dev/null
@@ -1,115 +0,0 @@
-package com.backend.user.service.anyame.service;
-
-import com.backend.user.service.anyame.component.AuthorizationProperties;
-import com.backend.user.service.anyame.component.JWTSecretProvider;
-import com.backend.user.service.anyame.entity.Role;
-import com.backend.user.service.anyame.entity.User;
-import com.backend.user.service.anyame.exception.InvalidRoleException;
-import com.backend.user.service.anyame.exception.NoExpiryDurationException;
-import io.jsonwebtoken.Claims;
-import io.jsonwebtoken.JwtBuilder;
-import io.jsonwebtoken.Jwts;
-import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
-import org.springframework.stereotype.Service;
-
-import java.time.Duration;
-import java.time.Instant;
-import java.util.Date;
-import java.util.List;
-import java.util.Optional;
-import java.util.function.Function;
-
-@Service
-public class JwtService {
-
- private final JWTSecretProvider secretProvider;
- private final AuthorizationProperties authorizationProperties;
- private final AuthorityResolver authorityResolver;
-
- public JwtService(JWTSecretProvider secretProvider,
- AuthorizationProperties authorizationProperties,
- AuthorityResolver authorityResolver) {
- this.secretProvider = secretProvider;
- this.authorizationProperties = authorizationProperties;
- this.authorityResolver = authorityResolver;
- }
-
- public String generateRefreshToken(User user) throws InvalidRoleException, NoExpiryDurationException {
- List authorities = authorityResolver.getAuthorities(user);
- List roles = user.getRoles().stream()
- .map(Role::getName)
- .toList();
- JwtBuilder builder = Jwts.builder()
- .subject(user.getEmail())
- .claim("name", user.getName())
- .claim("id", user.getId())
- .claim("roles", roles)
- .claim("authorities", authorities)
- .claim("type", "refresh");
- Duration refreshExpiry = getMaxExpiry(user,
- role -> authorizationProperties.getRefreshExpiry(role.getName()))
- .orElseThrow(NoExpiryDurationException::new);
-
- Date issuedAt = new Date();
- Date expiryDate = Date.from(Instant.now().plus(refreshExpiry));
-
- return builder.issuedAt(issuedAt)
- .expiration(expiryDate)
- .signWith(secretProvider.getSecretKey())
- .compact();
- }
-
- public String generateAccessToken(User user) throws InvalidRoleException, NoExpiryDurationException {
- List authorities = authorityResolver.getAuthorities(user);
- List roles = user.getRoles().stream()
- .map(Role::getName)
- .toList();
- JwtBuilder builder = Jwts.builder()
- .subject(user.getEmail())
- .claim("name", user.getName())
- .claim("id", user.getId())
- .claim("roles", roles)
- .claim("authorities", authorities)
- .claim("type", "access");
- Duration accessExpiry = getMaxExpiry(user,
- role -> authorizationProperties.getAccessExpiry(role.getName()))
- .orElseThrow(NoExpiryDurationException::new);
-
- Date issuedAt = new Date();
- Date expiryDate = Date.from(Instant.now().plus(accessExpiry));
-
- return builder.issuedAt(issuedAt)
- .expiration(expiryDate)
- .signWith(secretProvider.getSecretKey())
- .compact();
- }
-
- private Optional getMaxExpiry(User user, Function> mapper) {
- return user.getRoles().stream()
- .map(mapper)
- .filter(Optional::isPresent)
- .map(Optional::get)
- .max(Duration::compareTo);
- }
-
- public String extractEmail(String token) {
- return getClaims(token).getSubject();
- }
-
- public boolean isTokenValid(String token, User user) {
- return extractEmail(token).equals(user.getEmail()) && !isTokenExpired(token);
- }
-
- private boolean isTokenExpired(String token) {
- return getClaims(token).getExpiration().before(new Date());
- }
-
- private Claims getClaims(String token) {
- return Jwts.parser()
- .decryptWith(secretProvider.getSecretKey())
- .build()
- .parseEncryptedClaims(token)
- .getPayload();
- }
-
-}
diff --git a/src/main/java/com/backend/user/service/anyame/service/PasswordValidatorService.java b/src/main/java/com/backend/user/service/anyame/service/PasswordValidatorService.java
deleted file mode 100644
index bace87e..0000000
--- a/src/main/java/com/backend/user/service/anyame/service/PasswordValidatorService.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package com.backend.user.service.anyame.service;
-
-import org.passay.PasswordData;
-import org.passay.PasswordValidator;
-import org.passay.RuleResult;
-import org.springframework.stereotype.Service;
-
-@Service
-public class PasswordValidatorService {
-
- private final PasswordValidator passwordValidator;
-
- public PasswordValidatorService(PasswordValidator passwordValidator) {
- this.passwordValidator = passwordValidator;
- }
-
- public boolean validate(String password) {
- RuleResult result = passwordValidator.validate(new PasswordData(password));
- // TODO: Add HaveBeenIPwned support?
- return result.isValid();
- }
-
-}
diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml
index 4d4044c..2ece1f0 100644
--- a/src/main/resources/application.yaml
+++ b/src/main/resources/application.yaml
@@ -18,13 +18,32 @@ authorization:
default-role: ROLE_USER
spring:
+ application:
+ name: anyame-user-service
+ security:
+ oauth2:
+ authorizationserver:
+ issuer-url: http://localhost:8080
+ introspection-endpoint: /oauth2/token-info
datasource:
url: ${DATABASE_URL}
username: ${DATABASE_USERNAME}
password: ${DATABASE_PASSWORD}
jpa:
hibernate:
- ddl-auto: none
+ ddl-auto: create
+
+logging:
+ level:
+ root: DEBUG
+ org.springframework.security: DEBUG
+ logging.level.org.springframework.web: DEBUG
+ logging.level.org.springframework.security.oauth2: TRACE
+ org.apache.tomcat.util.net.NioEndpoint: ERROR
+ sun.rmi: ERROR
+ java.io: ERROR
+ javax.management: ERROR
+
server:
error:
- include-message: always
+ include-message: always
\ No newline at end of file