Read only view

Extended read and system read permission allow read only access to parts of Jenkins.

The jelly taglib’s in core will render a read only look and feel, if the user only has access to read the view.

This is handled for developers transparently if they are already using one of the supported views, like the job configure view.

Enabling permissions that use read-only views

There are 3 permissions currently that interact with read-only views:

  • Overall/SystemRead

  • Agent/ExtendedRead

  • Job/ExtendedRead

They are currently not enabled by default, the easiest way to enable them is to install the Extended read permission plugin v3.2 or above.

Enabling read-only view support

Jelly example:

<!-- configure permissions on a job required for write access -->
<j:set var="readOnlyMode" value="${!it.hasPermission(it.CONFIGURE)}" />

<!-- administrator permissions required for write access -->
<j:set var="readOnlyMode" value="${!app.hasPermission(app.ADMINISTER)}" />

Groovy example:

// configure permissions on a job required for write access
set("readOnlyMode", !it.hasPermission(it.CONFIGURE))

// administrator permissions required for write access
set("readOnlyMode", !app.hasPermission(app.ADMINISTER))

Partial view read-only

It’s also possible to only use read-only mode for part of a view,

This is an example taken from the /configure page showing the SYSTEM_READ and MANAGE permissions working together, non-editable parts of the view render in a read only manner:

<j:forEach var="descriptor" items="${h.getSortedDescriptorsForGlobalConfigUnclassifiedReadable()}">
    <j:set var="editable" value="${h.getSortedDescriptorsForGlobalConfigUnclassified()}"/>
    <j:set var="readOnlyMode" value="${!editable.contains(descriptor)}" />
  <!-- (unnecessary context removed) -->
  <st:include page="${descriptor.globalConfigPage}" from="${descriptor}" />
  <!-- (unnecessary context removed) -->
</j:forEach>

Taglib changes required

If you provide your own taglib that isn’t in Jenkins core you may need to make some changes.

Depending on what change is required there are a few different changes you might need to make, here are a few examples.

Rendering plain text instead of a control

We also add a default text when it is empty (N/A in this case):

<j:choose>
    <j:when test="${readOnlyMode}">
        <j:choose>
            <j:when test="${empty(value)}">
                <span class="jenkins-not-applicable">N/A</span>
            </j:when>
            <j:otherwise><pre class="jenkins-readonly">${value}</pre></j:otherwise>
        </j:choose>
    </j:when>
    <j:otherwise>
    ... your normal default configuration
    </j:otherwise>
</j:choose>

Disabling the control

It is preferable to render plain text or some other method that is obvious to the user what is going on (disabled controls aren’t very obvious), but a lot of the javascript and jelly taglibs depend on inputs of specific types being there for rendering, so sometimes it is preferable to just disable the control:

<input type="radio"
    checked="${attrs.checked?'true':null}"
    disabled="${readOnlyMode?'true':null}"
/>

Removing controls from a page

Like an X button on a repeatable element:

<j:if test="${!readOnlyMode}">
    <f:block>
    <div align="right">
        <f:repeatableDeleteButton value="${attrs.deleteCaption}" />
    </div>
    </f:block>
</j:if>

Allowing access to a view

Normally in Jenkins the layout tag of a page will have a permission attribute which governs which permission is required to access the page.

Jenkins has a concept of 'impliedBy' in it’s permission, which means that each permission is allowed to be automatically granted by another one. Job/ExtendedRead is implied by Job/Configure, which means that requiring Job/ExtendedRead to access a view, will also grant Job/Configure access.

Here are some examples that show what you will need to update the layout tag to:

Requiring the Job/ExtendedRead permission in Jelly:

<l:layout permission="${it.EXTENDED_READ}">
...
</l:layout>

Overall/SystemRead Jelly:

<l:layout permission="${app.SYSTEM_READ}">
...
</l:layout>

Overall/SystemRead Groovy:

l.layout(permission: app.SYSTEM_READ) {
...
}

Removing part of a view

Often it makes sense to remove parts of a view, such as 'Save' or 'Apply' buttons.

For system read the simplest way to do this is to use the l:isAdmin tag:

isAdmin Jelly:

<l:isAdmin>

</l:isAdmin>

isAdmin Groovy:

l.isAdmin() {
...
}

There’s also a l:hasAdministerOrManage tag that can be used to check for the Jenkins/Administer or `Jenkins/Manage permissions.

If those don’t fit, then you can write your own permission check:

hasPermission Jelly:

<j:if test="${it.hasPermission(it.CONFIGURE)">

</j:if>

hasPermission Groovy:

if (!it.hasPermission(it.CONFIGURE)) {
...
}

Server side changes required

Firstly you will need to update any server side permission checks to now check on load for the Read permission, rather than the Configure or Administer permission. See Using StaplerProxy for more information on this feature.

i.e.

public Object getTarget() {
    checkPermission(Jenkins.SYSTEM_READ);
}

Then you will need to review if additional permission checks are required, i.e. any methods that allow saving or sensitive data access should now require the permission that was previously in use.

Searching for 'public .* do' is a good start as web methods normally start with 'do', but you should carefully review to make sure nothing is now exposed that shouldn’t be.

public void doInstallPlugin() {
    checkPermission(Jenkins.ADMINISTER);
}

Compatibility with Jenkins core version

You do not need to worry about bumping your minimum required Jenkins core version as this feature is driven by variables set in the jelly context, old versions of Jenkins will just ignore it.

This was released in 2.222