|
87 | 87 | import jenkins.scm.api.SCMFile;
|
88 | 88 | import org.apache.commons.io.IOUtils;
|
89 | 89 | import org.apache.commons.lang.StringUtils;
|
| 90 | +import org.apache.http.Header; |
90 | 91 | import org.apache.http.HttpHost;
|
91 | 92 | import org.apache.http.HttpStatus;
|
| 93 | +import org.apache.http.client.methods.CloseableHttpResponse; |
92 | 94 | import org.apache.http.client.methods.HttpGet;
|
93 | 95 | import org.apache.http.conn.HttpClientConnectionManager;
|
94 | 96 | import org.apache.http.impl.client.CloseableHttpClient;
|
@@ -1051,4 +1053,49 @@ private Map<String,Object> collectLines(String response, final List<String> line
|
1051 | 1053 | return content;
|
1052 | 1054 | }
|
1053 | 1055 |
|
| 1056 | + @Override |
| 1057 | + protected BitbucketRequestException buildResponseException(CloseableHttpResponse response, String errorMessage) { |
| 1058 | + // If the HTTP request failed because of an authorization |
| 1059 | + // problem, then make the exception message also show the |
| 1060 | + // Bitbucket user name with which Jenkins authenticated, |
| 1061 | + // the project name, and the repository name. |
| 1062 | + // |
| 1063 | + // Such an authorization problem can occur especially in a |
| 1064 | + // pull request from a personal fork: if Jenkins has been |
| 1065 | + // granted READ access on the target repository of the PR but |
| 1066 | + // not on the fork, then it can read the PR information from |
| 1067 | + // the target repository and check out the files, but cannot |
| 1068 | + // post a build status to the fork. |
| 1069 | + // |
| 1070 | + // If the HTTP request already includes valid credentials, |
| 1071 | + // but the Bitbucket user has not been granted access on the |
| 1072 | + // repository, then Bitbucket Server responds with HTTP status |
| 1073 | + // 401 (Unauthorized) and a WWW-Authenticate header field that |
| 1074 | + // requests OAuth, even though RFC 7235 section 2.1 recommends |
| 1075 | + // 403 (Forbidden). Let's recognize both 401 and 403. |
| 1076 | + int httpStatus = response.getStatusLine().getStatusCode(); |
| 1077 | + if (httpStatus == HttpStatus.SC_UNAUTHORIZED || httpStatus == HttpStatus.SC_FORBIDDEN) { |
| 1078 | + Header userNameHeader = response.getFirstHeader("X-AUSERNAME"); |
| 1079 | + if (userNameHeader != null |
| 1080 | + && !userNameHeader.getValue().equals("anonymous")) { |
| 1081 | + String headers = StringUtils.join(response.getAllHeaders(), "\n"); |
| 1082 | + |
| 1083 | + // The message says "sufficient access" because it is |
| 1084 | + // too difficult for this method to know which level |
| 1085 | + // of access is actually needed. |
| 1086 | + // Posting a build status requires READ access, but |
| 1087 | + // deleting a build status requires ADMIN access. |
| 1088 | + String message = String.format("HTTP request error.%nPlease verify that the Bitbucket user \"%s\" is granted sufficient access on the repository \"%s/%s\".%nStatus: %s%nResponse: %s%n%s", |
| 1089 | + userNameHeader.getValue(), |
| 1090 | + getUserCentricOwner(), |
| 1091 | + getRepositoryName(), |
| 1092 | + response.getStatusLine(), |
| 1093 | + errorMessage, |
| 1094 | + headers); |
| 1095 | + return new BitbucketRequestException(httpStatus, message); |
| 1096 | + } |
| 1097 | + } |
| 1098 | + |
| 1099 | + return super.buildResponseException(response, errorMessage); |
| 1100 | + } |
1054 | 1101 | }
|
0 commit comments