com.epam.reportportal.auth.OAuthSecurityConfig.java Source code

Java tutorial

Introduction

Here is the source code for com.epam.reportportal.auth.OAuthSecurityConfig.java

Source

/*
 * Copyright 2016 EPAM Systems
 *
 *
 * This file is part of EPAM Report Portal.
 * https://github.com/reportportal/service-authorization
 *
 * Report Portal is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Report Portal is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Report Portal.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.epam.reportportal.auth;

import com.epam.reportportal.auth.integration.github.GitHubTokenServices;
import com.epam.reportportal.auth.integration.github.GitHubUserReplicator;
import com.google.common.collect.ImmutableList;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.oauth2.client.OAuth2ClientContext;
import org.springframework.security.oauth2.client.filter.OAuth2ClientAuthenticationProcessingFilter;
import org.springframework.security.oauth2.client.filter.OAuth2ClientContextFilter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableOAuth2Client;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.web.filter.CompositeFilter;
import org.springframework.web.util.UriComponentsBuilder;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;

/**
 * Main Security Extension Point.
 *
 * @author <a href="mailto:andrei_varabyeu@epam.com">Andrei Varabyeu</a>
 */
@Configuration
@EnableOAuth2Client
@Order(6)
@Conditional(OAuthSecurityConfig.HasExtensionsCondition.class)
public class OAuthSecurityConfig extends WebSecurityConfigurerAdapter {

    protected static final String SSO_LOGIN_PATH = "/sso/login";
    static final String GITHUB = "github";

    @Autowired
    private OAuth2ClientContext oauth2ClientContext;

    @Autowired
    private GitHubUserReplicator githubReplicator;

    @Autowired
    protected OAuthSuccessHandler authSuccessHandler;

    @Autowired
    protected DynamicAuthProvider dynamicAuthProvider;

    @Autowired
    private MongoOperations mongoOperations;

    /**
     * Extension point. Other Implementations can add their own OAuth processing filters
     *
     * @param oauth2ClientContext OAuth Client context
     * @return List of additional OAuth processing filters
     * @throws Exception in case of error
     */
    protected List<OAuth2ClientAuthenticationProcessingFilter> getAdditionalFilters(
            OAuth2ClientContext oauth2ClientContext) throws Exception {
        return Collections.emptyList();
    }

    @Override
    protected final void configure(HttpSecurity http) throws Exception {
        //@formatter:off
        http.antMatcher("/**").authorizeRequests()
                .antMatchers(SSO_LOGIN_PATH + "/**", "/webjars/**", "/index.html", "/epam/**", "/info", "/health")
                .permitAll().anyRequest().authenticated().and().csrf().disable().sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS);

        CompositeFilter authCompositeFilter = new CompositeFilter();
        List<OAuth2ClientAuthenticationProcessingFilter> additionalFilters = ImmutableList
                .<OAuth2ClientAuthenticationProcessingFilter>builder()
                .addAll(getDefaultFilters(oauth2ClientContext)).addAll(getAdditionalFilters(oauth2ClientContext))
                .build();

        /* make sure filters have correct exception handler */
        additionalFilters.forEach(filter -> filter.setAuthenticationFailureHandler(OAUTH_ERROR_HANDLER));
        authCompositeFilter.setFilters(additionalFilters);

        //install additional OAuth Authentication filters
        http.addFilterAfter(authCompositeFilter, BasicAuthenticationFilter.class);
        //@formatter:on
    }

    @Bean
    FilterRegistrationBean oauth2ClientFilterRegistration(OAuth2ClientContextFilter filter) {
        FilterRegistrationBean registration = new FilterRegistrationBean();
        registration.setFilter(filter);
        registration.setOrder(-100);
        return registration;
    }

    private List<OAuth2ClientAuthenticationProcessingFilter> getDefaultFilters(
            OAuth2ClientContext oauth2ClientContext) {
        OAuth2ClientAuthenticationProcessingFilter githubFilter = new OAuth2ClientAuthenticationProcessingFilter(
                SSO_LOGIN_PATH + "/github");

        githubFilter.setRestTemplate(dynamicAuthProvider.getRestTemplate(GITHUB, oauth2ClientContext));
        GitHubTokenServices tokenServices = new GitHubTokenServices(githubReplicator,
                dynamicAuthProvider.getLoginDetailsSupplier(GITHUB));
        githubFilter.setTokenServices(tokenServices);
        githubFilter.setAuthenticationSuccessHandler(authSuccessHandler);

        return Collections.singletonList(githubFilter);
    }

    /**
     * Condition. Load this config is there are no subclasses in the application context
     */
    protected static class HasExtensionsCondition extends SpringBootCondition {

        @Override
        public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
            String[] enablers = context.getBeanFactory().getBeanNamesForAnnotation(EnableOAuth2Client.class);
            boolean extensions = Arrays.stream(enablers)
                    .filter(name -> !context.getBeanFactory().getType(name).equals(OAuthSecurityConfig.class))
                    .filter(name -> context.getBeanFactory().isTypeMatch(name, OAuthSecurityConfig.class)).findAny()
                    .isPresent();
            if (extensions) {
                return ConditionOutcome.noMatch("found @EnableOAuth2Client on a OAuthSecurityConfig subclass");
            } else {
                return ConditionOutcome.match("found no @EnableOAuth2Client on a OAuthSecurityConfig subsclass");
            }

        }
    }

    private static final AuthenticationFailureHandler OAUTH_ERROR_HANDLER = (request, response, exception) -> {
        response.sendRedirect(
                UriComponentsBuilder.fromHttpRequest(new ServletServerHttpRequest(request)).replacePath("ui/#login")
                        .replaceQuery("errorAuth=" + exception.getMessage()).build().toUriString());
    };

}