jira-plugin icon indicating copy to clipboard operation
jira-plugin copied to clipboard

Log an appropriate AccessDenied message instead of throwing a RestClientException stacktrace

Open rantoniuk opened this issue 6 months ago • 2 comments

When the plugin finds a pattern that matches the configured patterns, it tries to access the issue status from the Jira instance. If it does not have the required permissions, it throws an ugly stacktrace instead of just stating in the build log that it doesn't have access to that issue.

This might be the beginning (or part of) a larger re-factoring of logging so that:

  • Rest exceptions are not thrown back (at least if DEBUG is not enabled) and instead a user friendly message is logged
  • that the log message goes to the build log and not (only) the Jenkins log, so that it's visible to the actual user accessing the build logs via Jenkins UI.
Jira REST client get issue error. cause: RestClientException{statusCode=Optional.of(403), errorCollections=[ErrorCollection{status=403, errors={}, errorMessages=[You do not have the permission to see the specified issue.]}]}
RestClientException{statusCode=Optional.of(403), errorCollections=[ErrorCollection{status=403, errors={}, errorMessages=[You do not have the permission to see the specified issue.]}]}
	at PluginClassLoader for jira//com.atlassian.jira.rest.client.internal.async.AbstractAsynchronousRestClient$2.apply(AbstractAsynchronousRestClient.java:189)
	at PluginClassLoader for jira//com.atlassian.jira.rest.client.internal.async.AbstractAsynchronousRestClient$2.apply(AbstractAsynchronousRestClient.java:183)
	at PluginClassLoader for jira//com.atlassian.httpclient.api.ResponsePromiseMapFunction.apply(ResponsePromiseMapFunction.java:49)
	at PluginClassLoader for jira//com.atlassian.httpclient.api.ResponsePromiseMapFunction.apply(ResponsePromiseMapFunction.java:10)
	at PluginClassLoader for jira//io.atlassian.util.concurrent.Promises$OfStage.lambda$fold$4(Promises.java:332)
	at PluginClassLoader for jira//io.atlassian.util.concurrent.Promises.lambda$biFunction$7(Promises.java:422)
	at java.base/java.util.concurrent.CompletableFuture.uniHandle(CompletableFuture.java:934)
	at java.base/java.util.concurrent.CompletableFuture$UniHandle.tryFire(CompletableFuture.java:911)
	at java.base/java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:510)
	at java.base/java.util.concurrent.CompletableFuture.postFire(CompletableFuture.java:614)
	at java.base/java.util.concurrent.CompletableFuture$UniHandle.tryFire(CompletableFuture.java:914)
	at java.base/java.util.concurrent.CompletableFuture$Completion.run(CompletableFuture.java:482)
	at PluginClassLoader for jira//com.atlassian.httpclient.apache.httpcomponents.CompletableFuturePromiseHttpPromiseAsyncClient.runInContext(CompletableFuturePromiseHttpPromiseAsyncClient.java:87)
	at PluginClassLoader for jira//com.atlassian.httpclient.apache.httpcomponents.CompletableFuturePromiseHttpPromiseAsyncClient$ThreadLocalDelegateRunnable.run(CompletableFuturePromiseHttpPromiseAsyncClient.java:158)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
	at java.base/java.lang.Thread.run(Thread.java:840)
Caused: java.util.concurrent.ExecutionException
	at java.base/java.util.concurrent.CompletableFuture.reportGet(CompletableFuture.java:396)
	at java.base/java.util.concurrent.CompletableFuture.get(CompletableFuture.java:2096)
	at PluginClassLoader for jira//io.atlassian.util.concurrent.Promises$OfStage.get(Promises.java:357)
	at PluginClassLoader for jira//com.atlassian.jira.rest.client.internal.async.DelegatingPromise.get(DelegatingPromise.java:106)
	at PluginClassLoader for jira//hudson.plugins.jira.JiraRestService.getIssue(JiraRestService.java:170)
	at PluginClassLoader for jira//hudson.plugins.jira.JiraSession.getIssue(JiraSession.java:122)
	at PluginClassLoader for jira//hudson.plugins.jira.JiraSite.lambda$getIssue$0(JiraSite.java:1115)
	at PluginClassLoader for caffeine-api//com.github.benmanes.caffeine.cache.BoundedLocalCache.lambda$doComputeIfAbsent$14(BoundedLocalCache.java:2704)
	at java.base/java.util.concurrent.ConcurrentHashMap.compute(ConcurrentHashMap.java:1916)
	at PluginClassLoader for caffeine-api//com.github.benmanes.caffeine.cache.BoundedLocalCache.doComputeIfAbsent(BoundedLocalCache.java:2702)
	at PluginClassLoader for caffeine-api//com.github.benmanes.caffeine.cache.BoundedLocalCache.computeIfAbsent(BoundedLocalCache.java:2684)
	at PluginClassLoader for caffeine-api//com.github.benmanes.caffeine.cache.LocalCache.computeIfAbsent(LocalCache.java:112)
	at PluginClassLoader for caffeine-api//com.github.benmanes.caffeine.cache.LocalManualCache.get(LocalManualCache.java:63)
	at PluginClassLoader for jira//hudson.plugins.jira.JiraSite.getIssue(JiraSite.java:1111)
	at PluginClassLoader for jira//hudson.plugins.jira.JiraJobAction.setAction(JiraJobAction.java:98)
	at PluginClassLoader for jira//hudson.plugins.jira.JiraJobAction$RunListenerImpl.onStarted(JiraJobAction.java:129)
	at PluginClassLoader for jira//hudson.plugins.jira.JiraJobAction$RunListenerImpl.onStarted(JiraJobAction.java:121)
	at hudson.model.listeners.RunListener.lambda$fireStarted$2(RunListener.java:246)
	at jenkins.util.Listeners.lambda$notify$0(Listeners.java:59)
	at jenkins.util.Listeners.notify(Listeners.java:67)
	at hudson.model.listeners.RunListener.fireStarted(RunListener.java:244)
	at PluginClassLoader for workflow-job//org.jenkinsci.plugins.workflow.job.WorkflowRun.run(WorkflowRun.java:308)
	at hudson.model.ResourceController.execute(ResourceController.java:101)
	at hudson.model.Executor.run(Executor.java:446)

Originally posted by @Maxo112 in #657

rantoniuk avatar May 30 '25 19:05 rantoniuk

@rantoniuk I think this is doable. In the first shot I can just solve only what is in the scope of this issue. That would be to log the error from this specific method: JiraRestService.getIssue in build log and try to have some kind of general approach that can be later on applied everywhere. Right now I am not sure what all changes will be required, but it looks like we need a mechanism to use TaskListener wherever there is a need to log error in build log.

If this works then as a separate bigger enhancement, I can apply it in other places too.

Edit 1: Another point, scoped tokens work different from simple API tokens. During my testing when scoped token was not giving any response, I stumbled upon this page: https://community.atlassian.com/forums/Confluence-questions/Scoped-API-Token-returns-Unauthorized-for-simple-query/qaq-p/3023662

and from there to this one: https://developer.atlassian.com/cloud/confluence/oauth-2-3lo-apps/#siteaccess

Which says, scoped token in jira cloud have different URL and need cloudid. So may be first there need to be a support for scoped token?

Hardikrathod01 avatar Jun 11 '25 11:06 Hardikrathod01

For this issue, I'd like it to be a complete approach of some sort - I mean a solution that would be applied to all methods of JiraRestService.java.

In the scope of that issue is another thing that isn't filed anywhere yet but I remember about it - there is a Validate button on the Jira configuration panel but that Validation is too simple. It happened many times during my testing that it returned OK but it didn't work for some reason (haven't had time back then to file an issue what exactly was the reason unfortunately).

Try to come up with an idea and test it with Jira Cloud - think about how this is going to help the end user find the root cause of the problem. When you're satisfied, open a PR and I'll take it for a spin.

So may be first there need to be a support for scoped token?

That should be a separate issue that we can work on if actually anyone needs it (votes for it).

rantoniuk avatar Jun 12 '25 18:06 rantoniuk

Any Update here ?

Maxo112 avatar Oct 14 '25 07:10 Maxo112

Hi, I would like to work on this issue as my first Jenkins contribution.
Could you please assign it to me?

viru0909-dev avatar Nov 25 '25 17:11 viru0909-dev