Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce policy decision API #1137

Open
vhdirk opened this issue Aug 3, 2021 · 7 comments · May be fixed by #2047
Open

Introduce policy decision API #1137

vhdirk opened this issue Aug 3, 2021 · 7 comments · May be fixed by #2047
Labels
community-interest Issues which were explicitly asked for by the Ditto community. enhancement in progress
Milestone

Comments

@vhdirk
Copy link
Contributor

vhdirk commented Aug 3, 2021

For our UI, we'd like an endpoint that we can call to check if we should enable/disable certain elements based on policies.
As briefly discussed on gitter with @thjaeckle, that endpoint could look something like:

POST /api/2/policies/{policyId}/actions/checkPermissions with a payload like

{
  "resource": "thing:/features/X/properties/Y", 
  "id": "org.eclipse.ditto:mythingId",
  "permissions": [ "READ" ],
 }

The response could be something like

[
   {
      "permission": "READ",
      "allowed": true/false
   }
]

That would require you to have the policyId at hand, potentially having to fetch the Thing prior to using this api. It does make it generic to use for any kind of resource (thing, policy, messages)

@thjaeckle
Copy link
Member

thjaeckle commented Aug 3, 2021

@vhdirk I don't get why you added the "id" of the Thing in the payload.
You already define by addressing the policy via the "policyId" in the HTTP path, which policy to check. And as inside of the policy there is no "link" to a special Thing ID, this makes no sense (the relation is the other way around, the thing points to the policy).

Basically what I think you need:

  • You have a Thing "namespace:thing-1" which uses policy "namespace:policy-1"
  • You check if you have "READ" permission on "namespace:thing-1" by doing POST /api/2/policies/namespace:policy-1/actions/checkPermissions
    •  {
         "resource": "thing:/", 
         "permissions": [ "READ" ]
        }
  • Response could IMO be much simpler:
    • true|false

By using the "actions", one would however require to have EXECUTE permission for policy:/actions/checkPermissions - I am not certain if that is a good idea or if that API endpoint should be callable by every authenticated user instead.

@vhdirk
Copy link
Contributor Author

vhdirk commented Aug 3, 2021

I forgot to update the payload after I added the policy id as a pathparam :)
I was pondering with the idea of not requiring any id in request url, so that you did not need to first fetch the policyId from the resource itself. You could just give it a thingId, policyId or messageId and it would fetch the policyId internally.

As far as the response goes; the idea was that you could test multiple permissions independently. But there's not that many combinations to be made with permissions, so I think the simple boolean response would be fine.

@thjaeckle
Copy link
Member

thjaeckle commented Aug 3, 2021

I was pondering with the idea of not requiring any id in request url, so that you did not need to first fetch the policyId from the resource itself. You could just give it a thingId, policyId or messageId and it would fetch the policyId internally.

That won't work as the Ditto "policies" service/persistence does not track "backward relations" and architecturally we don't want it to.

Solution could be simple: Always make "policyId" and "thingId" the same, then you won't need to look up the "policyId" before checking the policy permissions.

@thjaeckle thjaeckle added enhancement community-interest Issues which were explicitly asked for by the Ditto community. labels Aug 4, 2021
@w4tsn
Copy link
Contributor

w4tsn commented Sep 2, 2021

Since frontends usually have to check for multiple paths at once to reduce the number of requests per page load the endpoint should accept multiple requests in one. E.g. with a payload like:

[
  {
    "resource": "thing:/features/X/properties/Y",
    "permissions": [ "READ" ]
  },
  {
    "resource": "thing:/features/Z",
    "permissions": [ "WRITE" ]
  }
]

And a result payload like:

[
  {
    "resource": "thing:/features/X/properties/Y",
    "permissions": [ "READ" ],
    "match": true
  },
  {
    "resource": "thing:/features/Z",
    "permissions": [ "READ" ],
    "match": false
  }
]

Or simplified:

{
  "thing:/features/X/properties/Y": true,
  "thing:/features/Z": false
}

Maybe it's also useful if I can call that permission check action on a thing and ditto then forwards this to the policy check action with the corresponding thingId. That way I don't have to find out the policyId of a thing first.

When retrieving a thing or feature one could also supply a path parameter to request deviating permissions so if I GET a feature and I'm interested if there are properties on that feature I can't READ or WRITE then I'd like to know that. Similar to the extraFields parameter. Actually maybe only if I can't write, because if I can't read then I don't get it returned anyway.

@thjaeckle
Copy link
Member

Thinking about this feature request again ..

  • a user might not have the permission to even READ the "policyId" of a thing
  • but still, a UI would need to query Ditto which "permissions" a provided login/JWT has for a specific thing
  • so, maybe (thinking out loud) this would have to be an endpoint which is neither "thing" nor "policy" related - similar to the whoami endpoint

Idea (building on input provided by @w4tsn and @vhdirk):

POST /api/2/checkPermissions

Payload:

[
  {
    "resource": "thing:/features/lamp/properties/on",
    "entityId": "org.eclipse.ditto:some-thing-1",
    "hasPermissions": [ "READ" ]
  },
  {
    "resource": "message:/features/lamp/inbox/toggle",
    "entityId": "org.eclipse.ditto:some-thing-1",
    "hasPermissions": [ "WRITE" ]
  },
  {
    "resource": "policy:/",
    "entityId": "org.eclipse.ditto:some-policy-1",
    "hasPermissions": [ "READ", "WRITE" ]
  }
]

The response payload would either just be:

[
  true,
  true,
  false
]

Or it would include the whole "input" (maybe optionally with a "verbose" flag).

And an alternative to that (as JsonArrays are often problematic to handle, using indices, etc.):

{
  "lamp_reader": {
    "resource": "thing:/features/lamp/properties/on",
    "entityId": "org.eclipse.ditto:some-thing-1",
    "hasPermissions": [ "READ" ]
  },
  "lamp_toggler": {
    "resource": "message:/features/lamp/inbox/toggle",
    "entityId": "org.eclipse.ditto:some-thing-1",
    "hasPermissions": [ "WRITE" ]
  },
  "policy_admin": {
    "resource": "policy:/",
    "entityId": "org.eclipse.ditto:some-policy-1",
    "hasPermissions": [ "READ", "WRITE" ]
  }
}

Which could (by default) just return a response like:

{
  "lamp_reader": true,
  "lamp_toggler": true,
  "policy_admin": false
}

Or it would include the whole "input" (maybe optionally with a "verbose" flag).

@thjaeckle
Copy link
Member

Ideas:

  • optional "verbose" mode, responding with requested "input" on "resources": POST /api/2/checkPermissions?verbose=true
  • "entityId" could also be provided via the HTTP path or query params .. to reduce payload for large permission check requests: POST /api/2/checkPermissions?entityId=<entityId>

@thjaeckle
Copy link
Member

@hu-ahmed started to work on this ..

@thjaeckle thjaeckle assigned thjaeckle and unassigned thjaeckle Oct 7, 2024
@thjaeckle thjaeckle added this to the 3.7.0 milestone Oct 7, 2024
@hu-ahmed hu-ahmed linked a pull request Oct 21, 2024 that will close this issue
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
community-interest Issues which were explicitly asked for by the Ditto community. enhancement in progress
Projects
Status: In Progress
Development

Successfully merging a pull request may close this issue.

3 participants