Skip to content
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

Merged
merged 14 commits into from Oct 31, 2018
Merged

Conversation

vault
Copy link
Contributor

@vault vault commented Jan 3, 2018

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.

@jetersen
Copy link
Member

jetersen commented Jan 3, 2018

@vault think you need to rebase onto PR #92 again and resolve conflicts

@vault vault force-pushed the authentication-token-plugin branch from d337625 to e4130d2 Compare January 3, 2018 16:31
Copy link
Member

@stephenc stephenc left a 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) {

Copy link
Member

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> {
Copy link
Member

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;
Copy link
Member

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

Copy link
Member

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");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I strongly recommend waiting until after #80 is merged (or else rebasing this PR on top of #80)

URIRequirementBuilder.fromUri(serverUrl).build(),
CredentialsMatchers.instanceOf(StandardUsernamePasswordCredentials.class)
CredentialsMatchers.anyOf(
Copy link
Member

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;
Copy link
Member

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;

Copy link
Member

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;

Copy link
Member

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

@vault vault force-pushed the authentication-token-plugin branch 2 times, most recently from 5d4a3ff to 93b56e2 Compare January 9, 2018 16:17
@jetersen
Copy link
Member

@vault this should now be in a good state to rebase 👍

URIRequirementBuilder.fromUri(bitbucketServerUrl).build(),
CredentialsMatchers.anyOf(CredentialsMatchers.instanceOf(StandardUsernamePasswordCredentials.class))
CredentialsMatchers.anyOf(
Copy link
Member

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) {
Copy link
Member

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.

Copy link
Member

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

@vault vault force-pushed the authentication-token-plugin branch 2 times, most recently from 8c4a90c to 16859cb Compare January 26, 2018 02:40
@vault
Copy link
Contributor Author

vault commented Jan 26, 2018

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 TaskListener a bit but I wasn't able to figure out the right way to use it here.

Copy link
Member

@jetersen jetersen left a 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.

*/
public void configureBuilder(HttpClientBuilder builder) { }


Copy link
Member

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) { }


Copy link
Member

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) { }


Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👻 whitespace

Copy link
Member

@stephenc stephenc left a 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";
Copy link
Member

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

Copy link
Contributor Author

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?

Copy link
Member

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";
Copy link
Member

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";
Copy link
Member

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) {
Copy link
Member

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)
Copy link
Member

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)

fengxx pushed a commit to fengxx/bitbucket-branch-source-plugin that referenced this pull request Mar 13, 2018
[JENKINS-44784] Ensure workspace cleanup for deleted branch projects proceeds asynchronously, and will time out if the agent is stuck
@rsandell
Copy link
Member

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.)
In general we make an effort to keep old signatures (but deprecated) and make them work with the new code.

@vault
Copy link
Contributor Author

vault commented May 11, 2018

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.

@vault vault force-pushed the authentication-token-plugin branch from a9997a4 to bfcbbe0 Compare May 15, 2018 15:34
@vault
Copy link
Contributor Author

vault commented May 15, 2018

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.

@michelzanini
Copy link
Contributor

Apart from username/password and client certificates, does this PR also supports using a OAuth token to connect to the REST API of Bitbucket?

@vault
Copy link
Contributor Author

vault commented Jul 9, 2018

This PR doesn't add support for OAuth tokens, but it should make adding that support easier.

@andrewspiers
Copy link

Is the only thing blocking this @stephenc 's review of 4th January?

@stephenc
Copy link
Member

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

@hoegertn
Copy link

Are there plans to merge this? As I see it, currently it is not possible to use this plugin with Bitbucket Cloud.

@jetersen
Copy link
Member

@vault please rebase 😇

Copy link
Member

@jetersen jetersen left a 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 😇

Copy link
Member

@jetersen jetersen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM I would like @stephenc to do another review pass if you have the time to spare 😅
There is gonna be a merge conflict with #138, but will manage

@jetersen
Copy link
Member

Could you please rebase 😇

Copy link
Member

@stephenc stephenc left a 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());
Copy link
Member

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.

@jetersen
Copy link
Member

Just to cover @hoegertn comment about being unable to use for Bitbucket Cloud is not true.
Create an atlassian user account using username & password credentials with username being the email of the user seems to be working just fine.

* 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
@vault vault force-pushed the authentication-token-plugin branch from 0ebfc16 to fa8ec07 Compare October 31, 2018 19:36
@vault
Copy link
Contributor Author

vault commented Oct 31, 2018

Rebased and added license headers which I think addresses all outstanding issues.

@jetersen jetersen merged commit 85bc804 into jenkinsci:master Oct 31, 2018
@kurddt kurddt mentioned this pull request Dec 4, 2019
6 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

7 participants