Authenticated access to bitbucket URLs from inside bitbucket plugin/URL component extraction

Hi,

I’m trying to access an authenticated Bitbucket Server URL from within a Bitbucket server hook/servlet. I have it working but I feel like I’m going round the houses to do things like parse/split URLs. I’ve searched to see if this is built-in functionality but couldn’t find anything. Does anyone know a better way of doing this?

I want users to be able to configure my hook by pasting a URL into its settings screen that contains rules the plugin will use. The URL could be one referencing a file hosted inside a bitbucket repo or on the Internet. If anyone knows an easy way to cache this that would be really helpful too.

Eg:

  • http://bitbucket:7990/bitbucket/projects/RUL/repos/rules/raw/test.json → grab the URL inside the hook as the logged on user
  • http://github.com/foo/bar/test.json → grab the URL inside the hook, no authentication (unless specified inside the URL)

I wasn’t able to get this to pass on the user’s current credentials. The closest thing I saw were questions about using SAL TrustedRequestFactory with Jira and Bamboo:

I tried injecting TrustedRequestFactory in Bitbucket with @ComponentImport but I get missing class errors. I defined sal-trust-api in my pom.xml with variants of:

   <dependency>
       <groupId>com.atlassian.sal</groupId>
       <artifactId>sal-trust-api</artifactId>
       <scope>provided</scope> <!-- on and off -->
       <version>3.2.0-abbce37</version>
   </dependency>

… But nothing seemed to help. Looks like its just not supported in Bitbucket (5.16.0)?

The closest thing I could find that did work was based on another forum post which shows how to use the Java API to request files based on project keys and repository slugs:

I couldn’t see an easy way to extract these strings from a URL so I ended up writing my own[1] - surely this already exists somewhere? Anyone know what it called?

After a day of hacking around my working code looks like this (constructor injection used for contentService, applicationProperties and requestFactory):

        ...
        String baseUrl = applicationProperties.getBaseUrl(UrlMode.ABSOLUTE);
        BitbucketUrl bitbucketUrl = new BitbucketUrl(baseUrl, customRuleUrl);
        InputStream in;
        if (bitbucketUrl.isBitbucketUrl()) {
            // grab a file from local bitbucket instance using Java API
            Repository repository = repositoryService.getBySlug(
                    bitbucketUrl.getProject(),
                    bitbucketUrl.getSlug()
            );

            ByteArrayOutputStream out = new ByteArrayOutputStream();
            contentService.streamFile(
                    repository,
                    bitbucketUrl.getRevision(),
                    bitbucketUrl.getFilePath(),
                    (s) -> out
            );

            in = new ByteArrayInputStream(out.toByteArray());
        } else {
            // non-local URL - just make a regular request using RequestFactory
            in = (InputStream) requestFactory.createRequest(Request.MethodType.GET, customRuleUrl)
            ...
        }

        ...

Can anyone help me simplify this?

Thanks,
Geoff

[1] Bitbucket URL component extractor

package com.declarativesystems.bitbucket.commitscanner;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class BitbucketUrl {

    private String project;
    private String slug;
    private String filePath;
    private String revision;
    private boolean bitbucketUrl;
    private Matcher matcher;

    public String getProject() {
        return project;
    }

    public String getSlug() {
        return slug;
    }

    public String getFilePath() {
        return filePath;
    }


    public String getRevision() {
        return revision;
    }


    /**
     * /projects/RUL/repos/rules/raw/test.json
     * URLs must match this regex which we also use for field capture:
     * $1 - project key
     * $2 - slug/repository name
     * $3 - path
     * $4 - at parameter (all)
     * $5 - commit/branch
     */
    private static final Pattern regex = Pattern.compile(
            "^/projects/([^/]+)/repos/([^/]+)/raw/([^\\?]+)(\\?at=(.*))?$"
    );


    public BitbucketUrl(String baseUrl, String targetUrl) {
        bitbucketUrl = targetUrl.startsWith(baseUrl);
        if (bitbucketUrl) {
            parseBitbucketUrl(targetUrl.replace(baseUrl, ""));
        }


    }

    /**
     * should be left with something like
     * /projects/RUL/repos/rules/raw/test.json
     */
    private void parseBitbucketUrl(String path) {
        // only allow paths matching regex we expect
        matcher = regex.matcher(path);
        if (matcher.matches()) {
            project = matcher.group(1);
            slug = matcher.group(2);
            filePath = matcher.group(3);
            revision = matcher.group(5);
        } else {
            throw new RuntimeException(
                "Access denied: only files from bitbucket repositories can be accessed"
            );
        }
    }

    public boolean isBitbucketUrl() {
        return bitbucketUrl && matcher.matches();
    }
}