/*
 * Copyright 2018 NetIQ Corporation, a Micro Focus company.
 */
package com.microfocus.am.oauth.controller;

import java.io.IOException;
import java.net.HttpURLConnection;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.security.oauth2.client.token.AccessTokenRequest;
import org.springframework.security.oauth2.client.token.DefaultAccessTokenRequest;
import org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeAccessTokenProvider;
import org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeResourceDetails;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import com.microfocus.am.oauth.config.AppConfig;


/** 
 * Core workings of this sample app.
 * Landing controller invoked by the Access manager (as the Redirect URI is /callback) once the user logs in.
 * It will:
 * - convert the supplied authorization code to Access token
 * - use Access token to invoke Token Info endpoint
 * - Returns the token info, which includes the logged in username.
 *
 */
@RestController
public class AuthzCodeController {

	@Autowired
	AppConfig appConfig;

    @RequestMapping(value="/callback", produces="application/json")
    public String exchangeCodeForToken(@RequestParam("code") String codeStr,
    									@RequestParam("state") String stateStr)
    {
    
        AuthorizationCodeResourceDetails resource = new AuthorizationCodeResourceDetails();
        resource.setUserAuthorizationUri(appConfig.getAuthorizationEndpointURL());
        resource.setAccessTokenUri(appConfig.getTokenEndpointURL());
        resource.setClientId(appConfig.getClientId());
        resource.setClientSecret(appConfig.getClientSecret());
        resource.setPreEstablishedRedirectUri(appConfig.getRedirectURI());

        AccessTokenRequest request = new DefaultAccessTokenRequest();
        request.setAuthorizationCode(codeStr);
        request.setStateKey(stateStr);
        request.setPreservedState(new Object());
        
        AuthorizationCodeAccessTokenProvider provider = new AuthorizationCodeAccessTokenProvider();
        provider.setRequestFactory(createClientHttpRequestFactory());

        OAuth2AccessToken accessToken = provider.obtainAccessToken(resource, request);
        
        // Use the access token for authentication
		HttpHeaders headers1 = new HttpHeaders();
		headers1.add("Authorization", "Bearer " + accessToken.getValue());
		
		HttpEntity<String> entity = new HttpEntity<>(headers1);
		RestTemplate restTemplate = new RestTemplate();
		restTemplate.setRequestFactory(createClientHttpRequestFactory());
		ResponseEntity resp = restTemplate.exchange(appConfig.getTokenInfoEndpointURL(), HttpMethod.GET, entity, String.class);
		return resp.getBody().toString();
    }
        
	private ClientHttpRequestFactory createClientHttpRequestFactory() {
		TrustManager[] byPassTrustManagers = new TrustManager[] { new X509TrustManager() {
			@Override
			public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
				// TODO Auto-generated method stub
				
			}
			@Override
			public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
				// TODO Auto-generated method stub
				
			}
			@Override
			public X509Certificate[] getAcceptedIssuers() {
				return new X509Certificate[0];
			}
		} };
		final SSLContext sslContext;
		try {
			sslContext = SSLContext.getInstance("TLS");
			sslContext.init(null, byPassTrustManagers, new SecureRandom());
			sslContext.getSocketFactory();
		}
		catch (NoSuchAlgorithmException e) {
			throw new RuntimeException(e);
		}
		catch (KeyManagementException e) {
			throw new RuntimeException(e);
		}
		ClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory() {
			@Override
			protected void prepareConnection(HttpURLConnection connection,
					String httpMethod) throws IOException {
				super.prepareConnection(connection, httpMethod);
				if (connection instanceof HttpsURLConnection) {
					((HttpsURLConnection) connection).setSSLSocketFactory(
							sslContext.getSocketFactory());
				}
			}
		};
		return factory;
	}
	
}
