@@ -33,16 +33,21 @@ of this software and associated documentation files (the "Software"), to deal
33
33
import com .thoughtworks .xstream .io .HierarchicalStreamReader ;
34
34
import com .thoughtworks .xstream .io .HierarchicalStreamWriter ;
35
35
import hudson .Extension ;
36
+ import hudson .FilePath ;
36
37
import hudson .ProxyConfiguration ;
37
38
import hudson .Util ;
39
+ import hudson .cli .CLICommand ;
38
40
import hudson .model .Descriptor ;
39
41
import hudson .model .User ;
42
+ import hudson .security .AbstractPasswordBasedSecurityRealm ;
43
+ import hudson .security .CliAuthenticator ;
40
44
import hudson .security .GroupDetails ;
41
45
import hudson .security .SecurityRealm ;
42
46
import hudson .security .UserMayOrMayNotExistException ;
43
47
import hudson .tasks .Mailer ;
44
48
import hudson .util .Secret ;
45
49
import jenkins .model .Jenkins ;
50
+ import jenkins .security .MasterToSlaveCallable ;
46
51
import jenkins .security .SecurityListener ;
47
52
import org .acegisecurity .Authentication ;
48
53
import org .acegisecurity .AuthenticationException ;
@@ -64,6 +69,7 @@ of this software and associated documentation files (the "Software"), to deal
64
69
import org .apache .http .impl .client .HttpClients ;
65
70
import org .apache .http .util .EntityUtils ;
66
71
import org .jfree .util .Log ;
72
+ import org .kohsuke .args4j .Option ;
67
73
import org .kohsuke .github .GHEmail ;
68
74
import org .kohsuke .github .GHMyself ;
69
75
import org .kohsuke .github .GHOrganization ;
@@ -77,15 +83,16 @@ of this software and associated documentation files (the "Software"), to deal
77
83
import org .springframework .dao .DataAccessException ;
78
84
import org .springframework .dao .DataRetrievalFailureException ;
79
85
80
- import javax .annotation .Nonnull ;
81
- import javax .annotation .Nullable ;
86
+ import java .io .Console ;
82
87
import java .io .IOException ;
83
88
import java .net .InetSocketAddress ;
84
89
import java .net .Proxy ;
85
90
import java .util .Arrays ;
86
91
import java .util .HashSet ;
87
92
import java .util .Set ;
88
93
import java .util .logging .Logger ;
94
+ import javax .annotation .Nonnull ;
95
+ import javax .annotation .Nullable ;
89
96
90
97
/**
91
98
*
@@ -95,7 +102,7 @@ of this software and associated documentation files (the "Software"), to deal
95
102
* This is based on the MySQLSecurityRealm from the mysql-auth-plugin written by
96
103
* Alex Ackerman.
97
104
*/
98
- public class GithubSecurityRealm extends SecurityRealm implements UserDetailsService {
105
+ public class GithubSecurityRealm extends AbstractPasswordBasedSecurityRealm implements UserDetailsService {
99
106
private static final String DEFAULT_WEB_URI = "https://github.com" ;
100
107
private static final String DEFAULT_API_URI = "https://api.github.com" ;
101
108
private static final String DEFAULT_ENTERPRISE_API_SUFFIX = "/api/v3" ;
@@ -485,6 +492,57 @@ public UserDetails loadUserByUsername(String username)
485
492
});
486
493
}
487
494
495
+ @ Override
496
+ protected GithubOAuthUserDetails authenticate (String username , String password ) throws AuthenticationException {
497
+ try {
498
+ GithubAuthenticationToken github = new GithubAuthenticationToken (password , getGithubApiUri ());
499
+ if (username .equals (github .getPrincipal ())) {
500
+ SecurityContextHolder .getContext ().setAuthentication (github );
501
+ return github .getUserDetails (username );
502
+ }
503
+ } catch (IOException e ) {
504
+ throw new RuntimeException (e );
505
+ }
506
+ throw new BadCredentialsException ("Invalid GitHub username or personal access token: " + username );
507
+ }
508
+
509
+ @ Override
510
+ public CliAuthenticator createCliAuthenticator (final CLICommand command ) {
511
+ return new CliAuthenticator () {
512
+ @ Option (name ="--username" ,usage ="GitHub username to authenticate yourself to Jenkins." )
513
+ public String userName ;
514
+
515
+ @ Option (name ="--password" ,usage ="GitHub personal access token. Note that passing a password in arguments is insecure." )
516
+ public String password ;
517
+
518
+ @ Option (name ="--password-file" ,usage ="File that contains the personal access token." )
519
+ public String passwordFile ;
520
+
521
+ public Authentication authenticate () throws AuthenticationException , IOException , InterruptedException {
522
+ if (userName == null ) {
523
+ //return command.getTransportAuthentication(); // no authentication parameter. fallback to the transport
524
+ return Jenkins .ANONYMOUS ;
525
+ }
526
+ if (passwordFile != null ) {
527
+ try {
528
+ password = new FilePath (command .channel , passwordFile ).readToString ().trim ();
529
+ } catch (IOException e ) {
530
+ throw new BadCredentialsException ("Failed to read " + passwordFile , e );
531
+ }
532
+ }
533
+ if (password == null ) {
534
+ password = command .channel .call (new InteractivelyAskForPassword ());
535
+ }
536
+
537
+ if (password == null ) {
538
+ throw new BadCredentialsException ("No GitHub personal access token specified." );
539
+ }
540
+ GithubSecurityRealm .this .authenticate (userName , password );
541
+ return new GithubAuthenticationToken (password , getGithubApiUri ());
542
+ }
543
+ };
544
+ }
545
+
488
546
@ Override
489
547
public String getLoginUrl () {
490
548
return "securityRealm/commenceLogin" ;
@@ -686,4 +744,22 @@ static Jenkins getJenkins() {
686
744
private static final Logger LOGGER = Logger .getLogger (GithubSecurityRealm .class .getName ());
687
745
688
746
private static final String REFERER_ATTRIBUTE = GithubSecurityRealm .class .getName ()+".referer" ;
747
+
748
+ /**
749
+ * Asks for the password.
750
+ */
751
+ private static class InteractivelyAskForPassword extends MasterToSlaveCallable <String ,IOException > {
752
+ public String call () throws IOException {
753
+ Console console = System .console ();
754
+ if (console == null ) {
755
+ return null ; // no terminal
756
+ }
757
+ char [] w = console .readPassword ("GitHub Personal Access Token: " );
758
+ if (w ==null ) {
759
+ return null ;
760
+ }
761
+ return new String (w );
762
+ }
763
+ private static final long serialVersionUID = 1L ;
764
+ }
689
765
}
0 commit comments