/******************************************************************************* * Copyright (c) 2010-2014 SAP AG and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * SAP AG - initial API and implementation *******************************************************************************/ package org.eclipse.skalli.core.user; import java.text.MessageFormat; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; import java.util.regex.Pattern; import org.apache.commons.lang.StringUtils; import org.eclipse.skalli.model.Issue; import org.eclipse.skalli.model.Issuer; import org.eclipse.skalli.model.Severity; import org.eclipse.skalli.model.User; import org.eclipse.skalli.model.ValidationException; import org.eclipse.skalli.services.entity.EntityServiceBase; import org.eclipse.skalli.services.extension.DataMigration; import org.eclipse.skalli.services.user.UserService; import org.osgi.service.component.ComponentConstants; import org.osgi.service.component.ComponentContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Implementation of {@link UserService} based on XStream-serialized * {@link User} instances. The service expects to find XML files named * <tt><userId>.xml</tt> in the <tt>$workdir/storage/User</tt> * directory.<br> * Note, by default this service implementation is disabled (see component * descriptor <tt>LocalUserComponent</tt>). It must be enabled explicitly * in the OSGi shell (<tt>enable</tt> command, LDAP user service should * be disabled before that). */ public class LocalUserComponent extends EntityServiceBase<User> implements UserService, Issuer { private static final Logger LOG = LoggerFactory.getLogger(LocalUserComponent.class); private static final int CURRENT_MODEL_VERISON = 20; private Map<String, User> cache; protected void activate(ComponentContext context) { LOG.info(MessageFormat.format("[UserService][local] {0} : activated", (String) context.getProperties().get(ComponentConstants.COMPONENT_NAME))); } protected void deactivate(ComponentContext context) { LOG.info(MessageFormat.format("[UserService][local] {0} : deactivated", (String) context.getProperties().get(ComponentConstants.COMPONENT_NAME))); } @Override public Class<User> getEntityClass() { return User.class; } @Override public int getModelVersion() { return CURRENT_MODEL_VERISON; } @Override public Map<String, Class<?>> getAliases() { Map<String, Class<?>> aliases = super.getAliases(); aliases.put("entity-user", User.class); //$NON-NLS-1$ return aliases; } @Override public Set<DataMigration> getMigrations() { Set<DataMigration> migrations = super.getMigrations(); migrations.add(new LocalUserDataMigration19()); return migrations; } private synchronized Map<String, User> getCache() { if (cache == null) { cache = new HashMap<String, User>(); for (User user: getAll()) { cache.put(user.getUserId(), user); } } return cache; } @Override public String getType() { return "local"; //$NON-NLS-1$ } @Override public synchronized List<User> getUsers() { return getAll(); } @Override public User getUserById(String userId) { User user = getCache().get(userId); return user != null ? user : new User(userId); } @Override public List<User> findUser(String search) { List<User> result = new ArrayList<User>(); if (StringUtils.isNotBlank(search)) { String[] parts = StringUtils.split(NormalizeUtil.normalize(search), " ,"); //$NON-NLS-1$ Pattern[] patterns = new Pattern[parts.length]; for (int i = 0; i < parts.length; ++i) { patterns[i] = Pattern.compile(parts[i] + ".*", Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE) ;//$NON-NLS-1$ } for (User user : getAll()) { if (parts.length == 1) { match(user, patterns[0], result); } else if (parts.length == 2) { // givenname surname ('Michael Ochmann') if (matches(patterns, user.getFirstname(), user.getLastname())) { result.add(user); } // surname givenname('Ochmann, Michael') else if (matches(patterns, user.getLastname(), user.getFirstname())) { result.add(user); } } else if (parts.length == 3) { // givenname initial surname, e.g. 'Michael R. Ochmann' // or title givenname surname or given name surname title if (matches(patterns, user.getFirstname(), null, user.getLastname())) { result.add(user); } else if (matches(patterns, user.getLastname(), null, user.getFirstname())) { result.add(user); } else if (matches(patterns, null, user.getFirstname(), user.getLastname())) { result.add(user); } else if (matches(patterns, user.getFirstname(), user.getLastname(), null)) { result.add(user); } } } if (result.isEmpty()) { for (User user : getAll()) { // try to match each part individually for (int i = 0; i < parts.length; ++i) { match(user, patterns[i], result); } } } } return result; } private boolean matches(Pattern[] pattern, String... strings) { for (int i = 0; i < strings.length; ++i) { if (StringUtils.isNotBlank(strings[i]) && pattern[i].matcher(strings[i]).matches()) { return true; } } return false; } private void match(User user, Pattern pattern, List<User> result) { Pattern[] patterns = new Pattern[] { pattern }; // try a match with surname* if (matches(patterns, user.getLastname())) { result.add(user); } // try a match with firstname* else if (matches(patterns, user.getFirstname())) { result.add(user); } //try a match with the account name if (result.isEmpty() && matches(patterns, user.getUserId())) { result.add(user); } //try a match with the mail address if (result.isEmpty() && matches(patterns, user.getEmail())) { result.add(user); } //try a match with the department if (result.isEmpty() && matches(patterns, user.getDepartment())) { result.add(user); } } @Override public Set<User> getUsersById(Set<String> userIds) { Set<User> result = new HashSet<User>(); if (userIds != null) { for (String userId : userIds) { User user = getCache().get(userId); if (user != null) { result.add(user); } else { result.add(new User(userId)); } } } return result; } @Override protected void validateEntity(User entity) throws ValidationException { SortedSet<Issue> issues = validate(entity, Severity.FATAL); if (issues.size() > 0) { throw new ValidationException("User could not be saved due to the following reasons:", issues); } } @Override protected SortedSet<Issue> validateEntity(User entity, Severity minSeverity) { TreeSet<Issue> issues = new TreeSet<Issue>(); String userId = entity.getUserId(); if (StringUtils.isBlank(userId)) { issues.add(new Issue(Severity.FATAL, LocalUserComponent.class, entity.getUuid(), "Users must have a unique user identifier which is not blank")); } return issues; } }