Skip to main content

Advanced Authorization Queries

Answer authorization questions that a single permit.check() can't: which of many resources a user can access, which users can act on a resource, and what a user can do across tenants. This walkthrough is for developers who already run permission checks and need to query permissions in bulk. Each section names the query, the SDK function that runs it, a runnable sample, and the result the sample prints.

Prerequisites

Choose a query

QuestionQueryNode.jsPythonGoJavaPDP endpoint
Can the user perform each of these actions on these resources?Bulk checkpermit.bulkCheck()permit.bulk_check()permit.BulkCheck()permit.bulkCheck()POST /allowed/bulk
Which of these objects can the user access?Filter objectsNot availablepermit.filter_objects()permit.FilterObjects()Not available
Which users can perform this action on this resource?Authorized usersNot availablepermit.authorized_users()Not availableNot availablePOST /authorized_users
What can this user do, across tenants and resources?User permissionspermit.getUserPermissions()permit.get_user_permissions()permit.GetUserPermissions()permit.getUserPermissions()POST /user-permissions

filter_objects() and FilterObjects() have no PDP endpoint of their own. Both send one bulk check to POST /allowed/bulk and drop the denied objects in the SDK.

The example scenario

Every sample on this page uses the same blogging application, in the default tenant. The resource type is blog_post, with the instances 1, 2, and 3. The actions are read and edit. Each post's owner can read and edit it, and every user can read every post.

UserCan readCan edit
alice@permit.io (owns posts 1 and 2)blog_post:1, blog_post:2, blog_post:3blog_post:1, blog_post:2
bob@permit.io (owns post 3)blog_post:1, blog_post:2, blog_post:3blog_post:3

In every sample, replace <YOUR_API_KEY> with your environment API key. To run the samples against your own policy, replace the user keys, the blog_post resource type, and the instance keys with your own.

Bulk check

A bulk check sends several permission checks to the PDP in one request and returns one decision per check, in the same order as the checks. Use it when you need decisions for several resources or users at once, for example to show or hide actions in a list.

Example: Alice checks which posts she can read

Alice needs to know which blog posts she can read. Send one check per post, each with Alice's user key, the action read, and the post.

const { Permit } = require("permitio");

const permit = new Permit({
token: "<YOUR_API_KEY>",
pdp: "http://localhost:7766",
});

const decisions = await permit.bulkCheck([
{ user: "alice@permit.io", action: "read", resource: { type: "blog_post", key: "1" } },
{ user: "alice@permit.io", action: "read", resource: { type: "blog_post", key: "2" } },
{ user: "alice@permit.io", action: "read", resource: { type: "blog_post", key: "3" } },
]);

console.log(decisions);

With the policy in The example scenario, all three decisions are true: Alice can read all three posts. The Node.js sample prints [ true, true, true ], the Python sample prints [True, True, True], the Java sample prints [true, true, true], and the Go sample prints one line per post:

0. blog_post:1 read = true
1. blog_post:2 read = true
2. blog_post:3 read = true

One request returns all three decisions, instead of one request per post. If a decision is false where you expect true, confirm that the instance exists in the tenant you passed and that the policy grants read on it.

See Bulk check for the full reference.

Filter objects

Filtering objects removes the objects a user can't access from a list you already fetched. Fetch the records from your database, pass them to FilterObjects() (Go) or filter_objects() (Python), and get back the subset the policy allows, in the original order. The Node.js and Java SDKs have no equivalent function: send a bulk check and keep the records whose decision is true.

Example: Alice filters the posts she can edit

Alice wants the blog posts she can edit. Pass Alice as the user, edit as the action, and the three posts as the resources.

import asyncio
from permit import Permit

permit = Permit(
token="<YOUR_API_KEY>",
pdp="http://localhost:7766",
)


async def main():
posts = [
{"type": "blog_post", "key": "1", "tenant": "default"},
{"type": "blog_post", "key": "2", "tenant": "default"},
{"type": "blog_post", "key": "3", "tenant": "default"},
]

allowed = await permit.filter_objects(
user={"key": "alice@permit.io"},
action="edit",
context={},
resources=posts,
)

for post in allowed:
print(f"alice@permit.io can edit blog_post:{post['key']}")


asyncio.run(main())

Both samples print two lines, because Alice can edit posts 1 and 2 but not post 3:

alice@permit.io can edit blog_post:1
alice@permit.io can edit blog_post:2

The denied post is left out of the returned list, so the returned list is shorter than the list you passed.

See Filter data by permission to compare filtering approaches.

Get authorized users

The authorized users query lists the users who can perform an action on a resource type or a resource instance. It returns the users with the role assignments that grant the access. The Python SDK exposes it as permit.authorized_users(). In the other SDKs, call the PDP endpoint directly.

Example: Bob lists who can read Blog Post 3

Bob wants to know who can read Blog Post 3. Pass the action read and the resource instance.

import asyncio
from permit import Permit

permit = Permit(
token="<YOUR_API_KEY>",
pdp="http://localhost:7766",
)


async def main():
result = await permit.authorized_users("read", "blog_post:3")
print(result.resource, result.tenant)
for user_key in result.users:
print(user_key)


asyncio.run(main())

Both users can read Blog Post 3, so the users map has two keys, alice@permit.io and bob@permit.io. The Python sample prints blog_post:3 default and then one line per user key:

blog_post:3 default
alice@permit.io
bob@permit.io

The value behind each user key is the list of role assignments that grant read, each with the assignment's user, tenant, resource, and role. Alice is listed through the read permission every user has, and Bob is listed through his ownership of the post, which grants edit and read. An empty users map means no user in the tenant can read that post.

See Get resource authorized users for the arguments and the result format.

Get user permissions

The user permissions query returns all of a user's permissions for every registered resource, in every tenant the user is assigned to. You can narrow the result to a list of tenants, resource instances, or resource types. The result maps each tenant or resource instance to the user's permissions and roles on it.

Example: Bob's permissions

Get Bob's permissions on the blog_post resource type:

const { Permit } = require("permitio");

const permit = new Permit({
token: "<YOUR_API_KEY>",
pdp: "http://localhost:7766",
});

const permissions = await permit.getUserPermissions(
"bob@permit.io",
["default"], // tenants filter
undefined, // no resource instance filter
["blog_post"], // resource types filter
);

for (const [object, details] of Object.entries(permissions)) {
console.log(object, details.permissions);
}

The result has one entry per blog post, keyed by blog_post:<key>, and each entry lists the actions Bob can perform on that post:

Result keyBob's permissions
blog_post:1blog_post:read
blog_post:2blog_post:read
blog_post:3blog_post:read, blog_post:edit

Each permission is a resource_type:action string. An empty result means Bob has no role assignment in the default tenant on any blog_post instance.

See Get user permissions for the full reference.

Next steps