diff options
Diffstat (limited to 'bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/permadmin/SecurityTable.java')
-rw-r--r-- | bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/permadmin/SecurityTable.java | 143 |
1 files changed, 110 insertions, 33 deletions
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/permadmin/SecurityTable.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/permadmin/SecurityTable.java index bdd1c188c..43dc91154 100644 --- a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/permadmin/SecurityTable.java +++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/permadmin/SecurityTable.java @@ -7,13 +7,18 @@ * * Contributors: * IBM Corporation - initial API and implementation + * Connexta, LLC - evaluation cache implementation *******************************************************************************/ package org.eclipse.osgi.internal.permadmin; import java.security.Permission; import java.security.PermissionCollection; import java.util.Enumeration; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + import org.eclipse.osgi.internal.permadmin.SecurityRow.Decision; +import org.osgi.service.condpermadmin.Condition; public class SecurityTable extends PermissionCollection { private static final long serialVersionUID = -1800193310096318060L; @@ -22,9 +27,14 @@ public class SecurityTable extends PermissionCollection { static final int ABSTAIN = 0x0004; static final int POSTPONED = 0x0008; + private static final int MUTABLE = 0x0016; + private final SecurityRow[] rows; private final SecurityAdmin securityAdmin; + private final transient Map<EvaluationCacheKey, Integer> evaluationCache = + new ConcurrentHashMap<>(10000); + public SecurityTable(SecurityAdmin securityAdmin, SecurityRow[] rows) { if (rows == null) throw new NullPointerException("rows cannot be null!!"); //$NON-NLS-1$ @@ -37,63 +47,130 @@ public class SecurityTable extends PermissionCollection { } int evaluate(BundlePermissions bundlePermissions, Permission permission) { - if (isEmpty()) + if (bundlePermissions == null) { return ABSTAIN; + } + EvaluationCacheKey evaluationCacheKey = new EvaluationCacheKey(bundlePermissions, + permission); + if (isEmpty()) { + evaluationCache.put(evaluationCacheKey, ABSTAIN); + return ABSTAIN; + } + + //can't short-circuit early, so try cache + Integer result = evaluationCache.get(evaluationCacheKey); + boolean hasMutable = false; + if (result != null) { + hasMutable = (result & MUTABLE) == MUTABLE; + if (!hasMutable) { + return result; + } + } + //cache miss or has mutable rows boolean postponed = false; Decision[] results = new Decision[rows.length]; int immediateDecisionIdx = -1; // evaluate each row - for (int i = 0; i < rows.length; i++) { + for (int i = 0; i < rows.length && immediateDecisionIdx == -1; i++) { + if (result == null) { + //check all conditions for any that are mutable, this will turn off the cache + hasMutable |= checkMutable(bundlePermissions, + evaluationCacheKey, + rows[i]); + } try { results[i] = rows[i].evaluate(bundlePermissions, permission); - } catch (Throwable t) { + } catch (Exception e) { // TODO log? results[i] = SecurityRow.DECISION_ABSTAIN; } - if ((results[i].decision & ABSTAIN) != 0) + if ((results[i].decision & ABSTAIN) == ABSTAIN) continue; // ignore this row and continue to next row - if ((results[i].decision & POSTPONED) != 0) { + if ((results[i].decision & POSTPONED) == POSTPONED) { // row is postponed; we can no longer return quickly on a denied decision postponed = true; continue; // continue to next row } - if (!postponed) + if (!postponed) { // no postpones encountered yet; we can return the decision quickly + if (!hasMutable) { + evaluationCache.put(evaluationCacheKey, results[i].decision); + } return results[i].decision; // return GRANTED or DENIED + } // got an immediate answer; but it is after a postponed condition. // no need to process the rest of the rows immediateDecisionIdx = i; - break; } - if (postponed) { - int immediateDecision = immediateDecisionIdx < 0 ? DENIED : results[immediateDecisionIdx].decision; - // iterate over all postponed conditions; - // if they all provide the same decision as the immediate decision then return the immediate decision - boolean allSameDecision = true; - int i = immediateDecisionIdx < 0 ? results.length - 1 : immediateDecisionIdx - 1; - for (; i >= 0 && allSameDecision; i--) { - if (results[i] == null) - continue; - if ((results[i].decision & POSTPONED) != 0) { - if ((results[i].decision & immediateDecision) == 0) - allSameDecision = false; - else - results[i] = SecurityRow.DECISION_ABSTAIN; // we can clear postpones with the same decision as the immediate + Integer immediateDecision = handlePostponedConditions(evaluationCacheKey, + hasMutable, + postponed, + results, + immediateDecisionIdx); + if (immediateDecision != null) + return immediateDecision; + int finalDecision = postponed ? POSTPONED : ABSTAIN; + if (!hasMutable && (finalDecision & POSTPONED) != POSTPONED) { + evaluationCache.put(evaluationCacheKey, finalDecision); + } + return finalDecision; + } + + private boolean checkMutable(BundlePermissions bundlePermissions, + EvaluationCacheKey evaluationCacheKey, SecurityRow row) { + Condition[] conditions = row.getConditions(bundlePermissions); + if (conditions != null) { + for (Condition condition : conditions) { + if (condition.isMutable()) { + evaluationCache.put(evaluationCacheKey, MUTABLE); + return true; } } - if (allSameDecision) - return immediateDecision; - - // we now are forced to postpone; we need to also remember the postponed decisions and - // the immediate decision if there is one. - EquinoxSecurityManager equinoxManager = securityAdmin.getSupportedSecurityManager(); - if (equinoxManager == null) - // TODO this is really an error condition. - // This should never happen. We checked for a supported manager when the row was postponed - return ABSTAIN; - equinoxManager.addConditionsForDomain(results); } - return postponed ? POSTPONED : ABSTAIN; + return false; + } + + private Integer handlePostponedConditions(EvaluationCacheKey evaluationCacheKey, + boolean hasMutable, boolean postponed, Decision[] results, int immediateDecisionIdx) { + if (postponed) { + int immediateDecision = immediateDecisionIdx < 0 ? DENIED : results[immediateDecisionIdx].decision; + // iterate over all postponed conditions; + // if they all provide the same decision as the immediate decision then return the immediate decision + boolean allSameDecision = true; + int i = immediateDecisionIdx < 0 ? results.length - 1 : immediateDecisionIdx - 1; + for (; i >= 0 && allSameDecision; i--) { + if ((results[i].decision & POSTPONED) == POSTPONED) { + if ((results[i].decision & immediateDecision) == 0) + allSameDecision = false; + else + results[i] = SecurityRow.DECISION_ABSTAIN; // we can clear postpones with the same decision as the immediate + } + } + if (allSameDecision) { + if (!hasMutable) { + evaluationCache.put(evaluationCacheKey, immediateDecision); + } + return immediateDecision; + } + + // we now are forced to postpone; we need to also remember the postponed decisions and + // the immediate decision if there is one. + EquinoxSecurityManager equinoxManager = securityAdmin.getSupportedSecurityManager(); + if (equinoxManager == null) { + // TODO this is really an error condition. + // This should never happen. We checked for a supported manager when the row was postponed + if (!hasMutable) { + evaluationCache.put(evaluationCacheKey, ABSTAIN); + } + return ABSTAIN; + } + equinoxManager.addConditionsForDomain(results); + } + return null; + } + + void clearEvaluationCache() { + evaluationCache.clear(); } SecurityRow getRow(int i) { |