Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Merge pull request #77 from jenkinsci/jenkins-cli-token
[JENKINS-27045] Jenkins CLI --username/--password options
  • Loading branch information
samrocketman committed Mar 9, 2017
2 parents 120f8ec + e5858c3 commit 3eeba9c
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 7 deletions.
1 change: 1 addition & 0 deletions .mvn/jvm.config
@@ -0,0 +1 @@
-Xmx256m -XX:MaxPermSize=128m -Djava.awt.headless=true
3 changes: 2 additions & 1 deletion LICENSE.txt
Expand Up @@ -2,7 +2,8 @@ The MIT License

Copyright (c) 2011 Michael O'Cleirigh
Copyright (c) 2013-2014 Sam Kottler
Copyright (c) 2015 Sam Gleske
Copyright (c) 2015-2017 Sam Gleske
Copyright (c) 2004-, Kohsuke Kawaguchi, Sun Microsystems, Inc., and a number of other of contributors - https://github.com/jenkinsci/jenkins

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
82 changes: 79 additions & 3 deletions src/main/java/org/jenkinsci/plugins/GithubSecurityRealm.java
Expand Up @@ -33,16 +33,21 @@ of this software and associated documentation files (the "Software"), to deal
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import hudson.Extension;
import hudson.FilePath;
import hudson.ProxyConfiguration;
import hudson.Util;
import hudson.cli.CLICommand;
import hudson.model.Descriptor;
import hudson.model.User;
import hudson.security.AbstractPasswordBasedSecurityRealm;
import hudson.security.CliAuthenticator;
import hudson.security.GroupDetails;
import hudson.security.SecurityRealm;
import hudson.security.UserMayOrMayNotExistException;
import hudson.tasks.Mailer;
import hudson.util.Secret;
import jenkins.model.Jenkins;
import jenkins.security.MasterToSlaveCallable;
import jenkins.security.SecurityListener;
import org.acegisecurity.Authentication;
import org.acegisecurity.AuthenticationException;
Expand All @@ -64,6 +69,7 @@ of this software and associated documentation files (the "Software"), to deal
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.jfree.util.Log;
import org.kohsuke.args4j.Option;
import org.kohsuke.github.GHEmail;
import org.kohsuke.github.GHMyself;
import org.kohsuke.github.GHOrganization;
Expand All @@ -77,15 +83,16 @@ of this software and associated documentation files (the "Software"), to deal
import org.springframework.dao.DataAccessException;
import org.springframework.dao.DataRetrievalFailureException;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.Console;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.logging.Logger;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;

/**
*
Expand All @@ -95,7 +102,7 @@ of this software and associated documentation files (the "Software"), to deal
* This is based on the MySQLSecurityRealm from the mysql-auth-plugin written by
* Alex Ackerman.
*/
public class GithubSecurityRealm extends SecurityRealm implements UserDetailsService {
public class GithubSecurityRealm extends AbstractPasswordBasedSecurityRealm implements UserDetailsService {
private static final String DEFAULT_WEB_URI = "https://github.com";
private static final String DEFAULT_API_URI = "https://api.github.com";
private static final String DEFAULT_ENTERPRISE_API_SUFFIX = "/api/v3";
Expand Down Expand Up @@ -485,6 +492,57 @@ public UserDetails loadUserByUsername(String username)
});
}

@Override
protected GithubOAuthUserDetails authenticate(String username, String password) throws AuthenticationException {
try {
GithubAuthenticationToken github = new GithubAuthenticationToken(password, getGithubApiUri());
if(username.equals(github.getPrincipal())) {
SecurityContextHolder.getContext().setAuthentication(github);
return github.getUserDetails(username);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
throw new BadCredentialsException("Invalid GitHub username or personal access token: " + username);
}

@Override
public CliAuthenticator createCliAuthenticator(final CLICommand command) {
return new CliAuthenticator() {
@Option(name="--username",usage="GitHub username to authenticate yourself to Jenkins.")
public String userName;

@Option(name="--password",usage="GitHub personal access token. Note that passing a password in arguments is insecure.")
public String password;

@Option(name="--password-file",usage="File that contains the personal access token.")
public String passwordFile;

public Authentication authenticate() throws AuthenticationException, IOException, InterruptedException {
if(userName == null) {
//return command.getTransportAuthentication(); // no authentication parameter. fallback to the transport
return Jenkins.ANONYMOUS;
}
if(passwordFile != null) {
try {
password = new FilePath(command.channel, passwordFile).readToString().trim();
} catch (IOException e) {
throw new BadCredentialsException("Failed to read " + passwordFile, e);
}
}
if(password == null) {
password = command.channel.call(new InteractivelyAskForPassword());
}

if(password == null) {
throw new BadCredentialsException("No GitHub personal access token specified.");
}
GithubSecurityRealm.this.authenticate(userName, password);
return new GithubAuthenticationToken(password, getGithubApiUri());
}
};
}

@Override
public String getLoginUrl() {
return "securityRealm/commenceLogin";
Expand Down Expand Up @@ -686,4 +744,22 @@ static Jenkins getJenkins() {
private static final Logger LOGGER = Logger.getLogger(GithubSecurityRealm.class.getName());

private static final String REFERER_ATTRIBUTE = GithubSecurityRealm.class.getName()+".referer";

/**
* Asks for the password.
*/
private static class InteractivelyAskForPassword extends MasterToSlaveCallable<String,IOException> {
public String call() throws IOException {
Console console = System.console();
if(console == null) {
return null; // no terminal
}
char[] w = console.readPassword("GitHub Personal Access Token: ");
if(w==null) {
return null;
}
return new String(w);
}
private static final long serialVersionUID = 1L;
}
}
Expand Up @@ -25,7 +25,7 @@ of this software and associated documentation files (the "Software"), to deal
package org.jenkinsci.plugins;

import org.jenkinsci.plugins.GithubSecurityRealm.DescriptorImpl;
import org.junit.Rule;
import org.junit.ClassRule;
import org.junit.Test;
import org.jvnet.hudson.test.JenkinsRule;

Expand All @@ -35,8 +35,8 @@ of this software and associated documentation files (the "Software"), to deal

public class GithubSecurityRealmTest {

@Rule
public final JenkinsRule rule = new JenkinsRule();
@ClassRule
public final static JenkinsRule rule = new JenkinsRule();

@Test
public void testEquals_true() {
Expand Down

0 comments on commit 3eeba9c

Please sign in to comment.