/* * 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.http.servlet.peoplesearch; import com.novell.ldapchai.ChaiUser; import com.novell.ldapchai.exception.ChaiException; import com.novell.ldapchai.exception.ChaiOperationException; import com.novell.ldapchai.exception.ChaiUnavailableException; import com.novell.ldapchai.provider.ChaiProvider; import password.pwm.AppProperty; import password.pwm.PwmApplication; import password.pwm.PwmConstants; import password.pwm.bean.UserIdentity; import password.pwm.bean.UserInfoBean; import password.pwm.config.Configuration; import password.pwm.config.FormConfiguration; import password.pwm.config.PwmSetting; import password.pwm.config.UserPermission; import password.pwm.error.ErrorInformation; import password.pwm.error.PwmError; import password.pwm.error.PwmOperationalException; import password.pwm.error.PwmUnrecoverableException; import password.pwm.http.PwmRequest; import password.pwm.i18n.Display; import password.pwm.ldap.LdapPermissionTester; import password.pwm.ldap.LdapUserDataReader; import password.pwm.ldap.search.SearchConfiguration; import password.pwm.ldap.UserDataReader; import password.pwm.ldap.search.UserSearchEngine; import password.pwm.ldap.UserStatusReader; import password.pwm.ldap.search.UserSearchResults; import password.pwm.svc.cache.CacheKey; import password.pwm.svc.cache.CachePolicy; import password.pwm.svc.stats.Statistic; import password.pwm.svc.stats.StatisticsManager; import password.pwm.util.LocaleHelper; import password.pwm.util.java.JsonUtil; import password.pwm.util.java.StringUtil; import password.pwm.util.java.TimeDuration; import password.pwm.util.logging.PwmLogger; import password.pwm.util.macro.MacroMachine; import javax.servlet.ServletException; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.Serializable; import java.net.URLConnection; import java.time.Instant; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.TreeMap; public class PeopleSearchDataReader { private static final PwmLogger LOGGER = PwmLogger.forClass(PeopleSearchDataReader.class); private final PwmRequest pwmRequest; private final PeopleSearchConfiguration config; public PeopleSearchDataReader(final PwmRequest pwmRequest) { this.pwmRequest = pwmRequest; this.config= new PeopleSearchConfiguration(pwmRequest.getConfig()); } public SearchResultBean makeSearchResultBean( final String searchData, final boolean includeDisplayName ) throws PwmUnrecoverableException, ChaiUnavailableException { final CacheKey cacheKey = makeCacheKey(SearchResultBean.class.getSimpleName(), searchData + "|" + includeDisplayName); { // try to serve from cache first final String cachedOutput = pwmRequest.getPwmApplication().getCacheService().get(cacheKey); if (cachedOutput != null) { final SearchResultBean searchResultBean = JsonUtil.deserialize(cachedOutput, SearchResultBean.class); searchResultBean.setFromCache(true); StatisticsManager.incrementStat(pwmRequest, Statistic.PEOPLESEARCH_CACHE_HITS); return searchResultBean; } else { StatisticsManager.incrementStat(pwmRequest, Statistic.PEOPLESEARCH_CACHE_MISSES); } } // if not in cache, build results from ldap final SearchResultBean searchResultBean = makeSearchResultsImpl(pwmRequest, searchData, includeDisplayName); searchResultBean.setFromCache(false); StatisticsManager.incrementStat(pwmRequest, Statistic.PEOPLESEARCH_SEARCHES); storeDataInCache(pwmRequest.getPwmApplication(), cacheKey, searchResultBean); LOGGER.trace(pwmRequest, "returning " + searchResultBean.getSearchResults().size() + " results for search request '" + searchData + "'"); return searchResultBean; } public OrgChartDataBean makeOrgChartData( final UserIdentity userIdentity, final boolean noChildren ) throws PwmUnrecoverableException { final Instant startTime = Instant.now(); final CacheKey cacheKey = makeCacheKey(OrgChartDataBean.class.getSimpleName(), userIdentity.toDelimitedKey() + "|" + noChildren); { // if value is cached then return; final String cachedOutput = pwmRequest.getPwmApplication().getCacheService().get(cacheKey); if (cachedOutput != null) { StatisticsManager.incrementStat(pwmRequest, Statistic.PEOPLESEARCH_CACHE_HITS); LOGGER.trace(pwmRequest, "completed makeOrgChartData of " + userIdentity.toDisplayString() + " from cache"); return JsonUtil.deserialize(cachedOutput, OrgChartDataBean.class); } else { StatisticsManager.incrementStat(pwmRequest, Statistic.PEOPLESEARCH_CACHE_MISSES); } } final OrgChartDataBean orgChartData = new OrgChartDataBean(); // make self reference orgChartData.setSelf(makeOrgChartReferenceForIdentity(userIdentity)); { // make parent reference final List<UserIdentity> parentIdentities = readUserDNAttributeValues(userIdentity, config.getOrgChartParentAttr()); if (parentIdentities != null && !parentIdentities.isEmpty()) { final UserIdentity parentIdentity = parentIdentities.iterator().next(); orgChartData.setParent(makeOrgChartReferenceForIdentity(parentIdentity)); } } int childCount = 0; if (!noChildren) { // make children reference final Map<String,OrgChartReferenceBean> sortedChildren = new TreeMap<>(); final List<UserIdentity> childIdentities = readUserDNAttributeValues(userIdentity, config.getOrgChartChildAttr()); for (final UserIdentity childIdentity : childIdentities) { final OrgChartReferenceBean childReference = makeOrgChartReferenceForIdentity(childIdentity); if (childReference != null) { if (childReference.getDisplayNames() != null && !childReference.getDisplayNames().isEmpty()) { final String firstDisplayName = childReference.getDisplayNames().iterator().next(); sortedChildren.put(firstDisplayName, childReference); } else { sortedChildren.put(String.valueOf(childCount), childReference); } childCount++; } } orgChartData.setChildren(Collections.unmodifiableList(new ArrayList<>(sortedChildren.values()))); } final TimeDuration totalTime = TimeDuration.fromCurrent(startTime); storeDataInCache(pwmRequest.getPwmApplication(), cacheKey, orgChartData); LOGGER.trace(pwmRequest, "completed makeOrgChartData in " + totalTime.asCompactString() + " with " + childCount + " children" ); return orgChartData; } public UserDetailBean makeUserDetailRequest( final String userKey ) throws PwmUnrecoverableException, IOException, ServletException, PwmOperationalException, ChaiUnavailableException { final Instant startTime = Instant.now(); final UserIdentity userIdentity = UserIdentity.fromKey(userKey, pwmRequest.getPwmApplication()); final CacheKey cacheKey = makeCacheKey(UserDetailBean.class.getSimpleName(), userIdentity.toDelimitedKey()); { final String cachedOutput = pwmRequest.getPwmApplication().getCacheService().get(cacheKey); if (cachedOutput != null) { StatisticsManager.incrementStat(pwmRequest, Statistic.PEOPLESEARCH_CACHE_HITS); return JsonUtil.deserialize(cachedOutput, UserDetailBean.class); } else { StatisticsManager.incrementStat(pwmRequest, Statistic.PEOPLESEARCH_CACHE_MISSES); } } try { checkIfUserIdentityViewable(userIdentity); } catch (PwmOperationalException e) { LOGGER.error(pwmRequest.getPwmSession(), "error during detail results request while checking if requested userIdentity is within search scope: " + e.getMessage()); throw e; } final UserSearchResults detailResults = doDetailLookup(userIdentity); final Map<String, String> searchResults = detailResults.getResults().get(userIdentity); final UserDetailBean userDetailBean = new UserDetailBean(); userDetailBean.setUserKey(userKey); final List<FormConfiguration> detailFormConfig = pwmRequest.getConfig().readSettingAsForm( PwmSetting.PEOPLE_SEARCH_DETAIL_FORM); final Map<String,AttributeDetailBean> attributeBeans = convertResultMapToBeans(pwmRequest, userIdentity, detailFormConfig, searchResults); userDetailBean.setDetail(attributeBeans); final String photoURL = figurePhotoURL(pwmRequest, userIdentity); if (photoURL != null) { userDetailBean.setPhotoURL(photoURL); } final List<String> displayName = figureDisplaynames(pwmRequest, userIdentity); if (displayName != null) { userDetailBean.setDisplayNames(displayName); } userDetailBean.setLinks(makeUserDetailLinks(userIdentity)); LOGGER.trace(pwmRequest.getPwmSession(), "finished building userDetail result in " + TimeDuration.fromCurrent(startTime).asCompactString()); storeDataInCache(pwmRequest.getPwmApplication(), cacheKey, userDetailBean); return userDetailBean; } private List<LinkReferenceBean> makeUserDetailLinks(final UserIdentity actorIdentity) throws PwmUnrecoverableException { final String userLinksStr = pwmRequest.getConfig().readAppProperty(AppProperty.PEOPLESEARCH_VIEW_DETAIL_LINKS); if (StringUtil.isEmpty(userLinksStr)) { return Collections.emptyList(); } final Map<String,String> linkMap; try { linkMap = JsonUtil.deserializeStringMap(userLinksStr); } catch (Exception e) { LOGGER.warn(pwmRequest, "error de-serializing configured app property json for detail links: " + e.getMessage()); return Collections.emptyList(); } final List<LinkReferenceBean> returnList = new ArrayList<>(); final MacroMachine macroMachine = getMacroMachine(actorIdentity); for (final String key : linkMap.keySet()) { final String value = linkMap.get(key); final String parsedValue = macroMachine.expandMacros(value); final LinkReferenceBean linkReference = new LinkReferenceBean(); linkReference.setName(key); linkReference.setLink(parsedValue); returnList.add(linkReference); } return returnList; } private List<String> readUserMultiAttributeValues( final PwmRequest pwmRequest, final UserIdentity userIdentity, final String attributeName ) throws PwmUnrecoverableException { final List<String> returnObj = new ArrayList<>(); final int MAX_VALUES = Integer.parseInt(pwmRequest.getConfig().readAppProperty(AppProperty.PEOPLESEARCH_VALUE_MAXCOUNT)); final ChaiUser chaiUser = getChaiUser(userIdentity); try { final Set<String> ldapValues = chaiUser.readMultiStringAttribute(attributeName); if (ldapValues != null) { returnObj.addAll(ldapValues); } while (returnObj.size() > MAX_VALUES) { returnObj.remove(returnObj.size() - 1); } return Collections.unmodifiableList(returnObj); } catch (ChaiOperationException e) { throw new PwmUnrecoverableException(new ErrorInformation(PwmError.ERROR_DIRECTORY_UNAVAILABLE, "error reading attribute value '" + attributeName + "', error:" + e.getMessage())); } catch (ChaiUnavailableException e) { throw new PwmUnrecoverableException(new ErrorInformation(PwmError.ERROR_DIRECTORY_UNAVAILABLE, e.getMessage())); } } private CacheKey makeCacheKey( final String operationIdentifier, final String dataIdentifier ) throws PwmUnrecoverableException { final UserIdentity userIdentity; if (pwmRequest.isAuthenticated() && !useProxy()) { userIdentity = pwmRequest.getUserInfoIfLoggedIn(); } else { userIdentity = null; } final String keyString = operationIdentifier + "|" + pwmRequest.getPwmApplication().getSecureService().hash(dataIdentifier); return CacheKey.makeCacheKey( this.getClass(), userIdentity, keyString); } private static Set<String> getSearchAttributes(final Configuration configuration) { final List<String> searchResultForm = configuration.readSettingAsStringArray(PwmSetting.PEOPLE_SEARCH_SEARCH_ATTRIBUTES); return Collections.unmodifiableSet(new HashSet<>(searchResultForm)); } private OrgChartReferenceBean makeOrgChartReferenceForIdentity( final UserIdentity userIdentity ) throws PwmUnrecoverableException { final OrgChartReferenceBean orgChartReferenceBean = new OrgChartReferenceBean(); orgChartReferenceBean.setUserKey(userIdentity.toObfuscatedKey(pwmRequest.getPwmApplication())); orgChartReferenceBean.setPhotoURL(figurePhotoURL(pwmRequest, userIdentity)); final List<String> displayLabels = figureDisplaynames(pwmRequest, userIdentity); orgChartReferenceBean.setDisplayNames(displayLabels); return orgChartReferenceBean; } private List<UserIdentity> readUserDNAttributeValues( final UserIdentity userIdentity, final String attributeName ) throws PwmUnrecoverableException { final List<UserIdentity> returnObj = new ArrayList<>(); final int MAX_VALUES = Integer.parseInt(pwmRequest.getConfig().readAppProperty(AppProperty.PEOPLESEARCH_VALUE_MAXCOUNT)); final ChaiUser chaiUser = getChaiUser(userIdentity); final Set<String> ldapValues; try { ldapValues = chaiUser.readMultiStringAttribute(attributeName); } catch (ChaiOperationException e) { throw new PwmUnrecoverableException(new ErrorInformation(PwmError.ERROR_DIRECTORY_UNAVAILABLE, "error reading attribute value '" + attributeName + "', error:" + e.getMessage())); } catch (ChaiUnavailableException e) { throw new PwmUnrecoverableException(new ErrorInformation(PwmError.ERROR_DIRECTORY_UNAVAILABLE, e.getMessage())); } final boolean checkUserDNValues = Boolean.parseBoolean(pwmRequest.getConfig().readAppProperty(AppProperty.PEOPLESEARCH_MAX_VALUE_VERIFYUSERDN)); for (final String userDN : ldapValues) { final UserIdentity loopIdentity = new UserIdentity(userDN, userIdentity.getLdapProfileID()); if (returnObj.size() < MAX_VALUES) { try { if (checkUserDNValues) { checkIfUserIdentityViewable(loopIdentity); } returnObj.add(loopIdentity); } catch (PwmOperationalException e) { LOGGER.debug(pwmRequest, "discarding userDN " + userDN + " from attribute " + attributeName + " because it does not match search filter"); } } else { LOGGER.trace(pwmRequest, "discarding userDN " + userDN + " from attribute " + attributeName + " because maximum value count has been reached"); } } return returnObj; } private static void storeDataInCache( final PwmApplication pwmApplication, final CacheKey cacheKey, final Serializable data ) throws PwmUnrecoverableException { final long maxCacheSeconds = pwmApplication.getConfig().readSettingAsLong(PwmSetting.PEOPLE_SEARCH_MAX_CACHE_SECONDS); if (maxCacheSeconds > 0) { final CachePolicy cachePolicy = CachePolicy.makePolicyWithExpirationMS(maxCacheSeconds * 1000); pwmApplication.getCacheService().put(cacheKey, cachePolicy, JsonUtil.serialize(data)); } } private String figurePhotoURL( final PwmRequest pwmRequest, final UserIdentity userIdentity ) throws PwmUnrecoverableException { final PwmApplication pwmApplication = pwmRequest.getPwmApplication(); final List<UserPermission> showPhotoPermission = pwmApplication.getConfig().readSettingAsUserPermission(PwmSetting.PEOPLE_SEARCH_PHOTO_QUERY_FILTER); if (!LdapPermissionTester.testUserPermissions(pwmApplication, pwmRequest.getSessionLabel(), userIdentity, showPhotoPermission)) { LOGGER.debug(pwmRequest, "detailed user data lookup for " + userIdentity.toString() + ", failed photo query filter, denying photo view"); return null; } final String overrideURL = pwmApplication.getConfig().readSettingAsString(PwmSetting.PEOPLE_SEARCH_PHOTO_URL_OVERRIDE); try { if (overrideURL != null && !overrideURL.isEmpty()) { final MacroMachine macroMachine = getMacroMachine(userIdentity); return macroMachine.expandMacros(overrideURL); } try { readPhotoDataFromLdap(userIdentity); } catch (PwmOperationalException e) { LOGGER.debug(pwmRequest, "determined " + userIdentity + " does not have photo data available while generating detail data"); return null; } } catch (ChaiUnavailableException e) { throw PwmUnrecoverableException.fromChaiException(e); } return "PeopleSearch?processAction=photo&userKey=" + userIdentity.toObfuscatedKey(pwmApplication); } private String figureDisplaynameValue( final PwmRequest pwmRequest, final UserIdentity userIdentity ) throws PwmUnrecoverableException { final MacroMachine macroMachine = getMacroMachine(userIdentity); final String settingValue = pwmRequest.getConfig().readSettingAsString(PwmSetting.PEOPLE_SEARCH_DISPLAY_NAME); return macroMachine.expandMacros(settingValue); } private List<String> figureDisplaynames( final PwmRequest pwmRequest, final UserIdentity userIdentity ) throws PwmUnrecoverableException { final List<String> displayLabels = new ArrayList<>(); final List<String> displayStringSettings = pwmRequest.getConfig().readSettingAsStringArray(PwmSetting.PEOPLE_SEARCH_DISPLAY_NAMES_CARD_LABELS); if (displayStringSettings != null) { final MacroMachine macroMachine = getMacroMachine(userIdentity); for (final String displayStringSetting : displayStringSettings) { final String displayLabel = macroMachine.expandMacros(displayStringSetting); displayLabels.add(displayLabel); } } return displayLabels; } private Map<String,AttributeDetailBean> convertResultMapToBeans( final PwmRequest pwmRequest, final UserIdentity userIdentity, final List<FormConfiguration> detailForm, final Map<String, String> searchResults ) throws ChaiUnavailableException, PwmUnrecoverableException { final Set<String> searchAttributes = getSearchAttributes(pwmRequest.getConfig()); final Map<String,AttributeDetailBean> returnObj = new LinkedHashMap<>(); for (final FormConfiguration formConfiguration : detailForm) { if (formConfiguration.isRequired() || searchResults.containsKey(formConfiguration.getName())) { final AttributeDetailBean bean = new AttributeDetailBean(); bean.setName(formConfiguration.getName()); bean.setLabel(formConfiguration.getLabel(pwmRequest.getLocale())); bean.setType(formConfiguration.getType()); if (searchAttributes.contains(formConfiguration.getName())) { if (formConfiguration.getType() != FormConfiguration.Type.userDN) { bean.setSearchable(true); } } if (formConfiguration.getType() == FormConfiguration.Type.userDN) { if (searchResults.containsKey(formConfiguration.getName())) { final List<UserIdentity> identityValues = readUserDNAttributeValues(userIdentity, formConfiguration.getName()); final TreeMap<String, UserReferenceBean> userReferences = new TreeMap<>(); for (final UserIdentity loopIdentity : identityValues) { final String displayValue = figureDisplaynameValue(pwmRequest, loopIdentity); final UserReferenceBean userReference = new UserReferenceBean(); userReference.setUserKey(loopIdentity.toObfuscatedKey(pwmRequest.getPwmApplication())); userReference.setDisplayName(displayValue); userReferences.put(displayValue, userReference); } bean.setUserReferences(userReferences.values()); } } else { if (formConfiguration.isMultivalue()) { bean.setValues(readUserMultiAttributeValues(pwmRequest, userIdentity, formConfiguration.getName())); } else { if (searchResults.containsKey(formConfiguration.getName())) { bean.setValues(Collections.singletonList(searchResults.get(formConfiguration.getName()))); } else { bean.setValues(Collections.<String>emptyList()); } } } returnObj.put(formConfiguration.getName(),bean); } } return returnObj; } private ChaiUser getChaiUser( final UserIdentity userIdentity ) throws PwmUnrecoverableException { final boolean useProxy = useProxy(); return useProxy ? pwmRequest.getPwmApplication().getProxiedChaiUser(userIdentity) : pwmRequest.getPwmSession().getSessionManager().getActor(pwmRequest.getPwmApplication(), userIdentity); } private MacroMachine getMacroMachine( final UserIdentity userIdentity ) throws PwmUnrecoverableException { final ChaiUser chaiUser = getChaiUser(userIdentity); final UserInfoBean userInfoBean; if (Boolean.parseBoolean(pwmRequest.getConfig().readAppProperty(AppProperty.PEOPLESEARCH_DISPLAYNAME_USEALLMACROS))) { final Locale locale = pwmRequest.getLocale(); final ChaiProvider chaiProvider = pwmRequest.getPwmApplication().getProxiedChaiUser(userIdentity).getChaiProvider(); userInfoBean = new UserInfoBean(); final UserStatusReader userStatusReader = new UserStatusReader(pwmRequest.getPwmApplication(), pwmRequest.getSessionLabel()); userStatusReader.populateUserInfoBean(userInfoBean, locale, userIdentity, chaiProvider); } else { userInfoBean = null; } final UserDataReader userDataReader = new LdapUserDataReader(userIdentity, chaiUser); return new MacroMachine(pwmRequest.getPwmApplication(), pwmRequest.getSessionLabel(), userInfoBean, null, userDataReader); } public void checkIfUserIdentityViewable( final UserIdentity userIdentity ) throws PwmUnrecoverableException, PwmOperationalException { final String filterSetting = getSearchFilter(pwmRequest.getConfig()); String filterString = filterSetting.replace(PwmConstants.VALUE_REPLACEMENT_USERNAME, "*"); while (filterString.contains("**")) { filterString = filterString.replace("**", "*"); } final boolean match = LdapPermissionTester.testQueryMatch(pwmRequest.getPwmApplication(), pwmRequest.getSessionLabel(), userIdentity, filterString); if (!match) { throw new PwmOperationalException(new ErrorInformation(PwmError.ERROR_SERVICE_NOT_AVAILABLE, "requested userDN is not available within configured search filter")); } } private static String getSearchFilter(final Configuration configuration) { final String configuredFilter = configuration.readSettingAsString(PwmSetting.PEOPLE_SEARCH_SEARCH_FILTER); if (configuredFilter != null && !configuredFilter.isEmpty()) { return configuredFilter; } final List<String> defaultObjectClasses = configuration.readSettingAsStringArray(PwmSetting.DEFAULT_OBJECT_CLASSES); final Set<String> searchAttributes = getSearchAttributes(configuration); final StringBuilder filter = new StringBuilder(); filter.append("(&"); //open AND clause for objectclasses and attributes for (final String objectClass : defaultObjectClasses) { filter.append("(objectClass=").append(objectClass).append(")"); } filter.append("(|"); // open OR clause for attributes for (final String searchAttribute : searchAttributes) { filter.append("(").append(searchAttribute).append("=*").append(PwmConstants.VALUE_REPLACEMENT_USERNAME).append("*)"); } filter.append(")"); // close OR clause filter.append(")"); // close AND clause return filter.toString(); } private boolean useProxy() { final boolean useProxy = pwmRequest.getConfig().readSettingAsBoolean(PwmSetting.PEOPLE_SEARCH_USE_PROXY); final boolean publicAccessEnabled = pwmRequest.getConfig().readSettingAsBoolean(PwmSetting.PEOPLE_SEARCH_ENABLE_PUBLIC); return useProxy || !pwmRequest.isAuthenticated() && publicAccessEnabled; } private UserSearchResults doDetailLookup( final UserIdentity userIdentity ) throws PwmUnrecoverableException { final List<FormConfiguration> detailFormConfig = pwmRequest.getConfig().readSettingAsForm(PwmSetting.PEOPLE_SEARCH_DETAIL_FORM); final Map<String, String> attributeHeaderMap = UserSearchResults.fromFormConfiguration( detailFormConfig, pwmRequest.getLocale()); if (config.isOrgChartEnabled()) { final String orgChartParentAttr = config.getOrgChartParentAttr(); if (!attributeHeaderMap.containsKey(orgChartParentAttr)) { attributeHeaderMap.put(orgChartParentAttr, orgChartParentAttr); } final String orgChartChildAttr = config.getOrgChartParentAttr(); if (!attributeHeaderMap.containsKey(orgChartChildAttr)) { attributeHeaderMap.put(orgChartChildAttr, orgChartChildAttr); } } try { final ChaiUser theUser = getChaiUser(userIdentity); final Map<String, String> values = theUser.readStringAttributes(attributeHeaderMap.keySet()); return new UserSearchResults( attributeHeaderMap, Collections.singletonMap(userIdentity, values), false ); } catch (ChaiException e) { LOGGER.error("unexpected error during detail lookup of '" + userIdentity + "', error: " + e.getMessage()); throw PwmUnrecoverableException.fromChaiException(e); } } public PhotoDataBean readPhotoDataFromLdap( final UserIdentity userIdentity ) throws ChaiUnavailableException, PwmUnrecoverableException, PwmOperationalException { final String attribute = pwmRequest.getConfig().readSettingAsString(PwmSetting.PEOPLE_SEARCH_PHOTO_ATTRIBUTE); if (attribute == null || attribute.isEmpty()) { throw new PwmOperationalException(new ErrorInformation(PwmError.ERROR_SERVICE_NOT_AVAILABLE, "ldap photo attribute is not configured")); } final byte[] photoData; final String mimeType; try { final ChaiUser chaiUser = getChaiUser(userIdentity); final byte[][] photoAttributeData = chaiUser.readMultiByteAttribute(attribute); if (photoAttributeData == null || photoAttributeData.length == 0 || photoAttributeData[0].length == 0) { throw new PwmOperationalException(new ErrorInformation(PwmError.ERROR_SERVICE_NOT_AVAILABLE, "user has no photo data stored in LDAP attribute")); } photoData = photoAttributeData[0]; mimeType = URLConnection.guessContentTypeFromStream(new ByteArrayInputStream(photoData)); } catch (IOException | ChaiOperationException e) { throw new PwmOperationalException(new ErrorInformation(PwmError.ERROR_UNKNOWN, "error reading user photo ldap attribute: " + e.getMessage())); } return new PhotoDataBean(mimeType, photoData); } private SearchResultBean makeSearchResultsImpl( final PwmRequest pwmRequest, final String username, final boolean includeDisplayName ) throws ChaiUnavailableException, PwmUnrecoverableException { final Instant startTime = Instant.now(); if (username == null || username.length() < 1) { return new SearchResultBean(); } final boolean useProxy = useProxy(); final UserSearchEngine userSearchEngine = pwmRequest.getPwmApplication().getUserSearchEngine(); final SearchConfiguration searchConfiguration; { final SearchConfiguration.SearchConfigurationBuilder builder = SearchConfiguration.builder(); builder.contexts(pwmRequest.getConfig().readSettingAsStringArray(PwmSetting.PEOPLE_SEARCH_SEARCH_BASE)); builder.enableContextValidation(false); builder.username(username); builder.enableValueEscaping(false); builder.filter(getSearchFilter(pwmRequest.getConfig())); builder.enableSplitWhitespace(true); if (!useProxy) { builder.ldapProfile(pwmRequest.getPwmSession().getUserInfoBean().getUserIdentity().getLdapProfileID()); builder.chaiProvider(pwmRequest.getPwmSession().getSessionManager().getChaiProvider()); } searchConfiguration = builder.build(); } final UserSearchResults results; final boolean sizeExceeded; try { final List<FormConfiguration> searchForm = pwmRequest.getConfig().readSettingAsForm( PwmSetting.PEOPLE_SEARCH_RESULT_FORM); final int maxResults = (int) pwmRequest.getConfig().readSettingAsLong( PwmSetting.PEOPLE_SEARCH_RESULT_LIMIT); final Locale locale = pwmRequest.getLocale(); results = userSearchEngine.performMultiUserSearchFromForm(locale, searchConfiguration, maxResults, searchForm, pwmRequest.getSessionLabel()); sizeExceeded = results.isSizeExceeded(); } catch (PwmOperationalException e) { final ErrorInformation errorInformation = e.getErrorInformation(); LOGGER.error(pwmRequest.getSessionLabel(), errorInformation.toDebugStr()); throw new PwmUnrecoverableException(errorInformation); } final List<Map<String,Object>> resultOutput = new ArrayList<>(results.resultsAsJsonOutput(pwmRequest.getPwmApplication(),null)); if (includeDisplayName) { for (final Map<String,Object> map : resultOutput) { final String userKey = (String)map.get("userKey"); if (userKey != null) { final UserIdentity userIdentity = UserIdentity.fromKey(userKey, pwmRequest.getPwmApplication()); final String displayValue = figureDisplaynameValue(pwmRequest, userIdentity); map.put("_displayName",displayValue); } } } final TimeDuration searchDuration = TimeDuration.fromCurrent(startTime); LOGGER.trace(pwmRequest.getPwmSession(), "finished rest peoplesearch search in " + searchDuration.asCompactString() + " not using cache, size=" + results.getResults().size()); final SearchResultBean searchResultBean = new SearchResultBean(); searchResultBean.setSearchResults(resultOutput); searchResultBean.setSizeExceeded(sizeExceeded); final String aboutMessage = LocaleHelper.getLocalizedMessage( pwmRequest.getLocale(), Display.Display_SearchResultsInfo.getKey(), pwmRequest.getConfig(), Display.class, new String[]{String.valueOf(results.getResults().size()), searchDuration.asLongString(pwmRequest.getLocale())} ); searchResultBean.setAboutResultMessage(aboutMessage); return searchResultBean; } }