Considering you are in control on how the ticket is created and how claims
are set on it, would be an option to use a specific claim for each tenant
so that in your policies you check tenants based on the claim's key ?
On Thu, Jul 25, 2019 at 9:12 AM Álvaro Gómez <
alvaro.gomez.gimenez(a)tecsisa.com> wrote:
Hi Pedro,
We are performing HTTP ticket requests from our application (An API acting
as a Resource Server). As an example, having the following endpoint:
GET /api/tenants/__TENANT_ID__/products/__PRODUCT_ID__
If we use non-scoped resources (In order to simplify the example) the API
behaves as follows:
** The Requesting Party performs this action:
GET /api/tenants/Organization-1/products/Product-X
1.- If there is no "permissions" claim (Or it does not contain the
required authorization info, described in step 2) the API performs a ticket
request for the resource "Product-X" pushing the tenant
"Organization-1" in
a claim:
POST
https://localhost:8080/auth/realms/***/authz/protection/permission
[{
"resource_id": "Product-X",
"claims": { "tenant": [ "Organization-1" ] }
}]
The Requesting Party uses the ticket to obtain a valid RPT containing
the following authorization info:
"permissions": [
{
"resource_id": "Product-X",
"claims": { "tenant" : ["Organization-1"] }
}
]
** The Requesting Party performs the following action using the
previously obtained RPT:
GET /api/tenants/Organization-2/product/Product-X
2.- The API checks if the specified resource "Product-X" exists in the
RPT "permissions" claim and contains "Organization-2" in the
"tenant"
pushed claim. Since the resource "Product-X" is only provided for the
context "Organization-1" the API requests a ticket for the resource
"Product-X" in the context of the tenant "Organization-2".
POST
https://localhost:8080/auth/realms/***/authz/protection/permission
[{
"resource_id": "Product-X",
"claims": { "tenant": [ "Organization-2" ] }
}]
The Requesting Party uses the ticket to upgrade the previous RPT. The
upgraded RPT now contains both tenants in the pushed claims:
"permissions": [
{
"resource_id": "Product-X",
"claims": { "tenant" : ["Organization-1",
"Organization-2"] }
}
]
This works great with non-scoped resources since, for now on, the Resource
server can grant access to "Product-X" in both contexts
"Organization-1"
and "Organization-2". Also, the Resource Server will obtain new tickets if
new contexts (tenants) are requested. However, when we use
scoped-resources, since the pushing claims are not specific to the scopes
being requested, the Resource Server could not determine if the combination
of "Product-X" and some scope is defined for an specific tenant. We could
support this use-case removing scopes from the equation and creating
non-scoped resources like "Product-X:read", "Product-X:write", etc.
However, while we think that this should be implemented using scopes
instead of non-scoped resources, we don't know how to manage claims as we
discussed in the first mail.
Regards,
Álvaro.
El mié., 24 jul. 2019 a las 22:34, Pedro Igor Silva (<psilva(a)redhat.com>)
escribió:
> Hi Álvaro,
>
> You are not missing anything and that is how claims are handled. They are
> a permission-level (resource + scopes) info and not specific to only the
> scopes being requested/granted.
>
> Before finding alternatives, could you tell me how are you pushing these
> claims? Are you using our adapters or manually performing HTTP requests
> from your app?
>
> Regards.
>
> On Wed, Jul 24, 2019 at 10:20 AM Álvaro Gómez <
> alvaro.gomez.gimenez(a)tecsisa.com> wrote:
>
>> Hi,
>>
>> We are applying RBAC and CBAC models to evaluate permissions in a
>> multi-tenant UMA application. We are using Pushing Claims to let custom
>> policies determine if an user has an specific role in a provided context
>> (tenant) via Pushing Claims.
>>
>> Everything works fine if we use non-scoped resources but things get a bit
>> confusing when we use scoped ones since the pushing-claims (representing
>> the tenants) end up mixed in the RPT permission claim without leaving any
>> trace of the scopes with which they were pushed along. Consider the
>> following example:
>>
>> We have an application which manages products (represented by resources).
>> There are profiles (represented by roles) which allow users to sell,
>> modify
>> or delete products (represented by scopes). A certain user may interact
>> with one product in the context of a tenant (Determined by the Pushing
>> claim) with an specific role and with some different role from other
>> tenant.
>>
>> - Resource:
>> * product (With scopes sell and update)
>>
>> - Roles:
>> * Seller
>> * Product-Manager
>>
>> - Policies:
>> * Is-Seller (In the Tenant specified in the Pushing Claim "tenant")
>> * Is-Product-Manager (In the Tenant specified in the Pushing Claim
>> "tenant")
>>
>> - Permissions:
>> * product:sell -> Provides the "sell" scope of the resource
"product"
>> if
>> the "Is-Seller" policy evaluates to grant.
>> * product:update -> Provides the "update" scope of the resource
>> "product" if the "Is-Product-Manager" policy evaluates to
grant.
>>
>> - Users:
>> * Alice -> Alice is "Seller" in the tenant
"Organization-1" and is
>> "Product-Manager" in the tenant "Organization 2" so she
should be able to
>> sell products in the context of the tenant "Organization-1" and update
>> products in the context of "Organization-2" but neither
"update" products
>> in the context of "Organization-1" or sell products in the context of
>> "Organization-2".
>>
>> 1.- Alice requests an RPT using the following ticket:
>> { "resource": "product", "resource_scopes":
["sell"], "claims": {
>> "tenant": ["Organization-1"] } }
>>
>> Since Alice is "Seller" in the "Organization-1" (meaning
the Policy
>> "Is-Seller" will evaluate to "grant" if the provided claim
value is
>> "Organization-1" and the evaluated Identity is Alice) an RPT is
emitted
>> with the following "permission" claim:
>>
>> [{
>> "resource": "product",
>> "resource_scopes": ["sell"],
>> "claims": { "tenant": ["Organization-1"] }
>> }]
>>
>> 2.- Alice upgrades the previous RPT with the following ticket:
>> { "resource": "product", "resource_scopes":
["update"], "claims": {
>> "tenant": ["Organization-2"] } }
>>
>> Here is were things get confusing to us. We'd expect Alice to be
>> granted
>> when requesting the scope "update" in the context of
"Organization-2"
>> since
>> Alice has the role "Product-Manager" in that tenant. That would be
what
>> happened if Alice was requesting the RPT for the first time instead of
>> upgrading a previous one. However, since we are upgrading the RPT
>> obtained
>> in Step 1, when the policy "Is-Product-Manager" is evaluated, the
claim
>> "tenant" is mixed with the one in Step 1 (Since they are not grouped
by
>> scope) resulting in the following permission:
>>
>> {
>> "resource": "product",
>> "resource_scopes": ["sell", "update"],
>> "claims": {
>> "tenant": ["Organization-1",
"Organization-2"]
>> }
>> }
>>
>> The policy can't evaluate to grant since Alice is not
"Product-Manager"
>> in both tenants "Organization-1" and "Organization-2"
(Obtained through
>> $evaluation.getPermission().getClaims()). When evaluating this policy we
>> would only be interested in the pushing-claim `{ "tenant":
>> ["Organization-2"] }` which was pushed along with the scope
"update"
>> (which
>> is the one being evaluated by the permission "product:update"
associated
>> with this Policy).
>>
>> Shouldn't the claims be grouped by the scopes which with they were
>> pushed along? (See example at the end of this text), Are we missing
>> something?
>>
>> Example:
>> {
>> "resource": "product",
>> "resource_scopes": [
>> { "name": "sell", "claims": {
"tenant": ["Organization-1"] } },
>> { "name": "update", "claims": {
"tenant": ["Organization-2"] }
>> },
>> ]
>>
>> Thanks in advance,
>> Álvaro.
>> _______________________________________________
>> keycloak-user mailing list
>> keycloak-user(a)lists.jboss.org
>>
https://lists.jboss.org/mailman/listinfo/keycloak-user
>
>