/*
 * Licensed to Crate under one or more contributor license agreements.
 * See the NOTICE file distributed with this work for additional
 * information regarding copyright ownership.  Crate licenses this file
 * to you 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.
 *
 * However, if you have executed another commercial license agreement
 * with Crate these terms will supersede the license and you may use the
 * software solely pursuant to the terms of the relevant commercial
 * agreement.
 */

package io.crate.operation.auth;

import io.crate.operation.user.User;
import io.crate.operation.user.UserLookup;
import io.crate.protocols.postgres.ConnectionProperties;
import sun.security.x509.X500Name;

import javax.annotation.Nullable;
import java.io.IOException;
import java.security.Principal;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;

public class ClientCertAuth implements AuthenticationMethod {

    static final String NAME = "cert";
    private final UserLookup userLookup;

    ClientCertAuth(UserLookup userLookup) {
        this.userLookup = userLookup;
    }

    @Nullable
    @Override
    public User authenticate(String userName, ConnectionProperties connProperties) {
        Certificate clientCert = connProperties.clientCert();
        if (connProperties.hasSSL() && clientCert != null) {
            if (clientCert instanceof X509Certificate) {
                String CN = extractCN(((X509Certificate )clientCert).getSubjectDN());
                User user = lookupUser(userName, connProperties, CN);
                if (user != null) return user;
            }
        }
        throw new RuntimeException("Client certificate authentication failed for user \"" + userName + "\"");
    }

    @Nullable
    private User lookupUser(String userName, ConnectionProperties connProperties, String CN) {
        // userName is optional in HTTP;
        if (connProperties.protocol() == Protocol.HTTP) {
            if (userName == null || userName.equals("")) {
                userName = CN;
            }
        }
        if (userName.equals(CN)) {
            User user = userLookup.findUser(userName);
            if (user != null) {
                return user;
            }
        }
        return null;
    }

    private static String extractCN(Principal subjectDN) {
        if (subjectDN instanceof X500Name) {
            try {
                return ((X500Name) subjectDN).getCommonName();
            } catch (IOException e) {
                throw new RuntimeException("Could not extract commonName from client certificate", e);
            }
        }
        throw new RuntimeException("Could not extract commonName from client certificate subjectDN: " + subjectDN);
    }

    @Override
    public String name() {
        return NAME;
    }
}

@@ -1166,43 +1166,8 @@
 ies;
-%0Aimport sun.security.x509.X500Name;
 %0A%0Aim
@@ -1213,14 +1213,28 @@
 java
-.io.IO
+x.naming.InvalidName
 Exce
@@ -1255,27 +1255,60 @@
 java
-.security.Principal
+x.naming.ldap.LdapName;%0Aimport javax.naming.ldap.Rdn
 ;%0Aim
@@ -1993,18 +1993,39 @@
 tSubject
-DN
+X500Principal().getName
 ());%0A   
@@ -2840,25 +2840,22 @@
 tractCN(
-Principal
+String
  subject
@@ -2872,98 +2872,285 @@
     
-if (subjectDN instanceof X500Name) %7B%0A            try %7B%0A                return ((X500
+/*%0A         * Get commonName using LdapName API%0A         * The DN of X509 certificates are in rfc2253 format. Ldap uses the same format.%0A         *%0A         * Doesn't use X500Name because it's internal API%0A         */%0A        try %7B%0A            LdapName ldapName = new Ldap
 Name
-) 
+(
 subj
@@ -3159,68 +3159,205 @@
 tDN)
-.getCommonName();%0A            %7D catch (IOException e) %7B%0A    
+;%0A            for (Rdn rdn : ldapName.getRdns()) %7B%0A                if (%22CN%22.equalsIgnoreCase(rdn.getType())) %7B%0A                    return rdn.getValue().toString();%0A                %7D%0A            %7D%0A
     
@@ -3448,39 +3448,83 @@
 cate
-%22, e);%0A            %7D%0A    
+ subjectDN: %22 + subjectDN);%0A        %7D catch (InvalidNameException e) %7B%0A
     
-%7D%0A
     
@@ -3611,35 +3611,24 @@
 cate
- subjectDN: %22 + subjectDN);
+%22, e);%0A        %7D
 %0A   

Remove usage of internal X500Name API to parse CN