1. Docs
  2. Denies

Denies

Refuse a permission at a node and everything below it, whatever roles allow.

Overview

Every grant allows: a role held at a node gives its permissions there and at every node below it. A deny is how you take a permission away inside that reach. Use one for an exception, such as every office except one: grant at the region and deny at the office, instead of granting at each office and remembering to grant at the next one when it opens.

A deny names a target, one or more permissions, a node, optional exception roles, and a required reason. For example: refuse documents.view to everyone holding Regional Viewer at Office A, except holders of Compliance, because Office A's files are under legal hold.

How a Deny Is Evaluated

A deny changes the answer to a check, not the roles anyone holds.

It applies at its node and at every node beneath it.It is checked before grants. A deny at the node or above it refuses the permission, whatever roles the target holds there.A grant lower down cannot undo it.An Environment-wide check (scope: "app_wide") holds the permission while at least one grant of it stands clear of every deny.In a flat Environment the root is the only node, so a deny sits there and applies everywhere.

Who a Deny Applies To

A deny has exactly one target:

Identity: one person.Machine client: one machine client, named by its id (not its client_id).Role: everyone who holds the role, wherever they hold it. A person who holds the role anywhere is refused at the deny's node, even through another role.

Exceptions

A deny can name up to 20 exception roles. Anyone holding one of them keeps access despite the deny, wherever they hold that role, while that assignment is in effect and the role is active. Use one so the people who manage access are not locked out. A role cannot be the target and an exception of the same deny.

What a Refusal Looks Like

A refusal by a deny names it:

POST /api/v1/permissions/evaluate answers allowed: false, denial_reason: "DENIED" and the deny's deny_id.A 403 from Canopy's API carries the same id in error.data.deny_id, so your logs can name the deny.Explain Access in the Developer Console shows the deny on the inheritance path, at the node that holds it, and marks the grants it overrides.The grants routes (GET /api/v1/identities/:id/grants and the machine-client equivalent) report model_version: 2 and list the deny roots in an Environment that holds a deny, and model_version: 1 elsewhere.@canopy-io/node reads model version 1 only. In an Environment that holds a deny it refuses the grants it reads (authorizer.model_version_unknown) rather than answer from rows it does not understand, so checks there go to the evaluate endpoint until the SDK reads version 2.

Canopy's API refuses on the next request after a deny is created. For when an API guarded by the SDK sees it, see When a Change Takes Effect.

Who Can Change Denies

Taking access away is a separate act from assigning a role, so it is granted separately.

Reading denies needs deny.view on the API (env_denies.view in the Developer Console); creating and deleting them needs deny.manage (env_denies.manage). Assigning roles confers neither.A deny on a person is named, and created, only by a reader who may see the Environment's people; for anyone else its target reads as an identity with no name.There is no edit. Change a deny by deleting it and creating another, so every change is previewed and recorded.Every create and delete is in the audit log and sent to webhooks as deny.created and deny.deleted.A deny goes with what it names: the identity leaving the Environment, the role or machine client being deleted, the node being deleted, or the last permission it refuses being deleted. Each removal is a deny.deleted event with its cause.

Next Step

Create a deny in the Developer Console or the API, see who it refuses before you confirm, and delete it.

Environment
API version
v1.0
On this page Was this page helpful?

Tell us how we can improve this guide.