/*
* Copyright (c) Members of the EGEE Collaboration. 2006-2010.
* See http://www.eu-egee.org/partners/ for details on the copyright holders.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.glite.authz.pep.obligation;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import net.jcip.annotations.ThreadSafe;
import org.glite.authz.common.model.Obligation;
import org.glite.authz.common.model.Request;
import org.glite.authz.common.model.Result;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** A service for evaluating the obligations within a context. */
@ThreadSafe
public class ObligationService {
/** Class logger. */
private final Logger log = LoggerFactory.getLogger(ObligationService.class);
/** Read/write lock around the registered obligation handlers. */
private ReentrantReadWriteLock rwLock;
/** Registered obligation handlers. */
private Set<ObligationHandler> obligationHandlers;
/** Constructor. */
public ObligationService() {
rwLock = new ReentrantReadWriteLock(true);
obligationHandlers = new TreeSet<ObligationHandler>(new ObligationHandlerComparator());
}
/**
* Gets the registered obligation handlers.
*
* @return registered obligation handlers
*/
public Set<ObligationHandler> getObligationHandlers() {
return Collections.unmodifiableSet(obligationHandlers);
}
/**
* Adds an obligation handler to the list of registered handlers
*
* This method waits until a write lock is obtained for the set of registered obligation handlers.
*
* @param handler the handler to add to the list of registered handlers.
*/
public void addObligationhandler(ObligationHandler handler) {
if (handler == null) {
return;
}
Lock writeLock = rwLock.writeLock();
writeLock.lock();
try {
obligationHandlers.add(handler);
} finally {
writeLock.unlock();
}
}
/**
* Adds a collection of obligation handler to the list of registered handlers
*
* This method waits until a write lock is obtained for the set of registered obligation handlers.
*
* @param handlers the collection of handlers to add to the list of registered handlers.
*/
public void addObligationhandlers(Collection<ObligationHandler> handlers) {
if (handlers == null || handlers.isEmpty()) {
return;
}
Lock writeLock = rwLock.writeLock();
writeLock.lock();
try {
obligationHandlers.addAll(handlers);
} finally {
writeLock.unlock();
}
}
/**
* Removes an obligation handler from the list of registered handlers
*
* This method waits until a write lock is obtained for the set of registered obligation handlers.
*
* @param handler the handler to remove from the list of registered handlers.
*/
public void removeObligationHandler(ObligationHandler handler) {
if (handler == null) {
return;
}
Lock writeLock = rwLock.writeLock();
writeLock.lock();
try {
obligationHandlers.remove(handler);
} finally {
writeLock.unlock();
}
}
/**
* Processes the obligations within the effective XACML policy.
*
* This method waits until a read lock is obtained for the set of registered obligation handlers.
*
* @param request the authorization request
* @param result the result currently be processed
*
* @throws ObligationProcessingException thrown if there is a problem evaluating an obligation
*/
public void processObligations(Request request, Result result) throws ObligationProcessingException {
Lock readLock = rwLock.readLock();
readLock.lock();
try {
Iterator<ObligationHandler> handlerItr = obligationHandlers.iterator();
Map<String, Obligation> effectiveObligations = preprocessObligations(result);
log.debug("Obligations in effect for this result: {}", effectiveObligations.keySet());
ObligationHandler handler;
while (handlerItr.hasNext()) {
handler = handlerItr.next();
if (effectiveObligations.containsKey(handler.getObligationId())) {
log.debug("Processing obligation {}", handler.getObligationId());
handler.evaluateObligation(request, result);
}
}
} finally {
readLock.unlock();
}
}
/**
* Pre-processes the obligations returned within the result. This pre-processing determines which obligation
* handlers are active for a given result. An obligation handler is active if it exists and its {@code Fulfillon}
* property matches the result {@code Decision}.
*
* @param result the result currently be processed
*
* @return pre-processed obligations indexed by obligation ID
*/
protected Map<String, Obligation> preprocessObligations(Result result) {
HashMap<String, Obligation> effectiveObligations = new HashMap<String, Obligation>();
List<Obligation> obligations = result.getObligations();
if (obligations == null || obligations.isEmpty()) {
return effectiveObligations;
}
for (Obligation obligation : obligations) {
if (obligation != null && obligation.getFulfillOn() == result.getDecision()) {
effectiveObligations.put(obligation.getId(), obligation);
}
}
return effectiveObligations;
}
/** Comparator used to order obligation handlers by precedence. */
private class ObligationHandlerComparator implements Comparator<ObligationHandler> {
/** {@inheritDoc} */
public int compare(ObligationHandler o1, ObligationHandler o2) {
if (o1.getHandlerPrecedence() == o2.getHandlerPrecedence()) {
// If they have the same precedence sort lexigraphically
return o1.getObligationId().compareTo(o2.getObligationId());
}
if (o1.getHandlerPrecedence() < o2.getHandlerPrecedence()) {
return -1;
}
return 1;
}
}
}