Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import static org.easymock.EasyMock.anyObject;
import static org.easymock.EasyMock.expect;
Expand Down Expand Up @@ -193,7 +194,8 @@ private void setExpectations(
authorizer.authorize(
authResult,
new Resource(datasource, ResourceType.DATASOURCE),
expectedAction
expectedAction,
Map.of()
)
).andReturn(new Access(userHasAccess)).anyTimes();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@

import javax.servlet.http.HttpServletRequest;
import java.util.Collections;
import java.util.Map;

public class SamplerResourceTest
{
Expand Down Expand Up @@ -98,7 +99,8 @@ public void test_post_properResourcesAuthorized()
EasyMock.expect(mockAuthorizer.authorize(
EasyMock.anyObject(AuthenticationResult.class),
EasyMock.eq(Resource.STATE_RESOURCE),
EasyMock.eq(Action.WRITE))).andReturn(Access.OK);
EasyMock.eq(Action.WRITE),
EasyMock.eq(Map.of()))).andReturn(Access.OK);
EasyMock.expect(authConfig.isEnableInputSourceSecurity()).andReturn(false);
EasyMock.expect(samplerSpec.sample()).andReturn(null);
EasyMock.replay(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@
*/
public class AuthorizationUtils
{
/**
* Key used in the authorization context map to indicate the caller path that triggered the authorization check.
*/
public static final String AUTHORIZATION_CONTEXT_CALLER_PATH_CONTEXT_KEY = "callerPath";

public static final ImmutableSet<String> RESTRICTION_APPLICABLE_RESOURCE_TYPES = ImmutableSet.of(
ResourceType.DATASOURCE
);
Expand Down Expand Up @@ -74,6 +79,35 @@ public static AuthorizationResult authorizeResourceAction(
);
}

/**
* Performs authorization check on a single resource-action based on the authentication fields from the request,
* with additional context about the authorization request.
* <p>
* This function will set the DRUID_AUTHORIZATION_CHECKED attribute in the request. If this attribute is already set
* when this function is called, an exception is thrown.
*
* @param request HTTP request to be authorized
* @param resourceAction A resource identifier and the action to be taken the resource.
* @param authorizerMapper The singleton AuthorizerMapper instance
* @param context Additional context about the authorization request, such as information about the
* caller path to be authorized.
* @return AuthorizationResult containing allow/deny access to the resource action, along with policy restrictions.
*/
public static AuthorizationResult authorizeResourceAction(
final HttpServletRequest request,
final ResourceAction resourceAction,
final AuthorizerMapper authorizerMapper,
final Map<String, Object> context
)
{
return authorizeAllResourceActions(
request,
Collections.singletonList(resourceAction),
authorizerMapper,
context
);
}

/**
* Verifies that the user has unrestricted access to perform the required
* action on the given datasource.
Expand Down Expand Up @@ -181,6 +215,29 @@ public static AuthorizationResult authorizeAllResourceActions(
final Iterable<ResourceAction> resourceActions,
final AuthorizerMapper authorizerMapper
)
{
return authorizeAllResourceActions(authenticationResult, resourceActions, authorizerMapper, Map.of());
}

/**
* Performs authorization check on a list of resource-actions based on the authenticationResult, with additional
* context about the authorization request.
* <p>
* If one of the resource-actions denys access, returns deny access immediately.
*
* @param authenticationResult Authentication result representing identity of requester
* @param resourceActions An Iterable of resource-actions to authorize
* @param authorizerMapper The singleton AuthorizerMapper instance
* @param context Additional context about the authorization request, such as information about the
* caller path to be authorized.
* @return AuthorizationResult containing allow/deny access to the resource actions, along with policy restrictions.
*/
public static AuthorizationResult authorizeAllResourceActions(
final AuthenticationResult authenticationResult,
final Iterable<ResourceAction> resourceActions,
final AuthorizerMapper authorizerMapper,
final Map<String, Object> context
)
{
final Authorizer authorizer = authorizerMapper.getAuthorizer(authenticationResult.getAuthorizerName());
if (authorizer == null) {
Expand All @@ -198,7 +255,8 @@ public static AuthorizationResult authorizeAllResourceActions(
final Access access = authorizer.authorize(
authenticationResult,
resourceAction.getResource(),
resourceAction.getAction()
resourceAction.getAction(),
context
);
if (!access.isAllowed()) {
return AuthorizationResult.deny(access.getMessage());
Expand Down Expand Up @@ -260,6 +318,32 @@ public static AuthorizationResult authorizeAllResourceActions(
final Iterable<ResourceAction> resourceActions,
final AuthorizerMapper authorizerMapper
)
{
return authorizeAllResourceActions(request, resourceActions, authorizerMapper, Map.of());
}

/**
* Performs authorization check on a list of resource-actions based on the authentication fields from the request,
* with additional context about the authorization request.
* <p>
* If one of the resource-actions denys access, returns deny access immediately.
* <p>
* This function will set the DRUID_AUTHORIZATION_CHECKED attribute in the request. If this attribute is already set
* when this function is called, an exception is thrown.
*
* @param request HTTP request to be authorized
* @param resourceActions An Iterable of resource-actions to authorize
* @param authorizerMapper The singleton AuthorizerMapper instance
* @param context Additional context about the authorization request, such as information about the
* caller path to be authorized.
* @return AuthorizationResult containing allow/deny access to the resource actions, along with policy restrictions.
*/
public static AuthorizationResult authorizeAllResourceActions(
final HttpServletRequest request,
final Iterable<ResourceAction> resourceActions,
final AuthorizerMapper authorizerMapper,
final Map<String, Object> context
)
{
if (request.getAttribute(AuthConfig.DRUID_ALLOW_UNSECURED_PATH) != null) {
return AuthorizationResult.ALLOW_NO_RESTRICTION;
Expand All @@ -272,7 +356,8 @@ public static AuthorizationResult authorizeAllResourceActions(
AuthorizationResult authResult = authorizeAllResourceActions(
authenticationResultFromRequest(request),
resourceActions,
authorizerMapper
authorizerMapper,
context
);

request.setAttribute(AuthConfig.DRUID_AUTHORIZATION_CHECKED, authResult.allowBasicAccess());
Expand Down Expand Up @@ -321,6 +406,38 @@ public static <ResType> Iterable<ResType> filterAuthorizedResources(
final Function<? super ResType, Iterable<ResourceAction>> resourceActionGenerator,
final AuthorizerMapper authorizerMapper
)
{
return filterAuthorizedResources(request, resources, resourceActionGenerator, authorizerMapper, Map.of());
}

/**
* Return an iterable of authorized resources, by filtering the input resources with authorization checks based on the
* authentication fields from the request, with additional context about the authorization request. This method does:
* <li>
* For every resource, resourceActionGenerator generates an Iterable of ResourceAction or null.
* <li>
* If null, continue with next resource. If any resource-action in the iterable has deny-access, continue with next
* resource. Only when every resource-action has allow-access, add the resource to the result.
* </li>
* <p>
* This function will set the DRUID_AUTHORIZATION_CHECKED attribute in the request. If this attribute is already set
* when this function is called, an exception is thrown.
*
* @param request HTTP request to be authorized
* @param resources resources to be processed into resource-actions
* @param resourceActionGenerator Function that creates an iterable of resource-actions from a resource
* @param authorizerMapper authorizer mapper
* @param context Additional context about the authorization request, such as information about the
* caller path to be authorized.
* @return Iterable containing resources that were authorized
*/
public static <ResType> Iterable<ResType> filterAuthorizedResources(
final HttpServletRequest request,
final Iterable<ResType> resources,
final Function<? super ResType, Iterable<ResourceAction>> resourceActionGenerator,
final AuthorizerMapper authorizerMapper,
final Map<String, Object> context
)
{
if (request.getAttribute(AuthConfig.DRUID_ALLOW_UNSECURED_PATH) != null) {
return resources;
Expand All @@ -336,7 +453,8 @@ public static <ResType> Iterable<ResType> filterAuthorizedResources(
authenticationResult,
resources,
resourceActionGenerator,
authorizerMapper
authorizerMapper,
context
);

// We're filtering, so having access to none of the objects isn't an authorization failure (in terms of whether
Expand Down Expand Up @@ -367,6 +485,34 @@ public static <ResType> Iterable<ResType> filterAuthorizedResources(
final Function<? super ResType, Iterable<ResourceAction>> resourceActionGenerator,
final AuthorizerMapper authorizerMapper
)
{
return filterAuthorizedResources(authenticationResult, resources, resourceActionGenerator, authorizerMapper, Map.of());
}

/**
* Return an iterable of authorized resources, by filtering the input resources with authorization checks based on
* authenticationResult, with additional context about the authorization request. This method does:
* <li>
* For every resource, resourceActionGenerator generates an Iterable of ResourceAction or null.
* <li>
* If null, continue with next resource. If any resource-action in the iterable has deny-access, continue with next
* resource. Only when every resource-action has allow-access, add the resource to the result.
*
* @param authenticationResult Authentication result representing identity of requester
* @param resources resources to be processed into resource-actions
* @param resourceActionGenerator Function that creates an iterable of resource-actions from a resource
* @param authorizerMapper authorizer mapper
* @param context Additional context about the authorization request, such as information about the
* caller path to be authorized.
* @return Iterable containing resources that were authorized
*/
public static <ResType> Iterable<ResType> filterAuthorizedResources(
final AuthenticationResult authenticationResult,
final Iterable<ResType> resources,
final Function<? super ResType, Iterable<ResourceAction>> resourceActionGenerator,
final AuthorizerMapper authorizerMapper,
final Map<String, Object> context
)
{
final Authorizer authorizer = authorizerMapper.getAuthorizer(authenticationResult.getAuthorizerName());
if (authorizer == null) {
Expand All @@ -390,7 +536,8 @@ public static <ResType> Iterable<ResType> filterAuthorizedResources(
ra -> authorizer.authorize(
authenticationResult,
ra.getResource(),
ra.getAction()
ra.getAction(),
context
)
);
if (!access.isAllowed()) {
Expand Down Expand Up @@ -427,6 +574,38 @@ public static <KeyType, ResType> Map<KeyType, List<ResType>> filterAuthorizedRes
final Function<? super ResType, Iterable<ResourceAction>> resourceActionGenerator,
final AuthorizerMapper authorizerMapper
)
{
return filterAuthorizedResources(request, unfilteredResources, resourceActionGenerator, authorizerMapper, Map.of());
}

/**
* Return a map of authorized resources, by filtering the input resources with authorization checks based on the
* authentication fields from the request, with additional context about the authorization request. This method does:
* <li>
* For every resource, resourceActionGenerator generates an Iterable of ResourceAction or null.
* <li>
* If null, continue with next resource. If any resource-action in the iterable has deny-access, continue with next
* resource. Only when every resource-action has allow-access, add the resource to the result.
* </li>
* <p>
* This function will set the DRUID_AUTHORIZATION_CHECKED attribute in the request. If this attribute is already set
* when this function is called, an exception is thrown.
*
* @param request HTTP request to be authorized
* @param unfilteredResources Map of resource lists to be filtered
* @param resourceActionGenerator Function that creates an iterable of resource-actions from a resource
* @param authorizerMapper authorizer mapper
* @param context Additional context about the authorization request, such as information about the
* caller path to be authorized.
* @return Map containing lists of resources that were authorized
*/
public static <KeyType, ResType> Map<KeyType, List<ResType>> filterAuthorizedResources(
final HttpServletRequest request,
final Map<KeyType, List<ResType>> unfilteredResources,
final Function<? super ResType, Iterable<ResourceAction>> resourceActionGenerator,
final AuthorizerMapper authorizerMapper,
final Map<String, Object> context
)
{

if (request.getAttribute(AuthConfig.DRUID_ALLOW_UNSECURED_PATH) != null) {
Expand All @@ -450,7 +629,8 @@ public static <KeyType, ResType> Map<KeyType, List<ResType>> filterAuthorizedRes
authenticationResult,
entry.getValue(),
resourceActionGenerator,
authorizerMapper
authorizerMapper,
context
)
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;

import java.util.Map;

/**
* An Authorizer is responsible for performing authorization checks for resource accesses.
* <p>
Expand Down Expand Up @@ -51,4 +53,33 @@ public interface Authorizer
* @return An {@link Access} object representing the result of the authorization check. Must not be null.
*/
Access authorize(AuthenticationResult authenticationResult, Resource resource, Action action);

/**
* Check if the entity represented by the authentication result is authorized to perform the given action on the
* given resource, with additional context about the authorization request.
* <p>
* The {@code context} map can be used to provide supplementary information about the caller path to be authorized.
* For example, it may contain details about the API endpoint or query context that triggered the authorization
* check, allowing authorizer implementations to make more informed decisions.
* <p>
* If the action involves reading a table, the outcome could include {@link org.apache.druid.query.policy.Policy} restrictions.
* However, if the action does not involve reading a table, there must be no {@link org.apache.druid.query.policy.Policy} restrictions.
*
* @param authenticationResult The authentication result of the request
* @param resource The resource to be accessed
* @param action The action to perform on the resource
* @param context Additional context about the authorization request, such as information about the
* caller path to be authorized. Implementations that do not need this context can
* safely ignore it.
* @return An {@link Access} object representing the result of the authorization check. Must not be null.
*/
default Access authorize(
AuthenticationResult authenticationResult,
Resource resource,
Action action,
Map<String, Object> context
)
{
return authorize(authenticationResult, resource, action);
}
}
Loading
Loading