Package org.eclipse.osgi.internal.permadmin

Source Code of org.eclipse.osgi.internal.permadmin.SecurityRow$Decision

/*******************************************************************************
* Copyright (c) 2008, 2009 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
*     IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.osgi.internal.permadmin;

import java.lang.reflect.*;
import java.security.Permission;
import java.util.*;
import org.osgi.framework.Bundle;
import org.osgi.service.condpermadmin.*;
import org.osgi.service.permissionadmin.PermissionInfo;

public final class SecurityRow implements ConditionalPermissionInfo {
  /* Used to find condition constructors getConditions */
  static final Class[] conditionMethodArgs = new Class[] {Bundle.class, ConditionInfo.class};
  static Condition[] ABSTAIN_LIST = new Condition[0];
  static Condition[] SATISFIED_LIST = new Condition[0];
  static final Decision DECISION_ABSTAIN = new Decision(SecurityTable.ABSTAIN, null, null, null);
  static final Decision DECISION_GRANTED = new Decision(SecurityTable.GRANTED, null, null, null);
  static final Decision DECISION_DENIED = new Decision(SecurityTable.DENIED, null, null, null);

  private final SecurityAdmin securityAdmin;
  private final String name;
  private final ConditionInfo[] conditionInfos;
  private final PermissionInfoCollection permissionInfoCollection;
  private final boolean deny;
  /* GuardedBy(bundleConditions) */
  final HashMap bundleConditions;

  public SecurityRow(SecurityAdmin securityAdmin, String name, ConditionInfo[] conditionInfos, PermissionInfo[] permissionInfos, String decision) {
    if (permissionInfos == null || permissionInfos.length == 0)
      throw new IllegalArgumentException("It is invalid to have empty permissionInfos"); //$NON-NLS-1$
    this.securityAdmin = securityAdmin;
    this.conditionInfos = conditionInfos == null ? new ConditionInfo[0] : conditionInfos;
    decision = decision.toLowerCase();
    boolean d = ConditionalPermissionInfo.DENY.equals(decision);
    boolean a = ConditionalPermissionInfo.ALLOW.equals(decision);
    if (!(d | a))
      throw new IllegalArgumentException("Invalid decision: " + decision); //$NON-NLS-1$
    this.deny = d;
    this.name = name;
    this.permissionInfoCollection = new PermissionInfoCollection(permissionInfos);
    if (conditionInfos == null || conditionInfos.length == 0)
      bundleConditions = null;
    else
      bundleConditions = new HashMap();
  }

  static SecurityRowSnapShot createSecurityRowSnapShot(String encoded) {
    return (SecurityRowSnapShot) createConditionalPermissionInfo(null, encoded);
  }

  static SecurityRow createSecurityRow(SecurityAdmin securityAdmin, String encoded) {
    return (SecurityRow) createConditionalPermissionInfo(securityAdmin, encoded);
  }

  private static ConditionalPermissionInfo createConditionalPermissionInfo(SecurityAdmin securityAdmin, String encoded) {
    encoded = encoded.trim();
    if (encoded.length() == 0)
      throw new IllegalArgumentException("Empty encoded string is invalid"); //$NON-NLS-1$
    char[] chars = encoded.toCharArray();
    int end = encoded.length() - 1;
    char lastChar = chars[end];
    if (lastChar != '}' && lastChar != '"')
      throw new IllegalArgumentException(encoded);
    String encodedName = null;
    if (lastChar == '"') {
      // we have a name: an empty name must have at least 2 chars for the quotes
      if (chars.length < 2)
        throw new IllegalArgumentException(encoded);
      int endName = encoded.length() - 1;
      int startName = endName - 1;
      while (startName > 0) {
        if (chars[startName] == '"') {
          startName--;
          if (startName > 0 && chars[startName] == '\\')
            startName--;
          else {
            startName++;
            break;
          }
        }
        startName--;
      }
      if (chars[startName] != '"')
        throw new IllegalArgumentException(encoded);
      encodedName = unescapeString(encoded.substring(startName + 1, endName));
      end = encoded.lastIndexOf('}', startName);
    }
    int start = encoded.indexOf('{');
    if (start < 0 || end < start)
      throw new IllegalArgumentException(encoded);

    String decision = encoded.substring(0, start);
    decision = decision.trim();
    if (decision.length() == 0 || (!ConditionalPermissionInfo.DENY.equalsIgnoreCase(decision) && !ConditionalPermissionInfo.ALLOW.equalsIgnoreCase(decision)))
      throw new IllegalArgumentException(encoded);

    ArrayList condList = new ArrayList();
    ArrayList permList = new ArrayList();
    int pos = start + 1;
    while (pos < end) {
      while (pos < end && chars[pos] != '[' && chars[pos] != '(')
        pos++;
      if (pos == end)
        break; // no perms or conds left
      int startPos = pos;
      char endChar = chars[startPos] == '[' ? ']' : ')';
      while (pos < end && chars[pos] != endChar) {
        if (chars[pos] == '"') {
          pos++;
          while (chars[pos] != '"') {
            if (chars[pos] == '\\')
              pos++;
            pos++;
          }
        }
        pos++;
      }
      int endPos = pos;
      String token = new String(chars, startPos, endPos - startPos + 1);
      if (endChar == ']')
        condList.add(new ConditionInfo(token));
      else
        permList.add(new PermissionInfo(token));
      pos++;
    }
    if (permList.size() == 0)
      throw new IllegalArgumentException("No Permission infos: " + encoded); //$NON-NLS-1$
    ConditionInfo[] conds = (ConditionInfo[]) condList.toArray(new ConditionInfo[condList.size()]);
    PermissionInfo[] perms = (PermissionInfo[]) permList.toArray(new PermissionInfo[permList.size()]);
    if (securityAdmin == null)
      return new SecurityRowSnapShot(encodedName, conds, perms, decision);
    return new SecurityRow(securityAdmin, encodedName, conds, perms, decision);
  }

  static Object cloneArray(Object[] array) {
    if (array == null)
      return null;
    Object result = Array.newInstance(array.getClass().getComponentType(), array.length);
    System.arraycopy(array, 0, result, 0, array.length);
    return result;
  }

  private static void escapeString(String str, StringBuffer output) {
    int len = str.length();
    for (int i = 0; i < len; i++) {
      char c = str.charAt(i);
      switch (c) {
        case '"' :
        case '\\' :
          output.append('\\');
          output.append(c);
          break;
        case '\r' :
          output.append("\\r"); //$NON-NLS-1$
          break;
        case '\n' :
          output.append("\\n"); //$NON-NLS-1$
          break;
        default :
          output.append(c);
          break;
      }
    }
  }

  private static String unescapeString(String str) {
    StringBuffer output = new StringBuffer(str.length());
    int end = str.length();
    for (int i = 0; i < end; i++) {
      char c = str.charAt(i);
      if (c == '\\') {
        i++;
        if (i < end) {
          c = str.charAt(i);
          switch (c) {
            case '"' :
            case '\\' :
              break;
            case 'r' :
              c = '\r';
              break;
            case 'n' :
              c = '\n';
              break;
            default :
              c = '\\';
              i--;
              break;
          }
        }
      }
      output.append(c);
    }

    return output.toString();
  }

  public String getName() {
    return name;
  }

  public ConditionInfo[] getConditionInfos() {
    // must make a copy for the public API method to prevent modification
    return (ConditionInfo[]) cloneArray(conditionInfos);
  }

  ConditionInfo[] internalGetConditionInfos() {
    return conditionInfos;
  }

  public String getAccessDecision() {
    return deny ? ConditionalPermissionInfo.DENY : ConditionalPermissionInfo.ALLOW;
  }

  public PermissionInfo[] getPermissionInfos() {
    // must make a copy for the public API method to prevent modification
    return (PermissionInfo[]) cloneArray(permissionInfoCollection.getPermissionInfos());
  }

  PermissionInfo[] internalGetPermissionInfos() {
    return permissionInfoCollection.getPermissionInfos();
  }

  /**
   * @deprecated
   */
  public void delete() {
    securityAdmin.delete(this, true);
  }

  Condition[] getConditions(Bundle bundle) {
    Condition[] conditions = new Condition[conditionInfos.length];
    for (int i = 0; i < conditionInfos.length; i++) {
      /*
       * TODO: Can we pre-get the Constructors in our own constructor
       */
      Class clazz;
      try {
        clazz = Class.forName(conditionInfos[i].getType());
      } catch (ClassNotFoundException e) {
        /* If the class isn't there, we fail */
        return null;
      }
      Constructor constructor = null;
      Method method = null;
      try {
        method = clazz.getMethod("getCondition", conditionMethodArgs); //$NON-NLS-1$
        if ((method.getModifiers() & Modifier.STATIC) == 0)
          method = null;
      } catch (NoSuchMethodException e) {
        // This is a normal case
      }
      if (method == null)
        try {
          constructor = clazz.getConstructor(conditionMethodArgs);
        } catch (NoSuchMethodException e) {
          // TODO should post a FrameworkEvent of type error here
          conditions[i] = Condition.FALSE;
          continue;
        }

      Object args[] = {bundle, conditionInfos[i]};
      try {
        if (method != null)
          conditions[i] = (Condition) method.invoke(null, args);
        else
          conditions[i] = (Condition) constructor.newInstance(args);
      } catch (Throwable t) {
        // TODO should post a FrameworkEvent of type error here
        conditions[i] = Condition.FALSE;
      }
    }
    return conditions;
  }

  Decision evaluate(BundlePermissions bundlePermissions, Permission permission) {
    if (bundleConditions == null || bundlePermissions == null)
      return evaluatePermission(permission);
    Condition[] conditions;
    synchronized (bundleConditions) {
      conditions = (Condition[]) bundleConditions.get(bundlePermissions);
      if (conditions == null) {
        conditions = getConditions(bundlePermissions.getBundle());
        bundleConditions.put(bundlePermissions, conditions);
      }
    }
    if (conditions == ABSTAIN_LIST)
      return DECISION_ABSTAIN;
    if (conditions == SATISFIED_LIST)
      return evaluatePermission(permission);

    boolean empty = true;
    List postponedConditions = null;
    Decision postponedPermCheck = null;
    for (int i = 0; i < conditions.length; i++) {
      Condition condition = conditions[i];
      if (condition == null)
        continue; // this condition must have been satisfied && !mutable in a previous check
      if (!isPostponed(condition)) {
        // must call isMutable before calling isSatisfied according to the specification.
        boolean mutable = condition.isMutable();
        if (condition.isSatisfied()) {
          if (!mutable)
            conditions[i] = null; // ignore this condition for future checks
        } else {
          if (!mutable)
            // this will cause the row to always abstain; mark this to be ignored in future checks
            synchronized (bundleConditions) {
              bundleConditions.put(bundlePermissions, ABSTAIN_LIST);
            }
          return DECISION_ABSTAIN;
        }
      } else { // postponed case
        if (postponedPermCheck == null)
          // perform a permission check now
          postponedPermCheck = evaluatePermission(permission);
        if (postponedPermCheck == DECISION_ABSTAIN)
          return postponedPermCheck; // no need to postpone the condition if the row abstains
        // this row will deny or allow the permission; must queue the postponed condition
        if (postponedConditions == null)
          postponedConditions = new ArrayList(1);
        postponedConditions.add(condition);
      }
      empty &= conditions[i] == null;
    }
    if (empty) {
      synchronized (bundleConditions) {
        bundleConditions.put(bundlePermissions, SATISFIED_LIST);
      }
    }
    if (postponedPermCheck != null)
      return new Decision(postponedPermCheck.decision | SecurityTable.POSTPONED, (Condition[]) postponedConditions.toArray(new Condition[postponedConditions.size()]), this, bundlePermissions);
    return evaluatePermission(permission);
  }

  private boolean isPostponed(Condition condition) {
    // postponed checks can only happen if we are using a supported security manager
    return condition.isPostponed() && securityAdmin.getSupportedSecurityManager() != null;
  }

  private Decision evaluatePermission(Permission permission) {
    return permissionInfoCollection.implies(permission) ? (deny ? DECISION_DENIED : DECISION_GRANTED) : DECISION_ABSTAIN;
  }

  public String toString() {
    return getEncoded();
  }

  public String getEncoded() {
    return getEncoded(name, conditionInfos, internalGetPermissionInfos(), deny);
  }

  public boolean equals(Object obj) {
    // doing the simple (slow) thing for now
    if (obj == this)
      return true;
    if (!(obj instanceof ConditionalPermissionInfo))
      return false;
    // we assume the encoded string provides a canonical (comparable) form
    return getEncoded().equals(((ConditionalPermissionInfo) obj).getEncoded());
  }

  public int hashCode() {
    return getHashCode(name, internalGetConditionInfos(), internalGetPermissionInfos(), getAccessDecision());
  }

  static int getHashCode(String name, ConditionInfo[] conds, PermissionInfo[] perms, String decision) {
    int h = 31 * 17 + decision.hashCode();
    for (int i = 0; i < conds.length; i++)
      h = 31 * h + conds[i].hashCode();
    for (int i = 0; i < perms.length; i++)
      h = 31 * h + perms[i].hashCode();
    if (name != null)
      h = 31 * h + name.hashCode();
    return h;
  }

  static String getEncoded(String name, ConditionInfo[] conditionInfos, PermissionInfo[] permissionInfos, boolean deny) {
    StringBuffer result = new StringBuffer();
    if (deny)
      result.append(ConditionalPermissionInfo.DENY);
    else
      result.append(ConditionalPermissionInfo.ALLOW);
    result.append(" { "); //$NON-NLS-1$
    if (conditionInfos != null)
      for (int i = 0; i < conditionInfos.length; i++)
        result.append(conditionInfos[i].getEncoded()).append(' ');
    if (permissionInfos != null)
      for (int i = 0; i < permissionInfos.length; i++)
        result.append(permissionInfos[i].getEncoded()).append(' ');
    result.append('}');
    if (name != null) {
      result.append(" \""); //$NON-NLS-1$
      escapeString(name, result);
      result.append('"');
    }
    return result.toString();
  }

  PermissionInfoCollection getPermissionInfoCollection() {
    return permissionInfoCollection;
  }

  void clearCaches() {
    permissionInfoCollection.clearPermissionCache();
    if (bundleConditions != null)
      synchronized (bundleConditions) {
        bundleConditions.clear();
      }
  }

  static class Decision {
    final int decision;
    final Condition[] postponed;
    private final SecurityRow row;
    private final BundlePermissions bundlePermissions;

    Decision(int decision, Condition[] postponed, SecurityRow row, BundlePermissions bundlePermissions) {
      this.decision = decision;
      this.postponed = postponed;
      this.row = row;
      this.bundlePermissions = bundlePermissions;
    }

    void handleImmutable(Condition condition, boolean isSatisfied, boolean mutable) {
      if (mutable || !condition.isPostponed())
        return; // do nothing
      if (isSatisfied) {
        synchronized (row.bundleConditions) {
          Condition[] rowConditions = (Condition[]) row.bundleConditions.get(bundlePermissions);
          boolean isEmpty = true;
          for (int i = 0; i < rowConditions.length; i++) {
            if (rowConditions[i] == condition)
              if (isSatisfied)
                rowConditions[i] = null;
            isEmpty &= rowConditions[i] == null;
          }
          if (isEmpty)
            row.bundleConditions.put(bundlePermissions, SATISFIED_LIST);
        }
      } else {
        synchronized (row.bundleConditions) {
          row.bundleConditions.put(bundlePermissions, ABSTAIN_LIST);
        }
      }
    }
  }
}
TOP

Related Classes of org.eclipse.osgi.internal.permadmin.SecurityRow$Decision

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.