yifan-c commented on code in PR #137:
URL: https://github.com/apache/cassandra-sidecar/pull/137#discussion_r1796038108


##########
vertx-auth-mtls/src/main/java/io/vertx/ext/auth/mtls/impl/SpiffeIdentityExtractor.java:
##########
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF 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.
+ */
+
+package io.vertx.ext.auth.mtls.impl;
+
+import io.vertx.ext.auth.authentication.CredentialValidationException;
+import io.vertx.ext.auth.mtls.CertificateIdentityExtractor;
+
+import java.security.cert.Certificate;
+import java.security.cert.CertificateParsingException;
+import java.security.cert.X509Certificate;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * {@link CertificateIdentityExtractor} implementation for SPIFFE certificates 
for extracting SPIFFE identity.
+ * SPIFFE is a URI, present as part of SAN of client certificates, it uniquely 
identifies client.
+ */
+public class SpiffeIdentityExtractor implements CertificateIdentityExtractor
+{
+    private static final int SUBJECT_ALT_NAME_URI_TYPE = 6;
+    private static final String SPIFFE_PREFIX = "spiffe://";
+
+    @Override
+    public String identity(Certificate[] certificateChain) throws 
CredentialValidationException, CertificateParsingException
+    {
+        X509Certificate[] castedCerts = castCertsToX509(certificateChain);
+        if (castedCerts.length == 0)
+        {
+            throw new CredentialValidationException("No based X509Certificate 
found for validating");
+        }
+        X509Certificate privateCert = castedCerts[0];
+
+        Collection<List<?>> subjectAltNames = 
privateCert.getSubjectAlternativeNames();
+        if (subjectAltNames != null && !subjectAltNames.isEmpty())
+        {
+            for (List<?> item : subjectAltNames)
+            {
+                Integer type = (Integer) item.get(0);
+                String identity = (String) item.get(1);
+                if (type == SUBJECT_ALT_NAME_URI_TYPE && 
identity.startsWith(SPIFFE_PREFIX))
+                {
+                    return identity;
+                }
+            }
+        }
+
+        throw new CredentialValidationException("Unable to extract valid 
Spiffe ID from certificate");
+    }
+
+    /**
+     * Filters instances of {@link X509Certificate} certificates and returns 
the certificate chain as
+     * {@link X509Certificate} certificates.
+     *
+     * @param certificateChain client certificate chain
+     * @return an array of {@link X509Certificate} certificates
+     */
+    private X509Certificate[] castCertsToX509(Certificate[] certificateChain) 
throws CredentialValidationException
+    {
+        if (certificateChain == null || certificateChain.length == 0)
+        {
+            throw new CredentialValidationException("Certificate chain shared 
is empty");
+        }
+        return Arrays.stream(certificateChain).filter(certificate -> 
certificate instanceof X509Certificate).toArray(X509Certificate[]::new);
+    }

Review Comment:
   I would suggest doing this instead. You can avoid the throw statement.
   
   ```suggestion
       private List<X509Certificate> castCertsToX509(Certificate[] 
certificateChain) throws CredentialValidationException
       {
           List<X509Certificate> x509Certs = new ArrayList<>();
           for (Certificate cert : certificateChain)
           {
               if (cert instanceof X509Certificate)
               {
                   x509Certs.add((X509Certificate) cert);
               }
           }
           return x509Certs;
       }
   ```
   
   In fact, it 



##########
vertx-auth-mtls/src/main/java/io/vertx/ext/auth/mtls/impl/MutualTlsAuthenticationProvider.java:
##########
@@ -0,0 +1,93 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF 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.
+ */
+
+package io.vertx.ext.auth.mtls.impl;
+
+import io.vertx.core.AsyncResult;
+import io.vertx.core.Future;
+import io.vertx.core.Handler;
+import io.vertx.core.json.JsonObject;
+import io.vertx.ext.auth.User;
+import io.vertx.ext.auth.authentication.AuthenticationProvider;
+import io.vertx.ext.auth.authentication.CertificateCredentials;
+import io.vertx.ext.auth.authentication.Credentials;
+import io.vertx.ext.auth.mtls.CertificateIdentityExtractor;
+import io.vertx.ext.auth.mtls.CertificateIdentityValidator;
+import io.vertx.ext.auth.mtls.CertificateValidator;
+
+import java.security.cert.Certificate;
+
+/**
+ * {@link AuthenticationProvider} implementation for mTLS (MutualTLS) 
authentication. With mTLS authentication
+ * both server and client exchange certificates and validates each other's 
certificates.
+ */
+public class MutualTlsAuthenticationProvider implements AuthenticationProvider
+{
+    private final CertificateValidator certificateValidator;
+    private final CertificateIdentityExtractor identityExtractor;
+    private final CertificateIdentityValidator identityValidator;
+
+    public MutualTlsAuthenticationProvider(CertificateValidator 
certificateValidator,
+                                           CertificateIdentityExtractor 
identityExtractor,
+                                           CertificateIdentityValidator 
identityValidator)
+    {
+        this.certificateValidator = certificateValidator;
+        this.identityExtractor = identityExtractor;
+        this.identityValidator = identityValidator;
+    }
+
+    @Override
+    public Future<User> authenticate(Credentials credentials)
+    {
+        if (!(credentials instanceof CertificateCredentials))
+        {
+            return Future.failedFuture("CertificateCredentials expected for 
mTLS authentication");
+        }
+
+        CertificateCredentials certificateCredentials = 
(CertificateCredentials) credentials;
+        String identity;
+        try
+        {
+            certificateValidator.isValidCertificate(certificateCredentials);
+            identity = 
identityExtractor.identity(certificateCredentials.certificateChain().toArray(new
 Certificate[0]));
+        }
+        catch (Exception e)
+        {
+            return Future.failedFuture("Invalid certificate passed");

Review Comment:
   do not swallow the exception.



##########
vertx-auth-mtls/src/main/java/io/vertx/ext/auth/mtls/impl/SpiffeIdentityValidator.java:
##########
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF 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.
+ */
+
+package io.vertx.ext.auth.mtls.impl;
+
+import io.vertx.ext.auth.mtls.CertificateIdentityValidator;
+
+/**
+ * {@link CertificateIdentityValidator} implementation for SPIFFE certificates 
for validating SPIFFE identities.
+ * SPIFFE is a URI, present as part of SAN of client certificates, it uniquely 
identifies client.
+ */
+public class SpiffeIdentityValidator implements CertificateIdentityValidator
+{
+    private static final String SPIFFE_ID_PREFIX = "spiffe://";

Review Comment:
   The same constant is define in multiple classes. Another sign that those 
classes should be a single one. 



##########
vertx-auth-mtls/src/main/java/io/vertx/ext/auth/mtls/impl/SpiffeIdentityExtractor.java:
##########
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF 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.
+ */
+
+package io.vertx.ext.auth.mtls.impl;
+
+import io.vertx.ext.auth.authentication.CredentialValidationException;
+import io.vertx.ext.auth.mtls.CertificateIdentityExtractor;
+
+import java.security.cert.Certificate;
+import java.security.cert.CertificateParsingException;
+import java.security.cert.X509Certificate;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * {@link CertificateIdentityExtractor} implementation for SPIFFE certificates 
for extracting SPIFFE identity.
+ * SPIFFE is a URI, present as part of SAN of client certificates, it uniquely 
identifies client.
+ */
+public class SpiffeIdentityExtractor implements CertificateIdentityExtractor
+{
+    private static final int SUBJECT_ALT_NAME_URI_TYPE = 6;
+    private static final String SPIFFE_PREFIX = "spiffe://";
+
+    @Override
+    public String identity(Certificate[] certificateChain) throws 
CredentialValidationException, CertificateParsingException
+    {
+        X509Certificate[] castedCerts = castCertsToX509(certificateChain);
+        if (castedCerts.length == 0)
+        {
+            throw new CredentialValidationException("No based X509Certificate 
found for validating");

Review Comment:
   Is the word `based` added to the error message by accident? 



##########
vertx-auth-mtls/src/main/java/io/vertx/ext/auth/mtls/impl/SpiffeIdentityExtractor.java:
##########
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF 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.
+ */
+
+package io.vertx.ext.auth.mtls.impl;
+
+import io.vertx.ext.auth.authentication.CredentialValidationException;
+import io.vertx.ext.auth.mtls.CertificateIdentityExtractor;
+
+import java.security.cert.Certificate;
+import java.security.cert.CertificateParsingException;
+import java.security.cert.X509Certificate;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * {@link CertificateIdentityExtractor} implementation for SPIFFE certificates 
for extracting SPIFFE identity.
+ * SPIFFE is a URI, present as part of SAN of client certificates, it uniquely 
identifies client.
+ */
+public class SpiffeIdentityExtractor implements CertificateIdentityExtractor
+{
+    private static final int SUBJECT_ALT_NAME_URI_TYPE = 6;
+    private static final String SPIFFE_PREFIX = "spiffe://";
+
+    @Override
+    public String identity(Certificate[] certificateChain) throws 
CredentialValidationException, CertificateParsingException
+    {
+        X509Certificate[] castedCerts = castCertsToX509(certificateChain);
+        if (castedCerts.length == 0)
+        {
+            throw new CredentialValidationException("No based X509Certificate 
found for validating");
+        }
+        X509Certificate privateCert = castedCerts[0];

Review Comment:
   why checking only the first cert from the array? 



##########
vertx-auth-mtls/src/main/java/io/vertx/ext/auth/authentication/CertificateCredentials.java:
##########
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF 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.
+ */
+
+package io.vertx.ext.auth.authentication;
+
+import io.vertx.core.http.HttpServerRequest;
+import io.vertx.core.json.JsonObject;
+
+import java.security.cert.Certificate;
+import java.util.Collections;
+import java.util.List;
+import javax.net.ssl.SSLPeerUnverifiedException;
+
+/**
+ * Certificates based {@link Credentials} implementation, carries user's 
certificates, which can be used for
+ * authenticating or authorizing users.
+ */
+public class CertificateCredentials implements Credentials

Review Comment:
   Do you need to implement `applyHttpChallenge` and `toHttpAuthorization` 
methods? It is hard to see the need since this patch only adds the code that is 
not used. 



##########
vertx-auth-mtls/src/main/java/io/vertx/ext/auth/mtls/CertificateIdentityValidator.java:
##########
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF 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.
+ */
+
+package io.vertx.ext.auth.mtls;
+
+/**
+ * {@link CertificateIdentityValidator} interface can be extended to implement 
custom certificate identity validator.
+ */
+public interface CertificateIdentityValidator
+{
+    /**
+     * Verifies that identity extracted from certificate is a valid identity.
+     *
+     * @param identity {@code String} string extracted from certificate, 
uniquely represents client
+     * @return {@code true} if the identity is valid, {@code false otherwise}
+     */
+    boolean isValidIdentity(String identity);

Review Comment:
   Prefer throwing exception instead of returning boolean for the validation. 
The exception conveys better context when it throws. 
   
   ```suggestion
       void validateIdentity(String identity) throws InvalidIdentityException;
   ```



##########
vertx-auth-mtls/build.gradle:
##########
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF 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.
+ */
+
+import java.nio.file.Paths
+
+plugins {
+    id('java-library')
+    id('idea')
+    id('maven-publish')
+}
+
+group 'org.apache.cassandra.sidecar'
+version project.version
+
+sourceCompatibility = 1.8
+
+repositories {
+    mavenCentral()
+}
+
+test {
+    useJUnitPlatform()
+    maxParallelForks = Runtime.runtime.availableProcessors().intdiv(2) ?: 1
+    reports {
+        junitXml.enabled = true
+        def destDir = Paths.get(rootProject.rootDir.absolutePath, "build", 
"test-results", "vertx-auth-mtls").toFile()
+        println("Destination directory for vertx-auth-mtls tests: ${destDir}")
+        junitXml.destination = destDir
+        html.enabled = true
+    }
+}
+
+configurations {
+    all*.exclude(group: 'ch.qos.logback')
+}
+
+dependencies {
+    implementation(group: 'io.vertx', name: 'vertx-auth-common', version: 
'4.5.8')
+    testImplementation(group: 'io.vertx', name: 'vertx-junit5', version: 
'4.5.8')
+    testImplementation group: 'org.assertj', name: 'assertj-core', version: 
'3.26.3'
+    testImplementation group: 'org.mockito', name: 'mockito-core', version: 
'2.1.0'
+    testImplementation group: 'org.bouncycastle', name: 'bcpkix-jdk15on', 
version: '1.70'

Review Comment:
   nit: use the same style
   
   ```suggestion
       testImplementation(group: 'org.assertj', name: 'assertj-core', version: 
'3.26.3')
       testImplementation(group: 'org.mockito', name: 'mockito-core', version: 
'2.1.0')
       testImplementation(group: 'org.bouncycastle', name: 'bcpkix-jdk15on', 
version: '1.70')
   ```
   



##########
vertx-auth-mtls/src/main/java/io/vertx/ext/auth/mtls/impl/MutualTlsAuthenticationProvider.java:
##########
@@ -0,0 +1,93 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF 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.
+ */
+
+package io.vertx.ext.auth.mtls.impl;
+
+import io.vertx.core.AsyncResult;
+import io.vertx.core.Future;
+import io.vertx.core.Handler;
+import io.vertx.core.json.JsonObject;
+import io.vertx.ext.auth.User;
+import io.vertx.ext.auth.authentication.AuthenticationProvider;
+import io.vertx.ext.auth.authentication.CertificateCredentials;
+import io.vertx.ext.auth.authentication.Credentials;
+import io.vertx.ext.auth.mtls.CertificateIdentityExtractor;
+import io.vertx.ext.auth.mtls.CertificateIdentityValidator;
+import io.vertx.ext.auth.mtls.CertificateValidator;
+
+import java.security.cert.Certificate;
+
+/**
+ * {@link AuthenticationProvider} implementation for mTLS (MutualTLS) 
authentication. With mTLS authentication
+ * both server and client exchange certificates and validates each other's 
certificates.
+ */
+public class MutualTlsAuthenticationProvider implements AuthenticationProvider
+{
+    private final CertificateValidator certificateValidator;
+    private final CertificateIdentityExtractor identityExtractor;
+    private final CertificateIdentityValidator identityValidator;
+
+    public MutualTlsAuthenticationProvider(CertificateValidator 
certificateValidator,
+                                           CertificateIdentityExtractor 
identityExtractor,
+                                           CertificateIdentityValidator 
identityValidator)
+    {
+        this.certificateValidator = certificateValidator;
+        this.identityExtractor = identityExtractor;
+        this.identityValidator = identityValidator;
+    }
+
+    @Override
+    public Future<User> authenticate(Credentials credentials)
+    {
+        if (!(credentials instanceof CertificateCredentials))
+        {
+            return Future.failedFuture("CertificateCredentials expected for 
mTLS authentication");
+        }
+
+        CertificateCredentials certificateCredentials = 
(CertificateCredentials) credentials;
+        String identity;
+        try
+        {
+            certificateValidator.isValidCertificate(certificateCredentials);
+            identity = 
identityExtractor.identity(certificateCredentials.certificateChain().toArray(new
 Certificate[0]));
+        }
+        catch (Exception e)
+        {
+            return Future.failedFuture("Invalid certificate passed");
+        }
+
+        if (identity == null || identity.isEmpty())
+        {
+            return Future.failedFuture("Could not extract identity from 
certificate");
+        }

Review Comment:
   This block can be deleted, if you update the API to return non-null value 
only. 



##########
vertx-auth-mtls/src/main/java/io/vertx/ext/auth/mtls/impl/SpiffeIdentityExtractor.java:
##########
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF 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.
+ */
+
+package io.vertx.ext.auth.mtls.impl;
+
+import io.vertx.ext.auth.authentication.CredentialValidationException;
+import io.vertx.ext.auth.mtls.CertificateIdentityExtractor;
+
+import java.security.cert.Certificate;
+import java.security.cert.CertificateParsingException;
+import java.security.cert.X509Certificate;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * {@link CertificateIdentityExtractor} implementation for SPIFFE certificates 
for extracting SPIFFE identity.
+ * SPIFFE is a URI, present as part of SAN of client certificates, it uniquely 
identifies client.
+ */
+public class SpiffeIdentityExtractor implements CertificateIdentityExtractor
+{
+    private static final int SUBJECT_ALT_NAME_URI_TYPE = 6;

Review Comment:
   Consider add a comment to further explain what is 6. 
   
   According to https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.6, 
type 6 is for URI.



##########
vertx-auth-mtls/src/main/java/io/vertx/ext/auth/mtls/CertificateIdentityExtractor.java:
##########
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF 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.
+ */
+
+package io.vertx.ext.auth.mtls;
+
+import io.vertx.ext.auth.authentication.CredentialValidationException;
+
+import java.security.cert.Certificate;
+import java.security.cert.CertificateParsingException;
+
+/**
+ * Extracts a valid identity from certificate chain provided.
+ */
+public interface CertificateIdentityExtractor
+{
+    /**
+     * Extracts identity out of {@code Certificate[]} certificate chain. If 
required, this identity can later be used
+     * for authorizing the user's resource level permissions.
+     *
+     * <p>An example of identity could be the following:
+     * <ul>
+     *  <li>an identifier in SAN of the certificate like SPIFFE
+     *  <li>CN of the certificate
+     *  <li>any other fields in the certificate can be combined and be used as 
identifier of the certificate
+     * </ul>
+     *
+     * @param certificateChain certificate chain of user
+     * @return identity {@code String} extracted from certificate
+     * @throws CredentialValidationException when identity cannot be extracted

Review Comment:
   missing javadoc for `CertificateParsingException`



##########
vertx-auth-mtls/src/main/java/io/vertx/ext/auth/mtls/impl/MutualTlsAuthenticationProvider.java:
##########
@@ -0,0 +1,93 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF 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.
+ */
+
+package io.vertx.ext.auth.mtls.impl;
+
+import io.vertx.core.AsyncResult;
+import io.vertx.core.Future;
+import io.vertx.core.Handler;
+import io.vertx.core.json.JsonObject;
+import io.vertx.ext.auth.User;
+import io.vertx.ext.auth.authentication.AuthenticationProvider;
+import io.vertx.ext.auth.authentication.CertificateCredentials;
+import io.vertx.ext.auth.authentication.Credentials;
+import io.vertx.ext.auth.mtls.CertificateIdentityExtractor;
+import io.vertx.ext.auth.mtls.CertificateIdentityValidator;
+import io.vertx.ext.auth.mtls.CertificateValidator;
+
+import java.security.cert.Certificate;
+
+/**
+ * {@link AuthenticationProvider} implementation for mTLS (MutualTLS) 
authentication. With mTLS authentication
+ * both server and client exchange certificates and validates each other's 
certificates.
+ */
+public class MutualTlsAuthenticationProvider implements AuthenticationProvider
+{
+    private final CertificateValidator certificateValidator;
+    private final CertificateIdentityExtractor identityExtractor;
+    private final CertificateIdentityValidator identityValidator;
+
+    public MutualTlsAuthenticationProvider(CertificateValidator 
certificateValidator,
+                                           CertificateIdentityExtractor 
identityExtractor,
+                                           CertificateIdentityValidator 
identityValidator)
+    {
+        this.certificateValidator = certificateValidator;
+        this.identityExtractor = identityExtractor;
+        this.identityValidator = identityValidator;
+    }
+
+    @Override
+    public Future<User> authenticate(Credentials credentials)
+    {
+        if (!(credentials instanceof CertificateCredentials))
+        {
+            return Future.failedFuture("CertificateCredentials expected for 
mTLS authentication");
+        }
+
+        CertificateCredentials certificateCredentials = 
(CertificateCredentials) credentials;
+        String identity;
+        try
+        {
+            certificateValidator.isValidCertificate(certificateCredentials);
+            identity = 
identityExtractor.identity(certificateCredentials.certificateChain().toArray(new
 Certificate[0]));
+        }
+        catch (Exception e)
+        {
+            return Future.failedFuture("Invalid certificate passed");
+        }
+
+        if (identity == null || identity.isEmpty())
+        {
+            return Future.failedFuture("Could not extract identity from 
certificate");
+        }
+        if (!identityValidator.isValidIdentity(identity))
+        {
+            return Future.failedFuture("Certificate identity is not a valid 
identity");
+        }
+        return Future.succeededFuture(User.fromName(identity));
+    }
+
+    /**
+     * @deprecated use {@code authenticate(Credentials credentials, 
Handler<AsyncResult<User>> resultHandler)} instead

Review Comment:
   1. IIRC, codestyle plugin does not like `@deprecated`
   2. the message `use {@code authenticate(Credentials credentials, 
Handler<AsyncResult<User>> resultHandler)} instead` is incorrect. It is 
referring to itself. You probably want to have `use {@code 
authenticate(Credentials credentials} instead`



##########
vertx-auth-mtls/src/main/java/io/vertx/ext/auth/mtls/CertificateIdentityExtractor.java:
##########
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF 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.
+ */
+
+package io.vertx.ext.auth.mtls;
+
+import io.vertx.ext.auth.authentication.CredentialValidationException;
+
+import java.security.cert.Certificate;
+import java.security.cert.CertificateParsingException;
+
+/**
+ * Extracts a valid identity from certificate chain provided.
+ */
+public interface CertificateIdentityExtractor
+{
+    /**
+     * Extracts identity out of {@code Certificate[]} certificate chain. If 
required, this identity can later be used
+     * for authorizing the user's resource level permissions.
+     *
+     * <p>An example of identity could be the following:
+     * <ul>
+     *  <li>an identifier in SAN of the certificate like SPIFFE
+     *  <li>CN of the certificate
+     *  <li>any other fields in the certificate can be combined and be used as 
identifier of the certificate
+     * </ul>
+     *
+     * @param certificateChain certificate chain of user
+     * @return identity {@code String} extracted from certificate
+     * @throws CredentialValidationException when identity cannot be extracted
+     */
+    String identity(Certificate[] certificateChain) throws 
CredentialValidationException, CertificateParsingException;

Review Comment:
   1. Can we use `List<Certificate>` instead of array? 
   The only callsite, i.e. `MutualTlsAuthenticationProvider#authenticate` 
converts from List to array to call the method. 
   
   2. How about we refine the contract of this API that it only returns 
non-null string? When it cannot extract any identity, it throws the 
`CredentialValidationException`. So there is no scenario, that the API would 
return null. In other words, this method either extracts some identity or 
throws. 



##########
vertx-auth-mtls/src/main/java/io/vertx/ext/auth/mtls/impl/MutualTlsAuthenticationProvider.java:
##########
@@ -0,0 +1,93 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF 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.
+ */
+
+package io.vertx.ext.auth.mtls.impl;
+
+import io.vertx.core.AsyncResult;
+import io.vertx.core.Future;
+import io.vertx.core.Handler;
+import io.vertx.core.json.JsonObject;
+import io.vertx.ext.auth.User;
+import io.vertx.ext.auth.authentication.AuthenticationProvider;
+import io.vertx.ext.auth.authentication.CertificateCredentials;
+import io.vertx.ext.auth.authentication.Credentials;
+import io.vertx.ext.auth.mtls.CertificateIdentityExtractor;
+import io.vertx.ext.auth.mtls.CertificateIdentityValidator;
+import io.vertx.ext.auth.mtls.CertificateValidator;
+
+import java.security.cert.Certificate;
+
+/**
+ * {@link AuthenticationProvider} implementation for mTLS (MutualTLS) 
authentication. With mTLS authentication
+ * both server and client exchange certificates and validates each other's 
certificates.
+ */
+public class MutualTlsAuthenticationProvider implements AuthenticationProvider
+{
+    private final CertificateValidator certificateValidator;
+    private final CertificateIdentityExtractor identityExtractor;
+    private final CertificateIdentityValidator identityValidator;
+
+    public MutualTlsAuthenticationProvider(CertificateValidator 
certificateValidator,
+                                           CertificateIdentityExtractor 
identityExtractor,
+                                           CertificateIdentityValidator 
identityValidator)
+    {
+        this.certificateValidator = certificateValidator;
+        this.identityExtractor = identityExtractor;
+        this.identityValidator = identityValidator;
+    }
+
+    @Override
+    public Future<User> authenticate(Credentials credentials)
+    {
+        if (!(credentials instanceof CertificateCredentials))
+        {
+            return Future.failedFuture("CertificateCredentials expected for 
mTLS authentication");
+        }
+
+        CertificateCredentials certificateCredentials = 
(CertificateCredentials) credentials;
+        String identity;
+        try
+        {
+            certificateValidator.isValidCertificate(certificateCredentials);
+            identity = 
identityExtractor.identity(certificateCredentials.certificateChain().toArray(new
 Certificate[0]));
+        }
+        catch (Exception e)
+        {
+            return Future.failedFuture("Invalid certificate passed");
+        }
+
+        if (identity == null || identity.isEmpty())
+        {
+            return Future.failedFuture("Could not extract identity from 
certificate");
+        }
+        if (!identityValidator.isValidIdentity(identity))
+        {
+            return Future.failedFuture("Certificate identity is not a valid 
identity");
+        }

Review Comment:
   You can merge the validation code with the try-catch block above, once you 
made the interface to throw exception, instead of returning boolean. 



##########
vertx-auth-mtls/src/main/java/io/vertx/ext/auth/mtls/impl/CertificateValidatorImpl.java:
##########
@@ -0,0 +1,189 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF 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.
+ */
+
+package io.vertx.ext.auth.mtls.impl;
+
+import io.vertx.ext.auth.authentication.CertificateCredentials;
+import io.vertx.ext.auth.authentication.CredentialValidationException;
+import io.vertx.ext.auth.mtls.CertificateValidator;
+
+import javax.naming.InvalidNameException;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.ldap.LdapName;
+import javax.naming.ldap.Rdn;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateExpiredException;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * {@link CertificateValidator} implementation that can be used for validating 
certificates.
+ */
+public class CertificateValidatorImpl implements CertificateValidator
+{
+    private final Set<String> trustedCNs;
+    private final String trustedIssuerOrganization;
+    private final String trustedIssuerOrganizationUnit;
+    private final String trustedIssuerCountry;
+
+    public CertificateValidatorImpl()
+    {
+        this.trustedCNs = Collections.emptySet();
+        this.trustedIssuerOrganization = null;
+        this.trustedIssuerOrganizationUnit = null;
+        this.trustedIssuerCountry = null;
+    }
+

Review Comment:
   not used



##########
vertx-auth-mtls/src/main/java/io/vertx/ext/auth/mtls/CertificateValidator.java:
##########
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF 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.
+ */
+
+package io.vertx.ext.auth.mtls;
+
+import io.vertx.ext.auth.authentication.CertificateCredentials;
+
+import java.security.cert.CertificateParsingException;
+
+/**
+ * Interface for validating certificates for mutual TLS authentication.
+ * <p>
+ * This interface can be implemented to provide logic for validating various 
fields from Certificates.
+ */
+public interface CertificateValidator

Review Comment:
   I feel that all three interfaces `CertificateValidator`, 
`CertificateIdentityExtractor` and `CertificateIdentityValidator` can merge 
together to reduce cognitive complexity. They are tightly coupled logically, 
even if they are 3 individual interfaces as of now. For example, you always 
pair `SpiffeIdentityExtractor` with `SpiffeIdentityValidator`. For another 
example, it is probably never a valid scenario to have this combination, 
`AllowAllCertValidator + SpiffeIdentityExtractor + AllowAllIndentityValidator`, 
but the `MutualTlsAuthenticationProvider` constructor would allow it. 



##########
vertx-auth-mtls/src/main/java/io/vertx/ext/auth/mtls/impl/CertificateValidatorImpl.java:
##########
@@ -0,0 +1,189 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF 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.
+ */
+
+package io.vertx.ext.auth.mtls.impl;
+
+import io.vertx.ext.auth.authentication.CertificateCredentials;
+import io.vertx.ext.auth.authentication.CredentialValidationException;
+import io.vertx.ext.auth.mtls.CertificateValidator;
+
+import javax.naming.InvalidNameException;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.ldap.LdapName;
+import javax.naming.ldap.Rdn;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateExpiredException;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * {@link CertificateValidator} implementation that can be used for validating 
certificates.
+ */
+public class CertificateValidatorImpl implements CertificateValidator
+{
+    private final Set<String> trustedCNs;
+    private final String trustedIssuerOrganization;
+    private final String trustedIssuerOrganizationUnit;
+    private final String trustedIssuerCountry;
+
+    public CertificateValidatorImpl()
+    {
+        this.trustedCNs = Collections.emptySet();
+        this.trustedIssuerOrganization = null;
+        this.trustedIssuerOrganizationUnit = null;
+        this.trustedIssuerCountry = null;
+    }
+
+    public CertificateValidatorImpl(Set<String> trustedCNs,
+                                    String trustedIssuerOrganization,
+                                    String trustedIssuerOrganizationUnit,
+                                    String trustedIssuerCountry)
+    {
+        this.trustedCNs = Collections.unmodifiableSet(trustedCNs);
+        this.trustedIssuerOrganization = trustedIssuerOrganization;
+        this.trustedIssuerOrganizationUnit = trustedIssuerOrganizationUnit;
+        this.trustedIssuerCountry = trustedIssuerCountry;
+    }
+
+    @Override
+    public boolean isValidCertificate(CertificateCredentials credentials)
+    {
+        credentials.checkValid();
+        Certificate certificate = credentials.certificateChain().get(0);
+        if (certificate instanceof X509Certificate)
+        {
+            X509Certificate castedCert = (X509Certificate) certificate;
+            if (!isValidIssuer(castedCert))
+            {
+                return false;
+            }
+
+            try
+            {
+                castedCert.checkValidity();
+                return true;
+            }
+            catch (CertificateExpiredException e)
+            {
+                throw new CredentialValidationException("Expired certificates 
shared for authentication");
+            }
+            catch (Exception e)
+            {
+                return false;
+            }
+        }
+        return false;

Review Comment:
   It requires the first certificate to be a `X509Certificate`, otherwise 
returns false? Also, why only the first cert?



##########
vertx-auth-mtls/src/main/java/io/vertx/ext/auth/mtls/CertificateValidator.java:
##########
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF 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.
+ */
+
+package io.vertx.ext.auth.mtls;
+
+import io.vertx.ext.auth.authentication.CertificateCredentials;
+
+import java.security.cert.CertificateParsingException;
+
+/**
+ * Interface for validating certificates for mutual TLS authentication.
+ * <p>
+ * This interface can be implemented to provide logic for validating various 
fields from Certificates.
+ */
+public interface CertificateValidator
+{
+    /**
+     * Perform any checks that are to be performed on the certificate before 
authenticating user.
+     *
+     * <p>For example:
+     * <ul>
+     *  <li>Verifying CA information
+     *  <li>Checking CN information
+     *  <li>Validating Issuer information
+     *  <li>Checking organization information etc
+     * </ul>
+     *
+     * @param credentials user certificate credentials shared
+     * @return {@code true} if the credentials are valid, {@code false} 
otherwise
+     */
+    boolean isValidCertificate(CertificateCredentials credentials) throws 
CertificateParsingException;

Review Comment:
   if it throws exception, you do not need to return boolean. If it is valid, 
no throw; it throws otherwise. 



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]


Reply via email to