Skip to content

Commit

Permalink
Modify GitHubBuilder to resolve user credentials from the system envi…
Browse files Browse the repository at this point in the history
…ronment

Using the Jenkins EnvInject or Credentials Binding Plugins its possible to
pass credentials as Environment Variables.

Its useful for Github.connect() to be able to directly read the values of the
'login', 'password' and 'oauth' properties directly from the environment.

This commit modifies the base Github.connect() method to resolve credentials
in two steps:

1. ~/.github credentials file if it exists.
2. login, password or oauth variables from the environment

A further fromEnvironment() method is provided to support
loading from non-standard variable names.

The old Github.connect() method would throw an IOException if the ~/.github file
did not exist.  Now it will fail silently instead dropping back to the anonymous
users access level.

Added new unit tests into GitHubTest.
  • Loading branch information
mocleiri committed Sep 29, 2014
1 parent d228a5f commit 4d6c5c1
Show file tree
Hide file tree
Showing 3 changed files with 184 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
83 changes: 80 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,99 @@

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.
*
* the system properties for the login, password or oauth environment variables.
*
* @return the configured Builder from credentials defined on the system or in the environment.
*
* @throws IOException
*/
public static GitHubBuilder fromCredentials() throws IOException {

GitHubBuilder builder;
try {
builder = fromPropertyFile();

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

} catch (FileNotFoundException e) {
return fromEnvironment();
}

}

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 +106,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 4d6c5c1

Please sign in to comment.