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

add gitlab and github token Auth support #14 #18

Merged
merged 23 commits into from
Feb 12, 2021
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
39631fb
use gitlab api request as preperation to support auth for private rep…
Feb 5, 2021
34d9a43
support self hosted gitlab hosts
Feb 5, 2021
1b65580
fix empty repoSubfolder
Feb 5, 2021
178bced
add github and gitlab token based auth support #14
Feb 5, 2021
6df64aa
First step of refactoring GitHub into a fluent interface.
nedtwigg Feb 5, 2021
4d1312d
GitHub can now handle auth with fluent interface.
nedtwigg Feb 5, 2021
f9e526f
GitLab can now handle auth with fluent interface, but not yet custom …
nedtwigg Feb 5, 2021
fa82f52
GitLab can now handle custom domains and protocols.
nedtwigg Feb 5, 2021
ea28cb4
Fix security vuln where GitHub and GitLab could have sent auth over H…
nedtwigg Feb 5, 2021
9287589
Fixup GitLab test.
nedtwigg Feb 5, 2021
9e3967c
Minor fixup.
nedtwigg Feb 5, 2021
f8f1c11
Refactor gitlab and auth around fluent configurator pattern.
nedtwigg Feb 5, 2021
9bcddff
extract authToken Support to interface
Feb 7, 2021
98a19e2
Revert "extract authToken Support to interface"
nedtwigg Feb 8, 2021
b2c7f35
Rework the "assertPluginNotSet()" state management for the fluent con…
nedtwigg Feb 8, 2021
f3edc22
AuthPlugin ought to return void, because `Request.Builder` is mutable…
nedtwigg Feb 8, 2021
d4717db
Update changelog.
nedtwigg Feb 12, 2021
a85a5e9
Remove the ben-manes.versions plugin.
nedtwigg Feb 12, 2021
938daaf
Re-add the tests that @vgropp added which I accidentally reverted.
nedtwigg Feb 12, 2021
12b2bba
Add gradle wrapper validation.
nedtwigg Feb 12, 2021
0cee4e8
Replace vgropp test repo with diffplug test repo.
nedtwigg Feb 12, 2021
a2769d2
Reduce the number of repositories to maintain.
nedtwigg Feb 12, 2021
e50316f
Fix test on windows.
nedtwigg Feb 12, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion src/main/java/com/diffplug/blowdryer/Blowdryer.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.util.Base64;
Expand Down Expand Up @@ -132,7 +133,7 @@ private static Map<String, String> loadPropertyFile(File file) throws IOExceptio

private static void download(String url, File dst) throws IOException {
OkHttpClient client = new OkHttpClient.Builder().build();
Request req = new Request.Builder().url(url).build();
Request req = authPlugin.addAuthToken(url, new Request.Builder().url(url)).build();
try (Response response = client.newCall(req).execute()) {
if (!response.isSuccessful()) {
throw new IllegalArgumentException(url + "\nreceived http code " + response.code() + "\n" + response.body().string());
Expand Down Expand Up @@ -204,6 +205,19 @@ private static void assertInitialized() {
}
}

static interface AuthPlugin {
Request.Builder addAuthToken(String url, Request.Builder builder) throws MalformedURLException;
}

private static final AuthPlugin authPluginNone = (url, builder) -> builder;;
private static AuthPlugin authPlugin = authPluginNone;

public static void setAuthPlugin(AuthPlugin authPlugin) {
synchronized (Blowdryer.class) {
Blowdryer.authPlugin = authPlugin == null ? authPluginNone : authPlugin;
}
}

/** Returns the given resource as a File (as configured by {@link BlowdryerSetup}. */
public static File file(String resourcePath) {
synchronized (Blowdryer.class) {
Expand Down
118 changes: 108 additions & 10 deletions src/main/java/com/diffplug/blowdryer/BlowdryerSetup.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,20 @@
import com.diffplug.common.base.Errors;
import groovy.lang.Closure;
import java.io.File;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Objects;
import java.util.function.Function;
import javax.annotation.Nullable;
import org.jetbrains.annotations.NotNull;

/** Configures where {@link Blowdryer#file(String)} downloads files from. */
public class BlowdryerSetup {
static final String NAME = "blowdryerSetup";

private static final String GITHUB_HOST = "raw.githubusercontent.com";
private static final String GITLAB_HOST = "gitlab.com";

private final File referenceDirectory;

/** Pass in the directory that will be used to resolve string arguments to devLocal. */
Expand Down Expand Up @@ -54,19 +61,102 @@ public enum GitAnchorType {
}

/** Sets the source where we will grab these scripts. */
public void github(String repoOrg, GitAnchorType anchorType, String anchor) {
assertNoLeadingOrTrailingSlash(repoOrg);
assertNoLeadingOrTrailingSlash(anchor);
String root = "https://raw.githubusercontent.com/" + repoOrg + "/" + anchor + "/" + repoSubfolder + "/";
Blowdryer.setResourcePlugin(resource -> root + resource);
public GitHub github(String repoOrg, GitAnchorType anchorType, String anchor) {
// anchorType isn't used right now, but makes it easier to read what "anchor" is
return new GitHub(repoOrg, anchor);
}

public class GitHub {
private String repoOrg;
private String anchor;
private @Nullable String authToken;

private GitHub(String repoOrg, String anchor) {
this.repoOrg = assertNoLeadingOrTrailingSlash(repoOrg);
this.anchor = assertNoLeadingOrTrailingSlash(anchor);
setGlobals();
}

public GitHub authToken(String authToken) {
this.authToken = authToken;
return setGlobals();
}

private GitHub setGlobals() {
String root = "https://" + GITHUB_HOST + "/" + repoOrg + "/" + anchor + "/";
Blowdryer.setResourcePlugin(resource -> root + getFullResourcePath(resource));
if (authToken != null) {
Blowdryer.setAuthPlugin((url, builder) -> {
if (url.startsWith(root)) {
builder.addHeader("Authorization", "Bearer " + authToken);
}
return builder;
});
} else {
Blowdryer.setAuthPlugin(null);
}
return this;
}
}

/** Sets the source where we will grab these scripts. */
public void gitlab(String repoOrg, GitAnchorType anchorType, String anchor) {
assertNoLeadingOrTrailingSlash(repoOrg);
assertNoLeadingOrTrailingSlash(anchor);
String root = "https://gitlab.com/" + repoOrg + "/-/raw/" + anchor + "/" + repoSubfolder + "/";
Blowdryer.setResourcePlugin(resource -> root + resource);
public GitLab gitlab(String repoOrg, GitAnchorType anchorType, String anchor) {
// anchorType isn't used right now, but makes it easier to read what "anchor" is
return new GitLab(repoOrg, anchor);
}

public class GitLab {
private String repoOrg;
private String anchor;
private @Nullable String authToken;
private String protocol, host;

private GitLab(String repoOrg, String anchor) {
this.repoOrg = assertNoLeadingOrTrailingSlash(repoOrg);
this.anchor = assertNoLeadingOrTrailingSlash(anchor);
customDomainHttps(GITLAB_HOST);
}

public GitLab authToken(String authToken) {
this.authToken = authToken;
return setGlobals();
}

public GitLab customDomainHttp(String domain) {
return customProtocolAndDomain("http://", domain);
}

public GitLab customDomainHttps(String domain) {
return customProtocolAndDomain("https://", domain);
}

private GitLab customProtocolAndDomain(String protocol, String domain) {
this.protocol = protocol;
this.host = domain;
return setGlobals();
}

private GitLab setGlobals() {
String urlStart = protocol + host + "/api/v4/projects/" + encodeUrlPart(repoOrg) + "/repository/files/";
String urlEnd = "/raw?ref=" + encodeUrlPart(anchor);
Blowdryer.setResourcePlugin(resource -> urlStart + encodeUrlPart(getFullResourcePath(resource)) + urlEnd);
if (authToken != null) {
Blowdryer.setAuthPlugin((url, builder) -> {
if (url.startsWith(urlStart)) {
builder.addHeader("Authorization", "Bearer " + authToken);
}
return builder;
});
} else {
Blowdryer.setAuthPlugin(null);
}
return this;
}
}

@NotNull
private String getFullResourcePath(String resource) {
return (repoSubfolder.isEmpty() ? "" : repoSubfolder + "/") + resource;
}

/** Sets the mapping from `file(String)` to `immutableUrl(String)`. */
Expand Down Expand Up @@ -108,4 +198,12 @@ private static String assertNoLeadingOrTrailingSlash(String input) {
}
return input;
}

private static String encodeUrlPart(String part) {
try {
return URLEncoder.encode(part, "UTF-8");
} catch (UnsupportedEncodingException e) {
throw new IllegalArgumentException("error encoding part", e);
}
}
}