-
Notifications
You must be signed in to change notification settings - Fork 96
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
15 changed files
with
341 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
55 changes: 55 additions & 0 deletions
55
src/main/java/org/jenkinsci/plugins/oic/monitor/OicIdStrategyMonitor.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
package org.jenkinsci.plugins.oic.monitor; | ||
|
||
import com.google.common.annotations.VisibleForTesting; | ||
import hudson.Extension; | ||
import hudson.ExtensionList; | ||
import hudson.model.AdministrativeMonitor; | ||
import hudson.security.SecurityRealm; | ||
import java.io.IOException; | ||
import jenkins.model.Jenkins; | ||
import org.jenkinsci.plugins.oic.Messages; | ||
import org.jenkinsci.plugins.oic.OicSecurityRealm; | ||
import org.kohsuke.accmod.Restricted; | ||
import org.kohsuke.accmod.restrictions.NoExternalUse; | ||
import org.kohsuke.stapler.HttpResponse; | ||
import org.kohsuke.stapler.HttpResponses; | ||
import org.kohsuke.stapler.interceptor.RequirePOST; | ||
|
||
@Extension | ||
@Restricted(NoExternalUse.class) | ||
public class OicIdStrategyMonitor extends AdministrativeMonitor { | ||
|
||
// if null, means not evaluated yet | ||
Boolean missingIdStrategy; | ||
|
||
public OicIdStrategyMonitor() {} | ||
|
||
@VisibleForTesting | ||
protected static OicIdStrategyMonitor get() { | ||
return ExtensionList.lookupSingleton(OicIdStrategyMonitor.class); | ||
} | ||
|
||
@Override | ||
public String getDisplayName() { | ||
return Messages.OicSecurityRealm_monitor_DisplayName(); | ||
} | ||
|
||
@Override | ||
public boolean isActivated() { | ||
if (!Boolean.FALSE.equals(missingIdStrategy)) { | ||
SecurityRealm securityRealm = Jenkins.get().getSecurityRealm(); | ||
if (securityRealm instanceof OicSecurityRealm) { | ||
missingIdStrategy = ((OicSecurityRealm) securityRealm).isMissingIdStrategy(); | ||
} else { | ||
missingIdStrategy = Boolean.FALSE; | ||
} | ||
} | ||
return missingIdStrategy; | ||
} | ||
|
||
@RequirePOST | ||
public HttpResponse doForward() throws IOException { | ||
Jenkins.get().checkPermission(Jenkins.ADMINISTER); | ||
return HttpResponses.redirectViaContextPath("configureSecurity"); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
4 changes: 4 additions & 0 deletions
4
src/main/resources/org/jenkinsci/plugins/oic/monitor/OicIdStrategyMonitor/description.jelly
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
<?jelly escape-by-default='true'?> | ||
<j:jelly xmlns:j="jelly:core" xmlns:l="/lib/layout" xmlns:f="/lib/form" xmlns:i="jelly:fmt" xmlns:st="jelly:stapler"> | ||
${%blurb} | ||
</j:jelly> |
1 change: 1 addition & 0 deletions
1
...n/resources/org/jenkinsci/plugins/oic/monitor/OicIdStrategyMonitor/description.properties
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
blurb=The OpenId Connect Security Realm's "Username case sensitivity" and "Group name case sensitivity" options have not been configured. |
14 changes: 14 additions & 0 deletions
14
src/main/resources/org/jenkinsci/plugins/oic/monitor/OicIdStrategyMonitor/message.jelly
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
<?jelly escape-by-default='true'?> | ||
<j:jelly xmlns:j="jelly:core" xmlns:f="/lib/form" xmlns:l="/lib/layout"> | ||
<div class="alert alert-warning"> | ||
<l:isAdmin> | ||
<form method="post" action="${rootURL}/${it.url}/forward"> | ||
<f:submit value="${%configureSecurityRealm}"/> | ||
</form> | ||
</l:isAdmin> | ||
<j:set var="actionAnchor"> | ||
<a href="${rootURL}/configure">${%actionUrlContent}</a> | ||
</j:set> | ||
${%blurb} | ||
</div> | ||
</j:jelly> |
6 changes: 6 additions & 0 deletions
6
src/main/resources/org/jenkinsci/plugins/oic/monitor/OicIdStrategyMonitor/message.properties
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
blurb=\ | ||
<p>The Openid Connect Security Realm was configured before the introduction of the "Username case sensitivity" and "Group name case sensitivity" options.</p> \ | ||
<p>As a result, the current configuration treats these values as case-insensitive, whereas the default for new configurations is case-sensitive.</p> \ | ||
<p>This difference could introduce a security vulnerability depending on your specific use case. For further information, refer to the <a href="https://www.jenkins.io/security/advisory/2025-01-22/#SECURITY-3461">security advisory</a>. Please review and select the appropriate case sensitivity settings for your environment, then save the updated security realm configuration.</p> \ | ||
<p>Warning: Switching from case-insensitive to case-sensitive behavior can be a lossy operation if there are mixed-case usernames or group names. Users with externally-defined mixed-case names will effectively be treated as new users they next time they log in, and will lose their existing user preferences. Group-related configurations in Jenkins for externally-defined groups with mixed case names will no longer apply to members of those external groups. Users with mixed-case names previously defined in groups in Jenkins will also no longer be considered members of those groups after the switch.</p> | ||
configureSecurityRealm=Configure the Security Realm |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
117 changes: 117 additions & 0 deletions
117
src/test/java/org/jenkinsci/plugins/oic/OicSecurityRealmIdStrategyTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
package org.jenkinsci.plugins.oic; | ||
|
||
import com.github.tomakehurst.wiremock.core.WireMockConfiguration; | ||
import com.github.tomakehurst.wiremock.junit.WireMockRule; | ||
import hudson.model.User; | ||
import hudson.security.SecurityRealm; | ||
import jenkins.model.IdStrategy; | ||
import jenkins.model.Jenkins; | ||
import org.junit.Rule; | ||
import org.junit.Test; | ||
import org.jvnet.hudson.test.Issue; | ||
import org.jvnet.hudson.test.JenkinsSessionRule; | ||
|
||
import static org.hamcrest.MatcherAssert.assertThat; | ||
import static org.hamcrest.core.IsInstanceOf.instanceOf; | ||
import static org.junit.Assert.assertEquals; | ||
import static org.junit.Assert.assertFalse; | ||
import static org.junit.Assert.assertNotEquals; | ||
import static org.junit.Assert.assertNotNull; | ||
import static org.junit.Assert.assertTrue; | ||
|
||
public class OicSecurityRealmIdStrategyTest { | ||
|
||
@Rule | ||
public JenkinsSessionRule sessions = new JenkinsSessionRule(); | ||
|
||
@Rule | ||
public WireMockRule wireMockRule = new WireMockRule(new WireMockConfiguration().dynamicPort(), true); | ||
|
||
@Test | ||
@Issue("SECURITY-3461") | ||
public void testUserIdStrategy_caseInsensitive() throws Throwable { | ||
sessions.then(r -> { | ||
TestRealm realm = new TestRealm(new TestRealm.Builder(wireMockRule) | ||
.WithMinimalDefaults().WithUserIdStrategy(IdStrategy.CASE_INSENSITIVE)); | ||
Jenkins.get().setSecurityRealm(realm); | ||
User testuser = User.getById("testuser", true); | ||
assertNotNull(testuser); | ||
assertEquals("testuser", testuser.getDisplayName()); | ||
testuser.save(); | ||
|
||
User testUSER = User.getById("testUSER", true); | ||
assertNotNull(testUSER); | ||
assertEquals("testuser", testUSER.getDisplayName()); | ||
testUSER.save(); | ||
|
||
assertEquals(testuser, testUSER); | ||
}); | ||
sessions.then(r -> { | ||
User testuser = User.getById("testuser", false); | ||
assertNotNull(testuser); | ||
assertEquals("testuser", testuser.getDisplayName()); | ||
|
||
User testUSER = User.getById("testUSER", false); | ||
assertNotNull(testUSER); | ||
assertEquals("testuser", testUSER.getDisplayName()); | ||
assertEquals(testuser, testUSER); | ||
}); | ||
} | ||
|
||
@Test | ||
@Issue("SECURITY-3461") | ||
public void testUserIdStrategy_caseSensitive() throws Throwable { | ||
sessions.then(r -> { | ||
TestRealm realm = new TestRealm(new TestRealm.Builder(wireMockRule) | ||
.WithMinimalDefaults().WithUserIdStrategy(new IdStrategy.CaseSensitive())); | ||
Jenkins.get().setSecurityRealm(realm); | ||
User testuser = User.getById("testuser", true); | ||
assertNotNull(testuser); | ||
assertEquals("testuser", testuser.getDisplayName()); | ||
testuser.save(); | ||
|
||
User testUSER = User.getById("testUSER", true); | ||
assertNotNull(testUSER); | ||
assertEquals("testUSER", testUSER.getDisplayName()); | ||
testUSER.save(); | ||
|
||
assertNotEquals(testuser, testUSER); | ||
}); | ||
sessions.then(r -> { | ||
User testuser = User.getById("testuser", false); | ||
assertNotNull(testuser); | ||
assertEquals("testuser", testuser.getDisplayName()); | ||
|
||
User testUSER = User.getById("testUSER", false); | ||
assertNotNull(testUSER); | ||
assertEquals("testUSER", testUSER.getDisplayName()); | ||
|
||
assertNotEquals(testuser, testUSER); | ||
}); | ||
} | ||
|
||
@Test | ||
@Issue("SECURITY-3461") | ||
public void testUserIdStrategy_default() throws Throwable { | ||
sessions.then(r -> { | ||
TestRealm realm = new TestRealm(wireMockRule); | ||
Jenkins.get().setSecurityRealm(realm); | ||
}); | ||
sessions.then(r -> { | ||
// when restarting, ensure the default case-insensitive is used | ||
SecurityRealm securityRealm = Jenkins.get().getSecurityRealm(); | ||
assertThat(securityRealm, instanceOf(OicSecurityRealm.class)); | ||
OicSecurityRealm oicSecurityRealm = (OicSecurityRealm) securityRealm; | ||
assertTrue(oicSecurityRealm.isMissingIdStrategy()); | ||
assertEquals(IdStrategy.CASE_INSENSITIVE, securityRealm.getUserIdStrategy()); | ||
assertEquals(IdStrategy.CASE_INSENSITIVE, securityRealm.getGroupIdStrategy()); | ||
|
||
TestRealm realm = new TestRealm(new TestRealm.Builder(wireMockRule) | ||
.WithMinimalDefaults() | ||
.WithUserIdStrategy(IdStrategy.CASE_INSENSITIVE) | ||
.WithGroupIdStrategy(IdStrategy.CASE_INSENSITIVE)); | ||
Jenkins.get().setSecurityRealm(realm); | ||
assertFalse(realm.isMissingIdStrategy()); | ||
}); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.