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.
This commit is contained in:
Rishabh Singh 2024-02-02 21:49:57 +05:30 committed by GitHub
parent 8f5b7522c7
commit de959e513d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 85 additions and 20 deletions

View File

@ -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) private void preAuthorized(final AuthenticationResult authenticationResult, final Access access)
{ {
// gotta transition those states, even if we are already authorized // gotta transition those states, even if we are already authorized

View File

@ -188,15 +188,15 @@ public class QueryLifecycleTest
EasyMock.expect(authenticationResult.getIdentity()).andReturn(IDENTITY).anyTimes(); EasyMock.expect(authenticationResult.getIdentity()).andReturn(IDENTITY).anyTimes();
EasyMock.expect(authenticationResult.getAuthorizerName()).andReturn(AUTHORIZER).anyTimes(); EasyMock.expect(authenticationResult.getAuthorizerName()).andReturn(AUTHORIZER).anyTimes();
EasyMock.expect(authorizer.authorize(authenticationResult, new Resource(DATASOURCE, ResourceType.DATASOURCE), Action.READ)) 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)) 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)) 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())) EasyMock.expect(toolChestWarehouse.getToolChest(EasyMock.anyObject()))
.andReturn(toolChest) .andReturn(toolChest)
.once(); .times(2);
replayAll(); replayAll();
@ -223,6 +223,10 @@ public class QueryLifecycleTest
); );
Assert.assertTrue(lifecycle.authorize(mockRequest()).isAllowed()); Assert.assertTrue(lifecycle.authorize(mockRequest()).isAllowed());
lifecycle = createLifecycle(authConfig);
lifecycle.initialize(query);
Assert.assertTrue(lifecycle.authorize(authenticationResult).isAllowed());
} }
@Test @Test
@ -232,13 +236,15 @@ public class QueryLifecycleTest
EasyMock.expect(authenticationResult.getIdentity()).andReturn(IDENTITY).anyTimes(); EasyMock.expect(authenticationResult.getIdentity()).andReturn(IDENTITY).anyTimes();
EasyMock.expect(authenticationResult.getAuthorizerName()).andReturn(AUTHORIZER).anyTimes(); EasyMock.expect(authenticationResult.getAuthorizerName()).andReturn(AUTHORIZER).anyTimes();
EasyMock.expect(authorizer.authorize(authenticationResult, new Resource(DATASOURCE, ResourceType.DATASOURCE), Action.READ)) 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)) 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())) EasyMock.expect(toolChestWarehouse.getToolChest(EasyMock.anyObject()))
.andReturn(toolChest) .andReturn(toolChest)
.once(); .times(2);
replayAll(); replayAll();
@ -255,6 +261,10 @@ public class QueryLifecycleTest
QueryLifecycle lifecycle = createLifecycle(authConfig); QueryLifecycle lifecycle = createLifecycle(authConfig);
lifecycle.initialize(query); lifecycle.initialize(query);
Assert.assertFalse(lifecycle.authorize(mockRequest()).isAllowed()); Assert.assertFalse(lifecycle.authorize(mockRequest()).isAllowed());
lifecycle = createLifecycle(authConfig);
lifecycle.initialize(query);
Assert.assertFalse(lifecycle.authorize(authenticationResult).isAllowed());
} }
@Test @Test
@ -264,11 +274,12 @@ public class QueryLifecycleTest
EasyMock.expect(authenticationResult.getIdentity()).andReturn(IDENTITY).anyTimes(); EasyMock.expect(authenticationResult.getIdentity()).andReturn(IDENTITY).anyTimes();
EasyMock.expect(authenticationResult.getAuthorizerName()).andReturn(AUTHORIZER).anyTimes(); EasyMock.expect(authenticationResult.getAuthorizerName()).andReturn(AUTHORIZER).anyTimes();
EasyMock.expect(authorizer.authorize(authenticationResult, new Resource(DATASOURCE, ResourceType.DATASOURCE), Action.READ)) 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())) EasyMock.expect(toolChestWarehouse.getToolChest(EasyMock.anyObject()))
.andReturn(toolChest) .andReturn(toolChest)
.once(); .times(2);
replayAll(); replayAll();
@ -296,6 +307,10 @@ public class QueryLifecycleTest
); );
Assert.assertTrue(lifecycle.authorize(mockRequest()).isAllowed()); Assert.assertTrue(lifecycle.authorize(mockRequest()).isAllowed());
lifecycle = createLifecycle(authConfig);
lifecycle.initialize(query);
Assert.assertTrue(lifecycle.authorize(authenticationResult).isAllowed());
} }
@Test @Test
@ -305,11 +320,12 @@ public class QueryLifecycleTest
EasyMock.expect(authenticationResult.getIdentity()).andReturn(IDENTITY).anyTimes(); EasyMock.expect(authenticationResult.getIdentity()).andReturn(IDENTITY).anyTimes();
EasyMock.expect(authenticationResult.getAuthorizerName()).andReturn(AUTHORIZER).anyTimes(); EasyMock.expect(authenticationResult.getAuthorizerName()).andReturn(AUTHORIZER).anyTimes();
EasyMock.expect(authorizer.authorize(authenticationResult, new Resource(DATASOURCE, ResourceType.DATASOURCE), Action.READ)) 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())) EasyMock.expect(toolChestWarehouse.getToolChest(EasyMock.anyObject()))
.andReturn(toolChest) .andReturn(toolChest)
.once(); .times(2);
replayAll(); replayAll();
@ -338,6 +354,10 @@ public class QueryLifecycleTest
); );
Assert.assertTrue(lifecycle.authorize(mockRequest()).isAllowed()); Assert.assertTrue(lifecycle.authorize(mockRequest()).isAllowed());
lifecycle = createLifecycle(authConfig);
lifecycle.initialize(query);
Assert.assertTrue(lifecycle.authorize(authenticationResult).isAllowed());
} }
@Test @Test
@ -347,13 +367,15 @@ public class QueryLifecycleTest
EasyMock.expect(authenticationResult.getIdentity()).andReturn(IDENTITY).anyTimes(); EasyMock.expect(authenticationResult.getIdentity()).andReturn(IDENTITY).anyTimes();
EasyMock.expect(authenticationResult.getAuthorizerName()).andReturn(AUTHORIZER).anyTimes(); EasyMock.expect(authenticationResult.getAuthorizerName()).andReturn(AUTHORIZER).anyTimes();
EasyMock.expect(authorizer.authorize(authenticationResult, new Resource(DATASOURCE, ResourceType.DATASOURCE), Action.READ)) 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)) 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())) EasyMock.expect(toolChestWarehouse.getToolChest(EasyMock.anyObject()))
.andReturn(toolChest) .andReturn(toolChest)
.once(); .times(2);
replayAll(); replayAll();
@ -373,6 +395,10 @@ public class QueryLifecycleTest
QueryLifecycle lifecycle = createLifecycle(authConfig); QueryLifecycle lifecycle = createLifecycle(authConfig);
lifecycle.initialize(query); lifecycle.initialize(query);
Assert.assertFalse(lifecycle.authorize(mockRequest()).isAllowed()); Assert.assertFalse(lifecycle.authorize(mockRequest()).isAllowed());
lifecycle = createLifecycle(authConfig);
lifecycle.initialize(query);
Assert.assertFalse(lifecycle.authorize(authenticationResult).isAllowed());
} }
@Test @Test
@ -382,14 +408,18 @@ public class QueryLifecycleTest
EasyMock.expect(authenticationResult.getIdentity()).andReturn(IDENTITY).anyTimes(); EasyMock.expect(authenticationResult.getIdentity()).andReturn(IDENTITY).anyTimes();
EasyMock.expect(authenticationResult.getAuthorizerName()).andReturn(AUTHORIZER).anyTimes(); EasyMock.expect(authenticationResult.getAuthorizerName()).andReturn(AUTHORIZER).anyTimes();
EasyMock.expect(authorizer.authorize(authenticationResult, new Resource("fake", ResourceType.DATASOURCE), Action.READ)) 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)) EasyMock.expect(authorizer.authorize(authenticationResult, new Resource("foo", ResourceType.QUERY_CONTEXT), Action.WRITE))
.andReturn(Access.OK); .andReturn(Access.OK)
EasyMock.expect(authorizer.authorize(authenticationResult, new Resource("baz", ResourceType.QUERY_CONTEXT), Action.WRITE)).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())) EasyMock.expect(toolChestWarehouse.getToolChest(EasyMock.anyObject()))
.andReturn(toolChest) .andReturn(toolChest)
.once(); .times(2);
replayAll(); replayAll();
@ -408,6 +438,10 @@ public class QueryLifecycleTest
Assert.assertTrue(revisedContext.containsKey("queryId")); Assert.assertTrue(revisedContext.containsKey("queryId"));
Assert.assertTrue(lifecycle.authorize(mockRequest()).isAllowed()); Assert.assertTrue(lifecycle.authorize(mockRequest()).isAllowed());
lifecycle = createLifecycle(authConfig);
lifecycle.initialize(query);
Assert.assertTrue(lifecycle.authorize(mockRequest()).isAllowed());
} }
private HttpServletRequest mockRequest() private HttpServletRequest mockRequest()