Remove Public Access From All Jira Filters – An Exploration of the Jira Search Service

A long long time ago, I posted a complaint to this blog about how I had no idea what a Jira SearchContext was, and how it was very frustrating to try to figure it out.

Yesterday I realized that I had never really made any strides toward fixing my lack of knowledge around the search functionality of Jira.  I’m not talking JQL, I’m talking the actual Search Service waayyy down in the guts of Jira.

I set out wanting to update the permissions for a list of filters on Jira Server, based on the name of the server.  The list of filter names came from a migration we were doing from Jira Server to Jira Cloud. JCMA returned a list of the names of forty filters that were publicly accessible.  My task was to go in and update those permissions.  As I was going to have to repeat this process multiple times, it seemed an excellent candidate for a scripted solution.

As it turns out, it is extremely difficult to search for filters by name.   You can search for filters by their ID without issue, but searching by name is essentially not possible.

My next idea was to simply wipe public access from ALL filters on the system.  I figured I’d be able to call some method like “getFilters()” and simply have a list of all the filters.  Nope!  You need to use the search service, and that’s where my current knowledge ran out, and I had to learn something new.

The work below is based on a script from the Adaptavist library, linked here.   It was immensely helpful in getting started.  Most of the comments in the script below are mine, simply by way of my trying to explain to myself what was going on at each step.   I believe that this script is a good stepping stone toward learning how to use the search to return other types of items; the service is not limited to searching for types of shares.

The actual script simply searches for globally shared items, and updates the permissions on any of those items so that members of jira-users can access the shared item.  In this case the update is limited to changing the permissions of filters.

import com.atlassian.jira.bc.JiraServiceContextImpl
import com.atlassian.jira.bc.filter.SearchRequestService
import com.atlassian.jira.bc.portal.PortalPageService
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.portal.PortalPage
import com.atlassian.jira.sharing.SharePermissionImpl
import com.atlassian.jira.sharing.SharedEntity
import com.atlassian.jira.sharing.search.GlobalShareTypeSearchParameter
import com.atlassian.jira.sharing.search.SharedEntitySearchParametersBuilder
import com.atlassian.jira.sharing.type.ShareType
import com.atlassian.sal.api.ApplicationProperties
import com.atlassian.sal.api.UrlMode
import com.onresolve.scriptrunner.runner.ScriptRunnerImpl

def searchRequestService = ComponentAccessor.getComponent(SearchRequestService)
def currentUser = ComponentAccessor.jiraAuthenticationContext.loggedInUser
def applicationProperties = ScriptRunnerImpl.getOsgiService(ApplicationProperties)
def portalPageService = ComponentAccessor.getComponent(PortalPageService)
def globalPermissionManager = ComponentAccessor.globalPermissionManager

def contextPath = applicationProperties.getBaseUrl(UrlMode.RELATIVE)

def serviceContext = new JiraServiceContextImpl(currentUser)
//Define a new service context. Essentially, tell Jira that we want to define a search, and who it should run as.

def searchParameters = new SharedEntitySearchParametersBuilder().setShareTypeParameter(GlobalShareTypeSearchParameter.GLOBAL_PARAMETER).toSearchParameters()
//We're defining a series of parameters that will return items with the matching share permissions
//AuthenticatedUserShareTypeSearchParameter, GlobalShareTypeSearchParameter, GroupShareTypeSearchParameter, PrivateShareTypeSearchParameter, ProjectShareTypeSearchParameter

searchRequestService.validateForSearch(serviceContext, searchParameters)
//Validate the search before performing it

assert!serviceContext.errorCollection.hasAnyErrors()
//Confirm that the validation returned no errors

def searchFilterResult = searchRequestService.search(serviceContext, searchParameters, 0, Integer.MAX_VALUE)
//Actually perform the search, and save the results in an object
//The four parameters are the serviceContext (already established), the search parameters (already established), page position, and page width
//In other words, the last two integers are for pagination

final authenticatedUserSharePerms = new SharedEntity.SharePermissions([new SharePermissionImpl(null, ShareType.Name.GROUP, "jira-users", null)] as Set)
//SharedEntity is a class for any entity that can be shared or favorited
//https://docs.atlassian.com/software/jira/docs/api/7.2.0/com/atlassian/jira/sharing/SharedEntity.html 

//We're definine a new set of share permissions:
//https://docs.atlassian.com/software/jira/docs/api/7.6.1/com/atlassian/jira/sharing/SharePermissionImpl.html

searchFilterResult.results.each {
  filter ->

    filter.setPermissions(authenticatedUserSharePerms)
  //Set the new permissions on the filter

  def filterUpdateContext = new JiraServiceContextImpl(currentUser)
  //Define the context in which this update will be performed
  //If we try to run the update as the filter.owner, it doesn't work with filters that are exposed to anyone on the web

  searchRequestService.updateFilter(filterUpdateContext, filter)
  //Actually perform the update, using the context and running against the filter
  log.warn("Updating permissions for filter: ${filter.name}")

  if (filterUpdateContext.errorCollection.hasAnyErrors()) {
    log.warn("Error updating filter - possibly owner has been deleted. Just delete the filter. " + filterUpdateContext.errorCollection)
  }
}

Leave a Reply

Your email address will not be published. Required fields are marked *