New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Use AuthenticationTokens #101
Use AuthenticationTokens #101
Conversation
d337625
to
e4130d2
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A good start
try { | ||
builder.setSSLContext(buildSSLContext()); | ||
} catch (NoSuchAlgorithmException | UnrecoverableKeyException | KeyStoreException | KeyManagementException ignored) { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we'll need to either pass a TaskListener
to these methods or log the exceptions. Certainly unacceptable to ignore and continue.
May even need to wrap as an IOException
and throw
import org.apache.http.client.protocol.HttpClientContext; | ||
import org.apache.http.impl.client.HttpClientBuilder; | ||
|
||
public abstract class BitbucketAuthenticator<T extends Credentials> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On the plus side, you are implementing a strategy
|
||
public abstract class BitbucketAuthenticator<T extends Credentials> { | ||
|
||
public final T credentials; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On the minus side you are basically wrapping a Credentials... please don't do that, extract what you need from the Credential and move on, that way the BitbucketAuthenticator
can be used from remote agents rather than only from the master JVM
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes that means you need a subclass for each type of credential that is supported... but you needed that anyway. Also... just say no to public fields
import static com.cloudbees.jenkins.plugins.bitbucket.Utils.encodePath; | ||
|
||
public class BitbucketCloudApiClient implements BitbucketApi { | ||
private static final Logger LOGGER = Logger.getLogger(BitbucketCloudApiClient.class.getName()); | ||
private static final HttpHost API_HOST = HttpHost.create("https://api.bitbucket.org"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
src/main/java/com/cloudbees/jenkins/plugins/bitbucket/endpoints/AbstractBitbucketEndpoint.java
Show resolved
Hide resolved
URIRequirementBuilder.fromUri(serverUrl).build(), | ||
CredentialsMatchers.instanceOf(StandardUsernamePasswordCredentials.class) | ||
CredentialsMatchers.anyOf( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, you should be using AuthenticationTokens.matcher(BitbucketAuthenticator.class)
here. No need to test for instanceOf
any more. In fact this switch is the key reason to move to AuthenticationTokens... as it means an extension plugin can register more authenticator sources and then the support wires right through
import org.kohsuke.stapler.DataBoundConstructor; | ||
|
||
import javax.annotation.Nonnull; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is no published JSR 305. Please use edu.umd.cs.findbugs.annotations.NonNull
import org.apache.http.message.BasicNameValuePair; | ||
import org.apache.http.util.EntityUtils; | ||
import org.codehaus.jackson.type.TypeReference; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
all imports in one section sorted alphabetically: https://github.com/jenkinsci/scm-api-plugin/blob/master/CONTRIBUTING.md details the code style
import edu.umd.cs.findbugs.annotations.CheckForNull; | ||
import edu.umd.cs.findbugs.annotations.NonNull; | ||
import edu.umd.cs.findbugs.annotations.Nullable; | ||
import hudson.Extension; | ||
import hudson.ExtensionList; | ||
import org.apache.commons.lang.StringUtils; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Check your IDE settings for import sorting... it's all of them not just the two I have tagged
5d4a3ff
to
93b56e2
Compare
@vault this should now be in a good state to rebase 👍 |
URIRequirementBuilder.fromUri(bitbucketServerUrl).build(), | ||
CredentialsMatchers.anyOf(CredentialsMatchers.instanceOf(StandardUsernamePasswordCredentials.class)) | ||
CredentialsMatchers.anyOf( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AuthenticationTokens.matcher(BitbucketCredentials.class)
should suffice instead of the credentials matchers
NOTE: you will need to use an AuthenticationTokenContext
to handle the different types of auth for http
vs https
urls, so at that point you will change to AuthenticationTokens.matcher(BitbucketCredentials.class, authContext)
NOTE2: This comment applies to everywhere you are using CredentialsMatchers.instanceOf
CredentialsProvider.lookupCredentials(StandardCertificateCredentials.class, | ||
context, | ||
context instanceof Queue.Task ? Tasks.getDefaultAuthenticationOf((Queue.Task) context) : ACL.SYSTEM, | ||
URIRequirementBuilder.fromUri(bitbucketServerUrl).build()), CredentialsMatchers.withId(value)) != null) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You will need both withId
and AuthenticationTokens.matcher(BitbucketCredentials.class)
to handle the case where there are two different types of credentials with the same id at different layers in the folder hierarchy.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The point being that the drop-down list will be showing only those that AuthenticationTokens.matcher(BitbucketCredentials.class)
so may hide higher up ones that happen to have the same id but don't match the type we want...
say at the root there is a userpass with id foo
and one folder down there is a certificate with id foo
... if the url is http
we should be able to select foo
and mean the userpass...
and such a config could actually be desired, e.g. against https
we want to use the stronger client cert... against http
we have no choice... and we set up everything to always have id of foo
so that the choice is easy... not a config I'd recommend... but it is logical... and i think credentials-api would support it
8c4a90c
to
16859cb
Compare
I think I've addressed most of the requests here. The main omissions being I'm not sure what else to do in the client-cert exception case other than log something. What other changes do you think this needs? Any thoughts on how to handle that case correctly? I looked into |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, though a unit test for testing the convert different credential would be nice.
src/main/java/com/cloudbees/jenkins/plugins/bitbucket/api/BitbucketAuthenticator.java
Show resolved
Hide resolved
*/ | ||
public void configureBuilder(HttpClientBuilder builder) { } | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👻 whitespace
*/ | ||
public void configureContext(HttpClientContext context, HttpHost host) { } | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👻 whitespace
*/ | ||
public void configureRequest(HttpRequest request) { } | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👻 whitespace
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking good... I may use this PR as an example of how to do things... once it's ready
Some changes / improvements I have suggested
I have not had time to give a full review today
/** | ||
* The URL protocol as reported in an {@link AuthenticationTokenContext} | ||
*/ | ||
public static final String PROTOCOL_PURPOSE = "PROTOCOL"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would call this Scheme
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For the constant name, or its string value? Also, <blah>_PURPOSE
reads kind of weird. Is there a naming scheme that would be more preferred than that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I’d use SCHEME = “scheme”
or SERVER_URI_SCHEME = “bitbucket.server.uri.scheme”
The length of the constant doesn’t use up more space as it will still be an 8 byte reference to the constant pool anyway, so more descriptive is better
/** | ||
* The purpose value for HTTP instances | ||
*/ | ||
public static final String PROTOCOL_HTTP = "HTTP"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just use the URI scheme rather than a constant (i.e. parse the URL, or just substring up to the first :
and lowercase the scheme part)... if you want to have constants for HTTP and HTTPS their values should be the same as an URL (i.e. lowercase)
/** | ||
* The Bitbucket instance type as reported in an {@link AuthenticationTokenContext} | ||
*/ | ||
public static final String INSTANCE_TYPE_PURPOSE = "INSTANCE_TYPE"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Make it clear that this is bitbucket, a better value would be Bitbucket.Type
* @param serverUrl The URL being authenticated against | ||
* @return an {@link AuthenticationTokenContext} for use with the AuthenticationTokens APIs | ||
*/ | ||
public static AuthenticationTokenContext<BitbucketAuthenticator> authenticationContext(String serverUrl) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice
boolean isCloud = serverUrl == null || serverUrl.equals(BitbucketCloudEndpoint.SERVER_URL); | ||
|
||
return AuthenticationTokenContext.builder(BitbucketAuthenticator.class) | ||
.with(PROTOCOL_PURPOSE, isHttps ? PROTOCOL_HTTPS : PROTOCOL_HTTP) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you could be perfectly fine not to parse the URL and just put the URL in the token context as url
and be done... let the impls decide if the url is cloud or server...
fine to do it here, but I would recommend adding the URL to the token context also in that case, as you don't know what will need it further down the road
(for example a credential may only be valid against a specific server)
[JENKINS-44784] Ensure workspace cleanup for deleted branch projects proceeds asynchronously, and will time out if the agent is stuck
I'm not sure what @stephenc 's philosophy has been on this, but there are signature changes in a couple of unrestricted public classes and extension points (constructor changes, methods removed etc.) |
I should be able to fix that. I'll try to annotate changes that are problematic and try to get them fixed in the next day or so. |
src/main/java/com/cloudbees/jenkins/plugins/bitbucket/api/BitbucketApiFactory.java
Show resolved
Hide resolved
src/main/java/com/cloudbees/jenkins/plugins/bitbucket/api/BitbucketApiFactory.java
Show resolved
Hide resolved
src/main/java/com/cloudbees/jenkins/plugins/bitbucket/client/BitbucketCloudApiClient.java
Outdated
Show resolved
Hide resolved
src/main/java/com/cloudbees/jenkins/plugins/bitbucket/client/BitbucketCloudApiClient.java
Show resolved
Hide resolved
...ain/java/com/cloudbees/jenkins/plugins/bitbucket/server/client/BitbucketServerAPIClient.java
Show resolved
Hide resolved
src/main/java/com/cloudbees/jenkins/plugins/bitbucket/BitbucketSCMNavigator.java
Show resolved
Hide resolved
a9997a4
to
bfcbbe0
Compare
Rebased and added compatibility methods for what I think are all the important places this branch wasn't backwards compatible. Let me know if it seems like there's something I missed. |
Apart from username/password and client certificates, does this PR also supports using a OAuth token to connect to the REST API of Bitbucket? |
This PR doesn't add support for OAuth tokens, but it should make adding that support easier. |
Is the only thing blocking this @stephenc 's review of 4th January? |
When I moved projects, bobby was primarily assigned responsibilities for this plugin. If there are significant api changes, then he’s supposed to call me in, but I haven’t seen same. (Also I don’t know if bobby’s responsibilities have been reassigned since) When I looked at this pr last it seemed headed in the right direction, but I haven’t had the time to re-review and I haven’t been called in by bobby, so my current understanding is bobby needs to review... though that may be wrong |
Are there plans to merge this? As I see it, currently it is not possible to use this plugin with Bitbucket Cloud. |
@vault please rebase 😇 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM except for the recent unresolved conflict 😇
fd46cae
to
0ebfc16
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
...dbees/jenkins/plugins/bitbucket/api/credentials/BitbucketClientCertificateAuthenticator.java
Show resolved
Hide resolved
src/main/java/com/cloudbees/jenkins/plugins/bitbucket/api/BitbucketAuthenticator.java
Show resolved
Hide resolved
...udbees/jenkins/plugins/bitbucket/api/credentials/BitbucketUsernamePasswordAuthenticator.java
Show resolved
Hide resolved
...jenkins/plugins/bitbucket/api/credentials/BitbucketClientCertificateAuthenticatorSource.java
Show resolved
Hide resolved
.../jenkins/plugins/bitbucket/api/credentials/BitbucketUsernamePasswordAuthenticatorSource.java
Show resolved
Hide resolved
Could you please rebase 😇 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From an API and direction Point Of View: This looks great.
Assuming you:
- rebase
- fix the files missing license headers
- address other code review comments
You get my vote
cacheKey.append(owner); | ||
|
||
if (authenticator != null) { | ||
cacheKey.append("::").append(authenticator.getId()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So.... the change from username to credentials id may mean that we lose the cache where there are two credentials instances with the same username... OTOH that might have been a way to capture information if we had been caching more than repositories... so likely not a bad thing... and this cache is internal, so if needs be could be refactored to a different key later.
Just to cover @hoegertn comment about being unable to use for Bitbucket Cloud is not true. |
* Introduces a BitbucketAuthenticator class that is responsible for configuring the http client used to access the bitbucket rest API. * Adds 2 concrete subclasses of BitbucketAuthenticator capable of authentication with normal username/password and client certificates * Uses the authentication-tokens plugin to register these authentication methods and acquire the correct instance type for the supplied credentials. * Adds a message instructiong the user to configure SSH checkout if a certificate credentials have been selected.
* Don't just wrap Credentials in BitbucketAuthenticators * Use AuthenticationTokens.matcher * Log a message in exception handler
0ebfc16
to
fa8ec07
Compare
Rebased and added license headers which I think addresses all outstanding issues. |
Opening for initial feedback.
This is based on #92 and moves to using the AuthenticationTokens plugin as was suggested in a couple of other pull-requests (#69, #68, and #94). This also includes support for client certificates like #94.