Class GrantsSection

java.lang.Object
io.jenkins.plugins.batchcontrol.action.GrantsSection
All Implemented Interfaces:
ModelObject, org.kohsuke.stapler.StaplerProxy

@Restricted(org.kohsuke.accmod.restrictions.NoExternalUse.class) public class GrantsSection extends Object implements ModelObject, org.kohsuke.stapler.StaplerProxy
The grant screens at /batch-control/grants/ (SPEC item 8, JIT permission requests).

URL space (fixed contract, asserted by tests):

  • /batch-control/grants/ — pending requests, active and ended windows (served from index.jelly; PUT/DELETE/PATCH get 405). D-66: no form here; the old prefill links (?scopeFullName=, ?from=) are redirected to new
  • /batch-control/grants/new — the new request form as a page; /batch-control/grants/dialog — the same form for core's dialog (D-66)
  • POST /batch-control/grants/create — submit a new grant request
  • POST /batch-control/grants/checkScopeFullName — the form's check of the item name (D-71): its kind, or "No such item"
  • /batch-control/grants/<id>/ — request detail; POST approve / reject / cancel / revoke (see GrantRequestItem)
  • POST /batch-control/grants/active/<grantId>/revoke — revoke an active grant (see ActiveGrantsSection)

No state transition logic lives here; everything is delegated to GrantRequestService and GrantService. Viewing requires one of the plugin permissions (requesters see their requests, approvers their inbox, managers the active grants), and the whole subtree additionally closes while change control is off — both enforced by getTarget(). The one exception to the closure is POST <id>/approve, which goes on to GrantRequestService.approve(java.lang.String, java.lang.String) so that the service refuses and records the approval (LIMITATIONS 29).

The switch closes this screen and nothing else. It must never reach the audit trail: the Change Records and History screens keep showing the windows that existed and the changes made under them, because a switch flip that retroactively hid audit records would be a worse defect than the one the gate fixes.

  • Field Details

    • PAGE_SIZE

      public static final int PAGE_SIZE
      Page size for both the request list and the active grant list.
      See Also:
  • Constructor Details

    • GrantsSection

      public GrantsSection()
  • Method Details

    • getTarget

      public Object getTarget()
      Specified by:
      getTarget in interface org.kohsuke.stapler.StaplerProxy
    • getDisplayName

      public String getDisplayName()
      Specified by:
      getDisplayName in interface ModelObject
    • getViewPermissions

      public Permission[] getViewPermissions()
      Permissions for this screen's l:layout (the same set its section gate checks).
    • getDynamic

      @CheckForNull public GrantRequestItem getDynamic(String id)
      Stapler: serves /batch-control/grants/<id>/; null renders a 404. A grant request the caller may not see (P-09, S-01) renders exactly like a nonexistent one so its existence is not disclosed, and so does every failed lookup (S-39-01, RecordLookup).
    • getActive

      public ActiveGrantsSection getActive()
      Stapler: serves /batch-control/grants/active/... (revoke endpoints).
    • doCreate

      public void doCreate(org.kohsuke.stapler.StaplerRequest2 req, org.kohsuke.stapler.StaplerResponse2 rsp) throws IOException, jakarta.servlet.ServletException
      POST /batch-control/grants/create — submits a new grant request. This method only parses HTTP input; all business validation (action set non-empty, duration cap, approver eligibility, reason required) is enforced by GrantRequestService.create(io.jenkins.plugins.batchcontrol.model.GrantScope, java.util.List<io.jenkins.plugins.batchcontrol.model.GrantAction>, int, java.lang.String, java.lang.String).

      Form fields: scopeFullName (the one job or folder the window names, D-71; a scopeType field sent by an earlier form is ignored), actions (multi-valued checkboxes), durationMinutes (preset select), customDurationMinutes (optional free number overriding the preset, capped client-side at maxGrantMinutes and re-checked by the service), reason, approvers (repeated, one user id each; D-37) and the optional createNamePattern (exact name or /regex/, CREATE only; D-40). The form shows the name restriction only while Create is ticked, so it is read only for a request that includes CREATE, as core's f:optionalBlock does not count the fields of a collapsed block.

      Throws:
      IOException
      jakarta.servlet.ServletException
    • getLegacyFormQuery

      @CheckForNull public String getLegacyFormQuery()
      D-66: the query string to carry from an old prefill link of the list page (/batch-control/grants/?scopeFullName=... or ?from=...) to the form page, or null when the list page was opened without one. Only the prefill parameters are carried, each URL-encoded; the form page resolves them again before it shows anything.
    • getFormErrors

      public FormErrors getFormErrors()
      The refusal of the new-request form on this request, or an empty one (DEF-09, Jelly).
    • doCheckScopeFullName

      public FormValidation doCheckScopeFullName(@QueryParameter String value)
      POST /batch-control/grants/checkScopeFullName — the request form's check of the item name (D-71): the kind of the job or folder the window would name, for example Pipeline 'team/nightly', so the requester sees what they are asking for before submitting. Read-only; doCreate(org.kohsuke.stapler.StaplerRequest2, org.kohsuke.stapler.StaplerResponse2) and the service decide for real.

      The item is looked up as the caller (GrantRequestService.findScopeItem(java.lang.String)): an item that does not exist and one the caller may not read (Item/Discover only included) get the same answer, so the check cannot be used to probe for names. The answer only repeats the item's own full name and its descriptor's display name. A blank field gets no message (the form is not filled in yet; submitting it is refused next to the field).

      spec-review-S6 M-1: the kind is shown with its icon, as in the lists (tags/scopeItem.jelly), so the answer is markup (kindMarkup(io.jenkins.plugins.batchcontrol.model.ItemKind, java.lang.String)).

    • isCanRequest

      public boolean isCanRequest()
      View gating for the new-request form; doCreate(org.kohsuke.stapler.StaplerRequest2, org.kohsuke.stapler.StaplerResponse2) re-checks for real.
    • isCanRevoke

      public boolean isCanRevoke()
      View gating for the Revoke buttons; the revoke endpoint re-checks for real.
    • getDurationOptions

      public List<Integer> getDurationOptions()
      Preset duration choices from the global configuration.
    • getCreateNamePatternMaxLength

      public int getCreateNamePatternMaxLength()
      Maximum length of the CREATE name restriction, for the form's maxlength.
    • getMaxGrantMinutes

      public int getMaxGrantMinutes()
      Upper bound in minutes for a grant request (client-side cap; the service re-checks).
    • getApproverOptions

      public List<String> getApproverOptions()
      Approver candidates (global list minus the current user, per the self-approval policy).
    • formatParts

      public List<String> formatParts(Instant instant)
      e2e-09 DEF-01: a timestamp as its date and its time-with-zone, for the grant tables, which render each part unbroken so that a narrow window breaks a cell only between them (never inside a date) and the page does not scroll sideways at 1280 px. Empty for null.
    • remaining

      public String remaining(@CheckForNull Instant expiresAt)
      Jelly helper: how much of a grant window is left (12 min 30 sec), so the user does not have to subtract the absolute expiry time from the current time (UX-11).
      Returns:
      the remaining span, or "expired" once the window has closed (a grant can still be listed for the moment between expiry and the sweeper run)
    • minutesLabel

      public String minutesLabel(int minutes)
      Jelly helper: 1 minute / 15 minutes for the duration select (UX-12).
    • isPrefilledForFolder

      public boolean isPrefilledForFolder()
      Whether the form was opened for a folder where a window's Create applies (?scopeFullName= resolves, as the viewer, to a regular folder; D-71: not a job and not a computed folder such as a multibranch project or an organization folder).
    • getPrefillScopeFullName

      public String getPrefillScopeFullName()
      Full name the new-request form starts with: the renewed grant's scope (D-33), else ?scopeFullName= — how JobGrantRequestAction hands the job over, so a user who arrives from a job page does not have to retype its path (U-01).

      The parameter is resolved, never echoed: the name is looked up through Visibility.findVisibleItem(java.lang.String) and what the form receives is the model object's own getFullName(). An item that does not exist, or that the caller cannot see, yields an empty field, so this cannot be used to reflect arbitrary text into the page or to probe for names — it renders exactly what a caller could already read from the item's own URL. A renewed grant is likewise resolved from the store, and only the caller's own; its item's name is the one getRenewScope() shows (D-75 (1)).

      Returns:
      the canonical full name, or an empty string when there is nothing to prefill
    • isPrefilled

      public boolean isPrefilled()
      Whether the form was opened for a specific item: from a job's "Request Change Permission" entry (CONFIGURE then starts checked, the user is trying to change that job) or from the re-request link of an ended grant (its actions start checked, D-33).
    • getRenewScope

      @CheckForNull public ScopeDisplay getRenewScope()
      D-75 (1), security-39 S-39-04: the name the form's prefill note shows for the renewed grant's item (tags/scopeItem.jelly), or null when the form renews nothing. A window's followed name only to a viewer who may read the item now; otherwise the approved name with the fixed "moved" note, and the name field starts empty (getPrefillScopeFullName()).
    • getRenewSource

      @CheckForNull public Grant getRenewSource()
      The ended grant the form renews (?from=<grantId>), or null.
    • isPrefillAction

      public boolean isPrefillAction(String action)
      Whether the action checkbox starts checked: the renewed grant's actions; else the ?actions= values (matched against GrantAction, how a refusal such as the delete veto hands over the action it needs, DEF-26); else CONFIGURE when the form was opened for an item.
    • getPrefillCreateNamePattern

      public String getPrefillCreateNamePattern()
      The renewed grant's CREATE name restriction, or empty.
    • isPrefillDuration

      public boolean isPrefillDuration(int minutes)
      Whether the preset minutes starts selected: the renewed grant's length when it is one of the presets. Otherwise none is marked and the browser shows the first option.
    • getPendingItems

      public List<GrantsSection.Row> getPendingItems()
      Pending grant requests on the current page, oldest first (closest to its timeout on top).
    • getPendingPage

      public int getPendingPage()
    • getPendingTotal

      public int getPendingTotal()
    • isHasPendingPrevious

      public boolean isHasPendingPrevious()
    • isHasPendingNext

      public boolean isHasPendingNext()
    • getActiveItems

      public List<GrantsSection.Row> getActiveItems()
      Open permission windows on the current page, most recently granted first.
    • getActivePage

      public int getActivePage()
    • getActiveTotal

      public int getActiveTotal()
    • isHasActivePrevious

      public boolean isHasActivePrevious()
    • isHasActiveNext

      public boolean isHasActiveNext()
    • getEndedItems

      public List<GrantsSection.Row> getEndedItems()
      Ended windows and requests that never became one (rejected, cancelled, expired), on the current page, most recently ended first. SPEC item 8 (D-33): the history of expired windows with a re-request link.
    • getEndedPage

      public int getEndedPage()
    • getEndedTotal

      public int getEndedTotal()
    • isHasEndedPrevious

      public boolean isHasEndedPrevious()
    • isHasEndedNext

      public boolean isHasEndedNext()
    • endedLabel

      public String endedLabel(Grant grant)
      How a grant ended, in words: "Expired", "Revoked by admin", or for a revocation by the change control switch "Revoked (change control turned off) by admin".
    • endedAt

      public Instant endedAt(Grant grant)
      When a grant ended: its revocation time, else its expiry.
    • endedAt

      public Instant endedAt(GrantsSection.Row row)
      When a row ended: its window's end, else the request's decision (or creation).
    • howEnded

      public String howEnded(GrantsSection.Row row)
      How a row ended, in words: the window's end, else the request's outcome.
    • isCanRenew

      public boolean isCanRenew(GrantsSection.Row row)
      Whether the re-request button is shown: the caller's own ended window, and may request.
    • createNameLabel

      public String createNameLabel(GrantsSection.Row row)
      The CREATE name restriction of a row in words (DEF-19): the pattern, "any name" for an unrestricted CREATE, empty when the row does not include CREATE.
    • join

      public String join(Collection<?> items)
      Jelly helper: comma-joined action list ("CREATE, CONFIGURE").