From 580be2a0dfb38d494420901f03555092b885a85f Mon Sep 17 00:00:00 2001 From: Martin Spielmann Date: Thu, 13 Sep 2018 13:47:52 +0200 Subject: [PATCH] [SECURITY-1068] --- .../crowd/CrowdConfigurationService.java | 11 ++-- .../jenkins/crowd/CrowdSecurityRealm.java | 58 ++++++++++++++++--- 2 files changed, 58 insertions(+), 11 deletions(-) diff --git a/src/main/java/de/theit/jenkins/crowd/CrowdConfigurationService.java b/src/main/java/de/theit/jenkins/crowd/CrowdConfigurationService.java index a565bbee..ecc60714 100644 --- a/src/main/java/de/theit/jenkins/crowd/CrowdConfigurationService.java +++ b/src/main/java/de/theit/jenkins/crowd/CrowdConfigurationService.java @@ -46,6 +46,9 @@ import com.atlassian.crowd.service.client.ClientProperties; import com.atlassian.crowd.service.client.ClientPropertiesImpl; import com.atlassian.crowd.service.client.CrowdClient; + +import hudson.util.Secret; + import org.acegisecurity.GrantedAuthority; import org.acegisecurity.GrantedAuthorityImpl; @@ -166,11 +169,11 @@ public class CrowdConfigurationService { * @param pNestedGroups Specifies whether nested groups should be used when validating * users against a group name. */ - public CrowdConfigurationService(String url, String applicationName, String password, + public CrowdConfigurationService(String url, String applicationName, Secret password, int sessionValidationInterval, boolean useSSO, String cookieDomain, String cookieTokenkey, Boolean useProxy, String httpProxyHost, String httpProxyPort, String httpProxyUsername, - String httpProxyPassword, String socketTimeout, + Secret httpProxyPassword, String socketTimeout, String httpTimeout, String httpMaxConnections, boolean useCache, Integer cacheSize, Integer cacheTTL, String pGroupNames, boolean pNestedGroups) { @@ -193,9 +196,9 @@ public CrowdConfigurationService(String url, String applicationName, String pass this.useCache = useCache; this.cacheSize = cacheSize; this.cacheTTL = cacheTTL; - Properties props = getProperties(url, applicationName, password, sessionValidationInterval, + Properties props = getProperties(url, applicationName, Secret.toString(password), sessionValidationInterval, useSSO, cookieDomain, cookieTokenkey, useProxy, httpProxyHost, httpProxyPort, httpProxyUsername, - httpProxyPassword, socketTimeout, httpTimeout, httpMaxConnections); + Secret.toString(httpProxyPassword), socketTimeout, httpTimeout, httpMaxConnections); this.clientProperties = ClientPropertiesImpl.newInstanceFromProperties(props); this.crowdClient = new RestCrowdClientFactory().newInstance(this.clientProperties); this.tokenHelper = CrowdHttpTokenHelperImpl.getInstance(CrowdHttpValidationFactorExtractorImpl.getInstance()); diff --git a/src/main/java/de/theit/jenkins/crowd/CrowdSecurityRealm.java b/src/main/java/de/theit/jenkins/crowd/CrowdSecurityRealm.java index 4bc61994..c16ed180 100644 --- a/src/main/java/de/theit/jenkins/crowd/CrowdSecurityRealm.java +++ b/src/main/java/de/theit/jenkins/crowd/CrowdSecurityRealm.java @@ -42,6 +42,7 @@ import hudson.security.SecurityRealm; import hudson.util.FormValidation; import hudson.util.ListBoxModel; +import hudson.util.Secret; import jenkins.model.Jenkins; import org.acegisecurity.AccountExpiredException; @@ -92,7 +93,7 @@ public class CrowdSecurityRealm extends AbstractPasswordBasedSecurityRealm { public final String applicationName; /** Contains the application password to access Crowd. */ - public final String password; + public final Secret password; /** Contains the Crowd group to which a user must belong to. */ public final String group; @@ -129,7 +130,7 @@ public class CrowdSecurityRealm extends AbstractPasswordBasedSecurityRealm { public final String httpProxyHost; public final String httpProxyPort; public final String httpProxyUsername; - public final String httpProxyPassword; + public final Secret httpProxyPassword; public final String socketTimeout; public final String httpTimeout; @@ -176,10 +177,10 @@ public class CrowdSecurityRealm extends AbstractPasswordBasedSecurityRealm { * @param cache The cache configuration */ @DataBoundConstructor - public CrowdSecurityRealm(String url, String applicationName, String password, String group, boolean nestedGroups, + public CrowdSecurityRealm(String url, String applicationName, Secret password, String group, boolean nestedGroups, int sessionValidationInterval, boolean useSSO, String cookieDomain, String cookieTokenkey, Boolean useProxy, String httpProxyHost, String httpProxyPort, - String httpProxyUsername, String httpProxyPassword, String socketTimeout, + String httpProxyUsername, Secret httpProxyPassword, String socketTimeout, String httpTimeout, String httpMaxConnections, CacheConfiguration cache) { this.cookieTokenkey = cookieTokenkey; this.useProxy = useProxy; @@ -192,7 +193,7 @@ public CrowdSecurityRealm(String url, String applicationName, String password, S this.httpMaxConnections = httpMaxConnections; this.url = url.trim(); this.applicationName = applicationName.trim(); - this.password = password.trim(); + this.password = password; this.group = group.trim(); this.nestedGroups = nestedGroups; this.sessionValidationInterval = sessionValidationInterval; @@ -200,6 +201,49 @@ public CrowdSecurityRealm(String url, String applicationName, String password, S this.cookieDomain = cookieDomain; this.cache = cache; } + + /** + * Default constructor. Fields in config.jelly must match the parameter + * names in the "DataBoundConstructor". + * + * @param url The URL for Crowd. + * @param applicationName The application name. + * @param password The application password. + * @param group The group to which users must belong to. If this parameter is + * not specified, a users group membership will not be checked. + * @param nestedGroups true when nested groups may be used. + * false else. + * @param sessionValidationInterval The number of minutes to cache authentication validation in + * the session. If this value is set to 0, each HTTP + * request will be authenticated with the Crowd server. + * @param useSSO Enable SSO authentication. + * @param cookieDomain The cookie domain + * @param cookieTokenkey The cookie token key + * @param useProxy Specifies if a proxy should be used + * @param httpProxyHost The http proxy host + * @param httpProxyPort The http proxy port + * @param httpProxyUsername The http proxy username + * @param httpProxyPassword The http proxy password + * @param socketTimeout The socket timeout + * @param httpTimeout The http timeout + * @param httpMaxConnections The http max connections + * @param cache The cache configuration + * + * @deprecated retained for backwards binary compatibility. + */ + @Deprecated + public CrowdSecurityRealm(String url, String applicationName, String password, String group, boolean nestedGroups, + int sessionValidationInterval, boolean useSSO, String cookieDomain, + String cookieTokenkey, Boolean useProxy, String httpProxyHost, String httpProxyPort, + String httpProxyUsername, String httpProxyPassword, String socketTimeout, + String httpTimeout, String httpMaxConnections, CacheConfiguration cache) { + this(url, applicationName, Secret.fromString(password.trim()), group, nestedGroups, sessionValidationInterval, useSSO, + cookieDomain, cookieTokenkey, useProxy, httpProxyHost, httpProxyPort, httpProxyUsername, + Secret.fromString(httpProxyPassword), socketTimeout, httpTimeout, httpMaxConnections, cache); + // If this constructor is called, make sure to re-save the configuration. + // This way, migrated secrets are persisted securely without user interaction. + getDescriptor().save(); + } /** * Fields in config.jelly must match the parameter names in the "DataBoundConstructor". @@ -585,9 +629,9 @@ public FormValidation doTestConnection(@QueryParameter String url, @QueryParamet // Logger log = Logger.getLogger(getClass().getName()); Jenkins.getInstance().checkPermission(Jenkins.ADMINISTER); CrowdConfigurationService tConfiguration = new CrowdConfigurationService( - url, applicationName, password, sessionValidationInterval, + url, applicationName, Secret.fromString(password), sessionValidationInterval, useSSO, cookieDomain, cookieTokenkey, useProxy, httpProxyHost, httpProxyPort, httpProxyUsername, - httpProxyPassword, socketTimeout, httpTimeout, httpMaxConnections, + Secret.fromString(httpProxyPassword), socketTimeout, httpTimeout, httpMaxConnections, false, null, null, group, false );