blob: f8a7fafb7ddb63ff60d628d836e7539ca2febdd3 [file] [log] [blame]
/***********************************************************************************************************************
* Copyright (c) 2008 empolis GmbH and brox IT Solutions GmbH. 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: Daniel Stucky (empolis GmbH) - initial API and implementation
**********************************************************************************************************************/
package org.eclipse.smila.security.processing;
import java.io.InputStream;
import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import javax.xml.bind.Unmarshaller;
import org.apache.commons.io.IOUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.eclipse.smila.blackboard.BlackboardAccessException;
import org.eclipse.smila.blackboard.Blackboard;
import org.eclipse.smila.blackboard.path.Path;
import org.eclipse.smila.datamodel.id.Id;
import org.eclipse.smila.datamodel.record.Annotation;
import org.eclipse.smila.datamodel.record.Literal;
import org.eclipse.smila.processing.ProcessingException;
import org.eclipse.smila.processing.ProcessingService;
import org.eclipse.smila.processing.configuration.PipeletConfiguration;
import org.eclipse.smila.processing.configuration.PipeletConfigurationLoader;
import org.eclipse.smila.processing.parameters.SearchAnnotations;
import org.eclipse.smila.security.SecurityAnnotation;
import org.eclipse.smila.security.SecurityException;
import org.eclipse.smila.security.SecurityResolver;
import org.eclipse.smila.security.SecurityAnnotations.AccessRightType;
import org.eclipse.smila.security.SecurityAnnotations.EntityType;
import org.eclipse.smila.utils.config.ConfigUtils;
import org.osgi.service.component.ComponentContext;
/**
* Sample Security Converter Index Service.
*/
public class SampleSecurityConverter implements ProcessingService {
/**
* name of bundle. Used in configuration reading.
*/
public static final String BUNDLE_NAME = "org.eclipse.smila.security.processing";
/**
* name of configuration file. Hardcoded for now (or fallback), configuration properties should be received from
* configuration service later.
*/
public static final String CONFIG_FILE = "SampleSecurityConverter.xml";
/**
* Constant for the property readUsersAttributeName.
*/
public static final String PROP_READ_USERS_ATTRIBUTE_NAME = "readUsersAttributeName";
/**
* Constant for the property resolveGroups.
*/
public static final String PROP_RESOLVE_GROUPS = "resolveGroups";
/**
* Constant for the property resolveUserNames.
*/
public static final String PROP_RESOLVE_USER_NAMES = "resolveUserNames";
/**
* Constant for the property resolvedUserNamePropertyName.
*/
public static final String PROP_RESOLVED_USER_NAME_PROPERTY_NAME = "resolvedUserNamePropertyName";
/**
* name of annotation configuring the type of execution.
*/
public static final String EXECUTION_MODE = "executionMode";
/**
* Types of execution modes this service supports.
*/
public enum ExecutionMode {
/**
* Add the record to the index.
*/
INDEX,
/**
* Delete the id from the index.
*/
SEARCH
};
/**
* local logger.
*/
private final Log _log = LogFactory.getLog(SampleSecurityConverter.class);
/**
* The configuration.
*/
private PipeletConfiguration _configuration;
/**
* Name of the attribute to store the users with read access in.
*/
private String _readUsersAttributeName;
/**
* Boolean flag if to resolve groups to users.
*/
private boolean _resolveGroups;
/**
* Boolean flag if to resolver users to display names.
*/
private boolean _resolveUserNames;
/**
* The property to retrieve for a user as display name.
*/
private String _resolvedUserNameProperty;
/**
* The SecurityResolver to use (optional).
*/
private SecurityResolver _securityResolver;
/**
* DS activate method.
*
* @param context
* ComponentContext
*
* @throws Exception
* if any error occurs
*/
protected void activate(final ComponentContext context) throws Exception {
try {
// load configuration
if (_configuration == null) {
readConfiguration();
_readUsersAttributeName =
(String) _configuration.getPropertyFirstValueNotNull(PROP_READ_USERS_ATTRIBUTE_NAME);
_resolveGroups =
((Boolean) _configuration.getPropertyFirstValueNotNull(PROP_RESOLVE_GROUPS)).booleanValue();
_resolveUserNames =
((Boolean) _configuration.getPropertyFirstValueNotNull(PROP_RESOLVE_USER_NAMES)).booleanValue();
_resolvedUserNameProperty =
(String) _configuration.getPropertyFirstValueNotNull(PROP_RESOLVED_USER_NAME_PROPERTY_NAME);
}
} catch (final Exception e) {
if (_log.isErrorEnabled()) {
_log.error("error initializing SampleSecurityConverter", e);
}
throw e;
}
}
/**
* DS deactivate method.
*
* @param context
* the ComponentContext
*
* @throws Exception
* if any error occurs
*/
protected void deactivate(final ComponentContext context) throws Exception {
try {
_configuration = null;
_readUsersAttributeName = null;
_resolvedUserNameProperty = null;
} catch (final Exception e) {
if (_log.isErrorEnabled()) {
_log.error("error deactivating SampleSecurityConverter", e);
}
throw e;
}
}
/**
* Sets the _securityResolver. Used by OSGi Declarative Services.
*
* @param securityResolver
* the SecurityResolver to set
*/
public void setSecurityResolver(final SecurityResolver securityResolver) {
_securityResolver = securityResolver;
}
/**
* Set the _securityResolver to null. Used by OSGi Declarative Services.
*
* @param securityResolver
* the SecurityResolver to unset
*/
public void unsetSecurityResolver(final SecurityResolver securityResolver) {
if (_securityResolver == securityResolver) {
_securityResolver = null;
}
}
/**
* {@inheritDoc}
*
* @see org.eclipse.smila.processing.ProcessingService#process(Blackboard, Id[])
*/
public Id[] process(final Blackboard blackboard, final Id[] recordIds) throws ProcessingException {
for (int i = 0; i < recordIds.length; i++) {
try {
final Annotation pipeletAnnotation = blackboard.getAnnotation(recordIds[i], null, getClass().getName());
if (pipeletAnnotation != null) {
final String executionModeValue = pipeletAnnotation.getNamedValue(EXECUTION_MODE);
final ExecutionMode executionMode = ExecutionMode.valueOf(executionModeValue);
switch (executionMode) {
case INDEX:
convertToAttributes(blackboard, recordIds[i]);
break;
case SEARCH:
converteToFilter(blackboard, recordIds[i]);
break;
default:
break;
}
}
} catch (final Exception ex) {
if (_log.isErrorEnabled()) {
_log.error("error processing record " + recordIds[i], ex);
}
}
} // for
return recordIds;
}
/**
* Converts the security annotations of a record into an attribute with values for indexing.
*
* @param blackboard
* the BlackboardService
* @param id
* the record Id
* @throws BlackboardAccessException
* if any error occurs
* @throws SecurityException
* if any security error occurs
*/
private void convertToAttributes(final Blackboard blackboard, final Id id)
throws BlackboardAccessException, SecurityException {
final SecurityAnnotation sa = new SecurityAnnotation(blackboard.getRecord(id));
final Set<String> readAccessRights = getReadAccessRights(sa);
if (!readAccessRights.isEmpty()) {
// create attribute and add values
final Path path = new Path(_readUsersAttributeName);
for (String value : readAccessRights) {
final Literal literal = blackboard.getRecord(id).getFactory().createLiteral();
literal.setStringValue(value);
blackboard.addLiteral(id, path, literal);
}
}
if (_log.isTraceEnabled()) {
_log.trace("converted security annotations for id " + id + " into attribute values");
}
}
/**
* Converts the security annotations of a record into a query filter and appends it to the query.
*
* @param blackboard
* the BlackboardService
* @param id
* the record Id
* @throws BlackboardAccessException
* if any error occurs
* @throws SecurityException
* if any security error occurs
*/
private void converteToFilter(final Blackboard blackboard, final Id id) throws BlackboardAccessException,
SecurityException {
final SecurityAnnotation sa = new SecurityAnnotation(blackboard.getRecord(id));
final Set<String> readAccessRights = getReadAccessRights(sa);
if (!readAccessRights.isEmpty()) {
// create enumeration filter and add it to the configured _readUsersAttributeName
final Annotation filter = blackboard.getRecord(id).getFactory().createAnnotation();
filter.setNamedValue(SearchAnnotations.FILTER_TYPE, SearchAnnotations.FilterType.ENUMERATION.toString());
filter.setNamedValue(SearchAnnotations.FILTER_MODE, SearchAnnotations.FilterMode.ANY.name());
for (String value : readAccessRights) {
filter.addAnonValue(value);
}
final Path readUsersAttributePath = new Path(_readUsersAttributeName);
// ensure that the attribute exists
if (!blackboard.hasAttribute(id, readUsersAttributePath)) {
final Literal literal = blackboard.getRecord(id).getFactory().createLiteral();
literal.setStringValue("dummy");
blackboard.addLiteral(id, readUsersAttributePath, literal);
}
blackboard.getRecord(id).getMetadata().getAttribute(_readUsersAttributeName).addAnnotation(
SearchAnnotations.FACET_FILTER, filter);
blackboard.addAnnotation(id, readUsersAttributePath, SearchAnnotations.FACET_FILTER, filter);
}
if (_log.isTraceEnabled()) {
_log.trace("converted security annotations for id " + id + " into query filter");
}
}
/**
* Gets the access rights values from the security annotations. Depending on the configuration the return values are
* the plain values provided by a crawler/search client or are resolved against a SecurityResolver.
*
* @param sa
* the SecurityAnnotation
* @return a Set of Strings containing the values
* @throws BlackboardAccessException
* if any error occurs
* @throws SecurityException
* if any security error occurs
*/
private Set<String> getReadAccessRights(SecurityAnnotation sa) throws BlackboardAccessException,
SecurityException {
final HashSet<String> accessRights = new HashSet<String>();
final Collection<String> users =
sa.getAccessRights(AccessRightType.READ, EntityType.PRINCIPALS).getAnonValues();
// check if there was a security resolver set, else skip any resolving
if (_securityResolver != null) {
if (users != null) {
for (String user : users) {
final String userDN = _securityResolver.resolvePrincipal(user);
accessRights.add(userDN);
}
}
// check if to resolve members of groups
if (_resolveGroups) {
final Collection<String> groups =
sa.getAccessRights(AccessRightType.READ, EntityType.GROUPS).getAnonValues();
if (groups != null) {
for (String group : groups) {
final String groupDN = _securityResolver.resolvePrincipal(group);
final Set<String> groupMembers = _securityResolver.resolveGroupMembers(groupDN);
accessRights.addAll(groupMembers);
} // for
} // if
} // if
// check if to resolve user names to some display name
Set<String> displayNames = new HashSet<String>();
if (_resolveUserNames) {
for (String principalDN : accessRights) {
final Map<String, Collection<String>> properties = _securityResolver.getProperties(principalDN);
final Collection<String> resolvedUserNames = properties.get(_resolvedUserNameProperty);
if (resolvedUserNames != null && !resolvedUserNames.isEmpty()) {
displayNames.add(resolvedUserNames.iterator().next());
}
} // for
} else {
displayNames = accessRights;
}
return displayNames;
} else {
if (users != null) {
accessRights.addAll(users);
}
return accessRights;
}
}
/**
* Read configuration property file.
*
* @throws ProcessingException
* error reading configuration file
*/
private void readConfiguration() throws ProcessingException {
InputStream configurationFileStream = null;
try {
configurationFileStream = ConfigUtils.getConfigStream(BUNDLE_NAME, CONFIG_FILE);
final Unmarshaller unmarshaller = PipeletConfigurationLoader.createPipeletConfigurationUnmarshaller();
_configuration = (PipeletConfiguration) unmarshaller.unmarshal(configurationFileStream);
} catch (final Exception ex) {
if (_log.isErrorEnabled()) {
_log.error("Could not read configuration property file " + CONFIG_FILE, ex);
}
throw new ProcessingException("Could not read configuration property file " + CONFIG_FILE, ex);
} finally {
IOUtils.closeQuietly(configurationFileStream);
}
}
}