Package org.osgi.service.subsystem

Source Code of org.osgi.service.subsystem.SubsystemPermission

/*
* Copyright (c) OSGi Alliance (2000, 2013). All Rights Reserved.
*
* Licensed 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.osgi.service.subsystem;

import java.io.IOException;
import java.io.NotSerializableException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamField;
import java.security.AccessController;
import java.security.BasicPermission;
import java.security.Permission;
import java.security.PermissionCollection;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.osgi.framework.Filter;
import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.InvalidSyntaxException;

/**
* A bundle's authority to perform specific privileged administrative operations
* on or to get sensitive information about a subsystem. The actions for this
* permission are:
*
* <pre>
* Action    Methods
* context   Subsystem.getBundleContext
* execute   Subsystem.start
*           Subsystem.stop
* lifecycle Subsystem.install
*           Subsystem.uninstall
* metadata  Subsystem.getHeaders
*           Subsystem.getLocation
* </pre>
*
* <p>
* The name of this permission is a filter expression. The filter gives access
* to the following attributes:
* <ul>
* <li>location - The location of a subsystem.</li>
* <li>id - The subsystem ID of the designated subsystem.</li>
* <li>name - The symbolic name of a subsystem.</li>
* </ul>
* Filter attribute names are processed in a case sensitive manner.
*
* @ThreadSafe
* @author $Id: 29c3097457160250e064b8c44696dbe86240c0ca $
*/

public final class SubsystemPermission extends BasicPermission {
  static final long                serialVersionUID  = 307051004521261705L;

  /**
   * The action string {@code execute}.
   */
  public final static String            EXECUTE        = "execute";
  /**
   * The action string {@code lifecycle}.
   */
  public final static String            LIFECYCLE      = "lifecycle";
  /**
   * The action string {@code metadata}.
   */
  public final static String            METADATA      = "metadata";
  /**
   * The action string {@code context}.
   */
  public final static String            CONTEXT        = "context";

  private final static int            ACTION_EXECUTE    = 0x00000001;
  private final static int            ACTION_LIFECYCLE  = 0x00000002;
  private final static int            ACTION_METADATA    = 0x00000004;
  private final static int            ACTION_CONTEXT    = 0x00000008;
  private final static int            ACTION_ALL      = ACTION_EXECUTE | ACTION_LIFECYCLE | ACTION_METADATA | ACTION_CONTEXT;
  final static int                ACTION_NONE      = 0;

  /**
   * The actions in canonical form.
   *
   * @serial
   */
  private volatile String              actions        = null;

  /**
   * The actions mask.
   */
  transient int                  action_mask;

  /**
   * If this SubsystemPermission was constructed with a filter, this holds a
   * Filter matching object used to evaluate the filter in implies.
   */
  transient Filter                filter;

  /**
   * The subsystem governed by this SubsystemPermission - only used if filter
   * == null
   */
  transient final Subsystem            subsystem;

  /**
   * This map holds the properties of the permission, used to match a filter
   * in implies. This is not initialized until necessary, and then cached in
   * this object.
   */
  private transient volatile Map<String, Object>  properties;

  /**
   * ThreadLocal used to determine if we have recursively called
   * getProperties.
   */
  private static final ThreadLocal<Subsystem>    recurse        = new ThreadLocal<Subsystem>();

  /**
   * Create a new SubsystemPermission.
   *
   * This constructor must only be used to create a permission that is going
   * to be checked.
   * <p>
   * Examples:
   *
   * <pre>
   * (name=com.acme.*)(location=http://www.acme.com/subsystems/*))
   * (id&gt;=1)
   * </pre>
   *
   * @param filter A filter expression that can use, location, id, and name
   *        keys. Filter attribute names are processed in a case sensitive
   *        manner. A special value of {@code "*"} can be used to match all
   *        subsystems.
   * @param actions {@code execute}, {@code lifecycle}, {@code metadata}, or
   *        {@code context}.
   * @throws IllegalArgumentException If the filter has an invalid syntax.
   */
  public SubsystemPermission(String filter, String actions) {
    this(parseFilter(filter), parseActions(actions));
  }

  /**
   * Creates a new requested {@code SubsystemPermission} object to be used by
   * the code that must perform {@code checkPermission}.
   * {@code SubsystemPermission} objects created with this constructor cannot
   * be added to an {@code SubsystemPermission} permission collection.
   *
   * @param subsystem A subsystem.
   * @param actions {@code execute}, {@code lifecycle}, {@code metadata}, or
   *        {@code context}.
   */
  public SubsystemPermission(Subsystem subsystem, String actions) {
    super(createName(subsystem));
    setTransients(null, parseActions(actions));
    this.subsystem = subsystem;
  }

  /**
   * Create a permission name from a Subsystem
   *
   * @param subsystem Subsystem to use to create permission name.
   * @return permission name.
   */
  private static String createName(Subsystem subsystem) {
    if (subsystem == null) {
      throw new IllegalArgumentException("subsystem must not be null");
    }
    StringBuffer sb = new StringBuffer("(id=");
    sb.append(subsystem.getSubsystemId());
    sb.append(")");
    return sb.toString();
  }

  /**
   * Package private constructor used by SubsystemPermissionCollection.
   *
   * @param filter name filter or {@code null} for wildcard.
   * @param mask action mask
   */
  SubsystemPermission(Filter filter, int mask) {
    super((filter == null) ? "*" : filter.toString());
    setTransients(filter, mask);
    this.subsystem = null;
  }

  /**
   * Called by constructors and when deserialized.
   *
   * @param filter Permission's filter or {@code null} for wildcard.
   * @param mask action mask
   */
  private void setTransients(Filter filter, int mask) {
    this.filter = filter;
    if ((mask == ACTION_NONE) || ((mask & ACTION_ALL) != mask)) {
      throw new IllegalArgumentException("invalid action string");
    }
    this.action_mask = mask;
  }

  /**
   * Parse action string into action mask.
   *
   * @param actions Action string.
   * @return action mask.
   */
  private static int parseActions(String actions) {
    boolean seencomma = false;

    int mask = ACTION_NONE;

    if (actions == null) {
      return mask;
    }

    char[] a = actions.toCharArray();

    int i = a.length - 1;
    if (i < 0)
      return mask;

    while (i != -1) {
      char c;

      // skip whitespace
      while ((i != -1) && ((c = a[i]) == ' ' || c == '\r' || c == '\n' || c == '\f' || c == '\t'))
        i--;

      // check for the known strings
      int matchlen;

      if (i >= 6 && (a[i - 6] == 'e' || a[i - 6] == 'E')
          && (a[i - 5] == 'x' || a[i - 5] == 'X')
          && (a[i - 4] == 'e' || a[i - 4] == 'E')
          && (a[i - 3] == 'c' || a[i - 3] == 'C')
          && (a[i - 2] == 'u' || a[i - 2] == 'U')
          && (a[i - 1] == 't' || a[i - 1] == 'T')
          && (a[i] == 'e' || a[i] == 'E')) {
        matchlen = 7;
        mask |= ACTION_EXECUTE;
      } else
        if (i >= 8 && (a[i - 8] == 'l' || a[i - 8] == 'L')
            && (a[i - 7] == 'i' || a[i - 7] == 'I')
            && (a[i - 6] == 'f' || a[i - 6] == 'F')
            && (a[i - 5] == 'e' || a[i - 5] == 'E')
            && (a[i - 4] == 'c' || a[i - 4] == 'C')
            && (a[i - 3] == 'y' || a[i - 3] == 'Y')
            && (a[i - 2] == 'c' || a[i - 2] == 'C')
            && (a[i - 1] == 'l' || a[i - 1] == 'L')
            && (a[i] == 'e' || a[i] == 'E')) {
          matchlen = 9;
          mask |= ACTION_LIFECYCLE;
        } else
          if (i >= 7
              && (a[i - 7] == 'm' || a[i - 7] == 'M')
              && (a[i - 6] == 'e' || a[i - 6] == 'E')
              && (a[i - 5] == 't' || a[i - 5] == 'T')
              && (a[i - 4] == 'a' || a[i - 4] == 'A')
              && (a[i - 3] == 'd' || a[i - 3] == 'D')
              && (a[i - 2] == 'a' || a[i - 2] == 'A')
              && (a[i - 1] == 't' || a[i - 1] == 'T')
              && (a[i] == 'a' || a[i] == 'A')) {
            matchlen = 8;
            mask |= ACTION_METADATA;
          } else
            if (i >= 6
                && (a[i - 6] == 'c' || a[i - 6] == 'C')
                && (a[i - 5] == 'o' || a[i - 5] == 'O')
                && (a[i - 4] == 'n' || a[i - 4] == 'N')
                && (a[i - 3] == 't' || a[i - 3] == 'T')
                && (a[i - 2] == 'e' || a[i - 2] == 'E')
                && (a[i - 1] == 'x' || a[i - 1] == 'X')
                && (a[i] == 't' || a[i] == 'T')) {
              matchlen = 7;
              mask |= ACTION_CONTEXT;
            } else {
              // parse error
              throw new IllegalArgumentException("invalid permission: " + actions);
            }

      // make sure we didn't just match the tail of a word
      // like "ackbarfexecute". Also, skip to the comma.
      seencomma = false;
      while (i >= matchlen && !seencomma) {
        switch (a[i - matchlen]) {
          case ',' :
            seencomma = true;
            /* FALLTHROUGH */
          case ' ' :
          case '\r' :
          case '\n' :
          case '\f' :
          case '\t' :
            break;
          default :
            throw new IllegalArgumentException("invalid permission: " + actions);
        }
        i--;
      }

      // point i at the location of the comma minus one (or -1).
      i -= matchlen;
    }

    if (seencomma) {
      throw new IllegalArgumentException("invalid permission: " + actions);
    }

    return mask;
  }

  /**
   * Parse filter string into a Filter object.
   *
   * @param filterString The filter string to parse.
   * @return a Filter for this subsystem. If the specified filterString equals
   *         "*", then {@code null} is returned to indicate a wildcard.
   * @throws IllegalArgumentException If the filter syntax is invalid.
   */
  private static Filter parseFilter(String filterString) {
    filterString = filterString.trim();
    if (filterString.equals("*")) {
      return null;
    }

    try {
      return FrameworkUtil.createFilter(filterString);
    } catch (InvalidSyntaxException e) {
      IllegalArgumentException iae = new IllegalArgumentException("invalid filter");
      iae.initCause(e);
      throw iae;
    }
  }

  /**
   * Determines if the specified permission is implied by this object. This
   * method throws an exception if the specified permission was not
   * constructed with a subsystem.
   *
   * <p>
   * This method returns {@code true} if the specified permission is a
   * SubsystemPermission AND
   * <ul>
   * <li>this object's filter matches the specified permission's subsystem ID,
   * subsystem symbolic name, and subsystem location OR</li>
   * <li>this object's filter is "*"</li>
   * </ul>
   * AND this object's actions include all of the specified permission's
   * actions.
   * <p>
   * Special case: if the specified permission was constructed with "*"
   * filter, then this method returns {@code true} if this object's filter is
   * "*" and this object's actions include all of the specified permission's
   * actions
   *
   * @param p The requested permission.
   * @return {@code true} if the specified permission is implied by this
   *         object; {@code false} otherwise.
   */
  public boolean implies(Permission p) {
    if (!(p instanceof SubsystemPermission)) {
      return false;
    }
    SubsystemPermission requested = (SubsystemPermission) p;
    if (subsystem != null) {
      return false;
    }
    // if requested permission has a filter, then it is an invalid argument
    if (requested.filter != null) {
      return false;
    }
    return implies0(requested, ACTION_NONE);
  }

  /**
   * Internal implies method. Used by the implies and the permission
   * collection implies methods.
   *
   * @param requested The requested SubsystemPermision which has already been
   *        validated as a proper argument. The requested SubsystemPermission
   *        must not have a filter expression.
   * @param effective The effective actions with which to start.
   * @return {@code true} if the specified permission is implied by this
   *         object; {@code false} otherwise.
   */
  boolean implies0(SubsystemPermission requested, int effective) {
    /* check actions first - much faster */
    effective |= action_mask;
    final int desired = requested.action_mask;
    if ((effective & desired) != desired) {
      return false;
    }

    /* Get our filter */
    Filter f = filter;
    if (f == null) {
      // it's "*"
      return true;
    }
    /* is requested a wildcard filter? */
    if (requested.subsystem == null) {
      return false;
    }
    Map<String, Object> requestedProperties = requested.getProperties();
    if (requestedProperties == null) {
      /*
       * If the requested properties are null, then we have detected a
       * recursion getting the subsystem location. So we return true to
       * permit the subsystem location request in the SubsystemPermission
       * check up the stack to succeed.
       */
      return true;
    }
    return f.matches(requestedProperties);
  }

  /**
   * Returns the canonical string representation of the
   * {@code SubsystemPermission} actions.
   *
   * <p>
   * Always returns present {@code SubsystemPermission} actions in the
   * following order: {@code execute}, {@code lifecycle}, {@code metadata},
   * {@code context}.
   *
   * @return Canonical string representation of the
   *         {@code SubsystemPermission} actions.
   */
  public String getActions() {
    String result = actions;
    if (result == null) {
      StringBuffer sb = new StringBuffer();

      int mask = action_mask;

      if ((mask & ACTION_EXECUTE) == ACTION_EXECUTE) {
        sb.append(EXECUTE);
        sb.append(',');
      }

      if ((mask & ACTION_LIFECYCLE) == ACTION_LIFECYCLE) {
        sb.append(LIFECYCLE);
        sb.append(',');
      }

      if ((mask & ACTION_METADATA) == ACTION_METADATA) {
        sb.append(METADATA);
        sb.append(',');
      }

      if ((mask & ACTION_CONTEXT) == ACTION_CONTEXT) {
        sb.append(CONTEXT);
        sb.append(',');
      }

      // remove trailing comma
      if (sb.length() > 0) {
        sb.setLength(sb.length() - 1);
      }

      actions = result = sb.toString();
    }
    return result;
  }

  /**
   * Returns a new {@code PermissionCollection} object suitable for storing
   * {@code SubsystemPermission}s.
   *
   * @return A new {@code PermissionCollection} object.
   */
  public PermissionCollection newPermissionCollection() {
    return new SubsystemPermissionCollection();
  }

  /**
   * Determines the equality of two {@code SubsystemPermission} objects.
   *
   * @param obj The object being compared for equality with this object.
   * @return {@code true} if {@code obj} is equivalent to this
   *         {@code SubsystemPermission}; {@code false} otherwise.
   */
  public boolean equals(Object obj) {
    if (obj == this) {
      return true;
    }

    if (!(obj instanceof SubsystemPermission)) {
      return false;
    }

    SubsystemPermission sp = (SubsystemPermission) obj;

    return (action_mask == sp.action_mask) && ((subsystem == sp.subsystem) || ((subsystem != null) && subsystem.equals(sp.subsystem)))
        && (filter == null ? sp.filter == null : filter.equals(sp.filter));
  }

  /**
   * Returns the hash code value for this object.
   *
   * @return Hash code value for this object.
   */
  public int hashCode() {
    int h = 31 * 17 + getName().hashCode();
    h = 31 * h + getActions().hashCode();
    if (subsystem != null) {
      h = 31 * h + subsystem.hashCode();
    }
    return h;
  }

  /**
   * WriteObject is called to save the state of this permission object to a
   * stream. The actions are serialized, and the superclass takes care of the
   * name.
   */
  private synchronized void writeObject(java.io.ObjectOutputStream s) throws IOException {
    if (subsystem != null) {
      throw new NotSerializableException("cannot serialize");
    }
    // Write out the actions. The superclass takes care of the name
    // call getActions to make sure actions field is initialized
    if (actions == null)
      getActions();
    s.defaultWriteObject();
  }

  /**
   * readObject is called to restore the state of this permission from a
   * stream.
   */
  private synchronized void readObject(java.io.ObjectInputStream s) throws IOException, ClassNotFoundException {
    // Read in the data, then initialize the transients
    s.defaultReadObject();
    setTransients(parseFilter(getName()), parseActions(actions));
  }

  /**
   * Called by {@code implies0} on an SubsystemPermission which was
   * constructed with a Subsystem. This method loads a map with the
   * filter-matchable properties of this subsystem. The map is cached so this
   * lookup only happens once.
   *
   * This method should only be called on an SubsystemPermission which was
   * constructed with a subsystem
   *
   * @return a map of properties for this subsystem
   */
  private Map<String, Object> getProperties() {
    Map<String, Object> result = properties;
    if (result != null) {
      return result;
    }
    /*
     * We may have recursed here due to the Subsystem.getLocation call in
     * the doPrivileged below. If this is the case, return null to allow
     * implies to return true.
     */
    final Object mark = recurse.get();
    if (mark == subsystem) {
      return null;
    }
    recurse.set(subsystem);
    try {
      final Map<String, Object> map = new HashMap<String, Object>(4);
      AccessController.doPrivileged(new PrivilegedAction<Object>() {
        public Object run() {
          map.put("id", new Long(subsystem.getSubsystemId()));
          map.put("location", subsystem.getLocation());
          map.put("name", subsystem.getSymbolicName());
          return null;
        }
      });
      return properties = map;
    } finally {
      recurse.set(null);
    }
  }
}

/**
* Stores a collection of {@code SubsystemPermission}s.
*/
final class SubsystemPermissionCollection extends PermissionCollection {
  private static final long              serialVersionUID  = 3906372644575328048L;
  /**
   * Collection of permissions.
   *
   * @GuardedBy this
   */
  private transient Map<String, SubsystemPermission>  permissions;

  /**
   * Boolean saying if "*" is in the collection.
   *
   * @serial
   * @GuardedBy this
   */
  private boolean                    all_allowed;

  /**
   * Create an empty SubsystemPermissionCollection object.
   *
   */
  public SubsystemPermissionCollection() {
    permissions = new HashMap<String, SubsystemPermission>();
  }

  /**
   * Adds a permission to this permission collection.
   *
   * @param permission The {@code SubsystemPermission} object to add.
   * @throws IllegalArgumentException If the specified permission is not an
   *         {@code SubsystemPermission} instance or was constructed with a
   *         Subsystem object.
   * @throws SecurityException If this {@code SubsystemPermissionCollection}
   *         object has been marked read-only.
   */
  public void add(Permission permission) {
    if (!(permission instanceof SubsystemPermission)) {
      throw new IllegalArgumentException("invalid permission: " + permission);
    }
    if (isReadOnly()) {
      throw new SecurityException("attempt to add a Permission to a " + "readonly PermissionCollection");
    }
    final SubsystemPermission sp = (SubsystemPermission) permission;
    if (sp.subsystem != null) {
      throw new IllegalArgumentException("cannot add to collection: " + sp);
    }
    final String name = sp.getName();
    synchronized (this) {
      Map<String, SubsystemPermission> pc = permissions;
      SubsystemPermission existing = pc.get(name);
      if (existing != null) {
        int oldMask = existing.action_mask;
        int newMask = sp.action_mask;

        if (oldMask != newMask) {
          pc.put(name, new SubsystemPermission(existing.filter, oldMask | newMask));
        }
      } else {
        pc.put(name, sp);
      }
      if (!all_allowed) {
        if (name.equals("*")) {
          all_allowed = true;
        }
      }
    }
  }

  /**
   * Determines if the specified permissions implies the permissions expressed
   * in {@code permission}.
   *
   * @param permission The Permission object to compare with the
   *        {@code SubsystemPermission} objects in this collection.
   * @return {@code true} if {@code permission} is implied by an
   *         {@code SubsystemPermission} in this collection, {@code false}
   *         otherwise.
   */
  public boolean implies(Permission permission) {
    if (!(permission instanceof SubsystemPermission)) {
      return false;
    }

    SubsystemPermission requested = (SubsystemPermission) permission;
    // if requested permission has a filter, then it is an invalid argument
    if (requested.filter != null) {
      return false;
    }
    int effective = SubsystemPermission.ACTION_NONE;
    Collection<SubsystemPermission> perms;
    synchronized (this) {
      Map<String, SubsystemPermission> pc = permissions;
      // short circuit if the "*" Permission was added
      if (all_allowed) {
        SubsystemPermission sp = pc.get("*");
        if (sp != null) {
          effective |= sp.action_mask;
          final int desired = requested.action_mask;
          if ((effective & desired) == desired) {
            return true;
          }
        }
      }
      perms = pc.values();
    }

    // just iterate one by one
    for (SubsystemPermission perm : perms) {
      if (perm.implies0(requested, effective)) {
        return true;
      }
    }
    return false;
  }

  /**
   * Returns an enumeration of all {@code SubsystemPermission} objects in the
   * container.
   *
   * @return Enumeration of all {@code SubsystemPermission} objects.
   */
  public synchronized Enumeration<Permission> elements() {
    List<Permission> all = new ArrayList<Permission>(permissions.values());
    return Collections.enumeration(all);
  }

  /* serialization logic */
  private static final ObjectStreamField[]  serialPersistentFields  = {new ObjectStreamField("permissions", HashMap.class), new ObjectStreamField("all_allowed", Boolean.TYPE)};

  private synchronized void writeObject(ObjectOutputStream out) throws IOException {
    ObjectOutputStream.PutField pfields = out.putFields();
    pfields.put("permissions", permissions);
    pfields.put("all_allowed", all_allowed);
    out.writeFields();
  }

  private synchronized void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
    ObjectInputStream.GetField gfields = in.readFields();
    HashMap<String, SubsystemPermission> p = (HashMap<String, SubsystemPermission>) gfields.get("permissions", null);
    permissions = p;
    all_allowed = gfields.get("all_allowed", false);
  }
}
TOP

Related Classes of org.osgi.service.subsystem.SubsystemPermission

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.