/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2015 Red Hat, Inc., and individual contributors
 * as indicated by the @author tags.
 *
 * 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 org.wildfly.security.ssl;

import java.net.Socket;
import java.security.Principal;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

import javax.net.ssl.SSLEngine;
import javax.net.ssl.X509ExtendedTrustManager;
import javax.net.ssl.X509TrustManager;

import org.wildfly.common.Assert;
import org.wildfly.security._private.ElytronMessages;
import org.wildfly.security.auth.login.SecurityDomain;
import org.wildfly.security.auth.login.ServerAuthenticationContext;
import org.wildfly.security.auth.spi.CredentialSupport;
import org.wildfly.security.auth.spi.RealmUnavailableException;
import org.wildfly.security.auth.util.CredentialDecoder;
import org.wildfly.security.auth.util.PrincipalDecoder;

/**
 * @author <a href="mailto:david.lloyd@redhat.com">David M. Lloyd</a>
 */
class SecurityDomainTrustManager extends X509ExtendedTrustManager {

    private final X509ExtendedTrustManager delegate;
    private final SecurityDomain securityDomain;
    private final CredentialDecoder credentialDecoder;
    private final PrincipalDecoder principalDecoder;

    SecurityDomainTrustManager(final X509ExtendedTrustManager delegate, final SecurityDomain securityDomain, final CredentialDecoder credentialDecoder, final PrincipalDecoder principalDecoder) {
        this.delegate = delegate;
        this.securityDomain = securityDomain;
        this.credentialDecoder = credentialDecoder;
        this.principalDecoder = principalDecoder;
    }

    SecurityDomainTrustManager(final X509TrustManager delegate, final SecurityDomain securityDomain, final CredentialDecoder credentialDecoder, final PrincipalDecoder principalDecoder) {
        this(delegate instanceof X509ExtendedTrustManager ? (X509ExtendedTrustManager) delegate : new WrappingX509ExtendedTrustManager(delegate), securityDomain, credentialDecoder, principalDecoder);
    }

    public void checkClientTrusted(final X509Certificate[] chain, final String authType, final Socket socket) throws CertificateException {
        delegate.checkClientTrusted(chain, authType, socket);
        doClientTrustCheck(chain, authType);
    }

    public void checkClientTrusted(final X509Certificate[] chain, final String authType, final SSLEngine sslEngine) throws CertificateException {
        delegate.checkClientTrusted(chain, authType, sslEngine);
        doClientTrustCheck(chain, authType);
    }

    public void checkClientTrusted(final X509Certificate[] chain, final String authType) throws CertificateException {
        delegate.checkClientTrusted(chain, authType);
        doClientTrustCheck(chain, authType);
    }

    private void doClientTrustCheck(final X509Certificate[] chain, final String authType) throws CertificateException {
        Assert.checkNotNullParam("chain", chain);
        Assert.checkNotNullParam("authType", authType);
        if (chain.length == 0) {
            throw ElytronMessages.log.emptyChainNotTrusted();
        }
        final X509Certificate subjectCertificate = chain[0];
        Principal principal = credentialDecoder.getPrincipalFromCredential(subjectCertificate);
        String name = principalDecoder.getName(principal);
        final ServerAuthenticationContext authenticationContext = securityDomain.createNewAuthenticationContext();
        boolean ok = false;
        try {
            authenticationContext.setAuthenticationName(name);
            final CredentialSupport credentialSupport = authenticationContext.getCredentialSupport(X509Certificate.class);
            if (credentialSupport.mayBeVerifiable()) {
                if (! authenticationContext.verifyCredential(subjectCertificate)) {
                    throw ElytronMessages.log.notTrusted(principal, name);
                }
            } else {
                throw ElytronMessages.log.notTrusted(principal, name);
            }
            authenticationContext.succeed();
            ok = true;
        } catch (RealmUnavailableException e) {
            throw ElytronMessages.log.notTrustedRealmProblem(e, principal, name);
        } finally {
            if (! ok) {
                authenticationContext.fail();
            }
        }
    }

    public void checkServerTrusted(final X509Certificate[] chain, final String authType, final Socket socket) throws CertificateException {
        delegate.checkServerTrusted(chain, authType, socket);
    }

    public void checkServerTrusted(final X509Certificate[] chain, final String authType, final SSLEngine sslEngine) throws CertificateException {
        delegate.checkServerTrusted(chain, authType, sslEngine);
    }

    public void checkServerTrusted(final X509Certificate[] chain, final String authType) throws CertificateException {
        delegate.checkServerTrusted(chain, authType);
    }

    public X509Certificate[] getAcceptedIssuers() {
        return delegate.getAcceptedIssuers();
    }
}

@@ -916,16 +916,81 @@
 Engine;%0A
+import javax.net.ssl.SSLSession;%0Aimport javax.net.ssl.SSLSocket;%0A
 import j
@@ -2885,32 +2885,76 @@
 (chain, authType
+, ((SSLSocket) socket).getHandshakeSession()
 );%0A    %7D%0A%0A    pu
@@ -3192,32 +3192,65 @@
 (chain, authType
+, sslEngine.getHandshakeSession()
 );%0A    %7D%0A%0A    pu
@@ -3450,32 +3450,38 @@
 (chain, authType
+, null
 );%0A    %7D%0A%0A    pr
@@ -3554,32 +3554,67 @@
  String authType
+, final SSLSession handshakeSession
 ) throws Certifi
@@ -4778,24 +4778,207 @@
 .succeed();%0A
+            if (handshakeSession != null) %7B%0A                handshakeSession.putValue(SSLUtils.SSL_SESSION_IDENTITY_KEY, authenticationContext.getAuthorizedIdentity());%0A            %7D%0A
             

Put the authorized identity into the SSLSession