/*
* Password Management Servlets (PWM)
* http://www.pwm-project.org
*
* Copyright (c) 2006-2009 Novell, Inc.
* Copyright (c) 2009-2017 The PWM Project
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package password.pwm.ldap;
import com.novell.ldapchai.ChaiUser;
import com.novell.ldapchai.exception.ChaiException;
import com.novell.ldapchai.provider.ChaiProvider;
import password.pwm.PwmApplication;
import password.pwm.PwmConstants;
import password.pwm.bean.SessionLabel;
import password.pwm.bean.UserIdentity;
import password.pwm.config.PwmSetting;
import password.pwm.config.UserPermission;
import password.pwm.config.profile.LdapProfile;
import password.pwm.error.ErrorInformation;
import password.pwm.error.PwmError;
import password.pwm.error.PwmOperationalException;
import password.pwm.error.PwmUnrecoverableException;
import password.pwm.ldap.search.SearchConfiguration;
import password.pwm.ldap.search.UserSearchEngine;
import password.pwm.util.logging.PwmLogger;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
public class LdapPermissionTester {
private static final PwmLogger LOGGER = PwmLogger.forClass(LdapPermissionTester.class);
public static boolean testUserPermissions(
final PwmApplication pwmApplication,
final SessionLabel sessionLabel,
final UserIdentity userIdentity,
final List<UserPermission> userPermissions
)
throws PwmUnrecoverableException {
if (userPermissions == null) {
return false;
}
for (final UserPermission userPermission : userPermissions) {
if (testUserPermission(pwmApplication, sessionLabel, userIdentity, userPermission)) {
return true;
}
}
return false;
}
private static boolean testUserPermission(
final PwmApplication pwmApplication,
final SessionLabel sessionLabel,
final UserIdentity userIdentity,
final UserPermission userPermission
)
throws PwmUnrecoverableException {
if (userPermission == null || userIdentity == null) {
return false;
}
boolean profileAppliesToUser = false;
if (userPermission.getLdapProfileID() == null
|| userPermission.getLdapProfileID().isEmpty()
|| userPermission.getLdapProfileID().equals(PwmConstants.PROFILE_ID_ALL)) {
profileAppliesToUser = true;
} else if (userIdentity.getLdapProfileID().equals(userPermission.getLdapProfileID())) {
profileAppliesToUser = true;
}
if (!profileAppliesToUser) {
return false;
}
switch (userPermission.getType()) {
case ldapQuery: {
if (userPermission.getLdapBase() != null && !userPermission.getLdapBase().trim().isEmpty()) {
if (!testUserDNmatch(pwmApplication, sessionLabel, userPermission.getLdapBase(), userIdentity)) {
return false;
}
}
return testQueryMatch(pwmApplication, sessionLabel, userIdentity, userPermission.getLdapQuery());
}
case ldapGroup: {
return testGroupMatch(pwmApplication, sessionLabel, userIdentity, userPermission.getLdapBase());
}
default:
throw new PwmUnrecoverableException(new ErrorInformation(PwmError.ERROR_UNKNOWN, "unknown permission type: " + userPermission.getType()));
}
}
public static boolean testGroupMatch(
final PwmApplication pwmApplication,
final SessionLabel pwmSession,
final UserIdentity userIdentity,
final String groupDN
) throws PwmUnrecoverableException {
if (userIdentity == null) {
return false;
}
LOGGER.trace(pwmSession, "begin check for ldapGroup match for " + userIdentity + " using queryMatch: " + groupDN);
boolean result = false;
if (groupDN == null || groupDN.length() < 1) {
LOGGER.trace(pwmSession, "missing groupDN value, skipping check");
} else {
final LdapProfile ldapProfile = userIdentity.getLdapProfile(pwmApplication.getConfig());
final String filterString = "(" + ldapProfile.readSettingAsString(PwmSetting.LDAP_USER_GROUP_ATTRIBUTE) + "=" + groupDN + ")";
try {
LOGGER.trace(pwmSession, "checking ldap to see if " + userIdentity + " matches group '" + groupDN + "' using filter '" + filterString + "'");
final ChaiUser theUser = pwmApplication.getProxiedChaiUser(userIdentity);
final Map<String, Map<String, String>> results = theUser.getChaiProvider().search(theUser.getEntryDN(), filterString, Collections.<String>emptySet(), ChaiProvider.SEARCH_SCOPE.BASE);
if (results.size() == 1 && results.keySet().contains(theUser.getEntryDN())) {
result = true;
}
} catch (ChaiException e) {
LOGGER.warn(pwmSession, "LDAP error during group for " + userIdentity + " using " + filterString + ", error:" + e.getMessage());
}
}
if (result) {
LOGGER.debug(pwmSession, "user " + userIdentity + " is a match for group '" + groupDN + "'");
} else {
LOGGER.debug(pwmSession, "user " + userIdentity + " is not a match for group '" + groupDN + "'");
}
return result;
}
public static boolean testQueryMatch(
final PwmApplication pwmApplication,
final SessionLabel pwmSession,
final UserIdentity userIdentity,
final String filterString
)
throws PwmUnrecoverableException {
if (userIdentity == null) {
return false;
}
LOGGER.trace(pwmSession, "begin check for ldapQuery match for " + userIdentity + " using queryMatch: " + filterString);
boolean result = false;
if (filterString == null || filterString.length() < 1) {
LOGGER.trace(pwmSession, "missing queryMatch value, skipping check");
} else if ("(objectClass=*)".equalsIgnoreCase(filterString) || "objectClass=*".equalsIgnoreCase(filterString)) {
LOGGER.trace(pwmSession, "queryMatch check is guaranteed to be true, skipping ldap query");
result = true;
} else {
try {
LOGGER.trace(pwmSession, "checking ldap to see if " + userIdentity + " matches '" + filterString + "'");
final ChaiUser theUser = pwmApplication.getProxiedChaiUser(userIdentity);
final Map<String, Map<String, String>> results = theUser.getChaiProvider().search(theUser.getEntryDN(), filterString, Collections.<String>emptySet(), ChaiProvider.SEARCH_SCOPE.BASE);
if (results.size() == 1 && results.keySet().contains(theUser.getEntryDN())) {
result = true;
}
} catch (ChaiException e) {
LOGGER.warn(pwmSession, "LDAP error during check for " + userIdentity + " using " + filterString + ", error:" + e.getMessage());
}
}
if (result) {
LOGGER.debug(pwmSession, "user " + userIdentity + " is a match for '" + filterString + "'");
} else {
LOGGER.debug(pwmSession, "user " + userIdentity + " is not a match for '" + filterString + "'");
}
return result;
}
public static Map<UserIdentity, Map<String, String>> discoverMatchingUsers(
final PwmApplication pwmApplication,
final int maxResultSize,
final List<UserPermission> userPermissions,
final SessionLabel sessionLabel
)
throws Exception
{
final UserSearchEngine userSearchEngine = pwmApplication.getUserSearchEngine();
final Map<UserIdentity, Map<String, String>> results = new TreeMap<>();
for (final UserPermission userPermission : userPermissions) {
if ((maxResultSize) - results.size() > 0) {
final SearchConfiguration.SearchConfigurationBuilder builder = SearchConfiguration.builder();
switch (userPermission.getType()) {
case ldapQuery: {
builder.filter(userPermission.getLdapQuery());
if (userPermission.getLdapBase() != null && !userPermission.getLdapBase().isEmpty()) {
builder.enableContextValidation(false);
builder.contexts(Collections.singletonList(userPermission.getLdapBase()));
}
}
break;
case ldapGroup: {
builder.groupDN(userPermission.getLdapBase());
}
break;
default:
throw new PwmUnrecoverableException(new ErrorInformation(PwmError.ERROR_UNKNOWN,"unknown permission type: " + userPermission.getType()));
}
if (userPermission.getLdapProfileID() != null && !userPermission.getLdapProfileID().isEmpty() && !userPermission.getLdapProfileID().equals(PwmConstants.PROFILE_ID_ALL)) {
builder.ldapProfile(userPermission.getLdapProfileID());
}
final SearchConfiguration searchConfiguration = builder.build();
try {
results.putAll(userSearchEngine.performMultiUserSearch(
searchConfiguration,
(maxResultSize) - results.size(),
Collections.emptyList(),
sessionLabel
));
} catch (PwmUnrecoverableException e) {
LOGGER.error("error reading matching users: " + e.getMessage());
throw new PwmOperationalException(e.getErrorInformation());
}
}
}
return results;
}
private static boolean testUserDNmatch(
final PwmApplication pwmApplication,
final SessionLabel sessionLabel,
final String baseDN,
final UserIdentity userIdentity
)
throws PwmUnrecoverableException
{
if (baseDN == null || baseDN.trim().isEmpty()) {
return true;
}
final LdapProfile ldapProfile = userIdentity.getLdapProfile(pwmApplication.getConfig());
final String canonicalBaseDN = ldapProfile.readCanonicalDN(pwmApplication, baseDN);
final String userDN = userIdentity.getUserDN();
return userDN.endsWith(canonicalBaseDN);
}
}