Skip to content

Commit

Permalink
Merge pull request #28 from graph-quilt/remove_enforcement
Browse files Browse the repository at this point in the history
Changing the interface and removing the enforcement
  • Loading branch information
ashpak-shaikh authored Oct 5, 2023
2 parents 2fec08f + d79caf7 commit 5fb5a4f
Show file tree
Hide file tree
Showing 21 changed files with 133 additions and 150 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ jobs:
steps:
- uses: actions/checkout@v2
- name: Set up JDK 11
uses: actions/setup-java@v2
uses: actions/setup-java@v3
with:
java-version: '8'
distribution: 'adopt'
java-version: '11'
distribution: 'corretto'
server-id: ossrh
server-username: MAVEN_USERNAME
server-password: MAVEN_PASSWORD
Expand Down
5 changes: 3 additions & 2 deletions .github/workflows/maven_release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@ jobs:
- uses: actions/checkout@v2

- name: Set up Maven Central Repository
uses: actions/setup-java@v1
uses: actions/setup-java@v3
with:
java-version: 8
java-version: 11
distribution: corretto
server-id: ossrh
server-username: MAVEN_USERNAME
server-password: MAVEN_PASSWORD
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ has access to some of the requested fields/types will return:

```java
GraphQL.newGraphQL(schema)
.instrumentation(new AuthzInstrumentation(authzConfiguration, schema, principleFetcher))
.instrumentation(new AuthzInstrumentation(authzConfiguration, schema, scopeProvider))
.build();
```

Expand Down
4 changes: 2 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,8 @@
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>8</source>
<target>8</target>
<source>11</source>
<target>11</target>
</configuration>
</plugin>
<plugin>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,15 @@
package com.intuit.graphql.authorization.config;

import lombok.Data;
import lombok.Getter;

/**
* This class represents the client calling your GraphQL API. You can extend this class
* to add more attributes for your client.
*/
@Data
public class AuthzClient {

/*
The scopeid or clientid or appid of your client.
*/
private String id;
private String description;
private ClientAuthorizationType type;

public enum ClientAuthorizationType implements RuleType {
OFFLINE("offline"),
ONLINE("online");

@Getter
private String name;

ClientAuthorizationType(String name) {
this.name = name;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,15 @@
import java.util.List;
import java.util.Map;

/**
* This client represents the configuration that is needed to initialize the
* {@link com.intuit.graphql.authorization.enforcement.AuthzInstrumentation} class. It represnts a map of your client
* against the list of queries that defines the access control.
*/
public interface AuthzClientConfiguration {

/**
* Provide the access control map
*/
Map<AuthzClient, List<String>> getQueriesByClient();
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,7 @@ public AuthorizationHolder(Map<String, Map<String, Set<String>>> scopeToType) {
this.scopeToTypeMap = Collections.unmodifiableMap(scopeToType);
}


public TypeFieldPermissionVerifier getPermissionsVerifier(Set<String> scopes, GraphQLSchema schema) {

return new TypeFieldPermissionVerifier(schema,
Collections.unmodifiableMap(
scopes.stream()
Expand All @@ -31,5 +29,4 @@ public TypeFieldPermissionVerifier getPermissionsVerifier(Set<String> scopes, Gr
(oldSet, newSet) -> SetUtils.union(oldSet, newSet)
))));
}

}
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
package com.intuit.graphql.authorization.enforcement;

import static org.apache.commons.lang3.ObjectUtils.defaultIfNull;

import com.intuit.graphql.authorization.config.AuthzClientConfiguration;
import com.intuit.graphql.authorization.extension.AuthorizationExtension;
import com.intuit.graphql.authorization.extension.AuthorizationExtensionProvider;
import com.intuit.graphql.authorization.extension.DefaultAuthorizationExtensionProvider;
import com.intuit.graphql.authorization.rules.AuthorizationHolderFactory;
import com.intuit.graphql.authorization.rules.QueryRuleParser;
import com.intuit.graphql.authorization.util.GraphQLUtil;
import com.intuit.graphql.authorization.util.PrincipleFetcher;
import com.intuit.graphql.authorization.util.ScopeProvider;
import graphql.ExecutionResult;
import graphql.ExecutionResultImpl;
import graphql.GraphQLError;
Expand All @@ -26,13 +28,15 @@
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import lombok.Builder;
import lombok.Builder.Default;
import lombok.Data;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
Expand All @@ -43,27 +47,30 @@ public class AuthzInstrumentation extends SimpleInstrumentation {
private static final AuthzListener DEFAULT_AUTHZ_LISTENER = new SimpleAuthZListener();
private static final AuthorizationExtensionProvider DEFAULT_AUTH_EXTENSION_PROVIDER = new DefaultAuthorizationExtensionProvider();
private final AuthorizationHolder authorizationHolder;
private final PrincipleFetcher principleFetcher;
private AuthzListener authzListener = DEFAULT_AUTHZ_LISTENER;
private final AuthorizationExtensionProvider authorizationExtensionProvider;
private final ScopeProvider scopeProvider;

public AuthzInstrumentation(AuthzClientConfiguration configuration, GraphQLSchema schema,
PrincipleFetcher principleFetcher, AuthzListener authzListener,
@Default
private AuthzListener authzListener = DEFAULT_AUTHZ_LISTENER;
@Default
private AuthorizationExtensionProvider authorizationExtensionProvider = DEFAULT_AUTH_EXTENSION_PROVIDER;

@Builder
public AuthzInstrumentation(
@NonNull AuthzClientConfiguration configuration,
@NonNull GraphQLSchema schema,
@NonNull ScopeProvider scopeProvider,
AuthzListener authzListener,
AuthorizationExtensionProvider authorizationExtensionProvider) {

if (configuration.getQueriesByClient().isEmpty()) {
throw new IllegalArgumentException("Clients missing from AuthZClientConfiguration");
}

this.authorizationHolder = new AuthorizationHolder(
getAuthorizationFactory(schema).parse(configuration.getQueriesByClient()));
this.principleFetcher = principleFetcher;
this.authzListener = (Objects.nonNull(authzListener)) ? authzListener : DEFAULT_AUTHZ_LISTENER;
this.authorizationExtensionProvider = authorizationExtensionProvider;
}

public AuthzInstrumentation(AuthzClientConfiguration configuration, GraphQLSchema schema,
PrincipleFetcher principleFetcher, AuthzListener authzListener) {
this(configuration, schema, principleFetcher, authzListener, DEFAULT_AUTH_EXTENSION_PROVIDER);
this.scopeProvider = scopeProvider;
this.authzListener = defaultIfNull(authzListener, DEFAULT_AUTHZ_LISTENER);
this.authorizationExtensionProvider = defaultIfNull(authorizationExtensionProvider, DEFAULT_AUTH_EXTENSION_PROVIDER);
}

static AuthorizationHolderFactory getAuthorizationFactory(GraphQLSchema graphQLSchema) {
Expand All @@ -77,28 +84,23 @@ public AuthzInstrumentationState createState(InstrumentationCreateStateParameter
// instrumentation state is passed during each invocation of an Instrumentation method
// and allows you to put stateful data away and reference it during the query execution
//
Set<String> scopes = principleFetcher.getScopes(parameters.getExecutionInput().getContext());

//TODO: externalize enforcement open or close decision
boolean enforce =
!principleFetcher.authzEnforcementExemption(parameters.getExecutionInput().getContext())
&& CollectionUtils.isNotEmpty(scopes);
Set<String> scopes = scopeProvider.getScopes(parameters.getExecutionInput().getContext());

authzListener.onCreatingState(enforce, parameters.getSchema(), parameters.getExecutionInput());
return new AuthzInstrumentationState(
authorizationHolder.getPermissionsVerifier(scopes, parameters.getSchema()),
parameters.getSchema(), scopes, enforce);
authzListener.onCreatingState(parameters.getSchema(), parameters.getExecutionInput());
return new AuthzInstrumentationState(authorizationHolder.getPermissionsVerifier(scopes, parameters.getSchema()),
parameters.getSchema(), scopes);
}


@Override
public ExecutionContext instrumentExecutionContext(ExecutionContext executionContext,
InstrumentationExecutionParameters parameters) {
AuthzInstrumentationState state = parameters.getInstrumentationState();
AuthorizationExtension authorizationExtension = this.authorizationExtensionProvider.getAuthorizationExtension(executionContext, parameters);
ExecutionContext enforcedExecutionContext =
state.isEnforce() ? getAuthzExecutionContext(executionContext, state, authorizationExtension) : executionContext;
authzListener.onEnforcement(state.isEnforce(), executionContext, enforcedExecutionContext);
AuthorizationExtension authorizationExtension = this.authorizationExtensionProvider.getAuthorizationExtension(
executionContext, parameters);
ExecutionContext enforcedExecutionContext = getAuthzExecutionContext(executionContext, state,
authorizationExtension);
authzListener.onEnforcement(executionContext, enforcedExecutionContext);
return enforcedExecutionContext;
}

Expand Down Expand Up @@ -142,10 +144,11 @@ private QueryTransformer.Builder initQueryTransformerBuilder(ExecutionContext ex
.fragmentsByName(executionContext.getFragmentsByName());
}

Map<String, FragmentDefinition> redactFragments(ExecutionContext executionContext, AuthzInstrumentationState state, AuthorizationExtension authorizationExtension) {
Map<String, FragmentDefinition> redactFragments(ExecutionContext executionContext, AuthzInstrumentationState state,
AuthorizationExtension authorizationExtension) {
//treat each fragment as root and redact based on configuration
return executionContext.getFragmentsByName().values().stream().map(entry ->
redactFragment(entry, executionContext, state, authorizationExtension))
redactFragment(entry, executionContext, state, authorizationExtension))
.collect(Collectors.toMap(FragmentDefinition::getName, Function.identity()));
}

Expand All @@ -163,7 +166,8 @@ FragmentDefinition redactFragment(FragmentDefinition fragmentDefinition, Executi
}


SelectionSet redactSelectionSet(ExecutionContext executionContext, AuthzInstrumentationState state, AuthorizationExtension authorizationExtension) {
SelectionSet redactSelectionSet(ExecutionContext executionContext, AuthzInstrumentationState state,
AuthorizationExtension authorizationExtension) {
GraphQLObjectType rootType = GraphQLUtil.getRootTypeFromOperation(executionContext.getOperationDefinition(),
executionContext.getGraphQLSchema());

Expand All @@ -181,7 +185,7 @@ SelectionSet redactSelectionSet(ExecutionContext executionContext, AuthzInstrume
public DataFetcher<?> instrumentDataFetcher(DataFetcher<?> dataFetcher,
InstrumentationFieldFetchParameters parameters) {
AuthzInstrumentationState state = parameters.getInstrumentationState();
return state.isEnforce() ? new IntrospectionRedactingDataFetcher(dataFetcher, state) : dataFetcher;
return new IntrospectionRedactingDataFetcher(dataFetcher, state);
}

@Data
Expand All @@ -191,7 +195,6 @@ static class AuthzInstrumentationState implements InstrumentationState {
private final TypeFieldPermissionVerifier typeFieldPermissionVerifier;
private final GraphQLSchema graphQLSchema;
private final Set<String> scopes;
private final boolean enforce;
private List<GraphQLError> authzErrors = new LinkedList<>();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,17 @@ void onFieldRedaction(final ExecutionContext executionContext,
/**
* This will be called just before creating authz instrumentation state.
*
* @param isEnforce boolean representing if policy will be enforced.
* @param schema the graphql schema.
* @param executionInput the execution input.
*/
void onCreatingState(final boolean isEnforce, final GraphQLSchema schema, final ExecutionInput executionInput);
void onCreatingState( final GraphQLSchema schema, final ExecutionInput executionInput);

/**
* This will be called after enforcing authz policy on the execution input if applicable.
*
* @param isEnforce boolean representing if policy will be enforced.
* @param originalExecutionContext execution context before authz policy is enforced.
* @param enforcedExecutionContext execution context after authz policy is enforced.
*/
void onEnforcement(final boolean isEnforce, final ExecutionContext originalExecutionContext,
void onEnforcement(final ExecutionContext originalExecutionContext,
final ExecutionContext enforcedExecutionContext);
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ public void onFieldRedaction(ExecutionContext executionContext,
}

@Override
public void onCreatingState(boolean isEnforce, GraphQLSchema schema, ExecutionInput executionInput) {
public void onCreatingState(GraphQLSchema schema, ExecutionInput executionInput) {
//do nothing
}

@Override
public void onEnforcement(boolean isEnforce, ExecutionContext originalExecutionContext,
public void onEnforcement(ExecutionContext originalExecutionContext,
ExecutionContext enforcedExecutionContext) {
//do nothing
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,7 @@ public class TypeFieldPermissionVerifier implements PermissionVerifier {
private final Map<String, Set<String>> typeToFieldsMap;
private final GraphQLSchema schema;

TypeFieldPermissionVerifier(GraphQLSchema schema,
Map<String, Set<String>> typeToFieldsMap) {
TypeFieldPermissionVerifier(GraphQLSchema schema, Map<String, Set<String>> typeToFieldsMap) {
this.typeToFieldsMap = typeToFieldsMap;
this.schema = schema;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,11 @@
import java.util.HashSet;
import java.util.Set;

public interface PrincipleFetcher {
public interface ScopeProvider {

// This method needs to return a set of strings if the scopes are passed and an empty set if not passed
default Set<String> getScopes(Object o) {
return new HashSet<>();
}

// This method returns if a client/appid need to exempt enforcement
default boolean authzEnforcementExemption(Object o) {
return false;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@

import static org.junit.Assert.assertTrue;

import com.intuit.graphql.authorization.util.PrincipleFetcher;
import com.intuit.graphql.authorization.util.ScopeProvider;
import graphql.schema.GraphQLObjectType;
import org.junit.Test;
import org.mockito.Mock;

public class ExecutionScopeFetcherTest {

PrincipleFetcher principleFetcher = new PrincipleFetcher() {
ScopeProvider scopeProvider = new ScopeProvider() {
};

@Mock
Expand All @@ -19,7 +19,7 @@ public class ExecutionScopeFetcherTest {
@Test
public void getScopesByDefault() {

assertTrue(principleFetcher.getScopes(graphQLObjectType).isEmpty());
assertTrue(scopeProvider.getScopes(graphQLObjectType).isEmpty());

}

Expand Down
Loading

0 comments on commit 5fb5a4f

Please sign in to comment.