2525package com .cloudbees .jenkins .plugins .bitbucket .impl .credentials ;
2626
2727import com .cloudbees .jenkins .plugins .bitbucket .api .BitbucketAuthenticator ;
28+ import com .cloudbees .jenkins .plugins .bitbucket .api .BitbucketException ;
29+ import com .cloudbees .jenkins .plugins .bitbucket .client .Cache ;
2830import com .cloudbees .plugins .credentials .CredentialsScope ;
2931import com .cloudbees .plugins .credentials .common .StandardUsernameCredentials ;
3032import com .cloudbees .plugins .credentials .common .StandardUsernamePasswordCredentials ;
3133import com .cloudbees .plugins .credentials .impl .UsernamePasswordCredentialsImpl ;
3234import com .github .scribejava .core .builder .ServiceBuilder ;
3335import com .github .scribejava .core .httpclient .jdk .JDKHttpClientConfig ;
3436import com .github .scribejava .core .model .OAuth2AccessToken ;
37+ import com .github .scribejava .core .model .OAuth2AccessTokenErrorResponse ;
3538import com .github .scribejava .core .model .OAuthConstants ;
3639import com .github .scribejava .core .oauth .OAuth20Service ;
3740import hudson .model .Descriptor .FormException ;
3841import hudson .util .Secret ;
39- import java .io .IOException ;
4042import java .util .concurrent .ExecutionException ;
43+ import java .util .concurrent .TimeUnit ;
4144import jenkins .util .SetContextClassLoader ;
45+ import org .apache .commons .codec .digest .DigestUtils ;
46+ import org .apache .commons .lang .StringUtils ;
4247import org .apache .http .HttpRequest ;
4348
4449public class BitbucketOAuthAuthenticator implements BitbucketAuthenticator {
50+ private static final Cache <String , OAuth2AccessToken > cacheToken = new Cache <>(5 , TimeUnit .MINUTES );
4551
4652 private final String credentialsId ;
4753 private final String username ;
4854 private final Secret password ;
49- private OAuth2AccessToken token ;
5055
5156 /**
5257 * Constructor.
@@ -60,18 +65,26 @@ public BitbucketOAuthAuthenticator(StandardUsernamePasswordCredentials credentia
6065 }
6166
6267 private OAuth2AccessToken getToken () {
63- if (token == null ) {
64- try (SetContextClassLoader cl = new SetContextClassLoader (this .getClass ());
65- OAuth20Service service = new ServiceBuilder (username )
66- .apiSecret (Secret .toString (password ))
67- .httpClientConfig (JDKHttpClientConfig .defaultConfig ())
68- .build (BitbucketOAuth .instance ())) {
69- token = service .getAccessTokenClientCredentialsGrant ();
70- } catch (IOException | InterruptedException | ExecutionException e ) {
71- throw new RuntimeException (e );
68+ try {
69+ String plainSecret = Secret .toString (password );
70+ String cacheKey = DigestUtils .md2Hex (StringUtils .join (new String [] { credentialsId , username , plainSecret }, '/' ));
71+ return cacheToken .get (cacheKey , () -> {
72+ try (SetContextClassLoader cl = new SetContextClassLoader (this .getClass ());
73+ OAuth20Service service = new ServiceBuilder (username )
74+ .apiSecret (plainSecret )
75+ .httpClientConfig (JDKHttpClientConfig .defaultConfig ())
76+ .build (BitbucketOAuth .instance ())) {
77+ return service .getAccessTokenClientCredentialsGrant ();
78+ }
79+ });
80+ } catch (ExecutionException e ) {
81+ // unwrap exception
82+ Throwable cause = e .getCause ();
83+ if (cause instanceof OAuth2AccessTokenErrorResponse oauthEx ) {
84+ throw new BitbucketException (oauthEx .getErrorDescription () + ". Please check configured OAuth credentials client id and secret are correct." , e );
7285 }
86+ throw new RuntimeException (cause );
7387 }
74- return token ;
7588 }
7689
7790 /**
0 commit comments