Are service desk APIs meant to disallow portal/customer users from accessing them?

Jira Service Desk for Server provides Java APIs that list available service desks and the request types on them. These APIs take an ApplicationUser object and perform a security check to confirm that the user is allowed to access the service desk etc.

As far as I can tell, none of those APIs work for customers — ie portal-only users. The APIs respond with data when accessed by a normal Jira user, but not customers. This doesn’t make sense to me: customers can use the JSD portal website just fine… but the APIs refuse to provide the service desks and request types those same customers see in the portal.

This code demonstrates the problem:

  @GET
  @Path("/sd/{serviceDeskId}/rt/{requestTypeId}")
  public Response getTest(@PathParam("serviceDeskId") int serviceDeskId, @PathParam("requestTypeId") int requestTypeId) {

    ApplicationUser user = jiraAuthenticationContext.getLoggedInUser();
    String userKey = user.getKey();

    log.debug(
      serviceDeskService.getServiceDeskById(user, serviceDeskId).fold(
          (error) -> userKey + " getServiceDeskById error: " + error.getMessage(),
          (serviceDesk) -> userKey + " getServiceDeskById OK: " + serviceDesk.getProjectName()
      )
    );

    log.debug(
      portalService.getPortalForId(user, 1).fold(
          (error) -> userKey + " getPortalForId error: " + error.getMessage(),
          (portal) -> userKey + " getPortalForId OK: " + portal.getName()
      )
    );

    RequestTypeQuery requestTypeQuery = requestTypeService.newQueryBuilder().serviceDesk(serviceDeskId).requestType(requestTypeId).build();
    log.debug(
      requestTypeService.getRequestTypes(user, requestTypeQuery).fold(
          (error) -> userKey + " getRequestTypes error: " + error.getMessage(),
          (requestTypes) -> userKey + " getRequestTypes OK: " + requestTypes.size() + " request types"
      )
    );

    log.debug(
      serviceDeskService.getServiceDesks(user, new SimplePagedRequest(0, 100)).fold(
          (error) -> userKey + " getServiceDesks error: " + error.getMessage(),
          (serviceDesks) -> userKey + " getServiceDesks OK: " + serviceDesks.size() + " service desks"
      )
    );

    return Response.ok("Done").build();
  }

That code should load a service desk, a portal, and a request type, then log the results. It works fine for administrators and Jira agents, resulting in logs like this:

admin getServiceDeskById OK: Test Desk
admin getPortalForId OK: Test Desk
admin getRequestTypes OK: 1 request types
admin getServiceDesks OK: 2 service desks

But for a customer who only has portal access, the logs are:

portal@example.com getServiceDeskById error: sd.agent.servicedesk.error.project.nopermission : 'You don't have permission to access this Service Desk.'
portal@example.com getPortalForId error: sd.portal.error.permission : 'You do not have permission to view this Portal.'
portal@example.com getRequestTypes error: sd.agent.servicedesk.error.project.nopermission : 'You don't have permission to access this Service Desk.'
portal@example.com getServiceDesks OK: 0 service desks

The API is telling us that the customer cannot access that service desk, or indeed any service desks at all.

So my questions are:

  • Is this a bug?
    It seems wrong that APIs refuse permission to customers, because it is all information they can see in the portal. But perhaps it is deliberate? I have seen some bugs raised on this, such as JSDECO-80 and this question, but there has been no indication from Atlassian whether it considered a bug or not.

  • How are we meant to access the APIs for customers?
    Is there some other way we are meant to retrieve this data for customers? We could possibly use the ServiceDeskManager class which doesn’t take a user or perform a security check, but then how do we make sure the customer is authorised to view the service desk? ServiceDeskPermissionService doesn’t have any suitable methods to let us perform that security check ourselves.

Note that I tested this in several versions of Jira Service Desk (3.3.0, 3.5.0, 3.7.0 and 3.9.0) and found they all have the same behaviour.

4 Likes

Hi Charlie,

Permissions with JSD customers are a confusing thing, there is not much in the way of documentation, some actions are just refused by default for customers due to permission checks.

Have you tried executing your code via the CustomerContextService?

I ran into a similar problem yesterday whilst trying to use the IssueService as a portal user, and have seen similar problems when interacting with the JSD API’s. I think the Service Desk Customer - Portal Only permission only applies when executed in the customer context.

Do any Atlassian’s have some tips on how we should be performing actions as JSD Customers? Is the CustomerContextService the correct service to be using?

4 Likes

Thanks @anon19873023, it looks like CustomerContextService is exactly what I need!

I just ran the example code above inside a customerContextService.runInCustomerContext() call and found those API calls now work. The logs for a portal-only user are now:

portal@example.com getServiceDeskById OK: Test Desk
portal@example.com getPortalForId OK: Test Desk
portal@example.com getRequestTypes OK: 1 request types
portal@example.com getServiceDesks OK: 2 service desks

So my guess is that this behaviour isn’t a bug, but we’re probably expected to use CustomerContextService whenever calling an API as a customer?

I am concerned that I’d never heard of this until now. I just searched the Atlassian Developer site and the only mention of CustomerContextService is in the Jira 7.6 upgrade notes… and nothing explaining that we should use CustomerContextService, or when and how to use it. The documentation for Jira Service Desk Server is very limited overall, but this in particular seems like an important omission.

Are there any Atlassians here who can update that documentation?

1 Like

Glad to hear you made some progress Charlie. I too would like to see some confirmation from Atlassian that this is actually the correct approach to use when dealing with portal only users.

As far as I can see this is not documented anywhere to clearly indicate that this is the correct approach to use, I only stumbled across it after browsing the JSD API Javadoc.

Working with JSD is time consuming and frustrating, often things do not work as you would expect them to in any other area of Jira. I think some of the frustration could be alleviated if we actually had access to the JSD source code: JSDSERVER-397

3 Likes

Hi!
I know that there has been some time since this post, but, is there a way you can post the code with interface CustomerContextService that you used?
Thanks!

1 Like

Our company is also currently struggling with permission errors, even when using runInCustomerContext().
I created a request to Atlassian Dev Support but they have not replied in four months…

Code examples would be greatly appreciated!

We write our code in Scala so our exact code is probably not useful to you, however here’s a simple example of how it works in Java. If you take the code from my example above:

    log.debug(
      requestTypeService.getRequestTypes(user, requestTypeQuery).fold(
        (error) -> "getRequestTypes error: " + error.getMessage(),
        (requestTypes) -> "getRequestTypes OK: " + requestTypes.size() + " request types"
      )
    );

Then you need to put that getRequestTypes() call inside a call to runInCustomerContext() like this:

    log.debug(
      customerContextService.runInCustomerContext(() -> {
        requestTypeService.getRequestTypes(user, requestTypeQuery).fold(
            (error) -> "getRequestTypes error: " + error.getMessage(),
            (requestTypes) -> "getRequestTypes OK: " + requestTypes.size() + " request types"
        );
      })
    );

The customerContextService variable is is the instance of com.atlassian.servicedesk.api.customer.CustomerContextService which Jira provides. It can be injected into your class just like other Jira APIs. Just be aware that it only exists for customers with Jira Service Desk installed; you can’t use it when JSD isn’t installed.

2 Likes

Which specific APIs are you trying to access via the customer context? Can you provide code examples? What problems are you encountering?

1 Like

We are trying to list the organizations that a user is a part of via the organizationService in the Java API, we also get the same error using the REST API.

https://docs.atlassian.com/jira-servicedesk/4.5.0/com/atlassian/servicedesk/api/organization/OrganizationService.html

The call only works when the logged in user is a service desk user (agent). If the user is a customer we get a permission error as per the original post in this thread.

According to the documentation, the method should: "Returns a list of organizations in the JIRA instance based on query parameters. If the user is not an agent, the resource returns a list of organizations the user is a member of. "

Code example (Groovy via ScriptRunner, result is the same):

@WithPlugin("com.atlassian.servicedesk")

@PluginModule
ServiceDeskManager serviceDeskManager

@PluginModule
OrganizationService organizationService

@PluginModule
CustomerContextService customerContextService

MutableIssue issue = issue

def currentUser 	   = ComponentAccessor.getJiraAuthenticationContext().getLoggedInUser()
def serviceDeskProject = serviceDeskManager.getServiceDeskForProject(issue.projectObject)
def serviceDeskId      = serviceDeskProject?.getId() as Integer
def resultString = ""

// get organizations
customerContextService.runInCustomerContext({
    def organizationsQuery = organizationService.newOrganizationsQueryBuilder().serviceDeskId(serviceDeskId).build()
	organizationService.getOrganizations(currentUser, organizationsQuery).results.each({ org ->
        resultString = resultString + org.getName()
    })
})

EDIT: Extended Code Example

1 Like

Thank you very much! But we can’t use lambda expressions. An issue to be resolved.

However, your code (and the post in general!) did point me in the right dirrection. This is the code that finally worked for us.

CustomerContextService customerContextService = ComponentAccessor.getOSGiComponentInstanceOfType(CustomerContextService.class);
Either<AnError, Approval> result = customerContextService.runInCustomerContext(new NoExceptionsCallable<Either<AnError, Approval>>(){
	@Override
	public Either<AnError, Approval> call() {
		Either<AnError, Approval> result1 = approvalService.answerApproval(approver,unansweredApproval,ApprovalDecisionType.APPROVED);
		return result1;
	}
});

Just an explanation what is the purpose of the code - approval did not work if the user didn’t have browse permission on Jira Service desk project even though the person could approve the issue on Customer portal manually.

Cheers,
Marina

P.S. Wouldn’t mind for some tips if this code could be improved.

1 Like

Hi! Do you mean “result is the same” - i.e. it still doesn’t work? Did you manage to get this info without adding additional permissions to users? I try the same and it doesn’t work for me. Thanks

1 Like

I have the same problem as OscarSagemo and EugeneTs

the OrganizationService.getOrganizations() method does not seem to care about customerContext, I get sd.agent.servicedesk.error.project.nopermission in the 3 cases:

running without customerContextService = sd.agent.servicedesk.error.project.nopermission
running wtih runInCustomerContext = sd.agent.servicedesk.error.project.nopermission
running with runOutOfCustomerContext = sd.agent.servicedesk.error.project.nopermission

It’s very frustrating to say the least.

I figured out what caused the OrganizationService to give a nopermission error, even when using the CustomerContextService.

Apparently if you supply the OrganizationsQuery with a ServiceDeskId parameter, you get a permission error, regardless of using the customercontextservice or not.

So this causes a permission error:

OrganizationsQuery organizationsQuery = organizationServiceBridge
                    .newOrganizationsQueryBuilder()
                    .serviceDeskId(currentServiceDesk)
                    .build();

But this doesn’t cause any permission error:

OrganizationsQuery organizationsQuery = organizationServiceBridge
                    .newOrganizationsQueryBuilder()
                    //.serviceDeskId(currentServiceDesk)
                    .build();

Sadly my whole reason for using the organization service was to partially find out if a user was a customer on a specific service desk (Rather than for a specific issue or request, which the permissionHelper can be used for).

I ended up having to use the

ServiceDeskService.getServiceDesks(ApplicationUser user, PageRequest pagerequest)

to compare the service desk i was on, with all the available service desks for the user.