/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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.apache.felix.framework.security.util; import java.security.Permission; import java.util.Dictionary; import java.util.HashMap; import java.util.HashSet; import java.util.Hashtable; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.WeakHashMap; import org.apache.felix.framework.BundleRevisionImpl; import org.apache.felix.framework.security.condpermadmin.ConditionalPermissionInfoImpl; import org.apache.felix.framework.util.SecureAction; import org.osgi.framework.Bundle; import org.osgi.service.condpermadmin.Condition; import org.osgi.service.condpermadmin.ConditionInfo; /** * This class caches conditions instances by their infos. Furthermore, it allows * to eval postponed condition permission tuples as per spec (see 9.45). */ // TODO: maybe use bundle events instead of soft/weak references. public final class Conditions { private static final ThreadLocal m_conditionStack = new ThreadLocal(); private static final Map m_conditionCache = new WeakHashMap(); private final Map m_cache = new WeakHashMap(); private final BundleRevisionImpl m_module; private final ConditionInfo[] m_conditionInfos; private final Condition[] m_conditions; private final SecureAction m_action; public Conditions(SecureAction action) { this(null, null, action); } private Conditions(BundleRevisionImpl module, ConditionInfo[] conditionInfos, SecureAction action) { m_module = module; m_conditionInfos = conditionInfos; if ((module != null) && (conditionInfos != null)) { synchronized (m_conditionCache) { Map conditionMap = (Map) m_conditionCache.get(module); if (conditionMap == null) { conditionMap = new HashMap(); conditionMap.put(m_conditionInfos, new Condition[m_conditionInfos.length]); m_conditionCache.put(module, conditionMap); } Condition[] conditions = (Condition[]) conditionMap .get(m_conditionInfos); if (conditions == null) { conditions = new Condition[m_conditionInfos.length]; conditionMap.put(m_conditionInfos, conditions); } m_conditions = conditions; } } else { m_conditions = null; } m_action = action; } public Conditions getConditions(BundleRevisionImpl key, ConditionInfo[] conditions) { Conditions result = null; Map index = null; synchronized (m_cache) { index = (Map) m_cache.get(conditions); if (index == null) { index = new WeakHashMap(); m_cache.put(conditions, index); } } synchronized (index) { if (key != null) { result = (Conditions) index.get(key); } } if (result == null) { result = new Conditions(key, conditions, m_action); synchronized (index) { index.put(key, result); } } return result; } // See whether the given list is satisfied or not public boolean isSatisfied(List posts, Permissions permissions, Permission permission) { if (m_conditionInfos == null) { return true; } boolean check = true; for (int i = 0; i < m_conditionInfos.length; i++) { if (m_module == null) { // TODO: check whether this is correct! break; } try { Condition condition = null; boolean add = false; Class clazz = Class.forName(m_conditionInfos[i].getType()); synchronized (m_conditions) { if (m_conditions[i] == null) { m_conditions[i] = createCondition(m_module.getBundle(), clazz, m_conditionInfos[i]); } condition = m_conditions[i]; } Object current = m_conditionStack.get(); if (current != null) { if (current instanceof HashSet) { if (((HashSet) current).contains(clazz)) { return false; } } else { if (current == clazz) { return false; } } } if (condition.isPostponed()) { if (check && !permissions.implies(permission, null)) { return false; } else { check = false; } posts.add(new Object[] { condition, new Integer(i) }); } else { if (current == null) { m_conditionStack.set(clazz); } else { if (current instanceof HashSet) { if (((HashSet) current).contains(clazz)) { return false; } ((HashSet) current).add(clazz); } else { if (current == clazz) { return false; } HashSet frame = new HashSet(); frame.add(current); frame.add(clazz); m_conditionStack.set(frame); current = frame; } } try { boolean mutable = condition.isMutable(); boolean result = condition.isSatisfied(); if (!mutable && ((condition != Condition.TRUE) && (condition != Condition.FALSE))) { synchronized (m_conditions) { m_conditions[i] = result ? Condition.TRUE : Condition.FALSE; } } if (!result) { return false; } } finally { if (current == null) { m_conditionStack.set(null); } else { ((HashSet) current).remove(clazz); if (((HashSet) current).isEmpty()) { m_conditionStack.set(null); } } } } } catch (Exception e) { // TODO: log this as per spec e.printStackTrace(); return false; } } return true; } public boolean evalRecursive(List entries) { Map contexts = new HashMap(); outer: for (Iterator iter = entries.iterator(); iter.hasNext();) { List tuples = (List) iter.next(); inner: for (Iterator inner = tuples.iterator(); inner.hasNext();) { Object[] entry = (Object[]) inner.next(); List conditions = (List) entry[1]; if (conditions == null) { if (!((ConditionalPermissionInfoImpl) entry[0]).isAllow()) { return false; } continue outer; } for (Iterator iter2 = conditions.iterator(); iter2.hasNext();) { Object[] condEntry = (Object[]) iter2.next(); Condition cond = (Condition) condEntry[0]; Dictionary context = (Dictionary) contexts.get(cond .getClass()); if (context == null) { context = new Hashtable(); contexts.put(cond.getClass(), context); } Object current = m_conditionStack.get(); if (current == null) { m_conditionStack.set(cond.getClass()); } else { if (current instanceof HashSet) { ((HashSet) current).add(cond.getClass()); } else { HashSet frame = new HashSet(); frame.add(current); frame.add(cond.getClass()); m_conditionStack.set(frame); current = frame; } } boolean result; boolean mutable = cond.isMutable(); try { result = cond.isSatisfied(new Condition[] { cond }, context); } finally { if (current == null) { m_conditionStack.set(null); } else { ((HashSet) current).remove(cond.getClass()); if (((HashSet) current).isEmpty()) { m_conditionStack.set(null); } } } if (!mutable && (cond != Condition.TRUE) && (cond != Condition.FALSE)) { synchronized (((Conditions) entry[2]).m_conditions) { ((Conditions) entry[2]).m_conditions[((Integer) condEntry[1]) .intValue()] = result ? Condition.TRUE : Condition.FALSE; } } if (!result) { continue inner; } } if (!((ConditionalPermissionInfoImpl) entry[0]).isAllow()) { return false; } continue outer; } return false; } return true; } private Condition createCondition(final Bundle bundle, final Class clazz, final ConditionInfo info) throws Exception { try { return (Condition) m_action.getMethod(clazz, "getCondition", new Class[] { Bundle.class, ConditionInfo.class }).invoke(null, new Object[] { bundle, info }); } catch (Exception ex) { ex.printStackTrace(); return (Condition) m_action.getConstructor(clazz, new Class[] { Bundle.class, ConditionInfo.class }).newInstance( new Object[] { bundle, info }); } } }