Skip to content

Commit

Permalink
Merge pull request #40 from patelhumaira21/custom_error
Browse files Browse the repository at this point in the history
Customizing the error message
  • Loading branch information
ashpak-shaikh authored Oct 31, 2023
2 parents 8af8db0 + 04ec64f commit 419cf83
Show file tree
Hide file tree
Showing 6 changed files with 44 additions and 7 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
This library enforces access control on GraphQL queries by checking for allowed types and fields. A GraphQL query that
has access to some of the requested fields/types will return:
* Requested fields it has access to
* Authorization Error message for the fields it does not have access to
* Authorization Error message for the fields it does not have access to. You can customize the error message by over-riding the
`getErrorMessage` method in the `ScopeProvider` interface.

```json lines
"errors": [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ FragmentDefinition redactFragment(FragmentDefinition fragmentDefinition, Executi

return (FragmentDefinition)
queryTransformer.transform(new RedactingVisitor(state, executionContext, authzListener,
authorizationExtension));
authorizationExtension, scopeProvider));
}


Expand All @@ -177,7 +177,7 @@ SelectionSet redactSelectionSet(ExecutionContext executionContext, AuthzInstrume
.build();

return (SelectionSet) transformer.transform(new RedactingVisitor(state, executionContext, authzListener,
authorizationExtension));
authorizationExtension, scopeProvider));
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import com.intuit.graphql.authorization.extension.AuthorizationExtension;
import com.intuit.graphql.authorization.extension.FieldAuthorizationEnvironment;
import com.intuit.graphql.authorization.extension.FieldAuthorizationResult;
import com.intuit.graphql.authorization.util.ScopeProvider;
import graphql.GraphQLError;
import graphql.GraphqlErrorBuilder;
import graphql.analysis.QueryVisitorFieldEnvironment;
Expand All @@ -27,15 +28,18 @@ public class RedactingVisitor extends QueryVisitorStub {
private final AuthzListener authzListener;
private final AuthorizationExtension authorizationExtension;

private final ScopeProvider scopeProvider;


public RedactingVisitor(AuthzInstrumentation.AuthzInstrumentationState state,
ExecutionContext executionContext, AuthzListener authzListener,
AuthorizationExtension authorizationExtension) {
ExecutionContext executionContext, AuthzListener authzListener,
AuthorizationExtension authorizationExtension, ScopeProvider scopeProvider) {
this.instrumentationState = state;
this.executionContext = executionContext;
this.authzListener = authzListener;
this.authorizationExtension = authorizationExtension;
this.typeFieldPermissionVerifier = instrumentationState.getTypeFieldPermissionVerifier();
this.scopeProvider = scopeProvider;
}

@Override
Expand All @@ -51,9 +55,14 @@ public void visitField(QueryVisitorFieldEnvironment queryVisitorFieldEnvironment
authzListener.onFieldRedaction(executionContext, queryVisitorFieldEnvironment);
Field field = queryVisitorFieldEnvironment.getField();

String errorMessage = scopeProvider.getErrorMessage(RedactionContext.builder()
.fieldCoordinates(FieldCoordinates.coordinates(parentName, field.getName()))
.field(field)
.build());

GraphQLError error = GraphqlErrorBuilder.newError()
.errorType(DataFetchingException)
.message("403 - Not authorized to access field=%s of type=%s", field.getName(), parentName)
.message(errorMessage)
.location(field.getSourceLocation())
.build();
instrumentationState.getAuthzErrors().add(error);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.intuit.graphql.authorization.enforcement;

import graphql.language.Field;
import graphql.schema.FieldCoordinates;
import lombok.Builder;
import lombok.Getter;

@Builder
@Getter
public class RedactionContext {
Field field;
FieldCoordinates fieldCoordinates;
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,22 @@
package com.intuit.graphql.authorization.util;

import com.intuit.graphql.authorization.enforcement.RedactionContext;

import java.util.HashSet;
import java.util.Set;

public interface ScopeProvider {

String DEFAULT_ERROR_MESSAGE = "403 - Not authorized to access field=%s of type=%s";

// 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<>();
}

default String getErrorMessage(RedactionContext redactionContext) {
return String.format(DEFAULT_ERROR_MESSAGE,
redactionContext.getField().getName(), redactionContext.getFieldCoordinates().getTypeName());
}

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

import com.intuit.graphql.authorization.extension.AuthorizationExtension;
import com.intuit.graphql.authorization.extension.FieldAuthorizationResult;
import com.intuit.graphql.authorization.util.ScopeProvider;
import graphql.GraphQLError;
import graphql.GraphqlErrorException;
import graphql.analysis.QueryVisitorFieldEnvironment;
Expand Down Expand Up @@ -54,6 +55,10 @@ public class RedactingVisitorTest {

@Mock
private QueryVisitorFieldEnvironment queryVisitorFieldEnvironment;

@Mock
private ScopeProvider scopeProvider;

@Mock
private GraphQLFieldDefinition fieldDefinition;

Expand All @@ -75,7 +80,7 @@ public void setup() {
when(instrumentationState.getAuthzErrors()).thenReturn(graphQLErrorList);

subjectUnderTest = new RedactingVisitor(instrumentationState, executionContext, authzListener,
authorizationExtension);
authorizationExtension, scopeProvider);

when(queryVisitorFieldEnvironment.getParentType()).thenReturn(PARENT_TYPE);
when(queryVisitorFieldEnvironment.getFieldDefinition()).thenReturn(fieldDefinition);
Expand Down

0 comments on commit 419cf83

Please sign in to comment.