/* * 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.github.benmanes.caffeine.cache.Cache; import com.github.benmanes.caffeine.cache.Caffeine; import com.novell.ldapchai.ChaiFactory; import com.novell.ldapchai.ChaiUser; import com.novell.ldapchai.exception.ChaiOperationException; import com.novell.ldapchai.exception.ChaiUnavailableException; import com.novell.ldapchai.provider.ChaiProvider; import password.pwm.PwmApplication; import password.pwm.bean.UserIdentity; import password.pwm.error.PwmError; import password.pwm.error.PwmUnrecoverableException; import password.pwm.http.PwmSession; import password.pwm.util.java.JavaHelper; import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; public class LdapUserDataReader implements Serializable, UserDataReader { private static final Boolean NULL_CACHE_VALUE = Boolean.FALSE; private final Cache<String,Object> cacheMap = Caffeine.newBuilder() .maximumSize(100) // safety limit .build(); private final ChaiUser user; private final UserIdentity userIdentity; private enum PrivateFlag { MultiValueRead } public LdapUserDataReader( final UserIdentity userIdentity, final ChaiUser user ) { this.userIdentity = userIdentity; this.user = user; } public static UserDataReader appProxiedReader( final PwmApplication pwmApplication, final UserIdentity userIdentity ) throws PwmUnrecoverableException { final ChaiUser user; user = pwmApplication.getProxiedChaiUser(userIdentity); return new LdapUserDataReader(userIdentity, user); } public static UserDataReader selfProxiedReader( final PwmApplication pwmApplication, final PwmSession pwmSession, final UserIdentity userIdentity ) throws PwmUnrecoverableException, ChaiUnavailableException { if (!userIdentity.getLdapProfileID().equals(pwmSession.getUserInfoBean().getUserIdentity().getLdapProfileID())) { throw new PwmUnrecoverableException(PwmError.ERROR_NO_LDAP_CONNECTION); } final ChaiProvider chaiProvider = pwmSession.getSessionManager().getChaiProvider(); final ChaiUser chaiUser = ChaiFactory.createChaiUser(userIdentity.getUserDN(),chaiProvider); return new LdapUserDataReader(userIdentity,chaiUser); } @Override public String getUserDN() { return this.user.getEntryDN(); } @Override public String readStringAttribute( final String attribute, final Flag... flags ) throws ChaiUnavailableException, ChaiOperationException { final Map<String,String> results = readStringAttributes(Collections.singletonList(attribute), flags); if (results == null || results.isEmpty()) { return null; } return results.values().iterator().next(); } @Override public Date readDateAttribute(final String attribute) throws ChaiUnavailableException, ChaiOperationException { return user.readDateAttribute(attribute); } public Map<String,String> readStringAttributes( final Collection<String> attributes, final Flag... flags ) throws ChaiUnavailableException, ChaiOperationException { final Map<String,List<String>> valueMap = readMultiStringAttributesImpl(attributes, Collections.<PrivateFlag>emptyList(), flags); final Map<String,String> returnValue = new LinkedHashMap<>(); for (final String key : valueMap.keySet()) { final List<String> values = valueMap.get(key); if (values != null && !values.isEmpty()) { returnValue.put(key, values.iterator().next()); } } return returnValue; } public Map<String,List<String>> readMultiStringAttributes( final Collection<String> attributes, final Flag... flags ) throws ChaiUnavailableException, ChaiOperationException { return readMultiStringAttributesImpl(attributes, Collections.singletonList(PrivateFlag.MultiValueRead), flags); } private Map<String,List<String>> readMultiStringAttributesImpl( final Collection<String> attributes, final Collection<PrivateFlag> privateFlags, final Flag... flags ) throws ChaiUnavailableException, ChaiOperationException { final boolean ignoreCache = JavaHelper.enumArrayContainsValue(flags, Flag.ignoreCache); if (user == null || attributes == null || attributes.isEmpty()) { return Collections.emptyMap(); } if (ignoreCache) { cacheMap.invalidateAll(); } // figure out uncached attributes. final List<String> uncachedAttributes = new ArrayList<>(attributes); uncachedAttributes.removeAll(cacheMap.asMap().keySet()); // read uncached attributes into cache if (!uncachedAttributes.isEmpty()) { for (final String attribute : attributes) { if (privateFlags.contains(PrivateFlag.MultiValueRead)) { final Set<String> readData = user.readMultiStringAttribute(attribute); final List<String> stringList = readData == null ? null : new ArrayList<>(readData); cacheMap.put(attribute, stringList != null && !stringList.isEmpty() ? stringList : NULL_CACHE_VALUE); } else { final String readData = user.readStringAttribute(attribute); cacheMap.put(attribute, readData != null ? Collections.singletonList(readData) : NULL_CACHE_VALUE); } } } // build result data from cache final Map<String,List<String>> returnMap = new HashMap<>(); for (final String attribute : attributes) { final Object cachedValue = cacheMap.getIfPresent(attribute); if (cachedValue != null && !NULL_CACHE_VALUE.equals(cachedValue)) { returnMap.put(attribute,(List<String>)cachedValue); } } return Collections.unmodifiableMap(returnMap); } @Override public List<String> readMultiStringAttribute(final String attribute, final Flag... flags) throws ChaiUnavailableException, ChaiOperationException { return readMultiStringAttributesImpl(Collections.singletonList(attribute), Collections.singletonList(PrivateFlag.MultiValueRead), flags).get(attribute); } }