/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright (c) 2009 Sun Microsystems Inc. All Rights Reserved * * The contents of this file are subject to the terms * of the Common Development and Distribution License * (the License). You may not use this file except in * compliance with the License. * * You can obtain a copy of the License at * https://opensso.dev.java.net/public/CDDLv1.0.html or * opensso/legal/CDDLv1.0.txt * See the License for the specific language governing * permission and limitations under the License. * * When distributing Covered Code, include this CDDL * Header Notice in each file and include the License file * at opensso/legal/CDDLv1.0.txt. * If applicable, add the following below the CDDL Header, * with the fields enclosed by brackets [] replaced by * your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * * $Id: PrivilegeEvaluator.java,v 1.2 2009/10/07 06:36:40 veiming Exp $ * * Portions Copyrighted 2010-2016 ForgeRock AS. */ package com.sun.identity.entitlement; import static org.forgerock.openam.entitlement.PolicyConstants.SUPER_ADMIN_SUBJECT; import static org.forgerock.openam.entitlement.utils.EntitlementUtils.getApplicationService; import static org.forgerock.openam.entitlement.utils.EntitlementUtils.getEntitlementConfiguration; import java.security.Principal; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import javax.security.auth.Subject; import org.forgerock.openam.entitlement.PolicyConstants; import org.forgerock.openam.entitlement.PrivilegeEvaluatorContext; import org.forgerock.openam.session.util.AppTokenHandler; import org.forgerock.openam.utils.CollectionUtils; import com.sun.identity.entitlement.interfaces.IThreadPool; import com.sun.identity.shared.debug.Debug; /** * This class evaluates entitlements of a subject for a given resource * and a environment parameters. */ class PrivilegeEvaluator { private String realm = "/"; private Subject adminSubject; private Subject subject; private String applicationName; private String normalisedResourceName; private String requestedResourceName; private Map> envParameters; private ResourceSearchIndexes indexes; private List> resultQ = new LinkedList>(); private Application application; private Set actionNames; private EntitlementCombiner entitlementCombiner; private boolean recursive; private EntitlementException eException; private final Lock lock = new ReentrantLock(); private final Condition hasResults = lock.newCondition(); // Static variables // TODO determine number of tasks per thread private static int evalThreadSize = Evaluator.DEFAULT_POLICY_EVAL_THREAD; private static final int TASKS_PER_THREAD = 5; private static final IThreadPool threadPool; private static final boolean isMultiThreaded; static { EntitlementConfiguration ec = getEntitlementConfiguration(SUPER_ADMIN_SUBJECT, "/"); Set setPolicyEvalThread = ec.getConfiguration( EntitlementConfiguration.POLICY_EVAL_THREAD_SIZE); if ((setPolicyEvalThread != null) && !setPolicyEvalThread.isEmpty()) { try { evalThreadSize = Integer.parseInt(setPolicyEvalThread. iterator().next()); } catch (NumberFormatException e) { PolicyConstants.DEBUG.error( "PrivilegeEvaluator.: get evaluation thread pool size", e); } } isMultiThreaded = evalThreadSize > 1; threadPool = isMultiThreaded ? new EntitlementThreadPool(evalThreadSize) : new SequentialThreadPool(); } /** * Initializes the evaluator. * * @param adminSubject Administrator subject which is used fo evaluation. * @param subject Subject to be evaluated. * @param realm Realm Name * @param applicationName Application Name. * @param normalisedResourceName The normalised resource name. * @param requestedResourceName The requested resource name. * @param actions Action names. * @param envParameters Environment parameters. * @param recursive true for sub tree evaluation * @throws com.sun.identity.entitlement.EntitlementException if * initialization fails. */ private void init( Subject adminSubject, Subject subject, String realm, String applicationName, String normalisedResourceName, String requestedResourceName, Set actions, Map> envParameters, boolean recursive ) throws EntitlementException { this.adminSubject = adminSubject; this.subject = subject; this.realm = realm; this.applicationName = applicationName; this.normalisedResourceName = normalisedResourceName; this.requestedResourceName = requestedResourceName; this.envParameters = envParameters; this.actionNames = new HashSet(); if (CollectionUtils.isNotEmpty(actions)) { this.actionNames.addAll(actions); } Application appl = getApplication(); entitlementCombiner = appl.getEntitlementCombiner(); entitlementCombiner.init(realm, applicationName, normalisedResourceName, requestedResourceName, this.actionNames, recursive); this.recursive = recursive; if (PolicyConstants.DEBUG.messageEnabled()) { Debug debug = PolicyConstants.DEBUG; debug.message("[PolicyEval] PrivilegeEvaluator:init()"); debug.message("[PolicyEval] subject: " + getPrincipalId(subject)); debug.message("[PolicyEval] realm: " + realm); debug.message("[PolicyEval] applicationName: " + applicationName); debug.message("[PolicyEval] normalisedResourceName: " + this.normalisedResourceName); debug.message("[PolicyEval] requestedResourceName: " + this.requestedResourceName); debug.message("[PolicyEval] actions: " + actionNames); if ((envParameters != null) && !envParameters.isEmpty()) { debug.message("[PolicyEval] envParameters: " + envParameters.toString()); } } } private static String getPrincipalId(Subject subject) { if (subject == null) { return ""; } Set userPrincipals = subject.getPrincipals(); return ((userPrincipals != null) && !userPrincipals.isEmpty()) ? userPrincipals.iterator().next().getName() : null; } /** * Returrns true if the subject has privilege to have the * given entitlement. * * @param adminSubject Administrator subject which is used for evaluation. * @param subject Subject to be evaluated. * @param applicationName Application Name. * @param entitlement Entitlement to be evaluated. * @param envParameters Environment parameters. * @return true if the subject has privilege to have the * given entitlement. * @throws com.sun.identity.entitlement.EntitlementException if * evaluation fails. */ public boolean hasEntitlement( String realm, Subject adminSubject, Subject subject, String applicationName, Entitlement entitlement, Map> envParameters ) throws EntitlementException { init(adminSubject, subject, realm, applicationName, entitlement.getResourceName(), entitlement.getRequestedResourceName(), entitlement.getActionValues().keySet(), envParameters, false); entitlement.setApplicationName(applicationName); indexes = entitlement.getResourceSearchIndexes(adminSubject, realm); List results = evaluate(realm); Entitlement result = results.get(0); for (String action : entitlement.getActionValues().keySet()) { Boolean b = result.getActionValue(action); // TODO, use policy decision combining algorithm // Default is deny overrides if ((b == null) || !b.booleanValue()) { return false; } } return true; } /** * Returns list of entitlements which is entitled to a subject. * * @param adminSubject Administrator subject which is used for evaluation. * @param subject Subject to be evaluated. * @param applicationName Application Name. * @param normalisedResourceName The normalised resource name. * @param requestedResourceName The requested resource name. * @param envParameters Environment parameters. * @param recursive true for sub tree evaluation. * @return true if the subject has privilege to have the * given entitlement. * @throws com.sun.identity.entitlement.EntitlementException if * evaluation fails. */ public List evaluate( String realm, Subject adminSubject, Subject subject, String applicationName, String normalisedResourceName, String requestedResourceName, Map> envParameters, boolean recursive ) throws EntitlementException { init(adminSubject, subject, realm, applicationName, normalisedResourceName, requestedResourceName, null, envParameters, recursive); indexes = getApplication().getResourceSearchIndex(normalisedResourceName, realm); return evaluate(realm); } /** * Responsible for the core evaluation of policies associated with the request resource. * * @param realm * the evaluation realm * * @return a list of applicable entitlements * * @throws EntitlementException */ private List evaluate(String realm) throws EntitlementException { final Debug debug = PolicyConstants.DEBUG; // Search for relevant policies. final SubjectAttributesManager sam = SubjectAttributesManager.getInstance(adminSubject, realm); final Set subjectIndexes = sam.getSubjectSearchFilter(subject, applicationName); final PrivilegeIndexStore indexStore = PrivilegeIndexStore.getInstance(adminSubject, realm); final Iterator policyIterator = indexStore.search(realm, indexes, subjectIndexes, recursive); int totalCount = 0; IPrivilege policy; // First collect policies to be evaluated locally. final Set localBatch = new HashSet(2 * TASKS_PER_THREAD); while (totalCount < TASKS_PER_THREAD && policyIterator.hasNext()) { policy = policyIterator.next(); if (policy instanceof ReferralPrivilege) { // We want to ignore referrals - deprecated. continue; } if (debug.messageEnabled()) { debug.message("[PolicyEval] PolicyEvaluator.evaluate"); debug.message("[PolicyEval] search result: privilege=" + policy.getName()); } localBatch.add(policy); totalCount++; } // Define an evaluation context. final PrivilegeEvaluatorContext context = new PrivilegeEvaluatorContext(realm, normalisedResourceName, applicationName); final Object appToken = AppTokenHandler.getAndClear(); // Submit additional policies to be executed by worker threads. final Set threadBatch = new HashSet(2 * TASKS_PER_THREAD); boolean tasksSubmitted = false; while (policyIterator.hasNext()) { tasksSubmitted = true; policy = policyIterator.next(); if (policy instanceof ReferralPrivilege) { // We want to ignore referrals - deprecated. continue; } if (debug.messageEnabled()) { debug.message("[PolicyEval] PolicyEvaluator.evaluate"); debug.message("[PolicyEval] search result: privilege=" + policy.getName()); } threadBatch.add(policy); totalCount++; if (threadBatch.size() == TASKS_PER_THREAD) { final Set copiedBatch = new HashSet(threadBatch); threadPool.submit(new PrivilegeTask(this, copiedBatch, isMultiThreaded, appToken, context)); threadBatch.clear(); } } if (!threadBatch.isEmpty()) { // Submit any remaining policies. threadPool.submit(new PrivilegeTask(this, threadBatch, isMultiThreaded, appToken, context)); } // Submit the local policies. final Runnable localTask = new PrivilegeTask(this, localBatch, tasksSubmitted, appToken, context); localTask.run(); // Wait for submitted threads to complete evaluation. if (tasksSubmitted) { if (isMultiThreaded) { receiveEvalResults(totalCount); } else { boolean isDone = false; while (!resultQ.isEmpty() && !isDone) { entitlementCombiner.add(resultQ.remove(0)); isDone = entitlementCombiner.isDone(); } } } else if (eException == null) { boolean isDone = false; while (!resultQ.isEmpty() && !isDone) { entitlementCombiner.add(resultQ.remove(0)); isDone = entitlementCombiner.isDone(); } } if (eException != null) { // Throw caught exception. throw eException; } return entitlementCombiner.getResults(); } private void receiveEvalResults(int totalCount) { int counter = 0; lock.lock(); boolean isDone = (eException != null); try { while (!isDone && (counter < totalCount)) { if (resultQ.isEmpty()) { hasResults.await(); } while (!resultQ.isEmpty() && !isDone) { entitlementCombiner.add(resultQ.remove(0)); isDone = entitlementCombiner.isDone(); counter++; } } } catch (InterruptedException ex) { PolicyConstants.DEBUG.error("PrivilegeEvaluator.evaluate", ex); } finally { lock.unlock(); } } private Application getApplication() throws EntitlementException { if (application == null) { application = getApplicationService(SUPER_ADMIN_SUBJECT, realm).getApplication(applicationName); // If application is still null, throw an exception if (application == null) { String[] params = { realm }; throw (new EntitlementException(248, params)); } } return application; } class PrivilegeTask implements Runnable { final PrivilegeEvaluator parent; private final Set privileges; private final boolean isThreaded; private final Object context; private final PrivilegeEvaluatorContext ctx; PrivilegeTask(PrivilegeEvaluator parent, Set privileges, boolean isThreaded, Object context, PrivilegeEvaluatorContext ctx) { this.parent = parent; this.privileges = privileges; this.isThreaded = isThreaded; this.context = context; this.ctx = ctx; } public void run() { PrivilegeEvaluatorContext.setCurrent(ctx); try { for (final IPrivilege eval : privileges) { List entitlements = eval.evaluate( parent.adminSubject, parent.realm, parent.subject, parent.applicationName, parent.normalisedResourceName, parent.requestedResourceName, parent.actionNames, parent.envParameters, parent.recursive, context); if (entitlements != null) { if (isThreaded) { try { parent.lock.lock(); parent.resultQ.add(entitlements); parent.hasResults.signal(); } finally { parent.lock.unlock(); } } else { parent.resultQ.add(entitlements); } } } } catch (EntitlementException ex) { if (isThreaded) { try { parent.lock.lock(); parent.eException = ex; parent.hasResults.signal(); } finally { parent.lock.unlock(); } } else { parent.eException = ex; } } } } }