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;
static final int GRANTED = 0x0001;
static final int DENIED = 0x0002;
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!!");
this.rows = rows;
this.securityAdmin = securityAdmin;
}
boolean isEmpty() {
return rows.length == 0;
}
int evaluate(BundlePermissions bundlePermissions, Permission permission) {
if (bundlePermissions == null) {
return ABSTAIN;
}
EvaluationCacheKey evaluationCacheKey = new EvaluationCacheKey(bundlePermissions, permission);
if (isEmpty()) {
evaluationCache.put(evaluationCacheKey, ABSTAIN);
return ABSTAIN;
}
Integer result = evaluationCache.get(evaluationCacheKey);
boolean hasMutable = false;
if (result != null) {
hasMutable = (result & MUTABLE) == MUTABLE;
if (!hasMutable) {
return result;
}
}
boolean postponed = false;
Decision[] results = new Decision[rows.length];
int immediateDecisionIdx = -1;
for (int i = 0; i < rows.length && immediateDecisionIdx == -1; i++) {
if (result == null) {
hasMutable |= checkMutable(bundlePermissions, evaluationCacheKey, rows[i]);
}
try {
results[i] = rows[i].evaluate(bundlePermissions, permission);
} catch (Exception e) {
results[i] = SecurityRow.DECISION_ABSTAIN;
}
if ((results[i].decision & ABSTAIN) == ABSTAIN)
continue;
if ((results[i].decision & POSTPONED) == POSTPONED) {
postponed = true;
continue;
}
if (!postponed) {
if (!hasMutable) {
evaluationCache.put(evaluationCacheKey, results[i].decision);
}
return results[i].decision;
}
immediateDecisionIdx = i;
}
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 != null && condition.isMutable()) {
evaluationCache.put(evaluationCacheKey, MUTABLE);
return true;
}
}
}
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;
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;
}
}
if (allSameDecision) {
if (!hasMutable) {
evaluationCache.put(evaluationCacheKey, immediateDecision);
}
return immediateDecision;
}
EquinoxSecurityManager equinoxManager = securityAdmin.getSupportedSecurityManager();
if (equinoxManager == null) {
if (!hasMutable) {
evaluationCache.put(evaluationCacheKey, ABSTAIN);
}
return ABSTAIN;
}
equinoxManager.addConditionsForDomain(results);
}
return null;
}
void clearEvaluationCache() {
evaluationCache.clear();
}
SecurityRow getRow(int i) {
return rows.length <= i || i < 0 ? null : rows[i];
}
SecurityRow getRow(String name) {
for (SecurityRow row : rows) {
if (name.equals(row.getName())) {
return row;
}
}
return null;
}
SecurityRow[] getRows() {
return rows;
}
String[] getEncodedRows() {
String[] encoded = new String[rows.length];
for (int i = 0; i < rows.length; i++)
encoded[i] = rows[i].getEncoded();
return encoded;
}
@Override
public void add(Permission permission) {
throw new SecurityException();
}
@Override
public Enumeration<Permission> elements() {
return BundlePermissions.EMPTY_ENUMERATION;
}
@Override
public boolean implies(Permission permission) {
return (evaluate(null, permission) & SecurityTable.GRANTED) != 0;
}
}