/**
 * Copyright (C) 2015 The Gravitee team (http://gravitee.io)
 *
 * 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.gravitee.am.gateway.handler.oauth2;

import io.gravitee.am.definition.Domain;
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.AuthenticationManager;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.approval.UserApprovalHandler;
import org.springframework.security.oauth2.provider.client.ClientDetailsUserDetailsService;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
import org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler;
import org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;

import java.util.Collections;

/**
 * @author David BRASSELY (david.brassely at graviteesource.com)
 * @author GraviteeSource Team
 */
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private Domain domain;

    @Autowired
    private TokenStore tokenStore;

    @Autowired
    private AuthorizationCodeServices authorizationCodeServices;

    @Autowired
    private UserApprovalHandler userApprovalHandler;

    @Autowired
    @Qualifier("authenticationManagerBean")
    private AuthenticationManager authenticationManager;

    @Autowired
    private ClientDetailsService clientDetailsService;

    @Autowired
    @Qualifier("userDetailsServiceBean")
    private UserDetailsService userDetailsService;

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.withClientDetails(clientDetailsService);
    }

    @Bean
    public ClientDetailsUserDetailsService clientDetailsUserDetailsService() {
        return new ClientDetailsUserDetailsService(clientDetailsService);
    }

    @Bean
    public AuthenticationManager clientAuthenticationManager() {
        DaoAuthenticationProvider clientAuthenticationProvider = new DaoAuthenticationProvider();
        clientAuthenticationProvider.setUserDetailsService(clientDetailsUserDetailsService());
        clientAuthenticationProvider.setHideUserNotFoundExceptions(false);
        return new ProviderManager(Collections.singletonList(clientAuthenticationProvider));
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints
            .tokenStore(tokenStore)
            .reuseRefreshTokens(false)
            .userDetailsService(userDetailsService)
            .authorizationCodeServices(authorizationCodeServices)
            .userApprovalHandler(userApprovalHandler)
            .authenticationManager(authenticationManager);
    }

    @Override
    public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
        oauthServer
                .realm("realm-" + domain.getName())
                .checkTokenAccess("isAuthenticated()")
                .accessDeniedHandler(oAuth2AccessDeniedHandler())
                .allowFormAuthenticationForClients()
                .authenticationEntryPoint(oAuth2AuthenticationEntryPoint());
        oauthServer
                .addTokenEndpointAuthenticationFilter(new BasicAuthenticationFilter(clientAuthenticationManager(), oAuth2AuthenticationEntryPoint()));
    }

    @Bean
    public OAuth2AuthenticationEntryPoint oAuth2AuthenticationEntryPoint() {
        OAuth2AuthenticationEntryPoint oAuth2AuthenticationEntryPoint = new OAuth2AuthenticationEntryPoint();
        oAuth2AuthenticationEntryPoint.setRealmName("realm-" + domain.getName());
        oAuth2AuthenticationEntryPoint.setTypeName("Basic");

        return oAuth2AuthenticationEntryPoint;
    }

    @Bean
    public OAuth2AccessDeniedHandler oAuth2AccessDeniedHandler() {
        return new OAuth2AccessDeniedHandler();
    }
}

@@ -2473,16 +2473,183 @@
 Filter;%0A
+import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;%0A%0Aimport javax.servlet.http.HttpServletRequest;%0Aimport javax.servlet.http.HttpServletResponse;
 %0Aimport 
@@ -4659,16 +4659,703 @@
 Manager)
+%0A                .addInterceptor(new HandlerInterceptorAdapter() %7B%0A                    @Override%0A                    public boolean preHandle(HttpServletRequest hsr, HttpServletResponse rs, Object o) throws Exception %7B%0A                        rs.setHeader(%22Access-Control-Allow-Origin%22, hsr.getHeader(%22origin%22));%0A                        rs.setHeader(%22Access-Control-Allow-Methods%22, %22GET,POST,OPTIONS%22);%0A                        rs.setHeader(%22Access-Control-Max-Age%22, %223600%22);%0A                        rs.setHeader(%22Access-Control-Allow-Headers%22, %22Origin, X-Requested-With, Content-Type, Accept, Authorization%22);%0A                        return true;%0A                    %7D%0A                %7D)
 ;%0A    %7D%0A

Set a CORS request interceptor for OAuth endpoints