/* * (C) Copyright 2006-2008 Nuxeo SAS (http://nuxeo.com/) and contributors. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the GNU Lesser General Public License * (LGPL) version 2.1 which accompanies this distribution, and is available at * http://www.gnu.org/licenses/lgpl.html * * This library 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 * Lesser General Public License for more details. * * Contributors: * <a href="mailto:at@nuxeo.com">Anahide Tchertchian</a> * * $Id: $ */ package org.nuxeo.ecm.webapp.security; import static org.jboss.seam.ScopeType.PAGE; import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.faces.convert.Converter; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.seam.annotations.In; import org.jboss.seam.annotations.Name; import org.jboss.seam.annotations.Scope; import org.jboss.seam.annotations.web.RequestParameter; import org.jboss.seam.faces.FacesMessages; import org.jboss.seam.international.StatusMessage; import org.nuxeo.ecm.core.api.ClientException; import org.nuxeo.ecm.core.api.DocumentModel; import org.nuxeo.ecm.core.api.DocumentModelComparator; import org.nuxeo.ecm.core.api.DocumentModelList; import org.nuxeo.ecm.core.api.NuxeoGroup; import org.nuxeo.ecm.core.api.NuxeoPrincipal; import org.nuxeo.ecm.core.api.impl.DocumentModelListImpl; import org.nuxeo.ecm.directory.SizeLimitExceededException; import org.nuxeo.ecm.platform.ui.web.component.list.UIEditableList; import org.nuxeo.ecm.platform.usermanager.UserManager; import org.nuxeo.ecm.webapp.helpers.ResourcesAccessor; /** * Methods to get user/groups suggestions from searches. * * @author Anahide Tchertchian */ @Name("userSuggestionActions") @Scope(PAGE) public class UserSuggestionActionsBean implements Serializable { private static final long serialVersionUID = 1L; private static final Log log = LogFactory.getLog(UserSuggestionActionsBean.class); public static final String USER_TYPE = "USER_TYPE"; public static final String GROUP_TYPE = "GROUP_TYPE"; public static final String TYPE_KEY_NAME = "type"; public static final String PREFIXED_ID_KEY_NAME = "prefixed_id"; public static final String ID_KEY_NAME = "id"; public static final String ENTRY_KEY_NAME = "entry"; @In(create = true) protected transient UserManager userManager; @In(create = true, required = false) protected transient FacesMessages facesMessages; @In(create = true) protected transient ResourcesAccessor resourcesAccessor; @RequestParameter protected String userSuggestionSearchType; protected String cachedUserSuggestionSearchType; @RequestParameter protected Integer userSuggestionMaxSearchResults; @RequestParameter protected Boolean hideVirtualGroups; protected Integer cachedUserSuggestionMaxSearchResults; protected Object cachedInput; protected Object cachedSuggestions; @RequestParameter protected String userSuggestionMessageId; /** * Id of the editable list component where selection ids are put. * <p> * Component must be an instance of {@link UIEditableList} */ @RequestParameter protected String suggestionSelectionListId; protected void addSearchOverflowMessage() { if (userSuggestionMessageId != null) { facesMessages.addToControl(userSuggestionMessageId, StatusMessage.Severity.ERROR, resourcesAccessor.getMessages().get( "label.security.searchOverFlow")); } else { log.error("Search overflow"); } } public List<DocumentModel> getGroupsSuggestions(Object input) throws ClientException { try { Map<String, DocumentModel> uniqueGroups = new HashMap<String, DocumentModel>(); String pattern = (String) input; for (String field : userManager.getGroupSearchFields()) { // XXX: this doesn't fetch group members (references) Map<String, Serializable> filter = new HashMap<String, Serializable>(); if (pattern != null && pattern != "") { filter.put(field, pattern); } if (Boolean.TRUE.equals(hideVirtualGroups)) { filter.put("__virtualGroup", false); } for (DocumentModel group : userManager.searchGroups(filter, filter.keySet())) { uniqueGroups.put(group.getId(), group); } } DocumentModelList groups = new DocumentModelListImpl(); groups.addAll(uniqueGroups.values()); Collections.sort(groups, new DocumentModelComparator( userManager.getGroupSchemaName(), getGroupsOrderBy())); return groups; } catch (SizeLimitExceededException e) { addSearchOverflowMessage(); return Collections.emptyList(); } catch (Exception e) { throw new ClientException("error searching for groups", e); } } protected Map<String, String> getGroupsOrderBy() throws ClientException { Map<String, String> order = new HashMap<String, String>(); order.put(userManager.getGroupLabelField(), DocumentModelComparator.ORDER_ASC); return order; } public List<DocumentModel> getUserSuggestions(Object input) throws ClientException { try { String searchPattern = (String) input; return userManager.searchUsers(searchPattern); } catch (SizeLimitExceededException e) { addSearchOverflowMessage(); return Collections.emptyList(); } catch (Exception e) { throw new ClientException("error searching for principals", e); } } protected boolean equals(Object item1, Object item2) { if (item1 == null && item2 == null) { return true; } else if (item1 == null) { return false; } else { return item1.equals(item2); } } public Object getSuggestions(Object input) throws ClientException { if (equals(cachedUserSuggestionSearchType, userSuggestionSearchType) && equals(cachedUserSuggestionMaxSearchResults, userSuggestionMaxSearchResults) && equals(cachedInput, input)) { return cachedSuggestions; } List<DocumentModel> users = Collections.emptyList(); if (USER_TYPE.equals(userSuggestionSearchType) || StringUtils.isEmpty(userSuggestionSearchType)) { users = getUserSuggestions(input); } List<DocumentModel> groups = Collections.emptyList(); if (GROUP_TYPE.equals(userSuggestionSearchType) || StringUtils.isEmpty(userSuggestionSearchType)) { groups = getGroupsSuggestions(input); } int userSize = users.size(); int groupSize = groups.size(); int totalSize = userSize + groupSize; if (userSuggestionMaxSearchResults != null && userSuggestionMaxSearchResults > 0) { if (userSize > userSuggestionMaxSearchResults || groupSize > userSuggestionMaxSearchResults || totalSize > userSuggestionMaxSearchResults) { addSearchOverflowMessage(); return null; } } List<Map<String, Object>> result = new ArrayList<Map<String, Object>>( totalSize); for (DocumentModel user : users) { Map<String, Object> entry = new HashMap<String, Object>(); entry.put(TYPE_KEY_NAME, USER_TYPE); entry.put(ENTRY_KEY_NAME, user); String userId = user.getId(); entry.put(ID_KEY_NAME, userId); entry.put(PREFIXED_ID_KEY_NAME, NuxeoPrincipal.PREFIX + userId); result.add(entry); } for (DocumentModel group : groups) { Map<String, Object> entry = new HashMap<String, Object>(); entry.put(TYPE_KEY_NAME, GROUP_TYPE); entry.put(ENTRY_KEY_NAME, group); String groupId = group.getId(); entry.put(ID_KEY_NAME, groupId); entry.put(PREFIXED_ID_KEY_NAME, NuxeoGroup.PREFIX + groupId); result.add(entry); } cachedInput = input; cachedUserSuggestionSearchType = userSuggestionSearchType; cachedUserSuggestionMaxSearchResults = userSuggestionMaxSearchResults; cachedSuggestions = result; return result; } // XXX: needs optimisation public Map<String, Object> getPrefixedUserInfo(String id) throws ClientException { Map<String, Object> res = new HashMap<String, Object>(); res.put(PREFIXED_ID_KEY_NAME, id); if (!StringUtils.isBlank(id)) { if (id.startsWith(NuxeoPrincipal.PREFIX)) { res.put(TYPE_KEY_NAME, USER_TYPE); String username = id.substring(NuxeoPrincipal.PREFIX.length()); res.put(ID_KEY_NAME, username); res.put(ENTRY_KEY_NAME, userManager.getUserModel(username)); } else if (id.startsWith(NuxeoGroup.PREFIX)) { res.put(TYPE_KEY_NAME, GROUP_TYPE); String groupname = id.substring(NuxeoGroup.PREFIX.length()); res.put(ID_KEY_NAME, groupname); res.put(ENTRY_KEY_NAME, userManager.getGroupModel(groupname)); } else { res.putAll(getUserInfo(id)); } } return res; } // XXX: needs optimisation public Map<String, Object> getUserInfo(String id) throws ClientException { Map<String, Object> res = new HashMap<String, Object>(); res.put(ID_KEY_NAME, id); if (userManager.getGroup(id) != null) { // group res.put(PREFIXED_ID_KEY_NAME, NuxeoGroup.PREFIX + id); res.put(TYPE_KEY_NAME, GROUP_TYPE); res.put(ENTRY_KEY_NAME, userManager.getGroupModel(id)); } else if (!StringUtils.isBlank(id)){ // user res.put(PREFIXED_ID_KEY_NAME, NuxeoPrincipal.PREFIX + id); res.put(TYPE_KEY_NAME, USER_TYPE); res.put(ENTRY_KEY_NAME, userManager.getUserModel(id)); } return res; } public Converter getUserConverter() { return new UserDisplayConverter(); } }