You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@helix.apache.org by ne...@apache.org on 2022/03/14 23:44:03 UTC

[helix] branch master updated: Add Authorization Components to helix-rest (#1967) (#1981)

This is an automated email from the ASF dual-hosted git repository.

nealsun pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/helix.git


The following commit(s) were added to refs/heads/master by this push:
     new 54e25ce  Add Authorization Components to helix-rest (#1967) (#1981)
54e25ce is described below

commit 54e25ce30b1de41a7b4299d35809961d5c962570
Author: Neal Sun <ne...@linkedin.com>
AuthorDate: Mon Mar 14 16:30:43 2022 -0700

    Add Authorization Components to helix-rest (#1967) (#1981)
    
    * Add Authorization Components to helix-rest
    
    * Address some comments
---
 .../apache/helix/rest/server/HelixRestServer.java  |  24 ++++
 .../rest/server/authValidator/AuthValidator.java   |  27 ++++
 .../server/authValidator/NoopAuthValidator.java    |  29 +++++
 .../helix/rest/server/filters/ClusterAuth.java     |  33 +++++
 .../rest/server/filters/ClusterAuthFilter.java     |  46 +++++++
 .../helix/rest/server/filters/NamespaceAuth.java   |  33 +++++
 .../rest/server/filters/NamespaceAuthFilter.java   |  46 +++++++
 .../server/resources/helix/ClusterAccessor.java    |  38 ++++++
 .../server/resources/helix/InstancesAccessor.java  |   2 +
 .../rest/server/resources/helix/JobAccessor.java   |   2 +
 .../server/resources/helix/MetadataAccessor.java   |   2 +
 .../resources/helix/PerInstanceAccessor.java       |   2 +
 .../resources/helix/PropertyStoreAccessor.java     |   2 +
 .../server/resources/helix/ResourceAccessor.java   |   2 +
 .../helix/ResourceAssignmentOptimizerAccessor.java |   2 +
 .../rest/server/resources/helix/TaskAccessor.java  |   2 +
 .../server/resources/helix/WorkflowAccessor.java   |   2 +
 .../resources/metadata/NamespacesAccessor.java     |   2 +
 .../MetadataStoreDirectoryAccessor.java            |   2 +
 .../resources/zookeeper/ZooKeeperAccessor.java     |   2 +
 .../helix/rest/server/TestAuthValidator.java       | 143 +++++++++++++++++++++
 21 files changed, 443 insertions(+)

diff --git a/helix-rest/src/main/java/org/apache/helix/rest/server/HelixRestServer.java b/helix-rest/src/main/java/org/apache/helix/rest/server/HelixRestServer.java
index 8137739..b72f535 100644
--- a/helix-rest/src/main/java/org/apache/helix/rest/server/HelixRestServer.java
+++ b/helix-rest/src/main/java/org/apache/helix/rest/server/HelixRestServer.java
@@ -40,8 +40,12 @@ import org.apache.helix.rest.common.ContextPropertyKeys;
 import org.apache.helix.rest.common.HelixRestNamespace;
 import org.apache.helix.rest.common.ServletType;
 import org.apache.helix.rest.server.auditlog.AuditLogger;
+import org.apache.helix.rest.server.authValidator.AuthValidator;
+import org.apache.helix.rest.server.authValidator.NoopAuthValidator;
 import org.apache.helix.rest.server.filters.AuditLogFilter;
 import org.apache.helix.rest.server.filters.CORSFilter;
+import org.apache.helix.rest.server.filters.ClusterAuthFilter;
+import org.apache.helix.rest.server.filters.NamespaceAuthFilter;
 import org.eclipse.jetty.http.HttpVersion;
 import org.eclipse.jetty.server.HttpConfiguration;
 import org.eclipse.jetty.server.HttpConnectionFactory;
@@ -57,6 +61,7 @@ import org.glassfish.jersey.servlet.ServletContainer;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+
 public class HelixRestServer {
   private static Logger LOG = LoggerFactory.getLogger(HelixRestServer.class);
 
@@ -73,6 +78,8 @@ public class HelixRestServer {
   private List<HelixRestNamespace> _helixNamespaces;
   private ServletContextHandler _servletContextHandler;
   private List<AuditLogger> _auditLoggers;
+  private AuthValidator _clusterAuthValidator;
+  private AuthValidator _namespaceAuthValidator;
 
   // Key is name of namespace, value of the resource config of that namespace
   private Map<String, ResourceConfig> _resourceConfigMap;
@@ -94,8 +101,21 @@ public class HelixRestServer {
     init(namespaces, port, urlPrefix, auditLoggers);
   }
 
+  public HelixRestServer(List<HelixRestNamespace> namespaces, int port, String urlPrefix,
+      List<AuditLogger> auditLoggers, AuthValidator clusterAuthValidator,
+      AuthValidator namespaceAuthValidator) {
+    init(namespaces, port, urlPrefix, auditLoggers, clusterAuthValidator, namespaceAuthValidator);
+  }
+
   private void init(List<HelixRestNamespace> namespaces, int port, String urlPrefix,
       List<AuditLogger> auditLoggers) {
+    init(namespaces, port, urlPrefix, auditLoggers, new NoopAuthValidator(),
+        new NoopAuthValidator());
+  }
+
+  private void init(List<HelixRestNamespace> namespaces, int port, String urlPrefix,
+      List<AuditLogger> auditLoggers, AuthValidator clusterAuthValidator,
+      AuthValidator namespaceAuthValidator) {
     if (namespaces.size() == 0) {
       throw new IllegalArgumentException(
           "No namespace specified! Please provide ZOOKEEPER address or namespace manifest.");
@@ -108,6 +128,8 @@ public class HelixRestServer {
     _resourceConfigMap = new HashMap<>();
     _servletContextHandler = new ServletContextHandler(_server, _urlPrefix);
     _helixNamespaces = namespaces;
+    _clusterAuthValidator = clusterAuthValidator;
+    _namespaceAuthValidator = namespaceAuthValidator;
 
     // Initialize all namespaces.
     // If there is not a default namespace (namespace.isDefault() is false),
@@ -175,6 +197,8 @@ public class HelixRestServer {
       cfg.register(new CORSFilter());
     }
     cfg.register(new AuditLogFilter(_auditLoggers));
+    cfg.register(new ClusterAuthFilter(_clusterAuthValidator));
+    cfg.register(new NamespaceAuthFilter(_namespaceAuthValidator));
     return cfg;
   }
 
diff --git a/helix-rest/src/main/java/org/apache/helix/rest/server/authValidator/AuthValidator.java b/helix-rest/src/main/java/org/apache/helix/rest/server/authValidator/AuthValidator.java
new file mode 100644
index 0000000..801540e
--- /dev/null
+++ b/helix-rest/src/main/java/org/apache/helix/rest/server/authValidator/AuthValidator.java
@@ -0,0 +1,27 @@
+package org.apache.helix.rest.server.authValidator;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import javax.ws.rs.container.ContainerRequestContext;
+
+
+public interface AuthValidator {
+  boolean validate(ContainerRequestContext request);
+}
diff --git a/helix-rest/src/main/java/org/apache/helix/rest/server/authValidator/NoopAuthValidator.java b/helix-rest/src/main/java/org/apache/helix/rest/server/authValidator/NoopAuthValidator.java
new file mode 100644
index 0000000..4fc6e8c
--- /dev/null
+++ b/helix-rest/src/main/java/org/apache/helix/rest/server/authValidator/NoopAuthValidator.java
@@ -0,0 +1,29 @@
+package org.apache.helix.rest.server.authValidator;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import javax.ws.rs.container.ContainerRequestContext;
+
+
+public class NoopAuthValidator implements AuthValidator {
+  public boolean validate(ContainerRequestContext request) {
+    return true;
+  }
+}
diff --git a/helix-rest/src/main/java/org/apache/helix/rest/server/filters/ClusterAuth.java b/helix-rest/src/main/java/org/apache/helix/rest/server/filters/ClusterAuth.java
new file mode 100644
index 0000000..e807de6
--- /dev/null
+++ b/helix-rest/src/main/java/org/apache/helix/rest/server/filters/ClusterAuth.java
@@ -0,0 +1,33 @@
+package org.apache.helix.rest.server.filters;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import javax.ws.rs.NameBinding;
+
+
+@NameBinding
+@Target({ElementType.TYPE, ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface ClusterAuth {
+}
diff --git a/helix-rest/src/main/java/org/apache/helix/rest/server/filters/ClusterAuthFilter.java b/helix-rest/src/main/java/org/apache/helix/rest/server/filters/ClusterAuthFilter.java
new file mode 100644
index 0000000..4956ec9
--- /dev/null
+++ b/helix-rest/src/main/java/org/apache/helix/rest/server/filters/ClusterAuthFilter.java
@@ -0,0 +1,46 @@
+package org.apache.helix.rest.server.filters;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.Provider;
+
+import org.apache.helix.rest.server.authValidator.AuthValidator;
+
+
+@ClusterAuth
+@Provider
+public class ClusterAuthFilter implements ContainerRequestFilter {
+
+  AuthValidator _authValidator;
+
+  public ClusterAuthFilter(AuthValidator authValidator) {
+    _authValidator = authValidator;
+  }
+
+  @Override
+  public void filter(ContainerRequestContext request) {
+    if (!_authValidator.validate(request)) {
+      request.abortWith(Response.status(Response.Status.FORBIDDEN).build());
+    }
+  }
+}
diff --git a/helix-rest/src/main/java/org/apache/helix/rest/server/filters/NamespaceAuth.java b/helix-rest/src/main/java/org/apache/helix/rest/server/filters/NamespaceAuth.java
new file mode 100644
index 0000000..003bcb9
--- /dev/null
+++ b/helix-rest/src/main/java/org/apache/helix/rest/server/filters/NamespaceAuth.java
@@ -0,0 +1,33 @@
+package org.apache.helix.rest.server.filters;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import javax.ws.rs.NameBinding;
+
+
+@NameBinding
+@Target({ElementType.TYPE, ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface NamespaceAuth {
+}
diff --git a/helix-rest/src/main/java/org/apache/helix/rest/server/filters/NamespaceAuthFilter.java b/helix-rest/src/main/java/org/apache/helix/rest/server/filters/NamespaceAuthFilter.java
new file mode 100644
index 0000000..818c12f
--- /dev/null
+++ b/helix-rest/src/main/java/org/apache/helix/rest/server/filters/NamespaceAuthFilter.java
@@ -0,0 +1,46 @@
+package org.apache.helix.rest.server.filters;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.Provider;
+
+import org.apache.helix.rest.server.authValidator.AuthValidator;
+
+
+@NamespaceAuth
+@Provider
+public class NamespaceAuthFilter implements ContainerRequestFilter {
+
+  AuthValidator _authValidator;
+
+  public NamespaceAuthFilter(AuthValidator authValidator) {
+    _authValidator = authValidator;
+  }
+
+  @Override
+  public void filter(ContainerRequestContext request) {
+    if (!_authValidator.validate(request)) {
+      request.abortWith(Response.status(Response.Status.FORBIDDEN).build());
+    }
+  }
+}
diff --git a/helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/ClusterAccessor.java b/helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/ClusterAccessor.java
index daea45f..6c14207 100644
--- a/helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/ClusterAccessor.java
+++ b/helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/ClusterAccessor.java
@@ -67,6 +67,8 @@ import org.apache.helix.model.RESTConfig;
 import org.apache.helix.model.StateModelDefinition;
 import org.apache.helix.model.builder.HelixConfigScopeBuilder;
 import org.apache.helix.rest.common.HttpConstants;
+import org.apache.helix.rest.server.filters.ClusterAuth;
+import org.apache.helix.rest.server.filters.NamespaceAuth;
 import org.apache.helix.rest.server.json.cluster.ClusterTopology;
 import org.apache.helix.rest.server.service.ClusterService;
 import org.apache.helix.rest.server.service.ClusterServiceImpl;
@@ -98,6 +100,7 @@ public class ClusterAccessor extends AbstractHelixResource {
     clusterName
   }
 
+  @NamespaceAuth
   @ResponseMetered(name = HttpConstants.READ_REQUEST)
   @Timed(name = HttpConstants.READ_REQUEST)
   @GET
@@ -111,6 +114,7 @@ public class ClusterAccessor extends AbstractHelixResource {
     return JSONRepresentation(dataMap);
   }
 
+  @ClusterAuth
   @ResponseMetered(name = HttpConstants.READ_REQUEST)
   @Timed(name = HttpConstants.READ_REQUEST)
   @GET
@@ -149,6 +153,7 @@ public class ClusterAccessor extends AbstractHelixResource {
     return JSONRepresentation(clusterInfo);
   }
 
+  @NamespaceAuth
   @ResponseMetered(name = HttpConstants.WRITE_REQUEST)
   @Timed(name = HttpConstants.WRITE_REQUEST)
   @PUT
@@ -185,6 +190,7 @@ public class ClusterAccessor extends AbstractHelixResource {
     return created();
   }
 
+  @NamespaceAuth
   @ResponseMetered(name = HttpConstants.WRITE_REQUEST)
   @Timed(name = HttpConstants.WRITE_REQUEST)
   @DELETE
@@ -206,6 +212,7 @@ public class ClusterAccessor extends AbstractHelixResource {
     return OK();
   }
 
+  @ClusterAuth
   @ResponseMetered(name = HttpConstants.WRITE_REQUEST)
   @Timed(name = HttpConstants.WRITE_REQUEST)
   @POST
@@ -330,6 +337,7 @@ public class ClusterAccessor extends AbstractHelixResource {
     service.addVirtualTopologyGroup(clusterId, customFieldsMap);
   }
 
+  @ClusterAuth
   @ResponseMetered(name = HttpConstants.READ_REQUEST)
   @Timed(name = HttpConstants.READ_REQUEST)
   @GET
@@ -404,6 +412,7 @@ public class ClusterAccessor extends AbstractHelixResource {
     return details;
   }
 
+  @ClusterAuth
   @ResponseMetered(name = HttpConstants.WRITE_REQUEST)
   @Timed(name = HttpConstants.WRITE_REQUEST)
   @POST
@@ -437,6 +446,7 @@ public class ClusterAccessor extends AbstractHelixResource {
     return JSONRepresentation(ImmutableMap.of("acknowledged", true));
   }
 
+  @ClusterAuth
   @ResponseMetered(name = HttpConstants.READ_REQUEST)
   @Timed(name = HttpConstants.READ_REQUEST)
   @GET
@@ -461,6 +471,7 @@ public class ClusterAccessor extends AbstractHelixResource {
   }
 
 
+  @ClusterAuth
   @ResponseMetered(name = HttpConstants.WRITE_REQUEST)
   @Timed(name = HttpConstants.WRITE_REQUEST)
   @PUT
@@ -492,6 +503,7 @@ public class ClusterAccessor extends AbstractHelixResource {
     return OK();
   }
 
+  @ClusterAuth
   @ResponseMetered(name = HttpConstants.WRITE_REQUEST)
   @Timed(name = HttpConstants.WRITE_REQUEST)
   @DELETE
@@ -514,6 +526,7 @@ public class ClusterAccessor extends AbstractHelixResource {
     return OK();
   }
 
+  @ClusterAuth
   @ResponseMetered(name = HttpConstants.READ_REQUEST)
   @Timed(name = HttpConstants.READ_REQUEST)
   @GET
@@ -534,6 +547,7 @@ public class ClusterAccessor extends AbstractHelixResource {
     return notFound();
   }
 
+  @ClusterAuth
   @ResponseMetered(name = HttpConstants.WRITE_REQUEST)
   @Timed(name = HttpConstants.WRITE_REQUEST)
   @POST
@@ -576,6 +590,7 @@ public class ClusterAccessor extends AbstractHelixResource {
   }
 
 
+  @ClusterAuth
   @ResponseMetered(name = HttpConstants.READ_REQUEST)
   @Timed(name = HttpConstants.READ_REQUEST)
   @GET
@@ -590,6 +605,7 @@ public class ClusterAccessor extends AbstractHelixResource {
     return OK(objectMapper.writeValueAsString(clusterTopology));
   }
 
+  @ClusterAuth
   @ResponseMetered(name = HttpConstants.READ_REQUEST)
   @Timed(name = HttpConstants.READ_REQUEST)
   @GET
@@ -605,6 +621,7 @@ public class ClusterAccessor extends AbstractHelixResource {
     return JSONRepresentation(topologyMap);
   }
 
+  @ClusterAuth
   @ResponseMetered(name = HttpConstants.READ_REQUEST)
   @Timed(name = HttpConstants.READ_REQUEST)
   @GET
@@ -620,6 +637,7 @@ public class ClusterAccessor extends AbstractHelixResource {
     return JSONRepresentation(faultZoneMap);
   }
 
+  @ClusterAuth
   @ResponseMetered(name = HttpConstants.WRITE_REQUEST)
   @Timed(name = HttpConstants.WRITE_REQUEST)
   @POST
@@ -674,6 +692,7 @@ public class ClusterAccessor extends AbstractHelixResource {
     return OK();
   }
 
+  @ClusterAuth
   @ResponseMetered(name = HttpConstants.READ_REQUEST)
   @Timed(name = HttpConstants.READ_REQUEST)
   @GET
@@ -694,6 +713,7 @@ public class ClusterAccessor extends AbstractHelixResource {
     return JSONRepresentation(controllerInfo);
   }
 
+  @ClusterAuth
   @ResponseMetered(name = HttpConstants.READ_REQUEST)
   @Timed(name = HttpConstants.READ_REQUEST)
   @GET
@@ -703,6 +723,7 @@ public class ClusterAccessor extends AbstractHelixResource {
         getControllerHistory(clusterId, ControllerHistory.HistoryType.CONTROLLER_LEADERSHIP));
   }
 
+  @ClusterAuth
   @ResponseMetered(name = HttpConstants.READ_REQUEST)
   @Timed(name = HttpConstants.READ_REQUEST)
   @GET
@@ -712,6 +733,7 @@ public class ClusterAccessor extends AbstractHelixResource {
         getControllerHistory(clusterId, ControllerHistory.HistoryType.MAINTENANCE));
   }
 
+  @ClusterAuth
   @ResponseMetered(name = HttpConstants.READ_REQUEST)
   @Timed(name = HttpConstants.READ_REQUEST)
   @GET
@@ -728,6 +750,7 @@ public class ClusterAccessor extends AbstractHelixResource {
     return notFound(String.format("Cluster %s is not in maintenance mode!", clusterId));
   }
 
+  @ClusterAuth
   @ResponseMetered(name = HttpConstants.READ_REQUEST)
   @Timed(name = HttpConstants.READ_REQUEST)
   @GET
@@ -746,6 +769,7 @@ public class ClusterAccessor extends AbstractHelixResource {
     return JSONRepresentation(controllerMessages);
   }
 
+  @ClusterAuth
   @ResponseMetered(name = HttpConstants.READ_REQUEST)
   @Timed(name = HttpConstants.READ_REQUEST)
   @GET
@@ -758,6 +782,7 @@ public class ClusterAccessor extends AbstractHelixResource {
     return JSONRepresentation(message.getRecord());
   }
 
+  @ClusterAuth
   @ResponseMetered(name = HttpConstants.READ_REQUEST)
   @Timed(name = HttpConstants.READ_REQUEST)
   @GET
@@ -774,6 +799,7 @@ public class ClusterAccessor extends AbstractHelixResource {
     return JSONRepresentation(clusterStateModelDefs);
   }
 
+  @ClusterAuth
   @ResponseMetered(name = HttpConstants.READ_REQUEST)
   @Timed(name = HttpConstants.READ_REQUEST)
   @GET
@@ -790,6 +816,7 @@ public class ClusterAccessor extends AbstractHelixResource {
     return JSONRepresentation(stateModelDef.getRecord());
   }
 
+  @ClusterAuth
   @ResponseMetered(name = HttpConstants.WRITE_REQUEST)
   @Timed(name = HttpConstants.WRITE_REQUEST)
   @PUT
@@ -815,6 +842,7 @@ public class ClusterAccessor extends AbstractHelixResource {
     return OK();
   }
 
+  @ClusterAuth
   @ResponseMetered(name = HttpConstants.WRITE_REQUEST)
   @Timed(name = HttpConstants.WRITE_REQUEST)
   @POST
@@ -844,6 +872,7 @@ public class ClusterAccessor extends AbstractHelixResource {
     return OK();
   }
 
+  @ClusterAuth
   @ResponseMetered(name = HttpConstants.WRITE_REQUEST)
   @Timed(name = HttpConstants.WRITE_REQUEST)
   @DELETE
@@ -870,6 +899,7 @@ public class ClusterAccessor extends AbstractHelixResource {
     return OK();
   }
 
+  @ClusterAuth
   @ResponseMetered(name = HttpConstants.WRITE_REQUEST)
   @Timed(name = HttpConstants.WRITE_REQUEST)
   @PUT
@@ -902,6 +932,7 @@ public class ClusterAccessor extends AbstractHelixResource {
     return OK();
   }
 
+  @ClusterAuth
   @ResponseMetered(name = HttpConstants.WRITE_REQUEST)
   @Timed(name = HttpConstants.WRITE_REQUEST)
   @POST
@@ -951,6 +982,7 @@ public class ClusterAccessor extends AbstractHelixResource {
     return OK();
   }
 
+  @ClusterAuth
   @ResponseMetered(name = HttpConstants.READ_REQUEST)
   @Timed(name = HttpConstants.READ_REQUEST)
   @GET
@@ -973,6 +1005,7 @@ public class ClusterAccessor extends AbstractHelixResource {
     return JSONRepresentation(config.getRecord());
   }
 
+  @ClusterAuth
   @ResponseMetered(name = HttpConstants.WRITE_REQUEST)
   @Timed(name = HttpConstants.WRITE_REQUEST)
   @DELETE
@@ -991,6 +1024,7 @@ public class ClusterAccessor extends AbstractHelixResource {
     return OK();
   }
 
+  @ClusterAuth
   @ResponseMetered(name = HttpConstants.READ_REQUEST)
   @Timed(name = HttpConstants.READ_REQUEST)
   @GET
@@ -1005,6 +1039,7 @@ public class ClusterAccessor extends AbstractHelixResource {
     return ZKUtil.isClusterSetup(cluster, zkClient);
   }
 
+  @ClusterAuth
   @ResponseMetered(name = HttpConstants.WRITE_REQUEST)
   @Timed(name = HttpConstants.WRITE_REQUEST)
   @PUT
@@ -1039,6 +1074,7 @@ public class ClusterAccessor extends AbstractHelixResource {
     return OK();
   }
 
+  @ClusterAuth
   @ResponseMetered(name = HttpConstants.READ_REQUEST)
   @Timed(name = HttpConstants.READ_REQUEST)
   @GET
@@ -1060,6 +1096,7 @@ public class ClusterAccessor extends AbstractHelixResource {
     return notFound();
   }
 
+  @ClusterAuth
   @ResponseMetered(name = HttpConstants.WRITE_REQUEST)
   @Timed(name = HttpConstants.WRITE_REQUEST)
   @DELETE
@@ -1070,6 +1107,7 @@ public class ClusterAccessor extends AbstractHelixResource {
     return OK();
   }
 
+  @ClusterAuth
   @ResponseMetered(name = HttpConstants.WRITE_REQUEST)
   @Timed(name = HttpConstants.WRITE_REQUEST)
   @POST
diff --git a/helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/InstancesAccessor.java b/helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/InstancesAccessor.java
index 93537e3..fefe5c3 100644
--- a/helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/InstancesAccessor.java
+++ b/helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/InstancesAccessor.java
@@ -49,6 +49,7 @@ import org.apache.helix.model.ClusterConfig;
 import org.apache.helix.model.InstanceConfig;
 import org.apache.helix.rest.clusterMaintenanceService.MaintenanceManagementService;
 import org.apache.helix.rest.common.HttpConstants;
+import org.apache.helix.rest.server.filters.ClusterAuth;
 import org.apache.helix.rest.server.json.cluster.ClusterTopology;
 import org.apache.helix.rest.server.json.instance.StoppableCheck;
 import org.apache.helix.rest.server.resources.exceptions.HelixHealthException;
@@ -57,6 +58,7 @@ import org.apache.helix.rest.server.service.ClusterServiceImpl;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+@ClusterAuth
 @Path("/clusters/{clusterId}/instances")
 public class InstancesAccessor extends AbstractHelixResource {
   private final static Logger _logger = LoggerFactory.getLogger(InstancesAccessor.class);
diff --git a/helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/JobAccessor.java b/helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/JobAccessor.java
index d6dc610..912a00a 100644
--- a/helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/JobAccessor.java
+++ b/helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/JobAccessor.java
@@ -42,6 +42,7 @@ import com.fasterxml.jackson.databind.node.JsonNodeFactory;
 import com.fasterxml.jackson.databind.node.ObjectNode;
 import org.apache.helix.HelixException;
 import org.apache.helix.rest.common.HttpConstants;
+import org.apache.helix.rest.server.filters.ClusterAuth;
 import org.apache.helix.task.JobConfig;
 import org.apache.helix.task.JobContext;
 import org.apache.helix.task.TaskConfig;
@@ -52,6 +53,7 @@ import org.apache.helix.zookeeper.zkclient.exception.ZkNoNodeException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+@ClusterAuth
 @Path("/clusters/{clusterId}/workflows/{workflowName}/jobs")
 public class JobAccessor extends AbstractHelixResource {
   private static Logger _logger = LoggerFactory.getLogger(JobAccessor.class.getName());
diff --git a/helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/MetadataAccessor.java b/helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/MetadataAccessor.java
index 40b61d9..d9cc16b 100644
--- a/helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/MetadataAccessor.java
+++ b/helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/MetadataAccessor.java
@@ -29,8 +29,10 @@ import org.apache.helix.rest.common.ContextPropertyKeys;
 import org.apache.helix.rest.common.HelixRestNamespace;
 import org.apache.helix.rest.common.HelixRestUtils;
 import org.apache.helix.rest.common.HttpConstants;
+import org.apache.helix.rest.server.filters.NamespaceAuth;
 import org.apache.helix.rest.server.resources.AbstractResource;
 
+@NamespaceAuth
 @Path("")
 public class MetadataAccessor extends AbstractResource {
   @ResponseMetered(name = HttpConstants.READ_REQUEST)
diff --git a/helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/PerInstanceAccessor.java b/helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/PerInstanceAccessor.java
index 3fd284e..f9d171e 100644
--- a/helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/PerInstanceAccessor.java
+++ b/helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/PerInstanceAccessor.java
@@ -61,6 +61,7 @@ import org.apache.helix.model.builder.HelixConfigScopeBuilder;
 import org.apache.helix.rest.clusterMaintenanceService.HealthCheck;
 import org.apache.helix.rest.clusterMaintenanceService.MaintenanceManagementService;
 import org.apache.helix.rest.common.HttpConstants;
+import org.apache.helix.rest.server.filters.ClusterAuth;
 import org.apache.helix.rest.server.json.instance.InstanceInfo;
 import org.apache.helix.rest.server.json.instance.StoppableCheck;
 import org.apache.helix.zookeeper.datamodel.ZNRecord;
@@ -68,6 +69,7 @@ import org.eclipse.jetty.util.StringUtil;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+@ClusterAuth
 @Path("/clusters/{clusterId}/instances/{instanceName}")
 public class PerInstanceAccessor extends AbstractHelixResource {
   private final static Logger LOG = LoggerFactory.getLogger(PerInstanceAccessor.class);
diff --git a/helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/PropertyStoreAccessor.java b/helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/PropertyStoreAccessor.java
index 3dbb273..7fafcf1 100644
--- a/helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/PropertyStoreAccessor.java
+++ b/helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/PropertyStoreAccessor.java
@@ -38,12 +38,14 @@ import org.apache.helix.BaseDataAccessor;
 import org.apache.helix.PropertyPathBuilder;
 import org.apache.helix.msdcommon.util.ZkValidationUtil;
 import org.apache.helix.rest.common.HttpConstants;
+import org.apache.helix.rest.server.filters.ClusterAuth;
 import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.apache.helix.zookeeper.datamodel.serializer.ZNRecordSerializer;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 
+@ClusterAuth
 @Path("/clusters/{clusterId}/propertyStore")
 public class PropertyStoreAccessor extends AbstractHelixResource {
   private static Logger LOG = LoggerFactory.getLogger(PropertyStoreAccessor.class);
diff --git a/helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/ResourceAccessor.java b/helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/ResourceAccessor.java
index ec63076..cc49885 100644
--- a/helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/ResourceAccessor.java
+++ b/helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/ResourceAccessor.java
@@ -55,11 +55,13 @@ import org.apache.helix.model.ResourceConfig;
 import org.apache.helix.model.StateModelDefinition;
 import org.apache.helix.model.builder.HelixConfigScopeBuilder;
 import org.apache.helix.rest.common.HttpConstants;
+import org.apache.helix.rest.server.filters.ClusterAuth;
 import org.apache.helix.zookeeper.api.client.RealmAwareZkClient;
 import org.apache.helix.zookeeper.datamodel.ZNRecord;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+@ClusterAuth
 @Path("/clusters/{clusterId}/resources")
 public class ResourceAccessor extends AbstractHelixResource {
   private final static Logger _logger = LoggerFactory.getLogger(ResourceAccessor.class);
diff --git a/helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/ResourceAssignmentOptimizerAccessor.java b/helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/ResourceAssignmentOptimizerAccessor.java
index 0ecae71..9324523 100644
--- a/helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/ResourceAssignmentOptimizerAccessor.java
+++ b/helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/ResourceAssignmentOptimizerAccessor.java
@@ -54,11 +54,13 @@ import org.apache.helix.model.InstanceConfig;
 import org.apache.helix.model.ResourceAssignment;
 import org.apache.helix.model.ResourceConfig;
 import org.apache.helix.rest.common.HttpConstants;
+import org.apache.helix.rest.server.filters.ClusterAuth;
 import org.apache.helix.util.HelixUtil;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 
+@ClusterAuth
 @Path("/clusters/{clusterId}/partitionAssignment")
 public class ResourceAssignmentOptimizerAccessor extends AbstractHelixResource {
   private static Logger LOG = LoggerFactory.getLogger(
diff --git a/helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/TaskAccessor.java b/helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/TaskAccessor.java
index a8a7b74..70f17dd 100644
--- a/helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/TaskAccessor.java
+++ b/helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/TaskAccessor.java
@@ -33,11 +33,13 @@ import com.codahale.metrics.annotation.ResponseMetered;
 import com.codahale.metrics.annotation.Timed;
 import com.fasterxml.jackson.core.type.TypeReference;
 import org.apache.helix.rest.common.HttpConstants;
+import org.apache.helix.rest.server.filters.ClusterAuth;
 import org.apache.helix.task.TaskDriver;
 import org.apache.helix.zookeeper.zkclient.exception.ZkNoNodeException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+@ClusterAuth
 @Path("/clusters/{clusterId}/workflows/{workflowName}/jobs/{jobName}/tasks")
 public class TaskAccessor extends AbstractHelixResource {
   private static Logger _logger = LoggerFactory.getLogger(TaskAccessor.class.getName());
diff --git a/helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/WorkflowAccessor.java b/helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/WorkflowAccessor.java
index c2929a2..ddfa31e 100644
--- a/helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/WorkflowAccessor.java
+++ b/helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/WorkflowAccessor.java
@@ -47,6 +47,7 @@ import com.fasterxml.jackson.databind.node.TextNode;
 import com.fasterxml.jackson.databind.type.TypeFactory;
 import org.apache.helix.HelixException;
 import org.apache.helix.rest.common.HttpConstants;
+import org.apache.helix.rest.server.filters.ClusterAuth;
 import org.apache.helix.task.JobConfig;
 import org.apache.helix.task.JobDag;
 import org.apache.helix.task.JobQueue;
@@ -59,6 +60,7 @@ import org.apache.helix.zookeeper.zkclient.exception.ZkNoNodeException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+@ClusterAuth
 @Path("/clusters/{clusterId}/workflows")
 public class WorkflowAccessor extends AbstractHelixResource {
   private static Logger _logger = LoggerFactory.getLogger(WorkflowAccessor.class.getName());
diff --git a/helix-rest/src/main/java/org/apache/helix/rest/server/resources/metadata/NamespacesAccessor.java b/helix-rest/src/main/java/org/apache/helix/rest/server/resources/metadata/NamespacesAccessor.java
index 9ae0c19..0d29ab2 100644
--- a/helix-rest/src/main/java/org/apache/helix/rest/server/resources/metadata/NamespacesAccessor.java
+++ b/helix-rest/src/main/java/org/apache/helix/rest/server/resources/metadata/NamespacesAccessor.java
@@ -31,9 +31,11 @@ import com.codahale.metrics.annotation.Timed;
 import org.apache.helix.rest.common.ContextPropertyKeys;
 import org.apache.helix.rest.common.HelixRestNamespace;
 import org.apache.helix.rest.common.HttpConstants;
+import org.apache.helix.rest.server.filters.NamespaceAuth;
 import org.apache.helix.rest.server.resources.AbstractResource;
 
 
+@NamespaceAuth
 @Path("/namespaces")
 public class NamespacesAccessor extends AbstractResource {
   @ResponseMetered(name = HttpConstants.READ_REQUEST)
diff --git a/helix-rest/src/main/java/org/apache/helix/rest/server/resources/metadatastore/MetadataStoreDirectoryAccessor.java b/helix-rest/src/main/java/org/apache/helix/rest/server/resources/metadatastore/MetadataStoreDirectoryAccessor.java
index 781e723..4687f8b 100644
--- a/helix-rest/src/main/java/org/apache/helix/rest/server/resources/metadatastore/MetadataStoreDirectoryAccessor.java
+++ b/helix-rest/src/main/java/org/apache/helix/rest/server/resources/metadatastore/MetadataStoreDirectoryAccessor.java
@@ -53,6 +53,7 @@ import org.apache.helix.rest.metadatastore.MetadataStoreDirectory;
 import org.apache.helix.rest.metadatastore.ZkMetadataStoreDirectory;
 import org.apache.helix.rest.metadatastore.datamodel.MetadataStoreShardingKey;
 import org.apache.helix.rest.metadatastore.datamodel.MetadataStoreShardingKeysByRealm;
+import org.apache.helix.rest.server.filters.NamespaceAuth;
 import org.apache.helix.rest.server.resources.AbstractResource;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -62,6 +63,7 @@ import org.slf4j.LoggerFactory;
  * Provides REST endpoints for accessing metadata store directory service,
  * which responds to read/write requests of metadata store realms, sharding keys, etc..
  */
+@NamespaceAuth
 @Path("")
 public class MetadataStoreDirectoryAccessor extends AbstractResource {
   private static final Logger LOG = LoggerFactory.getLogger(MetadataStoreDirectoryAccessor.class);
diff --git a/helix-rest/src/main/java/org/apache/helix/rest/server/resources/zookeeper/ZooKeeperAccessor.java b/helix-rest/src/main/java/org/apache/helix/rest/server/resources/zookeeper/ZooKeeperAccessor.java
index 4450017..762d603 100644
--- a/helix-rest/src/main/java/org/apache/helix/rest/server/resources/zookeeper/ZooKeeperAccessor.java
+++ b/helix-rest/src/main/java/org/apache/helix/rest/server/resources/zookeeper/ZooKeeperAccessor.java
@@ -40,6 +40,7 @@ import org.apache.helix.msdcommon.util.ZkValidationUtil;
 import org.apache.helix.rest.common.ContextPropertyKeys;
 import org.apache.helix.rest.common.HttpConstants;
 import org.apache.helix.rest.server.ServerContext;
+import org.apache.helix.rest.server.filters.NamespaceAuth;
 import org.apache.helix.rest.server.resources.AbstractResource;
 import org.apache.zookeeper.data.Stat;
 import org.slf4j.Logger;
@@ -50,6 +51,7 @@ import org.slf4j.LoggerFactory;
  * ZooKeeperAccessor provides methods for accessing ZooKeeper resources (ZNodes).
  * It provides basic ZooKeeper features supported by ZkClient.
  */
+@NamespaceAuth
 @Path("/zookeeper{path: /.+}")
 public class ZooKeeperAccessor extends AbstractResource {
   private static final Logger LOG = LoggerFactory.getLogger(ZooKeeperAccessor.class.getName());
diff --git a/helix-rest/src/test/java/org/apache/helix/rest/server/TestAuthValidator.java b/helix-rest/src/test/java/org/apache/helix/rest/server/TestAuthValidator.java
new file mode 100644
index 0000000..bbc0e60
--- /dev/null
+++ b/helix-rest/src/test/java/org/apache/helix/rest/server/TestAuthValidator.java
@@ -0,0 +1,143 @@
+package org.apache.helix.rest.server;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import org.apache.helix.rest.common.HelixRestNamespace;
+import org.apache.helix.rest.common.HttpConstants;
+import org.apache.helix.rest.server.authValidator.AuthValidator;
+import org.apache.helix.rest.server.resources.helix.ClusterAccessor;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.methods.HttpDelete;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPut;
+import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.http.entity.ContentType;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.mockito.Mockito;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.when;
+
+
+public class TestAuthValidator extends AbstractTestClass {
+  private String _mockBaseUri;
+  private CloseableHttpClient _httpClient;
+
+  @Test
+  public void testDefaultAuthValidator() throws JsonProcessingException {
+    String testClusterName = "testDefaultAuthValidator";
+    put("clusters/" + testClusterName, null, Entity.entity("", MediaType.APPLICATION_JSON_TYPE),
+        Response.Status.CREATED.getStatusCode());
+    String body = get("clusters/", null, Response.Status.OK.getStatusCode(), true);
+    JsonNode node = OBJECT_MAPPER.readTree(body);
+    String clustersStr = node.get(ClusterAccessor.ClusterProperties.clusters.name()).toString();
+    Assert.assertTrue(clustersStr.contains(testClusterName));
+  }
+
+  @Test(dependsOnMethods = "testDefaultAuthValidator")
+  public void testCustomAuthValidator() throws IOException, InterruptedException {
+    String testClusterName = "testCustomAuthValidator";
+    int newPort = getBaseUri().getPort() + 1;
+
+    // Start a second server for testing Distributed Leader Election for writes
+    _mockBaseUri = HttpConstants.HTTP_PROTOCOL_PREFIX + getBaseUri().getHost() + ":" + newPort;
+    _httpClient = HttpClients.createDefault();
+
+    AuthValidator mockAuthValidatorPass = Mockito.mock(AuthValidator.class);
+    when(mockAuthValidatorPass.validate(any())).thenReturn(true);
+    AuthValidator mockAuthValidatorReject = Mockito.mock(AuthValidator.class);
+    when(mockAuthValidatorReject.validate(any())).thenReturn(false);
+
+    List<HelixRestNamespace> namespaces = new ArrayList<>();
+    namespaces.add(new HelixRestNamespace(HelixRestNamespace.DEFAULT_NAMESPACE_NAME,
+        HelixRestNamespace.HelixMetadataStoreType.ZOOKEEPER, ZK_ADDR, true));
+
+    // Create a server that allows operations based on namespace auth and rejects operations based
+    // on cluster auth
+    HelixRestServer server =
+        new HelixRestServer(namespaces, newPort, getBaseUri().getPath(), Collections.emptyList(),
+            mockAuthValidatorReject, mockAuthValidatorPass);
+    server.start();
+
+    HttpUriRequest request =
+        buildRequest("/clusters/" + testClusterName, HttpConstants.RestVerbs.PUT, "");
+    sendRequestAndValidate(request, Response.Status.CREATED.getStatusCode());
+    request = buildRequest("/clusters/" + testClusterName, HttpConstants.RestVerbs.GET, "");
+    sendRequestAndValidate(request, Response.Status.FORBIDDEN.getStatusCode());
+
+    server.shutdown();
+    _httpClient.close();
+
+    // Create a server that rejects operations based on namespace auth and allows operations based
+    // on cluster auth
+    server =
+        new HelixRestServer(namespaces, newPort, getBaseUri().getPath(), Collections.emptyList(),
+            mockAuthValidatorPass, mockAuthValidatorReject);
+    server.start();
+    _httpClient = HttpClients.createDefault();
+
+    request = buildRequest("/clusters/" + testClusterName, HttpConstants.RestVerbs.GET, "");
+    sendRequestAndValidate(request, Response.Status.OK.getStatusCode());
+    request = buildRequest("/clusters", HttpConstants.RestVerbs.GET, "");
+    sendRequestAndValidate(request, Response.Status.FORBIDDEN.getStatusCode());
+
+    server.shutdown();
+    _httpClient.close();
+  }
+
+  private HttpUriRequest buildRequest(String urlSuffix, HttpConstants.RestVerbs requestMethod,
+      String jsonEntity) {
+    String url = _mockBaseUri + urlSuffix;
+    switch (requestMethod) {
+      case PUT:
+        HttpPut httpPut = new HttpPut(url);
+        httpPut.setEntity(new StringEntity(jsonEntity, ContentType.APPLICATION_JSON));
+        return httpPut;
+      case DELETE:
+        return new HttpDelete(url);
+      case GET:
+        return new HttpGet(url);
+      default:
+        throw new IllegalArgumentException("Unsupported requestMethod: " + requestMethod);
+    }
+  }
+
+  private void sendRequestAndValidate(HttpUriRequest request, int expectedResponseCode)
+      throws IllegalArgumentException, IOException {
+    HttpResponse response = _httpClient.execute(request);
+    Assert.assertEquals(response.getStatusLine().getStatusCode(), expectedResponseCode);
+  }
+
+
+}