From 79dfe69726b6255464599ab852018e4d2ff96fdc Mon Sep 17 00:00:00 2001
From: James Moger <james.moger@gitblit.com>
Date: Fri, 19 Oct 2012 22:47:35 -0400
Subject: [PATCH] Split edit repository page into tabs
---
src/com/gitblit/LdapUserService.java | 249 ++++++++++++++++++++++++++++++++++++-------------
1 files changed, 180 insertions(+), 69 deletions(-)
diff --git a/src/com/gitblit/LdapUserService.java b/src/com/gitblit/LdapUserService.java
index 25f4e03..9ce18f6 100644
--- a/src/com/gitblit/LdapUserService.java
+++ b/src/com/gitblit/LdapUserService.java
@@ -27,14 +27,18 @@
import com.gitblit.models.TeamModel;
import com.gitblit.models.UserModel;
+import com.gitblit.utils.ArrayUtils;
import com.gitblit.utils.StringUtils;
import com.unboundid.ldap.sdk.Attribute;
+import com.unboundid.ldap.sdk.ExtendedResult;
import com.unboundid.ldap.sdk.LDAPConnection;
import com.unboundid.ldap.sdk.LDAPException;
import com.unboundid.ldap.sdk.LDAPSearchException;
+import com.unboundid.ldap.sdk.ResultCode;
import com.unboundid.ldap.sdk.SearchResult;
import com.unboundid.ldap.sdk.SearchResultEntry;
import com.unboundid.ldap.sdk.SearchScope;
+import com.unboundid.ldap.sdk.extensions.StartTLSExtendedRequest;
import com.unboundid.util.ssl.SSLUtil;
import com.unboundid.util.ssl.TrustAllTrustManager;
@@ -80,10 +84,22 @@
if (ldapPort == -1) // Default Port
ldapPort = 389;
- return new LDAPConnection(ldapUrl.getHost(), ldapPort, bindUserName, bindPassword);
+ LDAPConnection conn = new LDAPConnection(ldapUrl.getHost(), ldapPort, bindUserName, bindPassword);
+
+ if (ldapUrl.getScheme().equalsIgnoreCase("ldap+tls")) {
+ SSLUtil sslUtil = new SSLUtil(new TrustAllTrustManager());
+
+ ExtendedResult extendedResult = conn.processExtendedOperation(
+ new StartTLSExtendedRequest(sslUtil.createSSLContext()));
+
+ if (extendedResult.getResultCode() != ResultCode.SUCCESS) {
+ throw new LDAPException(extendedResult.getResultCode());
+ }
+ }
+ return conn;
}
} catch (URISyntaxException e) {
- logger.error("Bad LDAP URL, should be in the form: ldap(s)://<server>:<port>", e);
+ logger.error("Bad LDAP URL, should be in the form: ldap(s|+tls)://<server>:<port>", e);
} catch (GeneralSecurityException e) {
logger.error("Unable to create SSL Connection", e);
} catch (LDAPException e) {
@@ -106,6 +122,29 @@
}
/**
+ * If no displayName pattern is defined then Gitblit can manage the display name.
+ *
+ * @return true if Gitblit can manage the user display name
+ * @since 1.0.0
+ */
+ @Override
+ public boolean supportsDisplayNameChanges() {
+ return StringUtils.isEmpty(settings.getString(Keys.realm.ldap.displayName, ""));
+ }
+
+ /**
+ * If no email pattern is defined then Gitblit can manage the email address.
+ *
+ * @return true if Gitblit can manage the user email address
+ * @since 1.0.0
+ */
+ @Override
+ public boolean supportsEmailAddressChanges() {
+ return StringUtils.isEmpty(settings.getString(Keys.realm.ldap.email, ""));
+ }
+
+
+ /**
* If the LDAP server will maintain team memberships then LdapUserService
* will not allow team membership changes. In this scenario all team
* changes must be made on the LDAP server by the LDAP administrator.
@@ -117,74 +156,123 @@
return !settings.getBoolean(Keys.realm.ldap.maintainTeams, false);
}
- /**
- * Does the user service support cookie authentication?
- *
- * @return true or false
- */
- @Override
- public boolean supportsCookies() {
- // TODO cookies need to be reviewed
- return false;
- }
-
@Override
public UserModel authenticate(String username, char[] password) {
String simpleUsername = getSimpleUsername(username);
- LDAPConnection ldapConnection = getLdapConnection();
+ LDAPConnection ldapConnection = getLdapConnection();
if (ldapConnection != null) {
- // Find the logging in user's DN
- String accountBase = settings.getString(Keys.realm.ldap.accountBase, "");
- String accountPattern = settings.getString(Keys.realm.ldap.accountPattern, "(&(objectClass=person)(sAMAccountName=${username}))");
- accountPattern = StringUtils.replace(accountPattern, "${username}", simpleUsername);
+ try {
+ // Find the logging in user's DN
+ String accountBase = settings.getString(Keys.realm.ldap.accountBase, "");
+ String accountPattern = settings.getString(Keys.realm.ldap.accountPattern, "(&(objectClass=person)(sAMAccountName=${username}))");
+ accountPattern = StringUtils.replace(accountPattern, "${username}", escapeLDAPSearchFilter(simpleUsername));
- SearchResult result = doSearch(ldapConnection, accountBase, accountPattern);
- if (result != null && result.getEntryCount() == 1) {
- SearchResultEntry loggingInUser = result.getSearchEntries().get(0);
- String loggingInUserDN = loggingInUser.getDN();
-
- if (isAuthenticated(ldapConnection, loggingInUserDN, new String(password))) {
- logger.debug("Authenitcated: " + username);
-
- UserModel user = getUserModel(simpleUsername);
- if (user == null) // create user object for new authenticated user
- user = createUserFromLdap(simpleUsername, loggingInUser);
-
- user.password = "StoredInLDAP";
-
- if (!supportsTeamMembershipChanges())
- getTeamsFromLdap(ldapConnection, simpleUsername, loggingInUser, user);
-
- // Get Admin Attributes
- setAdminAttribute(user);
+ SearchResult result = doSearch(ldapConnection, accountBase, accountPattern);
+ if (result != null && result.getEntryCount() == 1) {
+ SearchResultEntry loggingInUser = result.getSearchEntries().get(0);
+ String loggingInUserDN = loggingInUser.getDN();
- // Push the ldap looked up values to backing file
- super.updateUserModel(user);
- if (!supportsTeamMembershipChanges()) {
- for (TeamModel userTeam : user.teams)
- updateTeamModel(userTeam);
+ if (isAuthenticated(ldapConnection, loggingInUserDN, new String(password))) {
+ logger.debug("LDAP authenticated: " + username);
+
+ UserModel user = getUserModel(simpleUsername);
+ if (user == null) // create user object for new authenticated user
+ user = new UserModel(simpleUsername);
+
+ // create a user cookie
+ if (StringUtils.isEmpty(user.cookie) && !ArrayUtils.isEmpty(password)) {
+ user.cookie = StringUtils.getSHA1(user.username + new String(password));
+ }
+
+ if (!supportsTeamMembershipChanges())
+ getTeamsFromLdap(ldapConnection, simpleUsername, loggingInUser, user);
+
+ // Get User Attributes
+ setUserAttributes(user, loggingInUser);
+
+ // Push the ldap looked up values to backing file
+ super.updateUserModel(user);
+ if (!supportsTeamMembershipChanges()) {
+ for (TeamModel userTeam : user.teams)
+ updateTeamModel(userTeam);
+ }
+
+ return user;
}
-
- return user;
+ }
+ } finally {
+ ldapConnection.close();
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Set the admin attribute from team memberships retrieved from LDAP.
+ * If we are not storing teams in LDAP and/or we have not defined any
+ * administrator teams, then do not change the admin flag.
+ *
+ * @param user
+ */
+ private void setAdminAttribute(UserModel user) {
+ if (!supportsTeamMembershipChanges()) {
+ List<String> admins = settings.getStrings(Keys.realm.ldap.admins);
+ // if we have defined administrative teams, then set admin flag
+ // otherwise leave admin flag unchanged
+ if (!ArrayUtils.isEmpty(admins)) {
+ user.canAdmin = false;
+ for (String admin : admins) {
+ if (admin.startsWith("@")) { // Team
+ if (user.getTeam(admin.substring(1)) != null)
+ user.canAdmin = true;
+ } else
+ if (user.getName().equalsIgnoreCase(admin))
+ user.canAdmin = true;
+ }
+ }
+ }
+ }
+
+ private void setUserAttributes(UserModel user, SearchResultEntry userEntry) {
+ // Is this user an admin?
+ setAdminAttribute(user);
+
+ // Don't want visibility into the real password, make up a dummy
+ user.password = "StoredInLDAP";
+
+ // Get full name Attribute
+ String displayName = settings.getString(Keys.realm.ldap.displayName, "");
+ if (!StringUtils.isEmpty(displayName)) {
+ // Replace embedded ${} with attributes
+ if (displayName.contains("${")) {
+ for (Attribute userAttribute : userEntry.getAttributes())
+ displayName = StringUtils.replace(displayName, "${" + userAttribute.getName() + "}", userAttribute.getValue());
+
+ user.displayName = displayName;
+ } else {
+ Attribute attribute = userEntry.getAttribute(displayName);
+ if (attribute != null && attribute.hasValue()) {
+ user.displayName = attribute.getValue();
}
}
}
- return null;
- }
+ // Get email address Attribute
+ String email = settings.getString(Keys.realm.ldap.email, "");
+ if (!StringUtils.isEmpty(email)) {
+ if (email.contains("${")) {
+ for (Attribute userAttribute : userEntry.getAttributes())
+ email = StringUtils.replace(email, "${" + userAttribute.getName() + "}", userAttribute.getValue());
- private void setAdminAttribute(UserModel user) {
- user.canAdmin = false;
- List<String> admins = settings.getStrings(Keys.realm.ldap.admins);
- for (String admin : admins) {
- if (admin.startsWith("@")) { // Team
- if (user.getTeam(admin.substring(1)) != null)
- user.canAdmin = true;
- } else
- if (user.getName().equalsIgnoreCase(admin))
- user.canAdmin = true;
- }
+ user.emailAddress = email;
+ } else {
+ Attribute attribute = userEntry.getAttribute(email);
+ if (attribute != null && attribute.hasValue()) {
+ user.emailAddress = attribute.getValue();
+ }
+ }
+ }
}
private void getTeamsFromLdap(LDAPConnection ldapConnection, String simpleUsername, SearchResultEntry loggingInUser, UserModel user) {
@@ -194,12 +282,12 @@
String groupBase = settings.getString(Keys.realm.ldap.groupBase, "");
String groupMemberPattern = settings.getString(Keys.realm.ldap.groupMemberPattern, "(&(objectClass=group)(member=${dn}))");
- groupMemberPattern = StringUtils.replace(groupMemberPattern, "${dn}", loggingInUserDN);
- groupMemberPattern = StringUtils.replace(groupMemberPattern, "${username}", simpleUsername);
+ groupMemberPattern = StringUtils.replace(groupMemberPattern, "${dn}", escapeLDAPSearchFilter(loggingInUserDN));
+ groupMemberPattern = StringUtils.replace(groupMemberPattern, "${username}", escapeLDAPSearchFilter(simpleUsername));
// Fill in attributes into groupMemberPattern
for (Attribute userAttribute : loggingInUser.getAttributes())
- groupMemberPattern = StringUtils.replace(groupMemberPattern, "${" + userAttribute.getName() + "}", userAttribute.getValue());
+ groupMemberPattern = StringUtils.replace(groupMemberPattern, "${" + userAttribute.getName() + "}", escapeLDAPSearchFilter(userAttribute.getValue()));
SearchResult teamMembershipResult = doSearch(ldapConnection, groupBase, groupMemberPattern);
if (teamMembershipResult != null && teamMembershipResult.getEntryCount() > 0) {
@@ -219,16 +307,9 @@
private TeamModel createTeamFromLdap(SearchResultEntry teamEntry) {
TeamModel answer = new TeamModel(teamEntry.getAttributeValue("cn"));
- // If attributes other than team name ever from from LDAP, this is where to get them
+ // potentially retrieve other attributes here in the future
return answer;
- }
-
- private UserModel createUserFromLdap(String simpleUserName, SearchResultEntry userEntry) {
- UserModel answer = new UserModel(simpleUserName);
- //If attributes other than user name ever from from LDAP, this is where to get them
-
- return answer;
}
private SearchResult doSearch(LDAPConnection ldapConnection, String base, String filter) {
@@ -243,10 +324,11 @@
private boolean isAuthenticated(LDAPConnection ldapConnection, String userDn, String password) {
try {
+ // Binding will stop any LDAP-Injection Attacks since the searched-for user needs to bind to that DN
ldapConnection.bind(userDn, password);
return true;
} catch (LDAPException e) {
- logger.error("Error authenitcating user", e);
+ logger.error("Error authenticating user", e);
return false;
}
}
@@ -263,6 +345,35 @@
if (lastSlash > -1) {
username = username.substring(lastSlash + 1);
}
+
return username;
}
+
+ // From: https://www.owasp.org/index.php/Preventing_LDAP_Injection_in_Java
+ public static final String escapeLDAPSearchFilter(String filter) {
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < filter.length(); i++) {
+ char curChar = filter.charAt(i);
+ switch (curChar) {
+ case '\\':
+ sb.append("\\5c");
+ break;
+ case '*':
+ sb.append("\\2a");
+ break;
+ case '(':
+ sb.append("\\28");
+ break;
+ case ')':
+ sb.append("\\29");
+ break;
+ case '\u0000':
+ sb.append("\\00");
+ break;
+ default:
+ sb.append(curChar);
+ }
+ }
+ return sb.toString();
+ }
}
--
Gitblit v1.9.1