Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow SecurityRealm#getUserIdStrategy to be customizable #493

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 20 additions & 18 deletions docs/configuration/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

The OpenID Connect authentication plugin tries to support a wide range
of OpenID providers. The configuration reflects the various ways the
plugin accomodates their differences and provide a way to select the
plugin accommodates their differences and provide a way to select the
information to extract.

There are specifics instructions for well known providers:
Expand All @@ -21,7 +21,7 @@ which will also help discovering your settings

From 1.5 and onward the well known configuration location may be used to
populate the configuration simplifying the configuration greatly.
The switch between modes is controled by the `serverConfiguration` field
The switch between modes is controlled by the `serverConfiguration` field

| field | format | description |
|----------------------|---------|------------------------------------------------------------------------------------------------------------------------------------------------------|
Expand Down Expand Up @@ -69,27 +69,28 @@ If the JWKS endpoint is configured, JWS' signatures will be verified
Providers have some variation in their implementation of OpenID Connect
or some oddities they required.

| field | format | description |
|---------------------------|----------|-----------------------------------------------------------------------------------------------------|
| logoutFromOpenidProvider | boolean | Enable the logout from provider when user logout from Jenkisn. |
| sendScopesInTokenRequest | boolean | Some providers expects scopes to be sent in token request |
| rootURLFromRequest | boolean | When computing Jenkins redirect, the root url is either deduced from configured root url or request |
| field | format | description |
|--------------------------|---------|-----------------------------------------------------------------------------------------------------|
| logoutFromOpenidProvider | boolean | Enable the logout from provider when user logout from Jenkins. |
| sendScopesInTokenRequest | boolean | Some providers expects scopes to be sent in token request |
| rootURLFromRequest | boolean | When computing Jenkins redirect, the root url is either deduced from configured root url or request |
| customUserIdStrategy | enum | ID strategy that should be used for turning the user name into an ID. Default is: case-insensitive. |

### Security configuration

Most security feature are activated by default if possible.

| field | format | description |
|----------------------------------------|-----------|-------------------------------------------------------------------------------------------------------------------------------|
| allowTokenAccessWithoutOicSession | boolean | Allows Jenkins API token based access even if the associated user has completly logged out from Jenkins and the OIC Provider |
| allowedTokenExpirationClockSkewSeconds | integer | Additional number of seconds to add to access token expiry time in case of clock sync issues |
| disableSslVerification | boolean | Disable SSL verification (in case of self signed certificates by example) |
| nonceDisabled | boolean | Disable nonce verification |
| pkceEnable | boolean | Enable PKCE challenge |
| disableTokenVerification | boolean | Disable IdToken and UserInfo verification (not recommended) |
| tokenFieldToCheckKey | jmespath | Field(s) to check to authorize user |
| tokenFieldToCheckValue | string | TokenFieldToCheckValue expected value |
| tokenExpirationCheckDisabled | boolean | Disable checking of token expiration |
| field | format | description |
|----------------------------------------|----------|-------------------------------------------------------------------------------------------------------------------------------|
| allowTokenAccessWithoutOicSession | boolean | Allows Jenkins API token based access even if the associated user has completely logged out from Jenkins and the OIC Provider |
| allowedTokenExpirationClockSkewSeconds | integer | Additional number of seconds to add to access token expiry time in case of clock sync issues |
| disableSslVerification | boolean | Disable SSL verification (in case of self signed certificates by example) |
| nonceDisabled | boolean | Disable nonce verification |
| pkceEnable | boolean | Enable PKCE challenge |
| disableTokenVerification | boolean | Disable IdToken and UserInfo verification (not recommended) |
| tokenFieldToCheckKey | jmespath | Field(s) to check to authorize user |
| tokenFieldToCheckValue | string | TokenFieldToCheckValue expected value |
| tokenExpirationCheckDisabled | boolean | Disable checking of token expiration |

## User information

Expand Down Expand Up @@ -142,6 +143,7 @@ jenkins:
rootURLFromRequest: <boolean>
sendScopesInTokenRequest: <boolean>
postLogoutRedirectUrl: <url>
customUserIdStrategy: <string:enum>
# Security
allowTokenAccessWithoutOicSession: <boolean>
allowedTokenExpirationClockSkewSeconds: <integer>
Expand Down
31 changes: 30 additions & 1 deletion src/main/java/org/jenkinsci/plugins/oic/OicSecurityRealm.java
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import jenkins.model.IdStrategy;
import jenkins.model.Jenkins;
import jenkins.security.ApiTokenProperty;
import jenkins.security.FIPS140;
Expand Down Expand Up @@ -133,6 +134,7 @@
import org.springframework.security.crypto.bcrypt.BCrypt;
import org.springframework.util.Assert;

import static jenkins.model.IdStrategy.CASE_INSENSITIVE;
import static org.apache.commons.lang.StringUtils.isNotBlank;

/**
Expand All @@ -159,7 +161,19 @@ public static enum TokenAuthMethod {
ClientAuthenticationMethod toClientAuthenticationMethod() {
return clientAuthMethod;
}
};
}

public enum UserIdStrategy {
defaultCase(CASE_INSENSITIVE),
caseInSensitive(CASE_INSENSITIVE),
caseSensitive(new IdStrategy.CaseSensitive());

final IdStrategy idStrategy;

UserIdStrategy(IdStrategy idStrategy) {
this.idStrategy = idStrategy;
}
}

private static final String ID_TOKEN_REQUEST_ATTRIBUTE = "oic-id-token";
private static final String NO_SECRET = "none";
Expand Down Expand Up @@ -192,6 +206,7 @@ ClientAuthenticationMethod toClientAuthenticationMethod() {
@Deprecated
private transient String userInfoServerUrl;

private transient UserIdStrategy customUserIdStrategy = UserIdStrategy.defaultCase;
private String userNameField = "sub";
private transient Expression<Object> userNameFieldExpr = null;
private String tokenFieldToCheckKey = null;
Expand Down Expand Up @@ -323,6 +338,11 @@ public OicSecurityRealm(
this.serverConfiguration = serverConfiguration;
}

@Override
public IdStrategy getUserIdStrategy() {
return customUserIdStrategy == null ? UserIdStrategy.defaultCase.idStrategy : customUserIdStrategy.idStrategy;
}

@SuppressWarnings("deprecated")
protected Object readResolve() throws ObjectStreamException {
// Fail if migrating to a FIPS non-compliant config
Expand Down Expand Up @@ -410,6 +430,10 @@ public OicServerConfiguration getServerConfiguration() {
return serverConfiguration;
}

public UserIdStrategy getCustomUserIdStrategy() {
return customUserIdStrategy;
}

public String getUserNameField() {
return userNameField;
}
Expand Down Expand Up @@ -689,6 +713,11 @@ protected OidcClient buildOidcClient() {
return client;
}

@DataBoundSetter
public void setCustomUserIdStrategy(UserIdStrategy customUserIdStrategy) {
this.customUserIdStrategy = customUserIdStrategy;
}

@DataBoundSetter
public void setUserNameField(String userNameField) {
this.userNameField = Util.fixNull(Util.fixEmptyAndTrim(userNameField), "sub");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@
<f:entry title="${%PostLogoutRedirectUrl}" field="postLogoutRedirectUrl" class="endSessionConfig">
<f:textbox />
</f:entry>
<f:entry title="${%IdStrategy}" field="customUserIdStrategy">
<f:radioBlock title="${%IdStrategyCaseInSensitive}" name="customUserIdStrategy"
checked="${instance.customUserIdStrategy == null || instance.customUserIdStrategy == 'caseInSensitive'}" value="caseInSensitive" inline="true" help="${null}"/>
<f:radioBlock title="${%IdStrategyCaseSensitive}" name="customUserIdStrategy"
checked="${instance.customUserIdStrategy == 'caseSensitive'}" value="caseSensitive" inline="true" help="${null}"/>
</f:entry>

<f:advanced title="${%SecurityConfiguration}">
<f:entry title="${%TokenFieldKeyToCheck}" field="tokenFieldToCheckKey">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ EnablePKCE=Enable Proof Key for Code Exchange (PKCE)
FullnameFieldName=Full name field name
Group=Group
GroupsFieldName=Groups field name
IdStrategy=ID Strategy
IdStrategyCaseInSensitive=Case Insensitive Strategy
IdStrategyCaseSensitive=Case Sensitive Strategy
LogoutFromOpenIDProvider=Logout from OpenID Provider
PostLogoutRedirectUrl=Post logout redirect URL
Secret=Secret
Expand All @@ -24,5 +27,5 @@ TokenFieldValueToCheck=Token Field Value To Check
UseRootUrlFromRequest=Use Root URL from request
UserFields=User fields
Username=Username
UsernameFieldName=User name field name
UsernameFieldName=Username field name
WellknownConfigurationEndpoint=Well-known configuration endpoint
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<div>
Required. ID strategy that should be used for turning the username into an ID. Default is: case-insensitive.
<br>
The ID strategy should match with the username case-sensitivity handling of the configured Identity Provider.
</div>
Loading
Loading