Skip to content

Commit

Permalink
Test JMESPath
Browse files Browse the repository at this point in the history
  • Loading branch information
michael-doubez committed Mar 23, 2024
1 parent 11a1d72 commit dd8eaf2
Show file tree
Hide file tree
Showing 6 changed files with 148 additions and 72 deletions.
129 changes: 73 additions & 56 deletions src/main/java/org/jenkinsci/plugins/oic/OicSecurityRealm.java
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ public OicSecurityRealm(String clientId, String clientSecret, String wellKnownOp
this.setScopes(scopes);
this.endSessionEndpoint = endSessionEndpoint;

if("auto".equals(automanualconfigure) ||
if ("auto".equals(automanualconfigure) ||
(Util.fixNull(automanualconfigure).isEmpty() &&
!Util.fixNull(wellKnownOpenIDConfigurationUrl).isEmpty())) {
this.automanualconfigure = "auto";
Expand Down Expand Up @@ -280,10 +280,10 @@ public OicSecurityRealm(String clientId, String clientSecret, String authorizati
}

protected Object readResolve() {
if(httpTransport==null) {
if (httpTransport==null) {
httpTransport = constructHttpTransport(isDisableSslVerification());
}
if(!Strings.isNullOrEmpty(endSessionUrl)) {
if (!Strings.isNullOrEmpty(endSessionUrl)) {
try {
Field field = getClass().getDeclaredField("endSessionEndpoint");
field.setAccessible(true);
Expand All @@ -294,9 +294,9 @@ protected Object readResolve() {
}

// backward compatibility with wrong groupsFieldName split
if(Strings.isNullOrEmpty(this.groupsFieldName) && !Strings.isNullOrEmpty(this.simpleGroupsFieldName)) {
if (Strings.isNullOrEmpty(this.groupsFieldName) && !Strings.isNullOrEmpty(this.simpleGroupsFieldName)) {
String originalGroupFieldName = this.simpleGroupsFieldName;
if(!Strings.isNullOrEmpty(this.nestedGroupFieldName)) {
if (!Strings.isNullOrEmpty(this.nestedGroupFieldName)) {
originalGroupFieldName += "[]." + this.nestedGroupFieldName;
}
this.setGroupsFieldName(originalGroupFieldName);
Expand Down Expand Up @@ -514,7 +514,7 @@ private void setWellKnownExpires(HttpHeaders headers) {

@DataBoundSetter
public void setWellKnownOpenIDConfigurationUrl(String wellKnownOpenIDConfigurationUrl) {
if( this.isAutoConfigure() ||
if ( this.isAutoConfigure() ||
(this.automanualconfigure.isEmpty() &&
!Util.fixNull(wellKnownOpenIDConfigurationUrl).isEmpty())) {
this.automanualconfigure = "auto";
Expand All @@ -527,11 +527,11 @@ public void setWellKnownOpenIDConfigurationUrl(String wellKnownOpenIDConfigurati
}

private void applyOverrideScopes() {
if(!"auto".equals(this.automanualconfigure) || this.overrideScopes == null) {
if (!"auto".equals(this.automanualconfigure) || this.overrideScopes == null) {
// only applies in "auto" mode when overrideScopes defined
return;
}
if(this.scopes == null) {
if (this.scopes == null) {
this.scopes = overrideScopes;
return;
}
Expand Down Expand Up @@ -570,18 +570,23 @@ public void setEmailFieldName(String emailFieldName) {
this.emailFieldExpr = compileJMESPath(this.emailFieldName, "email field");
}

static
protected Expression<Object> compileJMESPath(String str, String logComment) {
protected static Expression<Object> compileJMESPath(String str, String logComment) {
if (str == null) {
return null;
}

Expression<Object> expr = JMESPATH.compile(str);
if (expr == null && logComment != null) {
LOGGER.warning(logComment + " with config '" + str + "' is an invalid JMESPath expression ");
try {
Expression<Object> expr = JMESPATH.compile(str);
if (expr == null && logComment != null) {

Check warning on line 580 in src/main/java/org/jenkinsci/plugins/oic/OicSecurityRealm.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 580 is only partially covered, 3 branches are missing
LOGGER.warning(logComment + " with config '" + str + "' is an invalid JMESPath expression ");

Check warning on line 581 in src/main/java/org/jenkinsci/plugins/oic/OicSecurityRealm.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered line

Line 581 is not covered by tests

Check warning on line 581 in src/main/java/org/jenkinsci/plugins/oic/OicSecurityRealm.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/jenkinsci/plugins/oic/OicSecurityRealm.java#L581

Added line #L581 was not covered by tests
}
return expr;
} catch (RuntimeException e) {
if (logComment != null) {
LOGGER.warning(logComment + " config failed " + e.toString());
}
}

return expr;
return null;
}

private Object applyJMESPath(Expression<Object> expression, GenericJson json) {
Expand Down Expand Up @@ -631,7 +636,7 @@ public void setEscapeHatchGroup(String escapeHatchGroup) {

@DataBoundSetter
public void setOverrideScopesDefined(boolean overrideScopesDefined) {
if(overrideScopesDefined) {
if (overrideScopesDefined) {
this.overrideScopesDefined = Boolean.TRUE;
} else {
this.overrideScopesDefined = Boolean.FALSE;
Expand All @@ -642,7 +647,7 @@ public void setOverrideScopesDefined(boolean overrideScopesDefined) {

@DataBoundSetter
public void setOverrideScopes(String overrideScopes) {
if(this.overrideScopesDefined == null || this.overrideScopesDefined) {
if (this.overrideScopesDefined == null || this.overrideScopesDefined) {
this.overrideScopes = Util.fixEmptyAndTrim(overrideScopes);
this.applyOverrideScopes();
}
Expand Down Expand Up @@ -696,11 +701,11 @@ public Authentication authenticate(Authentication authentication) throws Authent

if (authentication instanceof UsernamePasswordAuthenticationToken && escapeHatchEnabled) {
randomWait(); // to slowdown brute forcing
if( authentication.getPrincipal().toString().equals(escapeHatchUsername) &&
if ( authentication.getPrincipal().toString().equals(escapeHatchUsername) &&
authentication.getCredentials().toString().equals(Secret.toString(escapeHatchSecret))) {
List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
grantedAuthorities.add(SecurityRealm.AUTHENTICATED_AUTHORITY2);
if(isNotBlank(escapeHatchGroup)) {
if (isNotBlank(escapeHatchGroup)) {
grantedAuthorities.add(new SimpleGrantedAuthority(escapeHatchGroup));
}
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
Expand Down Expand Up @@ -763,7 +768,7 @@ protected AuthorizationCodeFlow buildAuthorizationCodeFlow() {
)
.setScopes(Arrays.asList(this.getScopes()));

if(pkceEnabled) {
if (pkceEnabled) {
builder.enablePKCE();
}

Expand Down Expand Up @@ -826,7 +831,7 @@ public HttpResponse onSuccess(String authorizationCode) {
return HttpResponses.errorWithoutStack(401, "Unauthorized");
}

if(failedCheckOfTokenField(idToken)) {
if (failedCheckOfTokenField(idToken)) {
return HttpResponses.errorWithoutStack(401, "Unauthorized");
}

Expand All @@ -838,7 +843,7 @@ public HttpResponse onSuccess(String authorizationCode) {
}

String username = determineStringField(userNameFieldExpr, idToken, userInfo);
if(username == null) {
if (username == null) {
return HttpResponses.error(500,"no field '" + userNameField + "' was supplied in the UserInfo or the IdToken payload to be used as the username");
}

Expand Down Expand Up @@ -892,17 +897,17 @@ public void initialize(HttpRequest request) throws IOException {
}

private boolean failedCheckOfTokenField(IdToken idToken) {
if( tokenFieldToCheckKey == null ||
if ( tokenFieldToCheckKey == null ||

Check warning on line 900 in src/main/java/org/jenkinsci/plugins/oic/OicSecurityRealm.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 900 is only partially covered, 3 branches are missing
tokenFieldToCheckValue == null) {
return false;
}

if(idToken == null) {
if (idToken == null) {
return true;
}

String value = getStringField(idToken.getPayload(), tokenFieldToCheckExpr);

Check warning on line 909 in src/main/java/org/jenkinsci/plugins/oic/OicSecurityRealm.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/jenkinsci/plugins/oic/OicSecurityRealm.java#L909

Added line #L909 was not covered by tests
if(value == null) {
if (value == null) {
return true;

Check warning on line 911 in src/main/java/org/jenkinsci/plugins/oic/OicSecurityRealm.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/jenkinsci/plugins/oic/OicSecurityRealm.java#L911

Added line #L911 was not covered by tests
}

Expand All @@ -912,7 +917,7 @@ private boolean failedCheckOfTokenField(IdToken idToken) {
private UsernamePasswordAuthenticationToken loginAndSetUserData(String userName, IdToken idToken, GenericJson userInfo) throws IOException {

List<GrantedAuthority> grantedAuthorities = determineAuthorities(idToken, userInfo);
if(LOGGER.isLoggable(Level.FINEST)) {
if (LOGGER.isLoggable(Level.FINEST)) {
StringBuilder grantedAuthoritiesAsString = new StringBuilder(userName);
grantedAuthoritiesAsString.append(" (");

Check warning on line 922 in src/main/java/org/jenkinsci/plugins/oic/OicSecurityRealm.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered lines

Lines 905-922 are not covered by tests

Check warning on line 922 in src/main/java/org/jenkinsci/plugins/oic/OicSecurityRealm.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/jenkinsci/plugins/oic/OicSecurityRealm.java#L921-L922

Added lines #L921 - L922 were not covered by tests
for(GrantedAuthority grantedAuthority : grantedAuthorities) {
Expand All @@ -927,7 +932,7 @@ private UsernamePasswordAuthenticationToken loginAndSetUserData(String userName,
SecurityContextHolder.getContext().setAuthentication(token);

User user = User.get2(token);
if(user == null){
if (user == null){
// should not happen
throw new IOException("Cannot set OIDC property on anonymous user");
}
Expand Down Expand Up @@ -972,9 +977,9 @@ private String determineStringField(Expression<Object> fieldExpr, IdToken idToke
}

protected String getStringField(Object object, Expression<Object> fieldExpr) {
if( object != null && fieldExpr != null) {
if ( object != null && fieldExpr != null) {

Check warning on line 980 in src/main/java/org/jenkinsci/plugins/oic/OicSecurityRealm.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 980 is only partially covered, 2 branches are missing
Object value = fieldExpr.search(object);
if( (value != null) &&
if ( (value != null) &&

Check warning on line 982 in src/main/java/org/jenkinsci/plugins/oic/OicSecurityRealm.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 982 is only partially covered, one branch is missing
!(value instanceof Map) &&
!(value instanceof List)) {
return String.valueOf(value);
Expand All @@ -988,7 +993,7 @@ private List<GrantedAuthority> determineAuthorities(IdToken idToken, GenericJson
List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
grantedAuthorities.add(SecurityRealm.AUTHENTICATED_AUTHORITY2);
if (this.groupsFieldExpr == null) {
if(this.groupsFieldName == null) {
if (this.groupsFieldName == null) {

Check warning on line 996 in src/main/java/org/jenkinsci/plugins/oic/OicSecurityRealm.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 996 is only partially covered, one branch is missing
LOGGER.fine("Not adding groups because groupsFieldName is not set. groupsFieldName=" + groupsFieldName);
} else {
LOGGER.fine("Not adding groups because groupsFieldName is invalid. groupsFieldName=" + groupsFieldName);

Check warning on line 999 in src/main/java/org/jenkinsci/plugins/oic/OicSecurityRealm.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered line

Line 999 is not covered by tests

Check warning on line 999 in src/main/java/org/jenkinsci/plugins/oic/OicSecurityRealm.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/jenkinsci/plugins/oic/OicSecurityRealm.java#L999

Added line #L999 was not covered by tests
Expand All @@ -999,19 +1004,19 @@ private List<GrantedAuthority> determineAuthorities(IdToken idToken, GenericJson
Object groupsObject = null;

// userInfo has precedence when available
if(userInfo != null) {
if (userInfo != null) {
groupsObject = this.groupsFieldExpr.search(userInfo);
}
if(groupsObject == null && idToken != null) {
if (groupsObject == null && idToken != null) {

Check warning on line 1010 in src/main/java/org/jenkinsci/plugins/oic/OicSecurityRealm.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 1010 is only partially covered, one branch is missing
groupsObject = this.groupsFieldExpr.search(idToken.getPayload());
}
if(groupsObject == null) {
if (groupsObject == null) {
LOGGER.warning("idToken and userInfo did not contain group field name: " + this.groupsFieldName);
return grantedAuthorities;
}

List<String> groupNames = ensureString(groupsObject);
if(groupNames.isEmpty()){
if (groupNames.isEmpty()){
LOGGER.warning("Could not identify groups in " + groupsFieldName + "=" + groupsObject.toString());
return grantedAuthorities;
}
Expand Down Expand Up @@ -1072,7 +1077,7 @@ private List<String> ensureString(Object field) {
@Restricted(DoNotUse.class) // stapler only
public void doLogout(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException {
OicSession oicSession = OicSession.getCurrent();
if(oicSession!=null) {
if (oicSession!=null) {
// session will be invalidated but we still need this data for our redirect.
req.setAttribute(ID_TOKEN_REQUEST_ATTRIBUTE, oicSession.getIdToken());
req.setAttribute(STATE_REQUEST_ATTRIBUTE, oicSession.getState());
Expand Down Expand Up @@ -1131,7 +1136,7 @@ private String buildOAuthRedirectUrl() throws NullPointerException {
*/
public HttpResponse doFinishLogin(StaplerRequest request) throws IOException {
OicSession currentSession = OicSession.getCurrent();
if(currentSession==null) {
if (currentSession==null) {
LOGGER.fine("No session to resume (perhaps jenkins was restarted?)");
return HttpResponses.errorWithoutStack(401, "Unauthorized");
}
Expand Down Expand Up @@ -1186,7 +1191,7 @@ public FormValidation doCheckWellKnownOpenIDConfigurationUrl(@QueryParameter Str
WellKnownOpenIDConfigurationResponse config = GsonFactory.getDefaultInstance()
.fromInputStream(response.getContent(), Charset.defaultCharset(),
WellKnownOpenIDConfigurationResponse.class);
if(config.getAuthorizationEndpoint() == null || config.getTokenEndpoint() == null) {
if (config.getAuthorizationEndpoint() == null || config.getTokenEndpoint() == null) {
return FormValidation.warning(Messages.OicSecurityRealm_URLNotAOpenIdEnpoint());
}

Expand Down Expand Up @@ -1240,22 +1245,13 @@ public FormValidation doCheckAuthorizationServerUrl(@QueryParameter String autho
}
}

@RequirePOST
public FormValidation doCheckUserNameField(@QueryParameter String userNameField) {
Jenkins.get().checkPermission(Jenkins.ADMINISTER);
if (Util.fixEmptyAndTrim(userNameField) == null) {
return FormValidation.ok(Messages.OicSecurityRealm_UsingDefaultUsername());
}
return FormValidation.ok();
}

@RequirePOST
public FormValidation doCheckScopes(@QueryParameter String scopes) {
Jenkins.get().checkPermission(Jenkins.ADMINISTER);
if (Util.fixEmptyAndTrim(scopes) == null) {
return FormValidation.ok(Messages.OicSecurityRealm_UsingDefaultScopes());
}
if(!scopes.toLowerCase().contains("openid")) {
if (!scopes.toLowerCase().contains("openid")) {
return FormValidation.warning(Messages.OicSecurityRealm_RUSureOpenIdNotInScope());
}
return FormValidation.ok();
Expand All @@ -1267,7 +1263,7 @@ public FormValidation doCheckOverrideScopes(@QueryParameter String overrideScope
if (Util.fixEmptyAndTrim(overrideScopes) == null) {
return FormValidation.ok(Messages.OicSecurityRealm_UsingDefaultScopes());
}
if(!overrideScopes.toLowerCase().contains("openid")) {
if (!overrideScopes.toLowerCase().contains("openid")) {
return FormValidation.warning(Messages.OicSecurityRealm_RUSureOpenIdNotInScope());
}
return FormValidation.ok();
Expand Down Expand Up @@ -1303,17 +1299,38 @@ public FormValidation doCheckPostLogoutRedirectUrl(@QueryParameter String postLo
}

@RequirePOST
// method to check groupsFieldName matches the required format
// can contain the substring "[]." at most once.
// e.g. "groups", "groups[].name" are valid
// groups[].name[].id is not valid
public FormValidation doCheckUserNameField(@QueryParameter String userNameField) {
return this.doCheckFieldName(userNameField, FormValidation.ok(Messages.OicSecurityRealm_UsingDefaultUsername()));
}

@RequirePOST
public FormValidation doCheckFullNameFieldName(@QueryParameter String fullNameFieldName) {
return this.doCheckFieldName(fullNameFieldName, FormValidation.ok());
}

@RequirePOST
public FormValidation doCheckEmailFieldName(@QueryParameter String emailFieldName) {
return this.doCheckFieldName(emailFieldName, FormValidation.ok());
}

@RequirePOST
public FormValidation doCheckGroupsFieldName(@QueryParameter String groupsFieldName) {
return this.doCheckFieldName(groupsFieldName, FormValidation.ok());
}

@RequirePOST
public FormValidation doCheckTokenFieldToCheckKey(@QueryParameter String tokenFieldToCheckKey) {
return this.doCheckFieldName(tokenFieldToCheckKey, FormValidation.ok());
}

// method to check fieldName matches JMESPath format
private FormValidation doCheckFieldName(String fieldName, FormValidation validIfNull) {
Jenkins.get().checkPermission(Jenkins.ADMINISTER);
if (Util.fixEmptyAndTrim(groupsFieldName) == null) {
return FormValidation.ok();
if (Util.fixEmptyAndTrim(fieldName) == null) {
return validIfNull;
}
if (groupsFieldName.split("\\[\\]\\.").length > 2) {
return FormValidation.error(Messages.OicSecurityRealm_InvalidGroupsFieldName());
if (OicSecurityRealm.compileJMESPath(fieldName, null) == null) {
return FormValidation.error(Messages.OicSecurityRealm_InvalidFieldName());
}
return FormValidation.ok();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@ OicSecurityRealm.UsingDefaultUsername = Using ''sub''.
OicSecurityRealm.UsingDefaultScopes = Using ''openid email''.
OicSecurityRealm.RUSureOpenIdNotInScope = Are you sure you don''t want to include ''openid'' as an scope?
OicSecurityRealm.EndSessionURLKeyRequired = End Session URL Key is required.
OicSecurityRealm.InvalidGroupsFieldName = Invalid groups field name - may only contain letters, numbers, and underscores and one instance of [].
OicSecurityRealm.InvalidFieldName = Invalid field name - must be a valid JMESPath expression.
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,3 @@ OicSecurityRealm.UsingDefaultUsername = \u4f7f\u7528 sub
OicSecurityRealm.UsingDefaultScopes = \u4f7f\u7528 openid \u96fb\u5b50\u4fe1\u7bb1
OicSecurityRealm.RUSureOpenIdNotInScope = \u662f\u5426\u78ba\u8a8d\u4e0d\u5c07 \u300copenid\u300d\u4f5c\u70ba\u4e00\u500b\u7bc4\u570d scope \uff1f
OicSecurityRealm.EndSessionURLKeyRequired = \u9700\u8981\u63d0\u4f9b\u7d42\u6b62 Session \u4e4b Url \u91d1\u9470
OicSecurityRealm.InvalidGroupsFieldName = \u7121\u6548\u7684\u7fa4\u7d44\u6b04\u4f4d\u540d\u7a31 - \u53ea\u80fd\u5305\u542b\u5b57\u6bcd\u3001\u6578\u5b57\u3001\u5e95\u7dda\u548c\u4e00\u500b [] \u5be6\u4f8b
Loading

0 comments on commit dd8eaf2

Please sign in to comment.