# Understanding the Jenkins Permission System: PermissionGroup and Scopes Explained

> Master the Jenkins permission system. Learn how PermissionGroup and scopes organize access control for granular security in Jenkins.

- Repository: [Jenkins/jenkins](https://github.com/jenkinsci/jenkins)
- Tags: deep-dive
- Published: 2026-06-19

---

**The Jenkins permission system organizes access control through `PermissionGroup` containers that sort permissions by owner class, `PermissionScope` constants that define where permissions apply across the Jenkins model hierarchy, and `Permission` objects that represent individual rights with optional implication and enablement flags.**

The jenkinsci/jenkins repository implements a hierarchical access control layer that governs every user interaction, from global system configuration to individual build logs. Understanding the Jenkins permission system with PermissionGroup and scopes is essential for plugin developers and security administrators who need to implement fine-grained authorization strategies.

## Core Architecture: Groups, Scopes, and Permissions

Jenkins controls access through three tightly coupled constructs that form an extensible ACL framework:

- **`PermissionGroup`** – A logical container that groups related permissions by their owning class.
- **`Permission`** – An individual privilege (e.g., *Read*, *Configure*) that belongs to exactly one group.
- **`PermissionScope`** – Defines the level of the Jenkins model (global, item-group, item, run, or computer) where a permission can be configured.

When a `Permission` is instantiated, its constructor automatically registers itself with its parent `PermissionGroup`, which in turn adds the permission to the global `Permission.ALL` registry.

## How PermissionGroup Organizes Access Rights

Every permission belongs to a `PermissionGroup` whose **owner** is the class that defines the permissions (e.g., `Jenkins.class` or a plugin class). In [`core/src/main/java/hudson/security/PermissionGroup.java`](https://github.com/jenkinsci/jenkins/blob/main/core/src/main/java/hudson/security/PermissionGroup.java), the class maintains a sorted set of its `Permission` objects and registers itself in the global `PermissionGroup.PERMISSIONS` registry upon instantiation.

Key implementation details include:

- **`owner`** – The class that owns the group, used for sorting and uniqueness comparisons.
- **`title`** – A human-readable title displayed in the security configuration UI.
- **`add(Permission)`** – A private registration method invoked during permission construction that throws an exception if a duplicate permission ID is added.
- **`getAll()`** and **`get(Class)`** – Static utility methods to enumerate all loaded groups or locate a specific group by its owner class.
- **`compareTo`** / **`equals`** / **`hashCode`** – Implemented so groups remain comparable and usable in sorted sets.

When you define a new permission, the constructor automatically calls `group.add(this)`, ensuring the permission appears in both its group and the global list.

## PermissionScope and the Hierarchy of Application

The `PermissionScope` class in [`core/src/main/java/hudson/security/PermissionScope.java`](https://github.com/jenkinsci/jenkins/blob/main/core/src/main/java/hudson/security/PermissionScope.java) defines **where** a permission may be granted within the Jenkins object model. Scopes form a containment hierarchy where narrower scopes are contained within broader ones.

Built-in scopes include:

- **`JENKINS`** – Global Jenkins instance (top-level).
- **`ITEM_GROUP`** – Containers of items, such as folders.
- **`ITEM`** – Individual jobs or projects.
- **`RUN`** – Specific builds or runs of a job.
- **`COMPUTER`** – Individual nodes or agents.

The **`isContainedBy(PermissionScope)`** method walks this container hierarchy, allowing a permission declared for a finer scope (e.g., `RUN`) to be considered valid when checking a broader scope (e.g., `ITEM`). This enables cascading authorization where a user with item-level permissions implicitly holds rights to the runs within that item.

## The Permission Class: Tying Groups and Scopes Together

The `Permission` class in [`core/src/main/java/hudson/security/Permission.java`](https://github.com/jenkinsci/jenkins/blob/main/core/src/main/java/hudson/security/Permission.java) serves as the concrete representation of an access right. Each permission carries:

- **Owner** – The class that declared the permission.
- **Group** – The `PermissionGroup` instance it belongs to.
- **Name** – A unique identifier within its group (e.g., `"Read"`).
- **Description** – Tooltip text for UI rendering.
- **`impliedBy`** – An optional broader permission that automatically grants this one (e.g., `ADMINISTER` implies `CONFIGURE`).
- **`enabled`** – A runtime flag that controls visibility in the security matrix UI.
- **`scopes`** – One or more `PermissionScope` values indicating where the permission is configurable.

Critical methods include:

- **`isContainedBy(PermissionScope)`** – Verifies whether the permission is applicable for a given scope.
- **`getId()`** – Returns a stable string identifier like `hudson.model.Hudson.Read`.
- **`fromId(String)`** – A static lookup that loads the owning class via Jenkins’ classloader and retrieves the permission from its group.

The static fields `Permission.ALL` and `Permission.ALL_VIEW` expose all loaded permissions for programmatic queries and UI rendering in authorization strategies like `PermissionMatrixAuthorizationStrategy`.

## Practical Implementation Examples

### Defining Custom Permissions in a Plugin

To create plugin-specific permissions, define a static `PermissionGroup` and add permissions with appropriate scopes:

```java
// src/main/java/com/example/MyPlugin.java
package com.example;

import hudson.security.Permission;
import hudson.security.PermissionGroup;
import hudson.security.PermissionScope;
import org.jvnet.localizer.Localizable;

public class MyPlugin {
    // Create a group for the plugin
    public static final PermissionGroup PERMISSIONS =
        new PermissionGroup(MyPlugin.class,
            new Localizable("My Plugin Permissions"));

    // Define a permission configurable at Jenkins or Item level
    public static final Permission CONFIGURE_SPECIAL =
        new Permission(PERMISSIONS,
            "ConfigureSpecial",
            null,               // optional description
            Permission.CONFIGURE, // implied by generic Configure
            new PermissionScope[]{PermissionScope.JENKINS, PermissionScope.ITEM});
}

```

This registers the permission automatically with the global registry and its parent group.

### Runtime Permission Checks

Validate user access by checking permissions against the appropriate model object:

```java
import hudson.security.Permission;
import jenkins.model.Jenkins;

public boolean canConfigureSpecial(User user, Item item) {
    // Permission is scoped to ITEM, so we pass the item as context
    return user.hasPermission(item, MyPlugin.CONFIGURE_SPECIAL);
}

```

The `User.hasPermission` method internally calls `Permission.isContainedBy` to verify that the supplied `item` matches one of the permission's declared scopes.

### Dynamic Permission Management

You can toggle permission visibility at runtime without restarting Jenkins:

```java
// Hide the permission from the UI after a certain plugin version
MyPlugin.CONFIGURE_SPECIAL.setEnabled(false);

```

The `enabled` flag is consulted by the UI rendering code when building the security matrix, allowing gradual deprecation of legacy permissions.

## Summary

- **`PermissionGroup`** instances act as ownership containers that automatically register permissions in a global sorted set when instantiated.
- **`PermissionScope`** defines five hierarchical levels (JENKINS, ITEM_GROUP, ITEM, RUN, COMPUTER) and provides `isContainedBy` logic for scope validation.
- **`Permission`** objects encapsulate individual rights with unique IDs, implied-by hierarchies, enablement flags, and scope arrays.
- The permission system is extensible: plugins define groups and permissions via static fields, and the core handles registration via constructors in [`PermissionGroup.java`](https://github.com/jenkinsci/jenkins/blob/main/PermissionGroup.java) and [`Permission.java`](https://github.com/jenkinsci/jenkins/blob/main/Permission.java).
- Runtime checks use `isContainedBy` to verify that a permission's scope matches the authorization context, supporting hierarchical inheritance from runs up to the global Jenkins instance.

## Frequently Asked Questions

### What is the difference between PermissionGroup and PermissionScope?

A **`PermissionGroup`** is a logical container that organizes permissions by their owning class (e.g., `Jenkins.class`), primarily for UI grouping and namespace management. A **`PermissionScope`** defines the *level* of the Jenkins model where a permission can be applied—such as global Jenkins settings, individual items, or specific runs. While groups categorize permissions horizontally by owner, scopes categorize them vertically by model depth.

### How do I check if a user has a specific Jenkins permission?

Use the `hasPermission` method on the `User` object, passing the target model object and the `Permission` constant. For example, `user.hasPermission(item, MyPlugin.CONFIGURE_SPECIAL)` validates both the user's authorization strategy and whether the permission's scope contains the supplied item via `Permission.isContainedBy`.

### Can permissions be dynamically disabled in Jenkins?

Yes. Each `Permission` object includes an `enabled` boolean flag that can be toggled at runtime using `setEnabled(false)`. When disabled, the permission is hidden from the security matrix UI, though it remains in the system. This is useful for deprecating features or hiding permissions when specific plugin dependencies are unavailable.

### How does permission implication work in the Jenkins permission system?

The `impliedBy` field in [`Permission.java`](https://github.com/jenkinsci/jenkins/blob/main/Permission.java) allows a permission to specify a broader permission that automatically grants it. For example, if you set `Permission.CONFIGURE` as the `impliedBy` parameter when constructing your custom permission, any user with `CONFIGURE` rights automatically receives your permission without explicit assignment. This creates a hierarchical inheritance model that reduces administrative overhead.