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

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


The following commit(s) were added to refs/heads/master by this push:
     new de959e513d1 Add QueryLifecycle#authorize for grpc-query-extension 
(#15816)
de959e513d1 is described below

commit de959e513d120348dfbfcf3e13d265b200a7db6c
Author: Rishabh Singh <[email protected]>
AuthorDate: Fri Feb 2 21:49:57 2024 +0530

    Add QueryLifecycle#authorize for grpc-query-extension (#15816)
    
    Proposal #13469
    
    Original PR #14024
    
    A new method is being added in QueryLifecycle class to authorise a query 
based on authentication result.
    This method is required since we authenticate the query by intercepting it 
in the grpc extension and pass down the authentication result.
---
 .../org/apache/druid/server/QueryLifecycle.java    | 31 +++++++++
 .../apache/druid/server/QueryLifecycleTest.java    | 74 ++++++++++++++++------
 2 files changed, 85 insertions(+), 20 deletions(-)

diff --git a/server/src/main/java/org/apache/druid/server/QueryLifecycle.java 
b/server/src/main/java/org/apache/druid/server/QueryLifecycle.java
index 0f46e5da4d9..e0bb9875240 100644
--- a/server/src/main/java/org/apache/druid/server/QueryLifecycle.java
+++ b/server/src/main/java/org/apache/druid/server/QueryLifecycle.java
@@ -242,6 +242,37 @@ public class QueryLifecycle
     );
   }
 
+  /**
+   * Authorize the query using the authentication result.
+   * Will return an Access object denoting whether the query is authorized or 
not.
+   * This method is to be used by the grpc-query-extension.
+   *
+   * @param authenticationResult authentication result indicating identity of 
the requester
+   * @return authorization result of requester
+   */
+  public Access authorize(AuthenticationResult authenticationResult)
+  {
+    transition(State.INITIALIZED, State.AUTHORIZING);
+    final Iterable<ResourceAction> resourcesToAuthorize = Iterables.concat(
+        Iterables.transform(
+            baseQuery.getDataSource().getTableNames(),
+            AuthorizationUtils.DATASOURCE_READ_RA_GENERATOR
+        ),
+        Iterables.transform(
+            authConfig.contextKeysToAuthorize(userContextKeys),
+            contextParam -> new ResourceAction(new Resource(contextParam, 
ResourceType.QUERY_CONTEXT), Action.WRITE)
+        )
+    );
+    return doAuthorize(
+        authenticationResult,
+        AuthorizationUtils.authorizeAllResourceActions(
+            authenticationResult,
+            resourcesToAuthorize,
+            authorizerMapper
+        )
+    );
+  }
+
   private void preAuthorized(final AuthenticationResult authenticationResult, 
final Access access)
   {
     // gotta transition those states, even if we are already authorized
diff --git 
a/server/src/test/java/org/apache/druid/server/QueryLifecycleTest.java 
b/server/src/test/java/org/apache/druid/server/QueryLifecycleTest.java
index 578661ea768..a2691297d0b 100644
--- a/server/src/test/java/org/apache/druid/server/QueryLifecycleTest.java
+++ b/server/src/test/java/org/apache/druid/server/QueryLifecycleTest.java
@@ -188,15 +188,15 @@ public class QueryLifecycleTest
     
EasyMock.expect(authenticationResult.getIdentity()).andReturn(IDENTITY).anyTimes();
     
EasyMock.expect(authenticationResult.getAuthorizerName()).andReturn(AUTHORIZER).anyTimes();
     EasyMock.expect(authorizer.authorize(authenticationResult, new 
Resource(DATASOURCE, ResourceType.DATASOURCE), Action.READ))
-            .andReturn(Access.OK);
+            .andReturn(Access.OK).times(2);
     EasyMock.expect(authorizer.authorize(authenticationResult, new 
Resource("foo", ResourceType.QUERY_CONTEXT), Action.WRITE))
-            .andReturn(Access.OK);
+            .andReturn(Access.OK).times(2);
     EasyMock.expect(authorizer.authorize(authenticationResult, new 
Resource("baz", ResourceType.QUERY_CONTEXT), Action.WRITE))
-            .andReturn(Access.OK);
+            .andReturn(Access.OK).times(2);
 
     EasyMock.expect(toolChestWarehouse.getToolChest(EasyMock.anyObject()))
             .andReturn(toolChest)
-            .once();
+            .times(2);
 
     replayAll();
 
@@ -223,6 +223,10 @@ public class QueryLifecycleTest
     );
 
     Assert.assertTrue(lifecycle.authorize(mockRequest()).isAllowed());
+
+    lifecycle = createLifecycle(authConfig);
+    lifecycle.initialize(query);
+    Assert.assertTrue(lifecycle.authorize(authenticationResult).isAllowed());
   }
 
   @Test
@@ -232,13 +236,15 @@ public class QueryLifecycleTest
     
EasyMock.expect(authenticationResult.getIdentity()).andReturn(IDENTITY).anyTimes();
     
EasyMock.expect(authenticationResult.getAuthorizerName()).andReturn(AUTHORIZER).anyTimes();
     EasyMock.expect(authorizer.authorize(authenticationResult, new 
Resource(DATASOURCE, ResourceType.DATASOURCE), Action.READ))
-            .andReturn(Access.OK);
+            .andReturn(Access.OK)
+            .times(2);
     EasyMock.expect(authorizer.authorize(authenticationResult, new 
Resource("foo", ResourceType.QUERY_CONTEXT), Action.WRITE))
-            .andReturn(Access.DENIED);
+            .andReturn(Access.DENIED)
+            .times(2);
 
     EasyMock.expect(toolChestWarehouse.getToolChest(EasyMock.anyObject()))
             .andReturn(toolChest)
-            .once();
+            .times(2);
 
     replayAll();
 
@@ -255,6 +261,10 @@ public class QueryLifecycleTest
     QueryLifecycle lifecycle = createLifecycle(authConfig);
     lifecycle.initialize(query);
     Assert.assertFalse(lifecycle.authorize(mockRequest()).isAllowed());
+
+    lifecycle = createLifecycle(authConfig);
+    lifecycle.initialize(query);
+    Assert.assertFalse(lifecycle.authorize(authenticationResult).isAllowed());
   }
 
   @Test
@@ -264,11 +274,12 @@ public class QueryLifecycleTest
     
EasyMock.expect(authenticationResult.getIdentity()).andReturn(IDENTITY).anyTimes();
     
EasyMock.expect(authenticationResult.getAuthorizerName()).andReturn(AUTHORIZER).anyTimes();
     EasyMock.expect(authorizer.authorize(authenticationResult, new 
Resource(DATASOURCE, ResourceType.DATASOURCE), Action.READ))
-            .andReturn(Access.OK);
+            .andReturn(Access.OK)
+            .times(2);
 
     EasyMock.expect(toolChestWarehouse.getToolChest(EasyMock.anyObject()))
             .andReturn(toolChest)
-            .once();
+            .times(2);
 
     replayAll();
 
@@ -296,6 +307,10 @@ public class QueryLifecycleTest
     );
 
     Assert.assertTrue(lifecycle.authorize(mockRequest()).isAllowed());
+
+    lifecycle = createLifecycle(authConfig);
+    lifecycle.initialize(query);
+    Assert.assertTrue(lifecycle.authorize(authenticationResult).isAllowed());
   }
 
   @Test
@@ -305,11 +320,12 @@ public class QueryLifecycleTest
     
EasyMock.expect(authenticationResult.getIdentity()).andReturn(IDENTITY).anyTimes();
     
EasyMock.expect(authenticationResult.getAuthorizerName()).andReturn(AUTHORIZER).anyTimes();
     EasyMock.expect(authorizer.authorize(authenticationResult, new 
Resource(DATASOURCE, ResourceType.DATASOURCE), Action.READ))
-            .andReturn(Access.OK);
+            .andReturn(Access.OK)
+            .times(2);
 
     EasyMock.expect(toolChestWarehouse.getToolChest(EasyMock.anyObject()))
             .andReturn(toolChest)
-            .once();
+            .times(2);
 
     replayAll();
 
@@ -338,6 +354,10 @@ public class QueryLifecycleTest
     );
 
     Assert.assertTrue(lifecycle.authorize(mockRequest()).isAllowed());
+
+    lifecycle = createLifecycle(authConfig);
+    lifecycle.initialize(query);
+    Assert.assertTrue(lifecycle.authorize(authenticationResult).isAllowed());
   }
 
   @Test
@@ -347,13 +367,15 @@ public class QueryLifecycleTest
     
EasyMock.expect(authenticationResult.getIdentity()).andReturn(IDENTITY).anyTimes();
     
EasyMock.expect(authenticationResult.getAuthorizerName()).andReturn(AUTHORIZER).anyTimes();
     EasyMock.expect(authorizer.authorize(authenticationResult, new 
Resource(DATASOURCE, ResourceType.DATASOURCE), Action.READ))
-            .andReturn(Access.OK);
+            .andReturn(Access.OK)
+            .times(2);
     EasyMock.expect(authorizer.authorize(authenticationResult, new 
Resource("foo", ResourceType.QUERY_CONTEXT), Action.WRITE))
-            .andReturn(Access.DENIED);
+            .andReturn(Access.DENIED)
+            .times(2);
 
     EasyMock.expect(toolChestWarehouse.getToolChest(EasyMock.anyObject()))
             .andReturn(toolChest)
-            .once();
+            .times(2);
 
     replayAll();
 
@@ -373,6 +395,10 @@ public class QueryLifecycleTest
     QueryLifecycle lifecycle = createLifecycle(authConfig);
     lifecycle.initialize(query);
     Assert.assertFalse(lifecycle.authorize(mockRequest()).isAllowed());
+
+    lifecycle = createLifecycle(authConfig);
+    lifecycle.initialize(query);
+    Assert.assertFalse(lifecycle.authorize(authenticationResult).isAllowed());
   }
 
   @Test
@@ -382,22 +408,26 @@ public class QueryLifecycleTest
     
EasyMock.expect(authenticationResult.getIdentity()).andReturn(IDENTITY).anyTimes();
     
EasyMock.expect(authenticationResult.getAuthorizerName()).andReturn(AUTHORIZER).anyTimes();
     EasyMock.expect(authorizer.authorize(authenticationResult, new 
Resource("fake", ResourceType.DATASOURCE), Action.READ))
-            .andReturn(Access.OK);
+            .andReturn(Access.OK)
+            .times(2);
     EasyMock.expect(authorizer.authorize(authenticationResult, new 
Resource("foo", ResourceType.QUERY_CONTEXT), Action.WRITE))
-            .andReturn(Access.OK);
-    EasyMock.expect(authorizer.authorize(authenticationResult, new 
Resource("baz", ResourceType.QUERY_CONTEXT), 
Action.WRITE)).andReturn(Access.OK);
+            .andReturn(Access.OK)
+            .times(2);
+    EasyMock.expect(authorizer.authorize(authenticationResult, new 
Resource("baz", ResourceType.QUERY_CONTEXT), Action.WRITE))
+            .andReturn(Access.OK)
+            .times(2);
 
     EasyMock.expect(toolChestWarehouse.getToolChest(EasyMock.anyObject()))
             .andReturn(toolChest)
-            .once();
+            .times(2);
 
     replayAll();
 
     final QueryContextTest.LegacyContextQuery query = new 
QueryContextTest.LegacyContextQuery(ImmutableMap.of("foo", "bar", "baz", 
"qux"));
 
     AuthConfig authConfig = AuthConfig.newBuilder()
-        .setAuthorizeQueryContextParams(true)
-        .build();
+                                      .setAuthorizeQueryContextParams(true)
+                                      .build();
     QueryLifecycle lifecycle = createLifecycle(authConfig);
     lifecycle.initialize(query);
 
@@ -408,6 +438,10 @@ public class QueryLifecycleTest
     Assert.assertTrue(revisedContext.containsKey("queryId"));
 
     Assert.assertTrue(lifecycle.authorize(mockRequest()).isAllowed());
+
+    lifecycle = createLifecycle(authConfig);
+    lifecycle.initialize(query);
+    Assert.assertTrue(lifecycle.authorize(mockRequest()).isAllowed());
   }
 
   private HttpServletRequest mockRequest()


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

Reply via email to