All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.inverno.mod.security.http.internal.GenericSecurityInterceptor Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2022 Jeremy KUHN
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package io.inverno.mod.security.http.internal;

import io.inverno.mod.http.server.Exchange;
import io.inverno.mod.security.accesscontrol.AccessController;
import io.inverno.mod.security.accesscontrol.AccessControllerResolver;
import io.inverno.mod.security.authentication.Authentication;
import io.inverno.mod.security.authentication.AuthenticationException;
import io.inverno.mod.security.authentication.Authenticator;
import io.inverno.mod.security.authentication.Credentials;
import io.inverno.mod.security.context.SecurityContext;
import io.inverno.mod.security.http.CredentialsExtractor;
import io.inverno.mod.security.http.SecurityInterceptor;
import io.inverno.mod.security.http.context.InterceptingSecurityContext;
import io.inverno.mod.security.identity.Identity;
import io.inverno.mod.security.identity.IdentityResolver;
import java.util.Objects;
import java.util.Optional;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import reactor.core.publisher.Mono;

/**
 * 

* Generic {@link SecurityInterceptor} implementation. *

* * @author Jeremy Kuhn * @since 1.5 * * @param the credentials type * @param the authentication type * @param the identity type * @param the access controller type * @param the intercepting security context type * @param the exchange type */ public class GenericSecurityInterceptor, F extends Exchange> implements SecurityInterceptor { private static final Logger LOGGER = LogManager.getLogger(GenericSecurityInterceptor.class); /** * The credentials extractor. */ private final CredentialsExtractor credentialsExtractor; /** * The authenticator. */ private final Authenticator authenticator; /** * The identity resolver. */ private final Optional> identityResolver; /** * The access controller resolver. */ private final Optional> accessControllerResolver; /** *

* Creates a security interceptor with the specified credentials extractor and authenticator. *

* * @param credentialsExtractor a credentials extractor * @param authenticator an authenticator */ public GenericSecurityInterceptor(CredentialsExtractor credentialsExtractor, Authenticator authenticator) { this(credentialsExtractor, authenticator, null, null); } /** *

* Creates a security interceptor with the specified credentials extractor, authenticator and identity resolver. *

* * @param credentialsExtractor a credentials extractor * @param authenticator an authenticator * @param identityResolver an identity resolver */ public GenericSecurityInterceptor(CredentialsExtractor credentialsExtractor, Authenticator authenticator, IdentityResolver identityResolver) { this(credentialsExtractor, authenticator, identityResolver, null); } /** *

* Creates a security interceptor with the specified credentials extractor, authenticator and access controller resolver. *

* * @param credentialsExtractor a credentials extractor * @param authenticator an authenticator * @param accessControllerResolver an access controller resolver */ public GenericSecurityInterceptor(CredentialsExtractor credentialsExtractor, Authenticator authenticator, AccessControllerResolver accessControllerResolver) { this(credentialsExtractor, authenticator, null, accessControllerResolver); } /** *

* Creates a security interceptor with the specified credentials extractor, authenticator, identity resolver and access controller resolver. *

* * @param credentialsExtractor a credentials extractor * @param authenticator an authenticator * @param identityResolver an identity resolver * @param accessControllerResolver an access controller resolver */ public GenericSecurityInterceptor(CredentialsExtractor credentialsExtractor, Authenticator authenticator, IdentityResolver identityResolver, AccessControllerResolver accessControllerResolver) { this.credentialsExtractor = Objects.requireNonNull(credentialsExtractor); this.authenticator = Objects.requireNonNull(authenticator); this.identityResolver = Optional.ofNullable(identityResolver); this.accessControllerResolver = Optional.ofNullable(accessControllerResolver); } @Override @SuppressWarnings("unchecked") public Mono intercept(F exchange) { // 1. Extract credentials return this.credentialsExtractor.extract(exchange) // No credentials => anonymous access .switchIfEmpty(Mono.fromRunnable(() -> exchange.context().setSecurityContext((SecurityContext)SecurityContext.of(Authentication.anonymous())))) // 2. Authenticate .flatMap(credentials -> this.authenticator .authenticate(credentials) .switchIfEmpty(Mono.error(() -> new AuthenticationException("Unable to authenticate"))) .doOnError(io.inverno.mod.security.SecurityException.class, error -> LOGGER.debug("Failed to authenticate", error)) ) // 3. Resolve identity and access control .flatMap(authentication -> Mono.zip( Mono.just(authentication), this.identityResolver .filter(ign -> authentication.isAuthenticated()) .map(resolver -> resolver.resolveIdentity(authentication) .doOnError(io.inverno.mod.security.SecurityException.class, error -> LOGGER.debug("Failed to resolve identity", error)) .map(Optional::of) .switchIfEmpty(Mono.just(Optional.empty())) ) .orElse(Mono.just(Optional.empty())), this.accessControllerResolver .filter(ign -> authentication.isAuthenticated()) .map(resolver -> resolver.resolveAccessController(authentication) .doOnError(io.inverno.mod.security.SecurityException.class, error -> LOGGER.debug("Failed to resolve authorizations", error)) .map(Optional::of) .switchIfEmpty(Mono.just(Optional.empty())) ) .orElse(Mono.just(Optional.empty())) ) ) // 4. Create and set SecurityContext .doOnNext(tuple3 -> exchange.context().setSecurityContext( SecurityContext.of( (B) tuple3.getT1(), (Optional) tuple3.getT2(), (Optional) tuple3.getT3() ) )) // Return a denied authentication in case of security error, let any other error propagate .onErrorResume(io.inverno.mod.security.SecurityException.class, error -> { exchange.context().setSecurityContext((SecurityContext)SecurityContext.of(Authentication.denied(error))); return Mono.empty(); }) .thenReturn(exchange); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy