argos/annotation-service/annotation-web/src/main/java/gr/cite/annotation/web/config/SecurityConfiguration.java

152 lines
7.2 KiB
Java

package gr.cite.annotation.web.config;
import gr.cite.annotation.authorization.AffiliatedAuthorizationRequirement;
import gr.cite.annotation.authorization.AffiliatedResource;
import gr.cite.annotation.web.authorization.AffiliatedAuthorizationHandler;
import gr.cite.commons.web.authz.handler.AuthorizationHandler;
import gr.cite.commons.web.authz.handler.PermissionClientAuthorizationHandler;
import gr.cite.commons.web.authz.policy.AuthorizationRequirement;
import gr.cite.commons.web.authz.policy.AuthorizationRequirementMapper;
import gr.cite.commons.web.authz.policy.AuthorizationResource;
import gr.cite.commons.web.authz.policy.resolver.AuthorizationPolicyConfigurer;
import gr.cite.commons.web.authz.policy.resolver.AuthorizationPolicyResolverStrategy;
import gr.cite.commons.web.oidc.configuration.WebSecurityProperties;
import gr.cite.annotation.authorization.OwnedAuthorizationRequirement;
import gr.cite.annotation.authorization.OwnedResource;
import gr.cite.annotation.web.authorization.OwnedAuthorizationHandler;
import jakarta.servlet.Filter;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManagerResolver;
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;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter;
import java.util.List;
import java.util.Set;
@Configuration
@EnableWebSecurity
public class SecurityConfiguration {
private final WebSecurityProperties webSecurityProperties;
private final AuthenticationManagerResolver<HttpServletRequest> authenticationManagerResolver;
private final Filter apiKeyFilter;
private final OwnedAuthorizationHandler ownedAuthorizationHandler;
private final AffiliatedAuthorizationHandler affiliatedAuthorizationHandler;
@Autowired
public SecurityConfiguration(WebSecurityProperties webSecurityProperties,
@Qualifier("tokenAuthenticationResolver") AuthenticationManagerResolver<HttpServletRequest> authenticationManagerResolver,
@Qualifier("apiKeyFilter") Filter apiKeyFilter,
@Qualifier("ownedAuthorizationHandler") OwnedAuthorizationHandler ownedAuthorizationHandler,
@Qualifier("affiliatedAuthorizationHandler") AffiliatedAuthorizationHandler affiliatedAuthorizationHandler) {
this.webSecurityProperties = webSecurityProperties;
this.authenticationManagerResolver = authenticationManagerResolver;
this.apiKeyFilter = apiKeyFilter;
this.ownedAuthorizationHandler = ownedAuthorizationHandler;
this.affiliatedAuthorizationHandler = affiliatedAuthorizationHandler;
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
HttpSecurity tempHttp = http
.csrf(AbstractHttpConfigurer::disable)
.cors(httpSecurityCorsConfigurer -> {})
.headers(httpSecurityHeadersConfigurer -> httpSecurityHeadersConfigurer.frameOptions(HeadersConfigurer.FrameOptionsConfig::disable))
.addFilterBefore(apiKeyFilter, AbstractPreAuthenticatedProcessingFilter.class)
.authorizeHttpRequests(authRequest ->
authRequest.requestMatchers(buildAntPatterns(webSecurityProperties.getAllowedEndpoints())).anonymous()
.requestMatchers(buildAntPatterns(webSecurityProperties.getAuthorizedEndpoints())).authenticated())
.sessionManagement( sessionManagementConfigurer-> sessionManagementConfigurer.sessionCreationPolicy(SessionCreationPolicy.NEVER))
.oauth2ResourceServer(oauth2 -> oauth2.authenticationManagerResolver(authenticationManagerResolver));
return tempHttp.build();
}
@Bean
AuthorizationPolicyConfigurer authorizationPolicyConfigurer() {
return new AuthorizationPolicyConfigurer() {
@Override
public AuthorizationPolicyResolverStrategy strategy() {
return AuthorizationPolicyResolverStrategy.STRICT_CONSENSUS_BASED;
}
//Here you can register your custom authorization handlers, which will get used as well as the existing ones
//This is optional and can be omitted
//If not set / set to null, only the default authorization handlers will be used
@Override
public List<AuthorizationHandler<? extends AuthorizationRequirement>> addCustomHandlers() {
return List.of(affiliatedAuthorizationHandler, ownedAuthorizationHandler);
}
//Here you can register your custom authorization requirements (if any)
//This is optional and can be omitted
//If not set / set to null, only the default authorization requirements will be used
@Override
public List<? extends AuthorizationRequirement> extendRequirements() {
return List.of(
// new TimeOfDayAuthorizationRequirement(new TimeOfDay("08:00","16:00"), true)
);
}
//Here you can select handlers you want to disable by providing the classes they are implemented by
//You can disable any handler (including any custom one)
//This is optional and can be omitted
//If not set / set to null, all the handlers will be invoked, based on their requirement support
//In the example below, the default client handler will be ignored by the resolver
@Override
public List<Class<? extends AuthorizationHandler<? extends AuthorizationRequirement>>> disableHandlers() {
return List.of(PermissionClientAuthorizationHandler.class);
}
};
}
@Bean
AuthorizationRequirementMapper authorizationRequirementMapper() {
return new AuthorizationRequirementMapper() {
@Override
public AuthorizationRequirement map(AuthorizationResource resource, boolean matchAll, String[] permissions) {
Class<?> type = resource.getClass();
if (!AuthorizationResource.class.isAssignableFrom(type)) throw new IllegalArgumentException("resource");
if (OwnedResource.class.equals(type)) {
return new OwnedAuthorizationRequirement();
}
if (AffiliatedResource.class.equals(type)) {
return new AffiliatedAuthorizationRequirement(matchAll, permissions);
}
throw new IllegalArgumentException("resource");
}
};
}
private String[] buildAntPatterns(Set<String> endpoints) {
if (endpoints == null) {
return new String[0];
}
return endpoints.stream()
.filter(endpoint -> endpoint != null && !endpoint.isBlank())
.map(endpoint -> "/" + stripUnnecessaryCharacters(endpoint) + "/**")
.toArray(String[]::new);
}
private String stripUnnecessaryCharacters(String endpoint) {
endpoint = endpoint.strip();
if (endpoint.startsWith("/")) {
endpoint = endpoint.substring(1);
}
if (endpoint.endsWith("/")) {
endpoint = endpoint.substring(0, endpoint.length() - 1);
}
return endpoint;
}
}