In Part 1 of Updating Space Permissions without Space Admin, we created custom a REST Endpoint in Confluence using ScriptRunner. So now the question is how do we allow our users to call the endpoint? Ultimately this is up to you, there's probably more ways to do this than my convoluted way of using Jira and ScriptRunner for Jira. Here's my solution...
First I create a group for each space that I use to identify users that I want to be able to update permissions. The group doesn't need to be assigned any permissions within Confluence.
I then created an issue type in Jira and associated a workflow with that issue. My issue form asks the following:
- Confluence Space, Type: Select List (Single)
- User(s), Type: User Picker (multiple users)
- Space Permissions, Type: Check boxes
- Options (You can define whatever options you'd like, I left out any that I only want an Admin to assign)
- View Space
- Create/Edit Pages
- Delete Pages
- etc....
I have a Jira ScriptRunner job that runs to update the Confluence Space field with the current spaces, seen below:
com.atlassian.jira.component.ComponentAccessorimport <br>com.atlassian.jira.issue.context.GlobalIssueContext<br>import groovy.json.JsonSlurper <br><br>//Get the a list of space information so we can get all space names<br>def url =<span> </span>"<a href="https://base-url/rest/api/space" target="_blank" rel="noopener nofollow noreferrer">https://base-url/rest/api/space</a>" <br><br>//Use Postman encrypt your username and password for your Confluence service account with admin permissions.<br><br>def basicAuth =<span> </span>"Basic Y234msdlfms3nsnsdffas3=="<br>def connection = url.toURL().openConnection()
connection.setRequestProperty("Authorization", basicAuth)connection.connect()
def json =<span> </span>new JsonSlurper().parseText(connection.getInputStream().getText())
def spacesField = ComponentAccessor.customFieldManager.getCustomFieldObjectsByName("Confluence Space")<br>def optionsManager = ComponentAccessor.optionsManager<br>def fieldConfigSchemeManager = ComponentAccessor.fieldConfigSchemeManager<br>def globalIssueContext = GlobalIssueContext.getInstance()<br>def configScheme = fieldConfigSchemeManager.getConfigSchemesForField(spacesField[0])[0]<br>def fieldConfig = configScheme?.oneAndOnlyConfig ?: fieldConfigSchemeManager.getRelevantConfig(globalIssueContext, spacesField[0])<br>def existingOptions = optionsManager.getOptions(fieldConfig)<br>def option
//Loop through the rest call results to get all the space names and add them to the Confluence Space custom field.<br>json.results.each { space -> <br> //Check to see if the space name already exists as an option. <br> option = existingOptions?.find { it.value.equals(space.name<span> </span>as String) } <br><br> //If the space name is not already in the list, then add it. <br> if(!option) <br> { <br> optionsManager.createOption(fieldConfig,<span> </span>null,<span> </span>0, space.name<span> </span>as String) <br> } <br><br> optionsManager = ComponentAccessor.optionsManager <br> spacesField = ComponentAccessor.customFieldManager.getCustomFieldObjectsByName("Confluence Space") <br> fieldConfigSchemeManager = ComponentAccessor.fieldConfigSchemeManager <br> globalIssueContext = GlobalIssueContext.getInstance() <br> configScheme = fieldConfigSchemeManager.getConfigSchemesForField(spacesField[0])[0] <br> fieldConfig = configScheme?.oneAndOnlyConfig ?: fieldConfigSchemeManager.getRelevantConfig(globalIssueContext, spacesField[0]) <br> existingOptions = optionsManager.getOptions(fieldConfig) <br><br> //Loop through the existing options and disable any option where the space no longer exists (i.e. it has been deleted.) <br> for (opt<span> </span>in existingOptions) <br> { <br> def Boolean exists = false <br> json.results.each { spc -> <br> <br> if(opt.getValue().equals(spc.name<span> </span>as String)) <br> { <br> exists = true <br> } <br> } <br> <br> if(!exists) <br> { <br> opt.setDisabled(true) <br> } <br> }<br>}import
My workflow has a post function on the create that checks if the user submitting is in the group for the space, then interprets the fields on the form and creates the REST Call and finally makes the call using my service account, as seen below (if not the issue is assigned to me so I can provide the permissions).
com.atlassian.jira.component.ComponentAccessor<br>import com.atlassian.jira.issue.context.GlobalIssueContext
import groovy.json.JsonSlurper <br>import org.apache.log4j.Category
def Category log = Category.getInstance("com.onresolve.jira.groovy")<br>log.setLevel(org.apache.log4j.Level.DEBUG)
//Get the space selected on the issue.<br>def space = issue.getCustomFieldValue(ComponentAccessor.getCustomFieldManager().getCustomFieldObjectsByName("Confluence Space")[0])<span> </span>as String
//Get the a list of space information so we can associate the space name with the space key.<br>def url =<span> </span>"<a href="https://base-url/rest/api/space" target="_blank" rel="noopener nofollow noreferrer">https://base-url/rest/api/space</a>"
//Use Postman encrypt your username and password for your Confluence service account with admin permissions.<br>def basicAuth =<span> </span>"Basic Ymmdsdsd343sdfmsms=="<br>def connection = url.toURL().openConnection()
connection.setRequestProperty("Authorization", basicAuth)<br>connection.connect()
//Use the selected space to to find the space key from the rest call return.<br>def json =<span> </span>new JsonSlurper().parseText(connection.getInputStream().getText())<br>def spaceKey =<span> </span>"spaceKey=" + json.results[json.results.findIndexOf { it.name == space } ].key
def groupManager = ComponentAccessor.getGroupManager()
//Check that the user making the request is allowed to update permissions. Notice we concatenate the space key to get the correct owners group for the selected space.<br>if (groupManager.getUsersInGroup("space-owners-" + json.results[json.results.findIndexOf { it.name == space } ].key.toLowerCase()).contains(issue.getReporter()))<br>{ <br> //Get the selected permissions from the issue and start building our rest call. <br> def permissionsField = ComponentAccessor.getCustomFieldManager().getCustomFieldObjectsByName("Space Permissions") <br> def selectedPermissions = issue.getCustomFieldValue(permissionsField[0])
def permissions
<br> if (selectedPermissions !=<span> </span>null) <br> { <br> if (selectedPermissions*.value.contains("View Space")) <br> { <br> permissions =<span> </span>"&view=true" <br> } <br> else <br> { <br> permissions =<span> </span>"&view=false" <br> } <br> if (selectedPermissions*.value.contains("Create/Edit Pages")) <br> { <br> permissions = permissions +<span> </span>"&createEditPage=true" <br> } <br> else <br> { <br> permissions = permissions +<span> </span>"&createEditPage=false" <br> } <br> if (selectedPermissions*.value.contains("Delete Any Page")) <br> { <br> permissions = permissions +<span> </span>"&removePage=true" <br> } <br> else <br> { <br> permissions = permissions +<span> </span>"&removePage=false" <br> } <br> if (selectedPermissions*.value.contains("Create/Edit Blogs")) <br> { <br> permissions = permissions +<span> </span>"&editBlog=true" <br> } <br> else <br> { <br> permissions = permissions +<span> </span>"&editBlog=false"
} <br> if (selectedPermissions*.value.contains("Add Attachments")) <br> { <br> permissions = permissions +<span> </span>"&createAttachment=true" <br> } <br> else <br> { <br> permissions = permissions +<span> </span>"&createAttachment=false" <br> } <br> if (selectedPermissions*.value.contains("Delete Anyone's Attachments")) <br> { <br> permissions = permissions +<span> </span>"&removeAttachment=true" <br> } <br> else <br> { <br> permissions = permissions +<span> </span>"&removeAttachment=false" <br> } <br> if (selectedPermissions*.value.contains("Add Comments")) <br> { <br> permissions = permissions +<span> </span>"&comment=true" <br> } <br> else <br> { <br> permissions = permissions +<span> </span>"&comment=false" <br> } <br> if (selectedPermissions*.value.contains("Modify Page Restrictions")) <br> { <br> permissions = permissions +<span> </span>"&setPagePermissions=true" <br> } <br> else <br> { <br> permissions = permissions +<span> </span>"&setPagePermissions=false" <br> } <br> if (selectedPermissions*.value.contains("Export Space/Pages")) <br> { <br> permissions = permissions +<span> </span>"&exportSpace=true" <br> } <br> else <br> { <br> permissions = permissions +<span> </span>"&exportSpace=false" <br> } <br> if (selectedPermissions*.value.contains("Delete Own")) <br> { <br> permissions = permissions +<span> </span>"&removeOwn=true" <br> } <br> else <br> { <br> permissions = permissions +<span> </span>"&removeOwn=false" <br> } <br> } <br> else <br> { <br> permissions =<span> </span>"&view=false&removeOwn=false&createEditPage=false&removePage=false&editBlog=false&createAttachment=false&removeAttachment=false&comment=false&setPagePermissions=false&exportSpace=false" <br> } <br><br> //Get the selected users to apply the permissions to. <br> def userField = ComponentAccessor.getCustomFieldManager().getCustomFieldObjectsByName("User(s)") <br> def users = issue.getCustomFieldValue(userField[0]) <br><br> //Loop through the users and make the rest call to the rest end point we created in Confluence. <br> users.each { user -> <br> url =<span> </span>"<a href="https://base-url/rest/scriptrunner/latest/custom/updatePermissions?" target="_blank" rel="noopener nofollow noreferrer">https://base-url/rest/scriptrunner/latest/custom/updatePermissions?</a>" + spaceKey +<span> </span>"&user=" + user.getUsername() + permissions <br> connection = url.toURL().openConnection() <br> connection.setRequestProperty("Authorization", basicAuth) <br> connection.connect() <br><br> //Log an error in if the return code is not 200. <br> if(connection.getResponseCode() !=<span> </span>200)<span> </span>//Shows as error, but works. <br> { <br> log.debug("Permissions were not set for user " + user +<span> </span>"!") <br> } <br> }<br>}import