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


##########
server/src/main/java/org/apache/cassandra/sidecar/acl/authorization/RoleAuthorizationsCache.java:
##########
@@ -0,0 +1,100 @@
+/*
+ * 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.authorization;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import io.vertx.core.Vertx;
+import io.vertx.ext.auth.authorization.Authorization;
+import org.apache.cassandra.sidecar.acl.AuthCache;
+import org.apache.cassandra.sidecar.concurrent.ExecutorPools;
+import org.apache.cassandra.sidecar.config.SidecarConfiguration;
+import org.apache.cassandra.sidecar.db.SidecarPermissionsDatabaseAccessor;
+import org.apache.cassandra.sidecar.db.SystemAuthDatabaseAccessor;
+
+/**
+ * Caches role and authorizations held by it. Entries from 
system_auth.role_permissions table in Cassandra and
+ * sidecar_internal.role_permissions_v1 table are processed into 
authorizations and cached here. All table entries are
+ * stored against a unique cache key. Caching against UNIQUE_CACHE_ENTRY is 
done to make sure new entries in the table
+ * are picked up during cache refreshes.
+ */
+@Singleton
+public class RoleAuthorizationsCache extends AuthCache<String, Map<String, 
Set<Authorization>>>
+{
+    private static final String NAME = "role_permissions_cache";
+    protected static final String UNIQUE_CACHE_ENTRY = 
"unique_cache_entry_key";
+
+    @Inject
+    public RoleAuthorizationsCache(Vertx vertx,
+                                   ExecutorPools executorPools,
+                                   SidecarConfiguration sidecarConfiguration,
+                                   SystemAuthDatabaseAccessor 
systemAuthDatabaseAccessor,
+                                   SidecarPermissionsDatabaseAccessor 
sidecarPermissionsDatabaseAccessor)
+    {
+        super(NAME,
+              vertx,
+              executorPools,
+              k -> loadAuthorizations(systemAuthDatabaseAccessor,
+                                      
sidecarConfiguration.serviceConfiguration().schemaKeyspaceConfiguration().isEnabled(),
+                                      sidecarPermissionsDatabaseAccessor),
+              () -> Collections.singletonMap(UNIQUE_CACHE_ENTRY,
+                                             
loadAuthorizations(systemAuthDatabaseAccessor,
+                                                                
sidecarConfiguration.serviceConfiguration().schemaKeyspaceConfiguration().isEnabled(),
+                                                                
sidecarPermissionsDatabaseAccessor)),
+              
sidecarConfiguration.accessControlConfiguration().permissionCacheConfiguration());
+    }
+
+    /**
+     * Returns a {@code Set} of {@link Authorization} a role holds.
+     */
+    public Set<Authorization> getAuthorizations(String role)
+    {
+        Map<String, Set<Authorization>> roleAuthorizations = 
get(UNIQUE_CACHE_ENTRY);
+        return roleAuthorizations != null ? roleAuthorizations.get(role) : 
Collections.emptySet();
+    }
+
+    private static Map<String, Set<Authorization>> 
loadAuthorizations(SystemAuthDatabaseAccessor systemAuthDatabaseAccessor,
+                                                                      boolean 
isSidecarSchemaEnabled,
+                                                                      
SidecarPermissionsDatabaseAccessor sidecarPermissionsDatabaseAccessor)
+    {
+        // when entries in cache are not found, null is returned. We can not 
add null in Map
+        Map<String, Set<Authorization>>  roleAuthorizations
+        = 
Optional.ofNullable(systemAuthDatabaseAccessor.getAllRolesAndPermissions()).orElse(Collections.emptyMap());

Review Comment:
   `systemAuthDatabaseAccessor.getAllRolesAndPermissions()` does not return 
null. `Optional.ofNullable` is unnecessary. 



##########
server/src/main/java/org/apache/cassandra/sidecar/acl/authorization/RoleAuthorizationsCache.java:
##########
@@ -0,0 +1,100 @@
+/*
+ * 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.authorization;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import io.vertx.core.Vertx;
+import io.vertx.ext.auth.authorization.Authorization;
+import org.apache.cassandra.sidecar.acl.AuthCache;
+import org.apache.cassandra.sidecar.concurrent.ExecutorPools;
+import org.apache.cassandra.sidecar.config.SidecarConfiguration;
+import org.apache.cassandra.sidecar.db.SidecarPermissionsDatabaseAccessor;
+import org.apache.cassandra.sidecar.db.SystemAuthDatabaseAccessor;
+
+/**
+ * Caches role and authorizations held by it. Entries from 
system_auth.role_permissions table in Cassandra and
+ * sidecar_internal.role_permissions_v1 table are processed into 
authorizations and cached here. All table entries are
+ * stored against a unique cache key. Caching against UNIQUE_CACHE_ENTRY is 
done to make sure new entries in the table
+ * are picked up during cache refreshes.
+ */
+@Singleton
+public class RoleAuthorizationsCache extends AuthCache<String, Map<String, 
Set<Authorization>>>
+{
+    private static final String NAME = "role_permissions_cache";
+    protected static final String UNIQUE_CACHE_ENTRY = 
"unique_cache_entry_key";
+
+    @Inject
+    public RoleAuthorizationsCache(Vertx vertx,
+                                   ExecutorPools executorPools,
+                                   SidecarConfiguration sidecarConfiguration,
+                                   SystemAuthDatabaseAccessor 
systemAuthDatabaseAccessor,
+                                   SidecarPermissionsDatabaseAccessor 
sidecarPermissionsDatabaseAccessor)
+    {
+        super(NAME,
+              vertx,
+              executorPools,
+              k -> loadAuthorizations(systemAuthDatabaseAccessor,
+                                      
sidecarConfiguration.serviceConfiguration().schemaKeyspaceConfiguration().isEnabled(),
+                                      sidecarPermissionsDatabaseAccessor),
+              () -> Collections.singletonMap(UNIQUE_CACHE_ENTRY,
+                                             
loadAuthorizations(systemAuthDatabaseAccessor,
+                                                                
sidecarConfiguration.serviceConfiguration().schemaKeyspaceConfiguration().isEnabled(),
+                                                                
sidecarPermissionsDatabaseAccessor)),
+              
sidecarConfiguration.accessControlConfiguration().permissionCacheConfiguration());
+    }
+
+    /**
+     * Returns a {@code Set} of {@link Authorization} a role holds.
+     */
+    public Set<Authorization> getAuthorizations(String role)
+    {
+        Map<String, Set<Authorization>> roleAuthorizations = 
get(UNIQUE_CACHE_ENTRY);
+        return roleAuthorizations != null ? roleAuthorizations.get(role) : 
Collections.emptySet();
+    }
+
+    private static Map<String, Set<Authorization>> 
loadAuthorizations(SystemAuthDatabaseAccessor systemAuthDatabaseAccessor,
+                                                                      boolean 
isSidecarSchemaEnabled,
+                                                                      
SidecarPermissionsDatabaseAccessor sidecarPermissionsDatabaseAccessor)
+    {
+        // when entries in cache are not found, null is returned. We can not 
add null in Map
+        Map<String, Set<Authorization>>  roleAuthorizations
+        = 
Optional.ofNullable(systemAuthDatabaseAccessor.getAllRolesAndPermissions()).orElse(Collections.emptyMap());
+
+        if (isSidecarSchemaEnabled)
+        {
+            Map<String, Set<Authorization>> sidecarAuthorizations
+            = 
Optional.ofNullable(sidecarPermissionsDatabaseAccessor.getAllRolesAndPermissions()).orElse(Collections.emptyMap());

Review Comment:
   Similarly, `Optional.ofNullable` is unnecessary.



##########
server/src/main/java/org/apache/cassandra/sidecar/acl/authorization/RoleBasedAuthorizationProvider.java:
##########
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.cassandra.sidecar.acl.authorization;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+
+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.authorization.Authorization;
+import io.vertx.ext.auth.authorization.AuthorizationProvider;
+import io.vertx.ext.web.handler.HttpException;
+import org.apache.cassandra.sidecar.acl.IdentityToRoleCache;
+
+import static org.apache.cassandra.sidecar.utils.AuthUtils.extractIdentities;
+
+/**
+ * Provides authorizations based on user's role. Extracts permissions user 
holds from Cassandra's
+ * system_auth.role_permissions table and from Sidecar's 
sidecar_internal.role_permissions_v1 table and sets
+ * them in user.
+ */
+public class RoleBasedAuthorizationProvider implements AuthorizationProvider
+{
+    private final IdentityToRoleCache identityToRoleCache;
+    private final RoleAuthorizationsCache roleAuthorizationsCache;
+
+    public RoleBasedAuthorizationProvider(IdentityToRoleCache 
identityToRoleCache,
+                                          RoleAuthorizationsCache 
roleAuthorizationsCache)
+    {
+        this.identityToRoleCache = identityToRoleCache;
+        this.roleAuthorizationsCache = roleAuthorizationsCache;
+    }
+
+    @Override
+    public String getId()
+    {
+        return "RoleBasedAccessControl";
+    }
+
+    @Override
+    public void getAuthorizations(User user, Handler<AsyncResult<Void>> 
handler)
+    {
+        getAuthorizations(user).onComplete(handler);
+    }
+
+    @Override
+    public Future<Void> getAuthorizations(User user)
+    {
+        List<String> identities = extractIdentities(user);
+
+        if (identities.isEmpty())
+        {
+            return Future.failedFuture(new 
HttpException(HttpResponseStatus.FORBIDDEN.code(),
+                                                         "Missing client 
identities"));
+        }
+
+        Set<Authorization> authorizations = new HashSet<>();
+        for (String identity : identities)
+        {
+            String role = identityToRoleCache.get(identity);
+            if (role == null)
+            {
+                continue;
+            }
+            // when entries in cache are not found, null is returned. We can 
not add null in user.authorizations()
+            
Optional.ofNullable(roleAuthorizationsCache.getAuthorizations(role)).ifPresent(authorizations::addAll);

Review Comment:
   `Optional.ofNullable` is unnecessary. 
`roleAuthorizationsCache.getAuthorizations(role)` does not return `null`. 



##########
server/src/main/java/org/apache/cassandra/sidecar/acl/authorization/StandardPermission.java:
##########
@@ -0,0 +1,60 @@
+/*
+ * 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.authorization;
+
+import io.vertx.ext.auth.authorization.Authorization;
+import io.vertx.ext.auth.authorization.PermissionBasedAuthorization;
+import io.vertx.ext.auth.authorization.impl.PermissionBasedAuthorizationImpl;
+
+import static 
org.apache.cassandra.sidecar.acl.authorization.WildcardPermission.WILDCARD_TOKEN;
+
+/**
+ * Standard permissions need an exact match between allowed permissions.
+ */
+public class StandardPermission implements Permission
+{
+    protected final String name;
+
+    public StandardPermission(String name)
+    {
+        if (name == null || name.isEmpty())

Review Comment:
   Please update the condition to use this method 
`org.apache.cassandra.sidecar.common.utils.StringUtils#isNullOrEmpty`



##########
server/src/main/java/org/apache/cassandra/sidecar/acl/authorization/SidecarPermissions.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.authorization;
+
+/**
+ * Sidecar permissions allowed on specific targets are listed here. Majority 
of sidecar permissions are represented in
+ * format <action_allowed>:<action_target>.
+ * <p>
+ * Example, with CREATE:SNAPSHOT permission, CREATE action is allowed for 
SNAPSHOT target. Sample actions are
+ * CREATE, VIEW, UPDATE, DELETE, STREAM, IMPORT, UPLOAD, START, ABORT etc.
+ * <p>
+ * Wildcard permissions are supported with ':' wildcard parts divider and '*' 
wildcard token to match parts:
+ * <p>
+ * - *:SNAPSHOT allows CREATE:SNAPSHOT, VIEW:SNAPSHOT and DELETE:SNAPSHOT.
+ * - CREATE:* allows CREATE action on all possible targets.
+ * - *:* allows all possible permissions for specified resource
+ */
+public class SidecarPermissions
+{
+    // cassandra cluster related permissions
+    public static final Permission VIEW_CLUSTER = new 
WildcardPermission("VIEW:CLUSTER");
+
+    // SSTable related permissions
+    public static final Permission UPLOAD_SSTABLE = new 
WildcardPermission("UPLOAD:SSTABLE");
+    public static final Permission IMPORT_SSTABLE = new 
WildcardPermission("IMPORT:SSTABLE");
+    public static final Permission STREAM_SSTABLE = new 
WildcardPermission("STREAM:SSTABLE");
+
+    // Upload related permissions
+    public static final Permission DELETE_SSTABLE_UPLOAD = new 
WildcardPermission("DELETE:SSTABLE_UPLOAD");
+
+    // snapshot related permissions
+    public static final Permission CREATE_SNAPSHOT = new 
WildcardPermission("CREATE:SNAPSHOT");
+    public static final Permission VIEW_SNAPSHOT = new 
WildcardPermission("VIEW:SNAPSHOT");
+    public static final Permission DELETE_SNAPSHOT = new 
WildcardPermission("DELETE:SNAPSHOT");
+
+    // restore related permissions
+    public static final Permission CREATE_RESTORE_JOB = new 
WildcardPermission("CREATE:RESTORE_JOB");
+    public static final Permission VIEW_RESTORE_JOB = new 
WildcardPermission("VIEW:RESTORE_JOB");
+    public static final Permission UPDATE_RESTORE_JOB = new 
WildcardPermission("UPDATE:RESTORE_JOB");
+    public static final Permission ABORT_RESTORE_JOB = new 
WildcardPermission("ABORT:RESTORE_JOB");

Review Comment:
   How about `EDIT` instead of `UPDATE` and `DELETE` instead of `ABORT`? 



##########
server/src/main/java/org/apache/cassandra/sidecar/routes/AccessProtectedRouteBuilder.java:
##########
@@ -0,0 +1,208 @@
+/*
+ * 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.routes;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.function.BiConsumer;
+import java.util.stream.Collectors;
+
+import io.vertx.core.Handler;
+import io.vertx.core.http.HttpMethod;
+import io.vertx.ext.auth.authorization.AndAuthorization;
+import io.vertx.ext.auth.authorization.Authorization;
+import io.vertx.ext.auth.authorization.AuthorizationContext;
+import io.vertx.ext.auth.authorization.AuthorizationProvider;
+import io.vertx.ext.web.Route;
+import io.vertx.ext.web.Router;
+import io.vertx.ext.web.RoutingContext;
+import io.vertx.ext.web.handler.BodyHandler;
+import org.apache.cassandra.sidecar.acl.authorization.AdminIdentityResolver;
+import 
org.apache.cassandra.sidecar.acl.authorization.AuthorizationWithAdminBypassHandler;
+import org.apache.cassandra.sidecar.common.utils.Preconditions;
+import org.apache.cassandra.sidecar.config.AccessControlConfiguration;
+import org.apache.cassandra.sidecar.exceptions.ConfigurationException;
+
+import static org.apache.cassandra.sidecar.common.ApiEndpointsV1.KEYSPACE;
+import static org.apache.cassandra.sidecar.common.ApiEndpointsV1.TABLE;
+
+/**
+ * Builder for building authorized routes
+ */
+public class AccessProtectedRouteBuilder
+{
+    private final AccessControlConfiguration accessControlConfiguration;
+    private final AuthorizationProvider authorizationProvider;
+    private final AdminIdentityResolver adminIdentityResolver;
+
+    private Router router;
+    private HttpMethod method;
+    private String endpoint;
+    private boolean setBodyHandler;
+    private final List<Handler<RoutingContext>> handlers = new ArrayList<>();
+
+    public AccessProtectedRouteBuilder(AccessControlConfiguration 
accessControlConfiguration,
+                                       AuthorizationProvider 
authorizationProvider,
+                                       AdminIdentityResolver 
adminIdentityResolver)
+    {
+        this.accessControlConfiguration = accessControlConfiguration;
+        this.authorizationProvider = authorizationProvider;
+        this.adminIdentityResolver = adminIdentityResolver;
+    }
+
+    /**
+     * Sets {@link Router} authorized route is built for
+     *
+     * @param router Router authorized route is built for
+     * @return a reference to {@link AccessProtectedRouteBuilder} for chaining
+     */
+    public AccessProtectedRouteBuilder router(Router router)
+    {
+        this.router = router;
+        return this;
+    }
+
+    /**
+     * Sets {@link HttpMethod} for route
+     *
+     * @param method HttpMethod set for route
+     * @return a reference to {@link AccessProtectedRouteBuilder} for chaining
+     */
+    public AccessProtectedRouteBuilder method(HttpMethod method)
+    {
+        this.method = method;
+        return this;
+    }
+
+    /**
+     * Sets path for route
+     *
+     * @param endpoint REST path for route
+     * @return a reference to {@link AccessProtectedRouteBuilder} for chaining
+     */
+    public AccessProtectedRouteBuilder endpoint(String endpoint)
+    {
+        this.endpoint = endpoint;
+        return this;
+    }
+
+    /**
+     * Sets if BodyHandler should be created for the route.
+     *
+     * @param setBodyHandler boolean flag indicating if route requires 
BodyHandler
+     * @return a reference to {@link AccessProtectedRouteBuilder} for chaining
+     */
+    public AccessProtectedRouteBuilder setBodyHandler(Boolean setBodyHandler)
+    {
+        this.setBodyHandler = setBodyHandler;
+        return this;
+    }
+
+    /**
+     * Adds handler to handler chain of route. Handlers are ordered, they are 
called in order they are set in chain.
+     *
+     * @param handler handler for route
+     * @return a reference to {@link AccessProtectedRouteBuilder} for chaining
+     */
+    public AccessProtectedRouteBuilder handler(Handler<RoutingContext> handler)
+    {
+        this.handlers.add(handler);
+        return this;
+    }
+
+    /**
+     * Builds an authorized route. Adds {@link 
io.vertx.ext.web.handler.AuthorizationHandler} at top of handler
+     * chain if access control is enabled.
+     */
+    public void build()
+    {
+        Preconditions.checkArgument(router != null, "Router must be set");
+        Preconditions.checkArgument(method != null, "Http method must be set");
+        Preconditions.checkArgument(endpoint != null && !endpoint.isEmpty(), 
"Endpoint must be set");
+        Preconditions.checkArgument(!handlers.isEmpty(), "Handler chain can 
not be empty");
+
+        Route route = router.route(method, endpoint);
+
+        // BodyHandler should be at index 0 in handler chain
+        if (setBodyHandler)
+        {
+            route.handler(BodyHandler.create());
+        }
+
+        if (accessControlConfiguration.enabled())
+        {
+            // authorization handler added before route specific handler chain
+            AuthorizationWithAdminBypassHandler authorizationHandler
+            = new AuthorizationWithAdminBypassHandler(adminIdentityResolver, 
requiredAuthorization());
+            
authorizationHandler.addAuthorizationProvider(authorizationProvider);
+            
authorizationHandler.variableConsumer(routeGenericVariableConsumer());
+
+            route.handler(authorizationHandler);
+        }
+        handlers.forEach(route::handler);
+    }
+
+    private Authorization requiredAuthorization()
+    {
+        Set<Authorization> requiredAuthorizations = handlers
+                                                    .stream()
+                                                    .filter(handler -> handler 
instanceof AccessProtected)
+                                                    .map(handler -> 
(AccessProtected) handler)
+                                                    .flatMap(handler -> 
handler.requiredAuthorizations().stream())
+                                                    
.collect(Collectors.toSet());
+        if (requiredAuthorizations.isEmpty())
+        {
+            throw new ConfigurationException("Authorized route must have 
authorizations declared");
+        }
+        AndAuthorization andAuthorization = AndAuthorization.create();
+        requiredAuthorizations.forEach(andAuthorization::addAuthorization);
+        return andAuthorization;
+    }
+
+    private BiConsumer<RoutingContext, AuthorizationContext> 
routeGenericVariableConsumer()
+    {
+        return (routingCtx, authZContext) -> {
+            if (routingCtx.pathParams().containsKey(KEYSPACE))
+            {
+                authZContext.variables().add(KEYSPACE, 
routingCtx.pathParam(KEYSPACE));
+            }
+            if (routingCtx.pathParams().containsKey(TABLE))
+            {
+                authZContext.variables().add(TABLE, 
routingCtx.pathParam(TABLE));

Review Comment:
   Where are `authZContext.variables()` used? I can only see keyspace and table 
names get added.  



##########
server/src/main/java/org/apache/cassandra/sidecar/routes/AccessProtectedRouteBuilder.java:
##########
@@ -0,0 +1,208 @@
+/*
+ * 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.routes;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.function.BiConsumer;
+import java.util.stream.Collectors;
+
+import io.vertx.core.Handler;
+import io.vertx.core.http.HttpMethod;
+import io.vertx.ext.auth.authorization.AndAuthorization;
+import io.vertx.ext.auth.authorization.Authorization;
+import io.vertx.ext.auth.authorization.AuthorizationContext;
+import io.vertx.ext.auth.authorization.AuthorizationProvider;
+import io.vertx.ext.web.Route;
+import io.vertx.ext.web.Router;
+import io.vertx.ext.web.RoutingContext;
+import io.vertx.ext.web.handler.BodyHandler;
+import org.apache.cassandra.sidecar.acl.authorization.AdminIdentityResolver;
+import 
org.apache.cassandra.sidecar.acl.authorization.AuthorizationWithAdminBypassHandler;
+import org.apache.cassandra.sidecar.common.utils.Preconditions;
+import org.apache.cassandra.sidecar.config.AccessControlConfiguration;
+import org.apache.cassandra.sidecar.exceptions.ConfigurationException;
+
+import static org.apache.cassandra.sidecar.common.ApiEndpointsV1.KEYSPACE;
+import static org.apache.cassandra.sidecar.common.ApiEndpointsV1.TABLE;
+
+/**
+ * Builder for building authorized routes
+ */
+public class AccessProtectedRouteBuilder
+{
+    private final AccessControlConfiguration accessControlConfiguration;
+    private final AuthorizationProvider authorizationProvider;
+    private final AdminIdentityResolver adminIdentityResolver;
+
+    private Router router;
+    private HttpMethod method;
+    private String endpoint;
+    private boolean setBodyHandler;
+    private final List<Handler<RoutingContext>> handlers = new ArrayList<>();
+
+    public AccessProtectedRouteBuilder(AccessControlConfiguration 
accessControlConfiguration,
+                                       AuthorizationProvider 
authorizationProvider,
+                                       AdminIdentityResolver 
adminIdentityResolver)
+    {
+        this.accessControlConfiguration = accessControlConfiguration;
+        this.authorizationProvider = authorizationProvider;
+        this.adminIdentityResolver = adminIdentityResolver;
+    }
+
+    /**
+     * Sets {@link Router} authorized route is built for
+     *
+     * @param router Router authorized route is built for
+     * @return a reference to {@link AccessProtectedRouteBuilder} for chaining
+     */
+    public AccessProtectedRouteBuilder router(Router router)
+    {
+        this.router = router;
+        return this;
+    }
+
+    /**
+     * Sets {@link HttpMethod} for route
+     *
+     * @param method HttpMethod set for route
+     * @return a reference to {@link AccessProtectedRouteBuilder} for chaining
+     */
+    public AccessProtectedRouteBuilder method(HttpMethod method)
+    {
+        this.method = method;
+        return this;
+    }
+
+    /**
+     * Sets path for route
+     *
+     * @param endpoint REST path for route
+     * @return a reference to {@link AccessProtectedRouteBuilder} for chaining
+     */
+    public AccessProtectedRouteBuilder endpoint(String endpoint)
+    {
+        this.endpoint = endpoint;
+        return this;
+    }
+
+    /**
+     * Sets if BodyHandler should be created for the route.
+     *
+     * @param setBodyHandler boolean flag indicating if route requires 
BodyHandler
+     * @return a reference to {@link AccessProtectedRouteBuilder} for chaining
+     */
+    public AccessProtectedRouteBuilder setBodyHandler(Boolean setBodyHandler)
+    {
+        this.setBodyHandler = setBodyHandler;
+        return this;
+    }
+
+    /**
+     * Adds handler to handler chain of route. Handlers are ordered, they are 
called in order they are set in chain.
+     *
+     * @param handler handler for route
+     * @return a reference to {@link AccessProtectedRouteBuilder} for chaining
+     */
+    public AccessProtectedRouteBuilder handler(Handler<RoutingContext> handler)
+    {
+        this.handlers.add(handler);
+        return this;
+    }
+
+    /**
+     * Builds an authorized route. Adds {@link 
io.vertx.ext.web.handler.AuthorizationHandler} at top of handler
+     * chain if access control is enabled.
+     */
+    public void build()
+    {
+        Preconditions.checkArgument(router != null, "Router must be set");
+        Preconditions.checkArgument(method != null, "Http method must be set");
+        Preconditions.checkArgument(endpoint != null && !endpoint.isEmpty(), 
"Endpoint must be set");
+        Preconditions.checkArgument(!handlers.isEmpty(), "Handler chain can 
not be empty");
+
+        Route route = router.route(method, endpoint);
+
+        // BodyHandler should be at index 0 in handler chain
+        if (setBodyHandler)
+        {
+            route.handler(BodyHandler.create());
+        }
+
+        if (accessControlConfiguration.enabled())
+        {
+            // authorization handler added before route specific handler chain
+            AuthorizationWithAdminBypassHandler authorizationHandler
+            = new AuthorizationWithAdminBypassHandler(adminIdentityResolver, 
requiredAuthorization());
+            
authorizationHandler.addAuthorizationProvider(authorizationProvider);
+            
authorizationHandler.variableConsumer(routeGenericVariableConsumer());
+
+            route.handler(authorizationHandler);
+        }
+        handlers.forEach(route::handler);

Review Comment:
   We generally do not use `blockingHandler` to register handler. Dispatching 
blocking actions to executor pool provides better flexibility. 



##########
server/src/main/java/org/apache/cassandra/sidecar/acl/authorization/WildcardPermission.java:
##########
@@ -0,0 +1,66 @@
+/*
+ * 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.authorization;
+
+import java.util.Arrays;
+
+import io.vertx.ext.auth.authorization.Authorization;
+import io.vertx.ext.auth.authorization.WildcardPermissionBasedAuthorization;
+import 
io.vertx.ext.auth.authorization.impl.WildcardPermissionBasedAuthorizationImpl;
+
+/**
+ * Wildcard actions allow grouping allowed actions
+ */

Review Comment:
   It need to mention about the format, where you have already documented in 
`org.apache.cassandra.sidecar.acl.authorization.SidecarPermissions`
   
   ```
   /**
    * Sidecar permissions allowed on specific targets are listed here. Majority 
of sidecar permissions are represented in
    * format <action_allowed>:<action_target>.
    * <p>
    * Example, with CREATE:SNAPSHOT permission, CREATE action is allowed for 
SNAPSHOT target. Sample actions are
    * CREATE, VIEW, UPDATE, DELETE, STREAM, IMPORT, UPLOAD, START, ABORT etc.
    * <p>
    * Wildcard permissions are supported with ':' wildcard parts divider and 
'*' wildcard token to match parts:
    * <p>
    * - *:SNAPSHOT allows CREATE:SNAPSHOT, VIEW:SNAPSHOT and DELETE:SNAPSHOT.
    * - CREATE:* allows CREATE action on all possible targets.
    * - *:* allows all possible permissions for specified resource
    */
   ```



##########
server/src/main/java/org/apache/cassandra/sidecar/acl/authorization/RoleAuthorizationsCache.java:
##########
@@ -0,0 +1,100 @@
+/*
+ * 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.authorization;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import io.vertx.core.Vertx;
+import io.vertx.ext.auth.authorization.Authorization;
+import org.apache.cassandra.sidecar.acl.AuthCache;
+import org.apache.cassandra.sidecar.concurrent.ExecutorPools;
+import org.apache.cassandra.sidecar.config.SidecarConfiguration;
+import org.apache.cassandra.sidecar.db.SidecarPermissionsDatabaseAccessor;
+import org.apache.cassandra.sidecar.db.SystemAuthDatabaseAccessor;
+
+/**
+ * Caches role and authorizations held by it. Entries from 
system_auth.role_permissions table in Cassandra and
+ * sidecar_internal.role_permissions_v1 table are processed into 
authorizations and cached here. All table entries are
+ * stored against a unique cache key. Caching against UNIQUE_CACHE_ENTRY is 
done to make sure new entries in the table
+ * are picked up during cache refreshes.
+ */
+@Singleton
+public class RoleAuthorizationsCache extends AuthCache<String, Map<String, 
Set<Authorization>>>
+{
+    private static final String NAME = "role_permissions_cache";
+    protected static final String UNIQUE_CACHE_ENTRY = 
"unique_cache_entry_key";
+
+    @Inject
+    public RoleAuthorizationsCache(Vertx vertx,
+                                   ExecutorPools executorPools,
+                                   SidecarConfiguration sidecarConfiguration,
+                                   SystemAuthDatabaseAccessor 
systemAuthDatabaseAccessor,
+                                   SidecarPermissionsDatabaseAccessor 
sidecarPermissionsDatabaseAccessor)
+    {
+        super(NAME,
+              vertx,
+              executorPools,
+              k -> loadAuthorizations(systemAuthDatabaseAccessor,

Review Comment:
   Why the input `k` from the loadFunction is unused? It looks like that both 
`loadFunction` and `bulkLoadFunction` do the bulk load of all authorization. 
The same `Set<Authorization>` is duplicated in the cache. 



##########
server/src/main/java/org/apache/cassandra/sidecar/acl/authorization/RoleAuthorizationsCache.java:
##########
@@ -0,0 +1,100 @@
+/*
+ * 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.authorization;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import io.vertx.core.Vertx;
+import io.vertx.ext.auth.authorization.Authorization;
+import org.apache.cassandra.sidecar.acl.AuthCache;
+import org.apache.cassandra.sidecar.concurrent.ExecutorPools;
+import org.apache.cassandra.sidecar.config.SidecarConfiguration;
+import org.apache.cassandra.sidecar.db.SidecarPermissionsDatabaseAccessor;
+import org.apache.cassandra.sidecar.db.SystemAuthDatabaseAccessor;
+
+/**
+ * Caches role and authorizations held by it. Entries from 
system_auth.role_permissions table in Cassandra and
+ * sidecar_internal.role_permissions_v1 table are processed into 
authorizations and cached here. All table entries are
+ * stored against a unique cache key. Caching against UNIQUE_CACHE_ENTRY is 
done to make sure new entries in the table
+ * are picked up during cache refreshes.
+ */
+@Singleton
+public class RoleAuthorizationsCache extends AuthCache<String, Map<String, 
Set<Authorization>>>
+{
+    private static final String NAME = "role_permissions_cache";
+    protected static final String UNIQUE_CACHE_ENTRY = 
"unique_cache_entry_key";
+
+    @Inject
+    public RoleAuthorizationsCache(Vertx vertx,
+                                   ExecutorPools executorPools,
+                                   SidecarConfiguration sidecarConfiguration,
+                                   SystemAuthDatabaseAccessor 
systemAuthDatabaseAccessor,
+                                   SidecarPermissionsDatabaseAccessor 
sidecarPermissionsDatabaseAccessor)
+    {
+        super(NAME,
+              vertx,
+              executorPools,
+              k -> loadAuthorizations(systemAuthDatabaseAccessor,
+                                      
sidecarConfiguration.serviceConfiguration().schemaKeyspaceConfiguration().isEnabled(),
+                                      sidecarPermissionsDatabaseAccessor),
+              () -> Collections.singletonMap(UNIQUE_CACHE_ENTRY,
+                                             
loadAuthorizations(systemAuthDatabaseAccessor,
+                                                                
sidecarConfiguration.serviceConfiguration().schemaKeyspaceConfiguration().isEnabled(),
+                                                                
sidecarPermissionsDatabaseAccessor)),
+              
sidecarConfiguration.accessControlConfiguration().permissionCacheConfiguration());
+    }
+
+    /**
+     * Returns a {@code Set} of {@link Authorization} a role holds.
+     */
+    public Set<Authorization> getAuthorizations(String role)
+    {
+        Map<String, Set<Authorization>> roleAuthorizations = 
get(UNIQUE_CACHE_ENTRY);
+        return roleAuthorizations != null ? roleAuthorizations.get(role) : 
Collections.emptySet();
+    }
+
+    private static Map<String, Set<Authorization>> 
loadAuthorizations(SystemAuthDatabaseAccessor systemAuthDatabaseAccessor,
+                                                                      boolean 
isSidecarSchemaEnabled,
+                                                                      
SidecarPermissionsDatabaseAccessor sidecarPermissionsDatabaseAccessor)
+    {
+        // when entries in cache are not found, null is returned. We can not 
add null in Map
+        Map<String, Set<Authorization>>  roleAuthorizations
+        = 
Optional.ofNullable(systemAuthDatabaseAccessor.getAllRolesAndPermissions()).orElse(Collections.emptyMap());
+
+        if (isSidecarSchemaEnabled)

Review Comment:
   I think this condition is incomplete. You want to check 
`org.apache.cassandra.sidecar.db.schema.SidecarSchema#isInitialized` instead, 
before querying from the sidecar table. 



##########
server/src/main/java/org/apache/cassandra/sidecar/acl/authorization/SuperUserCache.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.acl.authorization;
+
+import java.util.Optional;
+
+import com.google.inject.Inject;
+import io.vertx.core.Vertx;
+import org.apache.cassandra.sidecar.acl.AuthCache;
+import org.apache.cassandra.sidecar.concurrent.ExecutorPools;
+import org.apache.cassandra.sidecar.config.SidecarConfiguration;
+import org.apache.cassandra.sidecar.db.SystemAuthDatabaseAccessor;
+
+/**
+ * Caches superuser status of cassandra roles.
+ */
+public class SuperUserCache extends AuthCache<String, Boolean>
+{
+    private static final String NAME = "super_user_cache";
+
+    @Inject
+    public SuperUserCache(Vertx vertx,
+                          ExecutorPools executorPools,
+                          SidecarConfiguration sidecarConfiguration,
+                          SystemAuthDatabaseAccessor 
systemAuthDatabaseAccessor)
+    {
+        super(NAME,
+              vertx,
+              executorPools,
+              systemAuthDatabaseAccessor::isSuperUser,
+              systemAuthDatabaseAccessor::getRoles,
+              
sidecarConfiguration.accessControlConfiguration().permissionCacheConfiguration());
+    }
+
+    public boolean isSuperUser(String role)
+    {
+        return Optional.ofNullable(get(role)).orElse(false);

Review Comment:
   `Optional` is used a lot in the patch. In some places, I agree. 
   
   For this one, you can have this much simplified form. 
   
   ```suggestion
           return get(role) != null;
   ```



##########
server/src/main/java/org/apache/cassandra/sidecar/acl/authorization/StandardPermission.java:
##########
@@ -0,0 +1,60 @@
+/*
+ * 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.authorization;
+
+import io.vertx.ext.auth.authorization.Authorization;
+import io.vertx.ext.auth.authorization.PermissionBasedAuthorization;
+import io.vertx.ext.auth.authorization.impl.PermissionBasedAuthorizationImpl;
+
+import static 
org.apache.cassandra.sidecar.acl.authorization.WildcardPermission.WILDCARD_TOKEN;
+
+/**
+ * Standard permissions need an exact match between allowed permissions.
+ */
+public class StandardPermission implements Permission
+{
+    protected final String name;
+
+    public StandardPermission(String name)
+    {
+        if (name == null || name.isEmpty())
+        {
+            throw new IllegalArgumentException("Permission name can not be 
null or empty. To allow wildcard permission "
+                                               + "across resource, use " + 
WILDCARD_TOKEN);

Review Comment:
   For exception message, it only needs to mention "Permission name cannot be 
null or empty."



##########
server/src/main/java/org/apache/cassandra/sidecar/acl/authorization/VariableAwareResource.java:
##########
@@ -0,0 +1,45 @@
+/*
+ * 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.authorization;
+
+import static org.apache.cassandra.sidecar.common.ApiEndpointsV1.KEYSPACE;
+import static org.apache.cassandra.sidecar.common.ApiEndpointsV1.TABLE;
+
+/**
+ * Resources sidecar can expect permissions for. This list is not exhaustive.
+ */
+public enum VariableAwareResource

Review Comment:
   I am not sure what this enum does? It seems to be just string constants. 



##########
server/src/main/java/org/apache/cassandra/sidecar/acl/authorization/StandardPermission.java:
##########
@@ -0,0 +1,60 @@
+/*
+ * 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.authorization;
+
+import io.vertx.ext.auth.authorization.Authorization;
+import io.vertx.ext.auth.authorization.PermissionBasedAuthorization;
+import io.vertx.ext.auth.authorization.impl.PermissionBasedAuthorizationImpl;
+
+import static 
org.apache.cassandra.sidecar.acl.authorization.WildcardPermission.WILDCARD_TOKEN;
+
+/**
+ * Standard permissions need an exact match between allowed permissions.
+ */
+public class StandardPermission implements Permission
+{
+    protected final String name;
+
+    public StandardPermission(String name)
+    {
+        if (name == null || name.isEmpty())
+        {
+            throw new IllegalArgumentException("Permission name can not be 
null or empty. To allow wildcard permission "
+                                               + "across resource, use " + 
WILDCARD_TOKEN);
+        }
+        this.name = name;
+    }
+
+    @Override
+    public String name()
+    {
+        return name;
+    }
+
+    @Override
+    public Authorization toAuthorization(String resource)
+    {
+        PermissionBasedAuthorization authorization = new 
PermissionBasedAuthorizationImpl(name);
+        if (resource != null && !resource.isEmpty())

Review Comment:
   same, use 
`org.apache.cassandra.sidecar.common.utils.StringUtils#isNullOrEmpty`



##########
server/src/main/java/org/apache/cassandra/sidecar/acl/authorization/VariableAwareResource.java:
##########
@@ -0,0 +1,45 @@
+/*
+ * 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.authorization;
+
+import static org.apache.cassandra.sidecar.common.ApiEndpointsV1.KEYSPACE;
+import static org.apache.cassandra.sidecar.common.ApiEndpointsV1.TABLE;
+
+/**
+ * Resources sidecar can expect permissions for. This list is not exhaustive.
+ */
+public enum VariableAwareResource
+{
+    CLUSTER("cluster"),
+    SIDECAR("sidecar"),
+    DATA_WITH_KEYSPACE(String.format("data/{%s}", KEYSPACE)),
+    DATA_WITH_KEYSPACE_TABLE(String.format("data/{%s}/{%s}", KEYSPACE, TABLE));

Review Comment:
   So this is used in combination with the variables set at the auth context at 
`AccessProtectedRouteBuilder`. 
   
   `{}` is the format required by `VariableAwareExpression`. Can you add a 
detailed javadoc to explain what is going how here? 
   
   I am still not sure about `data` and `/` in the string. 



##########
server/src/main/java/org/apache/cassandra/sidecar/db/SidecarPermissionsDatabaseAccessor.java:
##########
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.cassandra.sidecar.db;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+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 io.vertx.ext.auth.authorization.Authorization;
+import org.apache.cassandra.sidecar.common.server.CQLSessionProvider;
+import org.apache.cassandra.sidecar.db.schema.SidecarRolePermissionsSchema;
+
+import static org.apache.cassandra.sidecar.utils.AuthUtils.actionFromName;
+
+/**
+ * {@link SidecarPermissionsDatabaseAccessor} is an accessor for 
role_permissions_v1 table under sidecar_internal
+ * keyspace. Custom sidecar specific permissions are stored in this table.
+ */
+@Singleton
+public class SidecarPermissionsDatabaseAccessor extends 
DatabaseAccessor<SidecarRolePermissionsSchema>
+{
+    private static final Logger logger = 
LoggerFactory.getLogger(SidecarPermissionsDatabaseAccessor.class);
+
+    @Inject
+    protected SidecarPermissionsDatabaseAccessor(SidecarRolePermissionsSchema 
tableSchema,
+                                                 CQLSessionProvider 
sessionProvider)
+    {
+        super(tableSchema, sessionProvider);
+    }
+
+    /**
+     * Queries Sidecar for all rows in sidecar_internal.role_permissions_v1 
table. This table maps permissions of a
+     * role into {@link Authorization} and returns a {@code Map} of user role 
to authorizations.
+     *
+     * @return - {@code Map} contains role and granted authorizations
+     */
+    public Map<String, Set<Authorization>> getAllRolesAndPermissions()
+    {
+        BoundStatement statement = 
tableSchema.getAllRolesAndPermissions().bind();
+        ResultSet result = execute(statement);
+        Map<String, Set<Authorization>> roleAuthorizations = new HashMap<>();
+        for (Row row : result)
+        {
+            String role = row.getString("role");
+            String resource = row.getString("resource");
+            Set<String> permissions = row.getSet("permissions", String.class);
+            Set<Authorization> authorizations = new HashSet<>();
+            for (String permission : permissions)
+            {
+                try
+                {
+                    
authorizations.add(actionFromName(permission).toAuthorization(resource));
+                }
+                catch (Exception e)
+                {
+                    logger.error("Error reading sidecar permission {} for 
resource {} for role {}, e",

Review Comment:
   ```suggestion
                       logger.error("Error on parsing sidecar permission. 
permission={} resource={} role={}",
   ```



##########
server/src/main/java/org/apache/cassandra/sidecar/server/MainModule.java:
##########
@@ -245,11 +256,52 @@ public ChainAuthHandler chainAuthHandler(Vertx vertx,
         return chainAuthHandler;
     }
 
+    @Provides
+    @Singleton
+    public AuthorizationProvider authorizationProvider(SidecarConfiguration 
sidecarConfiguration,
+                                                       IdentityToRoleCache 
identityToRoleCache,
+                                                       RoleAuthorizationsCache 
roleAuthorizationsCache)
+    {
+        AccessControlConfiguration accessControlConfiguration = 
sidecarConfiguration.accessControlConfiguration();
+        if (!accessControlConfiguration.enabled())
+        {
+            return AllowAllAuthorizationProvider.INSTANCE;
+        }
+
+        ParameterizedClassConfiguration config = 
accessControlConfiguration.authorizerConfiguration();
+        if (config == null)
+        {
+            throw new ConfigurationException("Access control is enabled, but 
authorizer not set");
+        }

Review Comment:
   Can operator configure authentication only? Access control is enabled, only 
authenticators, but there is no authorizer.



##########
server/src/main/java/org/apache/cassandra/sidecar/utils/AuthUtils.java:
##########
@@ -0,0 +1,81 @@
+/*
+ * 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.utils;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+
+import io.vertx.ext.auth.User;
+import org.apache.cassandra.sidecar.acl.authorization.Permission;
+import org.apache.cassandra.sidecar.acl.authorization.StandardPermission;
+import org.apache.cassandra.sidecar.acl.authorization.WildcardPermission;
+
+import static 
org.apache.cassandra.sidecar.acl.authorization.WildcardPermission.WILDCARD_PART_DIVIDER_TOKEN;
+import static 
org.apache.cassandra.sidecar.acl.authorization.WildcardPermission.WILDCARD_TOKEN;
+
+/**
+ * Class with utility methods for Authentication and Authorization.
+ */
+public class AuthUtils
+{
+    /**
+     * Extracts a list of identities a user holds from their principal.
+     *
+     * @param user User object in Vertx
+     * @return extracted identities of user
+     */
+    public static List<String> extractIdentities(User user)
+    {
+        if (user.principal() == null)
+        {
+            return Collections.emptyList();
+        }
+
+        if (!user.principal().containsKey("identity") && 
!user.principal().containsKey("identities"))
+        {
+            return Collections.emptyList();
+        }
+
+        return Optional.ofNullable(user.principal().getString("identity"))
+                       .map(Collections::singletonList)
+                       .orElseGet(() -> Arrays.asList(user.principal()
+                                                          
.getString("identities")
+                                                          .split(",")));
+    }
+
+    /**
+     * @return an instance of {@link Permission} given the name
+     */
+    public static Permission actionFromName(String name)

Review Comment:
   rename to `permissionFromName`



##########
server/src/main/java/org/apache/cassandra/sidecar/utils/AuthUtils.java:
##########
@@ -0,0 +1,81 @@
+/*
+ * 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.utils;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+
+import io.vertx.ext.auth.User;
+import org.apache.cassandra.sidecar.acl.authorization.Permission;
+import org.apache.cassandra.sidecar.acl.authorization.StandardPermission;
+import org.apache.cassandra.sidecar.acl.authorization.WildcardPermission;
+
+import static 
org.apache.cassandra.sidecar.acl.authorization.WildcardPermission.WILDCARD_PART_DIVIDER_TOKEN;
+import static 
org.apache.cassandra.sidecar.acl.authorization.WildcardPermission.WILDCARD_TOKEN;
+
+/**
+ * Class with utility methods for Authentication and Authorization.
+ */
+public class AuthUtils
+{
+    /**
+     * Extracts a list of identities a user holds from their principal.
+     *
+     * @param user User object in Vertx
+     * @return extracted identities of user
+     */
+    public static List<String> extractIdentities(User user)
+    {
+        if (user.principal() == null)
+        {
+            return Collections.emptyList();
+        }
+
+        if (!user.principal().containsKey("identity") && 
!user.principal().containsKey("identities"))
+        {
+            return Collections.emptyList();
+        }
+
+        return Optional.ofNullable(user.principal().getString("identity"))
+                       .map(Collections::singletonList)
+                       .orElseGet(() -> Arrays.asList(user.principal()
+                                                          
.getString("identities")
+                                                          .split(",")));
+    }

Review Comment:
   What if principal has both `identity` and `identities`? This implementation 
returns early when `identity` exists. 
   
   ```suggestion
       public static List<String> extractIdentities(User user)
       {
           JsonObject principal = user.principal();
   
           if (principal == null)
           {
               return Collections.emptyList();
           }
   
           List<String> identities = new ArrayList<>();
           if (principal.containsKey("identity"))
           {
               identities.add(principal.getString("identity"));
           }
   
           if (principal.containsKey("identities"))
           {
               String[] parts = 
user.principal().getString("identities").split(",");
               identities.addAll(Arrays.asList(parts));
           }
   
           return identities;
       }
   ```



##########
server/src/main/java/org/apache/cassandra/sidecar/acl/authorization/SidecarPermissions.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.authorization;
+
+/**
+ * Sidecar permissions allowed on specific targets are listed here. Majority 
of sidecar permissions are represented in
+ * format <action_allowed>:<action_target>.
+ * <p>
+ * Example, with CREATE:SNAPSHOT permission, CREATE action is allowed for 
SNAPSHOT target. Sample actions are
+ * CREATE, VIEW, UPDATE, DELETE, STREAM, IMPORT, UPLOAD, START, ABORT etc.
+ * <p>
+ * Wildcard permissions are supported with ':' wildcard parts divider and '*' 
wildcard token to match parts:
+ * <p>
+ * - *:SNAPSHOT allows CREATE:SNAPSHOT, VIEW:SNAPSHOT and DELETE:SNAPSHOT.
+ * - CREATE:* allows CREATE action on all possible targets.
+ * - *:* allows all possible permissions for specified resource
+ */
+public class SidecarPermissions
+{
+    // cassandra cluster related permissions
+    public static final Permission VIEW_CLUSTER = new 
WildcardPermission("VIEW:CLUSTER");

Review Comment:
   `VIEW:CLUSTER` reads very general and vague. Can you make it more precise? 
   What does "CLUSTER" mean here? 



##########
server/src/main/java/org/apache/cassandra/sidecar/routes/AccessProtectedRouteBuilder.java:
##########
@@ -0,0 +1,208 @@
+/*
+ * 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.routes;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.function.BiConsumer;
+import java.util.stream.Collectors;
+
+import io.vertx.core.Handler;
+import io.vertx.core.http.HttpMethod;
+import io.vertx.ext.auth.authorization.AndAuthorization;
+import io.vertx.ext.auth.authorization.Authorization;
+import io.vertx.ext.auth.authorization.AuthorizationContext;
+import io.vertx.ext.auth.authorization.AuthorizationProvider;
+import io.vertx.ext.web.Route;
+import io.vertx.ext.web.Router;
+import io.vertx.ext.web.RoutingContext;
+import io.vertx.ext.web.handler.BodyHandler;
+import org.apache.cassandra.sidecar.acl.authorization.AdminIdentityResolver;
+import 
org.apache.cassandra.sidecar.acl.authorization.AuthorizationWithAdminBypassHandler;
+import org.apache.cassandra.sidecar.common.utils.Preconditions;
+import org.apache.cassandra.sidecar.config.AccessControlConfiguration;
+import org.apache.cassandra.sidecar.exceptions.ConfigurationException;
+
+import static org.apache.cassandra.sidecar.common.ApiEndpointsV1.KEYSPACE;
+import static org.apache.cassandra.sidecar.common.ApiEndpointsV1.TABLE;
+
+/**
+ * Builder for building authorized routes
+ */
+public class AccessProtectedRouteBuilder
+{
+    private final AccessControlConfiguration accessControlConfiguration;
+    private final AuthorizationProvider authorizationProvider;
+    private final AdminIdentityResolver adminIdentityResolver;
+
+    private Router router;
+    private HttpMethod method;
+    private String endpoint;
+    private boolean setBodyHandler;
+    private final List<Handler<RoutingContext>> handlers = new ArrayList<>();
+
+    public AccessProtectedRouteBuilder(AccessControlConfiguration 
accessControlConfiguration,
+                                       AuthorizationProvider 
authorizationProvider,
+                                       AdminIdentityResolver 
adminIdentityResolver)
+    {
+        this.accessControlConfiguration = accessControlConfiguration;
+        this.authorizationProvider = authorizationProvider;
+        this.adminIdentityResolver = adminIdentityResolver;
+    }
+
+    /**
+     * Sets {@link Router} authorized route is built for
+     *
+     * @param router Router authorized route is built for
+     * @return a reference to {@link AccessProtectedRouteBuilder} for chaining
+     */
+    public AccessProtectedRouteBuilder router(Router router)
+    {
+        this.router = router;
+        return this;
+    }
+
+    /**
+     * Sets {@link HttpMethod} for route
+     *
+     * @param method HttpMethod set for route
+     * @return a reference to {@link AccessProtectedRouteBuilder} for chaining
+     */
+    public AccessProtectedRouteBuilder method(HttpMethod method)
+    {
+        this.method = method;
+        return this;
+    }
+
+    /**
+     * Sets path for route
+     *
+     * @param endpoint REST path for route
+     * @return a reference to {@link AccessProtectedRouteBuilder} for chaining
+     */
+    public AccessProtectedRouteBuilder endpoint(String endpoint)
+    {
+        this.endpoint = endpoint;
+        return this;
+    }
+
+    /**
+     * Sets if BodyHandler should be created for the route.
+     *
+     * @param setBodyHandler boolean flag indicating if route requires 
BodyHandler
+     * @return a reference to {@link AccessProtectedRouteBuilder} for chaining
+     */
+    public AccessProtectedRouteBuilder setBodyHandler(Boolean setBodyHandler)
+    {
+        this.setBodyHandler = setBodyHandler;
+        return this;
+    }
+
+    /**
+     * Adds handler to handler chain of route. Handlers are ordered, they are 
called in order they are set in chain.
+     *
+     * @param handler handler for route
+     * @return a reference to {@link AccessProtectedRouteBuilder} for chaining
+     */
+    public AccessProtectedRouteBuilder handler(Handler<RoutingContext> handler)
+    {
+        this.handlers.add(handler);
+        return this;
+    }
+
+    /**
+     * Builds an authorized route. Adds {@link 
io.vertx.ext.web.handler.AuthorizationHandler} at top of handler
+     * chain if access control is enabled.
+     */
+    public void build()
+    {
+        Preconditions.checkArgument(router != null, "Router must be set");
+        Preconditions.checkArgument(method != null, "Http method must be set");
+        Preconditions.checkArgument(endpoint != null && !endpoint.isEmpty(), 
"Endpoint must be set");
+        Preconditions.checkArgument(!handlers.isEmpty(), "Handler chain can 
not be empty");
+
+        Route route = router.route(method, endpoint);
+
+        // BodyHandler should be at index 0 in handler chain
+        if (setBodyHandler)
+        {
+            route.handler(BodyHandler.create());
+        }
+
+        if (accessControlConfiguration.enabled())
+        {
+            // authorization handler added before route specific handler chain
+            AuthorizationWithAdminBypassHandler authorizationHandler
+            = new AuthorizationWithAdminBypassHandler(adminIdentityResolver, 
requiredAuthorization());
+            
authorizationHandler.addAuthorizationProvider(authorizationProvider);
+            
authorizationHandler.variableConsumer(routeGenericVariableConsumer());
+
+            route.handler(authorizationHandler);
+        }

Review Comment:
   Intuitively, the authorizationHandler, if exists, should be placed before 
the body handler, to avoid receiving body bytes prematurely. 



##########
server/src/main/java/org/apache/cassandra/sidecar/acl/authorization/SidecarPermissions.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.authorization;
+
+/**
+ * Sidecar permissions allowed on specific targets are listed here. Majority 
of sidecar permissions are represented in
+ * format <action_allowed>:<action_target>.
+ * <p>
+ * Example, with CREATE:SNAPSHOT permission, CREATE action is allowed for 
SNAPSHOT target. Sample actions are
+ * CREATE, VIEW, UPDATE, DELETE, STREAM, IMPORT, UPLOAD, START, ABORT etc.
+ * <p>
+ * Wildcard permissions are supported with ':' wildcard parts divider and '*' 
wildcard token to match parts:
+ * <p>
+ * - *:SNAPSHOT allows CREATE:SNAPSHOT, VIEW:SNAPSHOT and DELETE:SNAPSHOT.
+ * - CREATE:* allows CREATE action on all possible targets.
+ * - *:* allows all possible permissions for specified resource
+ */
+public class SidecarPermissions
+{
+    // cassandra cluster related permissions
+    public static final Permission VIEW_CLUSTER = new 
WildcardPermission("VIEW:CLUSTER");
+
+    // SSTable related permissions
+    public static final Permission UPLOAD_SSTABLE = new 
WildcardPermission("UPLOAD:SSTABLE");
+    public static final Permission IMPORT_SSTABLE = new 
WildcardPermission("IMPORT:SSTABLE");
+    public static final Permission STREAM_SSTABLE = new 
WildcardPermission("STREAM:SSTABLE");
+
+    // Upload related permissions
+    public static final Permission DELETE_SSTABLE_UPLOAD = new 
WildcardPermission("DELETE:SSTABLE_UPLOAD");
+
+    // snapshot related permissions
+    public static final Permission CREATE_SNAPSHOT = new 
WildcardPermission("CREATE:SNAPSHOT");
+    public static final Permission VIEW_SNAPSHOT = new 
WildcardPermission("VIEW:SNAPSHOT");
+    public static final Permission DELETE_SNAPSHOT = new 
WildcardPermission("DELETE:SNAPSHOT");
+
+    // restore related permissions
+    public static final Permission CREATE_RESTORE_JOB = new 
WildcardPermission("CREATE:RESTORE_JOB");
+    public static final Permission VIEW_RESTORE_JOB = new 
WildcardPermission("VIEW:RESTORE_JOB");
+    public static final Permission UPDATE_RESTORE_JOB = new 
WildcardPermission("UPDATE:RESTORE_JOB");
+    public static final Permission ABORT_RESTORE_JOB = new 
WildcardPermission("ABORT:RESTORE_JOB");
+
+    // cdc related permissions
+    public static final Permission STREAM_CDC = new 
WildcardPermission("STREAM:CDC");
+    public static final Permission VIEW_CDC = new 
WildcardPermission("VIEW:CDC");
+
+    // sidecar operation related permissions
+    public static final Permission VIEW_TASKS = new 
WildcardPermission("VIEW:TASKS");

Review Comment:
   `TASKS` is too general. It is `OPERATIONAL_JOBS`



##########
server/src/main/java/org/apache/cassandra/sidecar/acl/authorization/SidecarPermissions.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.authorization;
+
+/**
+ * Sidecar permissions allowed on specific targets are listed here. Majority 
of sidecar permissions are represented in
+ * format <action_allowed>:<action_target>.
+ * <p>
+ * Example, with CREATE:SNAPSHOT permission, CREATE action is allowed for 
SNAPSHOT target. Sample actions are
+ * CREATE, VIEW, UPDATE, DELETE, STREAM, IMPORT, UPLOAD, START, ABORT etc.
+ * <p>
+ * Wildcard permissions are supported with ':' wildcard parts divider and '*' 
wildcard token to match parts:
+ * <p>
+ * - *:SNAPSHOT allows CREATE:SNAPSHOT, VIEW:SNAPSHOT and DELETE:SNAPSHOT.
+ * - CREATE:* allows CREATE action on all possible targets.
+ * - *:* allows all possible permissions for specified resource
+ */
+public class SidecarPermissions
+{
+    // cassandra cluster related permissions
+    public static final Permission VIEW_CLUSTER = new 
WildcardPermission("VIEW:CLUSTER");
+
+    // SSTable related permissions
+    public static final Permission UPLOAD_SSTABLE = new 
WildcardPermission("UPLOAD:SSTABLE");
+    public static final Permission IMPORT_SSTABLE = new 
WildcardPermission("IMPORT:SSTABLE");
+    public static final Permission STREAM_SSTABLE = new 
WildcardPermission("STREAM:SSTABLE");

Review Comment:
   What about using `READ` to replace both `VIEW` and `STREAM`? Do we need to 
distinguish between those 2 read operations? 



##########
server/src/main/java/org/apache/cassandra/sidecar/acl/authorization/SidecarPermissions.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.authorization;
+
+/**
+ * Sidecar permissions allowed on specific targets are listed here. Majority 
of sidecar permissions are represented in
+ * format <action_allowed>:<action_target>.
+ * <p>
+ * Example, with CREATE:SNAPSHOT permission, CREATE action is allowed for 
SNAPSHOT target. Sample actions are
+ * CREATE, VIEW, UPDATE, DELETE, STREAM, IMPORT, UPLOAD, START, ABORT etc.
+ * <p>
+ * Wildcard permissions are supported with ':' wildcard parts divider and '*' 
wildcard token to match parts:
+ * <p>
+ * - *:SNAPSHOT allows CREATE:SNAPSHOT, VIEW:SNAPSHOT and DELETE:SNAPSHOT.
+ * - CREATE:* allows CREATE action on all possible targets.
+ * - *:* allows all possible permissions for specified resource
+ */
+public class SidecarPermissions
+{
+    // cassandra cluster related permissions
+    public static final Permission VIEW_CLUSTER = new 
WildcardPermission("VIEW:CLUSTER");
+
+    // SSTable related permissions
+    public static final Permission UPLOAD_SSTABLE = new 
WildcardPermission("UPLOAD:SSTABLE");
+    public static final Permission IMPORT_SSTABLE = new 
WildcardPermission("IMPORT:SSTABLE");
+    public static final Permission STREAM_SSTABLE = new 
WildcardPermission("STREAM:SSTABLE");
+
+    // Upload related permissions
+    public static final Permission DELETE_SSTABLE_UPLOAD = new 
WildcardPermission("DELETE:SSTABLE_UPLOAD");
+
+    // snapshot related permissions
+    public static final Permission CREATE_SNAPSHOT = new 
WildcardPermission("CREATE:SNAPSHOT");
+    public static final Permission VIEW_SNAPSHOT = new 
WildcardPermission("VIEW:SNAPSHOT");
+    public static final Permission DELETE_SNAPSHOT = new 
WildcardPermission("DELETE:SNAPSHOT");
+
+    // restore related permissions
+    public static final Permission CREATE_RESTORE_JOB = new 
WildcardPermission("CREATE:RESTORE_JOB");
+    public static final Permission VIEW_RESTORE_JOB = new 
WildcardPermission("VIEW:RESTORE_JOB");
+    public static final Permission UPDATE_RESTORE_JOB = new 
WildcardPermission("UPDATE:RESTORE_JOB");
+    public static final Permission ABORT_RESTORE_JOB = new 
WildcardPermission("ABORT:RESTORE_JOB");
+
+    // cdc related permissions
+    public static final Permission STREAM_CDC = new 
WildcardPermission("STREAM:CDC");
+    public static final Permission VIEW_CDC = new 
WildcardPermission("VIEW:CDC");
+
+    // sidecar operation related permissions
+    public static final Permission VIEW_TASKS = new 
WildcardPermission("VIEW:TASKS");
+
+    // cassandra data related actions
+    public static final Permission VIEW_SCHEMA = new 
WildcardPermission("VIEW:SCHEMA");
+    public static final Permission VIEW_TOPOLOGY = new 
WildcardPermission("VIEW:TOPOLOGY");

Review Comment:
   `SCHEMA` and `TOPOLOGY` is different from `CLUSTER`? (I do not like 
`CLUSTER`, but still curious about the difference here)



##########
server/src/main/java/org/apache/cassandra/sidecar/acl/authorization/RoleBasedAuthorizationProvider.java:
##########
@@ -0,0 +1,100 @@
+/*
+ * 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.authorization;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+
+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.authorization.Authorization;
+import io.vertx.ext.auth.authorization.AuthorizationProvider;
+import io.vertx.ext.web.handler.HttpException;
+import org.apache.cassandra.sidecar.acl.IdentityToRoleCache;
+
+import static org.apache.cassandra.sidecar.utils.AuthUtils.extractIdentities;
+
+/**
+ * Provides authorizations based on user's role. Extracts permissions user 
holds from Cassandra's
+ * system_auth.role_permissions table and from Sidecar's 
sidecar_internal.role_permissions_v1 table and sets
+ * them in user.
+ */
+public class RoleBasedAuthorizationProvider implements AuthorizationProvider
+{
+    private final IdentityToRoleCache identityToRoleCache;
+    private final RoleAuthorizationsCache roleAuthorizationsCache;
+
+    public RoleBasedAuthorizationProvider(IdentityToRoleCache 
identityToRoleCache,
+                                          RoleAuthorizationsCache 
roleAuthorizationsCache)
+    {
+        this.identityToRoleCache = identityToRoleCache;
+        this.roleAuthorizationsCache = roleAuthorizationsCache;
+    }
+
+    @Override
+    public String getId()
+    {
+        return "RoleBasedAccessControl";
+    }
+
+    @Override
+    public void getAuthorizations(User user, Handler<AsyncResult<Void>> 
handler)
+    {
+        getAuthorizations(user).onComplete(handler);
+    }
+
+    @Override
+    public Future<Void> getAuthorizations(User user)
+    {
+        List<String> identities = extractIdentities(user);
+
+        if (identities.isEmpty())
+        {
+            return Future.failedFuture(new 
HttpException(HttpResponseStatus.FORBIDDEN.code(),

Review Comment:
   I agree with Hari. HttpException is not the proper exception for this 
domain. Ideally, HttpException is only thrown from request handlers. 
   A domain specific exception is preferable 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: [email protected]

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


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

Reply via email to