From 5f7f7a25c38f5515a5a867a0cce97eb29d209a66 Mon Sep 17 00:00:00 2001 From: alvaromaoc Date: Mon, 26 May 2025 21:05:18 +0200 Subject: [PATCH] Replace registration hook by fallback in CustomOAuth2UserService --- .../ServerWebExchangeFactory.java | 23 ------------ .../CustomOAuth2UserService.java | 8 ++++- .../configuration/SecurityConfiguration.java | 36 ------------------- .../controller/inlinehook/HookController.java | 24 ------------- .../inlinehook/RegisterHookRequest.java | 17 --------- .../ClaimToHeaderGatewayFilterFactory.java | 13 ++++--- src/main/resources/application.yml | 3 -- 7 files changed, 16 insertions(+), 108 deletions(-) delete mode 100644 src/main/java/io/autoinvestor/ServerWebExchangeFactory.java delete mode 100644 src/main/java/io/autoinvestor/controller/inlinehook/HookController.java delete mode 100644 src/main/java/io/autoinvestor/controller/inlinehook/RegisterHookRequest.java diff --git a/src/main/java/io/autoinvestor/ServerWebExchangeFactory.java b/src/main/java/io/autoinvestor/ServerWebExchangeFactory.java deleted file mode 100644 index 093544a..0000000 --- a/src/main/java/io/autoinvestor/ServerWebExchangeFactory.java +++ /dev/null @@ -1,23 +0,0 @@ -package io.autoinvestor; - -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; -import org.springframework.web.server.ServerWebExchange; - -import java.util.List; - -@Slf4j -@Component -public class ServerWebExchangeFactory { - public ServerWebExchange withHeader(ServerWebExchange exchange, String headerName, String headerValue) { - return exchange.mutate().request(request -> request.headers(headers -> - headers.add(headerName, headerValue) - )).build(); - } - - public ServerWebExchange withHeader(ServerWebExchange exchange, String headerName, List headerValue) { - return exchange.mutate().request(request -> request.headers(headers -> - headers.addAll(headerName, headerValue) - )).build(); - } -} diff --git a/src/main/java/io/autoinvestor/configuration/CustomOAuth2UserService.java b/src/main/java/io/autoinvestor/configuration/CustomOAuth2UserService.java index 6fdaf73..b6672ea 100644 --- a/src/main/java/io/autoinvestor/configuration/CustomOAuth2UserService.java +++ b/src/main/java/io/autoinvestor/configuration/CustomOAuth2UserService.java @@ -25,6 +25,7 @@ public Mono loadUser(OAuth2UserRequest userRequest) { return delegate.loadUser(userRequest) .flatMap(oauth2User -> fetchUserId(oauth2User) + .switchIfEmpty(createUser(oauth2User)) .map(userId -> { Map attributes = new HashMap<>(oauth2User.getAttributes()); attributes.put("userId", userId); @@ -34,7 +35,6 @@ public Mono loadUser(OAuth2UserRequest userRequest) { "sub" ); }) - .switchIfEmpty(Mono.error(new RuntimeException("User not found"))) ); } @@ -43,5 +43,11 @@ private Mono fetchUserId(OAuth2User user) { .getUser(user.getAttribute("email")) .map(userResponse -> userResponse.userId().toString()); } + + private Mono createUser(OAuth2User user) { + return usersClient + .createUser(user.getAttribute("email")) + .then(fetchUserId(user)); + } } diff --git a/src/main/java/io/autoinvestor/configuration/SecurityConfiguration.java b/src/main/java/io/autoinvestor/configuration/SecurityConfiguration.java index df40ce9..8026f21 100644 --- a/src/main/java/io/autoinvestor/configuration/SecurityConfiguration.java +++ b/src/main/java/io/autoinvestor/configuration/SecurityConfiguration.java @@ -1,41 +1,18 @@ package io.autoinvestor.configuration; import lombok.RequiredArgsConstructor; -import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.core.annotation.Order; -import org.springframework.http.HttpStatus; import org.springframework.security.config.Customizer; -import org.springframework.security.config.web.server.SecurityWebFiltersOrder; import org.springframework.security.config.web.server.ServerHttpSecurity; import org.springframework.security.web.server.SecurityWebFilterChain; import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatchers; -import org.springframework.web.server.WebFilter; @Configuration @RequiredArgsConstructor public class SecurityConfiguration { - @Value("${autoinvestor.okta.hookAuthHeaderName}") - private String apiAuthHeaderName; - - @Value("${autoinvestor.okta.hookAuthHeaderValue}") - private String apiAuthHeaderValue; - @Bean - @Order(1) - public SecurityWebFilterChain hookSecurityWebFilterChain(ServerHttpSecurity http) { - return http - .securityMatcher(ServerWebExchangeMatchers.pathMatchers("/api/hook/**")) - .csrf(ServerHttpSecurity.CsrfSpec::disable) - .authorizeExchange(exchanges -> exchanges.anyExchange().permitAll()) - .addFilterAt(hookAuthenticationWebFilter(), SecurityWebFiltersOrder.AUTHENTICATION) - .build(); - } - - @Bean - @Order(2) public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) { return http .securityMatcher(ServerWebExchangeMatchers.anyExchange()) @@ -47,17 +24,4 @@ public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) { .oauth2Login(Customizer.withDefaults()) .build(); } - - private WebFilter hookAuthenticationWebFilter() { - return (exchange, chain) -> { - String headerValue = exchange.getRequest().getHeaders().getFirst(apiAuthHeaderName); - - if (apiAuthHeaderValue.equals(headerValue)) { - return chain.filter(exchange); - } else { - exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED); - return exchange.getResponse().setComplete(); - } - }; - } } diff --git a/src/main/java/io/autoinvestor/controller/inlinehook/HookController.java b/src/main/java/io/autoinvestor/controller/inlinehook/HookController.java deleted file mode 100644 index b6c8868..0000000 --- a/src/main/java/io/autoinvestor/controller/inlinehook/HookController.java +++ /dev/null @@ -1,24 +0,0 @@ -package io.autoinvestor.controller.inlinehook; - -import io.autoinvestor.client.users.UsersClient; -import lombok.RequiredArgsConstructor; -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 reactor.core.publisher.Mono; - -@RestController -@RequestMapping("/api/hook") -@RequiredArgsConstructor -public class HookController { - - private final UsersClient usersClient; - - @PostMapping("/register") - public Mono> register(@RequestBody RegisterHookRequest body) { - return usersClient.createUser(body.data().userProfile().email()) - .then(Mono.just(ResponseEntity.noContent().build())); - } -} diff --git a/src/main/java/io/autoinvestor/controller/inlinehook/RegisterHookRequest.java b/src/main/java/io/autoinvestor/controller/inlinehook/RegisterHookRequest.java deleted file mode 100644 index 5a76e3d..0000000 --- a/src/main/java/io/autoinvestor/controller/inlinehook/RegisterHookRequest.java +++ /dev/null @@ -1,17 +0,0 @@ -package io.autoinvestor.controller.inlinehook; - -public record RegisterHookRequest( - Data data -) { - public record Data( - UserProfile userProfile - ) { - public record UserProfile( - String firstName, - String lastName, - String login, - String email - ) { - } - } -} diff --git a/src/main/java/io/autoinvestor/filters/ClaimToHeaderGatewayFilterFactory.java b/src/main/java/io/autoinvestor/filters/ClaimToHeaderGatewayFilterFactory.java index 5bb32c3..0ba8330 100644 --- a/src/main/java/io/autoinvestor/filters/ClaimToHeaderGatewayFilterFactory.java +++ b/src/main/java/io/autoinvestor/filters/ClaimToHeaderGatewayFilterFactory.java @@ -1,6 +1,5 @@ package io.autoinvestor.filters; -import io.autoinvestor.ServerWebExchangeFactory; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.RequiredArgsConstructor; @@ -12,6 +11,7 @@ import org.springframework.security.core.context.SecurityContext; import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; import org.springframework.stereotype.Component; +import org.springframework.web.server.ServerWebExchange; import java.util.List; @@ -20,8 +20,6 @@ @RequiredArgsConstructor public class ClaimToHeaderGatewayFilterFactory implements GatewayFilterFactory { - private final ServerWebExchangeFactory serverWebExchangeFactory; - @Override public GatewayFilter apply(Config config) { return (exchange, chain) -> ReactiveSecurityContextHolder.getContext() @@ -29,11 +27,18 @@ public GatewayFilter apply(Config config) { .filter(authentication -> authentication instanceof OAuth2AuthenticationToken) .map(authentication -> (OAuth2AuthenticationToken) authentication) .mapNotNull(authentication -> authentication.getPrincipal().getAttribute(config.getClaim())) - .map(userId -> serverWebExchangeFactory.withHeader(exchange, config.getHeaderName(), userId.toString())) + .filter(userId -> userId instanceof String) + .map(userId -> withHeader(exchange, config.getHeaderName(), (String) userId)) .defaultIfEmpty(exchange) .flatMap(chain::filter); } + private static ServerWebExchange withHeader(ServerWebExchange exchange, String headerName, String headerValue) { + return exchange.mutate().request(request -> request.headers(headers -> + headers.add(headerName, headerValue) + )).build(); + } + @Override public Config newConfig() { return new Config(); diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 0e7403f..b932355 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,7 +1,4 @@ autoinvestor: - okta: - hookAuthHeaderName: "${OKTA_HOOK_AUTH_HEADER_NAME}" - hookAuthHeaderValue: "${OKTA_HOOK_AUTH_HEADER_VALUE}" client: users: url: "${USERS_BASE_URL}"