blob: 34468ebdbd62a6869f777ffad203fab4b68a7575 [file] [log] [blame]
/*
* 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 });
}
}
}