Skip to main content

Manage policy and check permissions with the Python SDK

This page collects short Python SDK examples for the most common calls: create a Permit client, manage resources, roles, tenants, and users, assign roles, handle API errors, and check permissions. It is for backend developers who have installed the permit package and want the call and argument shape for each task. To install the SDK and run a first check, see Check permissions with the Python SDK (asyncio).

The Permit class from from permit import Permit is asynchronous. Every method returns a coroutine, so run the examples with await inside an async function. For code that doesn't use asyncio, import Permit from permit.sync instead and call the same methods without await. See Check permissions with the Python SDK (sync).

Prerequisites

Create a Permit client

Create one Permit client and reuse it across your application.

ArgumentDescription
tokenAPI key of the Permit environment. The API key selects the project and environment of every call.
pdpAddress of the PDP that permit.check() calls, for example http://localhost:7766 for a PDP container.
logLogger settings. enable turns SDK logs on (default False). level sets the log level (default "info").
from permit import Permit

permit = Permit(
# the API key to the Permit environment you wish to connect to
token="<YOUR_API_KEY>",
# the url in which the SDK can connect to the PDP container
pdp="http://localhost:7766",
# use this to turn on sdk logs:
log={"level": "debug", "enable": True},
)

Manage resources

A resource is a type of object in your application, such as document, with the actions that policies grant on it.

Create a resource

permit.api.resources.create() takes a resource object and returns a ResourceRead object. The key and name fields and the actions dictionary are required. Each key of actions is an action key. The attributes dictionary defines resource attributes for attribute-based access control (ABAC) policies.

from permit import ResourceRead

document: ResourceRead = await permit.api.resources.create(
{
"key": "document",
"name": "Document",
"urn": "prn:gdrive:document",
"description": "google drive document",
"actions": {
"create": {},
"read": {},
"update": {},
"delete": {},
},
"attributes": {
"private": {
"type": "bool",
"description": "whether the document is private",
},
},
}
)

Update a resource

permit.api.resources.update() takes the resource key and an object with the fields to change, and returns the updated ResourceRead object. The SDK sends a PATCH request, so fields that you omit keep their values. The example changes the description of the document resource and sets its actions.

from permit import ResourceRead

resource_after_changes: ResourceRead = await permit.api.resources.update(
# the key of the resource
"document",
# updated fields
{
"description": "a document in the document store",
"actions": {
"find": {}
}
},
)

List resources

permit.api.resources.list() returns a list of ResourceRead objects. The method takes optional page and per_page arguments. per_page defaults to 100.

from typing import List

from permit import ResourceRead

resources: List[ResourceRead] = await permit.api.resources.list()

Get a resource

permit.api.resources.get() takes a resource key and returns a ResourceRead object. The example gets the resource with the key document:

from permit import ResourceRead

resource: ResourceRead = await permit.api.resources.get("document")

Handle API errors

If the Permit API returns an error status code, the SDK raises a PermitApiError. Read the HTTP status code from status_code. A missing object returns status code 404, and a create call with an existing key returns status code 409. The SDK also raises the subclasses PermitNotFoundError for 404 and PermitAlreadyExistsError for 409, which you can catch directly.

from permit import Permit, PermitApiError

permit = Permit(...)

# handle not found error
try:
await permit.api.resources.get("nosuchresource")
except PermitApiError as e:
if e.status_code == 404:
print("not found")
else:
...

# handle cannot create object due to key conflict:
try:
await permit.api.resources.create(
{"key": "document", "name": "document2", "actions": {}}
)
except PermitApiError as e:
if e.status_code == 409:
print("already exists!")
else:
...

Manage roles

Create a role

permit.api.roles.create() takes a role object and returns a RoleRead object. Each permission uses the format resource:action, for example document:read. Create the resource before the role.

from permit import RoleRead

admin: RoleRead = await permit.api.roles.create(
{
"key": "admin",
"name": "Admin",
"description": "an admin role",
"permissions": ["document:create", "document:read"],
}
)

Manage tenants

Create a tenant

permit.api.tenants.create() takes a tenant object with a key and a name, and returns a TenantRead object. A tenant is an isolated group of users and resources, such as one customer organization.

from permit import TenantRead

tenant: TenantRead = await permit.api.tenants.create(
{
"key": "acme",
"name": "Acme Inc",
"description": "An example customer",
}
)

Sync users

Create or update a user (sync user)

permit.api.users.sync() creates the user if the key doesn't exist in the environment, or replaces the user data if the key exists. The method returns a UserRead object. The key field is required. Pass the same key to permit.check(). For when and how to sync users, see Sync users.

from permit import UserRead

user: UserRead = await permit.api.users.sync(
{
"key": "auth0|user-1",
"email": "user1@example.com",
"first_name": "Ada",
"last_name": "Example",
"attributes": {
"age": 50,
"favorite_color": "red",
},
}
)

Assign and list roles

Assign a role to a user in a tenant

permit.api.users.assign_role() takes an object with the user, role, and tenant keys, and returns a RoleAssignmentRead object. Sync the user and create the role and the tenant before you assign the role.

ra = await permit.api.users.assign_role(
{
# the user key
"user": "auth0|user-1",
# the role key
"role": "viewer",
# the tenant key
"tenant": "acme",
}
)

List role assignments

permit.api.role_assignments.list() returns a list of RoleAssignmentRead objects. Filter the list with the keyword arguments user_key, role_key, tenant_key, resource_key, and resource_instance_key. The user_key, role_key, and tenant_key arguments accept one key or a list of keys. The method also takes page and per_page (default 100). For more filters, see List role assignments.

assignments = await permit.api.role_assignments.list(
tenant_key="acme",
role_key="viewer",
)

To filter by more than one role, pass a list:

assignments = await permit.api.role_assignments.list(
tenant_key="acme",
role_key=["viewer", "editor"],
)

Check permissions

permit.check() takes a user, an action, and a resource, sends the check to the PDP, and returns True or False. Pass the user as a key string, and the resource as a resource key string or a dictionary with type and tenant. The code comments in the example list the conditions that a role-based access control (RBAC) policy needs to return True.

from permit import Permit

permit = Permit(...)

# in order to be permitted according to the RBAC policy, a few conditions must be met:
# 1) the user must exist in the permit system (you called sync user before)
# 2) the checked resource belongs to tenant X
# 3) the user has an assigned role in tenant X (the user must have at
# least one assigned role in the tenant that contains the resource)
# 4) the role assigned to the user must have the permission to perform
# the checked action on the checked resource
permitted = await permit.check(
# the user key
"auth0|user-1",
# the action
"create",
# the resource
{
# the type of the resource (resource.key)
"type": "document",
# the tenant that contains the resource
"tenant": "acme"
},
)

if permitted:
print("permitted")
else:
print("denied")

To confirm the result, open the audit log in the Permit dashboard. The check appears with the user, action, resource, and decision.

Next steps