[keycloak-user] Using Keycloak for per field authorisation

Pedro Igor Silva psilva at redhat.com
Mon Aug 13 21:54:12 EDT 2018


Sorry for the late reply ...

On Fri, Aug 10, 2018 at 3:46 PM, Courtney Robinson <
courtney.robinson at hypi.io> wrote:

> I'm a little confused about what type A and B are. You also mentioned you
>>> may have an "instance of A".  Does that mean that types A and B represent
>>> generic resources which policies should be enforced on instances of A and B
>>> ? Are these instances user-managed resources ?
>>>
>>
> Types A and B are GraphQL objects. If you're not familiar then just think
> of them as classes A and B with only class members/fields defined.
> I'm not explaining this very well I think so another way to think about it
> is like this.
>
> By default I want to apply some global policies to classes A and B that
> says for example, anyone in Organisation 1 can read/write classes A and B.
> This is "system defined".
> More concretely imagine classes User and Book. Two organisations Org1 and
> Org2.
> When we create a realm for each org, we want to tell Keycloak that some
> app X has two types User and Book and any user in the current realm can
> read/write fields of both User and Book within this realm.
>
> We also want to tell Keycloak that an "admin" (I imagine a user with some
> role or in a specific group) in this realm can add policies that override
> the above behaviour.
> Given one or more of these admins, they can then choose to create a policy
> that says "Users from site A can read/write all fields of User and Book but
> users from site B can only read some fields from User and all fields from
> Book".
>
> The other case I mentioned in my first email is where, a user in an
> organisation has created e.g. a "Book" record. Under default policies only
> they can see and manage this Book they created. They can then share this
> book with site 2 so that users in site 2 can also read the book, they may
> also share it with the whole organisation so anyone in the realm can read
> it. The extension to this is when members of one site could normally only
> see a subset of fields from Book but one one occasion a user shares a
> single Book with one or more members giving then the permission to "manage"
> this one Book. This lifts the limitation on this one instance of book
> enabling the user it is shared with to access a field of Book they wouldn't
> have been able to see before.
>

Now I see, thanks. I was imagining something like that.

In Keycloak, resource can have a type [1]. A "typed" resource is a resource
which owner is the resource server (when I say resource server, I mean the
client application you are setting up permissions). You can define
permissions to these typed resources [2] where these permissions will be
considered when deciding whether or not access should be granted to other
resources with the same type, where these other resources are actually
instances of the "typed" resource (the class example you gave). Resource
instances have users as resource owners, not the resource server. That is
the difference between a typed vs resource instance.

Let's take the case you mentioned as an example. You have a "Book Resource"
with a type "book" and permissions associated with this book. Where
policies associated with this permission only grants access to the owner of
the resource. Once you have this configuration, create a resource as
follows:

curl -X POST \
  http://localhost:8180/auth/realms/{realm}/authz/protection/resource_set \
  -H 'Authorization: Bearer {access_token}' \
  -H 'Content-Type: application/json' \
  -d '{
"name":"Alice Book",
"type": "book",
"resource_scopes":["read", "manage"],
"owner":"alice"
      }'

The command above is creating a book where user "alice" is the owner. Note
the "type" field defined as "book". Once you do that, if you try to use the
policy evaluation tool [3] to check permissions for "alice" on resource
"Alice Book", access should be granted. Note that we did not define any
permission for "Alice Book" directly, permissions are being processed based
on what was defined for "Book Resource".

We also allow you to override permissions on a per resource instance basis.
So, suppose you want to also allow some other user to access "Alice Book".
You just create a permission for this resource granting access to the user
you want.

We also support UMA, a standard focused on privacy and resource sharing
requirements [4]. We have an extension to UMA (contribution from community)
that allows resource servers to define custom permissions for user-managed
resources [5]. When using UMA capabilities, your users are allowed to
manage their resources via Keycloak Account Service.

[1] https://www.keycloak.org/docs/latest/authorization_servi
ces/index.html#typed-resources
[2] https://www.keycloak.org/docs/latest/authorization_servi
ces/index.html#_permission_typed_resource
[3] https://www.keycloak.org/docs/latest/authorization_servi
ces/index.html#_policy_evaluation_overview
[4] https://www.keycloak.org/docs/latest/authorization_
services/index.html#_service_user_managed_access
[5] https://www.keycloak.org/docs/latest/authorization_
services/index.html#_service_authorization_uma_policy_api


>
>
>>
>>
>> Regarding how the adapter (policy enforcer in particular) work. It
>> verifies permissions locally in case the client is sending a bearer token
>> with permissions, otherwise the adapter will query the server for
>> permissions associated with resource the client is trying to access
>> (mapping is based on URIs).
>>
>
> I had a feeling. One of the reasons I'm so unclear about how to achieve
> what we want is that it feels like a mixture of the auth policies Keycloak
> supports is needed.
> I've been thinking that for each app, default Keycloak policies are
> created that e.g. allowed read only within an organisation.
> Use Keycloak groups to represent "sites" e.g. offices in an organisation
> and applied default policies to the groups.
> Then, the key thing I thought was that I'd have to register each type and
> it's fields in Keycloak as resources and then have a fixed list of scopes
> read, write, update, delete, share etc
> When an entry/instance (say of a Book) is shared, the model changes from
> being type based to being based on the ID of the object i.e. the Book's ID
>

It makes sense. However, I don't think the model needs to change. Your
typed resource will always be there. What you need to do is create new
resources (with their corresponding types) representing those
"entries/instances".


>
> If I understood correctly, that means the size of the token will grow with
> each object shared directly with a user, surely a problem.
>

Not really. It depends on how you want to enforce ermissions.

If you don't want to evaluate permissions all the time, you can obtain a
token with some initial permissions. Then you can perform incremental
authorization to obtain more permissions in addition to those previously
granted. We also support limiting the number of permissions in a response
from the server. [4]

In next release, we are also supporting a response_mode parameter that you
could use to define the format of responses from server. These formats are
specially useful in case you don't want to use permissions from access
tokens but invoke server to only obtain permissions.

[4] https://www.keycloak.org/docs/latest/authorization_services/
index.html#_service_obtaining_permissions


> In a similar vein, do I have to create an entry for every Book record in
> keycloak to be able to do per book permissions?
>

Yes. You could use a single resource + JS policy too, but there are several
issues doing that.

People suggested a Resource SPI, which could be used to fetch resources
from external databases ...


More information about the keycloak-user mailing list