Skip to content

Commit

Permalink
feat(authorization): Adding AuthorizerContext + ResourceSpecResolver …
Browse files Browse the repository at this point in the history
…to context (#4982)
  • Loading branch information
jjoyce0510 authored May 24, 2022
1 parent b77f381 commit ce061e3
Show file tree
Hide file tree
Showing 20 changed files with 147 additions and 81 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ public interface Authorizer {
* @param authorizerConfig config provided to the authenticator derived from the Metadata Service YAML config. This
* config comes from the "authorization.authorizers.config" configuration.
*/
void init(@Nonnull final Map<String, Object> authorizerConfig);
void init(@Nonnull final Map<String, Object> authorizerConfig, @Nonnull final AuthorizerContext ctx);

/**
* Authorizes an action based on the actor, the resource, & required privileges.
*/
AuthorizationResult authorize(AuthorizationRequest request);
AuthorizationResult authorize(@Nonnull final AuthorizationRequest request);

/**
* Retrieves the current list of actors authorized to for a particular privilege against
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.datahub.authorization;

import lombok.AllArgsConstructor;
import lombok.Data;


/**
* Context provided to an Authorizer on initialization.
*/
@Data
@AllArgsConstructor
public class AuthorizerContext {
/**
* A utility for resolving a {@link ResourceSpec} to resolved resource field values.
*/
private ResourceSpecResolver resourceSpecResolver;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package com.datahub.authorization;

import java.util.Collections;
import java.util.Map;
import java.util.Set;
import javax.annotation.Nullable;
import lombok.Getter;
import lombok.RequiredArgsConstructor;


/**
* Wrapper around authorization request with field resolvers for lazily fetching the field values for each field type
*/
@RequiredArgsConstructor
public class ResolvedResourceSpec {
@Getter
private final ResourceSpec spec;
private final Map<ResourceFieldType, FieldResolver> fieldResolvers;

public Set<String> getFieldValues(ResourceFieldType resourceFieldType) {
if (!fieldResolvers.containsKey(resourceFieldType)) {
return Collections.emptySet();
}
return fieldResolvers.get(resourceFieldType).getFieldValuesFuture().join().getValues();
}

/**
* Fetch the entity-registry type for a resource. ('dataset', 'dashboard', 'chart').
* @return the entity type.
*/
public String getType() {
if (!fieldResolvers.containsKey(ResourceFieldType.RESOURCE_TYPE)) {
throw new UnsupportedOperationException("Failed to resolve resource type! No field resolver for RESOURCE_TYPE provided.");
}
Set<String> resourceTypes = fieldResolvers.get(ResourceFieldType.RESOURCE_TYPE).getFieldValuesFuture().join().getValues();
assert resourceTypes.size() == 1; // There should always be a single resource type.
return resourceTypes.stream().findFirst().get();
}

/**
* Fetch the owners for a resource.
* @return a set of owner urns, or empty set if none exist.
*/
public Set<String> getOwners() {
if (!fieldResolvers.containsKey(ResourceFieldType.OWNER)) {
return Collections.emptySet();
}
return fieldResolvers.get(ResourceFieldType.OWNER).getFieldValuesFuture().join().getValues();
}

/**
* Fetch the domain for a Resolved Resource Spec
* @return a Domain or null if one does not exist.
*/
@Nullable
public String getDomain() {
if (!fieldResolvers.containsKey(ResourceFieldType.DOMAIN)) {
return null;
}
Set<String> domains = fieldResolvers.get(ResourceFieldType.DOMAIN).getFieldValuesFuture().join().getValues();
if (domains.size() > 0) {
return domains.stream().findFirst().get();
}
return null;
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.datahub.authorization.fieldresolverprovider;
package com.datahub.authorization;

/**
* List of resource field types to fetch for a given resource
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.datahub.authorization;

/**
* A Resource Spec Resolver is responsible for resolving a {@link ResourceSpec} to a {@link ResolvedResourceSpec}.
*/
public interface ResourceSpecResolver {
/**
Resolve a {@link ResourceSpec} to a resolved resource spec.
**/
ResolvedResourceSpec resolve(ResourceSpec resourceSpec);
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public AuthorizerChain(final List<Authorizer> authorizers) {
}

@Override
public void init(@Nonnull Map<String, Object> authorizerConfig) {
public void init(@Nonnull Map<String, Object> authorizerConfig, @Nonnull AuthorizerContext ctx) {
// pass.
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

import com.datahub.authentication.Authentication;
import com.google.common.annotations.VisibleForTesting;
import com.linkedin.common.Owner;
import com.linkedin.common.Ownership;
import com.linkedin.common.urn.Urn;
import com.linkedin.common.urn.UrnUtils;
import com.linkedin.entity.client.EntityClient;
Expand All @@ -13,18 +11,15 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import javax.annotation.Nonnull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import static com.linkedin.metadata.Constants.CORP_GROUP_ENTITY_NAME;
import static com.linkedin.metadata.Constants.CORP_USER_ENTITY_NAME;


/**
* The Authorizer is a singleton class responsible for authorizing
Expand All @@ -43,13 +38,12 @@ public enum AuthorizationMode {
*/
DEFAULT,
/**
* Allow all means that the AuthorizationManager will allow all actions. This is used as an override to disable the
* Allow all means that the DataHubAuthorizer will allow all actions. This is used as an override to disable the
* policies feature.
*/
ALLOW_ALL
}


// Credentials used to make / authorize requests as the internal system actor.
private final Authentication _systemAuthentication;

Expand All @@ -59,9 +53,8 @@ public enum AuthorizationMode {

private final ScheduledExecutorService _refreshExecutorService = Executors.newScheduledThreadPool(1);
private final PolicyRefreshRunnable _policyRefreshRunnable;

private final ResourceSpecResolver _resourceSpecResolver;
private final PolicyEngine _policyEngine;
private ResourceSpecResolver _resourceSpecResolver;
private AuthorizationMode _mode;

public static final String ALL = "ALL";
Expand All @@ -72,20 +65,20 @@ public DataHubAuthorizer(
final int delayIntervalSeconds,
final int refreshIntervalSeconds,
final AuthorizationMode mode) {
_systemAuthentication = systemAuthentication;
_systemAuthentication = Objects.requireNonNull(systemAuthentication);
_mode = Objects.requireNonNull(mode);
_policyEngine = new PolicyEngine(systemAuthentication, Objects.requireNonNull(entityClient));
_policyRefreshRunnable = new PolicyRefreshRunnable(systemAuthentication, new PolicyFetcher(entityClient), _policyCache);
_refreshExecutorService.scheduleAtFixedRate(_policyRefreshRunnable, delayIntervalSeconds, refreshIntervalSeconds, TimeUnit.SECONDS);
_mode = mode;
_resourceSpecResolver = new ResourceSpecResolver(systemAuthentication, entityClient);
_policyEngine = new PolicyEngine(systemAuthentication, entityClient);
}

@Override
public void init(@Nonnull Map<String, Object> authorizerConfig) {
public void init(@Nonnull Map<String, Object> authorizerConfig, @Nonnull AuthorizerContext ctx) {
// Pass. No static config.
_resourceSpecResolver = Objects.requireNonNull(ctx.getResourceSpecResolver());
}

public AuthorizationResult authorize(final AuthorizationRequest request) {
public AuthorizationResult authorize(@Nonnull final AuthorizationRequest request) {

// 0. Short circuit: If the action is being performed by the system (root), always allow it.
if (isSystemRequest(request, this._systemAuthentication)) {
Expand Down Expand Up @@ -265,20 +258,4 @@ private void addPolicyToCache(final Map<String, List<DataHubPolicyInfo>> cache,
cache.put(ALL, existingPolicies);
}
}

private List<Urn> userOwners(final Ownership ownership) {
return ownership.getOwners()
.stream()
.filter(owner -> CORP_USER_ENTITY_NAME.equals(owner.getOwner().getEntityType()))
.map(Owner::getOwner)
.collect(Collectors.toList());
}

private List<Urn> groupOwners(final Ownership ownership) {
return ownership.getOwners()
.stream()
.filter(owner -> CORP_GROUP_ENTITY_NAME.equals(owner.getOwner().getEntityType()))
.map(Owner::getOwner)
.collect(Collectors.toList());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import com.datahub.authorization.fieldresolverprovider.DomainFieldResolverProvider;
import com.datahub.authorization.fieldresolverprovider.EntityTypeFieldResolverProvider;
import com.datahub.authorization.fieldresolverprovider.EntityUrnFieldResolverProvider;
import com.datahub.authorization.fieldresolverprovider.ResourceFieldType;
import com.datahub.authorization.fieldresolverprovider.OwnerFieldResolverProvider;
import com.datahub.authorization.fieldresolverprovider.ResourceFieldResolverProvider;
import com.google.common.collect.ImmutableList;
Expand All @@ -14,21 +13,22 @@
import java.util.stream.Collectors;


public class ResourceSpecResolver {
public class DefaultResourceSpecResolver implements ResourceSpecResolver {
private final List<ResourceFieldResolverProvider> _resourceFieldResolverProviders;

public ResourceSpecResolver(Authentication systemAuthentication, EntityClient entityClient) {
public DefaultResourceSpecResolver(Authentication systemAuthentication, EntityClient entityClient) {
_resourceFieldResolverProviders =
ImmutableList.of(new EntityTypeFieldResolverProvider(), new EntityUrnFieldResolverProvider(),
new DomainFieldResolverProvider(entityClient, systemAuthentication),
new OwnerFieldResolverProvider(entityClient, systemAuthentication));
}

@Override
public ResolvedResourceSpec resolve(ResourceSpec resourceSpec) {
return new ResolvedResourceSpec(resourceSpec, getFieldResolvers(resourceSpec));
}

public Map<ResourceFieldType, FieldResolver> getFieldResolvers(ResourceSpec resourceSpec) {
private Map<ResourceFieldType, FieldResolver> getFieldResolvers(ResourceSpec resourceSpec) {
return _resourceFieldResolverProviders.stream()
.collect(Collectors.toMap(ResourceFieldResolverProvider::getFieldType,
hydrator -> hydrator.getFieldResolver(resourceSpec)));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.datahub.authorization;

import com.datahub.authorization.fieldresolverprovider.ResourceFieldType;
import com.linkedin.data.template.StringArray;
import com.linkedin.policy.PolicyMatchCondition;
import com.linkedin.policy.PolicyMatchCriterion;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package com.datahub.authorization;

import com.datahub.authentication.Authentication;
import com.datahub.authorization.fieldresolverprovider.ResourceFieldType;
import com.linkedin.common.urn.Urn;
import com.linkedin.common.urn.UrnUtils;
import com.linkedin.data.template.StringArray;
Expand Down

This file was deleted.

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

import com.datahub.authentication.Authentication;
import com.datahub.authorization.FieldResolver;
import com.datahub.authorization.ResourceFieldType;
import com.datahub.authorization.ResourceSpec;
import com.linkedin.common.urn.Urn;
import com.linkedin.common.urn.UrnUtils;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.datahub.authorization.fieldresolverprovider;

import com.datahub.authorization.FieldResolver;
import com.datahub.authorization.ResourceFieldType;
import com.datahub.authorization.ResourceSpec;
import java.util.Collections;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.datahub.authorization.fieldresolverprovider;

import com.datahub.authorization.FieldResolver;
import com.datahub.authorization.ResourceFieldType;
import com.datahub.authorization.ResourceSpec;
import java.util.Collections;

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

import com.datahub.authentication.Authentication;
import com.datahub.authorization.FieldResolver;
import com.datahub.authorization.ResourceFieldType;
import com.datahub.authorization.ResourceSpec;
import com.linkedin.common.Ownership;
import com.linkedin.common.urn.Urn;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.datahub.authorization.fieldresolverprovider;

import com.datahub.authorization.FieldResolver;
import com.datahub.authorization.ResourceFieldType;
import com.datahub.authorization.ResourceSpec;


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ inactivePolicyUrn, new EntityResponse().setUrn(inactivePolicyUrn).setAspects(ina
10,
DataHubAuthorizer.AuthorizationMode.DEFAULT
);
_dataHubAuthorizer.init(Collections.emptyMap(), createAuthorizerContext(systemAuthentication, _entityClient));
_dataHubAuthorizer.invalidateCache();
Thread.sleep(500); // Sleep so the runnable can execute. (not ideal)
}
Expand Down Expand Up @@ -282,4 +283,8 @@ private Ownership createOwnershipAspect(final List<Urn> userOwners, final List<U
ownershipAspect.setLastModified(new AuditStamp().setTime(0).setActor(Urn.createFromString("urn:li:corpuser:foo")));
return ownershipAspect;
}

private AuthorizerContext createAuthorizerContext(final Authentication systemAuthentication, final EntityClient entityClient) {
return new AuthorizerContext(new DefaultResourceSpecResolver(systemAuthentication, entityClient));
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package com.datahub.authorization;

import com.datahub.authentication.Authentication;
import com.datahub.authorization.fieldresolverprovider.ResourceFieldType;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
Expand Down
Loading

0 comments on commit ce061e3

Please sign in to comment.