You are viewing a plain text version of this content. The canonical link for it is here.
Posted to pr@cassandra.apache.org by "sarankk (via GitHub)" <gi...@apache.org> on 2024/11/05 18:51:28 UTC

[PR] CASSANDRASC-156 Add mTLS Authentication in Sidecar [cassandra-sidecar]

sarankk opened a new pull request, #145:
URL: https://github.com/apache/cassandra-sidecar/pull/145

   (no comment)


-- 
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: pr-unsubscribe@cassandra.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: pr-unsubscribe@cassandra.apache.org
For additional commands, e-mail: pr-help@cassandra.apache.org


Re: [PR] CASSANDRASC-156 Add mTLS Authentication in Sidecar [cassandra-sidecar]

Posted by "sarankk (via GitHub)" <gi...@apache.org>.
sarankk commented on code in PR #145:
URL: https://github.com/apache/cassandra-sidecar/pull/145#discussion_r1830370265


##########
server/src/main/java/org/apache/cassandra/sidecar/accesscontrol/AuthCacheService.java:
##########
@@ -0,0 +1,77 @@
+/*
+ * 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 org.apache.cassandra.sidecar.accesscontrol;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import com.google.common.base.Preconditions;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import org.apache.cassandra.sidecar.config.SidecarConfiguration;
+
+/**
+ * {@link AuthCacheService} allows for easier maintenance of all {@link AuthCache}.
+ */
+@Singleton
+public class AuthCacheService
+{
+    private static final Logger LOGGER = LoggerFactory.getLogger(AuthCacheService.class);
+    private final boolean enabled;
+    private final Set<AuthCache<?, ?>> caches = new HashSet<>();

Review Comment:
   it is for maintaining auth caches together, we warm caches. It will be useful for invalidate too. Right we dont have any use case for cache retrieval, hence didnt add getter methods



##########
server/src/main/java/org/apache/cassandra/sidecar/accesscontrol/AuthCacheService.java:
##########
@@ -0,0 +1,77 @@
+/*
+ * 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 org.apache.cassandra.sidecar.accesscontrol;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import com.google.common.base.Preconditions;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import org.apache.cassandra.sidecar.config.SidecarConfiguration;
+
+/**
+ * {@link AuthCacheService} allows for easier maintenance of all {@link AuthCache}.
+ */
+@Singleton
+public class AuthCacheService
+{
+    private static final Logger LOGGER = LoggerFactory.getLogger(AuthCacheService.class);
+    private final boolean enabled;
+    private final Set<AuthCache<?, ?>> caches = new HashSet<>();

Review Comment:
   It is for maintaining auth caches together, we warm caches. It will be useful for invalidate too. Right we dont have any use case for cache retrieval, hence didnt add getter methods



-- 
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: pr-unsubscribe@cassandra.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: pr-unsubscribe@cassandra.apache.org
For additional commands, e-mail: pr-help@cassandra.apache.org


Re: [PR] CASSANDRASC-156 Add mTLS Authentication in Sidecar [cassandra-sidecar]

Posted by "sarankk (via GitHub)" <gi...@apache.org>.
sarankk commented on code in PR #145:
URL: https://github.com/apache/cassandra-sidecar/pull/145#discussion_r1831557658


##########
vertx-auth-mtls/src/main/java/io/vertx/ext/auth/mtls/CertificateIdentityExtractor.java:
##########
@@ -40,8 +42,8 @@ public interface CertificateIdentityExtractor
      * </ul>
      *
      * @param certificateCredentials certificate chain of user that is already verified
-     * @return {@code String} identity string extracted from certificate, uniquely represents client
+     * @return list of valid identities extracted from certificate.
      * @throws CredentialValidationException when a valid identity cannot be extracted from certificate chain.
      */
-    String validIdentity(CertificateCredentials certificateCredentials) throws CredentialValidationException;
+    List<String> validIdentities(CertificateCredentials certificateCredentials) throws CredentialValidationException;

Review Comment:
   Sometimes we could extract multiple identities from a certificate. If there are multiple spiffe(s) listed in SAN, or just from multiple SAN entries we could extract different identities. Hence we need to return a list rather than a single identity from cert. 



-- 
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: pr-unsubscribe@cassandra.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: pr-unsubscribe@cassandra.apache.org
For additional commands, e-mail: pr-help@cassandra.apache.org


Re: [PR] CASSANDRASC-156: Add mTLS Authentication in Sidecar [cassandra-sidecar]

Posted by "yifan-c (via GitHub)" <gi...@apache.org>.
yifan-c commented on code in PR #145:
URL: https://github.com/apache/cassandra-sidecar/pull/145#discussion_r1837357600


##########
server/src/main/java/org/apache/cassandra/sidecar/acl/IdentityToRoleCache.java:
##########
@@ -47,11 +46,18 @@ public IdentityToRoleCache(Vertx vertx,
               systemAuthDatabaseAccessor::findRoleFromIdentity,
               systemAuthDatabaseAccessor::findAllIdentityToRoles,
               sidecarConfiguration.accessControlConfiguration().permissionCacheConfiguration());
-        this.systemAuthDatabaseAccessor = systemAuthDatabaseAccessor;
     }
 
     public boolean containsKey(String identity)
     {
-        return cache != null && get(identity) != null;
+        try
+        {
+            return cache() != null && get(identity) != null;
+        }
+        catch (IllegalStateException e)

Review Comment:
   nit: probably throw a dedicated exception, say `CqlPreparedStatementUnavailableException`, to avoid mute other ISE by accident.  



-- 
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: pr-unsubscribe@cassandra.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: pr-unsubscribe@cassandra.apache.org
For additional commands, e-mail: pr-help@cassandra.apache.org


Re: [PR] CASSANDRASC-156: Add mTLS Authentication in Sidecar [cassandra-sidecar]

Posted by "yifan-c (via GitHub)" <gi...@apache.org>.
yifan-c merged PR #145:
URL: https://github.com/apache/cassandra-sidecar/pull/145


-- 
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: pr-unsubscribe@cassandra.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: pr-unsubscribe@cassandra.apache.org
For additional commands, e-mail: pr-help@cassandra.apache.org


Re: [PR] CASSANDRASC-156 Add mTLS Authentication in Sidecar [cassandra-sidecar]

Posted by "sarankk (via GitHub)" <gi...@apache.org>.
sarankk commented on code in PR #145:
URL: https://github.com/apache/cassandra-sidecar/pull/145#discussion_r1831570316


##########
server/src/main/java/org/apache/cassandra/sidecar/accesscontrol/IdentityRoleCache.java:
##########
@@ -0,0 +1,57 @@
+/*
+ * 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 org.apache.cassandra.sidecar.accesscontrol;
+
+import org.apache.cassandra.sidecar.config.CacheConfiguration;
+import org.apache.cassandra.sidecar.db.SystemAuthDatabaseAccessor;
+
+/**
+ * Caches entries from identity_to_role table. identity_to_role table maps valid certificate identity to Cassandra
+ * role the identity holds. identity_to_role table is created in Cassandra versions 5.x and above
+ */
+public class IdentityRoleCache extends AuthCache<String, String>
+{
+    protected static final String NAME = "identity_to_role_cache";
+    protected final SystemAuthDatabaseAccessor systemAuthDatabaseAccessor;
+
+    public IdentityRoleCache(CacheConfiguration config,
+                             SystemAuthDatabaseAccessor systemAuthDatabaseAccessor)
+    {
+        super(NAME,
+              config.enabled(),
+              systemAuthDatabaseAccessor::findRoleFromIdentity,
+              systemAuthDatabaseAccessor::findAllIdentityRoles,
+              config.warmingRetries(),
+              2000,
+              config.expireAfterAccessMillis(),
+              config.maximumSize());
+        this.systemAuthDatabaseAccessor = systemAuthDatabaseAccessor;
+    }
+
+    public boolean contains(String identity)
+    {
+
+        if (cache == null)
+        {
+            return false;
+        }
+        String role = get(identity);
+        return  role != null && !role.isEmpty();

Review Comment:
   I think so, Cassandra doesn't allow empty string



-- 
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: pr-unsubscribe@cassandra.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: pr-unsubscribe@cassandra.apache.org
For additional commands, e-mail: pr-help@cassandra.apache.org


Re: [PR] CASSANDRASC-156 Add mTLS Authentication in Sidecar [cassandra-sidecar]

Posted by "yifan-c (via GitHub)" <gi...@apache.org>.
yifan-c commented on code in PR #145:
URL: https://github.com/apache/cassandra-sidecar/pull/145#discussion_r1833412360


##########
server-common/src/main/java/org/apache/cassandra/sidecar/db/schema/CassandraSystemTableSchema.java:
##########
@@ -0,0 +1,52 @@
+/*
+ * 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 org.apache.cassandra.sidecar.db.schema;
+
+import com.datastax.driver.core.Metadata;
+import com.datastax.driver.core.Session;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Schema for existing tables in Cassandra.
+ */
+public abstract class CassandraSystemTableSchema extends TableSchema
+{
+    @Override
+    protected boolean exists(@NotNull Metadata metadata)
+    {
+        return true;
+    }
+
+    @Override
+    protected boolean initializeInternal(@NotNull Session session)
+    {
+        prepareStatements(session);
+        return true;
+    }
+
+    /**
+     * Should not create new tables, since schema is for existing tables.
+     */
+    @Override
+

Review Comment:
   nit: empty line



##########
server/src/main/java/org/apache/cassandra/sidecar/db/SystemAuthDatabaseAccessor.java:
##########
@@ -0,0 +1,89 @@
+/*
+ * 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 org.apache.cassandra.sidecar.db;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.datastax.driver.core.BoundStatement;
+import com.datastax.driver.core.ResultSet;
+import com.datastax.driver.core.Row;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import org.apache.cassandra.sidecar.common.server.CQLSessionProvider;
+import org.apache.cassandra.sidecar.db.schema.SystemAuthSchema;
+
+/**
+ * Database Accessor that queries cassandra to get information maintained under system_auth keyspace.
+ */
+@Singleton
+public class SystemAuthDatabaseAccessor extends DatabaseAccessor<SystemAuthSchema>
+{
+    @Inject
+    public SystemAuthDatabaseAccessor(SystemAuthSchema systemAuthSchema,
+                                      CQLSessionProvider sessionProvider)
+    {
+        super(systemAuthSchema, sessionProvider);
+    }
+
+    /**
+     * Queries Cassandra for the role associated with given identity.
+     *
+     * @param identity  Identity of user extracted
+     * @return the role associated with the given identity in Cassandra
+     */
+    public String findRoleFromIdentity(String identity)
+    {
+        if (!tableSchemaPrepared())
+        {
+            throw new IllegalStateException("SystemAuthSchema was not prepared, values can not be retrieved from table");
+        }
+        BoundStatement statement = tableSchema.selectRoleFromIdentity()
+                                              .bind(identity);
+        ResultSet result = execute(statement);
+        return result.one().getString("role");
+    }
+
+    /**
+     * Queries Cassandra for all rows in identity_to_role table
+     *
+     * @return - {@code List<Row>} containing each row in the identity to roles table
+     */
+    public Map<String, String> findAllIdentityToRoles()
+    {
+        if (!tableSchemaPrepared())
+        {
+            throw new IllegalStateException("SystemAuthSchema was not prepared, values can not be retrieved from table");
+        }
+        BoundStatement statement = tableSchema.getAllRolesAndIdentities().bind();
+
+        ResultSet resultSet = execute(statement);
+        Map<String, String> results = new HashMap<>();
+        for (Row row : resultSet)
+        {
+            results.put(row.getString("identity"), row.getString("role"));
+        }
+        return results;
+    }
+
+    private boolean tableSchemaPrepared()
+    {
+        return tableSchema.selectRoleFromIdentity() != null && tableSchema.getAllRolesAndIdentities() != null;
+    }

Review Comment:
   How about replacing with this method and update the callsites?
   ```suggestion
       private void ensureIdentityToRoleTableAccess()
       {
           if (tableSchema.selectRoleFromIdentity() == null || tableSchema.getAllRolesAndIdentities() == null)
           {
               throw new IllegalStateException("SystemAuthSchema was not prepared, values can not be retrieved from table");
           }
       }
   ```



##########
server/src/main/java/org/apache/cassandra/sidecar/accesscontrol/authentication/MutualTlsAuthenticationHandler.java:
##########
@@ -0,0 +1,58 @@
+/*
+ * 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 org.apache.cassandra.sidecar.accesscontrol.authentication;
+
+import io.netty.handler.codec.http.HttpResponseStatus;
+import io.vertx.core.AsyncResult;
+import io.vertx.core.Future;
+import io.vertx.core.Handler;
+import io.vertx.ext.auth.User;
+import io.vertx.ext.auth.authentication.CertificateCredentials;
+import io.vertx.ext.auth.mtls.MutualTlsAuthentication;
+import io.vertx.ext.web.RoutingContext;
+import io.vertx.ext.web.handler.HttpException;
+import io.vertx.ext.web.handler.impl.AuthenticationHandlerImpl;
+
+/**
+ * Handler for verifying user certificates for Mutual TLS authentication. {@link MutualTlsAuthenticationHandler} can be
+ * chained with other {@link io.vertx.ext.web.handler.AuthenticationHandler} implementations.
+ */
+public class MutualTlsAuthenticationHandler extends AuthenticationHandlerImpl<MutualTlsAuthentication>
+{
+    public MutualTlsAuthenticationHandler(MutualTlsAuthentication authProvider)
+    {
+        super(authProvider);
+    }
+
+    @Override
+    public void authenticate(RoutingContext ctx, Handler<AsyncResult<User>> handler)
+    {
+        if (!ctx.request().isSSL())
+        {
+            ctx.response().setStatusCode(HttpResponseStatus.BAD_REQUEST.code()).end();
+            return;
+        }
+
+        CertificateCredentials certificateCredentials = CertificateCredentials.fromHttpRequest(ctx.request());
+
+        authProvider.authenticate(certificateCredentials)
+                    .onSuccess(user -> handler.handle(Future.succeededFuture(user)))
+                    .onFailure(cause -> handler.handle(Future.failedFuture(new HttpException(HttpResponseStatus.UNAUTHORIZED.code(), cause))));

Review Comment:
   nit: alternative that is less wordy
   
   ```suggestion
           authProvider.authenticate(certificateCredentials)
                       .recover(cause -> { // converts any exception to unauthorized http exception
                           throw new HttpException(HttpResponseStatus.UNAUTHORIZED.code(), cause);
                       })
                       .andThen(handler);
   ```



##########
server/src/main/java/org/apache/cassandra/sidecar/server/MainModule.java:
##########
@@ -182,9 +194,63 @@ public Vertx vertx(SidecarConfiguration sidecarConfiguration, MetricRegistryFact
         return Vertx.vertx(vertxOptions);
     }
 
+    @Provides
+    @Singleton
+    public IdentityToRoleCache identityRoleCache(Vertx vertx,
+                                                 SidecarConfiguration sidecarConfiguration,
+                                                 SystemAuthDatabaseAccessor systemAuthDatabaseAccessor)
+    {
+        IdentityToRoleCache identityToRoleCache
+        = new IdentityToRoleCache(vertx,
+                                  sidecarConfiguration.accessControlConfiguration().permissionCacheConfiguration(),
+                                  systemAuthDatabaseAccessor);
+        return identityToRoleCache;
+    }
+
+    @Provides
+    @Singleton
+    public ChainAuthHandler chainAuthHandler(Vertx vertx,
+                                             SidecarConfiguration sidecarConfiguration,
+                                             IdentityToRoleCache identityToRoleCache)
+    {
+        ChainAuthHandler chainAuthHandler = ChainAuthHandler.any();
+        try
+        {
+            MutualTlsAuthenticatorConfiguration mTLSAuthenticatorConfig = sidecarConfiguration
+                                                                          .accessControlConfiguration()
+                                                                          .authenticatorsConfiguration()
+                                                                          .mTlsAuthenticatorConfiguration();
+            if (mTLSAuthenticatorConfig.enabled())
+            {
+                CertificateValidator certificateValidator = (CertificateValidator) Class.forName(mTLSAuthenticatorConfig.certificateValidator()).newInstance();
+                CertificateIdentityExtractor certificateIdentityExtractor;
+                if (mTLSAuthenticatorConfig.certificateIdentityExtractor().equalsIgnoreCase(CassandraIdentityExtractor.class.getName()))
+                {
+                    certificateIdentityExtractor
+                    = new CassandraIdentityExtractor(identityToRoleCache, sidecarConfiguration.accessControlConfiguration().adminIdentities());
+                }
+                else
+                {
+                    certificateIdentityExtractor = new SpiffeIdentityExtractor();
+                }

Review Comment:
   nit: `CertificateIdentityExtractor` can be created the same way as `CertificateValidator`
   Not a big concern. Feel free to ignore. 



##########
server/src/main/java/org/apache/cassandra/sidecar/db/schema/SystemAuthSchema.java:
##########
@@ -0,0 +1,86 @@
+/*
+ * 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 org.apache.cassandra.sidecar.db.schema;
+
+import com.datastax.driver.core.KeyspaceMetadata;
+import com.datastax.driver.core.PreparedStatement;
+import com.datastax.driver.core.Session;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Schema for getting information stored in system_auth keyspace.
+ */
+public class SystemAuthSchema extends CassandraExistingSchema
+{
+    private static final String IDENTITY_TO_ROLE_TABLE = "identity_to_role";
+    private PreparedStatement selectRoleFromIdentity;
+    private PreparedStatement getAllRolesAndIdentities;
+
+    protected String keyspaceName()
+    {
+        return "system_auth";
+    }
+
+    @Override
+    protected void prepareStatements(@NotNull Session session)
+    {
+        KeyspaceMetadata keyspaceMetadata = session.getCluster().getMetadata().getKeyspace(keyspaceName());
+        // identity_to_role table exists in Cassandra versions starting 5.x
+        if (keyspaceMetadata == null || keyspaceMetadata.getTable(IDENTITY_TO_ROLE_TABLE) == null)
+        {
+            return;
+        }
+        selectRoleFromIdentity = prepare(selectRoleFromIdentity,
+                                         session,
+                                         CqlLiterals.selectRoleFromIdentity());
+
+        getAllRolesAndIdentities = prepare(getAllRolesAndIdentities,
+                                           session,
+                                           CqlLiterals.getAllRolesAndIdentities());
+    }
+
+    protected String tableName()
+    {
+        throw new UnsupportedOperationException("SystemAuthSchema supports reading information from multiple " +
+                                                "tables in system_auth keyspace");
+    }
+
+    public PreparedStatement selectRoleFromIdentity()
+    {
+        return selectRoleFromIdentity;
+    }
+
+    public PreparedStatement getAllRolesAndIdentities()
+    {
+        return getAllRolesAndIdentities;
+    }

Review Comment:
   nit: add `@Nullable` for the return value



##########
server/src/main/java/org/apache/cassandra/sidecar/accesscontrol/AuthCache.java:
##########
@@ -0,0 +1,157 @@
+/*
+ * 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 org.apache.cassandra.sidecar.accesscontrol;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.github.benmanes.caffeine.cache.Caffeine;
+import com.github.benmanes.caffeine.cache.LoadingCache;
+import io.vertx.core.Vertx;
+import io.vertx.core.eventbus.EventBus;
+import org.apache.cassandra.sidecar.config.CacheConfiguration;
+import org.jetbrains.annotations.VisibleForTesting;
+
+import static com.google.common.util.concurrent.Uninterruptibles.sleepUninterruptibly;
+import static org.apache.cassandra.sidecar.server.SidecarServerEvents.ON_ALL_CASSANDRA_CQL_READY;
+
+/**
+ * Caches information needed for authenticating sidecar users.
+ *
+ * @param <K> Key type
+ * @param <V> Value type
+ */
+public abstract class AuthCache<K, V>
+{
+    protected static final Logger LOGGER = LoggerFactory.getLogger(AuthCache.class);
+    protected final String name;
+    protected final Vertx vertx;
+    protected final Function<K, V> loadFunction;
+    protected final Supplier<Map<K, V>> bulkLoadFunction;
+    protected final CacheConfiguration config;
+
+    // cache is null when AuthCache is disabled
+    protected volatile LoadingCache<K, V> cache;
+
+    protected AuthCache(String name,
+                        Vertx vertx,
+                        Function<K, V> loadFunction,
+                        Supplier<Map<K, V>> bulkLoadFunction,
+                        CacheConfiguration cacheConfiguration)
+    {
+        this.name = name;
+        this.vertx = vertx;
+        this.loadFunction = loadFunction;
+        this.bulkLoadFunction = bulkLoadFunction;
+        this.config = cacheConfiguration;
+        this.cache = initCache();
+
+        if (this.config.enabled())
+        {
+            configureSidecarServerEventListener();
+        }
+    }
+
+    /**
+     * Retrieves a value from the cache. Will call {@link LoadingCache#get(Object)} which will
+     * "load" the value if it's not present, thus populating the key. When the cache is disabled, data is fetched
+     * with loadFunction.
+     *
+     * @param k key
+     * @return The current value of {@code K} if cached or loaded.
+     *
+     * See {@link LoadingCache#get(Object)} for possible exceptions.
+     */
+    public V get(K k)
+    {
+        if (!config.enabled())
+        {
+            return loadFunction.apply(k);
+        }
+        return cache.get(k);
+    }
+
+    /**
+     * Retrieves all cached entries. Will call {@link LoadingCache#asMap()} which does not trigger "load". When cache
+     * is disabled, data is fetched with bulkLoadFunction.
+     *
+     * @return a map of cached key-value pairs
+     */
+    public Map<K, V> getAll()
+    {
+        if (!config.enabled())
+        {
+            return bulkLoadFunction.get();
+        }
+        return Collections.unmodifiableMap(cache.asMap());
+    }
+
+    private LoadingCache<K, V> initCache()
+    {
+        if (!config.enabled())

Review Comment:
   nit: the condition can be combined with line#71 in the file.



##########
server/src/test/integration/org/apache/cassandra/sidecar/testing/IntegrationTestModule.java:
##########
@@ -47,13 +58,26 @@
  */
 public class IntegrationTestModule extends AbstractModule
 {
+    public static final String ADMIN_IDENTITY = "spiffe://cassandra/sidecar/admin";

Review Comment:
   nit: same string declared in multiple locations. maybe consolidate. 



-- 
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: pr-unsubscribe@cassandra.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: pr-unsubscribe@cassandra.apache.org
For additional commands, e-mail: pr-help@cassandra.apache.org


Re: [PR] CASSANDRASC-156 Add mTLS Authentication in Sidecar [cassandra-sidecar]

Posted by "sarankk (via GitHub)" <gi...@apache.org>.
sarankk commented on code in PR #145:
URL: https://github.com/apache/cassandra-sidecar/pull/145#discussion_r1837324786


##########
server/src/main/java/org/apache/cassandra/sidecar/acl/AuthCache.java:
##########
@@ -0,0 +1,164 @@
+/*
+ * 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 org.apache.cassandra.sidecar.acl;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.github.benmanes.caffeine.cache.Caffeine;
+import com.github.benmanes.caffeine.cache.LoadingCache;
+import io.vertx.core.Vertx;
+import io.vertx.core.eventbus.EventBus;
+import org.apache.cassandra.sidecar.concurrent.ExecutorPools;
+import org.apache.cassandra.sidecar.concurrent.TaskExecutorPool;
+import org.apache.cassandra.sidecar.config.CacheConfiguration;
+import org.jetbrains.annotations.VisibleForTesting;
+
+import static org.apache.cassandra.sidecar.server.SidecarServerEvents.ON_ALL_CASSANDRA_CQL_READY;
+
+/**
+ * Caches information needed for authenticating sidecar users.
+ *
+ * @param <K> Key type
+ * @param <V> Value type
+ */
+public abstract class AuthCache<K, V>
+{
+    protected static final Logger LOGGER = LoggerFactory.getLogger(AuthCache.class);
+    protected final String name;
+    protected final Vertx vertx;
+    protected final Function<K, V> loadFunction;
+    protected final Supplier<Map<K, V>> bulkLoadFunction;
+    protected final CacheConfiguration config;
+    private final TaskExecutorPool internalPool;
+
+    // cache is null when AuthCache is disabled
+    protected volatile LoadingCache<K, V> cache;
+

Review Comment:
   For `Name`, `Vertx` might be better to keep them protected, wdyt?
   



-- 
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: pr-unsubscribe@cassandra.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: pr-unsubscribe@cassandra.apache.org
For additional commands, e-mail: pr-help@cassandra.apache.org


Re: [PR] CASSANDRASC-156 Add mTLS Authentication in Sidecar [cassandra-sidecar]

Posted by "sarankk (via GitHub)" <gi...@apache.org>.
sarankk commented on code in PR #145:
URL: https://github.com/apache/cassandra-sidecar/pull/145#discussion_r1831544345


##########
server/src/main/java/org/apache/cassandra/sidecar/db/schema/SystemAuthSchema.java:
##########
@@ -0,0 +1,107 @@
+/*
+ * 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 org.apache.cassandra.sidecar.db.schema;
+
+import com.datastax.driver.core.KeyspaceMetadata;
+import com.datastax.driver.core.Metadata;
+import com.datastax.driver.core.PreparedStatement;
+import com.datastax.driver.core.Session;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Schema for getting information stored in system_auth keyspace.
+ */
+public class SystemAuthSchema extends TableSchema

Review Comment:
   I think you mean for `createSchemaStatement` ? updated it to return null. `ConnectedClientsSchema` does not check table existence before preparing staemtent.



##########
server/src/main/java/org/apache/cassandra/sidecar/db/schema/SystemAuthSchema.java:
##########
@@ -0,0 +1,107 @@
+/*
+ * 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 org.apache.cassandra.sidecar.db.schema;
+
+import com.datastax.driver.core.KeyspaceMetadata;
+import com.datastax.driver.core.Metadata;
+import com.datastax.driver.core.PreparedStatement;
+import com.datastax.driver.core.Session;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Schema for getting information stored in system_auth keyspace.
+ */
+public class SystemAuthSchema extends TableSchema

Review Comment:
   I think you mean for `createSchemaStatement` ? updated it to return null. `ConnectedClientsSchema` does not check table existence before preparing statemtent.



-- 
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: pr-unsubscribe@cassandra.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: pr-unsubscribe@cassandra.apache.org
For additional commands, e-mail: pr-help@cassandra.apache.org


Re: [PR] CASSANDRASC-156 Add mTLS Authentication in Sidecar [cassandra-sidecar]

Posted by "sarankk (via GitHub)" <gi...@apache.org>.
sarankk commented on code in PR #145:
URL: https://github.com/apache/cassandra-sidecar/pull/145#discussion_r1831551571


##########
server/src/main/java/org/apache/cassandra/sidecar/db/schema/SystemAuthSchema.java:
##########
@@ -0,0 +1,107 @@
+/*
+ * 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 org.apache.cassandra.sidecar.db.schema;
+
+import com.datastax.driver.core.KeyspaceMetadata;
+import com.datastax.driver.core.Metadata;
+import com.datastax.driver.core.PreparedStatement;
+import com.datastax.driver.core.Session;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Schema for getting information stored in system_auth keyspace.
+ */
+public class SystemAuthSchema extends TableSchema
+{
+    private static final String IDENTITY_TO_ROLE_TABLE = "identity_to_role";
+    PreparedStatement selectRoleFromIdentity;
+    PreparedStatement getAllRolesAndIdentities;
+
+    protected String keyspaceName()
+    {
+        return "system_auth";
+    }
+
+    @Override
+    protected void prepareStatements(@NotNull Session session)
+    {
+        selectRoleFromIdentity = prepare(selectRoleFromIdentity,
+                                          session,
+                                          CqlLiterals.selectRoleFromIdentity());
+
+        getAllRolesAndIdentities = prepare(getAllRolesAndIdentities,
+                                           session,
+                                           CqlLiterals.getAllRolesAndIdentities());
+    }
+
+    protected String tableName()
+    {
+        throw new UnsupportedOperationException("SystemAuthSchema supports reading information from multiple " +
+                                                "tables in system_auth keyspace");
+    }
+
+    @Override
+    protected boolean exists(@NotNull Metadata metadata)
+    {
+        // check tables exists before preparing
+        KeyspaceMetadata keyspaceMetadata = metadata.getKeyspace(keyspaceName());
+        // identity_to_role table exists in Cassandra versions starting 5.x
+        return keyspaceMetadata != null && keyspaceMetadata.getTable(IDENTITY_TO_ROLE_TABLE) != null;

Review Comment:
   system_auth keyspace doesn't allow table creation, it is only internal Cassandra tables. Do we still need to worry about this? 



##########
server/src/main/java/org/apache/cassandra/sidecar/db/schema/SystemAuthSchema.java:
##########
@@ -0,0 +1,107 @@
+/*
+ * 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 org.apache.cassandra.sidecar.db.schema;
+
+import com.datastax.driver.core.KeyspaceMetadata;
+import com.datastax.driver.core.Metadata;
+import com.datastax.driver.core.PreparedStatement;
+import com.datastax.driver.core.Session;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Schema for getting information stored in system_auth keyspace.
+ */
+public class SystemAuthSchema extends TableSchema
+{
+    private static final String IDENTITY_TO_ROLE_TABLE = "identity_to_role";
+    PreparedStatement selectRoleFromIdentity;
+    PreparedStatement getAllRolesAndIdentities;
+
+    protected String keyspaceName()
+    {
+        return "system_auth";
+    }
+
+    @Override
+    protected void prepareStatements(@NotNull Session session)
+    {
+        selectRoleFromIdentity = prepare(selectRoleFromIdentity,
+                                          session,
+                                          CqlLiterals.selectRoleFromIdentity());
+
+        getAllRolesAndIdentities = prepare(getAllRolesAndIdentities,
+                                           session,
+                                           CqlLiterals.getAllRolesAndIdentities());
+    }
+
+    protected String tableName()
+    {
+        throw new UnsupportedOperationException("SystemAuthSchema supports reading information from multiple " +
+                                                "tables in system_auth keyspace");
+    }
+
+    @Override
+    protected boolean exists(@NotNull Metadata metadata)
+    {
+        // check tables exists before preparing
+        KeyspaceMetadata keyspaceMetadata = metadata.getKeyspace(keyspaceName());
+        // identity_to_role table exists in Cassandra versions starting 5.x
+        return keyspaceMetadata != null && keyspaceMetadata.getTable(IDENTITY_TO_ROLE_TABLE) != null;

Review Comment:
   `system_auth` keyspace doesn't allow table creation, it is only internal Cassandra tables. Do we still need to worry about this? 



-- 
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: pr-unsubscribe@cassandra.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: pr-unsubscribe@cassandra.apache.org
For additional commands, e-mail: pr-help@cassandra.apache.org


Re: [PR] CASSANDRASC-156 Add mTLS Authentication in Sidecar [cassandra-sidecar]

Posted by "sarankk (via GitHub)" <gi...@apache.org>.
sarankk commented on code in PR #145:
URL: https://github.com/apache/cassandra-sidecar/pull/145#discussion_r1830372066


##########
server/src/main/java/org/apache/cassandra/sidecar/accesscontrol/authentication/MutualTlsAuthenticationHandler.java:
##########
@@ -0,0 +1,85 @@
+/*
+ * 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 org.apache.cassandra.sidecar.accesscontrol.authentication;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import io.netty.handler.codec.http.HttpResponseStatus;
+import io.vertx.core.AsyncResult;
+import io.vertx.core.Future;
+import io.vertx.core.Handler;
+import io.vertx.ext.auth.User;
+import io.vertx.ext.auth.authentication.CertificateCredentials;
+import io.vertx.ext.auth.authentication.CredentialValidationException;
+import io.vertx.ext.auth.mtls.MutualTlsAuthentication;
+import io.vertx.ext.web.RoutingContext;
+import io.vertx.ext.web.handler.HttpException;
+import io.vertx.ext.web.handler.impl.AuthenticationHandlerImpl;
+import org.apache.cassandra.sidecar.concurrent.ExecutorPools;
+
+/**
+ * Handler for verifying user certificates for Mutual TLS authentication. {@link MutualTlsAuthenticationHandler} can be
+ * chained with other {@link io.vertx.ext.web.handler.AuthenticationHandler} implementations.
+ */
+public class MutualTlsAuthenticationHandler extends AuthenticationHandlerImpl<MutualTlsAuthentication>
+{
+    private static final Logger LOGGER = LoggerFactory.getLogger(MutualTlsAuthenticationHandler.class);
+    private final ExecutorPools executorPools;
+
+    public MutualTlsAuthenticationHandler(MutualTlsAuthentication authProvider,
+                                          ExecutorPools executorPools)
+    {
+        super(authProvider);
+        this.executorPools = executorPools;
+    }
+
+    @Override
+    public void authenticate(RoutingContext ctx, Handler<AsyncResult<User>> handler)
+    {
+        if (!ctx.request().isSSL())
+        {
+            ctx.response().setStatusCode(HttpResponseStatus.BAD_REQUEST.code()).end();
+            return;
+        }
+
+        CertificateCredentials certificateCredentials = CertificateCredentials.fromHttpRequest(ctx.request());
+        executorPools
+        .service()
+        .executeBlocking(promise -> {
+            authProvider.authenticate(certificateCredentials)
+                        .onSuccess(promise::complete)
+                        .onFailure(cause -> {
+                            if (cause instanceof CredentialValidationException)
+                            {
+                                // If credentials are invalid, reject the promise with a BAD_REQUEST error
+                                promise.fail(new HttpException(HttpResponseStatus.BAD_REQUEST.code(),
+                                                               "Error validating credentials passed"));
+                            }
+                            else
+                            {
+                                // In case of other failures, reject the promise with an UNAUTHORIZED error
+                                promise.fail(new HttpException(HttpResponseStatus.UNAUTHORIZED.code()));
+                            }
+                        });
+        })
+        .onSuccess(result -> handler.handle(Future.succeededFuture((User) result)))
+        .onFailure(cause -> handler.handle(Future.failedFuture(cause)));

Review Comment:
   The reason we hand off to executor pool, is because, we use `identityRoleCache`, during loads, since database calls are involved, it blocks thread. I can move hand off to executor pool inside `CassandraIdentityExtractor` 



-- 
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: pr-unsubscribe@cassandra.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: pr-unsubscribe@cassandra.apache.org
For additional commands, e-mail: pr-help@cassandra.apache.org


Re: [PR] CASSANDRASC-156: Add mTLS Authentication in Sidecar [cassandra-sidecar]

Posted by "sarankk (via GitHub)" <gi...@apache.org>.
sarankk commented on code in PR #145:
URL: https://github.com/apache/cassandra-sidecar/pull/145#discussion_r1837544314


##########
server/src/main/java/org/apache/cassandra/sidecar/acl/IdentityToRoleCache.java:
##########
@@ -47,11 +46,18 @@ public IdentityToRoleCache(Vertx vertx,
               systemAuthDatabaseAccessor::findRoleFromIdentity,
               systemAuthDatabaseAccessor::findAllIdentityToRoles,
               sidecarConfiguration.accessControlConfiguration().permissionCacheConfiguration());
-        this.systemAuthDatabaseAccessor = systemAuthDatabaseAccessor;
     }
 
     public boolean containsKey(String identity)
     {
-        return cache != null && get(identity) != null;
+        try
+        {
+            return cache() != null && get(identity) != null;
+        }
+        catch (IllegalStateException e)

Review Comment:
   Named it `ScehmaUnavailableException`



-- 
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: pr-unsubscribe@cassandra.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: pr-unsubscribe@cassandra.apache.org
For additional commands, e-mail: pr-help@cassandra.apache.org


Re: [PR] CASSANDRASC-156 Add mTLS Authentication in Sidecar [cassandra-sidecar]

Posted by "sarankk (via GitHub)" <gi...@apache.org>.
sarankk commented on code in PR #145:
URL: https://github.com/apache/cassandra-sidecar/pull/145#discussion_r1831593704


##########
server/src/main/java/org/apache/cassandra/sidecar/accesscontrol/AuthCacheService.java:
##########
@@ -0,0 +1,77 @@
+/*
+ * 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 org.apache.cassandra.sidecar.accesscontrol;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import com.google.common.base.Preconditions;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import org.apache.cassandra.sidecar.config.SidecarConfiguration;
+
+/**
+ * {@link AuthCacheService} allows for easier maintenance of all {@link AuthCache}.
+ */
+@Singleton
+public class AuthCacheService
+{
+    private static final Logger LOGGER = LoggerFactory.getLogger(AuthCacheService.class);
+    private final boolean enabled;
+    private final Set<AuthCache<?, ?>> caches = new HashSet<>();

Review Comment:
   We could add retrieval to `AuthCacheService` that way we dont have to provide each cache in `MainModule` 



-- 
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: pr-unsubscribe@cassandra.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: pr-unsubscribe@cassandra.apache.org
For additional commands, e-mail: pr-help@cassandra.apache.org


Re: [PR] CASSANDRASC-156 Add mTLS Authentication in Sidecar [cassandra-sidecar]

Posted by "sarankk (via GitHub)" <gi...@apache.org>.
sarankk commented on code in PR #145:
URL: https://github.com/apache/cassandra-sidecar/pull/145#discussion_r1830370265


##########
server/src/main/java/org/apache/cassandra/sidecar/accesscontrol/AuthCacheService.java:
##########
@@ -0,0 +1,77 @@
+/*
+ * 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 org.apache.cassandra.sidecar.accesscontrol;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import com.google.common.base.Preconditions;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import org.apache.cassandra.sidecar.config.SidecarConfiguration;
+
+/**
+ * {@link AuthCacheService} allows for easier maintenance of all {@link AuthCache}.
+ */
+@Singleton
+public class AuthCacheService
+{
+    private static final Logger LOGGER = LoggerFactory.getLogger(AuthCacheService.class);
+    private final boolean enabled;
+    private final Set<AuthCache<?, ?>> caches = new HashSet<>();

Review Comment:
   It is for maintaining auth caches together, we warm caches. It will be useful for invalidate too. Right now we dont have any use case for cache retrieval, hence didnt add getter methods



-- 
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: pr-unsubscribe@cassandra.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: pr-unsubscribe@cassandra.apache.org
For additional commands, e-mail: pr-help@cassandra.apache.org


Re: [PR] CASSANDRASC-156: Add mTLS Authentication in Sidecar [cassandra-sidecar]

Posted by "yifan-c (via GitHub)" <gi...@apache.org>.
yifan-c commented on code in PR #145:
URL: https://github.com/apache/cassandra-sidecar/pull/145#discussion_r1837355258


##########
server/src/main/java/org/apache/cassandra/sidecar/acl/AuthCache.java:
##########
@@ -155,10 +163,7 @@ protected void warm(int availableRetries)
         catch (Exception e)
         {
             LOGGER.warn("Unexpected error encountered during pre-warming of cache={} ", name, e);
-            vertx.setTimer(config.warmupRetryIntervalMillis(), t -> internalPool.executeBlocking(() -> {
-                warm(availableRetries - 1);
-                return null;
-            }));
+            vertx.setTimer(config.warmupRetryIntervalMillis(), t -> warmUp(availableRetries - 1));

Review Comment:
   I think you want to call `warmUpAsync` to keep the behavior. 



-- 
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: pr-unsubscribe@cassandra.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: pr-unsubscribe@cassandra.apache.org
For additional commands, e-mail: pr-help@cassandra.apache.org


Re: [PR] CASSANDRASC-156: Add mTLS Authentication in Sidecar [cassandra-sidecar]

Posted by "frankgh (via GitHub)" <gi...@apache.org>.
frankgh commented on code in PR #145:
URL: https://github.com/apache/cassandra-sidecar/pull/145#discussion_r1835174994


##########
spotbugs-exclude.xml:
##########
@@ -83,6 +83,7 @@
             <Class name="org.apache.cassandra.sidecar.routes.StreamSSTableComponentHandlerTest" />
             <Class name="org.apache.cassandra.sidecar.db.RestoreSliceDatabaseAccessor" />
             <Class name="org.apache.cassandra.sidecar.restore.StorageClient" />
+            <Class name="org.apache.cassandra.sidecar.db.SystemAuthDatabaseAccessor" />

Review Comment:
   we probably shouldn't have this here?



-- 
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: pr-unsubscribe@cassandra.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: pr-unsubscribe@cassandra.apache.org
For additional commands, e-mail: pr-help@cassandra.apache.org


Re: [PR] CASSANDRASC-156 Add mTLS Authentication in Sidecar [cassandra-sidecar]

Posted by "sarankk (via GitHub)" <gi...@apache.org>.
sarankk commented on code in PR #145:
URL: https://github.com/apache/cassandra-sidecar/pull/145#discussion_r1830406893


##########
server/src/main/java/org/apache/cassandra/sidecar/db/schema/SystemAuthSchema.java:
##########
@@ -0,0 +1,107 @@
+/*
+ * 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 org.apache.cassandra.sidecar.db.schema;
+
+import com.datastax.driver.core.KeyspaceMetadata;
+import com.datastax.driver.core.Metadata;
+import com.datastax.driver.core.PreparedStatement;
+import com.datastax.driver.core.Session;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Schema for getting information stored in system_auth keyspace.
+ */
+public class SystemAuthSchema extends TableSchema
+{
+    private static final String IDENTITY_TO_ROLE_TABLE = "identity_to_role";
+    PreparedStatement selectRoleFromIdentity;
+    PreparedStatement getAllRolesAndIdentities;
+
+    protected String keyspaceName()
+    {
+        return "system_auth";
+    }
+
+    @Override
+    protected void prepareStatements(@NotNull Session session)
+    {
+        selectRoleFromIdentity = prepare(selectRoleFromIdentity,
+                                          session,
+                                          CqlLiterals.selectRoleFromIdentity());
+
+        getAllRolesAndIdentities = prepare(getAllRolesAndIdentities,
+                                           session,
+                                           CqlLiterals.getAllRolesAndIdentities());
+    }
+
+    protected String tableName()
+    {
+        throw new UnsupportedOperationException("SystemAuthSchema supports reading information from multiple " +
+                                                "tables in system_auth keyspace");

Review Comment:
   When we add authorization, there will be one more table `role_permissions`



-- 
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: pr-unsubscribe@cassandra.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: pr-unsubscribe@cassandra.apache.org
For additional commands, e-mail: pr-help@cassandra.apache.org


Re: [PR] CASSANDRASC-156 Add mTLS Authentication in Sidecar [cassandra-sidecar]

Posted by "yifan-c (via GitHub)" <gi...@apache.org>.
yifan-c commented on code in PR #145:
URL: https://github.com/apache/cassandra-sidecar/pull/145#discussion_r1837058203


##########
server/src/main/java/org/apache/cassandra/sidecar/acl/AuthCache.java:
##########
@@ -0,0 +1,164 @@
+/*
+ * 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 org.apache.cassandra.sidecar.acl;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.github.benmanes.caffeine.cache.Caffeine;
+import com.github.benmanes.caffeine.cache.LoadingCache;
+import io.vertx.core.Vertx;
+import io.vertx.core.eventbus.EventBus;
+import org.apache.cassandra.sidecar.concurrent.ExecutorPools;
+import org.apache.cassandra.sidecar.concurrent.TaskExecutorPool;
+import org.apache.cassandra.sidecar.config.CacheConfiguration;
+import org.jetbrains.annotations.VisibleForTesting;
+
+import static org.apache.cassandra.sidecar.server.SidecarServerEvents.ON_ALL_CASSANDRA_CQL_READY;
+
+/**
+ * Caches information needed for authenticating sidecar users.
+ *
+ * @param <K> Key type
+ * @param <V> Value type
+ */
+public abstract class AuthCache<K, V>
+{
+    protected static final Logger LOGGER = LoggerFactory.getLogger(AuthCache.class);
+    protected final String name;
+    protected final Vertx vertx;
+    protected final Function<K, V> loadFunction;
+    protected final Supplier<Map<K, V>> bulkLoadFunction;
+    protected final CacheConfiguration config;
+    private final TaskExecutorPool internalPool;
+
+    // cache is null when AuthCache is disabled
+    protected volatile LoadingCache<K, V> cache;
+

Review Comment:
   Actually none of them are needed for the subclass. They are exposed already via the methods, which is the preferred in terms of encapsulation. 
   
   Can you change all the `protected` variables to `private`? 
   
   Regarding `cache`, can you create a `protected` method to access the field? 



##########
server/src/main/java/org/apache/cassandra/sidecar/acl/authentication/AuthenticationProviderFactory.java:
##########
@@ -0,0 +1,38 @@
+/*
+ * 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 org.apache.cassandra.sidecar.acl.authentication;
+
+import java.util.Map;
+
+import io.vertx.core.Vertx;
+import io.vertx.ext.web.handler.impl.AuthenticationHandlerInternal;
+import org.apache.cassandra.sidecar.config.AccessControlConfiguration;
+import org.apache.cassandra.sidecar.exceptions.ConfigurationException;
+
+public interface AuthenticationProviderFactory
+{
+    default void validate(Map<String, String> parameters) throws ConfigurationException
+    {
+        // no validation
+    }
+
+    AuthenticationHandlerInternal initialize(Vertx vertx,

Review Comment:
   1. add an explanation about why `AuthenticationHandlerInternal` instead of `AuthenticationHandler`. (vertx auth framework flaw).
   2. the name should be `create`. The method does not initialize the factory, but create auth handlers.



##########
server/src/main/java/org/apache/cassandra/sidecar/acl/authentication/AuthenticationProviderFactory.java:
##########
@@ -0,0 +1,38 @@
+/*
+ * 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 org.apache.cassandra.sidecar.acl.authentication;
+
+import java.util.Map;
+
+import io.vertx.core.Vertx;
+import io.vertx.ext.web.handler.impl.AuthenticationHandlerInternal;
+import org.apache.cassandra.sidecar.config.AccessControlConfiguration;
+import org.apache.cassandra.sidecar.exceptions.ConfigurationException;
+
+public interface AuthenticationProviderFactory

Review Comment:
   javadoc for the class and methods



##########
server/src/main/java/org/apache/cassandra/sidecar/acl/authentication/MutualTLSAuthenticationProviderFactory.java:
##########
@@ -0,0 +1,101 @@
+/*
+ * 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 org.apache.cassandra.sidecar.acl.authentication;
+
+import java.util.Map;
+
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import io.vertx.core.Vertx;
+import io.vertx.ext.auth.mtls.CertificateIdentityExtractor;
+import io.vertx.ext.auth.mtls.CertificateValidator;
+import io.vertx.ext.auth.mtls.MutualTlsAuthentication;
+import io.vertx.ext.auth.mtls.impl.MutualTlsAuthenticationImpl;
+import io.vertx.ext.auth.mtls.impl.SpiffeIdentityExtractor;
+import io.vertx.ext.web.handler.impl.AuthenticationHandlerInternal;
+import org.apache.cassandra.sidecar.acl.IdentityToRoleCache;
+import org.apache.cassandra.sidecar.config.AccessControlConfiguration;
+import org.apache.cassandra.sidecar.exceptions.ConfigurationException;
+
+@Singleton
+public class MutualTLSAuthenticationProviderFactory implements AuthenticationProviderFactory
+{
+    static final String CERTIFICATE_VALIDATOR_PARAM_KEY = "certificate_validator";
+    static final String CERTIFICATE_IDENTITY_EXTRACTOR_PARAM_KEY = "certificate_identity_extractor";
+    private final IdentityToRoleCache identityToRoleCache;

Review Comment:
   private



##########
server/src/main/java/org/apache/cassandra/sidecar/exceptions/ConfigurationException.java:
##########
@@ -0,0 +1,32 @@
+/*
+ * 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 org.apache.cassandra.sidecar.exceptions;
+
+public class ConfigurationException extends RuntimeException

Review Comment:
   Maybe `InvalidConfigurationException`? And javadoc



##########
server/src/main/java/org/apache/cassandra/sidecar/config/yaml/AuthenticatorsConfigurationImpl.java:
##########
@@ -0,0 +1,54 @@
+///*
+// * 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 org.apache.cassandra.sidecar.config.yaml;
+//
+//import com.fasterxml.jackson.annotation.JsonProperty;
+//import org.apache.cassandra.sidecar.config.AuthenticatorsConfiguration;
+//import org.apache.cassandra.sidecar.config.MutualTlsAuthenticatorConfiguration;
+//
+///**
+// * {@inheritDoc}
+// */
+//public class AuthenticatorsConfigurationImpl implements AuthenticatorsConfiguration
+//{
+//    private static final MutualTlsAuthenticatorConfiguration DEFAULT_MTLS_AUTHENTICATOR_CONFIGURATION = null;
+//
+//    @JsonProperty(value = "mtls_authenticator")
+//    protected final MutualTlsAuthenticatorConfiguration mTlsAuthenticatorConfiguration;
+//
+//    public AuthenticatorsConfigurationImpl()
+//    {
+//        this(DEFAULT_MTLS_AUTHENTICATOR_CONFIGURATION);
+//    }
+//
+//    public AuthenticatorsConfigurationImpl(MutualTlsAuthenticatorConfiguration mTlsAuthenticatorConfiguration)
+//    {
+//        this.mTlsAuthenticatorConfiguration = mTlsAuthenticatorConfiguration;
+//    }
+//
+//    /**
+//     * {@inheritDoc}
+//     */
+//    @Override
+//    @JsonProperty(value = "mtls_authenticator")
+//    public MutualTlsAuthenticatorConfiguration mTlsAuthenticatorConfiguration()
+//    {
+//        return mTlsAuthenticatorConfiguration;
+//    }
+//}

Review Comment:
   remove



##########
server/src/main/java/org/apache/cassandra/sidecar/acl/authentication/AuthenticationProviderFactory.java:
##########
@@ -0,0 +1,38 @@
+/*
+ * 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 org.apache.cassandra.sidecar.acl.authentication;
+
+import java.util.Map;
+
+import io.vertx.core.Vertx;
+import io.vertx.ext.web.handler.impl.AuthenticationHandlerInternal;
+import org.apache.cassandra.sidecar.config.AccessControlConfiguration;
+import org.apache.cassandra.sidecar.exceptions.ConfigurationException;
+
+public interface AuthenticationProviderFactory
+{
+    default void validate(Map<String, String> parameters) throws ConfigurationException

Review Comment:
   `validate` should be part of `initialize/create` method, instead of a standalone step. Since both methods take a map as input, there is no guarantee (from the perspective of the interface) that the map used for creating the handler, is the same as the map for validation. 
   I would suggest removing the `validate` method. The implementations run validation on the parameters anyway. 



##########
server/src/main/java/org/apache/cassandra/sidecar/config/ParameterizedClassConfiguration.java:
##########
@@ -0,0 +1,31 @@
+/*
+ * 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 org.apache.cassandra.sidecar.config;
+
+import java.util.Map;
+
+import org.jetbrains.annotations.Nullable;
+
+public interface ParameterizedClassConfiguration

Review Comment:
   javadoc



##########
server/src/main/java/org/apache/cassandra/sidecar/config/ParameterizedClassConfiguration.java:
##########
@@ -0,0 +1,31 @@
+/*
+ * 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 org.apache.cassandra.sidecar.config;
+
+import java.util.Map;
+
+import org.jetbrains.annotations.Nullable;
+
+public interface ParameterizedClassConfiguration
+{
+    String className();

Review Comment:
   nit: `classCanonicalName`



##########
server/src/main/java/org/apache/cassandra/sidecar/acl/AuthCache.java:
##########
@@ -0,0 +1,164 @@
+/*
+ * 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 org.apache.cassandra.sidecar.acl;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.github.benmanes.caffeine.cache.Caffeine;
+import com.github.benmanes.caffeine.cache.LoadingCache;
+import io.vertx.core.Vertx;
+import io.vertx.core.eventbus.EventBus;
+import org.apache.cassandra.sidecar.concurrent.ExecutorPools;
+import org.apache.cassandra.sidecar.concurrent.TaskExecutorPool;
+import org.apache.cassandra.sidecar.config.CacheConfiguration;
+import org.jetbrains.annotations.VisibleForTesting;
+
+import static org.apache.cassandra.sidecar.server.SidecarServerEvents.ON_ALL_CASSANDRA_CQL_READY;
+
+/**
+ * Caches information needed for authenticating sidecar users.
+ *
+ * @param <K> Key type
+ * @param <V> Value type
+ */
+public abstract class AuthCache<K, V>
+{
+    protected static final Logger LOGGER = LoggerFactory.getLogger(AuthCache.class);
+    protected final String name;
+    protected final Vertx vertx;
+    protected final Function<K, V> loadFunction;
+    protected final Supplier<Map<K, V>> bulkLoadFunction;
+    protected final CacheConfiguration config;
+    private final TaskExecutorPool internalPool;
+
+    // cache is null when AuthCache is disabled
+    protected volatile LoadingCache<K, V> cache;
+
+    protected AuthCache(String name,
+                        Vertx vertx,
+                        ExecutorPools executorPools,
+                        Function<K, V> loadFunction,
+                        Supplier<Map<K, V>> bulkLoadFunction,
+                        CacheConfiguration cacheConfiguration)
+    {
+        this.name = name;
+        this.vertx = vertx;
+        this.internalPool = executorPools.internal();
+        this.loadFunction = loadFunction;
+        this.bulkLoadFunction = bulkLoadFunction;
+        this.config = cacheConfiguration;
+
+        if (this.config.enabled())
+        {
+            this.cache = initCache();
+            configureSidecarServerEventListener();
+        }
+    }
+
+    /**
+     * Retrieves a value from the cache. Will call {@link LoadingCache#get(Object)} which will
+     * "load" the value if it's not present, thus populating the key. When the cache is disabled, data is fetched
+     * with loadFunction.
+     *
+     * @param k key
+     * @return The current value of {@code K} if cached or loaded.
+     * <p>
+     * See {@link LoadingCache#get(Object)} for possible exceptions.
+     */
+    public V get(K k)
+    {
+        if (!config.enabled())
+        {
+            return loadFunction.apply(k);
+        }
+        return cache.get(k);
+    }
+
+    /**
+     * Retrieves all cached entries. Will call {@link LoadingCache#asMap()} which does not trigger "load". When cache
+     * is disabled, data is fetched with bulkLoadFunction.
+     *
+     * @return a map of cached key-value pairs
+     */
+    public Map<K, V> getAll()
+    {
+        if (!config.enabled())
+        {
+            return bulkLoadFunction.get();
+        }
+        return Collections.unmodifiableMap(cache.asMap());
+    }
+
+    private LoadingCache<K, V> initCache()
+    {
+        return Caffeine.newBuilder()
+                       // setting refreshAfterWrite and expireAfterWrite to same value makes sure no stale
+                       // data is fetched after expire time
+                       .refreshAfterWrite(config.expireAfterAccessMillis(), TimeUnit.MILLISECONDS)
+                       .expireAfterWrite(config.expireAfterAccessMillis(), TimeUnit.MILLISECONDS)
+                       .maximumSize(config.maximumSize())
+                       .build(loadFunction::apply);
+    }
+
+    private void configureSidecarServerEventListener()
+    {
+        EventBus eventBus = vertx.eventBus();
+        eventBus.localConsumer(ON_ALL_CASSANDRA_CQL_READY.address(),
+                               message -> internalPool.executeBlocking(() -> {
+                                   warm(config.warmupRetries());
+                                   return null;
+                               }));
+    }
+
+    @VisibleForTesting
+    protected void warm(int availableRetries)
+    {
+        if (!config.enabled())
+        {
+            LOGGER.info("Cache={} not enabled, skipping pre-warming", name);
+            return;
+        }
+
+        if (availableRetries < 1)
+        {
+            LOGGER.warn("Retries exhausted, unexpected error pre-warming cache={}", name);
+            return;
+        }
+
+        try
+        {
+            cache.putAll(bulkLoadFunction.get());
+        }
+        catch (Exception e)
+        {
+            LOGGER.warn("Unexpected error encountered during pre-warming of cache={} ", name, e);
+            vertx.setTimer(config.warmupRetryIntervalMillis(), t -> internalPool.executeBlocking(() -> {
+                warm(availableRetries - 1);
+                return null;
+            }));

Review Comment:
   The async warmup can be extracted out. The same function is called in `configureSidecarServerEventListener`.
   
   ```java
       private void warmUpAsync(int availableRetries)
       {
           internalPool.runBlocking(() -> warmUp(availableRetries));
       }
   ```
   
   Then, have this in the catch block and `configureSidecarServerEventListener` respectively 
   
   ```java
   internalPool.setTimer(config.warmupRetryIntervalMillis(), t -> warmUpAsync(availableRetries - 1));
   
   eventBus.localConsumer(ON_ALL_CASSANDRA_CQL_READY.address(), message -> warmUpAsync(config.warmupRetries()));
   ```



##########
server/src/main/java/org/apache/cassandra/sidecar/acl/AuthCache.java:
##########
@@ -0,0 +1,164 @@
+/*
+ * 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 org.apache.cassandra.sidecar.acl;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.github.benmanes.caffeine.cache.Caffeine;
+import com.github.benmanes.caffeine.cache.LoadingCache;
+import io.vertx.core.Vertx;
+import io.vertx.core.eventbus.EventBus;
+import org.apache.cassandra.sidecar.concurrent.ExecutorPools;
+import org.apache.cassandra.sidecar.concurrent.TaskExecutorPool;
+import org.apache.cassandra.sidecar.config.CacheConfiguration;
+import org.jetbrains.annotations.VisibleForTesting;
+
+import static org.apache.cassandra.sidecar.server.SidecarServerEvents.ON_ALL_CASSANDRA_CQL_READY;
+
+/**
+ * Caches information needed for authenticating sidecar users.
+ *
+ * @param <K> Key type
+ * @param <V> Value type
+ */
+public abstract class AuthCache<K, V>
+{
+    protected static final Logger LOGGER = LoggerFactory.getLogger(AuthCache.class);

Review Comment:
   ```suggestion
       private static final Logger LOGGER = LoggerFactory.getLogger(AuthCache.class);
   ```
   
   Let's avoid populating the loggers in the concrete implementations. 



##########
server/src/main/java/org/apache/cassandra/sidecar/acl/authentication/MutualTLSAuthenticationProviderFactory.java:
##########
@@ -0,0 +1,101 @@
+/*
+ * 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 org.apache.cassandra.sidecar.acl.authentication;
+
+import java.util.Map;
+
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import io.vertx.core.Vertx;
+import io.vertx.ext.auth.mtls.CertificateIdentityExtractor;
+import io.vertx.ext.auth.mtls.CertificateValidator;
+import io.vertx.ext.auth.mtls.MutualTlsAuthentication;
+import io.vertx.ext.auth.mtls.impl.MutualTlsAuthenticationImpl;
+import io.vertx.ext.auth.mtls.impl.SpiffeIdentityExtractor;
+import io.vertx.ext.web.handler.impl.AuthenticationHandlerInternal;
+import org.apache.cassandra.sidecar.acl.IdentityToRoleCache;
+import org.apache.cassandra.sidecar.config.AccessControlConfiguration;
+import org.apache.cassandra.sidecar.exceptions.ConfigurationException;
+
+@Singleton
+public class MutualTLSAuthenticationProviderFactory implements AuthenticationProviderFactory
+{
+    static final String CERTIFICATE_VALIDATOR_PARAM_KEY = "certificate_validator";
+    static final String CERTIFICATE_IDENTITY_EXTRACTOR_PARAM_KEY = "certificate_identity_extractor";
+    private final IdentityToRoleCache identityToRoleCache;
+
+    @Inject
+    public MutualTLSAuthenticationProviderFactory(IdentityToRoleCache identityToRoleCache)
+    {
+        this.identityToRoleCache = identityToRoleCache;
+    }
+
+    @Override
+    public void validate(Map<String, String> parameters) throws ConfigurationException
+    {
+        if (parameters == null)
+        {
+            throw new ConfigurationException("message");
+        }
+
+        if (!parameters.containsKey(CERTIFICATE_VALIDATOR_PARAM_KEY))
+        {
+            throw new ConfigurationException("message");
+        }
+
+        if (!parameters.containsKey(CERTIFICATE_IDENTITY_EXTRACTOR_PARAM_KEY))
+        {
+            throw new ConfigurationException("message");

Review Comment:
   update "message"



##########
server/src/main/java/org/apache/cassandra/sidecar/acl/AuthCache.java:
##########
@@ -0,0 +1,164 @@
+/*
+ * 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 org.apache.cassandra.sidecar.acl;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.github.benmanes.caffeine.cache.Caffeine;
+import com.github.benmanes.caffeine.cache.LoadingCache;
+import io.vertx.core.Vertx;
+import io.vertx.core.eventbus.EventBus;
+import org.apache.cassandra.sidecar.concurrent.ExecutorPools;
+import org.apache.cassandra.sidecar.concurrent.TaskExecutorPool;
+import org.apache.cassandra.sidecar.config.CacheConfiguration;
+import org.jetbrains.annotations.VisibleForTesting;
+
+import static org.apache.cassandra.sidecar.server.SidecarServerEvents.ON_ALL_CASSANDRA_CQL_READY;
+
+/**
+ * Caches information needed for authenticating sidecar users.
+ *
+ * @param <K> Key type
+ * @param <V> Value type
+ */
+public abstract class AuthCache<K, V>
+{
+    protected static final Logger LOGGER = LoggerFactory.getLogger(AuthCache.class);
+    protected final String name;
+    protected final Vertx vertx;
+    protected final Function<K, V> loadFunction;
+    protected final Supplier<Map<K, V>> bulkLoadFunction;
+    protected final CacheConfiguration config;
+    private final TaskExecutorPool internalPool;
+
+    // cache is null when AuthCache is disabled
+    protected volatile LoadingCache<K, V> cache;
+
+    protected AuthCache(String name,
+                        Vertx vertx,
+                        ExecutorPools executorPools,
+                        Function<K, V> loadFunction,
+                        Supplier<Map<K, V>> bulkLoadFunction,
+                        CacheConfiguration cacheConfiguration)
+    {
+        this.name = name;
+        this.vertx = vertx;
+        this.internalPool = executorPools.internal();
+        this.loadFunction = loadFunction;
+        this.bulkLoadFunction = bulkLoadFunction;
+        this.config = cacheConfiguration;
+
+        if (this.config.enabled())
+        {
+            this.cache = initCache();
+            configureSidecarServerEventListener();
+        }
+    }
+
+    /**
+     * Retrieves a value from the cache. Will call {@link LoadingCache#get(Object)} which will
+     * "load" the value if it's not present, thus populating the key. When the cache is disabled, data is fetched
+     * with loadFunction.
+     *
+     * @param k key
+     * @return The current value of {@code K} if cached or loaded.
+     * <p>
+     * See {@link LoadingCache#get(Object)} for possible exceptions.
+     */
+    public V get(K k)
+    {
+        if (!config.enabled())
+        {
+            return loadFunction.apply(k);
+        }
+        return cache.get(k);
+    }
+
+    /**
+     * Retrieves all cached entries. Will call {@link LoadingCache#asMap()} which does not trigger "load". When cache
+     * is disabled, data is fetched with bulkLoadFunction.
+     *
+     * @return a map of cached key-value pairs
+     */
+    public Map<K, V> getAll()
+    {
+        if (!config.enabled())
+        {
+            return bulkLoadFunction.get();
+        }
+        return Collections.unmodifiableMap(cache.asMap());
+    }
+
+    private LoadingCache<K, V> initCache()
+    {
+        return Caffeine.newBuilder()
+                       // setting refreshAfterWrite and expireAfterWrite to same value makes sure no stale
+                       // data is fetched after expire time
+                       .refreshAfterWrite(config.expireAfterAccessMillis(), TimeUnit.MILLISECONDS)
+                       .expireAfterWrite(config.expireAfterAccessMillis(), TimeUnit.MILLISECONDS)
+                       .maximumSize(config.maximumSize())
+                       .build(loadFunction::apply);
+    }
+
+    private void configureSidecarServerEventListener()
+    {
+        EventBus eventBus = vertx.eventBus();
+        eventBus.localConsumer(ON_ALL_CASSANDRA_CQL_READY.address(),
+                               message -> internalPool.executeBlocking(() -> {
+                                   warm(config.warmupRetries());
+                                   return null;
+                               }));
+    }
+
+    @VisibleForTesting
+    protected void warm(int availableRetries)

Review Comment:
   ```suggestion
       protected void warmUp(int availableRetries)
   ```
   
   to avoid misreading as `warn`



##########
server/src/main/java/org/apache/cassandra/sidecar/config/AuthenticatorsConfiguration.java:
##########
@@ -0,0 +1,31 @@
+///*
+// * 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 org.apache.cassandra.sidecar.config;
+//
+///**
+// * Configuration used to support multiple authenticators in Sidecar.
+// */
+//public interface AuthenticatorsConfiguration
+//{
+//    /**
+//     * @return configuration needed for mTLS authentication in Sidecar. With mTLS, both server and client exchange
+//     * certificates and verify each other
+//     */
+//    MutualTlsAuthenticatorConfiguration mTlsAuthenticatorConfiguration();
+//}

Review Comment:
   remove it?



##########
server/src/main/java/org/apache/cassandra/sidecar/acl/authentication/AuthenticationProviderFactoryRegistry.java:
##########
@@ -0,0 +1,37 @@
+/*
+ * 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 org.apache.cassandra.sidecar.acl.authentication;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+public class AuthenticationProviderFactoryRegistry

Review Comment:
   javadoc



##########
server/src/main/java/org/apache/cassandra/sidecar/config/yaml/MutualTlsAuthenticatorConfigurationImpl.java:
##########
@@ -0,0 +1,70 @@
+///*
+// * 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 org.apache.cassandra.sidecar.config.yaml;
+//
+//import com.fasterxml.jackson.annotation.JsonProperty;
+//import io.vertx.ext.auth.mtls.impl.CertificateValidatorImpl;
+//import org.apache.cassandra.sidecar.acl.authentication.CassandraIdentityExtractor;
+//import org.apache.cassandra.sidecar.config.MutualTlsAuthenticatorConfiguration;
+//
+///**
+// * {@inheritDoc}
+// */
+//public class MutualTlsAuthenticatorConfigurationImpl implements MutualTlsAuthenticatorConfiguration
+//{
+//    private static final String DEFAULT_CERTIFICATE_VALIDATOR = CertificateValidatorImpl.class.getCanonicalName();
+//    private static final String DEFAULT_CERTIFICATE_IDENTITY_EXTRACTOR = CassandraIdentityExtractor.class.getCanonicalName();
+//
+//    @JsonProperty(value = "certificate_validator")
+//    protected final String certificateValidator;
+//
+//    @JsonProperty(value = "certificate_identity_extractor")
+//    protected final String identityExtractor;
+//
+//    public MutualTlsAuthenticatorConfigurationImpl()
+//    {
+//        this(DEFAULT_CERTIFICATE_VALIDATOR, DEFAULT_CERTIFICATE_IDENTITY_EXTRACTOR);
+//    }
+//
+//    public MutualTlsAuthenticatorConfigurationImpl(String certificateValidator, String identityExtractor)
+//    {
+//        this.certificateValidator = certificateValidator;
+//        this.identityExtractor = identityExtractor;
+//    }
+//
+//    /**
+//     * {@inheritDoc}
+//     */
+//    @Override
+//    @JsonProperty(value = "certificate_validator")
+//    public String certificateValidator()
+//    {
+//        return certificateValidator;
+//    }
+//
+//    /**
+//     * {@inheritDoc}
+//     */
+//    @Override
+//    @JsonProperty(value = "certificate_identity_extractor")
+//    public String certificateIdentityExtractor()
+//    {
+//        return identityExtractor;
+//    }
+//}

Review Comment:
   remove



##########
server/src/main/java/org/apache/cassandra/sidecar/config/ParameterizedClassConfiguration.java:
##########
@@ -0,0 +1,31 @@
+/*
+ * 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 org.apache.cassandra.sidecar.config;
+
+import java.util.Map;
+
+import org.jetbrains.annotations.Nullable;
+
+public interface ParameterizedClassConfiguration
+{
+    String className();
+    
+    @Nullable
+    Map<String, String> parameters();

Review Comment:
   nit: `namedParameters` for better clarity about the map. 



##########
server/src/main/java/org/apache/cassandra/sidecar/acl/authentication/CassandraIdentityExtractor.java:
##########
@@ -0,0 +1,68 @@
+/*
+ * 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 org.apache.cassandra.sidecar.acl.authentication;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+import io.vertx.ext.auth.authentication.CertificateCredentials;
+import io.vertx.ext.auth.authentication.CredentialValidationException;
+import io.vertx.ext.auth.mtls.impl.SpiffeIdentityExtractor;
+import org.apache.cassandra.sidecar.acl.IdentityToRoleCache;
+
+/**
+ * {@link CassandraIdentityExtractor} verifies {@code SPIFFE} identities extracted from certificate are mapped
+ * to a valid role in Cassandra or a pre-configured administrative identity.
+ */
+public class CassandraIdentityExtractor extends SpiffeIdentityExtractor
+{
+    protected final IdentityToRoleCache identityToRoleCache;
+    protected final Set<String> adminIdentities;

Review Comment:
   prefer private



-- 
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: pr-unsubscribe@cassandra.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: pr-unsubscribe@cassandra.apache.org
For additional commands, e-mail: pr-help@cassandra.apache.org


Re: [PR] CASSANDRASC-156 Add mTLS Authentication in Sidecar [cassandra-sidecar]

Posted by "sarankk (via GitHub)" <gi...@apache.org>.
sarankk commented on code in PR #145:
URL: https://github.com/apache/cassandra-sidecar/pull/145#discussion_r1837338170


##########
server/src/main/java/org/apache/cassandra/sidecar/acl/authentication/AuthenticationProviderFactory.java:
##########
@@ -0,0 +1,38 @@
+/*
+ * 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 org.apache.cassandra.sidecar.acl.authentication;
+
+import java.util.Map;
+
+import io.vertx.core.Vertx;
+import io.vertx.ext.web.handler.impl.AuthenticationHandlerInternal;
+import org.apache.cassandra.sidecar.config.AccessControlConfiguration;
+import org.apache.cassandra.sidecar.exceptions.ConfigurationException;
+
+public interface AuthenticationProviderFactory
+{
+    default void validate(Map<String, String> parameters) throws ConfigurationException

Review Comment:
   Makes sense, was thinking the same.



##########
server/src/main/java/org/apache/cassandra/sidecar/exceptions/ConfigurationException.java:
##########
@@ -0,0 +1,32 @@
+/*
+ * 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 org.apache.cassandra.sidecar.exceptions;
+
+public class ConfigurationException extends RuntimeException

Review Comment:
   Added Javadoc, we could also use it for missing configuration, so left the name as is.



-- 
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: pr-unsubscribe@cassandra.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: pr-unsubscribe@cassandra.apache.org
For additional commands, e-mail: pr-help@cassandra.apache.org


Re: [PR] CASSANDRASC-156: Add mTLS Authentication in Sidecar [cassandra-sidecar]

Posted by "sarankk (via GitHub)" <gi...@apache.org>.
sarankk commented on code in PR #145:
URL: https://github.com/apache/cassandra-sidecar/pull/145#discussion_r1837544314


##########
server/src/main/java/org/apache/cassandra/sidecar/acl/IdentityToRoleCache.java:
##########
@@ -47,11 +46,18 @@ public IdentityToRoleCache(Vertx vertx,
               systemAuthDatabaseAccessor::findRoleFromIdentity,
               systemAuthDatabaseAccessor::findAllIdentityToRoles,
               sidecarConfiguration.accessControlConfiguration().permissionCacheConfiguration());
-        this.systemAuthDatabaseAccessor = systemAuthDatabaseAccessor;
     }
 
     public boolean containsKey(String identity)
     {
-        return cache != null && get(identity) != null;
+        try
+        {
+            return cache() != null && get(identity) != null;
+        }
+        catch (IllegalStateException e)

Review Comment:
   Named it `SchemaUnavailableException`



-- 
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: pr-unsubscribe@cassandra.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: pr-unsubscribe@cassandra.apache.org
For additional commands, e-mail: pr-help@cassandra.apache.org


Re: [PR] CASSANDRASC-156 Add mTLS Authentication in Sidecar [cassandra-sidecar]

Posted by "sarankk (via GitHub)" <gi...@apache.org>.
sarankk commented on code in PR #145:
URL: https://github.com/apache/cassandra-sidecar/pull/145#discussion_r1830413696


##########
server/src/main/java/org/apache/cassandra/sidecar/db/schema/SystemAuthSchema.java:
##########
@@ -0,0 +1,107 @@
+/*
+ * 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 org.apache.cassandra.sidecar.db.schema;
+
+import com.datastax.driver.core.KeyspaceMetadata;
+import com.datastax.driver.core.Metadata;
+import com.datastax.driver.core.PreparedStatement;
+import com.datastax.driver.core.Session;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Schema for getting information stored in system_auth keyspace.
+ */
+public class SystemAuthSchema extends TableSchema
+{
+    private static final String IDENTITY_TO_ROLE_TABLE = "identity_to_role";
+    PreparedStatement selectRoleFromIdentity;
+    PreparedStatement getAllRolesAndIdentities;
+
+    protected String keyspaceName()
+    {
+        return "system_auth";
+    }
+
+    @Override
+    protected void prepareStatements(@NotNull Session session)
+    {
+        selectRoleFromIdentity = prepare(selectRoleFromIdentity,
+                                          session,
+                                          CqlLiterals.selectRoleFromIdentity());
+
+        getAllRolesAndIdentities = prepare(getAllRolesAndIdentities,
+                                           session,
+                                           CqlLiterals.getAllRolesAndIdentities());
+    }
+
+    protected String tableName()
+    {
+        throw new UnsupportedOperationException("SystemAuthSchema supports reading information from multiple " +
+                                                "tables in system_auth keyspace");
+    }
+
+    @Override
+    protected boolean exists(@NotNull Metadata metadata)
+    {
+        // check tables exists before preparing
+        KeyspaceMetadata keyspaceMetadata = metadata.getKeyspace(keyspaceName());
+        // identity_to_role table exists in Cassandra versions starting 5.x
+        return keyspaceMetadata != null && keyspaceMetadata.getTable(IDENTITY_TO_ROLE_TABLE) != null;
+    }
+
+    @Override
+    protected String createSchemaStatement()
+    {
+        return "";
+    }

Review Comment:
   Makes sense, I left it empty, so that no schema related statement will be executed. We can throw too, we don't call that method during initialize



-- 
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: pr-unsubscribe@cassandra.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: pr-unsubscribe@cassandra.apache.org
For additional commands, e-mail: pr-help@cassandra.apache.org


Re: [PR] CASSANDRASC-156 Add mTLS Authentication in Sidecar [cassandra-sidecar]

Posted by "yifan-c (via GitHub)" <gi...@apache.org>.
yifan-c commented on code in PR #145:
URL: https://github.com/apache/cassandra-sidecar/pull/145#discussion_r1830197515


##########
server/src/main/java/org/apache/cassandra/sidecar/accesscontrol/AuthCache.java:
##########
@@ -0,0 +1,214 @@
+/*
+ * 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 org.apache.cassandra.sidecar.accesscontrol;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+import com.google.common.util.concurrent.Uninterruptibles;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.github.benmanes.caffeine.cache.Caffeine;
+import com.github.benmanes.caffeine.cache.LoadingCache;
+
+/**
+ * {@link AuthCache} caches information needed for authenticating sidecar users.
+ *
+ * @param <K> key stored in cache for retrieving value
+ * @param <V> value cached
+ *
+ * for {@link IdentityRoleCache} key will be String representing identity extracted from certificate and value will
+ * be String representing Cassandra role associated with the identity
+ */
+public abstract class AuthCache<K, V>
+{
+    protected static final Logger LOGGER = LoggerFactory.getLogger(AuthCache.class);
+    protected final String name;
+    protected final boolean enabled;
+    protected final Function<K, V> loadFunction;
+    protected final Supplier<Map<K, V>> bulkLoadFunction;
+    protected final int warmingRetries;
+    protected final long warmingRetryIntervalInMillis;
+
+    protected long expireAfterMillis;
+    protected long maxEntries;
+
+    protected volatile LoadingCache<K, V> cache;
+
+    protected AuthCache(String name,
+                        boolean enabled,
+                        Function<K, V> loadFunction,
+                        Supplier<Map<K, V>> bulkLoadFunction,
+                        int warmingRetries,
+                        long warmingRetryIntervalInMillis,
+                        long expireAfterMillis,
+                        long maxEntries)
+    {
+        this.name = name;
+        this.enabled = enabled;
+        this.loadFunction = loadFunction;
+        this.bulkLoadFunction = bulkLoadFunction;
+        this.warmingRetries = warmingRetries;
+        this.warmingRetryIntervalInMillis = warmingRetryIntervalInMillis;
+        this.expireAfterMillis = expireAfterMillis;
+        this.maxEntries = maxEntries;
+        this.cache = initCache(null);
+    }
+
+    public void setExpireAfterMillis(long expireAfterMillis)

Review Comment:
   `@VisibleForTesting`? 



##########
server/src/main/java/org/apache/cassandra/sidecar/accesscontrol/AuthCache.java:
##########
@@ -0,0 +1,214 @@
+/*
+ * 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 org.apache.cassandra.sidecar.accesscontrol;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+import com.google.common.util.concurrent.Uninterruptibles;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.github.benmanes.caffeine.cache.Caffeine;
+import com.github.benmanes.caffeine.cache.LoadingCache;
+
+/**
+ * {@link AuthCache} caches information needed for authenticating sidecar users.
+ *
+ * @param <K> key stored in cache for retrieving value
+ * @param <V> value cached
+ *
+ * for {@link IdentityRoleCache} key will be String representing identity extracted from certificate and value will
+ * be String representing Cassandra role associated with the identity
+ */

Review Comment:
   ```suggestion
   /**
    * Caches information needed for authenticating sidecar users.
    *
    * @param <K> Key type
    * @param <V> Value type
    */
   ```
   
   I removed the paragraph about "for {@link IdentityRoleCache}" because it is specific to one implementation, and similar comment has added to the class in question. 



##########
server/src/main/java/org/apache/cassandra/sidecar/accesscontrol/AuthCache.java:
##########
@@ -0,0 +1,214 @@
+/*
+ * 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 org.apache.cassandra.sidecar.accesscontrol;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+import com.google.common.util.concurrent.Uninterruptibles;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.github.benmanes.caffeine.cache.Caffeine;
+import com.github.benmanes.caffeine.cache.LoadingCache;
+
+/**
+ * {@link AuthCache} caches information needed for authenticating sidecar users.
+ *
+ * @param <K> key stored in cache for retrieving value
+ * @param <V> value cached
+ *
+ * for {@link IdentityRoleCache} key will be String representing identity extracted from certificate and value will
+ * be String representing Cassandra role associated with the identity
+ */
+public abstract class AuthCache<K, V>
+{
+    protected static final Logger LOGGER = LoggerFactory.getLogger(AuthCache.class);
+    protected final String name;
+    protected final boolean enabled;
+    protected final Function<K, V> loadFunction;
+    protected final Supplier<Map<K, V>> bulkLoadFunction;
+    protected final int warmingRetries;
+    protected final long warmingRetryIntervalInMillis;
+
+    protected long expireAfterMillis;
+    protected long maxEntries;
+
+    protected volatile LoadingCache<K, V> cache;
+
+    protected AuthCache(String name,
+                        boolean enabled,
+                        Function<K, V> loadFunction,
+                        Supplier<Map<K, V>> bulkLoadFunction,
+                        int warmingRetries,
+                        long warmingRetryIntervalInMillis,
+                        long expireAfterMillis,
+                        long maxEntries)
+    {
+        this.name = name;
+        this.enabled = enabled;
+        this.loadFunction = loadFunction;
+        this.bulkLoadFunction = bulkLoadFunction;
+        this.warmingRetries = warmingRetries;
+        this.warmingRetryIntervalInMillis = warmingRetryIntervalInMillis;
+        this.expireAfterMillis = expireAfterMillis;
+        this.maxEntries = maxEntries;
+        this.cache = initCache(null);
+    }
+
+    public void setExpireAfterMillis(long expireAfterMillis)
+    {
+        if (enabled && this.expireAfterMillis != expireAfterMillis)
+        {
+            synchronized (this)
+            {
+                if (this.expireAfterMillis == expireAfterMillis)
+                {
+                    return;
+                }
+                this.expireAfterMillis = expireAfterMillis;
+                cache = initCache(cache);
+            }
+        }
+    }
+
+    public long getExpireAfterMillis()
+    {
+        return this.expireAfterMillis;
+    }
+
+    public void setMaxEntries(long maxEntries)
+    {
+        if (enabled && this.maxEntries != maxEntries)
+        {
+            synchronized (this)
+            {
+                if (this.maxEntries == maxEntries)
+                {
+                    return;
+                }
+                this.maxEntries = maxEntries;
+                cache = initCache(cache);
+            }
+        }
+    }
+
+    public long getMaxEntries()
+    {
+        return this.maxEntries;
+    }
+
+    /**
+     * Retrieve a value from the cache. Will call {@link LoadingCache#get(Object)} which will
+     * "load" the value if it's not present, thus populating the key.
+     * @param k key
+     * @return The current value of {@code K} if cached or loaded.
+     *
+     * See {@link LoadingCache#get(Object)} for possible exceptions.
+     */
+    public V get(K k)
+    {
+        if (cache == null)
+        {
+            return loadFunction.apply(k);
+        }
+        return cache.get(k);
+    }
+
+    /**
+     * Retrieve all cached entries. Will call {@link LoadingCache#asMap()} which does not trigger "load".
+     * @return a map of cached key-value pairs
+     */
+    public Map<K, V> getAll()
+    {
+        if (cache == null)
+        {
+            return Collections.emptyMap();
+        }
+        return Collections.unmodifiableMap(cache.asMap());
+    }
+
+    // We might need to add support for invalidating cache entries when we support removing auth entries from
+    // Cassandra auth tables through sidecar
+    public void invalidate(K k)
+    {
+        throw new UnsupportedOperationException("Invalidate functionality is not yet supported for AuthCache");
+    }
+
+    private LoadingCache<K, V> initCache(LoadingCache<K, V> existing)
+    {
+        if (!enabled)
+            return null;
+
+        LoadingCache<K, V> updatedCache;
+        if (existing == null)
+        {
+            updatedCache = Caffeine.newBuilder()
+                                   // setting refreshAfterWrite and expireAfterWrite to same value makes sure no stale
+                                   // data is fetched after expire time
+                                   .refreshAfterWrite(expireAfterMillis, TimeUnit.MILLISECONDS)
+                                   .expireAfterWrite(expireAfterMillis, TimeUnit.MILLISECONDS)
+                                   .maximumSize(getMaxEntries())
+                                   .build(loadFunction::apply);
+        }
+        else
+        {
+            updatedCache = cache;
+            // Always set as mandatory
+            cache.policy().expireAfterWrite().ifPresent(policy -> policy.setExpiresAfter(expireAfterMillis, TimeUnit.MILLISECONDS));
+            cache.policy().eviction().ifPresent(policy -> policy.setMaximum(getMaxEntries()));
+        }
+        return updatedCache;

Review Comment:
   Simplify it once the cache config cannot be updated. 



##########
server/src/main/java/org/apache/cassandra/sidecar/accesscontrol/AuthCache.java:
##########
@@ -0,0 +1,214 @@
+/*
+ * 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 org.apache.cassandra.sidecar.accesscontrol;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+import com.google.common.util.concurrent.Uninterruptibles;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.github.benmanes.caffeine.cache.Caffeine;
+import com.github.benmanes.caffeine.cache.LoadingCache;
+
+/**
+ * {@link AuthCache} caches information needed for authenticating sidecar users.
+ *
+ * @param <K> key stored in cache for retrieving value
+ * @param <V> value cached
+ *
+ * for {@link IdentityRoleCache} key will be String representing identity extracted from certificate and value will
+ * be String representing Cassandra role associated with the identity
+ */
+public abstract class AuthCache<K, V>
+{
+    protected static final Logger LOGGER = LoggerFactory.getLogger(AuthCache.class);
+    protected final String name;
+    protected final boolean enabled;
+    protected final Function<K, V> loadFunction;
+    protected final Supplier<Map<K, V>> bulkLoadFunction;
+    protected final int warmingRetries;
+    protected final long warmingRetryIntervalInMillis;
+
+    protected long expireAfterMillis;
+    protected long maxEntries;
+
+    protected volatile LoadingCache<K, V> cache;
+
+    protected AuthCache(String name,
+                        boolean enabled,
+                        Function<K, V> loadFunction,
+                        Supplier<Map<K, V>> bulkLoadFunction,
+                        int warmingRetries,
+                        long warmingRetryIntervalInMillis,
+                        long expireAfterMillis,
+                        long maxEntries)
+    {
+        this.name = name;
+        this.enabled = enabled;
+        this.loadFunction = loadFunction;
+        this.bulkLoadFunction = bulkLoadFunction;
+        this.warmingRetries = warmingRetries;
+        this.warmingRetryIntervalInMillis = warmingRetryIntervalInMillis;
+        this.expireAfterMillis = expireAfterMillis;
+        this.maxEntries = maxEntries;
+        this.cache = initCache(null);
+    }
+
+    public void setExpireAfterMillis(long expireAfterMillis)
+    {
+        if (enabled && this.expireAfterMillis != expireAfterMillis)
+        {
+            synchronized (this)
+            {
+                if (this.expireAfterMillis == expireAfterMillis)
+                {
+                    return;
+                }
+                this.expireAfterMillis = expireAfterMillis;
+                cache = initCache(cache);
+            }
+        }
+    }
+
+    public long getExpireAfterMillis()
+    {
+        return this.expireAfterMillis;
+    }
+
+    public void setMaxEntries(long maxEntries)
+    {
+        if (enabled && this.maxEntries != maxEntries)
+        {
+            synchronized (this)
+            {
+                if (this.maxEntries == maxEntries)
+                {
+                    return;
+                }
+                this.maxEntries = maxEntries;
+                cache = initCache(cache);
+            }
+        }
+    }
+
+    public long getMaxEntries()
+    {
+        return this.maxEntries;
+    }
+
+    /**
+     * Retrieve a value from the cache. Will call {@link LoadingCache#get(Object)} which will
+     * "load" the value if it's not present, thus populating the key.
+     * @param k key
+     * @return The current value of {@code K} if cached or loaded.
+     *
+     * See {@link LoadingCache#get(Object)} for possible exceptions.
+     */
+    public V get(K k)
+    {
+        if (cache == null)
+        {
+            return loadFunction.apply(k);
+        }
+        return cache.get(k);
+    }
+
+    /**
+     * Retrieve all cached entries. Will call {@link LoadingCache#asMap()} which does not trigger "load".
+     * @return a map of cached key-value pairs
+     */
+    public Map<K, V> getAll()
+    {
+        if (cache == null)
+        {
+            return Collections.emptyMap();
+        }
+        return Collections.unmodifiableMap(cache.asMap());
+    }

Review Comment:
   The implementation conflicting with `get(k)`
   
   When the cache is disabled, `get(k)` returns something, but `getAll()` returns empty map. Can you make it consistent? Either call `bulkLoadFunction` in this method or return null in `get(k)`



##########
server/src/main/java/org/apache/cassandra/sidecar/accesscontrol/IdentityRoleCache.java:
##########
@@ -0,0 +1,57 @@
+/*
+ * 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 org.apache.cassandra.sidecar.accesscontrol;
+
+import org.apache.cassandra.sidecar.config.CacheConfiguration;
+import org.apache.cassandra.sidecar.db.SystemAuthDatabaseAccessor;
+
+/**
+ * Caches entries from identity_to_role table. identity_to_role table maps valid certificate identity to Cassandra
+ * role the identity holds. identity_to_role table is created in Cassandra versions 5.x and above
+ */
+public class IdentityRoleCache extends AuthCache<String, String>
+{
+    protected static final String NAME = "identity_to_role_cache";
+    protected final SystemAuthDatabaseAccessor systemAuthDatabaseAccessor;
+
+    public IdentityRoleCache(CacheConfiguration config,
+                             SystemAuthDatabaseAccessor systemAuthDatabaseAccessor)
+    {
+        super(NAME,
+              config.enabled(),
+              systemAuthDatabaseAccessor::findRoleFromIdentity,
+              systemAuthDatabaseAccessor::findAllIdentityRoles,
+              config.warmingRetries(),
+              2000,
+              config.expireAfterAccessMillis(),
+              config.maximumSize());
+        this.systemAuthDatabaseAccessor = systemAuthDatabaseAccessor;
+    }
+
+    public boolean contains(String identity)

Review Comment:
   Plz rename to `containsKey`. The method is to check the existence of a key. 
   In the context of map-like data structure, the `contains` can also mean the value existence check. Rename to `containsKey` to be clear. 



##########
server/src/main/java/org/apache/cassandra/sidecar/accesscontrol/authentication/CassandraIdentityExtractor.java:
##########
@@ -0,0 +1,65 @@
+/*
+ * 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 org.apache.cassandra.sidecar.accesscontrol.authentication;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import io.vertx.ext.auth.authentication.CertificateCredentials;
+import io.vertx.ext.auth.authentication.CredentialValidationException;
+import io.vertx.ext.auth.mtls.impl.SpiffeIdentityExtractor;
+import org.apache.cassandra.sidecar.accesscontrol.IdentityRoleCache;
+
+/**
+ * {@link CassandraIdentityExtractor} verifies SPIFFE identities extracted from certificate are mapped to a valid
+ * role in Cassandra.
+ */
+public class CassandraIdentityExtractor extends SpiffeIdentityExtractor
+{
+    protected final IdentityRoleCache identityRoleCache;
+    protected final Set<String> adminIdentities = new HashSet<>();
+
+    public CassandraIdentityExtractor(IdentityRoleCache identityRoleCache,
+                                      Set<String> adminIdentities)
+    {
+        this.identityRoleCache = identityRoleCache;
+        this.adminIdentities.addAll(adminIdentities);
+    }
+
+    public List<String> validIdentities(CertificateCredentials certificateCredentials) throws CredentialValidationException

Review Comment:
   Add `@Override`



##########
server/src/main/java/org/apache/cassandra/sidecar/config/yaml/AuthenticatorsConfigurationImpl.java:
##########
@@ -0,0 +1,55 @@
+/*
+ * 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 org.apache.cassandra.sidecar.config.yaml;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import org.apache.cassandra.sidecar.config.AuthenticatorsConfiguration;
+import org.apache.cassandra.sidecar.config.MutualTlsAuthenticatorConfiguration;
+
+/**
+ * Encapsulates configuration needed to support multiple authenticators in Sidecar.

Review Comment:
   ```suggestion
    * {@inheritDoc}
   ```



##########
vertx-auth-mtls/src/main/java/io/vertx/ext/auth/mtls/CertificateIdentityExtractor.java:
##########
@@ -40,8 +42,8 @@ public interface CertificateIdentityExtractor
      * </ul>
      *
      * @param certificateCredentials certificate chain of user that is already verified
-     * @return {@code String} identity string extracted from certificate, uniquely represents client
+     * @return list of valid identities extracted from certificate.
      * @throws CredentialValidationException when a valid identity cannot be extracted from certificate chain.
      */
-    String validIdentity(CertificateCredentials certificateCredentials) throws CredentialValidationException;
+    List<String> validIdentities(CertificateCredentials certificateCredentials) throws CredentialValidationException;

Review Comment:
   What is the reason for the interface change?



##########
server/src/main/java/org/apache/cassandra/sidecar/db/schema/SystemAuthSchema.java:
##########
@@ -0,0 +1,107 @@
+/*
+ * 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 org.apache.cassandra.sidecar.db.schema;
+
+import com.datastax.driver.core.KeyspaceMetadata;
+import com.datastax.driver.core.Metadata;
+import com.datastax.driver.core.PreparedStatement;
+import com.datastax.driver.core.Session;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Schema for getting information stored in system_auth keyspace.
+ */
+public class SystemAuthSchema extends TableSchema
+{
+    private static final String IDENTITY_TO_ROLE_TABLE = "identity_to_role";
+    PreparedStatement selectRoleFromIdentity;
+    PreparedStatement getAllRolesAndIdentities;
+
+    protected String keyspaceName()
+    {
+        return "system_auth";
+    }
+
+    @Override
+    protected void prepareStatements(@NotNull Session session)
+    {
+        selectRoleFromIdentity = prepare(selectRoleFromIdentity,
+                                          session,
+                                          CqlLiterals.selectRoleFromIdentity());
+
+        getAllRolesAndIdentities = prepare(getAllRolesAndIdentities,
+                                           session,
+                                           CqlLiterals.getAllRolesAndIdentities());
+    }
+
+    protected String tableName()
+    {
+        throw new UnsupportedOperationException("SystemAuthSchema supports reading information from multiple " +
+                                                "tables in system_auth keyspace");
+    }
+
+    @Override
+    protected boolean exists(@NotNull Metadata metadata)

Review Comment:
   Is there integration test for the scenario when connecting to cassandra version, say 4.0, and the table does not exist. 



##########
server/src/main/dist/conf/sidecar.yaml:
##########
@@ -136,6 +136,22 @@ vertx:
 #      path: "path/to/truststore.p12"
 #      password: password
 
+access_control:
+  enabled: false
+  authenticators:
+    mtls_authenticator:
+      enabled: false
+      certificate_validator: io.vertx.ext.auth.mtls.impl.AllowAllCertificateValidator
+      certificate_identity_extractor: org.apache.cassandra.sidecar.accesscontrol.authentication.CassandraIdentityExtractor
+  admin_identities:
+#    - spiffe://authorized/admin/identities
+  permission_cache:
+    enabled: true
+    expire_after_access_millis: 300000
+    maximum_size: 10000
+    warming_retries: 5
+    warming_retry_interval_millis: 2000
+

Review Comment:
   It would be nice to add comments to describe the configurations. For example, https://github.com/apache/cassandra/blob/7feb03e75b7249a8f86023713b28471a0fb6d309/conf/cassandra.yaml#L185-L219
   The yaml file is operator-oriented. It would be nice for them to learn the feature without navigating through code. 



##########
server/src/main/java/org/apache/cassandra/sidecar/accesscontrol/AuthCache.java:
##########
@@ -0,0 +1,214 @@
+/*
+ * 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 org.apache.cassandra.sidecar.accesscontrol;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+import com.google.common.util.concurrent.Uninterruptibles;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.github.benmanes.caffeine.cache.Caffeine;
+import com.github.benmanes.caffeine.cache.LoadingCache;
+
+/**
+ * {@link AuthCache} caches information needed for authenticating sidecar users.
+ *
+ * @param <K> key stored in cache for retrieving value
+ * @param <V> value cached
+ *
+ * for {@link IdentityRoleCache} key will be String representing identity extracted from certificate and value will
+ * be String representing Cassandra role associated with the identity
+ */
+public abstract class AuthCache<K, V>
+{
+    protected static final Logger LOGGER = LoggerFactory.getLogger(AuthCache.class);
+    protected final String name;
+    protected final boolean enabled;
+    protected final Function<K, V> loadFunction;
+    protected final Supplier<Map<K, V>> bulkLoadFunction;
+    protected final int warmingRetries;
+    protected final long warmingRetryIntervalInMillis;
+
+    protected long expireAfterMillis;
+    protected long maxEntries;
+
+    protected volatile LoadingCache<K, V> cache;
+
+    protected AuthCache(String name,
+                        boolean enabled,
+                        Function<K, V> loadFunction,
+                        Supplier<Map<K, V>> bulkLoadFunction,
+                        int warmingRetries,
+                        long warmingRetryIntervalInMillis,
+                        long expireAfterMillis,
+                        long maxEntries)
+    {
+        this.name = name;
+        this.enabled = enabled;
+        this.loadFunction = loadFunction;
+        this.bulkLoadFunction = bulkLoadFunction;
+        this.warmingRetries = warmingRetries;
+        this.warmingRetryIntervalInMillis = warmingRetryIntervalInMillis;
+        this.expireAfterMillis = expireAfterMillis;
+        this.maxEntries = maxEntries;
+        this.cache = initCache(null);
+    }
+
+    public void setExpireAfterMillis(long expireAfterMillis)
+    {
+        if (enabled && this.expireAfterMillis != expireAfterMillis)
+        {
+            synchronized (this)
+            {
+                if (this.expireAfterMillis == expireAfterMillis)
+                {
+                    return;
+                }
+                this.expireAfterMillis = expireAfterMillis;
+                cache = initCache(cache);
+            }
+        }
+    }
+
+    public long getExpireAfterMillis()
+    {
+        return this.expireAfterMillis;
+    }

Review Comment:
   The method is not used. Its value should be retrieved from `CacheConfiguration`.
   Maybe remove the method.



##########
server/src/main/java/org/apache/cassandra/sidecar/accesscontrol/AuthCache.java:
##########
@@ -0,0 +1,214 @@
+/*
+ * 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 org.apache.cassandra.sidecar.accesscontrol;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+import com.google.common.util.concurrent.Uninterruptibles;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.github.benmanes.caffeine.cache.Caffeine;
+import com.github.benmanes.caffeine.cache.LoadingCache;
+
+/**
+ * {@link AuthCache} caches information needed for authenticating sidecar users.
+ *
+ * @param <K> key stored in cache for retrieving value
+ * @param <V> value cached
+ *
+ * for {@link IdentityRoleCache} key will be String representing identity extracted from certificate and value will
+ * be String representing Cassandra role associated with the identity
+ */
+public abstract class AuthCache<K, V>
+{
+    protected static final Logger LOGGER = LoggerFactory.getLogger(AuthCache.class);
+    protected final String name;
+    protected final boolean enabled;
+    protected final Function<K, V> loadFunction;
+    protected final Supplier<Map<K, V>> bulkLoadFunction;
+    protected final int warmingRetries;
+    protected final long warmingRetryIntervalInMillis;
+
+    protected long expireAfterMillis;
+    protected long maxEntries;
+
+    protected volatile LoadingCache<K, V> cache;
+
+    protected AuthCache(String name,
+                        boolean enabled,
+                        Function<K, V> loadFunction,
+                        Supplier<Map<K, V>> bulkLoadFunction,
+                        int warmingRetries,
+                        long warmingRetryIntervalInMillis,
+                        long expireAfterMillis,
+                        long maxEntries)

Review Comment:
   How about just passing cacheConfig as input? Making a more succinct version
   
   ```java
       protected AuthCache(String name,
                           Function<K, V> loadFunction,
                           Supplier<Map<K, V>> bulkLoadFunction,
                           CacheConfiguration cacheConfiguration)
   ```
   



##########
server/src/main/java/org/apache/cassandra/sidecar/accesscontrol/AuthCache.java:
##########
@@ -0,0 +1,214 @@
+/*
+ * 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 org.apache.cassandra.sidecar.accesscontrol;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+import com.google.common.util.concurrent.Uninterruptibles;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.github.benmanes.caffeine.cache.Caffeine;
+import com.github.benmanes.caffeine.cache.LoadingCache;
+
+/**
+ * {@link AuthCache} caches information needed for authenticating sidecar users.
+ *
+ * @param <K> key stored in cache for retrieving value
+ * @param <V> value cached
+ *
+ * for {@link IdentityRoleCache} key will be String representing identity extracted from certificate and value will
+ * be String representing Cassandra role associated with the identity
+ */
+public abstract class AuthCache<K, V>
+{
+    protected static final Logger LOGGER = LoggerFactory.getLogger(AuthCache.class);
+    protected final String name;
+    protected final boolean enabled;
+    protected final Function<K, V> loadFunction;
+    protected final Supplier<Map<K, V>> bulkLoadFunction;
+    protected final int warmingRetries;
+    protected final long warmingRetryIntervalInMillis;
+
+    protected long expireAfterMillis;
+    protected long maxEntries;
+
+    protected volatile LoadingCache<K, V> cache;
+
+    protected AuthCache(String name,
+                        boolean enabled,
+                        Function<K, V> loadFunction,
+                        Supplier<Map<K, V>> bulkLoadFunction,
+                        int warmingRetries,
+                        long warmingRetryIntervalInMillis,
+                        long expireAfterMillis,
+                        long maxEntries)
+    {
+        this.name = name;
+        this.enabled = enabled;
+        this.loadFunction = loadFunction;
+        this.bulkLoadFunction = bulkLoadFunction;
+        this.warmingRetries = warmingRetries;
+        this.warmingRetryIntervalInMillis = warmingRetryIntervalInMillis;
+        this.expireAfterMillis = expireAfterMillis;
+        this.maxEntries = maxEntries;
+        this.cache = initCache(null);
+    }
+
+    public void setExpireAfterMillis(long expireAfterMillis)
+    {
+        if (enabled && this.expireAfterMillis != expireAfterMillis)
+        {
+            synchronized (this)
+            {
+                if (this.expireAfterMillis == expireAfterMillis)
+                {
+                    return;
+                }
+                this.expireAfterMillis = expireAfterMillis;
+                cache = initCache(cache);
+            }
+        }
+    }
+
+    public long getExpireAfterMillis()
+    {
+        return this.expireAfterMillis;
+    }
+
+    public void setMaxEntries(long maxEntries)
+    {
+        if (enabled && this.maxEntries != maxEntries)
+        {
+            synchronized (this)
+            {
+                if (this.maxEntries == maxEntries)
+                {
+                    return;
+                }
+                this.maxEntries = maxEntries;
+                cache = initCache(cache);
+            }
+        }
+    }
+
+    public long getMaxEntries()
+    {
+        return this.maxEntries;
+    }
+
+    /**
+     * Retrieve a value from the cache. Will call {@link LoadingCache#get(Object)} which will
+     * "load" the value if it's not present, thus populating the key.
+     * @param k key
+     * @return The current value of {@code K} if cached or loaded.
+     *
+     * See {@link LoadingCache#get(Object)} for possible exceptions.
+     */
+    public V get(K k)
+    {
+        if (cache == null)

Review Comment:
   It is probably easier to understand with the condition, `if (!enabled)` 



##########
server/src/main/java/org/apache/cassandra/sidecar/accesscontrol/AuthCache.java:
##########
@@ -0,0 +1,214 @@
+/*
+ * 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 org.apache.cassandra.sidecar.accesscontrol;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+import com.google.common.util.concurrent.Uninterruptibles;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.github.benmanes.caffeine.cache.Caffeine;
+import com.github.benmanes.caffeine.cache.LoadingCache;
+
+/**
+ * {@link AuthCache} caches information needed for authenticating sidecar users.
+ *
+ * @param <K> key stored in cache for retrieving value
+ * @param <V> value cached
+ *
+ * for {@link IdentityRoleCache} key will be String representing identity extracted from certificate and value will
+ * be String representing Cassandra role associated with the identity
+ */
+public abstract class AuthCache<K, V>
+{
+    protected static final Logger LOGGER = LoggerFactory.getLogger(AuthCache.class);
+    protected final String name;
+    protected final boolean enabled;
+    protected final Function<K, V> loadFunction;
+    protected final Supplier<Map<K, V>> bulkLoadFunction;
+    protected final int warmingRetries;
+    protected final long warmingRetryIntervalInMillis;
+
+    protected long expireAfterMillis;
+    protected long maxEntries;
+
+    protected volatile LoadingCache<K, V> cache;
+
+    protected AuthCache(String name,
+                        boolean enabled,
+                        Function<K, V> loadFunction,
+                        Supplier<Map<K, V>> bulkLoadFunction,
+                        int warmingRetries,
+                        long warmingRetryIntervalInMillis,
+                        long expireAfterMillis,
+                        long maxEntries)
+    {
+        this.name = name;
+        this.enabled = enabled;
+        this.loadFunction = loadFunction;
+        this.bulkLoadFunction = bulkLoadFunction;
+        this.warmingRetries = warmingRetries;
+        this.warmingRetryIntervalInMillis = warmingRetryIntervalInMillis;
+        this.expireAfterMillis = expireAfterMillis;
+        this.maxEntries = maxEntries;
+        this.cache = initCache(null);
+    }
+
+    public void setExpireAfterMillis(long expireAfterMillis)
+    {
+        if (enabled && this.expireAfterMillis != expireAfterMillis)
+        {
+            synchronized (this)
+            {
+                if (this.expireAfterMillis == expireAfterMillis)
+                {
+                    return;
+                }
+                this.expireAfterMillis = expireAfterMillis;
+                cache = initCache(cache);
+            }
+        }
+    }
+
+    public long getExpireAfterMillis()
+    {
+        return this.expireAfterMillis;
+    }
+
+    public void setMaxEntries(long maxEntries)
+    {
+        if (enabled && this.maxEntries != maxEntries)
+        {
+            synchronized (this)
+            {
+                if (this.maxEntries == maxEntries)
+                {
+                    return;
+                }
+                this.maxEntries = maxEntries;
+                cache = initCache(cache);
+            }
+        }
+    }
+
+    public long getMaxEntries()
+    {
+        return this.maxEntries;
+    }
+
+    /**
+     * Retrieve a value from the cache. Will call {@link LoadingCache#get(Object)} which will
+     * "load" the value if it's not present, thus populating the key.

Review Comment:
   Write javadoc to explain the behavior of the method when cache is disabled. 



##########
server/src/main/java/org/apache/cassandra/sidecar/accesscontrol/AuthCache.java:
##########
@@ -0,0 +1,214 @@
+/*
+ * 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 org.apache.cassandra.sidecar.accesscontrol;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+import com.google.common.util.concurrent.Uninterruptibles;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.github.benmanes.caffeine.cache.Caffeine;
+import com.github.benmanes.caffeine.cache.LoadingCache;
+
+/**
+ * {@link AuthCache} caches information needed for authenticating sidecar users.
+ *
+ * @param <K> key stored in cache for retrieving value
+ * @param <V> value cached
+ *
+ * for {@link IdentityRoleCache} key will be String representing identity extracted from certificate and value will
+ * be String representing Cassandra role associated with the identity
+ */
+public abstract class AuthCache<K, V>
+{
+    protected static final Logger LOGGER = LoggerFactory.getLogger(AuthCache.class);
+    protected final String name;
+    protected final boolean enabled;
+    protected final Function<K, V> loadFunction;
+    protected final Supplier<Map<K, V>> bulkLoadFunction;
+    protected final int warmingRetries;
+    protected final long warmingRetryIntervalInMillis;
+
+    protected long expireAfterMillis;
+    protected long maxEntries;
+
+    protected volatile LoadingCache<K, V> cache;

Review Comment:
   add comment to explain when cache can be null. 



##########
server/src/main/java/org/apache/cassandra/sidecar/accesscontrol/AuthCache.java:
##########
@@ -0,0 +1,214 @@
+/*
+ * 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 org.apache.cassandra.sidecar.accesscontrol;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+import com.google.common.util.concurrent.Uninterruptibles;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.github.benmanes.caffeine.cache.Caffeine;
+import com.github.benmanes.caffeine.cache.LoadingCache;
+
+/**
+ * {@link AuthCache} caches information needed for authenticating sidecar users.
+ *
+ * @param <K> key stored in cache for retrieving value
+ * @param <V> value cached
+ *
+ * for {@link IdentityRoleCache} key will be String representing identity extracted from certificate and value will
+ * be String representing Cassandra role associated with the identity
+ */
+public abstract class AuthCache<K, V>
+{
+    protected static final Logger LOGGER = LoggerFactory.getLogger(AuthCache.class);
+    protected final String name;
+    protected final boolean enabled;
+    protected final Function<K, V> loadFunction;
+    protected final Supplier<Map<K, V>> bulkLoadFunction;
+    protected final int warmingRetries;
+    protected final long warmingRetryIntervalInMillis;
+
+    protected long expireAfterMillis;
+    protected long maxEntries;
+
+    protected volatile LoadingCache<K, V> cache;
+
+    protected AuthCache(String name,
+                        boolean enabled,
+                        Function<K, V> loadFunction,
+                        Supplier<Map<K, V>> bulkLoadFunction,
+                        int warmingRetries,
+                        long warmingRetryIntervalInMillis,
+                        long expireAfterMillis,
+                        long maxEntries)
+    {
+        this.name = name;
+        this.enabled = enabled;
+        this.loadFunction = loadFunction;
+        this.bulkLoadFunction = bulkLoadFunction;
+        this.warmingRetries = warmingRetries;
+        this.warmingRetryIntervalInMillis = warmingRetryIntervalInMillis;
+        this.expireAfterMillis = expireAfterMillis;
+        this.maxEntries = maxEntries;
+        this.cache = initCache(null);
+    }
+
+    public void setExpireAfterMillis(long expireAfterMillis)
+    {
+        if (enabled && this.expireAfterMillis != expireAfterMillis)
+        {
+            synchronized (this)
+            {
+                if (this.expireAfterMillis == expireAfterMillis)
+                {
+                    return;
+                }
+                this.expireAfterMillis = expireAfterMillis;
+                cache = initCache(cache);
+            }
+        }
+    }
+
+    public long getExpireAfterMillis()
+    {
+        return this.expireAfterMillis;
+    }
+
+    public void setMaxEntries(long maxEntries)
+    {
+        if (enabled && this.maxEntries != maxEntries)
+        {
+            synchronized (this)
+            {
+                if (this.maxEntries == maxEntries)
+                {
+                    return;
+                }
+                this.maxEntries = maxEntries;
+                cache = initCache(cache);
+            }
+        }
+    }
+
+    public long getMaxEntries()
+    {
+        return this.maxEntries;
+    }

Review Comment:
   Unused methods in prod code. 



##########
server/src/main/java/org/apache/cassandra/sidecar/accesscontrol/AuthCache.java:
##########
@@ -0,0 +1,214 @@
+/*
+ * 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 org.apache.cassandra.sidecar.accesscontrol;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+import com.google.common.util.concurrent.Uninterruptibles;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.github.benmanes.caffeine.cache.Caffeine;
+import com.github.benmanes.caffeine.cache.LoadingCache;
+
+/**
+ * {@link AuthCache} caches information needed for authenticating sidecar users.
+ *
+ * @param <K> key stored in cache for retrieving value
+ * @param <V> value cached
+ *
+ * for {@link IdentityRoleCache} key will be String representing identity extracted from certificate and value will
+ * be String representing Cassandra role associated with the identity
+ */
+public abstract class AuthCache<K, V>
+{
+    protected static final Logger LOGGER = LoggerFactory.getLogger(AuthCache.class);
+    protected final String name;
+    protected final boolean enabled;
+    protected final Function<K, V> loadFunction;
+    protected final Supplier<Map<K, V>> bulkLoadFunction;
+    protected final int warmingRetries;
+    protected final long warmingRetryIntervalInMillis;
+
+    protected long expireAfterMillis;
+    protected long maxEntries;
+
+    protected volatile LoadingCache<K, V> cache;
+
+    protected AuthCache(String name,
+                        boolean enabled,
+                        Function<K, V> loadFunction,
+                        Supplier<Map<K, V>> bulkLoadFunction,
+                        int warmingRetries,
+                        long warmingRetryIntervalInMillis,
+                        long expireAfterMillis,
+                        long maxEntries)
+    {
+        this.name = name;
+        this.enabled = enabled;
+        this.loadFunction = loadFunction;
+        this.bulkLoadFunction = bulkLoadFunction;
+        this.warmingRetries = warmingRetries;
+        this.warmingRetryIntervalInMillis = warmingRetryIntervalInMillis;
+        this.expireAfterMillis = expireAfterMillis;
+        this.maxEntries = maxEntries;
+        this.cache = initCache(null);
+    }
+
+    public void setExpireAfterMillis(long expireAfterMillis)
+    {
+        if (enabled && this.expireAfterMillis != expireAfterMillis)
+        {
+            synchronized (this)
+            {
+                if (this.expireAfterMillis == expireAfterMillis)
+                {
+                    return;
+                }
+                this.expireAfterMillis = expireAfterMillis;
+                cache = initCache(cache);
+            }
+        }
+    }
+
+    public long getExpireAfterMillis()
+    {
+        return this.expireAfterMillis;
+    }
+
+    public void setMaxEntries(long maxEntries)
+    {
+        if (enabled && this.maxEntries != maxEntries)
+        {
+            synchronized (this)
+            {
+                if (this.maxEntries == maxEntries)
+                {
+                    return;
+                }
+                this.maxEntries = maxEntries;
+                cache = initCache(cache);
+            }
+        }
+    }
+
+    public long getMaxEntries()
+    {
+        return this.maxEntries;
+    }
+
+    /**
+     * Retrieve a value from the cache. Will call {@link LoadingCache#get(Object)} which will
+     * "load" the value if it's not present, thus populating the key.
+     * @param k key
+     * @return The current value of {@code K} if cached or loaded.
+     *
+     * See {@link LoadingCache#get(Object)} for possible exceptions.
+     */
+    public V get(K k)
+    {
+        if (cache == null)
+        {
+            return loadFunction.apply(k);
+        }
+        return cache.get(k);
+    }
+
+    /**
+     * Retrieve all cached entries. Will call {@link LoadingCache#asMap()} which does not trigger "load".
+     * @return a map of cached key-value pairs
+     */
+    public Map<K, V> getAll()
+    {
+        if (cache == null)
+        {
+            return Collections.emptyMap();
+        }
+        return Collections.unmodifiableMap(cache.asMap());
+    }
+
+    // We might need to add support for invalidating cache entries when we support removing auth entries from
+    // Cassandra auth tables through sidecar
+    public void invalidate(K k)
+    {
+        throw new UnsupportedOperationException("Invalidate functionality is not yet supported for AuthCache");
+    }

Review Comment:
   If it is not supported, just remove the method. 
   It can always be introduced later when adding the functionality described in the comment. 



##########
server/src/main/java/org/apache/cassandra/sidecar/accesscontrol/AuthCache.java:
##########
@@ -0,0 +1,214 @@
+/*
+ * 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 org.apache.cassandra.sidecar.accesscontrol;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+import com.google.common.util.concurrent.Uninterruptibles;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.github.benmanes.caffeine.cache.Caffeine;
+import com.github.benmanes.caffeine.cache.LoadingCache;
+
+/**
+ * {@link AuthCache} caches information needed for authenticating sidecar users.
+ *
+ * @param <K> key stored in cache for retrieving value
+ * @param <V> value cached
+ *
+ * for {@link IdentityRoleCache} key will be String representing identity extracted from certificate and value will
+ * be String representing Cassandra role associated with the identity
+ */
+public abstract class AuthCache<K, V>
+{
+    protected static final Logger LOGGER = LoggerFactory.getLogger(AuthCache.class);
+    protected final String name;
+    protected final boolean enabled;
+    protected final Function<K, V> loadFunction;
+    protected final Supplier<Map<K, V>> bulkLoadFunction;
+    protected final int warmingRetries;
+    protected final long warmingRetryIntervalInMillis;
+
+    protected long expireAfterMillis;
+    protected long maxEntries;
+
+    protected volatile LoadingCache<K, V> cache;
+
+    protected AuthCache(String name,
+                        boolean enabled,
+                        Function<K, V> loadFunction,
+                        Supplier<Map<K, V>> bulkLoadFunction,
+                        int warmingRetries,
+                        long warmingRetryIntervalInMillis,
+                        long expireAfterMillis,
+                        long maxEntries)
+    {
+        this.name = name;
+        this.enabled = enabled;
+        this.loadFunction = loadFunction;
+        this.bulkLoadFunction = bulkLoadFunction;
+        this.warmingRetries = warmingRetries;
+        this.warmingRetryIntervalInMillis = warmingRetryIntervalInMillis;
+        this.expireAfterMillis = expireAfterMillis;
+        this.maxEntries = maxEntries;
+        this.cache = initCache(null);
+    }
+
+    public void setExpireAfterMillis(long expireAfterMillis)
+    {
+        if (enabled && this.expireAfterMillis != expireAfterMillis)
+        {
+            synchronized (this)
+            {
+                if (this.expireAfterMillis == expireAfterMillis)
+                {
+                    return;
+                }
+                this.expireAfterMillis = expireAfterMillis;
+                cache = initCache(cache);
+            }
+        }
+    }
+
+    public long getExpireAfterMillis()
+    {
+        return this.expireAfterMillis;
+    }
+
+    public void setMaxEntries(long maxEntries)
+    {
+        if (enabled && this.maxEntries != maxEntries)
+        {
+            synchronized (this)
+            {
+                if (this.maxEntries == maxEntries)
+                {
+                    return;
+                }
+                this.maxEntries = maxEntries;
+                cache = initCache(cache);
+            }
+        }
+    }
+
+    public long getMaxEntries()
+    {
+        return this.maxEntries;
+    }
+
+    /**
+     * Retrieve a value from the cache. Will call {@link LoadingCache#get(Object)} which will
+     * "load" the value if it's not present, thus populating the key.
+     * @param k key
+     * @return The current value of {@code K} if cached or loaded.
+     *
+     * See {@link LoadingCache#get(Object)} for possible exceptions.
+     */
+    public V get(K k)
+    {
+        if (cache == null)
+        {
+            return loadFunction.apply(k);
+        }
+        return cache.get(k);
+    }
+
+    /**
+     * Retrieve all cached entries. Will call {@link LoadingCache#asMap()} which does not trigger "load".
+     * @return a map of cached key-value pairs
+     */
+    public Map<K, V> getAll()
+    {
+        if (cache == null)
+        {
+            return Collections.emptyMap();
+        }
+        return Collections.unmodifiableMap(cache.asMap());
+    }
+
+    // We might need to add support for invalidating cache entries when we support removing auth entries from
+    // Cassandra auth tables through sidecar
+    public void invalidate(K k)
+    {
+        throw new UnsupportedOperationException("Invalidate functionality is not yet supported for AuthCache");
+    }
+
+    private LoadingCache<K, V> initCache(LoadingCache<K, V> existing)
+    {
+        if (!enabled)
+            return null;
+
+        LoadingCache<K, V> updatedCache;
+        if (existing == null)
+        {
+            updatedCache = Caffeine.newBuilder()
+                                   // setting refreshAfterWrite and expireAfterWrite to same value makes sure no stale
+                                   // data is fetched after expire time
+                                   .refreshAfterWrite(expireAfterMillis, TimeUnit.MILLISECONDS)
+                                   .expireAfterWrite(expireAfterMillis, TimeUnit.MILLISECONDS)
+                                   .maximumSize(getMaxEntries())
+                                   .build(loadFunction::apply);
+        }
+        else
+        {
+            updatedCache = cache;
+            // Always set as mandatory
+            cache.policy().expireAfterWrite().ifPresent(policy -> policy.setExpiresAfter(expireAfterMillis, TimeUnit.MILLISECONDS));
+            cache.policy().eviction().ifPresent(policy -> policy.setMaximum(getMaxEntries()));
+        }
+        return updatedCache;
+    }
+
+    public void warm()
+    {
+        if (cache == null)
+        {
+            LOGGER.warn("Cache {} not enabled, skipping pre-warming", name);
+            return;
+        }
+
+        warm(warmingRetries);
+    }
+
+    private void warm(int retry)
+    {
+        if (retry < 1)
+        {
+            LOGGER.warn("Retries exhausted, unexpected error pre warming cache {}", name);
+            return;
+        }
+        try
+        {
+            Map<K, V> entries = bulkLoadFunction.get();
+            cache.putAll(entries);
+        }
+        catch (Exception e)
+        {
+            LOGGER.warn("Unexpected error encountered during cache {} pre-warming, ", name, e);
+            Uninterruptibles.sleepUninterruptibly(warmingRetryIntervalInMillis, TimeUnit.MILLISECONDS);
+            warm(retry - 1);

Review Comment:
   avoid tail recursion. it can be easily rewrite as a while loop, which is more performant. 



##########
server/src/main/java/org/apache/cassandra/sidecar/accesscontrol/IdentityRoleCache.java:
##########
@@ -0,0 +1,57 @@
+/*
+ * 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 org.apache.cassandra.sidecar.accesscontrol;
+
+import org.apache.cassandra.sidecar.config.CacheConfiguration;
+import org.apache.cassandra.sidecar.db.SystemAuthDatabaseAccessor;
+
+/**
+ * Caches entries from identity_to_role table. identity_to_role table maps valid certificate identity to Cassandra
+ * role the identity holds. identity_to_role table is created in Cassandra versions 5.x and above
+ */
+public class IdentityRoleCache extends AuthCache<String, String>

Review Comment:
   Let's rename the class to `IdentityToRoleCache`. It is import to keep "TO" to avoid ambiguity. 



##########
server/src/main/java/org/apache/cassandra/sidecar/accesscontrol/IdentityRoleCache.java:
##########
@@ -0,0 +1,57 @@
+/*
+ * 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 org.apache.cassandra.sidecar.accesscontrol;
+
+import org.apache.cassandra.sidecar.config.CacheConfiguration;
+import org.apache.cassandra.sidecar.db.SystemAuthDatabaseAccessor;
+
+/**
+ * Caches entries from identity_to_role table. identity_to_role table maps valid certificate identity to Cassandra
+ * role the identity holds. identity_to_role table is created in Cassandra versions 5.x and above
+ */
+public class IdentityRoleCache extends AuthCache<String, String>
+{
+    protected static final String NAME = "identity_to_role_cache";
+    protected final SystemAuthDatabaseAccessor systemAuthDatabaseAccessor;
+
+    public IdentityRoleCache(CacheConfiguration config,
+                             SystemAuthDatabaseAccessor systemAuthDatabaseAccessor)
+    {
+        super(NAME,
+              config.enabled(),
+              systemAuthDatabaseAccessor::findRoleFromIdentity,
+              systemAuthDatabaseAccessor::findAllIdentityRoles,
+              config.warmingRetries(),
+              2000,
+              config.expireAfterAccessMillis(),
+              config.maximumSize());
+        this.systemAuthDatabaseAccessor = systemAuthDatabaseAccessor;
+    }
+
+    public boolean contains(String identity)
+    {
+

Review Comment:
   remove the empty line



##########
server/src/main/java/org/apache/cassandra/sidecar/accesscontrol/AuthCache.java:
##########
@@ -0,0 +1,214 @@
+/*
+ * 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 org.apache.cassandra.sidecar.accesscontrol;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+import com.google.common.util.concurrent.Uninterruptibles;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.github.benmanes.caffeine.cache.Caffeine;
+import com.github.benmanes.caffeine.cache.LoadingCache;
+
+/**
+ * {@link AuthCache} caches information needed for authenticating sidecar users.
+ *
+ * @param <K> key stored in cache for retrieving value
+ * @param <V> value cached
+ *
+ * for {@link IdentityRoleCache} key will be String representing identity extracted from certificate and value will
+ * be String representing Cassandra role associated with the identity
+ */
+public abstract class AuthCache<K, V>
+{
+    protected static final Logger LOGGER = LoggerFactory.getLogger(AuthCache.class);
+    protected final String name;
+    protected final boolean enabled;
+    protected final Function<K, V> loadFunction;
+    protected final Supplier<Map<K, V>> bulkLoadFunction;
+    protected final int warmingRetries;
+    protected final long warmingRetryIntervalInMillis;
+
+    protected long expireAfterMillis;
+    protected long maxEntries;
+
+    protected volatile LoadingCache<K, V> cache;
+
+    protected AuthCache(String name,
+                        boolean enabled,
+                        Function<K, V> loadFunction,
+                        Supplier<Map<K, V>> bulkLoadFunction,
+                        int warmingRetries,
+                        long warmingRetryIntervalInMillis,
+                        long expireAfterMillis,
+                        long maxEntries)
+    {
+        this.name = name;
+        this.enabled = enabled;
+        this.loadFunction = loadFunction;
+        this.bulkLoadFunction = bulkLoadFunction;
+        this.warmingRetries = warmingRetries;
+        this.warmingRetryIntervalInMillis = warmingRetryIntervalInMillis;
+        this.expireAfterMillis = expireAfterMillis;
+        this.maxEntries = maxEntries;
+        this.cache = initCache(null);
+    }
+
+    public void setExpireAfterMillis(long expireAfterMillis)
+    {
+        if (enabled && this.expireAfterMillis != expireAfterMillis)
+        {
+            synchronized (this)
+            {
+                if (this.expireAfterMillis == expireAfterMillis)
+                {
+                    return;
+                }
+                this.expireAfterMillis = expireAfterMillis;
+                cache = initCache(cache);
+            }
+        }
+    }
+
+    public long getExpireAfterMillis()
+    {
+        return this.expireAfterMillis;
+    }
+
+    public void setMaxEntries(long maxEntries)
+    {
+        if (enabled && this.maxEntries != maxEntries)
+        {
+            synchronized (this)
+            {
+                if (this.maxEntries == maxEntries)
+                {
+                    return;
+                }
+                this.maxEntries = maxEntries;
+                cache = initCache(cache);
+            }
+        }
+    }
+
+    public long getMaxEntries()
+    {
+        return this.maxEntries;
+    }
+
+    /**
+     * Retrieve a value from the cache. Will call {@link LoadingCache#get(Object)} which will
+     * "load" the value if it's not present, thus populating the key.
+     * @param k key
+     * @return The current value of {@code K} if cached or loaded.
+     *
+     * See {@link LoadingCache#get(Object)} for possible exceptions.
+     */
+    public V get(K k)
+    {
+        if (cache == null)
+        {
+            return loadFunction.apply(k);
+        }
+        return cache.get(k);
+    }
+
+    /**
+     * Retrieve all cached entries. Will call {@link LoadingCache#asMap()} which does not trigger "load".
+     * @return a map of cached key-value pairs
+     */
+    public Map<K, V> getAll()
+    {
+        if (cache == null)
+        {
+            return Collections.emptyMap();
+        }
+        return Collections.unmodifiableMap(cache.asMap());
+    }
+
+    // We might need to add support for invalidating cache entries when we support removing auth entries from
+    // Cassandra auth tables through sidecar
+    public void invalidate(K k)
+    {
+        throw new UnsupportedOperationException("Invalidate functionality is not yet supported for AuthCache");
+    }
+
+    private LoadingCache<K, V> initCache(LoadingCache<K, V> existing)
+    {
+        if (!enabled)
+            return null;
+
+        LoadingCache<K, V> updatedCache;
+        if (existing == null)
+        {
+            updatedCache = Caffeine.newBuilder()
+                                   // setting refreshAfterWrite and expireAfterWrite to same value makes sure no stale
+                                   // data is fetched after expire time
+                                   .refreshAfterWrite(expireAfterMillis, TimeUnit.MILLISECONDS)
+                                   .expireAfterWrite(expireAfterMillis, TimeUnit.MILLISECONDS)
+                                   .maximumSize(getMaxEntries())
+                                   .build(loadFunction::apply);
+        }
+        else
+        {
+            updatedCache = cache;
+            // Always set as mandatory
+            cache.policy().expireAfterWrite().ifPresent(policy -> policy.setExpiresAfter(expireAfterMillis, TimeUnit.MILLISECONDS));
+            cache.policy().eviction().ifPresent(policy -> policy.setMaximum(getMaxEntries()));
+        }
+        return updatedCache;
+    }
+
+    public void warm()
+    {
+        if (cache == null)
+        {
+            LOGGER.warn("Cache {} not enabled, skipping pre-warming", name);

Review Comment:
   Maybe just `info`



##########
server/src/main/java/org/apache/cassandra/sidecar/accesscontrol/AuthCacheService.java:
##########
@@ -0,0 +1,77 @@
+/*
+ * 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 org.apache.cassandra.sidecar.accesscontrol;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import com.google.common.base.Preconditions;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import org.apache.cassandra.sidecar.config.SidecarConfiguration;
+
+/**
+ * {@link AuthCacheService} allows for easier maintenance of all {@link AuthCache}.

Review Comment:
   ```suggestion
    * Manages all {@link AuthCache}.
   ```
   
   Avoid "easier" as it does not compare to anything else. Hopefully, the simple sentence in the suggestion captures what this class does. 



##########
server/src/main/java/org/apache/cassandra/sidecar/accesscontrol/IdentityRoleCache.java:
##########
@@ -0,0 +1,57 @@
+/*
+ * 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 org.apache.cassandra.sidecar.accesscontrol;
+
+import org.apache.cassandra.sidecar.config.CacheConfiguration;
+import org.apache.cassandra.sidecar.db.SystemAuthDatabaseAccessor;
+
+/**
+ * Caches entries from identity_to_role table. identity_to_role table maps valid certificate identity to Cassandra
+ * role the identity holds. identity_to_role table is created in Cassandra versions 5.x and above

Review Comment:
   ```suggestion
    * Caches entries from system_auth.identity_to_role table. The table maps valid certificate identities to Cassandra
    * roles. identity_to_role table is available since Cassandra versions 5.0
   ```



##########
server/src/main/java/org/apache/cassandra/sidecar/accesscontrol/IdentityRoleCache.java:
##########
@@ -0,0 +1,57 @@
+/*
+ * 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 org.apache.cassandra.sidecar.accesscontrol;
+
+import org.apache.cassandra.sidecar.config.CacheConfiguration;
+import org.apache.cassandra.sidecar.db.SystemAuthDatabaseAccessor;
+
+/**
+ * Caches entries from identity_to_role table. identity_to_role table maps valid certificate identity to Cassandra
+ * role the identity holds. identity_to_role table is created in Cassandra versions 5.x and above
+ */
+public class IdentityRoleCache extends AuthCache<String, String>
+{
+    protected static final String NAME = "identity_to_role_cache";
+    protected final SystemAuthDatabaseAccessor systemAuthDatabaseAccessor;
+
+    public IdentityRoleCache(CacheConfiguration config,
+                             SystemAuthDatabaseAccessor systemAuthDatabaseAccessor)
+    {
+        super(NAME,
+              config.enabled(),
+              systemAuthDatabaseAccessor::findRoleFromIdentity,
+              systemAuthDatabaseAccessor::findAllIdentityRoles,
+              config.warmingRetries(),
+              2000,
+              config.expireAfterAccessMillis(),
+              config.maximumSize());
+        this.systemAuthDatabaseAccessor = systemAuthDatabaseAccessor;
+    }
+
+    public boolean contains(String identity)
+    {
+
+        if (cache == null)
+        {
+            return false;
+        }
+        String role = get(identity);
+        return  role != null && !role.isEmpty();

Review Comment:
   Should it be an invalid state if the retrieved `role` string is empty? 



##########
server/src/main/java/org/apache/cassandra/sidecar/accesscontrol/AuthCacheService.java:
##########
@@ -0,0 +1,77 @@
+/*
+ * 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 org.apache.cassandra.sidecar.accesscontrol;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import com.google.common.base.Preconditions;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import org.apache.cassandra.sidecar.config.SidecarConfiguration;
+
+/**
+ * {@link AuthCacheService} allows for easier maintenance of all {@link AuthCache}.
+ */
+@Singleton
+public class AuthCacheService
+{
+    private static final Logger LOGGER = LoggerFactory.getLogger(AuthCacheService.class);
+    private final boolean enabled;
+    private final Set<AuthCache<?, ?>> caches = new HashSet<>();

Review Comment:
   Using `Set` here? How to retrieve a specific `AuthCache`? 
   
   hmmm... looks like `AuthCacheService` does not support retrieval, which makes the service quite useless. It does not manage anything. 
   
   How about removing the class? 



##########
server/src/main/java/org/apache/cassandra/sidecar/accesscontrol/AuthCacheService.java:
##########
@@ -0,0 +1,77 @@
+/*
+ * 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 org.apache.cassandra.sidecar.accesscontrol;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import com.google.common.base.Preconditions;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import org.apache.cassandra.sidecar.config.SidecarConfiguration;
+
+/**
+ * {@link AuthCacheService} allows for easier maintenance of all {@link AuthCache}.
+ */
+@Singleton
+public class AuthCacheService
+{
+    private static final Logger LOGGER = LoggerFactory.getLogger(AuthCacheService.class);
+    private final boolean enabled;
+    private final Set<AuthCache<?, ?>> caches = new HashSet<>();
+
+    @Inject
+    public AuthCacheService(SidecarConfiguration sidecarConfiguration)
+    {
+        this.enabled = sidecarConfiguration.accessControlConfiguration().enabled();
+    }
+
+    public synchronized void register(AuthCache<?, ?> cache)
+    {
+        if (!enabled)
+        {
+            return;
+        }
+        Preconditions.checkNotNull(cache, "AuthCache can not be null");
+        caches.add(cache);
+    }
+
+    public synchronized void warmCaches()
+    {
+        if (!enabled)
+        {
+            LOGGER.warn("Access control is disabled in sidecar, hence skipping auth cache warming");
+            return;
+        }
+
+        LOGGER.info("Initializing bulk load of {} auth caches", caches.size());
+        for (AuthCache<?, ?> cache : caches)
+        {
+            cache.warm();
+        }
+    }
+
+    public synchronized void invalidateCaches()
+    {
+        throw new UnsupportedOperationException("Currently invalidate of caches not supported");
+    }

Review Comment:
   remove the unsupported method 



##########
server/src/main/java/org/apache/cassandra/sidecar/config/CacheConfiguration.java:
##########
@@ -41,4 +41,14 @@ default boolean enabled()
     {
         return true;
     }
+
+    /**
+     * @return number of retries for cache warming
+     */
+    int warmingRetries();

Review Comment:
   How about `warmUpRetries`? 
   
   `warming` looks too similar to `warning`



##########
server/src/main/java/org/apache/cassandra/sidecar/accesscontrol/authentication/MutualTlsAuthenticationHandler.java:
##########
@@ -0,0 +1,85 @@
+/*
+ * 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 org.apache.cassandra.sidecar.accesscontrol.authentication;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import io.netty.handler.codec.http.HttpResponseStatus;
+import io.vertx.core.AsyncResult;
+import io.vertx.core.Future;
+import io.vertx.core.Handler;
+import io.vertx.ext.auth.User;
+import io.vertx.ext.auth.authentication.CertificateCredentials;
+import io.vertx.ext.auth.authentication.CredentialValidationException;
+import io.vertx.ext.auth.mtls.MutualTlsAuthentication;
+import io.vertx.ext.web.RoutingContext;
+import io.vertx.ext.web.handler.HttpException;
+import io.vertx.ext.web.handler.impl.AuthenticationHandlerImpl;
+import org.apache.cassandra.sidecar.concurrent.ExecutorPools;
+
+/**
+ * Handler for verifying user certificates for Mutual TLS authentication. {@link MutualTlsAuthenticationHandler} can be
+ * chained with other {@link io.vertx.ext.web.handler.AuthenticationHandler} implementations.
+ */
+public class MutualTlsAuthenticationHandler extends AuthenticationHandlerImpl<MutualTlsAuthentication>
+{
+    private static final Logger LOGGER = LoggerFactory.getLogger(MutualTlsAuthenticationHandler.class);

Review Comment:
   `LOGGER` not used



##########
server/src/main/java/org/apache/cassandra/sidecar/db/schema/SystemAuthSchema.java:
##########
@@ -0,0 +1,107 @@
+/*
+ * 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 org.apache.cassandra.sidecar.db.schema;
+
+import com.datastax.driver.core.KeyspaceMetadata;
+import com.datastax.driver.core.Metadata;
+import com.datastax.driver.core.PreparedStatement;
+import com.datastax.driver.core.Session;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Schema for getting information stored in system_auth keyspace.
+ */
+public class SystemAuthSchema extends TableSchema
+{
+    private static final String IDENTITY_TO_ROLE_TABLE = "identity_to_role";
+    PreparedStatement selectRoleFromIdentity;
+    PreparedStatement getAllRolesAndIdentities;

Review Comment:
   `private final`?



##########
server/src/main/java/org/apache/cassandra/sidecar/db/schema/SystemAuthSchema.java:
##########
@@ -0,0 +1,107 @@
+/*
+ * 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 org.apache.cassandra.sidecar.db.schema;
+
+import com.datastax.driver.core.KeyspaceMetadata;
+import com.datastax.driver.core.Metadata;
+import com.datastax.driver.core.PreparedStatement;
+import com.datastax.driver.core.Session;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Schema for getting information stored in system_auth keyspace.
+ */
+public class SystemAuthSchema extends TableSchema

Review Comment:
   Can you use `org.apache.cassandra.sidecar.adapters.base.db.schema.ConnectedClientsSchema` as an example? It is similar to the use case here.  



##########
server/src/main/java/org/apache/cassandra/sidecar/db/SystemAuthDatabaseAccessor.java:
##########
@@ -0,0 +1,89 @@
+/*
+ * 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 org.apache.cassandra.sidecar.db;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.datastax.driver.core.BoundStatement;
+import com.datastax.driver.core.ResultSet;
+import com.datastax.driver.core.Row;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import org.apache.cassandra.sidecar.common.server.CQLSessionProvider;
+import org.apache.cassandra.sidecar.db.schema.SystemAuthSchema;
+
+/**
+ * Database Accessor that queries cassandra to get information maintained under system_auth keyspace.
+ */
+@Singleton
+public class SystemAuthDatabaseAccessor extends DatabaseAccessor<SystemAuthSchema>
+{
+    @Inject
+    public SystemAuthDatabaseAccessor(SystemAuthSchema systemAuthSchema,
+                                      CQLSessionProvider sessionProvider)
+    {
+        super(systemAuthSchema, sessionProvider);
+    }
+
+    /**
+     * Queries Cassandra for the role associated with given identity.
+     *
+     * @param identity  Identity of user extracted
+     * @return the role associated with the given identity in Cassandra
+     */
+    public String findRoleFromIdentity(String identity)
+    {
+        if (!tableSchemaPrepared())
+        {
+            throw new IllegalStateException("SystemAuthSchema was not prepared, values can not be retrieved from table");
+        }
+        BoundStatement statement = tableSchema.selectRoleFromIdentity()
+                                              .bind(identity);
+        ResultSet result = execute(statement);
+        return result.one().getString("role");
+    }
+
+    /**
+     * Queries Cassandra for all rows in identity_to_role table
+     *
+     * @return - {@code List<Row>} containing each row in the identity to roles table
+     */
+    public Map<String, String> findAllIdentityRoles()

Review Comment:
   ```suggestion
       public Map<String, String> findAllIdentitiesToRoles()
   ```
   Adds the word "to" back. And use plural form consistently for both words. 



##########
server/src/main/java/org/apache/cassandra/sidecar/config/AccessControlConfiguration.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 org.apache.cassandra.sidecar.config;
+
+import java.util.Set;
+
+/**
+ * Configuration stored for controlling user access to Sidecar.
+ */
+public interface AccessControlConfiguration
+{
+    /**
+     * @return whether access control is enabled, if yes requests need to be authenticated/authorized before allowed

Review Comment:
   `authenticated/authorized` ==> `authenticated and authorized`



##########
server/src/main/java/org/apache/cassandra/sidecar/accesscontrol/authentication/MutualTlsAuthenticationHandler.java:
##########
@@ -0,0 +1,85 @@
+/*
+ * 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 org.apache.cassandra.sidecar.accesscontrol.authentication;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import io.netty.handler.codec.http.HttpResponseStatus;
+import io.vertx.core.AsyncResult;
+import io.vertx.core.Future;
+import io.vertx.core.Handler;
+import io.vertx.ext.auth.User;
+import io.vertx.ext.auth.authentication.CertificateCredentials;
+import io.vertx.ext.auth.authentication.CredentialValidationException;
+import io.vertx.ext.auth.mtls.MutualTlsAuthentication;
+import io.vertx.ext.web.RoutingContext;
+import io.vertx.ext.web.handler.HttpException;
+import io.vertx.ext.web.handler.impl.AuthenticationHandlerImpl;
+import org.apache.cassandra.sidecar.concurrent.ExecutorPools;
+
+/**
+ * Handler for verifying user certificates for Mutual TLS authentication. {@link MutualTlsAuthenticationHandler} can be
+ * chained with other {@link io.vertx.ext.web.handler.AuthenticationHandler} implementations.
+ */
+public class MutualTlsAuthenticationHandler extends AuthenticationHandlerImpl<MutualTlsAuthentication>
+{
+    private static final Logger LOGGER = LoggerFactory.getLogger(MutualTlsAuthenticationHandler.class);
+    private final ExecutorPools executorPools;
+
+    public MutualTlsAuthenticationHandler(MutualTlsAuthentication authProvider,
+                                          ExecutorPools executorPools)
+    {
+        super(authProvider);
+        this.executorPools = executorPools;
+    }
+
+    @Override
+    public void authenticate(RoutingContext ctx, Handler<AsyncResult<User>> handler)
+    {
+        if (!ctx.request().isSSL())
+        {
+            ctx.response().setStatusCode(HttpResponseStatus.BAD_REQUEST.code()).end();
+            return;
+        }
+
+        CertificateCredentials certificateCredentials = CertificateCredentials.fromHttpRequest(ctx.request());
+        executorPools
+        .service()
+        .executeBlocking(promise -> {
+            authProvider.authenticate(certificateCredentials)
+                        .onSuccess(promise::complete)
+                        .onFailure(cause -> {
+                            if (cause instanceof CredentialValidationException)
+                            {
+                                // If credentials are invalid, reject the promise with a BAD_REQUEST error
+                                promise.fail(new HttpException(HttpResponseStatus.BAD_REQUEST.code(),
+                                                               "Error validating credentials passed"));
+                            }
+                            else
+                            {
+                                // In case of other failures, reject the promise with an UNAUTHORIZED error
+                                promise.fail(new HttpException(HttpResponseStatus.UNAUTHORIZED.code()));
+                            }
+                        });
+        })
+        .onSuccess(result -> handler.handle(Future.succeededFuture((User) result)))
+        .onFailure(cause -> handler.handle(Future.failedFuture(cause)));

Review Comment:
   ```suggestion
           authProvider.authenticate(certificateCredentials)
                       .onSuccess(result -> handler.handle(Future.succeededFuture(result)))
                       .onFailure(cause -> ctx.fail(new HttpException(HttpResponseStatus.UNAUTHORIZED.code(), cause)));
   ```
   
   1. `authProvider.authenticate` is already async. No need to dispatch to another executor pool again. 
   2. All exceptions should be mapped to `UNAUTHORIZED`, IMO. Invalid cert is not a bad request. 
   3. Do not call the next handler when auth failed. Simply fail the request instead. 



##########
server/src/main/java/org/apache/cassandra/sidecar/config/yaml/MutualTlsAuthenticatorConfigurationImpl.java:
##########
@@ -0,0 +1,86 @@
+/*
+ * 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 org.apache.cassandra.sidecar.config.yaml;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import io.vertx.ext.auth.mtls.impl.CertificateValidatorImpl;
+import io.vertx.ext.auth.mtls.impl.SpiffeIdentityExtractor;
+import org.apache.cassandra.sidecar.config.MutualTlsAuthenticatorConfiguration;
+
+/**
+ * Encapsulates configuration needed for creating {@link io.vertx.ext.auth.mtls.impl.MutualTlsAuthenticationImpl}
+ * authentication provider.

Review Comment:
   ```suggestion
    * {@inheritDoc}
   ```



##########
server/src/main/java/org/apache/cassandra/sidecar/config/yaml/AccessControlConfigurationImpl.java:
##########
@@ -0,0 +1,107 @@
+/*
+ * 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 org.apache.cassandra.sidecar.config.yaml;
+
+import java.util.Collections;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import org.apache.cassandra.sidecar.config.AccessControlConfiguration;
+import org.apache.cassandra.sidecar.config.AuthenticatorsConfiguration;
+import org.apache.cassandra.sidecar.config.CacheConfiguration;
+
+/**
+ * Encapsulates configuration needed for creating authenticators in Sidecar.

Review Comment:
   ```suggestion
    * {@inheritDoc}
   ```
   
   It is clearer to inherit the doc from interface, rather than writing a new version of it.



##########
server/src/main/java/org/apache/cassandra/sidecar/db/schema/SystemAuthSchema.java:
##########
@@ -0,0 +1,107 @@
+/*
+ * 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 org.apache.cassandra.sidecar.db.schema;
+
+import com.datastax.driver.core.KeyspaceMetadata;
+import com.datastax.driver.core.Metadata;
+import com.datastax.driver.core.PreparedStatement;
+import com.datastax.driver.core.Session;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Schema for getting information stored in system_auth keyspace.
+ */
+public class SystemAuthSchema extends TableSchema
+{
+    private static final String IDENTITY_TO_ROLE_TABLE = "identity_to_role";
+    PreparedStatement selectRoleFromIdentity;
+    PreparedStatement getAllRolesAndIdentities;
+
+    protected String keyspaceName()
+    {
+        return "system_auth";
+    }
+
+    @Override
+    protected void prepareStatements(@NotNull Session session)
+    {
+        selectRoleFromIdentity = prepare(selectRoleFromIdentity,
+                                          session,
+                                          CqlLiterals.selectRoleFromIdentity());
+
+        getAllRolesAndIdentities = prepare(getAllRolesAndIdentities,
+                                           session,
+                                           CqlLiterals.getAllRolesAndIdentities());
+    }
+
+    protected String tableName()
+    {
+        throw new UnsupportedOperationException("SystemAuthSchema supports reading information from multiple " +
+                                                "tables in system_auth keyspace");

Review Comment:
   There is only one table declared in the class. no?



##########
server/src/main/java/org/apache/cassandra/sidecar/db/schema/SystemAuthSchema.java:
##########
@@ -0,0 +1,107 @@
+/*
+ * 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 org.apache.cassandra.sidecar.db.schema;
+
+import com.datastax.driver.core.KeyspaceMetadata;
+import com.datastax.driver.core.Metadata;
+import com.datastax.driver.core.PreparedStatement;
+import com.datastax.driver.core.Session;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Schema for getting information stored in system_auth keyspace.
+ */
+public class SystemAuthSchema extends TableSchema
+{
+    private static final String IDENTITY_TO_ROLE_TABLE = "identity_to_role";
+    PreparedStatement selectRoleFromIdentity;
+    PreparedStatement getAllRolesAndIdentities;
+
+    protected String keyspaceName()
+    {
+        return "system_auth";
+    }
+
+    @Override
+    protected void prepareStatements(@NotNull Session session)
+    {
+        selectRoleFromIdentity = prepare(selectRoleFromIdentity,
+                                          session,
+                                          CqlLiterals.selectRoleFromIdentity());
+
+        getAllRolesAndIdentities = prepare(getAllRolesAndIdentities,
+                                           session,
+                                           CqlLiterals.getAllRolesAndIdentities());
+    }
+
+    protected String tableName()
+    {
+        throw new UnsupportedOperationException("SystemAuthSchema supports reading information from multiple " +
+                                                "tables in system_auth keyspace");
+    }
+
+    @Override
+    protected boolean exists(@NotNull Metadata metadata)
+    {
+        // check tables exists before preparing
+        KeyspaceMetadata keyspaceMetadata = metadata.getKeyspace(keyspaceName());
+        // identity_to_role table exists in Cassandra versions starting 5.x
+        return keyspaceMetadata != null && keyspaceMetadata.getTable(IDENTITY_TO_ROLE_TABLE) != null;
+    }
+
+    @Override
+    protected String createSchemaStatement()
+    {
+        return "";
+    }

Review Comment:
   It should never attempt to create system table. 



##########
server/src/main/java/org/apache/cassandra/sidecar/db/schema/SystemAuthSchema.java:
##########
@@ -0,0 +1,107 @@
+/*
+ * 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 org.apache.cassandra.sidecar.db.schema;
+
+import com.datastax.driver.core.KeyspaceMetadata;
+import com.datastax.driver.core.Metadata;
+import com.datastax.driver.core.PreparedStatement;
+import com.datastax.driver.core.Session;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Schema for getting information stored in system_auth keyspace.
+ */
+public class SystemAuthSchema extends TableSchema
+{
+    private static final String IDENTITY_TO_ROLE_TABLE = "identity_to_role";
+    PreparedStatement selectRoleFromIdentity;
+    PreparedStatement getAllRolesAndIdentities;
+
+    protected String keyspaceName()
+    {
+        return "system_auth";
+    }
+
+    @Override
+    protected void prepareStatements(@NotNull Session session)
+    {
+        selectRoleFromIdentity = prepare(selectRoleFromIdentity,
+                                          session,
+                                          CqlLiterals.selectRoleFromIdentity());
+
+        getAllRolesAndIdentities = prepare(getAllRolesAndIdentities,
+                                           session,
+                                           CqlLiterals.getAllRolesAndIdentities());
+    }
+
+    protected String tableName()
+    {
+        throw new UnsupportedOperationException("SystemAuthSchema supports reading information from multiple " +
+                                                "tables in system_auth keyspace");
+    }
+
+    @Override
+    protected boolean exists(@NotNull Metadata metadata)
+    {
+        // check tables exists before preparing
+        KeyspaceMetadata keyspaceMetadata = metadata.getKeyspace(keyspaceName());
+        // identity_to_role table exists in Cassandra versions starting 5.x
+        return keyspaceMetadata != null && keyspaceMetadata.getTable(IDENTITY_TO_ROLE_TABLE) != null;

Review Comment:
   note that if the method returns false, it will try to create the table, which is not desirable. 



-- 
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: pr-unsubscribe@cassandra.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: pr-unsubscribe@cassandra.apache.org
For additional commands, e-mail: pr-help@cassandra.apache.org


Re: [PR] CASSANDRASC-156 Add mTLS Authentication in Sidecar [cassandra-sidecar]

Posted by "sarankk (via GitHub)" <gi...@apache.org>.
sarankk commented on code in PR #145:
URL: https://github.com/apache/cassandra-sidecar/pull/145#discussion_r1830372066


##########
server/src/main/java/org/apache/cassandra/sidecar/accesscontrol/authentication/MutualTlsAuthenticationHandler.java:
##########
@@ -0,0 +1,85 @@
+/*
+ * 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 org.apache.cassandra.sidecar.accesscontrol.authentication;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import io.netty.handler.codec.http.HttpResponseStatus;
+import io.vertx.core.AsyncResult;
+import io.vertx.core.Future;
+import io.vertx.core.Handler;
+import io.vertx.ext.auth.User;
+import io.vertx.ext.auth.authentication.CertificateCredentials;
+import io.vertx.ext.auth.authentication.CredentialValidationException;
+import io.vertx.ext.auth.mtls.MutualTlsAuthentication;
+import io.vertx.ext.web.RoutingContext;
+import io.vertx.ext.web.handler.HttpException;
+import io.vertx.ext.web.handler.impl.AuthenticationHandlerImpl;
+import org.apache.cassandra.sidecar.concurrent.ExecutorPools;
+
+/**
+ * Handler for verifying user certificates for Mutual TLS authentication. {@link MutualTlsAuthenticationHandler} can be
+ * chained with other {@link io.vertx.ext.web.handler.AuthenticationHandler} implementations.
+ */
+public class MutualTlsAuthenticationHandler extends AuthenticationHandlerImpl<MutualTlsAuthentication>
+{
+    private static final Logger LOGGER = LoggerFactory.getLogger(MutualTlsAuthenticationHandler.class);
+    private final ExecutorPools executorPools;
+
+    public MutualTlsAuthenticationHandler(MutualTlsAuthentication authProvider,
+                                          ExecutorPools executorPools)
+    {
+        super(authProvider);
+        this.executorPools = executorPools;
+    }
+
+    @Override
+    public void authenticate(RoutingContext ctx, Handler<AsyncResult<User>> handler)
+    {
+        if (!ctx.request().isSSL())
+        {
+            ctx.response().setStatusCode(HttpResponseStatus.BAD_REQUEST.code()).end();
+            return;
+        }
+
+        CertificateCredentials certificateCredentials = CertificateCredentials.fromHttpRequest(ctx.request());
+        executorPools
+        .service()
+        .executeBlocking(promise -> {
+            authProvider.authenticate(certificateCredentials)
+                        .onSuccess(promise::complete)
+                        .onFailure(cause -> {
+                            if (cause instanceof CredentialValidationException)
+                            {
+                                // If credentials are invalid, reject the promise with a BAD_REQUEST error
+                                promise.fail(new HttpException(HttpResponseStatus.BAD_REQUEST.code(),
+                                                               "Error validating credentials passed"));
+                            }
+                            else
+                            {
+                                // In case of other failures, reject the promise with an UNAUTHORIZED error
+                                promise.fail(new HttpException(HttpResponseStatus.UNAUTHORIZED.code()));
+                            }
+                        });
+        })
+        .onSuccess(result -> handler.handle(Future.succeededFuture((User) result)))
+        .onFailure(cause -> handler.handle(Future.failedFuture(cause)));

Review Comment:
   The reason we hand off to executor pool, is because, we use `identityRoleCache`, during loads, since database calls are involved, it blocks thread. I can move hand off to executor pool inside `IdentityRoleCache`  



-- 
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: pr-unsubscribe@cassandra.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: pr-unsubscribe@cassandra.apache.org
For additional commands, e-mail: pr-help@cassandra.apache.org


Re: [PR] CASSANDRASC-156 Add mTLS Authentication in Sidecar [cassandra-sidecar]

Posted by "sarankk (via GitHub)" <gi...@apache.org>.
sarankk commented on code in PR #145:
URL: https://github.com/apache/cassandra-sidecar/pull/145#discussion_r1837324786


##########
server/src/main/java/org/apache/cassandra/sidecar/acl/AuthCache.java:
##########
@@ -0,0 +1,164 @@
+/*
+ * 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 org.apache.cassandra.sidecar.acl;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.github.benmanes.caffeine.cache.Caffeine;
+import com.github.benmanes.caffeine.cache.LoadingCache;
+import io.vertx.core.Vertx;
+import io.vertx.core.eventbus.EventBus;
+import org.apache.cassandra.sidecar.concurrent.ExecutorPools;
+import org.apache.cassandra.sidecar.concurrent.TaskExecutorPool;
+import org.apache.cassandra.sidecar.config.CacheConfiguration;
+import org.jetbrains.annotations.VisibleForTesting;
+
+import static org.apache.cassandra.sidecar.server.SidecarServerEvents.ON_ALL_CASSANDRA_CQL_READY;
+
+/**
+ * Caches information needed for authenticating sidecar users.
+ *
+ * @param <K> Key type
+ * @param <V> Value type
+ */
+public abstract class AuthCache<K, V>
+{
+    protected static final Logger LOGGER = LoggerFactory.getLogger(AuthCache.class);
+    protected final String name;
+    protected final Vertx vertx;
+    protected final Function<K, V> loadFunction;
+    protected final Supplier<Map<K, V>> bulkLoadFunction;
+    protected final CacheConfiguration config;
+    private final TaskExecutorPool internalPool;
+
+    // cache is null when AuthCache is disabled
+    protected volatile LoadingCache<K, V> cache;
+

Review Comment:
   For `Vertx` might be better to keep them protected, wdyt?
   



-- 
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: pr-unsubscribe@cassandra.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: pr-unsubscribe@cassandra.apache.org
For additional commands, e-mail: pr-help@cassandra.apache.org


Re: [PR] CASSANDRASC-156 Add mTLS Authentication in Sidecar [cassandra-sidecar]

Posted by "sarankk (via GitHub)" <gi...@apache.org>.
sarankk commented on code in PR #145:
URL: https://github.com/apache/cassandra-sidecar/pull/145#discussion_r1833454961


##########
server/src/test/integration/org/apache/cassandra/sidecar/testing/IntegrationTestModule.java:
##########
@@ -47,13 +58,26 @@
  */
 public class IntegrationTestModule extends AbstractModule
 {
+    public static final String ADMIN_IDENTITY = "spiffe://cassandra/sidecar/admin";

Review Comment:
   It is declared once in `IntegrationTestModule` and once in `MutualTlsAuthenticationHandlerTest`. Since both are different packages, integration test and unit test, there is no repetition. 



-- 
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: pr-unsubscribe@cassandra.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: pr-unsubscribe@cassandra.apache.org
For additional commands, e-mail: pr-help@cassandra.apache.org


Re: [PR] CASSANDRASC-156 Add mTLS Authentication in Sidecar [cassandra-sidecar]

Posted by "sarankk (via GitHub)" <gi...@apache.org>.
sarankk commented on code in PR #145:
URL: https://github.com/apache/cassandra-sidecar/pull/145#discussion_r1837324786


##########
server/src/main/java/org/apache/cassandra/sidecar/acl/AuthCache.java:
##########
@@ -0,0 +1,164 @@
+/*
+ * 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 org.apache.cassandra.sidecar.acl;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.github.benmanes.caffeine.cache.Caffeine;
+import com.github.benmanes.caffeine.cache.LoadingCache;
+import io.vertx.core.Vertx;
+import io.vertx.core.eventbus.EventBus;
+import org.apache.cassandra.sidecar.concurrent.ExecutorPools;
+import org.apache.cassandra.sidecar.concurrent.TaskExecutorPool;
+import org.apache.cassandra.sidecar.config.CacheConfiguration;
+import org.jetbrains.annotations.VisibleForTesting;
+
+import static org.apache.cassandra.sidecar.server.SidecarServerEvents.ON_ALL_CASSANDRA_CQL_READY;
+
+/**
+ * Caches information needed for authenticating sidecar users.
+ *
+ * @param <K> Key type
+ * @param <V> Value type
+ */
+public abstract class AuthCache<K, V>
+{
+    protected static final Logger LOGGER = LoggerFactory.getLogger(AuthCache.class);
+    protected final String name;
+    protected final Vertx vertx;
+    protected final Function<K, V> loadFunction;
+    protected final Supplier<Map<K, V>> bulkLoadFunction;
+    protected final CacheConfiguration config;
+    private final TaskExecutorPool internalPool;
+
+    // cache is null when AuthCache is disabled
+    protected volatile LoadingCache<K, V> cache;
+

Review Comment:
   Changed everything except `Vertx` incase we use it in AuthCache implementations
   



-- 
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: pr-unsubscribe@cassandra.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: pr-unsubscribe@cassandra.apache.org
For additional commands, e-mail: pr-help@cassandra.apache.org


Re: [PR] CASSANDRASC-156 Add mTLS Authentication in Sidecar [cassandra-sidecar]

Posted by "sarankk (via GitHub)" <gi...@apache.org>.
sarankk commented on code in PR #145:
URL: https://github.com/apache/cassandra-sidecar/pull/145#discussion_r1831549306


##########
server/src/main/java/org/apache/cassandra/sidecar/db/schema/SystemAuthSchema.java:
##########
@@ -0,0 +1,107 @@
+/*
+ * 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 org.apache.cassandra.sidecar.db.schema;
+
+import com.datastax.driver.core.KeyspaceMetadata;
+import com.datastax.driver.core.Metadata;
+import com.datastax.driver.core.PreparedStatement;
+import com.datastax.driver.core.Session;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Schema for getting information stored in system_auth keyspace.
+ */
+public class SystemAuthSchema extends TableSchema
+{
+    private static final String IDENTITY_TO_ROLE_TABLE = "identity_to_role";
+    PreparedStatement selectRoleFromIdentity;
+    PreparedStatement getAllRolesAndIdentities;
+
+    protected String keyspaceName()
+    {
+        return "system_auth";
+    }
+
+    @Override
+    protected void prepareStatements(@NotNull Session session)
+    {
+        selectRoleFromIdentity = prepare(selectRoleFromIdentity,
+                                          session,
+                                          CqlLiterals.selectRoleFromIdentity());
+
+        getAllRolesAndIdentities = prepare(getAllRolesAndIdentities,
+                                           session,
+                                           CqlLiterals.getAllRolesAndIdentities());
+    }
+
+    protected String tableName()
+    {
+        throw new UnsupportedOperationException("SystemAuthSchema supports reading information from multiple " +
+                                                "tables in system_auth keyspace");
+    }
+
+    @Override
+    protected boolean exists(@NotNull Metadata metadata)

Review Comment:
   test ` testAssociatingRoleWithNonAdminIdentity` in `MutualTlsAuthenticationIntegrationTest` tests this. in 4.x cluster we expect a failure if the identity is not an admin identity (when table does not exists, it allows only admin identities). In 5.x cluster, if the identity is part of the table and not an admin identity we allow it through. 



-- 
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: pr-unsubscribe@cassandra.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: pr-unsubscribe@cassandra.apache.org
For additional commands, e-mail: pr-help@cassandra.apache.org


Re: [PR] CASSANDRASC-156 Add mTLS Authentication in Sidecar [cassandra-sidecar]

Posted by "sarankk (via GitHub)" <gi...@apache.org>.
sarankk commented on code in PR #145:
URL: https://github.com/apache/cassandra-sidecar/pull/145#discussion_r1831593704


##########
server/src/main/java/org/apache/cassandra/sidecar/accesscontrol/AuthCacheService.java:
##########
@@ -0,0 +1,77 @@
+/*
+ * 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 org.apache.cassandra.sidecar.accesscontrol;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import com.google.common.base.Preconditions;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import org.apache.cassandra.sidecar.config.SidecarConfiguration;
+
+/**
+ * {@link AuthCacheService} allows for easier maintenance of all {@link AuthCache}.
+ */
+@Singleton
+public class AuthCacheService
+{
+    private static final Logger LOGGER = LoggerFactory.getLogger(AuthCacheService.class);
+    private final boolean enabled;
+    private final Set<AuthCache<?, ?>> caches = new HashSet<>();

Review Comment:
   We could add retrieval to `AuthCacheService` that way we dont have to give provide each cache in `MainModule` 



-- 
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: pr-unsubscribe@cassandra.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: pr-unsubscribe@cassandra.apache.org
For additional commands, e-mail: pr-help@cassandra.apache.org


Re: [PR] CASSANDRASC-156 Add mTLS Authentication in Sidecar [cassandra-sidecar]

Posted by "sarankk (via GitHub)" <gi...@apache.org>.
sarankk commented on code in PR #145:
URL: https://github.com/apache/cassandra-sidecar/pull/145#discussion_r1831595333


##########
server/src/main/java/org/apache/cassandra/sidecar/accesscontrol/authentication/MutualTlsAuthenticationHandler.java:
##########
@@ -0,0 +1,85 @@
+/*
+ * 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 org.apache.cassandra.sidecar.accesscontrol.authentication;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import io.netty.handler.codec.http.HttpResponseStatus;
+import io.vertx.core.AsyncResult;
+import io.vertx.core.Future;
+import io.vertx.core.Handler;
+import io.vertx.ext.auth.User;
+import io.vertx.ext.auth.authentication.CertificateCredentials;
+import io.vertx.ext.auth.authentication.CredentialValidationException;
+import io.vertx.ext.auth.mtls.MutualTlsAuthentication;
+import io.vertx.ext.web.RoutingContext;
+import io.vertx.ext.web.handler.HttpException;
+import io.vertx.ext.web.handler.impl.AuthenticationHandlerImpl;
+import org.apache.cassandra.sidecar.concurrent.ExecutorPools;
+
+/**
+ * Handler for verifying user certificates for Mutual TLS authentication. {@link MutualTlsAuthenticationHandler} can be
+ * chained with other {@link io.vertx.ext.web.handler.AuthenticationHandler} implementations.
+ */
+public class MutualTlsAuthenticationHandler extends AuthenticationHandlerImpl<MutualTlsAuthentication>
+{
+    private static final Logger LOGGER = LoggerFactory.getLogger(MutualTlsAuthenticationHandler.class);
+    private final ExecutorPools executorPools;
+
+    public MutualTlsAuthenticationHandler(MutualTlsAuthentication authProvider,
+                                          ExecutorPools executorPools)
+    {
+        super(authProvider);
+        this.executorPools = executorPools;
+    }
+
+    @Override
+    public void authenticate(RoutingContext ctx, Handler<AsyncResult<User>> handler)
+    {
+        if (!ctx.request().isSSL())
+        {
+            ctx.response().setStatusCode(HttpResponseStatus.BAD_REQUEST.code()).end();
+            return;
+        }
+
+        CertificateCredentials certificateCredentials = CertificateCredentials.fromHttpRequest(ctx.request());
+        executorPools
+        .service()
+        .executeBlocking(promise -> {
+            authProvider.authenticate(certificateCredentials)
+                        .onSuccess(promise::complete)
+                        .onFailure(cause -> {
+                            if (cause instanceof CredentialValidationException)
+                            {
+                                // If credentials are invalid, reject the promise with a BAD_REQUEST error
+                                promise.fail(new HttpException(HttpResponseStatus.BAD_REQUEST.code(),
+                                                               "Error validating credentials passed"));
+                            }
+                            else
+                            {
+                                // In case of other failures, reject the promise with an UNAUTHORIZED error
+                                promise.fail(new HttpException(HttpResponseStatus.UNAUTHORIZED.code()));
+                            }
+                        });
+        })
+        .onSuccess(result -> handler.handle(Future.succeededFuture((User) result)))
+        .onFailure(cause -> handler.handle(Future.failedFuture(cause)));

Review Comment:
   Regarding 3. since we use `ChainAuthHandler` calling next, allows the next authentication handler to be called. `ChainAuthHandler` finally returns `UNAUTHORIZED` if all handlers fail.



-- 
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: pr-unsubscribe@cassandra.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: pr-unsubscribe@cassandra.apache.org
For additional commands, e-mail: pr-help@cassandra.apache.org