Implement ApiProperty security attribute#3227
Conversation
f53b219 to
071b71b
Compare
071b71b to
65cbde8
Compare
|
I find this interesting, @dunglas is this what you had in mind? |
antograssiot
left a comment
There was a problem hiding this comment.
doesn't the api_platform.jsonld.normalizer.item, api_platform.jsonapi.normalizer.item and api_platform.hal.normalizer.item services declaration needs to be updated as well for this to work in real apps ?
| protected $localCache = []; | ||
|
|
||
| public function __construct(PropertyNameCollectionFactoryInterface $propertyNameCollectionFactory, PropertyMetadataFactoryInterface $propertyMetadataFactory, IriConverterInterface $iriConverter, ResourceClassResolverInterface $resourceClassResolver, PropertyAccessorInterface $propertyAccessor = null, NameConverterInterface $nameConverter = null, ClassMetadataFactoryInterface $classMetadataFactory = null, ItemDataProviderInterface $itemDataProvider = null, bool $allowPlainIdentifiers = false, array $defaultContext = [], iterable $dataTransformers = [], ResourceMetadataFactoryInterface $resourceMetadataFactory = null) | ||
| public function __construct(PropertyNameCollectionFactoryInterface $propertyNameCollectionFactory, PropertyMetadataFactoryInterface $propertyMetadataFactory, IriConverterInterface $iriConverter, ResourceClassResolverInterface $resourceClassResolver, ResourceAccessCheckerInterface $resourceAccessChecker, PropertyAccessorInterface $propertyAccessor = null, NameConverterInterface $nameConverter = null, ClassMetadataFactoryInterface $classMetadataFactory = null, ItemDataProviderInterface $itemDataProvider = null, bool $allowPlainIdentifiers = false, array $defaultContext = [], iterable $dataTransformers = [], ResourceMetadataFactoryInterface $resourceMetadataFactory = null) |
There was a problem hiding this comment.
Additional arguments must be added at the end with and with a default value or it is a BC break
| return false; | ||
| } | ||
|
|
||
| return parent::isAllowedAttribute($classOrObject, $attribute, $format, $context); |
There was a problem hiding this comment.
The parent method should be called first. No need to do extra work when we can return the value directly if it's already false. We only need to check against the security attribute when the parent method returns true, since we're taking a blacklist approach here.
| * @ORM\Column | ||
| * @Assert\NotBlank | ||
| * @ApiProperty(iri="http://schema.org/name") | ||
| * @ApiProperty(iri="http://schema.org/name", security="is_granted('ROLE_USER')") |
There was a problem hiding this comment.
Please don't make this change. I suspect this will affect many existing tests. The new property you've added ($adminOnlyProperty) is enough.
|
Just to provide some background: @fredericbarthelet discussed this with me during the Hackday in Paris. Thanks for following up on this! |
67cf534 to
c56fe21
Compare
| * @ORM\Column(nullable=true) | ||
| * @ApiProperty(security="is_granted('ROLE_ADMIN')") | ||
| */ | ||
| private $adminOnlyProperty; |
There was a problem hiding this comment.
the same changes must be done in tests/Fixtures/TestBundle/Document/Dummy.php so the mongoDB suite will pass too
There was a problem hiding this comment.
Just a suggestion, but using another Entity/Document will avoid to have to modify filters tests
Maybe this could be added to the SecuredDummy instead, it will have less side impacts and will be at the same place than the security attribute from ApiResource
There was a problem hiding this comment.
@antograssiot that what I noticed as well when I started implementing behat tests. I will move it to SecuredDummy instead :)
7bffc91 to
4bcb395
Compare
|
@soyuka, @antograssiot, @teohhanhui I updated my PR to take into account your comments. Thank you for your feedbacks :) ! I encountered an issue when integrated security property on graphQL API. Resource Request I get a 200 HTTP answer with an My idea would be to inject as well |
49ad651 to
28b4d38
Compare
28b4d38 to
2aa51f9
Compare
|
@fredericbarthelet I'm not sure it's a good idea to check the security inside the FieldsBuilder. The GraphQL system has two steps:
The built schema should not be dynamic and dependent on the query. That's why you can remove fields for instance by using non-dynamic normalization context but not in your case IMO. I'm not sure it's the best way, but my proposal would be to make the field nullable (or to throw an exception to force the user to make it nullable?) if it has a security attribute and set it to null if the user doesn't have the rights to see the value of this field. |
That does not make sense... :x We should find a better solution. |
|
From what I read in the GraphQL examples, it seems to be the current practice: https://graphql.org/learn/authorization/ |
|
I don't see the relevance of the link you've shared. I'm not advocating doing it at the GraphQL layer. Making fields nullable is propagating this authorization logic to the GraphQL layer, which is exactly what they've warned against. |
|
In most GraphQL systems, the schema cannot change dynamically depending on the context (very often it's a static file). So you have to make sure the fields can be nullable if you want some users to not see the corresponding value. That's also why in the type system, the types are nullable by default. If you take the GitHub API (https://developer.github.com/v4), 95% of the fields are nullable. |
|
See also: graphql/graphql-js#113 |
|
GraphQL Ruby seems to be the only one doing it. I'm not sure we should too. https://graphql-ruby.org/authorization/visibility |
|
That sounds like a good strategy, yes. Let's get this feature for REST first. |
|
I think it will be good to be coherent with GraphQL too...
The @api-platform/core-team should make a choice and not just leave the problem because it's GraphQL. |
|
I've added an issue for the |
|
I agree that it should be supported for GraphQL too, but it needs more work in the GraphQL system of API Platform before that's possible. We don't need to block this PR because of that. The nullable proposal is not feasible, per discussion above. |
|
The nullable property is totally feasible.
I think you misunderstood what they say. First of all, they are talking about the resolver part of the GraphQL system. We are talking about the building part here. Secondly, it's only a recommendation to avoid duplication. Here we can do this with no issue like this. And finally the "problem" would be the same for the Now that the GraphQL system is introduced and is fully working, we should make sure new features are working for it too. |
|
I've already responded to this comment. |
|
Blocked? No we're not. This |
|
Do you have examples of other GraphQL implementations which return null on unauthorized access? I'm not convinced that it's a feasible alternative. From the discussions I've seen, it's either no authorization on property level, or implement the visibility feature. |
|
Sure... I'm returning your question: please find a GraphQL system which doesn't work like this. |
|
Can also confirm it’s the case for OnyxGraphQL as well. Note that it’s the
same on error: but in both cases you may have an error entry besides the
data one in the returned payload
…On Wed 20 Nov 2019 at 16:30, Alan Poulain ***@***.***> wrote:
Sure...
GraphQLite:
https://graphqlite.thecodingmachine.io/docs/authentication_authorization#constant-schemas
GraphQL Java: graphql-java/graphql-java#290 (comment)
<graphql-java/graphql-java#290 (comment)>
Graphene:
http://docs.graphene-python.org/projects/django/en/latest/authorization/#filtering-id-based-node-access
I'm returning your question: please find a GraphQL system which *doesn't*
work like this.
—
You are receiving this because you are on a team that was mentioned.
Reply to this email directly, view it on GitHub
<#3227?email_source=notifications&email_token=ABHPVAKG6NWE35E5VL7N6OLQUVJZ3A5CNFSM4JHMSQR2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEESLZ3I#issuecomment-556055789>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/ABHPVAJPEYOIOBKE2M7A4JTQUVJZ3ANCNFSM4JHMSQRQ>
.
|
|
Some GraphQL systems have not implemented the visibility feature yet. |
|
It is, because you requested access to a field you don’t have access to.
That said I would recommend to stick to the standards and not going for a
new exotic solution
…On Wed 20 Nov 2019 at 16:54, Alan Poulain ***@***.***> wrote:
Some GraphQL systems have not implemented the visibility feature yet.
@theofidry <https://github.com/theofidry> having an error entry could be
a third solution. I don't really like it because it's not really an error
for me but we can discuss it.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#3227?email_source=notifications&email_token=ABHPVAPDGUK6PKY4OUIUKO3QUVMTLA5CNFSM4JHMSQR2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEESPGAA#issuecomment-556069632>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/ABHPVAKJFCF7OLQXBMW2XOLQUVMTLANCNFSM4JHMSQRQ>
.
|
|
Note however that I’m no GraphQL expert. I’m merely using one PHP
implementation and was pointing out how it behaves as a reference
…On Wed 20 Nov 2019 at 16:57, Théo FIDRY ***@***.***> wrote:
It is, because you requested access to a field you don’t have access to.
That said I would recommend to stick to the standards and not going for a
new exotic solution
On Wed 20 Nov 2019 at 16:54, Alan Poulain ***@***.***>
wrote:
> Some GraphQL systems have not implemented the visibility feature yet.
> @theofidry <https://github.com/theofidry> having an error entry could be
> a third solution. I don't really like it because it's not really an error
> for me but we can discuss it.
>
> —
> You are receiving this because you were mentioned.
>
>
> Reply to this email directly, view it on GitHub
> <#3227?email_source=notifications&email_token=ABHPVAPDGUK6PKY4OUIUKO3QUVMTLA5CNFSM4JHMSQR2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEESPGAA#issuecomment-556069632>,
> or unsubscribe
> <https://github.com/notifications/unsubscribe-auth/ABHPVAKJFCF7OLQXBMW2XOLQUVMTLANCNFSM4JHMSQRQ>
> .
>
|
b1f159a to
399ab49
Compare
|
I updated my PR, removing the implementation I had for GraphQL part for now. @alanpoulain I would be happy to help implement the desired strategy on graphQL in a seperate PR related to the newly opened issue #3279 if that's OK for you :) Any remaining comments on rest part on this one @teohhanhui ? |
399ab49 to
5f1bbf1
Compare
|
I like this, could you also add tests for writing operations? |
dunglas
left a comment
There was a problem hiding this comment.
Sorry for the late review, but it's awesome!
|
Finished in #3503, can be closed. |
|
thanks @fredericbarthelet |
|
For the record, I'm also in favor of returning |
Implement new
securityattribute on ApiProperty annotations.Allows built-in support for property removal from resource using acl expression language.
This initial PR is a POC to explain my intention :
securityResourceAccessCheckerwithinAbstractItemNormalizerisAllowedAttributemethod inherited from symfonyAbstractNormalizerserviceWDYT ?