@@ -42,30 +42,24 @@ of this software and associated documentation files (the "Software"), to deal
42
42
import hudson .security .AbstractPasswordBasedSecurityRealm ;
43
43
import hudson .security .GroupDetails ;
44
44
import hudson .security .SecurityRealm ;
45
- import hudson .security .UserMayOrMayNotExistException ;
45
+ import hudson .security .UserMayOrMayNotExistException2 ;
46
46
import hudson .tasks .Mailer ;
47
47
import hudson .util .Secret ;
48
+ import jakarta .servlet .http .HttpSession ;
48
49
import java .io .IOException ;
50
+ import java .io .UncheckedIOException ;
49
51
import java .net .InetSocketAddress ;
50
52
import java .net .Proxy ;
51
53
import java .security .SecureRandom ;
52
54
import java .util .Arrays ;
55
+ import java .util .Collection ;
53
56
import java .util .HashSet ;
57
+ import java .util .List ;
54
58
import java .util .Set ;
55
59
import java .util .logging .Level ;
56
60
import java .util .logging .Logger ;
57
- import javax .servlet .http .HttpSession ;
58
61
import jenkins .model .Jenkins ;
59
62
import jenkins .security .SecurityListener ;
60
- import org .acegisecurity .Authentication ;
61
- import org .acegisecurity .AuthenticationException ;
62
- import org .acegisecurity .AuthenticationManager ;
63
- import org .acegisecurity .BadCredentialsException ;
64
- import org .acegisecurity .context .SecurityContextHolder ;
65
- import org .acegisecurity .providers .UsernamePasswordAuthenticationToken ;
66
- import org .acegisecurity .userdetails .UserDetails ;
67
- import org .acegisecurity .userdetails .UserDetailsService ;
68
- import org .acegisecurity .userdetails .UsernameNotFoundException ;
69
63
import org .apache .commons .lang .builder .HashCodeBuilder ;
70
64
import org .apache .http .HttpEntity ;
71
65
import org .apache .http .HttpHost ;
@@ -83,15 +77,24 @@ of this software and associated documentation files (the "Software"), to deal
83
77
import org .kohsuke .github .GHMyself ;
84
78
import org .kohsuke .github .GHOrganization ;
85
79
import org .kohsuke .github .GHTeam ;
80
+ import org .kohsuke .github .GHUser ;
86
81
import org .kohsuke .stapler .DataBoundConstructor ;
87
82
import org .kohsuke .stapler .Header ;
88
83
import org .kohsuke .stapler .HttpRedirect ;
89
84
import org .kohsuke .stapler .HttpResponse ;
90
85
import org .kohsuke .stapler .HttpResponses ;
91
86
import org .kohsuke .stapler .QueryParameter ;
92
- import org .kohsuke .stapler .StaplerRequest ;
93
- import org .springframework .dao .DataAccessException ;
94
- import org .springframework .dao .DataRetrievalFailureException ;
87
+ import org .kohsuke .stapler .StaplerRequest2 ;
88
+ import org .springframework .security .authentication .AuthenticationManager ;
89
+ import org .springframework .security .authentication .AuthenticationServiceException ;
90
+ import org .springframework .security .authentication .BadCredentialsException ;
91
+ import org .springframework .security .authentication .UsernamePasswordAuthenticationToken ;
92
+ import org .springframework .security .core .Authentication ;
93
+ import org .springframework .security .core .AuthenticationException ;
94
+ import org .springframework .security .core .GrantedAuthority ;
95
+ import org .springframework .security .core .context .SecurityContextHolder ;
96
+ import org .springframework .security .core .userdetails .UserDetails ;
97
+ import org .springframework .security .core .userdetails .UsernameNotFoundException ;
95
98
96
99
/**
97
100
*
@@ -101,7 +104,7 @@ of this software and associated documentation files (the "Software"), to deal
101
104
* This is based on the MySQLSecurityRealm from the mysql-auth-plugin written by
102
105
* Alex Ackerman.
103
106
*/
104
- public class GithubSecurityRealm extends AbstractPasswordBasedSecurityRealm implements UserDetailsService {
107
+ public class GithubSecurityRealm extends AbstractPasswordBasedSecurityRealm {
105
108
private static final String DEFAULT_WEB_URI = "https://github.com" ;
106
109
private static final String DEFAULT_API_URI = "https://api.github.com" ;
107
110
private static final String DEFAULT_ENTERPRISE_API_SUFFIX = "/api/v3" ;
@@ -332,7 +335,7 @@ public String getOauthScopes() {
332
335
return oauthScopes ;
333
336
}
334
337
335
- public HttpResponse doCommenceLogin (StaplerRequest request , @ QueryParameter String from , @ Header ("Referer" ) final String referer )
338
+ public HttpResponse doCommenceLogin (StaplerRequest2 request , @ QueryParameter String from , @ Header ("Referer" ) final String referer )
336
339
throws IOException {
337
340
// https://tools.ietf.org/html/rfc6749#section-10.10 dictates that probability that an attacker guesses the string
338
341
// SHOULD be less than or equal to 2^(-160) and our Strings consist of 65 chars. (65^27 ~= 2^160)
@@ -355,7 +358,7 @@ public HttpResponse doCommenceLogin(StaplerRequest request, @QueryParameter Stri
355
358
}
356
359
String suffix ="" ;
357
360
if (!scopes .isEmpty ()) {
358
- suffix = "&scope=" + Util .join (scopes , "," )+ "&state=" + state ;
361
+ suffix = "&scope=" + String .join ("," , scopes ) + "&state=" + state ;
359
362
} else {
360
363
// We need repo scope in order to access private repos
361
364
// See https://developer.github.com/v3/oauth/#scopes
@@ -370,7 +373,7 @@ public HttpResponse doCommenceLogin(StaplerRequest request, @QueryParameter Stri
370
373
* This is where the user comes back to at the end of the OAuth redirect
371
374
* ping-pong.
372
375
*/
373
- public HttpResponse doFinishLogin (StaplerRequest request )
376
+ public HttpResponse doFinishLogin (StaplerRequest2 request )
374
377
throws IOException {
375
378
String code = request .getParameter ("code" );
376
379
String state = request .getParameter (STATE_ATTRIBUTE );
@@ -435,7 +438,7 @@ public HttpResponse doFinishLogin(StaplerRequest request)
435
438
}
436
439
}
437
440
438
- SecurityListener .fireAuthenticated (new GithubOAuthUserDetails (self .getLogin (), auth .getAuthorities ()));
441
+ SecurityListener .fireAuthenticated2 (new GithubOAuthUserDetails (self .getLogin (), auth .getAuthorities ()));
439
442
440
443
// While LastGrantedAuthorities are triggered by that event, we cannot trigger it there
441
444
// or modifications in organizations will be not reflected when using API Token, due to that caching
@@ -561,7 +564,7 @@ public Authentication authenticate(Authentication authentication)
561
564
GithubSecretStorage .put (user , token .getCredentials ().toString ());
562
565
}
563
566
564
- SecurityListener .fireAuthenticated (new GithubOAuthUserDetails (token .getName (), github .getAuthorities ()));
567
+ SecurityListener .fireAuthenticated2 (new GithubOAuthUserDetails (token .getName (), github .getAuthorities ()));
565
568
566
569
return github ;
567
570
} catch (IOException e ) {
@@ -570,11 +573,11 @@ public Authentication authenticate(Authentication authentication)
570
573
throw new BadCredentialsException (
571
574
"Unexpected authentication type: " + authentication );
572
575
}
573
- }, GithubSecurityRealm .this ::loadUserByUsername );
576
+ }, GithubSecurityRealm .this ::loadUserByUsername2 );
574
577
}
575
578
576
579
@ Override
577
- protected GithubOAuthUserDetails authenticate (String username , String password ) throws AuthenticationException {
580
+ protected GithubOAuthUserDetails authenticate2 (String username , String password ) throws AuthenticationException {
578
581
try {
579
582
GithubAuthenticationToken github = new GithubAuthenticationToken (password , getGithubApiUri ());
580
583
if (username .equals (github .getPrincipal ())) {
@@ -593,12 +596,12 @@ public String getLoginUrl() {
593
596
}
594
597
595
598
@ Override
596
- protected String getPostLogOutUrl ( StaplerRequest req , Authentication auth ) {
599
+ protected String getPostLogOutUrl2 ( StaplerRequest2 req , Authentication auth ) {
597
600
// if we just redirect to the root and anonymous does not have Overall read then we will start a login all over again.
598
601
// we are actually anonymous here as the security context has been cleared
599
602
Jenkins j = Jenkins .get ();
600
603
if (j .hasPermission (Jenkins .READ )) {
601
- return super .getPostLogOutUrl (req , auth );
604
+ return super .getPostLogOutUrl2 (req , auth );
602
605
}
603
606
return req .getContextPath ()+ "/" + GithubLogoutAction .POST_LOGOUT_URL ;
604
607
}
@@ -654,8 +657,8 @@ public DescriptorImpl getDescriptor() {
654
657
* @return userDetails
655
658
*/
656
659
@ Override
657
- public UserDetails loadUserByUsername (String username )
658
- throws UsernameNotFoundException , DataAccessException {
660
+ public UserDetails loadUserByUsername2 (String username )
661
+ throws UsernameNotFoundException {
659
662
//username is in org*team format
660
663
if (username .contains (GithubOAuthGroupDetails .ORG_TEAM_SEPARATOR )) {
661
664
throw new UsernameNotFoundException ("Using org*team format instead of username: " + username );
@@ -672,7 +675,7 @@ public UserDetails loadUserByUsername(String username)
672
675
}
673
676
} catch (IOException | UsernameNotFoundException e ) {
674
677
if (e instanceof IOException ) {
675
- throw new UserMayOrMayNotExistException ("Could not connect to GitHub API server, target URL = " + getGithubApiUri (), e );
678
+ throw new UserMayOrMayNotExistException2 ("Could not connect to GitHub API server, target URL = " + getGithubApiUri (), e );
676
679
} else {
677
680
// user not found so continuing normally re-using the current context holder
678
681
LOGGER .log (Level .FINE , "Attempted to impersonate " + username + " but token in user property was invalid." );
@@ -684,15 +687,27 @@ public UserDetails loadUserByUsername(String username)
684
687
if (token instanceof GithubAuthenticationToken ) {
685
688
authToken = (GithubAuthenticationToken ) token ;
686
689
} else {
687
- throw new UserMayOrMayNotExistException ("Unexpected authentication type: " + token );
690
+ throw new UserMayOrMayNotExistException2 ("Unexpected authentication type: " + token );
688
691
}
689
692
690
693
/**
691
694
* Always lookup the local user first. If we can't resolve it then we can burn an API request to Github for this user
692
695
* Taken from hudson.security.HudsonPrivateSecurityRealm#loadUserByUsername(java.lang.String)
693
696
*/
694
697
if (localUser != null ) {
695
- return new GithubOAuthUserDetails (username , authToken );
698
+ GHUser user ;
699
+ try {
700
+ user = authToken .loadUser (username );
701
+ } catch (IOException e ) {
702
+ throw new UncheckedIOException (e );
703
+ }
704
+ Collection <GrantedAuthority > authorities ;
705
+ if (user != null ) {
706
+ authorities = authToken .getAuthorities ();
707
+ } else {
708
+ authorities = List .of ();
709
+ }
710
+ return new GithubOAuthUserDetails (username , authorities );
696
711
}
697
712
698
713
try {
@@ -709,7 +724,7 @@ public UserDetails loadUserByUsername(String username)
709
724
710
725
return userDetails ;
711
726
} catch (IOException | Error e ) {
712
- throw new DataRetrievalFailureException ("loadUserByUsername (username=" + username +")" , e );
727
+ throw new AuthenticationServiceException ("loadUserByUsername (username=" + username +")" , e );
713
728
}
714
729
}
715
730
@@ -749,8 +764,8 @@ public int hashCode() {
749
764
* @return groupDetails
750
765
*/
751
766
@ Override
752
- public GroupDetails loadGroupByGroupname (String groupName )
753
- throws UsernameNotFoundException , DataAccessException {
767
+ public GroupDetails loadGroupByGroupname2 (String groupName , boolean fetchMembers )
768
+ throws UsernameNotFoundException {
754
769
GithubAuthenticationToken authToken = (GithubAuthenticationToken ) SecurityContextHolder .getContext ().getAuthentication ();
755
770
756
771
if (authToken == null )
@@ -776,7 +791,7 @@ public GroupDetails loadGroupByGroupname(String groupName)
776
791
return new GithubOAuthGroupDetails (ghOrg );
777
792
}
778
793
} catch (Error e ) {
779
- throw new DataRetrievalFailureException ("loadGroupByGroupname (groupname=" + groupName + ")" , e );
794
+ throw new AuthenticationServiceException ("loadGroupByGroupname (groupname=" + groupName + ")" , e );
780
795
}
781
796
}
782
797
0 commit comments