Skip to content

Commit

Permalink
Merge pull request #131 from mocleiri/resolve-credentials-from-enviro…
Browse files Browse the repository at this point in the history
…nment

Modify GitHubBuilder to resolve user credentials from the system environ...
  • Loading branch information
mocleiri committed Sep 30, 2014
2 parents ac64c20 + 0f64994 commit 556786f
Show file tree
Hide file tree
Showing 3 changed files with 198 additions and 5 deletions.
4 changes: 2 additions & 2 deletions src/main/java/org/kohsuke/github/GitHub.java
Original file line number Diff line number Diff line change
Expand Up @@ -128,10 +128,10 @@ public class GitHub {
}

/**
* Obtains the credential from "~/.github"
* Obtains the credential from "~/.github" or from the System Environment Properties.
*/
public static GitHub connect() throws IOException {
return GitHubBuilder.fromPropertyFile().build();
return GitHubBuilder.fromCredentials().build();
}

/**
Expand Down
97 changes: 94 additions & 3 deletions src/main/java/org/kohsuke/github/GitHubBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,113 @@

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Map;
import java.util.Properties;

/**
* @since 1.59
*/
public class GitHubBuilder {
private String endpoint = GitHub.GITHUB_URL;
private String user;
private String password;
private String oauthToken;

// default scoped so unit tests can read them.
/* private */ String user;
/* private */ String password;
/* private */ String oauthToken;

private HttpConnector connector;

public GitHubBuilder() {
}

/**
* First check if the credentials are configured using the ~/.github properties file.
*
* If no user is specified it means there is no configuration present so check the environment instead.
*
* If there is still no user it means there are no credentials defined and throw an IOException.
*
* @return the configured Builder from credentials defined on the system or in the environment.
*
* @throws IOException If there are no credentials defined in the ~/.github properties file or the process environment.
*/
public static GitHubBuilder fromCredentials() throws IOException {

GitHubBuilder builder;
try {
builder = fromPropertyFile();

if (builder.user != null)
return builder;
else {

// this is the case where the ~/.github file exists but has no content.

builder = fromEnvironment();

if (builder.user != null)
return builder;
else
throw new IOException("Failed to resolve credentials from ~/.github or the environment.");
}

} catch (FileNotFoundException e) {
builder = fromEnvironment();

if (builder.user != null)
return builder;
else
throw new IOException("Failed to resolve credentials from ~/.github or the environment.", e);
}

}

public static GitHubBuilder fromEnvironment(String loginVariableName, String passwordVariableName, String oauthVariableName) throws IOException {


Properties env = new Properties();

Object loginValue = System.getenv(loginVariableName);

if (loginValue != null)
env.put("login", loginValue);

Object passwordValue = System.getenv(passwordVariableName);

if (passwordValue != null)
env.put("password", passwordValue);

Object oauthValue = System.getenv(oauthVariableName);

if (oauthValue != null)
env.put("oauth", oauthValue);

return fromProperties(env);

}

public static GitHubBuilder fromEnvironment() throws IOException {

Properties props = new Properties();

Map<String, String> env = System.getenv();

for (Map.Entry<String, String> element : env.entrySet()) {

props.put(element.getKey(), element.getValue());
}

return fromProperties(props);
}

public static GitHubBuilder fromPropertyFile() throws IOException {
File homeDir = new File(System.getProperty("user.home"));
File propertyFile = new File(homeDir, ".github");
return fromPropertyFile(propertyFile.getPath());
}

public static GitHubBuilder fromPropertyFile(String propertyFileName) throws IOException {
Properties props = new Properties();
FileInputStream in = null;
Expand All @@ -34,6 +120,11 @@ public static GitHubBuilder fromPropertyFile(String propertyFileName) throws IOE
} finally {
IOUtils.closeQuietly(in);
}

return fromProperties(props);
}

public static GitHubBuilder fromProperties(Properties props) {
GitHubBuilder self = new GitHubBuilder();
self.withOAuthToken(props.getProperty("oauth"), props.getProperty("login"));
self.withPassword(props.getProperty("login"), props.getProperty("password"));
Expand Down
102 changes: 102 additions & 0 deletions src/test/java/org/kohsuke/github/GitHubTest.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
package org.kohsuke.github;

import java.io.IOException;
import java.lang.reflect.Field;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

import junit.framework.TestCase;

/**
Expand All @@ -21,4 +28,99 @@ public void testGitHubServerWithoutServer() throws Exception {
GitHub hub = GitHub.connectUsingPassword("kohsuke", "bogus");
assertEquals("https://api.github.com/test", hub.getApiURL("/test").toString());
}

public void testGitHubFromEnvironment() throws IOException {

Map<String, String>props = new HashMap<String, String>();

props.put("login", "bogus");

setupEnvironment(props);

GitHub hub = GitHub.connect();

assertEquals("bogus", hub.login);

}

public void testGitHubBuilderFromEnvironment() throws IOException {

Map<String, String>props = new HashMap<String, String>();

props.put("login", "bogus");
props.put("oauth", "bogus");
props.put("password", "bogus");

setupEnvironment(props);

GitHubBuilder builder = GitHubBuilder.fromEnvironment();

assertEquals("bogus", builder.user);
assertEquals("bogus", builder.oauthToken);
assertEquals("bogus", builder.password);

}

/*
* Copied from StackOverflow: http://stackoverflow.com/a/7201825/2336755
*
* This allows changing the in memory process environment.
*
* Its used to wire in values for the github credentials to test that the GitHubBuilder works properly to resolve them.
*/
private void setupEnvironment(Map<String, String> newenv) {
try
{
Class<?> processEnvironmentClass = Class.forName("java.lang.ProcessEnvironment");
Field theEnvironmentField = processEnvironmentClass.getDeclaredField("theEnvironment");
theEnvironmentField.setAccessible(true);
Map<String, String> env = (Map<String, String>) theEnvironmentField.get(null);
env.putAll(newenv);
Field theCaseInsensitiveEnvironmentField = processEnvironmentClass.getDeclaredField("theCaseInsensitiveEnvironment");
theCaseInsensitiveEnvironmentField.setAccessible(true);
Map<String, String> cienv = (Map<String, String>) theCaseInsensitiveEnvironmentField.get(null);
cienv.putAll(newenv);
}
catch (NoSuchFieldException e)
{
try {
Class[] classes = Collections.class.getDeclaredClasses();
Map<String, String> env = System.getenv();
for(Class cl : classes) {
if("java.util.Collections$UnmodifiableMap".equals(cl.getName())) {
Field field = cl.getDeclaredField("m");
field.setAccessible(true);
Object obj = field.get(env);
Map<String, String> map = (Map<String, String>) obj;
map.clear();
map.putAll(newenv);
}
}
} catch (Exception e2) {
e2.printStackTrace();
}
} catch (Exception e1) {
e1.printStackTrace();
}

}

public void testGitHubBuilderFromCustomEnvironment() throws IOException {

Map<String, String>props = new HashMap<String, String>();

props.put("customLogin", "bogusLogin");
props.put("customOauth", "bogusOauth");
props.put("customPassword", "bogusPassword");

setupEnvironment(props);

GitHubBuilder builder = GitHubBuilder.fromEnvironment("customLogin", "customPassword", "customOauth");

assertEquals("bogusLogin", builder.user);
assertEquals("bogusOauth", builder.oauthToken);
assertEquals("bogusPassword", builder.password);

}

}

0 comments on commit 556786f

Please sign in to comment.