Graphql-shield: How to use rules on input types and arguments for authorization?

Created on 16 Apr 2019  Â·  8Comments  Â·  Source: maticzav/graphql-shield

According to https://github.com/maticzav/graphql-shield#rules-on-input-types-or-arguments, it's possible to use rules to validate argument value.

  1. How to apply rules to input types (and specific fields of input types)? In the https://github.com/maticzav/graphql-shield#rules-on-input-types-or-arguments example the rule is in fact applied to the argument, not the input type, and validates the argument as a nested object, instead of applying rules to the fields of the input type.

  2. How to apply rules to arguments and input types (and specific fields of input types) for authorization (permission validation), not value validation?

Suppose there is the following schema:

type Mutation {
  createUser(name: String, email: String, age: Int, verified: Boolean): User!
  updateUser(id: ID!, name: String, email: String, age: Int, verified: Boolean): User!
  deleteUser(id: ID!): User
  ...
}

It's possible to restrict the whole mutation, e.g.:

const permissions = shield({
  Mutation: {
    updateUser: isAdmin,
  }
})

But what if I want to restrict only updating of the verified field (but allow updating the rest fileds) in the updateUser mutation, not the whole mutation (by applying the rule to the argument)?

Is it possible to do something like the following?

const permissions = shield({
  Mutation: {
    updateUser(
      verified: isAdmin
    )
  }
})

The mutation arguments may be extracted to separate input types (hello, Prisma! :wave:). In this case the rules should be applied to input type fields. Something like:

type Mutation {
  updateUser(data: UserUpdateInput!, where: UserWhereUniqueInput!): User!
  ...
}

...

input UserUpdateInput {
  name: String
  email: String
  age: Int
  verified: Boolean
}
const permissions = shield({
  UserUpdateInput: {
    email: or(isOwner, isAdmin)
    verified: isAdmin
  }
})

Is it possible?

Expected behavior

The _partial response_ is one of the basic principles of GraphQL: a response may contain both a partial data as well as encountered errors in the case that a field error occurred on a field which was replaced with null.

If a field’s resolve function throws an error, the error will be inserted into the response’s errors key and the field will resolve to null.

If the null value returns in a resolver for a non-null field, the null result bubbles up to the nearest nullable parent.

This is what concerns data querying (reading). But what about data mutating (writing)?

Suppose there is a mutation that has 3 arguments, and we add validations to all these arguments.

Current behavior: if at least one of the mutation arguments fails validation, the entire mutation will not be executed. Right?

But what if this argument (which failed validation) is not non-null (optional / not required)? In this case the mutation could be executed with two rest arguments.

Is it possible to just remove this argument from mutation if it fails validation?

The argument can be input type (nested object) i.e. contains other input types with its own fields that can also be input types, and so on.

If the field from depth (deep nested input type) fails (permission or value) validation and this field is not non-null (optional / not required), this field should just be removed.

If the field from depth (deep nested input type) fails (permission or value) validation and this field is non-null (required), the null should bubbles up to the nearest nullable parent.

In the case of field (optional or required) was removed, the response should contain both a mutation response as well as encountered errors in the case that a field error occurred on a input field which was removed.

kinfeature

All 8 comments

Hey @FluorescentHallucinogen :wave:,

Thank you for opening an issue. We will get back to you as soon as we can. Also, check out our Open Collective and consider backing us.

https://opencollective.com/graphql-shield

PS.: We offer priority support for all backers. Don't forget to add priority label when you start backing us :smile:

I am not sure I understand the idea behind your issue. Could you read my summary and verify that I correctly understand your problem?

First, let me clarify that docs in README are in a way misleading towards input types. You cannot apply rules to InputType however my reasoning behind it is that it makes very little difference in usage and a significant one in implementation of graphql-middleware and graphql-shield if we were to support "the" input types, as opposed to merely verifying arguments.

Back to the point, if I understand correctly, you would want to limit the arguments in a way that would delete the provided fields if they don't match the rule. Is this the expected behaviour that you are explaining? 🙂

@maticzav

you would want to limit the arguments in a way that would delete the provided fields if they don't match the rule. Is this the expected behaviour that you are explaining?

Yes.

docs in README are in a way misleading towards input types. You cannot apply rules to InputType

It would be nice to clarify the docs. :slightly_smiling_face:

@maticzav

it makes very little difference in usage and a significant one in implementation of graphql-middleware and graphql-shield if we were to support "the" input types, as opposed to merely verifying arguments.

My reasoning is applying rules directly to the input types (instead of arguments) is more natural and intuitive. This is how for example the another approach with GraphQL custom directives works (input types can be nested and reusable between more than one query/mutation and directives applies directly to the input types).

@maticzav

if we were to support "the" input types, as opposed to merely verifying arguments.

_in addition to_, not _as opposed to_. :wink:

Sometimes it may be useful to apply the rules to the argument instead of input types e.g. to

  • keep the "context" of input type in the case when the input types are reusable between more than one query/mutation (since input types are not self-sufficient things and can be used only in the "context" of specific queries/mutations).

  • compounded validation: checking (using) the values of multiple fields at the same time inside the rule.

@maticzav PTAL at the implementation by @uxname:

https://github.com/uxname/uxbackend/blob/master/src/helper/InputSheldFilter.js

Could you please integrate it (or something similar) into graphql-shield?

Hey đź‘‹,

Excuse my absence, I had final examinations at school, back at programming now. My point with nesting input types etc., stemmed from the idea that you can also reuse rules across your rule tree. Therefore, I don't see benefit in having rules applicable to input types if we could make them more declarative by applying the same rule in multiple places.

Perhaps I haven't thought this through completely, is the problem here that you cannot nest input rules when inputs take ”objects” as parameters?

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

nolandg picture nolandg  Â·  5Comments

donedgardo picture donedgardo  Â·  6Comments

devautor picture devautor  Â·  8Comments

tnolan8 picture tnolan8  Â·  5Comments

jgoux picture jgoux  Â·  5Comments