Skip to content

Commit

Permalink
Merge pull request #1909 from madhukarbharti/issue-1840
Browse files Browse the repository at this point in the history
Issue-1840: Improve password encoding support for LDAP
  • Loading branch information
steve-todorov committed Dec 1, 2020
2 parents 4650e59 + 7f340d4 commit 5e71385
Show file tree
Hide file tree
Showing 25 changed files with 754 additions and 93 deletions.
12 changes: 7 additions & 5 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,13 @@ services:
- ./:/workspace
working_dir: /workspace
openldap:
image: osixia/openldap:1.3.0
image: osixia/openldap:1.4.0
ports:
- 53389:389
environment:
- LDAP_BACKEND=bdb
# uid=admin,ou=system,dc=carlspring,dc=com
- LDAP_ADMIN_PASSWORD=secret
- LDAP_BACKEND=mdb
# cn=admin,dc=carlspring,dc=com
- LDAP_ADMIN_PASSWORD=password
- LDAP_READONLY_USER=false
- LDAP_DOMAIN=carlspring.com
# Check http://www.openldap.org/doc/admin24/slapdconf2.html for increased level of logging
Expand All @@ -38,5 +38,7 @@ services:
- LDAP_RFC2307BIS_SCHEMA=true
- LDAP_TLS=false
volumes:
- ./strongbox-security/strongbox-authentication-providers/strongbox-ldap-authentication-provider/src/test/resources:/container/service/slapd/assets/config/bootstrap/ldif/custom
# Manually map files which need to be imported to allow for importing from multi-modules.
- ./strongbox-testing/strongbox-testing-core/src/main/resources/ldap/00-strongbox-base.ldif:/container/service/slapd/assets/config/bootstrap/ldif/custom/00-strongbox-base.ldif
- ./strongbox-security/strongbox-authentication-providers/strongbox-ldap-authentication-provider/src/test/resources/ldap/10-issue-1840.ldif:/container/service/slapd/assets/config/bootstrap/ldif/custom/10-issue-1840.ldif
command: --copy-service --loglevel trace
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ strongbox:
enabled: true
ldap:
url: ldap://127.0.0.1:53389/dc=carlspring,dc=com
managerDn: uid=admin,ou=system,dc=carlspring,dc=com
managerPassword: secret
managerDn: cn=admin,dc=carlspring,dc=com
managerPassword: password
rolesMapping:
- externalRole: Admins
strongboxRole: ADMIN
Expand All @@ -30,6 +30,7 @@ strongbox:
- uid={0},ou=Users
userSearchBase: ou=Users
userSearchFilter: (uid={0})
userPasswordEncoded: false
authorities:
groupSearchBase: ou=Groups
groupSearchFilter: (uniqueMember={0})
Expand Down
13 changes: 12 additions & 1 deletion strongbox-security/strongbox-authentication-providers/README.md
Original file line number Diff line number Diff line change
@@ -1 +1,12 @@
Strongbox authentication providers enumerates all supported builin authentication providers.
# strongbox-authentication-providers

Strongbox authentication providers enumerates all supported built-in authentication providers.

NOTE:

There is currently some conflicting terminology due to Spring's conventions:

* `Authentication Provider` in Spring is a mechanism which provides some sort of authentication credentials
(i.e. Basic Authentication).
* `Authentication Provider` in Strongbox is a `database` of some sort, which provides the users to authenticate against.

Original file line number Diff line number Diff line change
@@ -1 +1,31 @@
Strongbox LDAP authentication provider.
# strongbox-ldap-authentication-provider

This module provides the functionality to authenticate users using an LDAP server as the data source.

## Manual testing with OpenLDAP

Sometimes you might need to do manual testing with an OpenLDAP server. In the `project root` path we already have
`docker-compose.yml` which has everything you need to proceed. The only thing you need is an installed Docker
([guide here](https://docs.docker.com/get-docker/)).

Terminal 1:

1. `cd project root`
2. `docker-compose up openldap` (if you've made changes to the ldif files you might need to
`docker-compose up --force-recreate openldap` instead)

Terminal 2:

1. `cd project root`
2. `mvn clean install -DskipTests`
3. `mvn -pl strongbox-web-core spring-boot:run`

Browser:

1. Open `http://localhost:48080/`
2. Go over the security settings
3. Testing using curl should return `Status 200`
```
curl -I -u an-existing-ldap-user http://localhost:48080/api/account
```

Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
package org.carlspring.strongbox.authentication.api.ldap;

import java.util.Base64;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.crypto.codec.Hex;
import org.springframework.security.crypto.codec.Utf8;
import org.springframework.security.ldap.userdetails.LdapUserDetailsMapper;
import org.springframework.util.StringUtils;

/**
* This class handles password base64 decoding based on the property strongbox.authentication.ldap.userPasswordEncoded.
*
* <p>
* When set to true will handle these possible cases:
* {ALG}base64.encode(md5/sha1/bcrypt(mypassword))
* base64.encode({ALG}md5/sha1/bcrypt(mypassword))
* <p>
*
* <p>
* When set to false will handle the ordinary case:
* {ALG}md5/sha1/bcrypt(mypassword)
* </p>
*
* @author mbharti
* @date 19/10/20
*/
public class CustomLdapUserDetailsMapper
extends LdapUserDetailsMapper
{

private static final String PREFIX = "{";

private static final String SUFFIX = "}";

private static final String EMPTY_STRING = "";

private static final Logger logger = LoggerFactory.getLogger(CustomLdapUserDetailsMapper.class);

private boolean isUserPasswordEncoded;

protected String mapPassword(Object passwordValue)
{
String passwordValueString = super.mapPassword(passwordValue);

if (!isUserPasswordEncoded())
{
return passwordValueString;
}

return decodeBase64EncodedPassword(passwordValueString);
}

private String decodeBase64EncodedPassword(String prefixEncodedPasswordString)
{
try
{
String algorithmUsed = extractId(prefixEncodedPasswordString);
String extractBase64EncodedHash = prefixEncodedPasswordString;

if (!StringUtils.isEmpty(algorithmUsed))
{
extractBase64EncodedHash = extractEncodedPassword(prefixEncodedPasswordString);

return PREFIX + algorithmUsed + SUFFIX + decodeBase64EncodedHashWithHex(extractBase64EncodedHash);
}
else
{
return new String(Base64.getDecoder().decode(Utf8.encode(extractBase64EncodedHash)));
}
}
catch (Exception e)
{
logger.warn("Failed to match password after decoding base64encoded hash after algorithm", e);

return prefixEncodedPasswordString;
}
}

private String decodeBase64EncodedHashWithHex(String base64EncodedHash)
{
try
{
return new String(Hex.encode(Base64.getDecoder().decode(Utf8.encode(base64EncodedHash))));
}
catch (Exception ex)
{
logger.warn("decode hash using base64! " + ex.getMessage(), ex);
}

return base64EncodedHash;
}

private String extractEncodedPassword(String prefixEncodedPassword)
{
int start = prefixEncodedPassword.indexOf(SUFFIX);

return prefixEncodedPassword.substring(start + 1);
}

private String extractId(String prefixEncodedPassword)
{
int start = prefixEncodedPassword.indexOf(PREFIX);

if (start != 0)
{
return EMPTY_STRING;
}

int end = prefixEncodedPassword.indexOf(SUFFIX, start);

if (end < 0)
{
return EMPTY_STRING;
}

return prefixEncodedPassword.substring(start + 1, end);
}


/**
* Getting value whether Base64EncodedPassword is enabled or not
*
* @return boolean
*/
public boolean isUserPasswordEncoded()
{
return isUserPasswordEncoded;
}


/**
* Setting value whether Base64EncodedPassword is enabled or not
*
* @param userPasswordEncoded
*/
public void setUserPasswordEncoded(boolean userPasswordEncoded)
{
isUserPasswordEncoded = userPasswordEncoded;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ public class LdapAuthenticationConfigurationManager
public static final String USER_DN_PATTERNS = "userDnPatterns";
public static final String ROLES_MAPPING = "rolesMapping";
public static final String CONVERT_TO_UPPER_CASE = "convertToUpperCase";
public static final String USER_PASSWORD_ENCODED = "userPasswordEncoded";
public static final String ROLE_PREFIX = "rolePrefix";
public static final String GROUP_ROLE_ATTRIBUTE = "groupRoleAttribute";
public static final String GROUP_SEARCH_FILTER = "groupSearchFilter";
Expand Down Expand Up @@ -160,6 +161,7 @@ public Map<String, Object> map(LdapConfiguration source)
.collect(Collectors.toList()));

result.put(USER_DN_PATTERNS, source.getUserDnPatternList());
result.put(USER_PASSWORD_ENCODED, source.isUserPasswordEncoded());

return result;
}
Expand Down Expand Up @@ -189,6 +191,7 @@ public LdapConfiguration map(Map<String, Object> source)
result.setUrl((String) source.get(URL));
result.setManagerDn((String) source.get(MANAGER_DN));
result.setManagerPassword(String.valueOf(source.get(MANAGER_PASSWORD)));
result.setUserPasswordEncoded(Boolean.TRUE.equals(source.get(USER_PASSWORD_ENCODED)));

LdapUserSearch userSearch = new LdapUserSearch();
userSearch.setUserSearchBase((String) source.get(USER_SEARCH_BASE));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ public class LdapConfiguration

private String managerPassword;

private boolean userPasswordEncoded;

private LdapAuthoritiesConfiguration authoritiesConfiguration = new LdapAuthoritiesConfiguration();

private LdapUserSearch userSearch = new LdapUserSearch();
Expand Down Expand Up @@ -118,4 +120,14 @@ public void setEnableProvider(boolean enableProvider)
{
this.enableProvider = enableProvider;
}

public boolean isUserPasswordEncoded()
{
return userPasswordEncoded;
}

public void setUserPasswordEncoded(boolean userPasswordEncoded)
{
this.userPasswordEncoded = userPasswordEncoded;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,11 @@
</bean>
</constructor-arg>
<constructor-arg ref="ldapAuthoritiesPopulator"/>
<property name="userDetailsMapper" ref="customUserDetailsMapper"/>
</bean>

<bean id="customUserDetailsMapper" class="org.carlspring.strongbox.authentication.api.ldap.CustomLdapUserDetailsMapper">
<property name="userPasswordEncoded" value="${strongbox.authentication.ldap.userPasswordEncoded}"/>
</bean>

</beans>
</beans>

0 comments on commit 5e71385

Please sign in to comment.