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:
I have a Jira ScriptRunner job that runs to update the Confluence Space field with the current spaces, seen below:
import
com.atlassian.jira.component.ComponentAccessor
import
com.atlassian.jira.issue.context.GlobalIssueContextimport
groovy.json.JsonSlurper
//Get the a list of space information so we can get all space namesdef
url =
"https://base-url/rest/api/space"
//Use Postman encrypt your username and password for your Confluence service account with admin permissions.def
basicAuth =
"Basic Y234msdlfms3nsnsdffas3=="
def
connection = url.toURL().openConnection()
connection.setRequestProperty(
"Authorization"
, basicAuth)
connection.connect()
def
json =
new
JsonSlurper().parseText(connection.getInputStream().
getText
())
def
spacesField = ComponentAccessor.customFieldManager.getCustomFieldObjectsByName(
"Confluence Space"
)
def
optionsManager = ComponentAccessor.optionsManager
def
fieldConfigSchemeManager = ComponentAccessor.fieldConfigSchemeManager
def
globalIssueContext = GlobalIssueContext.getInstance()
def
configScheme = fieldConfigSchemeManager.getConfigSchemesForField(spacesField[
0
])[
0
]
def
fieldConfig = configScheme?.oneAndOnlyConfig ?: fieldConfigSchemeManager.getRelevantConfig(globalIssueContext, spacesField[
0
])
def
existingOptions = optionsManager.getOptions(fieldConfig)
def
option
//Loop through the rest call results to get all the space names and add them to the Confluence Space custom field.
json.results.
each
{ space ->
//Check to see if the space name already exists as an option.
option = existingOptions?.
find
{ it.value.equals(space.name
as
String) }
//If the space name is not already in the list, then add it.
if
(!option)
{
optionsManager.createOption(fieldConfig,
null
,
0
, space.name
as
String)
}
optionsManager = ComponentAccessor.optionsManager
spacesField = ComponentAccessor.customFieldManager.getCustomFieldObjectsByName(
"Confluence Space"
)
fieldConfigSchemeManager = ComponentAccessor.fieldConfigSchemeManager
globalIssueContext = GlobalIssueContext.getInstance()
configScheme = fieldConfigSchemeManager.getConfigSchemesForField(spacesField[0
])[
0
]
fieldConfig = configScheme?.oneAndOnlyConfig ?: fieldConfigSchemeManager.getRelevantConfig(globalIssueContext, spacesField[
0
])
existingOptions = optionsManager.getOptions(fieldConfig)
//Loop through the existing options and disable any option where the space no longer exists (i.e. it has been deleted.)
for (opt
in
existingOptions)
{
def
Boolean exists = false
json.results.
each
{ spc ->
if
(opt.getValue().equals(spc.name
as
String))
{
exists = true
}
}
if
(!exists)
{
opt.setDisabled(true)
}
}}
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).
import
com.atlassian.jira.component.ComponentAccessor
import
com.atlassian.jira.issue.context.GlobalIssueContext
import
groovy.json.JsonSlurper
importorg.apache.log4j.Category
def
Category log = Category.getInstance(
"com.onresolve.jira.groovy"
)
log.setLevel(org.apache.log4j.Level.DEBUG)
//Get the space selected on the issue.
def
space = issue.getCustomFieldValue(ComponentAccessor.getCustomFieldManager().getCustomFieldObjectsByName(
"Confluence Space"
)[
0
])
as
String
//Get the a list of space information so we can associate the space name with the space key.
def
url =
"https://base-url/rest/api/space"
//Use Postman encrypt your username and password for your Confluence service account with admin permissions.
def
basicAuth =
"Basic Ymmdsdsd343sdfmsms=="
defconnection = url.toURL().openConnection()
connection.setRequestProperty(
"Authorization"
, basicAuth)
connection.connect()
//Use the selected space to to find the space key from the rest call return.
def
json =
new
JsonSlurper().parseText(connection.getInputStream().
getText
())
def
spaceKey =
"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.
if
(groupManager.getUsersInGroup(
"space-owners-"
+ json.results[json.results.
findIndexOf
{ it.name == space } ].key.toLowerCase()).
contains
(issue.getReporter()))
{
//Get the selected permissions from the issue and start building our rest call.
def
permissionsField = ComponentAccessor.getCustomFieldManager().getCustomFieldObjectsByName(
"Space Permissions"
)
def
selectedPermissions = issue.getCustomFieldValue(permissionsField[
0
])
def
permissions
if
(selectedPermissions !=
null
)
{
if
(selectedPermissions*.value.
contains
(
"View Space"
))
{
permissions =
"&view=true"
}
else
{
permissions =
"&view=false"
}
if
(selectedPermissions*.value.
contains
(
"Create/Edit Pages"
))
{
permissions = permissions +
"&createEditPage=true"
}
else
{
permissions = permissions +
"&createEditPage=false"
}
if
(selectedPermissions*.value.
contains
(
"Delete Any Page"
))
{
permissions = permissions +
"&removePage=true"
}
else
{
permissions = permissions +
"&removePage=false"
}
if
(selectedPermissions*.value.
contains
(
"Create/Edit Blogs"
))
{
permissions = permissions +"&editBlog=true"
}
else
{
permissions = permissions +
"&editBlog=false"
}
if
(selectedPermissions*.value.
contains
(
"Add Attachments"
))
{
permissions = permissions +
"&createAttachment=true"
}
else
{
permissions = permissions +
"&createAttachment=false"
}
if
(selectedPermissions*.value.
contains
(
"Delete Anyone's Attachments"
))
{
permissions = permissions +
"&removeAttachment=true"
}
else
{
permissions = permissions +
"&removeAttachment=false"
}
if
(selectedPermissions*.value.
contains
(
"Add Comments"
))
{
permissions = permissions +
"&comment=true"
}
else
{
permissions = permissions +
"&comment=false"
}
if
(selectedPermissions*.value.
contains
(
"Modify Page Restrictions"
))
{
permissions = permissions +
"&setPagePermissions=true"
}
else
{
permissions = permissions +
"&setPagePermissions=false"
}
if
(selectedPermissions*.value.
contains
(
"Export Space/Pages"
))
{
permissions = permissions +
"&exportSpace=true"
}
else
{
permissions = permissions +
"&exportSpace=false"
}
if
(selectedPermissions*.value.
contains
(
"Delete Own"
))
{
permissions = permissions +
"&removeOwn=true"
}
else
{
permissions = permissions +
"&removeOwn=false"
}
}
else
{
permissions =
"&view=false&removeOwn=false&createEditPage=false&removePage=false&editBlog=false&createAttachment=false&removeAttachment=false&comment=false&setPagePermissions=false&exportSpace=false"
}
//Get the selected users to apply the permissions to.
def
userField = ComponentAccessor.getCustomFieldManager().getCustomFieldObjectsByName(
"User(s)"
)
def
users = issue.getCustomFieldValue(userField[
0
])
//Loop through the users and make the rest call to the rest end point we created in Confluence.
users.
each
{ user ->
url ="https://base-url/rest/scriptrunner/latest/custom/updatePermissions?"
+ spaceKey +
"&user="
+ user.getUsername() + permissions
connection = url.toURL().openConnection()
connection.setRequestProperty(
"Authorization"
, basicAuth)
connection.connect()
//Log an error in if the return code is not 200.
if
(connection.getResponseCode() !=
200
)
//Shows as error, but works.
{
log.debug(
"Permissions were not set for user "
+ user +
"!"
)
}
}
}
scott_boisvert
Senior Systems Integration Engineer
Sierra Space
Thornton, CO
6 accepted answers
0 comments