This is an automated email from the ASF dual-hosted git repository.
jackie pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pinot.git
The following commit(s) were added to refs/heads/master by this push:
new fed2d5f1b6 update access control check error handling to catch
throwable and log errors (#13209)
fed2d5f1b6 is described below
commit fed2d5f1b613371237b5a29348f0c043200671ad
Author: dang-stripe <[email protected]>
AuthorDate: Fri May 24 14:15:51 2024 -0700
update access control check error handling to catch throwable and log
errors (#13209)
---
.../controller/api/access/AccessControlUtils.java | 8 +-
.../api/access/AccessControlUtilsTest.java | 75 +++++++++++++++++
.../pinot/core/auth/FineGrainedAuthUtils.java | 19 ++++-
.../pinot/core/auth/FineGrainedAuthUtilsTest.java | 94 ++++++++++++++++++++++
4 files changed, 192 insertions(+), 4 deletions(-)
diff --git
a/pinot-controller/src/main/java/org/apache/pinot/controller/api/access/AccessControlUtils.java
b/pinot-controller/src/main/java/org/apache/pinot/controller/api/access/AccessControlUtils.java
index 4e56339a4c..825776e38f 100644
---
a/pinot-controller/src/main/java/org/apache/pinot/controller/api/access/AccessControlUtils.java
+++
b/pinot-controller/src/main/java/org/apache/pinot/controller/api/access/AccessControlUtils.java
@@ -67,9 +67,11 @@ public final class AccessControlUtils {
} catch (WebApplicationException exception) {
// throwing the exception if it's WebApplicationException
throw exception;
- } catch (Exception e) {
- throw new ControllerApplicationException(LOGGER, "Caught exception while
validating permission for "
- + userMessage, Response.Status.INTERNAL_SERVER_ERROR, e);
+ } catch (Throwable t) {
+ // catch and log Throwable for NoSuchMethodError which can happen when
there are classpath conflicts
+ // otherwise, grizzly will return a 500 without any logs or indication
of what failed
+ throw new ControllerApplicationException(LOGGER,
+ "Caught exception while validating permission for " + userMessage,
Response.Status.INTERNAL_SERVER_ERROR, t);
}
throw new ControllerApplicationException(LOGGER, "Permission is denied for
" + userMessage,
diff --git
a/pinot-controller/src/test/java/org/apache/pinot/controller/api/access/AccessControlUtilsTest.java
b/pinot-controller/src/test/java/org/apache/pinot/controller/api/access/AccessControlUtilsTest.java
new file mode 100644
index 0000000000..5270414d34
--- /dev/null
+++
b/pinot-controller/src/test/java/org/apache/pinot/controller/api/access/AccessControlUtilsTest.java
@@ -0,0 +1,75 @@
+/**
+ * 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.pinot.controller.api.access;
+
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.Response;
+import
org.apache.pinot.controller.api.exception.ControllerApplicationException;
+import org.mockito.Mockito;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+
+public class AccessControlUtilsTest {
+
+ private final String _table = "testTable";
+ private final String _endpoint = "/testEndpoint";
+
+ @Test
+ public void testValidatePermissionAllowed() {
+ AccessControl ac = Mockito.mock(AccessControl.class);
+ HttpHeaders mockHttpHeaders = Mockito.mock(HttpHeaders.class);
+
+ Mockito.when(ac.hasAccess(_table, AccessType.READ, mockHttpHeaders,
_endpoint)).thenReturn(true);
+
+ AccessControlUtils.validatePermission(_table, AccessType.READ,
mockHttpHeaders, _endpoint, ac);
+ }
+
+ @Test
+ public void testValidatePermissionDenied() {
+ AccessControl ac = Mockito.mock(AccessControl.class);
+ HttpHeaders mockHttpHeaders = Mockito.mock(HttpHeaders.class);
+
+ Mockito.when(ac.hasAccess(_table, AccessType.READ, mockHttpHeaders,
_endpoint)).thenReturn(false);
+
+ try {
+ AccessControlUtils.validatePermission(_table, AccessType.READ,
mockHttpHeaders, _endpoint, ac);
+ Assert.fail("Expected ControllerApplicationException");
+ } catch (ControllerApplicationException e) {
+ Assert.assertTrue(e.getMessage().contains("Permission is denied"));
+ Assert.assertEquals(e.getResponse().getStatus(),
Response.Status.FORBIDDEN.getStatusCode());
+ }
+ }
+
+ @Test
+ public void testValidatePermissionWithNoSuchMethodError() {
+ AccessControl ac = Mockito.mock(AccessControl.class);
+ HttpHeaders mockHttpHeaders = Mockito.mock(HttpHeaders.class);
+
+ Mockito.when(ac.hasAccess(_table, AccessType.READ, mockHttpHeaders,
_endpoint))
+ .thenThrow(new NoSuchMethodError("Method not found"));
+
+ try {
+ AccessControlUtils.validatePermission(_table, AccessType.READ,
mockHttpHeaders, _endpoint, ac);
+ } catch (ControllerApplicationException e) {
+ Assert.assertTrue(e.getMessage().contains("Caught exception while
validating permission"));
+ Assert.assertEquals(e.getResponse().getStatus(),
Response.Status.INTERNAL_SERVER_ERROR.getStatusCode());
+ }
+ }
+}
diff --git
a/pinot-core/src/main/java/org/apache/pinot/core/auth/FineGrainedAuthUtils.java
b/pinot-core/src/main/java/org/apache/pinot/core/auth/FineGrainedAuthUtils.java
index 4a93bc3f10..ba8854818c 100644
---
a/pinot-core/src/main/java/org/apache/pinot/core/auth/FineGrainedAuthUtils.java
+++
b/pinot-core/src/main/java/org/apache/pinot/core/auth/FineGrainedAuthUtils.java
@@ -28,12 +28,17 @@ import javax.ws.rs.core.UriInfo;
import org.apache.commons.lang.StringUtils;
import org.apache.pinot.common.utils.DatabaseUtils;
import org.apache.pinot.spi.utils.builder.TableNameBuilder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
* Utility methods to share in Broker and Controller request filters related
to fine grain authorization.
*/
public class FineGrainedAuthUtils {
+
+ private static final Logger LOGGER =
LoggerFactory.getLogger(FineGrainedAuthUtils.class);
+
private FineGrainedAuthUtils() {
}
@@ -106,8 +111,20 @@ public class FineGrainedAuthUtils {
Response.Status.INTERNAL_SERVER_ERROR);
}
+ boolean hasAccess;
+ try {
+ hasAccess = accessControl.hasAccess(httpHeaders, auth.targetType(),
targetId, auth.action());
+ } catch (Throwable t) {
+ // catch and log Throwable for NoSuchMethodError which can happen when
there are classpath conflicts
+ // otherwise, grizzly will return a 500 without any logs or indication
of what failed
+ String errorMsg = String.format("Failed to check for access for target
type %s and target ID %s with action %s",
+ auth.targetType(), targetId, auth.action());
+ LOGGER.error(errorMsg, t);
+ throw new WebApplicationException(errorMsg, t,
Response.Status.INTERNAL_SERVER_ERROR);
+ }
+
// Check for access now
- if (!accessControl.hasAccess(httpHeaders, auth.targetType(), targetId,
auth.action())) {
+ if (!hasAccess) {
throw new WebApplicationException(accessDeniedMsg,
Response.Status.FORBIDDEN);
}
} else if (!accessControl.defaultAccess(httpHeaders)) {
diff --git
a/pinot-core/src/test/java/org/apache/pinot/core/auth/FineGrainedAuthUtilsTest.java
b/pinot-core/src/test/java/org/apache/pinot/core/auth/FineGrainedAuthUtilsTest.java
new file mode 100644
index 0000000000..c18f30ecf6
--- /dev/null
+++
b/pinot-core/src/test/java/org/apache/pinot/core/auth/FineGrainedAuthUtilsTest.java
@@ -0,0 +1,94 @@
+/**
+ * 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.pinot.core.auth;
+
+import java.lang.reflect.Method;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+import org.mockito.Mockito;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+
+public class FineGrainedAuthUtilsTest {
+
+ @Test
+ public void testValidateFineGrainedAuthAllowed() {
+ FineGrainedAccessControl ac = Mockito.mock(FineGrainedAccessControl.class);
+ Mockito.when(ac.hasAccess(Mockito.any(HttpHeaders.class), Mockito.any(),
Mockito.any(), Mockito.any()))
+ .thenReturn(true);
+
+ UriInfo mockUriInfo = Mockito.mock(UriInfo.class);
+ HttpHeaders mockHttpHeaders = Mockito.mock(HttpHeaders.class);
+
+ FineGrainedAuthUtils.validateFineGrainedAuth(getAnnotatedMethod(),
mockUriInfo, mockHttpHeaders, ac);
+ }
+
+ @Test
+ public void testValidateFineGrainedAuthDenied() {
+ FineGrainedAccessControl ac = Mockito.mock(FineGrainedAccessControl.class);
+ Mockito.when(ac.hasAccess(Mockito.any(HttpHeaders.class), Mockito.any(),
Mockito.any(), Mockito.any()))
+ .thenReturn(false);
+
+ UriInfo mockUriInfo = Mockito.mock(UriInfo.class);
+ HttpHeaders mockHttpHeaders = Mockito.mock(HttpHeaders.class);
+
+ try {
+ FineGrainedAuthUtils.validateFineGrainedAuth(getAnnotatedMethod(),
mockUriInfo, mockHttpHeaders, ac);
+ Assert.fail("Expected WebApplicationException");
+ } catch (WebApplicationException e) {
+ Assert.assertTrue(e.getMessage().contains("Access denied to getCluster
in the cluster"));
+ Assert.assertEquals(e.getResponse().getStatus(),
Response.Status.FORBIDDEN.getStatusCode());
+ }
+ }
+
+ @Test
+ public void testValidateFineGrainedAuthWithNoSuchMethodError() {
+ FineGrainedAccessControl ac = Mockito.mock(FineGrainedAccessControl.class);
+ Mockito.when(ac.hasAccess(Mockito.any(HttpHeaders.class), Mockito.any(),
Mockito.any(), Mockito.any()))
+ .thenThrow(new NoSuchMethodError("Method not found"));
+
+ UriInfo mockUriInfo = Mockito.mock(UriInfo.class);
+ HttpHeaders mockHttpHeaders = Mockito.mock(HttpHeaders.class);
+
+ try {
+ FineGrainedAuthUtils.validateFineGrainedAuth(getAnnotatedMethod(),
mockUriInfo, mockHttpHeaders, ac);
+ Assert.fail("Expected WebApplicationException");
+ } catch (WebApplicationException e) {
+ Assert.assertTrue(e.getMessage().contains("Failed to check for access"));
+ Assert.assertEquals(e.getResponse().getStatus(),
Response.Status.INTERNAL_SERVER_ERROR.getStatusCode());
+ }
+ }
+
+ static class TestResource {
+ @Authorize(targetType = TargetType.CLUSTER, action = "getCluster")
+ void getCluster() {
+ }
+ }
+
+ private Method getAnnotatedMethod() {
+ try {
+ return TestResource.class.getDeclaredMethod("getCluster");
+ } catch (NoSuchMethodException e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]