Skip to content
This repository has been archived by the owner on Dec 10, 2023. It is now read-only.

Commit

Permalink
Add SSO auth token caching
Browse files Browse the repository at this point in the history
  • Loading branch information
akouznetchik authored and DuMaM committed Apr 22, 2023
1 parent 78cb72d commit 7e31abe
Show file tree
Hide file tree
Showing 10 changed files with 82 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,8 @@ public class CrowdConfigurationService {

private final boolean useCache;

private final boolean useTokenCache;

private final Integer cacheSize;

private final Integer cacheTTL;
Expand All @@ -154,6 +156,8 @@ public class CrowdConfigurationService {

private CacheMap<String, Collection<GrantedAuthority>> authoritiesForUserCache = null;

private CacheMap<String, Boolean> validationCache = null;

/**
* Creates a new Crowd configuration object.
*
Expand All @@ -175,6 +179,7 @@ public class CrowdConfigurationService {
* @param useCache The use cache
* @param cacheSize the cache size
* @param cacheTTL The cache TTL
* @param useTokenCache Enable SSO token caching
* @param pGroupNames The group names to use when authenticating
* Crowd users. May
* not be <code>null</code>.
Expand All @@ -188,7 +193,7 @@ public CrowdConfigurationService(String url, String applicationName, Secret pass
String httpProxyHost, String httpProxyPort, String httpProxyUsername,
Secret httpProxyPassword, String socketTimeout,
String httpTimeout, String httpMaxConnections,
boolean useCache, Integer cacheSize, Integer cacheTTL,
boolean useCache, Integer cacheSize, Integer cacheTTL, boolean useTokenCache,
String pGroupNames, boolean pNestedGroups) {

LOG.log(Level.INFO, "Groups given for Crowd configuration service: {0}", pGroupNames);
Expand All @@ -204,6 +209,7 @@ public CrowdConfigurationService(String url, String applicationName, Secret pass
this.nestedGroups = pNestedGroups;
this.useSSO = useSSO;
this.useCache = useCache;
this.useTokenCache = useTokenCache;
this.cacheSize = cacheSize;
this.cacheTTL = cacheTTL;

Expand All @@ -213,6 +219,9 @@ public CrowdConfigurationService(String url, String applicationName, Secret pass
this.userCache = new CacheMap<>(cacheSize);
this.groupCache = new CacheMap<>(cacheSize);
this.authoritiesForUserCache = new CacheMap<>(cacheSize);
if (useTokenCache) {
this.validationCache = new CacheMap<>(cacheSize);
}
}

Properties props = getProperties(url, applicationName, Secret.toString(password), sessionValidationInterval,
Expand Down Expand Up @@ -579,10 +588,16 @@ public boolean isUserNestedGroupMember(String username, String groupname)
}
}

public void validateSSOAuthentication(String token, List<ValidationFactor> list)
throws OperationFailedException, InvalidAuthenticationException, ApplicationPermissionException,
InvalidTokenException {
LOG.log(Level.FINEST, "CrowdClient.validateSSOAuthentication()");
public void validateSSOAuthentication(String token, List<ValidationFactor> list) throws OperationFailedException, InvalidAuthenticationException, ApplicationPermissionException, InvalidTokenException {
// Load the entry from cache
// if it's located in cache this means it was already validated
Boolean retval = getValidValueFromCache(token, validationCache);
if (retval != null) {
LOG.log(Level.FINEST, "validateSSOAuthentication() cache hit");
return;
}

LOG.log(Level.FINEST, "validateSSOAuthentication() cache hit MISS");

ClassLoader orgContextClassLoader = null;
Thread currentThread = null;
Expand All @@ -593,11 +608,17 @@ public void validateSSOAuthentication(String token, List<ValidationFactor> list)
}
try {
crowdClient.validateSSOAuthentication(token, list);
} finally {
LOG.log(Level.FINEST, "Valid Token");
retval = true;
}
finally {
if (currentThread != null) {
currentThread.setContextClassLoader(orgContextClassLoader);
}
}

// Successful validation call means token is valid
setValueToCache(token, retval, validationCache);
}

public User findUserFromSSOToken(String token) throws OperationFailedException, InvalidAuthenticationException,
Expand Down
38 changes: 34 additions & 4 deletions src/main/java/de/theit/jenkins/crowd/CrowdSecurityRealm.java
Original file line number Diff line number Diff line change
Expand Up @@ -314,18 +314,34 @@ public CrowdSecurityRealm(String url, String applicationName, String password, S
httpTimeout, httpMaxConnections, null);
}

/**
* @return the cache configuration object
*/
public CacheConfiguration getCache() {
return cache;
}

/**
* @return the cache size
*/
public Integer getCacheSize() {
return cache == null ? null : cache.getSize();
}

/**
* @return cache TTL
*/
public Integer getCacheTTL() {
return cache == null ? null : cache.getTtl();
}

/**
* @return the value of useTokenCache
*/
public boolean getUseTokenCache() {
return cache == null ? false : cache.getUseTokenCache();
}

/**
* Initializes all objects necessary to talk to / with Crowd.
*/
Expand All @@ -334,7 +350,7 @@ private void initializeConfiguration() {
url, applicationName, password, sessionValidationInterval,
useSSO, cookieDomain, cookieTokenkey, useProxy, httpProxyHost, httpProxyPort, httpProxyUsername,
httpProxyPassword, socketTimeout, httpTimeout, httpMaxConnections,
cache != null, getCacheSize(), getCacheTTL(),
cache != null, getCacheSize(), getCacheTTL(), getUseTokenCache(),
group, nestedGroups);
}

Expand Down Expand Up @@ -689,7 +705,7 @@ public FormValidation doTestConnection(@QueryParameter String url, @QueryParamet
url, applicationName, Secret.fromString(password), sessionValidationInterval,
useSSO, cookieDomain, cookieTokenkey, useProxy, httpProxyHost, httpProxyPort, httpProxyUsername,
Secret.fromString(httpProxyPassword), socketTimeout, httpTimeout, httpMaxConnections,
false, null, null,
false, null, null, false,
group, false);

try {
Expand Down Expand Up @@ -731,21 +747,35 @@ public String getDisplayName() {
public static class CacheConfiguration extends AbstractDescribableImpl<CacheConfiguration> {
private final int size;
private final int ttl;
private final boolean useTokenCache;

@DataBoundConstructor
public CacheConfiguration(int size, int ttl) {
public CacheConfiguration(int size, int ttl, boolean useTokenCache) {
this.size = Math.max(10, Math.min(size, 1000));
this.ttl = Math.max(30, Math.min(ttl, 3600));
this.useTokenCache = useTokenCache;
}

/**
* @return the cache size
*/
public int getSize() {
return size;
}

/**
* @return the cache TTL
*/
public int getTtl() {
return ttl;
}

/**
* @return the value of useTokenCache
*/
public boolean getUseTokenCache() {
return useTokenCache;
}

@Extension
public static class DescriptorImpl extends Descriptor<CacheConfiguration> {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,7 @@ THE SOFTWARE.
<f:entry field="ttl" title="${%crowd.cache.TTL}">
<f:select default="300"/>
</f:entry>
<f:entry title="${%crowd.useTokenCache}" field="useTokenCache">
<f:checkbox />
</f:entry>
</j:jelly>
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,4 @@

crowd.cache.size=Used cache size
crowd.cache.TTL=Cache records timeout
crowd.useTokenCache=Cache SSO authentication tokens
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,4 @@

crowd.cache.size=Taille de cache utilis\u00e9e
crowd.cache.TTL=D\u00e9lai d'expiration des enregistrements de cache
crowd.useTokenCache=Mettre en cache les jetons d\u0027authentification SSO
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,4 @@

crowd.cache.size=Rozmiar pami\u0119ci podr\u0119cznej
crowd.cache.TTL=Czas \u017cycia wpis\u00f3w w pami\u0119ci podr\u0119cznej
crowd.useTokenCache=Przechowuj tokeny uwierzytelniania SSO
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,4 @@

crowd.cache.size=\u4f7f\u7528\u7684\u7de9\u5b58\u5927\u5c0f
crowd.cache.TTL=\u7de9\u5b58\u8a18\u9304\u8d85\u6642
crowd.useTokenCache=\u7F13\u5B58\u0020\u0053\u0053\u004F\u0020\u8EAB\u4EFD\u9A8C\u8BC1\u4EE4\u724C
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<meta charset="UTF-8">
<div>
Enable this option to cache the Crowd SSO authentication token validity for the duration of the cache records timeout (TTL).
This can <strong>significantly</strong> improve overall performance.
<br/><br/>
<strong>Important:</strong> While other caches create delayed propagation of user changes in Crowd,
enabling this option will delay even the Crowd user being disabled or the Crowd application
being disabled from being enforced for the duration of the Cache TTL.
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ THE SOFTWARE.
<f:password />
</f:entry>
</f:optionalBlock>
<f:optionalProperty title="${%crowd.cache}" field="cache" />
<f:optionalProperty title="${%crowd.cache}" field="cache" help="${requestScope.descriptor.getHelpFile('name')}" />
</f:advanced>
<f:validateButton method="testConnection" title="${%crowd.checkConnection}" with="url,applicationName,password,group,useSSO,cookieDomain,cookieTokenkey,sessionValidationInterval,httpMaxConnections,httpTimeout,socketTimeout,useProxy,httpProxyHost,httpProxyPort,httpProxyUsername,httpProxyPassword" />
</j:jelly>
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public void testCrowdSecurityRealmConstructorWithTypicalData() {
String socketTimeout = "20000";
String httpTimeout = "5000";
String httpMaxConnections = "20";
CacheConfiguration cache = new CacheConfiguration(20, 300);
CacheConfiguration cache = new CacheConfiguration(20, 300, true);
CrowdSecurityRealm realm = new CrowdSecurityRealm(
url,
applicationName,
Expand All @@ -56,6 +56,7 @@ public void testCrowdSecurityRealmConstructorWithTypicalData() {

Assertions.assertThat(realm.getCacheSize()).isEqualTo(20);
Assertions.assertThat(realm.getCacheTTL()).isEqualTo(300);
Assertions.assertThat(realm.getUseTokenCache()).isTrue();
}

@Test
Expand Down Expand Up @@ -100,6 +101,7 @@ public void testCrowdSecurityRealmConstructorWithNullData() {

Assertions.assertThat(realm.getCacheSize()).isNull();
Assertions.assertThat(realm.getCacheTTL()).isNull();
Assertions.assertThat(realm.getUseTokenCache()).isFalse();
}

@Test
Expand Down Expand Up @@ -142,6 +144,7 @@ public void testCrowdSecurityRealmDeprecatedConstructorWithNullData() {

Assertions.assertThat(realm.getCacheSize()).isNull();
Assertions.assertThat(realm.getCacheTTL()).isNull();
Assertions.assertThat(realm.getUseTokenCache()).isFalse();
}

}

0 comments on commit 7e31abe

Please sign in to comment.