Class GoogleGroupsSecurityRealm

java.lang.Object
hudson.security.SecurityRealm
hudson.security.AbstractPasswordBasedSecurityRealm
io.jenkins.plugins.googlegroupsoauth.GoogleGroupsSecurityRealm
All Implemented Interfaces:
ExtensionPoint, Describable<SecurityRealm>

public class GoogleGroupsSecurityRealm extends AbstractPasswordBasedSecurityRealm
Hybrid security realm: sign in with Google OAuth (with the user's direct Google Group memberships — group emails — as Jenkins authorities) or with a local username and password. Group resolution uses the logged-in user's own OAuth token against the Cloud Identity API — no service account or Workspace admin grant needed.

The standard Jenkins login page is kept: its username/password form is checked against the HudsonPrivateSecurityRealm.Details property stored on existing local user records (created under the local-database realm), which is the break-glass path if Google OAuth is down or misconfigured. GoogleFederatedLoginService adds the "Sign in with Google" button underneath that form.

Google user id = full email (lowercase), bound to the Google account's stable sub on first login (GoogleAccountProperty). Authorities are re-resolved at each Google login and live for the session; API-token requests get the authorities recorded at the user's last Google login via LastGrantedAuthoritiesProperty, but only for getGroupAuthorityMaxAgeDays() days, because Jenkins cannot see later removals from Google Groups or from the Workspace. Pre-existing local users' API tokens keep working because token authentication checks stored user records independent of this realm.

  • Constructor Details

    • GoogleGroupsSecurityRealm

      @DataBoundConstructor public GoogleGroupsSecurityRealm(String clientId, Secret clientSecret, String hostedDomain)
  • Method Details

    • getClientId

      public String getClientId()
    • getClientSecret

      public Secret getClientSecret()
    • getHostedDomain

      public String getHostedDomain()
    • getGroupIncludePattern

      @CheckForNull public String getGroupIncludePattern()
    • setGroupIncludePattern

      @DataBoundSetter public void setGroupIncludePattern(@CheckForNull String groupIncludePattern)
    • getOnGroupLookupFailure

      @NonNull public GroupLookupFailurePolicy getOnGroupLookupFailure()
    • setOnGroupLookupFailure

      @DataBoundSetter public void setOnGroupLookupFailure(@NonNull GroupLookupFailurePolicy onGroupLookupFailure)
    • getGroupAuthorityMaxAgeDays

      public int getGroupAuthorityMaxAgeDays()
      How many days the group authorities recorded at a user's last Google login stay valid for API-token requests. Jenkins never re-checks Google between logins, so without a limit someone removed from a group (or from the Workspace) keeps that group's permissions through their API tokens indefinitely. After expiry, token requests carry only authenticated until the user signs in with Google again. 0 disables expiry. Sessions are not affected; they end with the normal session timeout.
    • setGroupAuthorityMaxAgeDays

      @DataBoundSetter public void setGroupAuthorityMaxAgeDays(int groupAuthorityMaxAgeDays)
    • allowsSignup

      public boolean allowsSignup()
      Overrides:
      allowsSignup in class SecurityRealm
    • authenticate2

      protected org.springframework.security.core.userdetails.UserDetails authenticate2(String username, String password) throws org.springframework.security.core.AuthenticationException
      Break-glass password login on the standard Jenkins login form: checks the candidate password against the HudsonPrivateSecurityRealm.Details property stored on the user record (present on users created under the local-database realm). Users without a stored password — i.e. everyone who only ever logged in via Google — cannot log in by password at all.
      Overrides:
      authenticate2 in class AbstractPasswordBasedSecurityRealm
      Throws:
      org.springframework.security.core.AuthenticationException
    • loadUserByUsername2

      public org.springframework.security.core.userdetails.UserDetails loadUserByUsername2(String username) throws org.springframework.security.core.userdetails.UsernameNotFoundException
      Returns the user if a Jenkins user record exists — this keeps API-token users resolvable and lets authorization strategies validate names. Authorities come from the last interactive login (LastGrantedAuthoritiesProperty), so API-token requests carry the user's group authorities too, until getGroupAuthorityMaxAgeDays() days after their last Google login. Local users (no GoogleAccountProperty) never expire: their access comes from user: grants, not Google Groups.
      Overrides:
      loadUserByUsername2 in class AbstractPasswordBasedSecurityRealm
      Throws:
      org.springframework.security.core.userdetails.UsernameNotFoundException
    • loadGroupByGroupname2

      public GroupDetails loadGroupByGroupname2(String groupname, boolean fetchMembers) throws org.springframework.security.core.userdetails.UsernameNotFoundException
      Accepts any well-formed group email. We cannot enumerate or validate groups server-side in user-token mode, so this never makes a directory call.
      Overrides:
      loadGroupByGroupname2 in class AbstractPasswordBasedSecurityRealm
      Throws:
      org.springframework.security.core.userdetails.UsernameNotFoundException
    • doCommenceLogin

      public org.kohsuke.stapler.HttpResponse doCommenceLogin(org.kohsuke.stapler.StaplerRequest2 request, @QueryParameter String from, @Header("Referer") String referer) throws IOException
      Starts the OAuth code flow. Reached by the "Sign in with Google" link, so it must accept GET and anonymous callers. Its only effect is storing a fresh state and PKCE verifier in the caller's own session; forcing a victim here merely restarts their login.
      Throws:
      IOException
    • doFinishLogin

      public org.kohsuke.stapler.HttpResponse doFinishLogin(org.kohsuke.stapler.StaplerRequest2 request, @QueryParameter String code, @QueryParameter String state, @QueryParameter String error) throws IOException
      OAuth redirect URI. Google redirects the browser here with a GET request (RFC 6749 section 4.1.2) before the user is logged in, so neither POST nor a permission check is possible. CSRF protection is the OAuth state parameter, which must match the single-use value stored in the caller's session, plus PKCE; the token request to Google's fixed endpoint only happens after that check.
      Throws:
      IOException