From af58203481b757e72730bf28bde05f751e4154fd Mon Sep 17 00:00:00 2001 From: Filip Daca Date: Mon, 12 Sep 2022 14:53:37 +0200 Subject: [PATCH 1/4] Add configurable LDAP timeout properties --- .../ReverseProxySecurityRealm.java | 36 +++++++++++++------ .../ReverseProxySecurityRealm/config.jelly | 6 ++++ .../ReverseProxySecurityRealmTest.java | 2 ++ 3 files changed, 33 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/jenkinsci/plugins/reverse_proxy_auth/ReverseProxySecurityRealm.java b/src/main/java/org/jenkinsci/plugins/reverse_proxy_auth/ReverseProxySecurityRealm.java index 32d3010..36e43ea 100644 --- a/src/main/java/org/jenkinsci/plugins/reverse_proxy_auth/ReverseProxySecurityRealm.java +++ b/src/main/java/org/jenkinsci/plugins/reverse_proxy_auth/ReverseProxySecurityRealm.java @@ -41,14 +41,7 @@ import java.net.InetAddress; import java.net.Socket; import java.net.UnknownHostException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.Hashtable; -import java.util.List; -import java.util.Set; -import java.util.StringTokenizer; +import java.util.*; import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Matcher; @@ -115,6 +108,9 @@ public class ReverseProxySecurityRealm extends SecurityRealm { private static final Logger LOGGER = Logger.getLogger(ReverseProxySecurityRealm.class.getName()); + public static final int CONNECT_TIMEOUT = Integer.getInteger("ldap.connect.timeout", 5000); + public static final int READ_TIMEOUT = Integer.getInteger("ldap.read.timeout", 60000); + /** * LDAP filter to look for groups by their names. * @@ -236,6 +232,10 @@ public class ReverseProxySecurityRealm extends SecurityRealm { * Sets an interval for updating the LDAP authorities. The interval is specified in minutes. */ public final int updateInterval; + + public final int ldapConnectTimeout; + + public final int ldapReadTimeout; /** * The authorities that are granted to the authenticated user. @@ -279,7 +279,7 @@ public class ReverseProxySecurityRealm extends SecurityRealm { @DataBoundConstructor public ReverseProxySecurityRealm(String forwardedUser, String headerGroups, String headerGroupsDelimiter, String customLogInUrl, String customLogOutUrl, String server, String rootDN, boolean inhibitInferRootDN, String userSearchBase, String userSearch, String groupSearchBase, String groupSearchFilter, String groupMembershipFilter, String groupNameAttribute, String managerDN, String managerPassword, - Integer updateInterval, boolean disableLdapEmailResolver, String displayNameLdapAttribute, String emailAddressLdapAttribute) { + Integer updateInterval, Integer ldapConnectTimeout, Integer ldapReadTimeout, boolean disableLdapEmailResolver, String displayNameLdapAttribute, String emailAddressLdapAttribute) { this.forwardedUser = fixEmptyAndTrim(forwardedUser); @@ -323,6 +323,8 @@ public ReverseProxySecurityRealm(String forwardedUser, String headerGroups, Stri this.groupNameAttribute = fixEmptyAndTrim(groupNameAttribute); this.updateInterval = (updateInterval == null || updateInterval <= 0) ? CHECK_INTERVAL : updateInterval; + this.ldapConnectTimeout = (ldapConnectTimeout == null || ldapConnectTimeout <= 0) ? CONNECT_TIMEOUT : ldapConnectTimeout; + this.ldapReadTimeout = (ldapReadTimeout == null || ldapReadTimeout <= 0) ? READ_TIMEOUT : ldapReadTimeout; authorities = new GrantedAuthority[0]; @@ -446,7 +448,15 @@ public String getManagerPassword() { public int getUpdateInterval() { return updateInterval; } - + + public int getLdapConnectTimeout() { + return ldapConnectTimeout; + } + + public int getLdapReadTimeout() { + return ldapReadTimeout; + } + public String getLDAPURL() { return toProviderUrl(getServerUrl(), fixNull(rootDN)); } @@ -612,7 +622,11 @@ public SecurityComponents createSecurityComponents() throws DataAccessException dirContextFactory.setManagerDn(managerDN); dirContextFactory.setManagerPassword(getManagerPassword()); } - dirContextFactory.setExtraEnvVars(Collections.singletonMap(Context.REFERRAL, "follow")); + Map envVars = new HashMap<>(); + envVars.put(Context.REFERRAL, "follow"); + envVars.put("com.sun.jndi.ldap.connect.timeout", Integer.toString(CONNECT_TIMEOUT)); + envVars.put("com.sun.jndi.ldap.read.timeout", Integer.toString(READ_TIMEOUT)); + dirContextFactory.setExtraEnvVars(envVars); ldapTemplate = new LdapTemplate(dirContextFactory); FilterBasedLdapUserSearch ldapUserSearch = new FilterBasedLdapUserSearch(userSearchBase, userSearch, dirContextFactory); ldapUserSearch.setSearchSubtree(true); diff --git a/src/main/resources/org/jenkinsci/plugins/reverse_proxy_auth/ReverseProxySecurityRealm/config.jelly b/src/main/resources/org/jenkinsci/plugins/reverse_proxy_auth/ReverseProxySecurityRealm/config.jelly index 5912ca2..c4a63ee 100644 --- a/src/main/resources/org/jenkinsci/plugins/reverse_proxy_auth/ReverseProxySecurityRealm/config.jelly +++ b/src/main/resources/org/jenkinsci/plugins/reverse_proxy_auth/ReverseProxySecurityRealm/config.jelly @@ -87,6 +87,12 @@ THE SOFTWARE. + + + + + + diff --git a/src/test/java/org/jenkinsci/plugins/reverse_proxy_auth/ReverseProxySecurityRealmTest.java b/src/test/java/org/jenkinsci/plugins/reverse_proxy_auth/ReverseProxySecurityRealmTest.java index cb73aa6..2e86782 100644 --- a/src/test/java/org/jenkinsci/plugins/reverse_proxy_auth/ReverseProxySecurityRealmTest.java +++ b/src/test/java/org/jenkinsci/plugins/reverse_proxy_auth/ReverseProxySecurityRealmTest.java @@ -74,6 +74,8 @@ private ReverseProxySecurityRealm createBasicRealm() { "", // managerDN "", // managerPassword 15, // updateInterval + 5000, // ldapConnectTimeout + 60000, // ldapReadTimeout false, // disableLdapEmailResolver "", // displayNameLdapAttribute "" // emailAddressLdapAttribute From 1c382bb4e6f44ef985e1051eff1903704b483c6d Mon Sep 17 00:00:00 2001 From: Filip Daca Date: Mon, 12 Sep 2022 16:05:49 +0200 Subject: [PATCH 2/4] Add comments --- pom.xml | 2 +- .../ReverseProxySecurityRealm.java | 67 ++++++++++++------- 2 files changed, 42 insertions(+), 27 deletions(-) diff --git a/pom.xml b/pom.xml index 4ed4ce0..7cc9c76 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,7 @@ hpi 1.7.4 - -SNAPSHOT + -SUMO jenkinsci/reverse-proxy-auth-plugin 2.334 8 diff --git a/src/main/java/org/jenkinsci/plugins/reverse_proxy_auth/ReverseProxySecurityRealm.java b/src/main/java/org/jenkinsci/plugins/reverse_proxy_auth/ReverseProxySecurityRealm.java index 36e43ea..0f2b01c 100644 --- a/src/main/java/org/jenkinsci/plugins/reverse_proxy_auth/ReverseProxySecurityRealm.java +++ b/src/main/java/org/jenkinsci/plugins/reverse_proxy_auth/ReverseProxySecurityRealm.java @@ -233,8 +233,23 @@ public class ReverseProxySecurityRealm extends SecurityRealm { */ public final int updateInterval; + /** + * This is the socket connection timeout in milliseconds. If your LDAP servers are all close to your Jenkins server + * you can probably set a small value, e.g. 5000 milliseconds. Setting a value smaller that this may result + * in excessive timeouts due to the TCP/IP connection establishment retry mechanism. + * + * Passed to jndi context as 'com.sun.jndi.ldap.connect.timeout'. + * Change requires Jenkins restart. + */ public final int ldapConnectTimeout; + /** + * This is the socket read timeout in milliseconds. If your LDAP queries are all fast you can probably set a low + * value. A reasonable default is 60000 milliseconds. + * + * Passed to jndi context as 'com.sun.jndi.ldap.read.timeout' + * Change requires Jenkins restart. + */ public final int ldapReadTimeout; /** @@ -628,33 +643,33 @@ public SecurityComponents createSecurityComponents() throws DataAccessException envVars.put("com.sun.jndi.ldap.read.timeout", Integer.toString(READ_TIMEOUT)); dirContextFactory.setExtraEnvVars(envVars); ldapTemplate = new LdapTemplate(dirContextFactory); - FilterBasedLdapUserSearch ldapUserSearch = new FilterBasedLdapUserSearch(userSearchBase, userSearch, dirContextFactory); - ldapUserSearch.setSearchSubtree(true); - BindAuthenticator2 bindAuthenticator = new BindAuthenticator2(dirContextFactory); - // this is when we need to find it. - bindAuthenticator.setUserSearch(ldapUserSearch); - ProxyLDAPAuthoritiesPopulator authoritiesPopulator = new ProxyLDAPAuthoritiesPopulator(dirContextFactory, groupSearchBase); - // see DefaultLdapAuthoritiesPopulator for other possible configurations - authoritiesPopulator.setSearchSubtree(true); - authoritiesPopulator.setGroupSearchFilter("(| (member={0}) (uniqueMember={0}) (memberUid={1}))"); - ProviderManager pm = new ProviderManager(); - List providers = new ArrayList<>(); - // talk to Reverse Proxy Authentication + Authorisation via LDAP - LdapAuthenticationProvider authenticationProvider = new LdapAuthenticationProvider(bindAuthenticator, authoritiesPopulator); - providers.add(authenticationProvider); - RememberMeAuthenticationProvider rmap = new RememberMeAuthenticationProvider(); - rmap.setKey(Jenkins.getInstance().getSecretKey()); - providers.add(rmap); - AnonymousAuthenticationProvider aap = new AnonymousAuthenticationProvider(); - aap.setKey("anonymous"); - providers.add(aap); - pm.setProviders(providers); + FilterBasedLdapUserSearch ldapUserSearch = new FilterBasedLdapUserSearch(userSearchBase, userSearch, dirContextFactory); + ldapUserSearch.setSearchSubtree(true); + BindAuthenticator2 bindAuthenticator = new BindAuthenticator2(dirContextFactory); + // this is when we need to find it. + bindAuthenticator.setUserSearch(ldapUserSearch); + ProxyLDAPAuthoritiesPopulator authoritiesPopulator = new ProxyLDAPAuthoritiesPopulator(dirContextFactory, groupSearchBase); + // see DefaultLdapAuthoritiesPopulator for other possible configurations + authoritiesPopulator.setSearchSubtree(true); + authoritiesPopulator.setGroupSearchFilter("(| (member={0}) (uniqueMember={0}) (memberUid={1}))"); + ProviderManager pm = new ProviderManager(); + List providers = new ArrayList<>(); + // talk to Reverse Proxy Authentication + Authorisation via LDAP + LdapAuthenticationProvider authenticationProvider = new LdapAuthenticationProvider(bindAuthenticator, authoritiesPopulator); + providers.add(authenticationProvider); + RememberMeAuthenticationProvider rmap = new RememberMeAuthenticationProvider(); + rmap.setKey(Jenkins.getInstance().getSecretKey()); + providers.add(rmap); + AnonymousAuthenticationProvider aap = new AnonymousAuthenticationProvider(); + aap.setKey("anonymous"); + providers.add(aap); + pm.setProviders(providers); if (groupMembershipFilter != null || groupNameAttribute != null) { - if (groupMembershipFilter != null) { - authoritiesPopulator.setGroupSearchFilter(groupMembershipFilter); - } - if (groupNameAttribute != null) { - authoritiesPopulator.setGroupRoleAttribute(groupNameAttribute); + if (groupMembershipFilter != null) { + authoritiesPopulator.setGroupSearchFilter(groupMembershipFilter); + } + if (groupNameAttribute != null) { + authoritiesPopulator.setGroupRoleAttribute(groupNameAttribute); } } return new SecurityComponents(pm, new ProxyLDAPUserDetailsService(ldapUserSearch, authoritiesPopulator)); From e75c98bfff8c8ffce37547bfa7f8e366068acc7c Mon Sep 17 00:00:00 2001 From: Filip Daca Date: Mon, 12 Sep 2022 16:05:59 +0200 Subject: [PATCH 3/4] Fix indent --- pom.xml | 2 +- .../ReverseProxySecurityRealm.java | 54 +++++++++---------- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/pom.xml b/pom.xml index 7cc9c76..4ed4ce0 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,7 @@ hpi 1.7.4 - -SUMO + -SNAPSHOT jenkinsci/reverse-proxy-auth-plugin 2.334 8 diff --git a/src/main/java/org/jenkinsci/plugins/reverse_proxy_auth/ReverseProxySecurityRealm.java b/src/main/java/org/jenkinsci/plugins/reverse_proxy_auth/ReverseProxySecurityRealm.java index 0f2b01c..79bcd27 100644 --- a/src/main/java/org/jenkinsci/plugins/reverse_proxy_auth/ReverseProxySecurityRealm.java +++ b/src/main/java/org/jenkinsci/plugins/reverse_proxy_auth/ReverseProxySecurityRealm.java @@ -613,35 +613,35 @@ public String getPostLogOutUrl(StaplerRequest req, Authentication auth) { public SecurityComponents createSecurityComponents() throws DataAccessException { if (getLDAPURL() == null) { proxyTemplate = new ReverseProxySearchTemplate(); - DefaultReverseProxyAuthenticator authenticator = new DefaultReverseProxyAuthenticator(retrievedUser, authorities); - ReverseProxyAuthoritiesPopulatorImpl authoritiesPopulator = new ReverseProxyAuthoritiesPopulatorImpl(authContext); - ProviderManager pm = new ProviderManager(); - List providers = new ArrayList<>(); - // talk to Reverse Proxy Authentication - providers.add(new ReverseProxyAuthenticationProvider(authenticator, authoritiesPopulator)); - // these providers apply everywhere - RememberMeAuthenticationProvider rmap = new RememberMeAuthenticationProvider(); - rmap.setKey(Jenkins.getInstance().getSecretKey()); - providers.add(rmap); - // this doesn't mean we allow anonymous access. - // we just authenticate anonymous users as such, - // so that later authorization can reject them if so configured - AnonymousAuthenticationProvider aap = new AnonymousAuthenticationProvider(); - aap.setKey("anonymous"); - providers.add(aap); - pm.setProviders(providers); + DefaultReverseProxyAuthenticator authenticator = new DefaultReverseProxyAuthenticator(retrievedUser, authorities); + ReverseProxyAuthoritiesPopulatorImpl authoritiesPopulator = new ReverseProxyAuthoritiesPopulatorImpl(authContext); + ProviderManager pm = new ProviderManager(); + List providers = new ArrayList<>(); + // talk to Reverse Proxy Authentication + providers.add(new ReverseProxyAuthenticationProvider(authenticator, authoritiesPopulator)); + // these providers apply everywhere + RememberMeAuthenticationProvider rmap = new RememberMeAuthenticationProvider(); + rmap.setKey(Jenkins.getInstance().getSecretKey()); + providers.add(rmap); + // this doesn't mean we allow anonymous access. + // we just authenticate anonymous users as such, + // so that later authorization can reject them if so configured + AnonymousAuthenticationProvider aap = new AnonymousAuthenticationProvider(); + aap.setKey("anonymous"); + providers.add(aap); + pm.setProviders(providers); return new SecurityComponents(pm, new ReverseProxyUserDetailsService(authoritiesPopulator)); } else { - DefaultInitialDirContextFactory dirContextFactory = new DefaultInitialDirContextFactory(getLDAPURL()); - if (managerDN != null) { - dirContextFactory.setManagerDn(managerDN); - dirContextFactory.setManagerPassword(getManagerPassword()); - } - Map envVars = new HashMap<>(); - envVars.put(Context.REFERRAL, "follow"); - envVars.put("com.sun.jndi.ldap.connect.timeout", Integer.toString(CONNECT_TIMEOUT)); - envVars.put("com.sun.jndi.ldap.read.timeout", Integer.toString(READ_TIMEOUT)); - dirContextFactory.setExtraEnvVars(envVars); + DefaultInitialDirContextFactory dirContextFactory = new DefaultInitialDirContextFactory(getLDAPURL()); + if (managerDN != null) { + dirContextFactory.setManagerDn(managerDN); + dirContextFactory.setManagerPassword(getManagerPassword()); + } + Map envVars = new HashMap<>(); + envVars.put(Context.REFERRAL, "follow"); + envVars.put("com.sun.jndi.ldap.connect.timeout", Integer.toString(CONNECT_TIMEOUT)); + envVars.put("com.sun.jndi.ldap.read.timeout", Integer.toString(READ_TIMEOUT)); + dirContextFactory.setExtraEnvVars(envVars); ldapTemplate = new LdapTemplate(dirContextFactory); FilterBasedLdapUserSearch ldapUserSearch = new FilterBasedLdapUserSearch(userSearchBase, userSearch, dirContextFactory); ldapUserSearch.setSearchSubtree(true); From 0824a25a1c9c1d78de5f25f1321c9901a7d72131 Mon Sep 17 00:00:00 2001 From: Filip Daca Date: Mon, 12 Sep 2022 16:44:41 +0200 Subject: [PATCH 4/4] Update Changelog --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index b7f2c58..b986e78 100644 --- a/README.md +++ b/README.md @@ -239,6 +239,15 @@ directive must be placed inside the *VirtualHost* definition. (TODO switch to Release Drafter) +### Version 1.7.4 (unreleased) + +- Support for setting `com.sun.jndi.ldap.connect.timeout` using 'LDAP Connect Timeout' field +- Support for setting `com.sun.jndi.ldap.read.timeout` using 'LDAP Read Timeout' field + +### Version 1.7.3 (2022-03-04) + +- Fixed Javadoc + ### Version 1.7.2 (2022-03-02) Release failed due to Javadoc issues.