Skip to content

Permissions

Nautobot provides an object-based permissions framework, which replace's Django's built-in permissions model. Object-based permissions enable an administrator to grant users or groups the ability to perform an action on arbitrary subsets of objects in Nautobot, rather than all objects of a certain type. For example, it is possible to grant a user permission to view only locations within a particular parent location, or to modify only VLANs with a numeric ID within a certain range.

See also the documentation on user permissions.

Example Constraint Definitions

Constraints Description
{"status__name": "Active"} Status name is active
{"status__name__in": ["Planned", "Reserved"]} Status name is active OR reserved
{"status__name": "Active", "role__name": "testing"} Status name is active OR role name is testing
{"name__startswith": "Foo"} Name starts with "Foo" (case-sensitive)
{"name__iendswith": "bar"} Name ends with "bar" (case-insensitive)
{"vid__gte": 100, "vid__lt": 200} VLAN ID is greater than or equal to 100 AND less than 200
[{"vid__lt": 200}, {"status__name": "Reserved"}] VLAN ID is less than 200 OR status is reserved

Permissions Enforcement

Viewing Objects

Object-based permissions work by filtering the database query generated by a user's request to restrict the set of objects returned. When a request is received, Nautobot first determines whether the user is authenticated and has been granted to perform the requested action. For example, if the requested URL is /dcim/devices/, Nautobot will check for the dcim.view_device permission. If the user has not been assigned this permission (either directly or via a group assignment), Nautobot will return a 403 (forbidden) HTTP response.

If the permission has been granted, Nautobot will compile any specified constraints for the model and action. For example, suppose two permissions have been assigned to the user granting view access to the device model, with the following constraints:

[
    {"location__name__in":  ["NYC1", "NYC2"]},
    {"location__location_type__name__in":  ["City"]},
    {"status__name":  "Offline", "tenant__isnull":  true}
]

This grants the user access to view any device that is assigned to a location named NYC1 or NYC2 with location type City, or which has a status name of "Offline" and has no tenant assigned. These constraints are equivalent to the following ORM query:

Location.objects.filter(
    Q(location__name__in=['NYC1', 'NYC2']),
    Q(location__location_type__name__in=['City']),
    Q(status__name='Active', tenant__isnull=True)
)

Tokens

Note

The below mechanism is only applicable for applying constraints where the model has user relationship (eg Change Log, Job results).

Permissions constraints can be defined by using the special token $user to reference the current user at evaluation. This can be beneficial in order to restrict users to only view their own Change log entries for example. Such a constraint can be defined as:

{
    "user": "$user"
}

Creating and Modifying Objects

The same sort of logic is in play when a user attempts to create or modify an object in Nautobot, with a twist. Once validation has completed, Nautobot starts an atomic database transaction to facilitate the change, and the object is created or saved normally. Next, still within the transaction, Nautobot issues a second query to retrieve the newly created/updated object, filtering the restricted queryset with the object's primary key. If this query fails to return the object, Nautobot knows that the new revision does not match the constraints imposed by the permission. The transaction is then rolled back, leaving the database in its original state prior to the change, and the user is informed of the violation.

Accounts with Elevated Access

Two user account flags exist outside of the object permissions framework entirely: is_superuser and is_staff. Neither is granted through an ObjectPermission, and neither can be constrained by one.

Superusers

A superuser implicitly holds every permission on every object. The superuser check happens before any object permission is evaluated: the authentication backend returns "allowed" for any object-permission check, and restricted querysets return all rows unfiltered. As a consequence:

  • Assigning an ObjectPermission with constraints to a superuser has no effect — constraints are never evaluated for superusers.
  • A superuser does not need (and is not limited by) any permission recipe described in this document.

Warning

Superuser status is distinct from staff status. A superuser without is_staff cannot log into the Django admin UI, but still has unrestricted access to all objects via the Nautobot UI and APIs.

Staff Users (is_staff)

The is_staff flag primarily controls access to the Django admin UI, but a few Nautobot views are also gated on it (staff or superuser):

  • The Worker Status view (Celery worker/queue details).
  • Certain administrative configuration views (for example, the data validation device-uniqueness constraints configuration).
  • The "new release available" banner on the home page.

is_staff by itself grants no object permissions — a staff user still needs ObjectPermission assignments (or superuser status) to view or modify objects in the Nautobot UI and APIs. However, staff status combined with certain permissions unlocks high-impact actions in the Django admin, described below.

The Django Admin

The Django admin UI (/admin/) is intentionally trimmed down in Nautobot, but the models it does expose are among the most security-sensitive in the application:

Model Risk if misused
Users Password resets, granting is_staff/is_superuser, assigning permissions
Groups Adding oneself/others to privileged groups
Object Permissions Authoring or widening any permission in the system
Tokens Creating an API token for any user, with a chosen key
Configuration (Constance) Changing runtime application settings
File Proxies, Job Results, Admin Log Entries Reading operational data

Access to each admin page still requires the corresponding model permission (e.g. users.change_user), but note the following:

Staff users can reset other users' passwords

When using local (database-backed) authentication, a staff user with users.change_user permission can set a new password for any user account — including superusers — through the admin UI's password change form, without knowing the current password. Treat is_staff + users.change_user as equivalent to full control of the system.

Token administration allows impersonation

A staff user with token permissions can create or modify an API token for any other user, including choosing the token key. Since API requests authenticate as the token's owner, this is effectively the ability to impersonate any user (including superusers). This is the only place in Nautobot where one user can access another user's tokens — see API Tokens below.

Assigning Permissions

Permissions are implemented by assigning them to specific users and/or to groups of users. Users can have a combination of permissions and groups assigned to their account. All of the permissions granted to the user's groups and directly to the user's account will be used when determining authorization to access an object or view.

Assigning Permissions to Individual Users

Permissions can be related directly to users from the Admin UI or the API:

- Admin UI API
Staff superusers Yes Yes
Non-staff superusers No Yes
Staff users with users.add_permission or users.change_permission Yes Yes
Non-staff users with users.add_permission or users.change_permission No Yes

Multiple permissions can be assigned to a user account.

Info

User permission relationships can be managed in the Admin UI by modifying the user or the permission.

Warning

Granting a user users.change_permission or users.add_permission gives the user the ability to modify their own permissions. This permission should be restricted to trusted accounts and should be considered the same as giving a user full access.

Creating Groups

Groups of users can be created to provide role-based access control and simplify user permissions management. Permissions related to a group will apply to all users in the group. A user can belong to any number of groups. Groups can be created from the Admin UI or the API:

- Admin UI API
Superusers Yes Yes
Users with auth.add_group or auth.change_group No Yes

Adding Users to Groups

Users can be added to groups through the Admin UI by superusers or automatically assigned to externally authenticated users through the EXTERNAL_AUTH_DEFAULT_GROUPS and EXTERNAL_AUTH_DEFAULT_PERMISSIONS settings. Nautobot groups can optionally be mapped to LDAP groups when using LDAP authentication.

Assigning Permissions to Groups

Permissions can be related to groups by superusers or users with users.add_permission or users.change_permission permissions.

- Admin UI API
Superusers Yes Yes
Staff users with users.add_permission or users.change_permission Yes Yes
Regular users with users.add_permission or users.change_permission No Yes

Multiple permissions can be assigned to a user group.

Info

Group permission relationships can be managed in the Admin UI by modifying the group (superusers only) or the permission.

Special Permission Behaviors

Most models follow the standard pattern: view/add/change/delete actions enforced against a queryset restricted by your permission constraints. The features below deviate from that pattern, usually because a user has inherent access to their own objects that differs from the model-level permission.

Self-Service Behaviors ("mine" vs. "everyone's")

Several features grant a user access to objects they own without requiring any explicit permission. This is a summary; details follow in the subsections below.

Feature Access to your own Access to others'
API tokens Always (list/create/edit/delete your own) Never via UI or REST API, even for superusers (Django admin is the exception)
Saved views Always (view/create/edit your own), plus all shared views Requires extras.view_savedview / extras.change_savedview
Job results (cancel) Always (cancel a job you submitted) Requires extras.cancel_job on the Job
User profile / preferences Always (profile, config, navbar favorites, own password) Never
Approval requests ("My Requests") Always (view workflows you submitted) Standard model permissions

API Tokens

Token access is hard-scoped to the requesting user rather than governed by object permissions:

  • Any authenticated user can list, view, create, edit, and delete their own tokens from their user profile. The token key is displayed in clear text on this page.
  • The REST API /api/users/tokens/ endpoint requires the users.view_token permission, but the results are always filtered to the requesting user's own tokens — granting users.view_token (even without constraints) does not expose other users' tokens.
  • Not even a superuser can view another user's tokens through the Nautobot UI or REST API.
  • The one exception is the Django admin, where staff users with token permissions can view and manage all users' tokens.

Saved Views

Saved views intentionally relax the standard permission model so that any user who can view a list page can also use saved views of it:

  • Any authenticated user can create saved views and view/edit/delete their own, with no extras.*_savedview permissions required.
  • Any authenticated user can see saved views that are marked shared.
  • The dedicated saved views list page, and access to other users' non-shared views, require extras.view_savedview; editing views you don't own requires extras.change_savedview.
  • These relaxations apply to the UI only — the REST API endpoint for saved views enforces standard object permissions.

Change Log

Warning

The extras.view_objectchange permission currently grants visibility into change records for all objects — including full before/after snapshots of objects the user has no view permission on. The change log does not re-check the viewing user's permission on the changed object, only on the ObjectChange record itself.

If a user can view change log entries, they can read the serialized contents (including all field values captured in the diff) of any object type that is change-logged. To limit this, add constraints to the view_objectchange permission itself, for example:

  • {"user": "$user"} — only changes the user made themselves.
  • {"changed_object_type__app_label": "dcim"} — only changes to DCIM objects.

Note that the per-object "Change Log" tab additionally requires view permission on the parent object, but the global change log list and change detail pages do not at this time.

Jobs

Jobs deserve special attention because they execute code:

  • Running: requires the run action on the Job model (extras.run_job). Constraints can limit which specific jobs a user may run (see the Export Job recipe below).
  • Database access: the permission check (and the enabled flag on an individual Job record) gate only whether the job may be launched. Once running, job code accesses the database without any per-user restriction by default — a job can read and write objects its submitter has no permission on, unless the job's own code enforces otherwise. Enable a job only if you trust what that job does with full database access.
  • Canceling: a user can always cancel a job they submitted, with no additional permission. Canceling another user's job requires the cancel action (extras.cancel_job), which is object-level and can be constrained to specific jobs.
  • Re-running: re-running a previous job result is gated by the same run permission as a fresh run.
  • Scheduled jobs: visibility of scheduled jobs follows the standard extras.view_scheduledjob permission — it is not limited to your own schedules. Taking ownership of another user's schedule requires extras.change_scheduledjob plus run on the underlying job.
  • Sensitive variables: jobs flagged as having sensitive input variables cannot be scheduled (only run immediately) and cannot be combined with approval workflows, so that sensitive input is never persisted.

Approval Workflows

Approval of a workflow stage requires membership in the stage's designated approver group (or superuser status), plus change permission on the object under review.

No self-approval prevention

Nautobot does not currently prevent a user from approving a request they themselves submitted, as long as they are a member of the approver group. If your process requires separation of duties, ensure that users who submit requests subject to a workflow are not members of that workflow's approver groups.

Other notable behaviors:

  • Only the submitter (or a superuser) can cancel an in-flight approval workflow.
  • Any authenticated user with view access can comment on a workflow — commenting is not limited to approvers.

Limitations and Edge Cases

No Field-Level Permissions

Nautobot's permission model is attribute-based (ABAC) at the object instance level: constraints select which objects a user may act on, not which fields they may modify. There is no way to express "may edit status on a device, but not name". A user with change permission on an object can modify any field of that object.

Constraint enforcement on writes validates the final state of the object (the save is rolled back if the resulting object no longer matches your constraints). This has two practical consequences:

  • A user cannot move an object out of their permitted set (the post-save check fails and the change is rolled back).
  • A constraint cannot forbid changing a particular field, as long as the object still matches the constraint after the change.

If you need field-level control, the available approximations are constraining on that field's value (e.g. {"status__name": "Active"} prevents saving the object in any other status) or splitting the workflow so the sensitive field is managed by a job or a more privileged team.

When viewing a list of objects, or the details of a single object, in the Nautobot UI, the table row or panel describing an object will often include brief information (such as the "name" or other identifier) of relevant individual related objects. For example, a Location may display the names of its related Status, Location Type, Tenant, etc. By design, Nautobot does not generally enforce view permissions on such related objects before displaying this brief information, as it's considered a necessary part of basic platform functionality. Clicking the hyperlink to any such related object (to view more detailed information about it) will, if the user lacks view permission for the related object, result in the expected HTTP 403 or 404 error.

Note that the behavior for "multiply-related" objects, where the "primary" object relates to a list of related objects (a reverse-foreign-key relation, a many-to-many relation, or similar), is different by design. In this case, the Nautobot UI generally filters or restricts the list of related objects by "view" permission, such that the UI will omit entirely from the list any related object(s) that the user lacks permission to view. Similar behavior is seen for multiply-related object retrieval in the REST API and GraphQL, as described below.

In some cases, a specific view may, as a part of its functionality, necessarily include brief information about distantly-related objects, that is to say objects that are not directly related to the base object(s) being viewed, but may be related to other objects, which are related to other objects, which are eventually in turn related to the base object(s) in some relevant way. If this information is essential to Nautobot's functionality and usability, even these distantly-related objects may necessarily display some information to the user regardless of their "view" permissions or lack thereof. Some examples:

  • Hierarchical models (Locations, Prefixes, Racks and Rack Groups, Tenants and Tenant Groups, etc.) often need to display a summary of their hierarchy (a Location's sequence of ancestors, a Prefix's sequence of container prefixes, a Rack's sequence of containing Rack Groups, etc.) as relevant contextual information.
  • Cable terminations (Interface, Front Port, Console Port, etc.) may necessarily render names or other information about the remote objects to which they are connected.
  • Cable-path tracing views may necessarily render the names and other relevant attributes of all Devices, Interfaces, patch panels, other cables, etc. involved in the trace.

Tip

In general, the contextual information provided in these various views is considered a necessary aspect of Nautobot's functionality, and by design Nautobot may not necessarily enforce "view" permissions on individual data points in this information. Disclosure of otherwise non-permitted information to the user in these contexts is not generally considered to be a security issue in Nautobot; rather it generally points to a less-than-ideal usage of Nautobot's flexible permissions system.

Conversely, in some cases, overly-restrictive configuration of user "view" permissions may result in "incorrect" or "suboptimal" display of data - for example, the "tree" rendering of Location and Prefix list views may behave oddly if a user's constrained view permissions result in the display of some "leaf" objects but force omission of other "parent" or "ancestor" objects entirely from the list. This too is generally considered a misconfiguration of permissions, rather than a Nautobot bug to be fixed in code.

When a user edits object A that references a related object B (for example, a device and its location), form dropdowns only offer related objects the user has view permission on. This can be confusing, and in some situations of overly-restrictive permissions, may actually result in unintended removal of relevant data, for example:

  • If the object's currently assigned related object is one the user cannot view, the form will not present the currently assigned object as an option, instead rendering with an empty selection for that relation. If the field is required, submitting the form as-is will fail with a generic "Select a valid choice" validation error. The error does not explain that the underlying cause is a missing view permission on the related object. Conversely, if the field is not required, submitting the form as-is may silently unassign the currently assigned related object from the object being edited.
  • For multiply-related objects, where the form permits selection of multiple such related objects, the form will only include related objects the user is permitted to view, even if other (non-permitted) objects were previously selected and assigned. A user with limited related-object view permissions editing an object may again result in silently unassigning previously related objects from the object being edited.

Tip

When granting add or change on a model, remember to also grant view on the models it commonly references (status, role, location, tenant, etc.), scoped appropriately.

  • Similarly, users can only select and assign related objects they can view. When a user reports they "can't find" an object that clearly exists in a dropdown, a missing view permission on the related model is the most common cause.

Tip

For models that have the ability to restrict individual records' applicability by content type (Status, Role, Location Type, etc.) a missing content-type assignment is another common cause of records "missing" from a dropdown.

Much like the UI behavior, the REST API for a given object(s) will limit the information returned about related objects by the user's view permission. Multiply-related object lists will omit related objects that the user lacks permission to view, while singly-related objects will provide only very limited information if not viewable by the user.

When the REST API is called with a depth query parameter greater than zero, detailed information about related objects may be included in the serialized response. This information is constrained by view permissions as appropriate, such that related objects that the user has permissions to view will provide full data, but singly-related objects that the user does not have permissions to view will return only a minimal summary of the object, much in the same way as the UI (as described above) will display brief information about related objects even if the user lacks appropriate related-object view permissions. At depth, multiply-related objects that are not viewable will still be omitted from related-object lists as described above.

Additionally, object writes (POST/PATCH/PUT) via the REST API also enforce view permissions for related objects - much like the UI behavior described above, a user cannot create or update an object via the REST API to include new references to related objects that they lack view permission for. Unlike the UI, it's possible via the REST API to partially update specific fields on an object (via a PATCH request), so a user with limited view permissions on related objects may be able, with appropriately constructed PATCH data, to update related-object fields that they do have permission to view, without necessarily causing data loss on related-object fields that they lack permissions for.

Refer to REST API Object Permissions for more details.

When querying related objects via GraphQL, view permissions are also enforced. Traversal from an object to a list of related objects behaves like the UI and REST API in that the related-object list will be filtered/restricted by view permissions, with unviewable related objects omitted entirely from the list as if not present. However, for singly-related objects, unlike the UI or REST API, a GraphQL query is unable to provide "limited" information about a related object, so any queries that traverse from a viewable base object to a non-viewable single related object in GraphQL will simply report the related object as being null, as if it didn't exist at all (or, equivalently, as if the foreign key on the base object was set to NULL). This can be surprising to users.

Refer to GraphQL Permissions Enforcement for more details.

EXEMPT_VIEW_PERMISSIONS

The EXEMPT_VIEW_PERMISSIONS setting globally disables view permission enforcement for the listed models (or all models, with "*"). This applies to anonymous users as well as authenticated ones. The wildcard deliberately never exempts users, groups, or object permissions, but any other model listed becomes world-viewable. Audit this setting first when reviewing a deployment's exposure — it neutralizes every view constraint discussed on this page.

Baseline Secure Configuration

The permissions framework is flexible enough to hang yourself with. The guidance below is a conservative starting point for a typical deployment.

Use External Authentication

Prefer SSO or LDAP over local passwords. This centralizes credential policy (MFA, lockout, offboarding) and lets you drive group membership — and therefore permissions — from your identity provider via EXTERNAL_AUTH_DEFAULT_GROUPS or SSO group sync. With external authentication, the staff password-reset concern above is largely moot because local passwords are not used for login.

Permissions Reserved for Highly Trusted Users

Grant the following only to administrators — each is a potential escalation path to broad control of Nautobot, regardless of any constraints on the user's other permissions:

Permission Escalation path
users.add_objectpermission / users.change_objectpermission Author or widen any permission, including their own
users.add_user / users.change_user Edit users — including granting themselves or others elevated permissions, superuser/staff flags, or (via the admin, if staff) resetting passwords
users.delete_user Lock others out
auth.add_group / auth.change_group Join themselves to privileged groups
users.add_token / users.change_token + is_staff Mint API tokens as any user (impersonation)
extras.add_gitrepository / extras.change_gitrepository Point Nautobot at a Git repository containing attacker-controlled Python code
extras.change_job Enable disabled jobs (including those provided by a Git repository, above), override job settings (e.g. approval and sensitive-variable overrides)
extras.add_jobhook, extras.change_jobhook Cause other users' actions (create/edit/delete of records) to run job code
extras.run_job Run jobs — recall that jobs execute with unrestricted database access
extras.*_secret / extras.*_secretsgroup Re-point secrets and potentially exfiltrate credentials
extras.*_approvalworkflowdefinition Weaken or remove approval requirements
extras.add_webhook, extras.change_webhook Automatically export Nautobot data to other systems
is_staff flag Django admin access; combined with the model permissions above, full takeover
is_superuser flag Bypass all object permissions, full takeover

Broadly: permissions about who can do what (users, groups, permissions, tokens) and permissions about what code runs (Git repositories, jobs) or where data is sent to (webhooks) should be restricted to administrative users.

Permissions To Grant With Caution

Some other permissions, while not posing the same inherent security risks as those in the previous section, should still be granted with caution due to potential for wide-ranging impact.

  • Data Validation rules - misdefined validation rules can flag swathes of existing data as invalid, or even entirely prevent saving records.
  • Computed Fields - poorly defined (expensive to calculate/render) custom field Jinja2 templates can significantly reduce performance of list views and object detail views.
  • Custom Fields - the background task (job) started when a new custom field is defined or an existing custom field is deleted can consume significant resources in updating a large number of records. Large numbers of custom fields can clutter the UI and reduce performance.
  • Relationships - large numbers of relationships can clutter the UI and reduce performance.

Permissions That Are Generally Safe to Delegate

Day-to-day network data management is what the constraint system is designed for. Granting view/add/change/delete (scoped by constraints as needed) on models in these areas is the intended use:

  • Circuits
  • Cloud
  • DCIM (devices, interfaces, racks, cables, locations — remember ancestor permissions)
  • IPAM (prefixes, IP addresses, VLANs, VRFs)
  • Load Balancers
  • Tenancy
  • Virtualization
  • VPN
  • Wireless
  • Specific "Extras" data models such as tags, notes, statuses, and roles.
    • Scope extras.view_objectchange (e.g. {"user": "$user"}) unless users may see all change history.

When delegating, also grant run on the specific system jobs users need (exports, bulk edits) with constraints, per the recipes below.

Recipes

Below are examples of common permissions that a Nautobot admin may want to grant.

Export Job

As of Nautobot 2.1, the Export action has been converted into a Job and requires permissions with constraints. (#4745)

Create a Permission to allow users to start an Export job:

  • Permission Name: Start Export job
  • Action: run
  • Users/Groups: Any user or restrict as needed
  • Constraints: [{"module_name": "nautobot.core.jobs", "job_class_name": "ExportObjectList"}]

Create a Permission to allow users to view the results of Export jobs they ran:

  • Permission Name: View Export results
  • Action: View
  • Users/Groups: Any user or restrict as needed
  • Constraints: [{"user": "$user", "task_name": "nautobot.core.jobs.ExportObjectList"}]

Create a Permission to allow users to download a job's output file(s), such as the result file produced by an Export job:

  • Permission Name: Download Job output files
  • Object type: Extras > File proxy
  • Action: View
  • Users/Groups: Any user or restrict as needed

Info

Export jobs run with the permissions of the user that start the job.