/*********************************************************************************************************************** | |
* Copyright (c) 2008, 2011 Attensity Europe 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.util.Collection; | |
import java.util.HashSet; | |
import java.util.Map; | |
import java.util.Set; | |
import org.apache.commons.logging.Log; | |
import org.apache.commons.logging.LogFactory; | |
import org.eclipse.smila.blackboard.Blackboard; | |
import org.eclipse.smila.blackboard.BlackboardAccessException; | |
import org.eclipse.smila.datamodel.Any; | |
import org.eclipse.smila.datamodel.AnyMap; | |
import org.eclipse.smila.datamodel.AnySeq; | |
import org.eclipse.smila.datamodel.Value; | |
import org.eclipse.smila.processing.Pipelet; | |
import org.eclipse.smila.processing.ProcessingException; | |
import org.eclipse.smila.search.api.QueryConstants; | |
import org.eclipse.smila.security.SecurityAttribute; | |
import org.eclipse.smila.security.SecurityAttributes.AccessRightType; | |
import org.eclipse.smila.security.SecurityAttributes.EntityType; | |
import org.eclipse.smila.security.SecurityException; | |
import org.eclipse.smila.security.SecurityResolver; | |
import org.eclipse.smila.utils.service.ServiceUtils; | |
/** | |
* Sample Security Converter Pipelet. | |
*/ | |
public class SampleSecurityConverterPipelet implements Pipelet { | |
/** | |
* 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(SampleSecurityConverterPipelet.class); | |
/** | |
* The configuration. | |
*/ | |
private AnyMap _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; | |
/** | |
* Reads the configuration. | |
* | |
* {@inheritDoc} | |
*/ | |
@Override | |
public void configure(final AnyMap configuration) throws ProcessingException { | |
_configuration = configuration; | |
if (_configuration != null) { | |
_readUsersAttributeName = (String) _configuration.getStringValue(PROP_READ_USERS_ATTRIBUTE_NAME); | |
_resolveGroups = _configuration.getBooleanValue(PROP_RESOLVE_GROUPS) == null ? false : true; | |
_resolveUserNames = _configuration.getBooleanValue(PROP_RESOLVE_USER_NAMES) == null ? false : true; | |
_resolvedUserNameProperty = _configuration.getStringValue(PROP_RESOLVED_USER_NAME_PROPERTY_NAME); | |
} else { | |
_readUsersAttributeName = null; | |
_resolveGroups = false; | |
_resolvedUserNameProperty = null; | |
_resolveUserNames = false; | |
} | |
} | |
/** | |
* {@inheritDoc} | |
*/ | |
@Override | |
public String[] process(Blackboard blackboard, String[] recordIds) throws ProcessingException { | |
for (int i = 0; i < recordIds.length; i++) { | |
try { | |
final String executionModeValue = blackboard.getMetadata(recordIds[i]).getStringValue(EXECUTION_MODE); | |
if (executionModeValue != null) { | |
final ExecutionMode executionMode = ExecutionMode.valueOf(executionModeValue); | |
switch (executionMode) { | |
case INDEX: | |
convertToAttributes(blackboard, recordIds[i]); | |
break; | |
case SEARCH: | |
convertToFilter(blackboard, recordIds[i]); | |
break; | |
default: | |
break; | |
} | |
} | |
} catch (final Exception ex) { | |
if (_log.isErrorEnabled()) { | |
_log.error("error processing record " + recordIds[i], ex); | |
} | |
} | |
} // for | |
return recordIds; | |
} | |
/** | |
* gets the SecurityResolver service. | |
* | |
* @return the SecurityResolver. | |
*/ | |
private synchronized SecurityResolver getSecurityResolver() { | |
if (_securityResolver == null) { | |
try { | |
_securityResolver = ServiceUtils.getService(SecurityResolver.class); | |
} catch (final InterruptedException e) { | |
if (_log.isWarnEnabled()) { | |
_log.warn("Interrupted while getting SecurityResolver service."); | |
} | |
} | |
} | |
return _securityResolver; | |
} | |
/** | |
* Converts the security attributes into record metadata 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 String id) throws BlackboardAccessException, | |
SecurityException { | |
final SecurityAttribute sa = new SecurityAttribute(blackboard.getRecord(id)); | |
final Set<String> readAccessRights = getReadAccessRights(sa); | |
if (!readAccessRights.isEmpty()) { | |
// create attribute and add values | |
final AnySeq values = blackboard.getDataFactory().createAnySeq(); | |
for (final String value : readAccessRights) { | |
values.add(value); | |
} | |
blackboard.getMetadata(id).put(_readUsersAttributeName, values); | |
} | |
if (_log.isTraceEnabled()) { | |
_log.trace("converted security information for id " + id + " into attribute values"); | |
} | |
} | |
/** | |
* Converts the security information 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 convertToFilter(final Blackboard blackboard, final String id) throws BlackboardAccessException, | |
SecurityException { | |
final SecurityAttribute sa = new SecurityAttribute(blackboard.getRecord(id)); | |
final Set<String> readAccessRights = getReadAccessRights(sa); | |
if (!readAccessRights.isEmpty()) { | |
/* filter structure in search request record: | |
<Seq key="filter"> | |
<Map> | |
<Val key="attribute">_readUsersAttributeName</Val> | |
<Seq key="oneOf"> | |
<Val>pratchett</Val> | |
<Val>adams</Val> | |
</Seq> | |
</Map> | |
<Map> | |
<Val key="attribute">...</Val> | |
... | |
</Map> | |
</Seq> | |
*/ | |
// get filter section (create filter section if it doesn't exist) | |
AnySeq filterSection = (AnySeq) blackboard.getRecord(id).getMetadata().get(QueryConstants.FILTER); | |
if (filterSection == null) { | |
filterSection = blackboard.getDataFactory().createAnySeq(); | |
blackboard.getRecord(id).getMetadata().put(QueryConstants.FILTER, filterSection); | |
} | |
// find filter section for attribute (create if it doesn't exist) | |
AnyMap filterSectionForAttribute = null; | |
for (final Any filterSectionEntry : filterSection) { | |
if (_readUsersAttributeName.equals(((AnyMap) filterSectionEntry).getStringValue(QueryConstants.ATTRIBUTE))) { | |
filterSectionForAttribute = (AnyMap) filterSectionEntry; | |
} | |
} | |
if (filterSectionForAttribute == null) { | |
filterSectionForAttribute = blackboard.getDataFactory().createAnyMap(); | |
filterSectionForAttribute.put(QueryConstants.ATTRIBUTE, _readUsersAttributeName); | |
filterSection.add(filterSectionForAttribute); | |
} | |
// create security "oneOf" filter and put new filter in attribute's filter section | |
final AnySeq oneOfFilter = blackboard.getDataFactory().createAnySeq(); | |
for (final String value : readAccessRights) { | |
oneOfFilter.add(value); | |
} | |
filterSectionForAttribute.put(QueryConstants.FILTER_ONEOF, oneOfFilter); | |
// ensure that the attribute exists (some search engines need attribute value when applying filters) | |
if (!blackboard.getMetadata(id).containsKey(_readUsersAttributeName)) { | |
blackboard.getMetadata(id).put(_readUsersAttributeName, "dummy"); | |
} | |
} | |
if (_log.isTraceEnabled()) { | |
_log.trace("converted security information for id " + id + " into query filter"); | |
} | |
} | |
/** | |
* Gets the access rights values from the security information. 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(final SecurityAttribute sa) throws BlackboardAccessException, | |
SecurityException { | |
final HashSet<String> accessRights = new HashSet<String>(); | |
final AnySeq users = sa.getAccessRights(AccessRightType.READ, EntityType.PRINCIPALS); | |
final SecurityResolver securityResolver = getSecurityResolver(); | |
// check if there was a security resolver set, else skip any resolving | |
if (securityResolver != null) { | |
if (users != null) { | |
for (final Any user : users) { | |
final String userDN = securityResolver.resolvePrincipal(((Value) user).asString()); | |
accessRights.add(userDN); | |
} | |
} | |
// check if to resolve members of groups | |
if (_resolveGroups) { | |
final AnySeq groups = sa.getAccessRights(AccessRightType.READ, EntityType.GROUPS); | |
if (groups != null) { | |
for (final Any group : groups) { | |
final String groupDN = securityResolver.resolvePrincipal(((Value) group).asString()); | |
final Set<String> groupMembers = securityResolver.resolveGroupMembers(groupDN); | |
accessRights.addAll(groupMembers); | |
} // for | |
} // if | |
} // if | |
// check if to resolve user names to some display name | |
final Set<String> displayNames; | |
if (_resolveUserNames) { | |
displayNames = new HashSet<String>(); | |
for (final 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) { | |
for (final Any user : users) { | |
accessRights.add(((Value) user).asString()); | |
} | |
} | |
return accessRights; | |
} | |
} | |
} |