Skip to content
Permalink
Browse files
Add AclRegister interfaces and ClusterAccessor integration (#2153)
Add AclRegister interfaces and ClusterAccessor integration
  • Loading branch information
NealSun96 committed Jun 21, 2022
1 parent 8a19aa4 commit b4f8bd52ffad0154cb0967e0817df134f8a39e0b
Show file tree
Hide file tree
Showing 7 changed files with 208 additions and 7 deletions.
@@ -0,0 +1,28 @@
package org.apache.helix.rest.acl;

/*
* 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.servlet.http.HttpServletRequest;


public interface AclRegister {
// Create an ACL entry based on the request
void createACL(HttpServletRequest request);
}
@@ -0,0 +1,30 @@
package org.apache.helix.rest.acl;

/*
* 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.servlet.http.HttpServletRequest;


public class NoopAclRegister implements AclRegister {

public void createACL(HttpServletRequest request) {

}
}
@@ -22,5 +22,6 @@
public enum ContextPropertyKeys {
SERVER_CONTEXT,
METADATA,
ALL_NAMESPACES
ALL_NAMESPACES,
ACL_REGISTER,
}
@@ -36,6 +36,8 @@
import com.codahale.metrics.jersey2.InstrumentedResourceMethodApplicationListener;
import com.codahale.metrics.jmx.JmxReporter;
import org.apache.helix.HelixException;
import org.apache.helix.rest.acl.AclRegister;
import org.apache.helix.rest.acl.NoopAclRegister;
import org.apache.helix.rest.common.ContextPropertyKeys;
import org.apache.helix.rest.common.HelixRestNamespace;
import org.apache.helix.rest.common.ServletType;
@@ -80,6 +82,7 @@ public class HelixRestServer {
private List<AuditLogger> _auditLoggers;
private AuthValidator _clusterAuthValidator;
private AuthValidator _namespaceAuthValidator;
private AclRegister _aclRegister;

// Key is name of namespace, value of the resource config of that namespace
private Map<String, ResourceConfig> _resourceConfigMap;
@@ -103,19 +106,20 @@ public HelixRestServer(List<HelixRestNamespace> namespaces, int port, String url

public HelixRestServer(List<HelixRestNamespace> namespaces, int port, String urlPrefix,
List<AuditLogger> auditLoggers, AuthValidator clusterAuthValidator,
AuthValidator namespaceAuthValidator) {
init(namespaces, port, urlPrefix, auditLoggers, clusterAuthValidator, namespaceAuthValidator);
AuthValidator namespaceAuthValidator, AclRegister aclRegister) {
init(namespaces, port, urlPrefix, auditLoggers, clusterAuthValidator, namespaceAuthValidator,
aclRegister);
}

private void init(List<HelixRestNamespace> namespaces, int port, String urlPrefix,
List<AuditLogger> auditLoggers) {
init(namespaces, port, urlPrefix, auditLoggers, new NoopAuthValidator(),
new NoopAuthValidator());
new NoopAuthValidator(), new NoopAclRegister());
}

private void init(List<HelixRestNamespace> namespaces, int port, String urlPrefix,
List<AuditLogger> auditLoggers, AuthValidator clusterAuthValidator,
AuthValidator namespaceAuthValidator) {
AuthValidator namespaceAuthValidator, AclRegister aclRegister) {
if (namespaces.size() == 0) {
throw new IllegalArgumentException(
"No namespace specified! Please provide ZOOKEEPER address or namespace manifest.");
@@ -130,6 +134,7 @@ private void init(List<HelixRestNamespace> namespaces, int port, String urlPrefi
_helixNamespaces = namespaces;
_clusterAuthValidator = clusterAuthValidator;
_namespaceAuthValidator = namespaceAuthValidator;
_aclRegister = aclRegister;

// Initialize all namespaces.
// If there is not a default namespace (namespace.isDefault() is false),
@@ -191,6 +196,7 @@ protected ResourceConfig getResourceConfig(HelixRestNamespace namespace, Servlet
cfg.property(ContextPropertyKeys.ALL_NAMESPACES.name(), _helixNamespaces);
}
cfg.property(ContextPropertyKeys.METADATA.name(), namespace);
cfg.property(ContextPropertyKeys.ACL_REGISTER.name(), _aclRegister);

if (Boolean.getBoolean(CORS_ENABLED)) {
// NOTE: CORS is disabled by default unless otherwise specified in System Properties.
@@ -66,6 +66,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.acl.AclRegister;
import org.apache.helix.rest.common.ContextPropertyKeys;
import org.apache.helix.rest.common.HttpConstants;
import org.apache.helix.rest.server.filters.ClusterAuth;
import org.apache.helix.rest.server.filters.NamespaceAuth;
@@ -181,6 +183,13 @@ public Response createCluster(@PathParam("clusterId") String clusterId,
}
}

try {
getAclRegister().createACL(_servletRequest);
} catch (Exception ex) {
LOG.error("Failed to create ACL for cluster {}. Exception: {}.", clusterId, ex);
return serverError(ex);
}

try {
clusterSetup.addCluster(clusterId, recreateIfExists, cloudConfig);
} catch (Exception ex) {
@@ -1212,4 +1221,8 @@ private Map<String, Object> getControllerHistory(String clusterId,
}
return history;
}

private AclRegister getAclRegister() {
return (AclRegister) _application.getProperties().get(ContextPropertyKeys.ACL_REGISTER.name());
}
}
@@ -0,0 +1,122 @@
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 org.apache.helix.TestHelper;
import org.apache.helix.rest.acl.AclRegister;
import org.apache.helix.rest.common.HelixRestNamespace;
import org.apache.helix.rest.common.HttpConstants;
import org.apache.helix.rest.server.authValidator.NoopAuthValidator;
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.AfterClass;
import org.testng.annotations.Test;

import static org.mockito.ArgumentMatchers.any;


public class TestAclRegister extends AbstractTestClass {
private String _mockBaseUri;
private CloseableHttpClient _httpClient;

private static String CLASSNAME_TEST_DEFAULT_ACL_REGISTER = "testDefaultAclRegister";
private static String CLASSNAME_TEST_CUSTOM_ACL_REGISTER = "testCustomACLRegister";

@Test
public void testDefaultAclRegister() {
put("clusters/testCluster", null, Entity.entity("", MediaType.APPLICATION_JSON_TYPE),
Response.Status.CREATED.getStatusCode());
TestHelper.dropCluster("testCluster", _gZkClient);
}

@Test(dependsOnMethods = "testDefaultAclRegister")
public void testCustomACLRegister() throws IOException, InterruptedException {
int newPort = getBaseUri().getPort() + 1;

_mockBaseUri = HttpConstants.HTTP_PROTOCOL_PREFIX + getBaseUri().getHost() + ":" + newPort;
_httpClient = HttpClients.createDefault();

AclRegister mockAclRegister = Mockito.mock(AclRegister.class);
Mockito.doThrow(new RuntimeException()).when(mockAclRegister).createACL(any());

List<HelixRestNamespace> namespaces = new ArrayList<>();
namespaces.add(new HelixRestNamespace(HelixRestNamespace.DEFAULT_NAMESPACE_NAME,
HelixRestNamespace.HelixMetadataStoreType.ZOOKEEPER, ZK_ADDR, true));

// Create a server that passes acl resource creation
HelixRestServer server =
new HelixRestServer(namespaces, newPort, getBaseUri().getPath(), Collections.emptyList(),
new NoopAuthValidator(), new NoopAuthValidator(), mockAclRegister);
server.start();

HttpUriRequest request =
buildRequest("/clusters/testCluster", HttpConstants.RestVerbs.PUT, "");
sendRequestAndValidate(request, Response.Status.INTERNAL_SERVER_ERROR.getStatusCode());
request =
buildRequest("/clusters/testCluster", HttpConstants.RestVerbs.GET, "");
sendRequestAndValidate(request, Response.Status.NOT_FOUND.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);
}


}
@@ -30,6 +30,7 @@
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import org.apache.helix.TestHelper;
import org.apache.helix.rest.acl.NoopAclRegister;
import org.apache.helix.rest.common.HelixRestNamespace;
import org.apache.helix.rest.common.HttpConstants;
import org.apache.helix.rest.server.authValidator.AuthValidator;
@@ -96,7 +97,7 @@ public void testCustomAuthValidator() throws IOException, InterruptedException {
// on cluster auth
HelixRestServer server =
new HelixRestServer(namespaces, newPort, getBaseUri().getPath(), Collections.emptyList(),
mockAuthValidatorReject, mockAuthValidatorPass);
mockAuthValidatorReject, mockAuthValidatorPass, new NoopAclRegister());
server.start();

HttpUriRequest request =
@@ -112,7 +113,7 @@ public void testCustomAuthValidator() throws IOException, InterruptedException {
// on cluster auth
server =
new HelixRestServer(namespaces, newPort, getBaseUri().getPath(), Collections.emptyList(),
mockAuthValidatorPass, mockAuthValidatorReject);
mockAuthValidatorPass, mockAuthValidatorReject, new NoopAclRegister());
server.start();
_httpClient = HttpClients.createDefault();

0 comments on commit b4f8bd5

Please sign in to comment.