Skip to content

Commit

Permalink
fix: manage partners endpoints authorizations
Browse files Browse the repository at this point in the history
  • Loading branch information
Fabien authored and fabiengo committed Feb 28, 2024
1 parent cc5066d commit 5227203
Show file tree
Hide file tree
Showing 12 changed files with 56 additions and 80 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package fr.dossierfacile.api.dossierfacileapiowner.config;

import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.actuate.autoconfigure.endpoint.web.CorsEndpointProperties;
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties;
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementPortType;
Expand All @@ -15,7 +16,6 @@
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;

import java.util.ArrayList;
Expand Down Expand Up @@ -45,6 +45,6 @@ public WebMvcEndpointHandlerMapping webEndpointServletHandlerMapping(WebEndpoint


private boolean shouldRegisterLinksMapping(WebEndpointProperties webEndpointProperties, Environment environment, String basePath) {
return webEndpointProperties.getDiscovery().isEnabled() && (StringUtils.hasText(basePath) || ManagementPortType.get(environment).equals(ManagementPortType.DIFFERENT));
return webEndpointProperties.getDiscovery().isEnabled() && (StringUtils.isNotBlank(basePath) || ManagementPortType.get(environment).equals(ManagementPortType.DIFFERENT));
}
}
Original file line number Diff line number Diff line change
@@ -1,22 +1,21 @@
package fr.dossierfacile.api.front.config;

import fr.dossierfacile.api.front.config.filter.ConnectionContextFilter;
import fr.dossierfacile.api.front.security.CustomWebSecurityExpressionHandler;
import fr.dossierfacile.api.front.security.PartnerAuthorizationManager;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.expression.SecurityExpressionHandler;
import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer.FrameOptionsConfig;
import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer;
import org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.access.expression.WebExpressionAuthorizationManager;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.access.intercept.RequestAuthorizationContext;
import org.springframework.security.web.header.writers.StaticHeadersWriter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
Expand All @@ -43,7 +42,7 @@ SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
.cacheControl(withDefaults())
.httpStrictTransportSecurity(transport -> transport.maxAgeInSeconds(63072000).includeSubDomains(true))
.contentSecurityPolicy(csp -> csp.policyDirectives("frame-ancestors 'none'; frame-src 'none'; child-src 'none'; upgrade-insecure-requests; default-src 'none'; script-src 'self'; style-src 'self' 'unsafe-inline'; object-src 'none'; img-src 'self' data:; font-src 'self'; connect-src *.dossierfacile.fr *.dossierfacile.fr:*; base-uri 'self'; form-action 'none'; media-src 'none'; worker-src 'none'; manifest-src 'none'; prefetch-src 'none';"))
.frameOptions(FrameOptionsConfig::sameOrigin)
.frameOptions(HeadersConfigurer.FrameOptionsConfig::sameOrigin)
)
.formLogin(AbstractHttpConfigurer::disable)
.httpBasic(AbstractHttpConfigurer::disable)
Expand All @@ -59,10 +58,10 @@ SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
"/api/support/email",
"/api/stats/**",
"/api/onetimesecret/**",
"/actuator/health").permitAll()
.requestMatchers("/api-partner/**").access(new WebExpressionAuthorizationManager("hasAuthority(\"SCOPE_api-partner\") && isClient()"))
"/actuator/health").permitAll()
.requestMatchers("/api-partner/**").access(apiPartnerAuthorizationManager())
.requestMatchers("/dfc/api/**").access(dfcPartnerServiceAuthorizationManager())
.requestMatchers("/dfc/**").hasAuthority("SCOPE_dfc")
.requestMatchers("/dfc/api/**").access(new WebExpressionAuthorizationManager("isClient()"))
.anyRequest().hasAuthority("SCOPE_dossier")
)
.oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt);
Expand All @@ -71,18 +70,23 @@ SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
}

@Bean
public SecurityExpressionHandler<FilterInvocation> customWebSecurityExpressionHandler() {
return new CustomWebSecurityExpressionHandler();
AuthorizationManager<RequestAuthorizationContext> apiPartnerAuthorizationManager() {
return new PartnerAuthorizationManager("api-partner");
}

@Bean
AuthorizationManager<RequestAuthorizationContext> dfcPartnerServiceAuthorizationManager() {
return new PartnerAuthorizationManager("dfc");
}

@Bean
CorsConfigurationSource corsConfigurationSource() {
var configuration = new CorsConfiguration();
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOriginPatterns(Collections.singletonList("*"));
configuration.setAllowCredentials(true);
configuration.setAllowedHeaders(Arrays.asList("Access-Control-Allow-Headers", "Access-Control-Allow-Origin", "Access-Control-Request-Method", "Access-Control-Request-Headers", "Origin", "Cache-Control", "Content-Type", "Authorization", "Baggage", "Sentry-trace"));
configuration.setAllowedMethods(Arrays.asList("DELETE", "GET", "POST", "PATCH", "PUT"));
var source = new UrlBasedCorsConfigurationSource();
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
Expand All @@ -91,4 +95,4 @@ CorsConfigurationSource corsConfigurationSource() {
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package fr.dossierfacile.api.front.config.filter;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package fr.dossierfacile.api.front.config.filter;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@

import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;

@Data
@AllArgsConstructor
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,12 @@
import fr.dossierfacile.api.front.form.interfaces.FormWithTenantId;
import fr.dossierfacile.api.front.validator.anotation.tenant.type_guarantor.MaxGuarantor;
import fr.dossierfacile.api.front.validator.group.ApiPartner;
import fr.dossierfacile.api.front.validator.group.Dossier;
import fr.dossierfacile.common.enums.TypeGuarantor;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.validation.constraints.NotNull;
import javax.validation.constraints.Null;

@Data
@AllArgsConstructor
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,13 @@

import fr.dossierfacile.api.front.form.interfaces.FormWithTenantId;
import fr.dossierfacile.api.front.validator.anotation.tenant.name.CheckFranceConnect;
import fr.dossierfacile.api.front.validator.group.ApiPartner;
import fr.dossierfacile.api.front.validator.group.Dossier;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Null;

@Data
@AllArgsConstructor
Expand Down

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package fr.dossierfacile.api.front.security;

import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.web.access.intercept.RequestAuthorizationContext;

import java.util.function.Supplier;

public class PartnerAuthorizationManager implements AuthorizationManager<RequestAuthorizationContext> {
private final String authScope;

public PartnerAuthorizationManager(String scope) {
this.authScope = "SCOPE_" + scope;
}

private boolean hasScope(Authentication authentication) {
return authentication.getAuthorities().stream().anyMatch(a -> authScope.equals(a.getAuthority()));
}

private boolean isClient(Authentication authentication) {
try {
return ((Jwt) authentication.getPrincipal()).getClaimAsString("client_id") != null;
} catch (Throwable t) {
return false;
}
}

@Override
public AuthorizationDecision check(Supplier<Authentication> authentication, RequestAuthorizationContext object) {
return new AuthorizationDecision(isClient(authentication.get()) && hasScope(authentication.get()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,12 @@


import fr.dossierfacile.api.front.register.form.tenant.ApplicationFormV2;
import fr.dossierfacile.api.front.security.interfaces.AuthenticationFacade;
import fr.dossierfacile.api.front.service.interfaces.TenantService;
import fr.dossierfacile.api.front.validator.TenantConstraintValidator;
import fr.dossierfacile.api.front.validator.anotation.tenant.application.v2.DeniedJoinTenant;
import fr.dossierfacile.common.entity.Tenant;
import fr.dossierfacile.common.enums.TenantType;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

@Component
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import fr.gouv.bo.security.UserPrincipal;
import fr.gouv.bo.security.oauth2.user.OAuth2UserInfo;
import fr.gouv.bo.security.oauth2.user.OAuth2UserInfoFactory;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
Expand All @@ -19,7 +20,6 @@
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.HashSet;
import java.util.List;
Expand Down Expand Up @@ -55,7 +55,7 @@ public OAuth2User loadUser(OAuth2UserRequest oAuth2UserRequest) {

public OAuth2User processOAuth2User(OAuth2UserRequest oAuth2UserRequest, OAuth2User oAuth2User) {
OAuth2UserInfo oAuth2UserInfo = OAuth2UserInfoFactory.getOAuth2UserInfo(oAuth2UserRequest.getClientRegistration().getRegistrationId(), oAuth2User.getAttributes());
if (!StringUtils.hasLength(oAuth2UserInfo.getEmail())) {
if (!StringUtils.isNotBlank(oAuth2UserInfo.getEmail())) {
throw new OAuth2AuthenticationProcessingException("Email not found from OAuth2 provider");
}
if (!Objects.requireNonNull(StringUtils.split(oAuth2UserInfo.getEmail(), "@"))[1].contains(authorizeDomainBo)) {
Expand Down

0 comments on commit 5227203

Please sign in to comment.