Class GrantService

java.lang.Object
io.jenkins.plugins.batchcontrol.security.GrantService

@Restricted(org.kohsuke.accmod.restrictions.NoExternalUse.class) public final class GrantService extends Object
Runtime authority on active grants (SPEC item 8). Keeps an in-memory cache of all grants, backed by the file store (grants/<id>.xml), so the permission-check hot path (GrantAwareACL) never touches disk.

Restart safety: Jenkins is a singleton, so there is exactly one live cache to keep fresh; resetCacheOnStartup() clears it unconditionally on every Jenkins startup, so the very first query after a restart reloads straight from the files instead of serving whatever happened to be in memory before. A grant that is still inside its window therefore survives a restart and one whose window ended during the downtime is gone from the very first check (SPEC item 8). Activity is always judged by Grant.isActiveAt(java.time.Instant) against BatchClock at check time — no timers.

S-15: the change-control switch gates whether a grant confers anything (that check is in GrantAwareACL), and turning the switch off also closes the windows that are open at that moment — see revokeAllActive().

D-74: a window names one item by its full name and follows that item. Item events keep the names right (WindowItemListener): a rename or move updates the windows naming the item (followItem(hudson.model.Item, java.lang.String, java.lang.String)), a deletion ends them (endWindowsOf(hudson.model.Item, java.lang.String)). Renaming through a window is refused (D-71c, GrantAwareACL), so only an administrator or a user with their own permissions can move a window's name. S-39-02: no window or D-35c record is left on a name its item no longer has: registration verifies the approved item is still at its name (register(io.jenkins.plugins.batchcontrol.model.Grant, hudson.model.Item), D-71c (3)), and a window that cannot follow its item for certain ends instead (followItem(hudson.model.Item, java.lang.String, java.lang.String)). D-35c created-item records and, D-75 (2), the D-58a changed-under-grant lists follow in memory first and their write is retried; an entry at a name another item has just taken is dropped (relocateCreatedItem(hudson.model.Item, java.lang.String, java.lang.String), relocateChanged(hudson.model.Item, java.lang.String, java.lang.String)).

D-74, a window's end is never lost: a window ends in memory first, so it confers nothing from that moment, and its GRANT_REVOKE record is appended before its file is rewritten. If the file cannot be written, the end is kept in unsavedEnds and written again before every later grant write, by every item event and by the periodic work (flushUnsavedEnds()); every read of a grant file in this class applies it, so no other write can bring the window back. If Jenkins stops before the end is written, the record stands in for it: when the cache is loaded, a window whose file still says it is open but which has a GRANT_REVOKE record is ended again (applyRecordedEnds(java.util.List<io.jenkins.plugins.batchcontrol.model.Grant>)), so a restart cannot re-open it, also not on an item created at its name in the meantime. S-39-03: that read is bounded by time, not by a record count, and when it cannot be completed every open window ends (fail-closed).

  • Method Summary

    Modifier and Type
    Method
    Description
    D-36: marks and returns every active grant whose window ends within lead and whose GRANT_EXPIRING notification was not sent yet.
    void
    S-29-04: an administrator clears a listed entry whose item no longer resolves (a stale entry).
    void
    endWindowsOf(Item item, String user)
    D-74: the item fullName was deleted; every active window naming it, or an item below it, ends: it is revoked by user, with Grant.REVOKED_ITEM_DELETED as the reason and a GRANT_REVOKE record, so it keeps its history.
    void
    D-74: after all items are loaded at startup, ends every active window whose item no longer exists (exists is false for its full name): it was deleted while Jenkins was down, or Jenkins stopped between deleting it and handling the deletion.
    void
    D-74: a new item was created (or copied) under fullName.
    The grant id as the permission checks see it (see listAll()), or null.
    findActiveCreateGrant(String user, ItemGroup<?> group, String itemName)
    D-40: the first active Create grant of user on the item group group (named exactly) whose name restriction (if any) allows itemName, or null.
    D-71: the active DELETE window of user on item, or null.
    findActiveDeleteGrant(String user, Item item, String itemFullName)
    As findActiveDeleteGrant(String, Item) for the item under the name itemFullName (its name before a move, for example).
    findActiveGrant(String user, Item item, GrantAction action)
    The first active grant of user naming exactly item's full name and including action (null: any action), or null.
    findActiveGrant(String user, String itemFullName, Item item, GrantAction action)
    As findActiveGrant(String, Item, GrantAction) for a window naming itemFullName rather than the item's current full name: its name before a rename or move, whose window was the one in use (change records; item events update the window's name only after those are written, WindowItemListener).
    findActiveGrant(String user, String itemFullName, GrantAction action)
    The first active grant of user whose scope is the item itemFullName (an item the caller can see must be at that name) and that includes action, or null.
    findActiveGrants(String user, Item item, GrantAction action)
    Every active grant of user naming exactly item's full name and including action (D-40: the CREATE check has to see all of them, since each may carry a different name restriction).
    The active grant that gives user Item/Configure on item: a grant with the CONFIGURE action naming the item, or a Create grant through which the user created it (D-35c).
    D-35c: the active grant of user through whose Create item was created, or null.
    findCreatingGrant(String user, String itemFullName, Item item)
    As findCreatingGrant(String, Item) for the record under the name itemFullName (its name before a rename, whose record was the one in use); the parent is item's.
    void
    D-74: writes the ends (and, S-39-02, the created-item records, and, D-75 (2), the changed-item lists) that could not be written so far (called by the periodic work; every grant write and every item event does the same first).
    void
    followItem(Item moved, String oldFullName, String newFullName)
    D-74: the item moved was renamed or moved from oldFullName to newFullName; every active window naming exactly oldFullName now names newFullName.
    void
    D-58a: a deleted item (and what was below it) leaves the "changed under a grant" state.
    void
    D-35c: an item recorded as created through an active grant was deleted.
    void
    Drops grants that retention has deleted from the store.
    void
    D-35c, D-74: a new item was created under fullName; a created-item record under that name belongs to an item that disappeared without a deletion event (deleted on disk and reloaded) and is dropped, so it never applies to the new item.
    get()
     
    guardingGrantId(String itemFullName)
    D-58a: the id of a grant that makes the item guarded (one that lists it as changed, else an active one covering it), or null when it is not guarded.
    boolean
    hasActiveGrant(String user, String itemFullName, Permission permission)
    Whether user currently holds permission on the item named itemFullName through an active grant.
    boolean
    D-58b: whether the item, or an item above it, is in the "changed under a grant" state (any grant, active or ended).
    boolean
    isGuardedItem(String itemFullName)
    D-58a: whether the item is guarded: covered by an active grant (its scope includes the item, the scope lies below the item when the item is a folder, or the item was created through it), or changed under a grant (active or ended) and not reviewed since.
    D-58a: the items in the "changed under a grant" state, sorted, at most limit.
    Every grant that is active right now (not expired, not revoked).
    Every known grant, active or ended, as the permission checks see it (a copy of the list): an end not yet written to its file is already in it.
    void
    markChanged(String grantId, String itemFullName)
    D-58a: notes that itemFullName was changed under grantId (a save or creation by the holder while their permission came only from the grant), persisting the grant.
    D-58c: the ids (job#number) of the runs of the item (a job, or the jobs below a folder) that were replayed under a grant, at most 50.
    void
    D-58b (3): the deliberate review.
    void
    noteMarkedRun(String jobFullName, String runId)
    D-58c: a marked run started (called by the run listener).
    void
    D-35c, S-09: drops from the created-items lists of active grants every item for which exists is false.
    D-35c: notes that user created item through the Create of an active grant, persisting the grant.
    boolean
    register(Grant grant, Item item)
    Persists a freshly created grant and makes it effective immediately.
    boolean
    registerApproved(Item item, Supplier<Grant> approval)
    #39: the approval's registration.
    void
    relocateChanged(Item moved, String oldFullName, String newFullName)
    D-58a (S-27-03): an item was renamed or moved.
    void
    relocateCreatedItem(Item moved, String oldFullName, String newFullName)
    D-35c: the item moved, recorded as created through an active grant, was renamed or moved from oldFullName to newFullName; the record follows it.
    static void
    Clears the in-memory cache.
    revoke(String grantId)
    Immediately revokes a grant (SPEC item 8: Manage holders only — the web action checks; the service double-checks) and appends a ChangeRecord(GRANT_REVOKE).
    int
    S-15, the kill switch: revokes every grant that is active right now and returns how many were closed.

    Methods inherited from class java.lang.Object

    clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
  • Method Details

    • get

      public static GrantService get()
    • resetCacheOnStartup

      @Initializer(after=PLUGINS_STARTED) public static void resetCacheOnStartup()
      Clears the in-memory cache. Jenkins is a singleton, so this is not about telling one running instance apart from another — it simply makes sure the first query after a fresh Jenkins startup (a real restart, or a test harness booting a new session) reloads from the files instead of serving a cache built for whatever was on disk before.
    • forgetDeleted

      public void forgetDeleted(Collection<String> grantIds)
      Drops grants that retention has deleted from the store. They ended before the retention cut-off, so none of them can be active; this only keeps the cache from holding them until the next restart.
    • hasActiveGrant

      public boolean hasActiveGrant(String user, String itemFullName, Permission permission)
      Whether user currently holds permission on the item named itemFullName through an active grant. Only the three grantable item permissions can ever match; any other permission returns false immediately.

      The item currently at that name is resolved as the caller sees it; none, no grant. Whether the action can apply to the item's kind is decided when the request is submitted and approved, and again by the grant layer (GrantAwareACL) and findActiveDeleteGrant(java.lang.String, hudson.model.Item).

    • findActiveGrant

      @CheckForNull public Grant findActiveGrant(String user, String itemFullName, @CheckForNull GrantAction action)
      The first active grant of user whose scope is the item itemFullName (an item the caller can see must be at that name) and that includes action, or null. A null action matches any action.

      For DELETE this does not look at the item's kind; callers holding the item use findActiveDeleteGrant(java.lang.String, hudson.model.Item), which also requires the item to be a job (D-71).

    • findActiveGrant

      @CheckForNull public Grant findActiveGrant(String user, @CheckForNull Item item, @CheckForNull GrantAction action)
      The first active grant of user naming exactly item's full name and including action (null: any action), or null.
    • findActiveGrant

      @CheckForNull public Grant findActiveGrant(String user, @CheckForNull String itemFullName, @CheckForNull Item item, @CheckForNull GrantAction action)
      As findActiveGrant(String, Item, GrantAction) for a window naming itemFullName rather than the item's current full name: its name before a rename or move, whose window was the one in use (change records; item events update the window's name only after those are written, WindowItemListener).
    • findActiveDeleteGrant

      @CheckForNull public Grant findActiveDeleteGrant(String user, @CheckForNull Item item)
      D-71: the active DELETE window of user on item, or null. A window's DELETE applies only to a job (GrantScope.deleteAppliesTo(hudson.model.Item)), so for any other item — a folder, a multibranch project, an organization folder — this is null whatever windows exist: deleting such a group would delete its children as SYSTEM.
    • findActiveDeleteGrant

      @CheckForNull public Grant findActiveDeleteGrant(String user, @CheckForNull Item item, @CheckForNull String itemFullName)
      As findActiveDeleteGrant(String, Item) for the item under the name itemFullName (its name before a move, for example).
    • findActiveGrants

      public List<Grant> findActiveGrants(String user, @CheckForNull Item item, GrantAction action)
      Every active grant of user naming exactly item's full name and including action (D-40: the CREATE check has to see all of them, since each may carry a different name restriction).

      For GrantAction.CREATE, item is the item group the new item is created in (Item/Create is checked on the group's ACL), so a CREATE window confers Create in its own folder only, never in a nested folder or at the root (D-71).

    • findActiveCreateGrant

      @CheckForNull public Grant findActiveCreateGrant(String user, @CheckForNull ItemGroup<?> group, String itemName)
      D-40: the first active Create grant of user on the item group group (named exactly) whose name restriction (if any) allows itemName, or null. null for the Jenkins root, where no window exists (S-13).
    • claimExpiringNotifications

      public List<Grant> claimExpiringNotifications(Duration lead)
      D-36: marks and returns every active grant whose window ends within lead and whose GRANT_EXPIRING notification was not sent yet. The flag is persisted before the caller dispatches, so a restart never resends.

      Each window is claimed on its own: a grant file that cannot be read or written now is logged and left unclaimed for the next run, and it never keeps the windows after it from being claimed nor drops the claims already written, which are all returned (a claim written but not returned would never be sent).

    • findCreatingGrant

      @CheckForNull public Grant findCreatingGrant(String user, @CheckForNull Item item)
      D-35c: the active grant of user through whose Create item was created, or null. While such a grant is active its holder also holds Item/Read and Item/Configure on that item (see GrantAwareACL), so matrix-auth's creator listener finds the permissions already held and writes no permanent entry; the permissions end with the window.

      The item must still lie directly inside the grant's scope folder, matched by parent, not by name prefix (D-71). Item events keep the record's name right (D-74): a rename or move updates it, a deletion drops it, and a new item arriving under a recorded name drops the record (forgetStaleCreatedItem(hudson.model.Item)), so the record never applies to an item someone else made.

    • findCreatingGrant

      @CheckForNull public Grant findCreatingGrant(String user, @CheckForNull String itemFullName, @CheckForNull Item item)
      As findCreatingGrant(String, Item) for the record under the name itemFullName (its name before a rename, whose record was the one in use); the parent is item's.
    • findConfigureGrant

      @CheckForNull public Grant findConfigureGrant(String user, @CheckForNull Item item)
      The active grant that gives user Item/Configure on item: a grant with the CONFIGURE action naming the item, or a Create grant through which the user created it (D-35c). null if neither exists. Used to name the grant a change or a violation came from (D-35b, SPEC item 9).
    • isGuardedItem

      public boolean isGuardedItem(String itemFullName)
      D-58a: whether the item is guarded: covered by an active grant (its scope includes the item, the scope lies below the item when the item is a folder, or the item was created through it), or changed under a grant (active or ended) and not reviewed since. Answered from the in-memory grant cache.
    • noteMarkedRun

      public void noteMarkedRun(String jobFullName, String runId)
      D-58c: a marked run started (called by the run listener).
    • markedRuns

      public List<String> markedRuns(Item item)
      D-58c: the ids (job#number) of the runs of the item (a job, or the jobs below a folder) that were replayed under a grant, at most 50. Served from the in-memory index; no build is loaded.
    • isChangedUnderGrant

      public boolean isChangedUnderGrant(Item item)
      D-58b: whether the item, or an item above it, is in the "changed under a grant" state (any grant, active or ended). Served from the in-memory grant cache, for page rendering.
    • guardingGrantId

      @CheckForNull public String guardingGrantId(String itemFullName)
      D-58a: the id of a grant that makes the item guarded (one that lists it as changed, else an active one covering it), or null when it is not guarded.
    • itemsChangedUnderGrant

      public List<String> itemsChangedUnderGrant(int limit)
      D-58a: the items in the "changed under a grant" state, sorted, at most limit.
    • markChanged

      public void markChanged(String grantId, String itemFullName)
      D-58a: notes that itemFullName was changed under grantId (a save or creation by the holder while their permission came only from the grant), persisting the grant. The mark is in effect in memory at once and a failed write is retried (rewriteChangedItems(io.jenkins.plugins.batchcontrol.model.Grant, java.util.function.UnaryOperator<java.util.List<java.lang.String>>, java.lang.String)); it never fails the save.
    • markReviewed

      public void markReviewed(Item item)
      D-58b (3): the deliberate review. The item and everything below it leave the "changed under a grant" state, and a GUARD_REVIEWED change record names the reviewer. The caller must hold Item/Configure on the item natively (asked with every grant layer off) or Overall/Administer; a user whose permission on the item comes from a grant cannot review it. The entries leave the state in memory at once and a grant file that cannot be written is written again later (removeChanged(java.util.Set<java.lang.String>)), so the record says what the guard does.
      Throws:
      org.springframework.security.access.AccessDeniedException - (AccessDeniedException3) when the caller may not review the item
    • clearStaleEntry

      public void clearStaleEntry(String fullName)
      S-29-04: an administrator clears a listed entry whose item no longer resolves (a stale entry). Checked here too: Overall/Administer.
    • relocateChanged

      public void relocateChanged(@CheckForNull Item moved, String oldFullName, String newFullName)
      D-58a (S-27-03): an item was renamed or moved. The "changed under a grant" state follows it and what is below it; and an item that was covered by an active grant under its old name, but is not under its new name, is marked as changed under that grant, so it stays guarded. A window naming the item or an item below it follows it (D-74, followItem(hudson.model.Item, java.lang.String, java.lang.String)), so it keeps covering the item and marks nothing.

      D-75 (2), as for created-item records (relocateCreatedItem(hudson.model.Item, java.lang.String, java.lang.String)): an entry already naming newFullName (in any letter case) or an item below it, other than the moved item's own entries, cannot be about the moved item, which only now took that name; it is dropped first, so the moved item does not inherit another item's state. An entry whose own item is still at exactly its name, next to the moved item, stays (DEF-E17-01, ownItemStillAt(java.lang.String, hudson.model.Item)). The entries change in the cache first and their write is retried (rewriteChangedItems(io.jenkins.plugins.batchcontrol.model.Grant, java.util.function.UnaryOperator<java.util.List<java.lang.String>>, java.lang.String)).

      T-GAP-320: core reports the location change of a folder and then of every item below it, each with its own old and new name, and this drop runs again in each of those events. So the entries of the items below a renamed or moved folder follow in their own item's event, exact names first, as windows and created-item records do (entriesFollowingLater(hudson.model.Item, java.lang.String, java.lang.String)); were they moved here, the drop of the later event would take them for stale entries at a name the item below has just taken. An entry below the item that no item below it carries (its item is gone, so no event of its own comes) follows here.

      Parameters:
      moved - the renamed or moved item (null when unknown)
    • forgetChanged

      public void forgetChanged(String fullName)
      D-58a: a deleted item (and what was below it) leaves the "changed under a grant" state.
    • listActive

      public List<Grant> listActive()
      Every grant that is active right now (not expired, not revoked).
    • register

      public boolean register(Grant grant, Item item)
      Persists a freshly created grant and makes it effective immediately. Called only by policy.GrantRequestService on approval, with the item the approval checked.

      D-74: item events do not wait for the approval, so the item may have been renamed or moved after it was checked and before this runs; the event then found no window to update. The window is therefore written under the item's full name as it is now. From here on, every event sees the window: item events are handled under this monitor too.

      D-71c (3), S-39-02: the item may also have been deleted after it was checked, and another item created, renamed or moved to its name; neither event found a window to end. So the item's identity is verified here, under this monitor, before the window becomes effective: unless the item at its full name is still the approved object and its deletion has not been reported, the window ends at once, with Grant.REVOKED_ITEM_DELETED as the reason and a GRANT_REVOKE record, like any other end (revokeOne(io.jenkins.plugins.batchcontrol.model.Grant, java.lang.String, java.lang.String, java.lang.String)).

      Returns:
      true when the window is in effect; false when it ended at once because its item was deleted (the caller's APPROVED notice says so, owner decision 2026-10-06)
    • registerApproved

      public boolean registerApproved(Item item, Supplier<Grant> approval)
      #39: the approval's registration. approval decides the request and returns the window to register; it runs under this monitor, and the window is registered as register(Grant, Item) does before the monitor is released.

      Why under this monitor: turning change control off writes the switch first and then calls revokeAllActive(), which holds this monitor while it ends every active window. A switch check made inside approval is therefore atomic with the registration relative to that revocation. Either the check runs first, so the window is in the cache when revokeAllActive lists it and the switch-off ends it, or it runs after, sees the switch off, and refuses before anything is decided or registered. No window can be registered after a switch-off has returned.

      Lock order, which this method keeps acyclic: policy.GrantRequestService's lock, then this monitor, then the store. The switch-off path takes the configuration's monitor and then this monitor. Nothing that runs under this monitor (including approval) takes the configuration's monitor or GrantRequestService's lock: the switch is a volatile read.

      Parameters:
      approval - decides the request (re-checks the switch, persists the decision) and returns the window to register; an exception it throws registers nothing
      Returns:
      as register(Grant, Item)
    • recordCreatedItem

      @CheckForNull public Grant recordCreatedItem(String user, Item item)
      D-35c: notes that user created item through the Create of an active grant, persisting the grant. Called by listener.CreatedItemGrantListener only when the creation was not possible without the grant. The Create window is the one on the item's parent folder whose name restriction admits the item's name (D-40, chosen outside the monitor, S-03).
      Returns:
      the grant that now records the item, or null when no active Create grant of user covers the item
    • relocateCreatedItem

      public void relocateCreatedItem(@CheckForNull Item moved, String oldFullName, String newFullName)
      D-35c: the item moved, recorded as created through an active grant, was renamed or moved from oldFullName to newFullName; the record follows it. Whether it still confers anything is decided by the parent check at query time, so moving the item out of the scope folder ends the permission. Core reports the change for every item below a renamed or moved folder too, so each record follows in its own item's event (exact names, as windows do).

      S-39-02, as for windows (followItem(hudson.model.Item, java.lang.String, java.lang.String)): a record already naming newFullName (in any letter case, other than the moved item's own old name) cannot be about the moved item and is dropped first, unless its own item is still at exactly its name, next to the moved item (DEF-E17-01, ownItemStillAt(java.lang.String, hudson.model.Item)); and when another item is at oldFullName again by the time the event is handled, the records naming it are dropped instead of moved. Records change in the cache first and their write is retried (rewriteCreatedItems(io.jenkins.plugins.batchcontrol.model.Grant, java.util.function.UnaryOperator<java.util.List<java.lang.String>>, java.lang.String)).

    • forgetCreatedItem

      public void forgetCreatedItem(String fullName)
      D-35c: an item recorded as created through an active grant was deleted. The record (and any record of an item below it) is dropped, so an item created later under the same name by someone else confers nothing.
    • followItem

      public void followItem(@CheckForNull Item moved, String oldFullName, String newFullName)
      D-74: the item moved was renamed or moved from oldFullName to newFullName; every active window naming exactly oldFullName now names newFullName. Core reports the location change of a folder and then of every item below it, each with its own old and new name, so the windows on the items inside a renamed or moved folder follow too, one event each. Ended windows keep the name they had: they record what was approved.

      S-39-02, so that no window is left on a name its item no longer has, and none reaches an item nobody approved:

      • an active window already naming newFullName (in any letter case, other than the moved item's own old name) cannot be about the moved item, which only now took that name; it ends first, as on the creation of a new item at its name (its own item vanished without an event, or moved away and its event has not been handled yet), unless its own item is still at exactly its name, next to the moved item (DEF-E17-01, ownItemStillAt(java.lang.String, hudson.model.Item));
      • when another item is already at oldFullName again by the time the event is handled (renames whose events interleave), a window naming it can no longer be told to be about the moved item rather than that other item; it ends instead of following;
      • a window whose file cannot be written with its new name ends (revokeOne(io.jenkins.plugins.batchcontrol.model.Grant, java.lang.String, java.lang.String, java.lang.String): in memory at once, durably through its GRANT_REVOKE record and the retried write) instead of staying on the old name, where an item renamed or moved there later would find it.
      Each of these ends has Grant.REVOKED_ITEM_NOT_FOLLOWED as its reason. They are fail-closed: the holder requests the window again.
    • endWindowsOf

      public void endWindowsOf(Item item, String user)
      D-74: the item fullName was deleted; every active window naming it, or an item below it, ends: it is revoked by user, with Grant.REVOKED_ITEM_DELETED as the reason and a GRANT_REVOKE record, so it keeps its history. Core reports the deletion before it frees the name, so no item can be created under that name first.

      S-39-02: the item is remembered as deleted, so a window approved on it whose registration runs after this event, while core has not freed the name yet, still ends (register(io.jenkins.plugins.batchcontrol.model.Grant, hudson.model.Item)).

      Parameters:
      user - the user who deleted the item, also when core deleted it as SYSTEM because it lay below a folder that user deleted (SPEC 6: the history names who did what; WindowItemListener)
    • endWindowsOnNewItem

      public void endWindowsOnNewItem(Item created)
      D-74: a new item was created (or copied) under fullName. An active window naming that name cannot be about the new item: its own item disappeared without a deletion event (deleted on disk, then reloaded), or was deleted while the window was being approved. It ends, as for a deletion, so it never applies to the new item. The name is compared as Jenkins looks names up, without regard to letter case (S-39-02): a window under another spelling of the name could otherwise reach the new item through a later rename that only changes the case. A window whose own item is still at exactly its name, next to the new item in a folder that looks its children up by exact name, is about that item and stays (DEF-E17-01, ownItemStillAt(java.lang.String, hudson.model.Item)).
    • endWindowsOfMissingItems

      public void endWindowsOfMissingItems(Predicate<String> exists)
      D-74: after all items are loaded at startup, ends every active window whose item no longer exists (exists is false for its full name): it was deleted while Jenkins was down, or Jenkins stopped between deleting it and handling the deletion.
    • pruneCreatedItems

      public void pruneCreatedItems(Predicate<String> exists)
      D-35c, S-09: drops from the created-items lists of active grants every item for which exists is false. Called after all items are loaded (startup and reload), so a record of an item deleted on disk while no listener saw it cannot confer anything on an item created later under the same name by someone else. An item that failed to load loses its record too, which only takes permissions away (fail-safe).
    • forgetStaleCreatedItem

      public void forgetStaleCreatedItem(Item created)
      D-35c, D-74: a new item was created under fullName; a created-item record under that name belongs to an item that disappeared without a deletion event (deleted on disk and reloaded) and is dropped, so it never applies to the new item. The name is compared as Jenkins looks names up, without regard to letter case (S-39-02), except that a record whose own item is still at exactly its name, next to the new item, stays (DEF-E17-01, ownItemStillAt(java.lang.String, hudson.model.Item)). That name only: a copied folder's children are created (and possibly recorded) before the folder's own creation event.
    • revoke

      public Grant revoke(String grantId)
      Immediately revokes a grant (SPEC item 8: Manage holders only — the web action checks; the service double-checks) and appends a ChangeRecord(GRANT_REVOKE).
      Returns:
      the revoked grant
      Throws:
      IllegalArgumentException - if no such grant exists
      IllegalStateException - if the grant was already revoked
    • revokeAllActive

      public int revokeAllActive()
      S-15, the kill switch: revokes every grant that is active right now and returns how many were closed. Called when the change-control switch is turned off (see config.BatchControlGlobalConfiguration#setChangeControlEnabled) — turning the switch off has to close the windows that are already open, not merely stop new ones from conferring.

      Two things this does that GrantAwareACL's switch check alone does not:

      • it is durable. The ACL check makes an open window stop conferring while the switch is off; without the revocation, switching change control back on would bring every one of those windows back to life for the remainder of its duration (up to maxGrantMinutes, default 240). A revoked grant never returns — Grant.isActiveAt(java.time.Instant) is false for it for good.
      • it is visible. Each revocation appends a ChangeRecord(GRANT_REVOKE) naming the account that flipped the switch, the grantee whose window was closed and the position in the batch, so the audit history says who cut the work off and how much of it there was. A silent mass revocation would be worse than none: the users whose in-flight configuration changes suddenly stop working would have nothing to read.

      This is destructive to other people's in-flight work, by design. Whoever turns the switch off cuts off every change currently underway with no warning; that is the trade the owner chose over a switch that leaves windows quietly open. The caller writes its own CONFIG_TOGGLE record before calling this, so the history reads "the switch went off" and then "these windows were closed", in that order.

      No permission check of its own, deliberately, and unlike revoke(String): the caller is a plain configuration setter whose only web entry point is core's /manage/configure submission, already gated on Overall/Administer — which implies BatchControl/Manage in any case. Putting a check here would instead impose a permission gate on a setter that JCasC and startup also drive.

      Returns:
      the number of active windows that were closed
    • flushUnsavedEnds

      public void flushUnsavedEnds()
      D-74: writes the ends (and, S-39-02, the created-item records, and, D-75 (2), the changed-item lists) that could not be written so far (called by the periodic work; every grant write and every item event does the same first).
    • listAll

      public List<Grant> listAll()
      Every known grant, active or ended, as the permission checks see it (a copy of the list): an end not yet written to its file is already in it. Screens that list windows read this, not the files, so they agree with listActive().
    • find

      @CheckForNull public Grant find(String id)
      The grant id as the permission checks see it (see listAll()), or null.