blob: 4cbab14c9f6d00880f241bdf375f88991b4300be [file] [log] [blame]
/*
* $Header: /cvshome/build/org.osgi.service.application/src/org/osgi/service/application/ApplicationAdminPermission.java,v 1.31 2005/12/20 11:23:39 ckarai Exp $
*
* Copyright (c) OSGi Alliance (2004, 2005). 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.
*/
package org.osgi.service.application;
import java.security.Permission;
import java.util.Hashtable;
import java.util.StringTokenizer;
import java.util.Vector;
import org.osgi.framework.Filter;
import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.InvalidSyntaxException;
/**
* This class implements permissions for manipulating applications and
* their instances.
* <P>
* ApplicationAdminPermission can be targeted to applications that matches the
* specified filter.
* <P>
* ApplicationAdminPermission may be granted for different actions:
* <code>lifecycle</code>, <code>schedule</code> and <code>lock</code>.
* The permission <code>schedule</code> implies the permission
* <code>lifecycle</code>.
*/
public class ApplicationAdminPermission extends Permission {
private static final long serialVersionUID = 1L;
/**
* Allows the lifecycle management of the target applications.
*/
public static final String LIFECYCLE_ACTION = "lifecycle";
/**
* Allows scheduling of the target applications. The permission to
* schedule an application implies that the scheduler can also
* manage the lifecycle of that application i.e. <code>schedule</code>
* implies <code>lifecycle</code>
*/
public static final String SCHEDULE_ACTION = "schedule";
/**
* Allows setting/unsetting the locking state of the target applications.
*/
public static final String LOCK_ACTION = "lock";
private ApplicationDescriptor applicationDescriptor;
/**
* Constructs an ApplicationAdminPermission. The <code>filter</code>
* specifies the target application. The <code>filter</code> is an
* LDAP-style filter, the recognized properties are <code>signer</code>
* and <code>pid</code>. The pattern specified in the <code>signer</code>
* is matched with the Distinguished Name chain used to sign the application.
* Wildcards in a DN are not matched according to the filter string rules,
* but according to the rules defined for a DN chain. The attribute
* <code>pid</code> is matched with the PID of the application according to
* the filter string rules.
* <p>
* If the <code>filter</code> is <code>null</code> then it matches
* <code>"*"</code>. If
* <code>actions</code> is <code>"*"</code> then it identifies all the
* possible actions.
*
* @param filter
* filter to identify application. The value <code>null</code>
* is equivalent to <code>"*"</code> and it indicates "all application".
* @param actions
* comma-separated list of the desired actions granted on the
* applications or "*" means all the actions. It must not be
* <code>null</code>. The order of the actions in the list is
* not significant.
* @throws InvalidSyntaxException
* is thrown if the specified <code>filter</code> is not syntactically
* correct.
*
* @exception NullPointerException
* is thrown if the actions parameter is <code>null</code>
*
* @see ApplicationDescriptor
* @see org.osgi.framework.AdminPermission
*/
public ApplicationAdminPermission(String filter, String actions) throws InvalidSyntaxException {
super(filter == null ? "*" : filter);
if( filter == null )
filter = "*";
if( actions == null )
throw new NullPointerException( "Action string cannot be null!" );
this.applicationDescriptor = null;
this.filter = (filter == null ? "*" : filter);
this.actions = actions;
if( !filter.equals( "*" ) && !filter.equals( "<<SELF>>" ) )
FrameworkUtil.createFilter( this.filter ); // check if the filter is valid
init();
}
/**
* This contructor should be used when creating <code>ApplicationAdminPermission</code>
* instance for <code>checkPermission</code> call.
* @param application the tareget of the operation, it must not be <code>null</code>
* @param actions the required operation. it must not be <code>null</code>
* @throws NullPointerException if any of the arguments is null.
*/
public ApplicationAdminPermission(ApplicationDescriptor application, String actions) {
super(application.getApplicationId());
if( application == null || actions == null )
throw new NullPointerException( "ApplicationDescriptor and action string cannot be null!" );
this.filter = application.getApplicationId();
this.applicationDescriptor = application;
this.actions = actions;
init();
}
/**
* This method can be used in the {@link java.security.ProtectionDomain}
* implementation in the <code>implies</code> method to insert the
* application ID of the current application into the permission being
* checked. This enables the evaluation of the
* <code>&lt;&lt;SELF&gt;&gt;</code> pseudo targets.
* @param applicationId the ID of the current application.
* @return the permission updated with the ID of the current application
*/
public ApplicationAdminPermission setCurrentApplicationId(String applicationId) {
ApplicationAdminPermission newPerm = null;
if( this.applicationDescriptor == null ) {
try {
newPerm = new ApplicationAdminPermission( this.filter, this.actions );
}catch( InvalidSyntaxException e ) {
throw new RuntimeException( "Internal error" ); /* this can never happen */
}
}
else
newPerm = new ApplicationAdminPermission( this.applicationDescriptor, this.actions );
newPerm.applicationID = applicationId;
return newPerm;
}
/**
* Checks if the specified <code>permission</code> is implied by this permission.
* The method returns true under the following conditions:
* <UL>
* <LI> This permission was created by specifying a filter (see {@link #ApplicationAdminPermission(String, String)})
* <LI> The implied <code>otherPermission</code> was created for a particular {@link ApplicationDescriptor}
* (see {@link #ApplicationAdminPermission(ApplicationDescriptor, String)})
* <LI> The <code>filter</code> of this permission mathes the <code>ApplicationDescriptor</code> specified
* in the <code>otherPermission</code>. If the filter in this permission is the
* <code>&lt;&lt;SELF&gt;&gt;</code> pseudo target, then the currentApplicationId set in the
* <code>otherPermission</code> is compared to the application Id of the target
* <code>ApplicationDescriptor</code>.
* <LI> The list of permitted actions in this permission contains all actions required in the
* <code>otherPermission</code>
* </UL>
* Otherwise the method returns false.
* @param otherPermission the implied permission
* @return true if this permission implies the <code>otherPermission</code>, false otherwise.
*/
public boolean implies(Permission otherPermission) {
if( otherPermission == null )
return false;
if(!(otherPermission instanceof ApplicationAdminPermission))
return false;
ApplicationAdminPermission other = (ApplicationAdminPermission) otherPermission;
if( !filter.equals("*") ) {
if( other.applicationDescriptor == null )
return false;
if( filter.equals( "<<SELF>>") ) {
if( other.applicationID == null )
return false; /* it cannot be, this might be a bug */
if( !other.applicationID.equals( other.applicationDescriptor.getApplicationId() ) )
return false;
}
else {
Hashtable props = new Hashtable();
props.put( "pid", other.applicationDescriptor.getApplicationId() );
props.put( "signer", new SignerWrapper( other.applicationDescriptor ) );
Filter flt = getFilter();
if( flt == null )
return false;
if( !flt.match( props ) )
return false;
}
}
if( !actionsVector.containsAll( other.actionsVector ) )
return false;
return true;
}
public boolean equals(Object with) {
if( with == null || !(with instanceof ApplicationAdminPermission) )
return false;
ApplicationAdminPermission other = (ApplicationAdminPermission)with;
// Compare actions:
if( other.actionsVector.size() != actionsVector.size() )
return false;
for( int i=0; i != actionsVector.size(); i++ )
if( !other.actionsVector.contains( actionsVector.get( i ) ) )
return false;
return equal(this.filter, other.filter ) && equal(this.applicationDescriptor, other.applicationDescriptor)
&& equal(this.applicationID, other.applicationID);
}
/**
* Compares parameters for equality. If both object are null, they are considered
* equal.
* @param a object to compare
* @param b other object to compare
* @return true if both objects are equal or both are null
*/
private static boolean equal(Object a, Object b) {
// This equation is true if both references are null or both point
// to the same object. In both cases they are considered as equal.
if( a == b ) {
return true;
}
return a.equals(b);
}
public int hashCode() {
int hc = 0;
for( int i=0; i != actionsVector.size(); i++ )
hc ^= ((String)actionsVector.get( i )).hashCode();
hc ^= (null == this.filter )? 0 : this.filter.hashCode();
hc ^= (null == this.applicationDescriptor) ? 0 : this.applicationDescriptor.hashCode();
hc ^= (null == this.applicationID) ? 0 : this.applicationID.hashCode();
return hc;
}
/**
* Returns the actions of this permission.
* @return the actions specified when this permission was created
*/
public String getActions() {
return actions;
}
private String applicationID;
private static final Vector ACTIONS = new Vector();
private Vector actionsVector;
private final String filter;
private final String actions;
private Filter appliedFilter = null;
static {
ACTIONS.add(LIFECYCLE_ACTION);
ACTIONS.add(SCHEDULE_ACTION);
ACTIONS.add(LOCK_ACTION);
}
private static Vector actionsVector(String actions) {
Vector v = new Vector();
StringTokenizer t = new StringTokenizer(actions.toUpperCase(), ",");
while (t.hasMoreTokens()) {
String action = t.nextToken().trim();
v.add(action.toLowerCase());
}
if( v.contains( SCHEDULE_ACTION ) && !v.contains( LIFECYCLE_ACTION ) )
v.add( LIFECYCLE_ACTION );
return v;
}
private static class SignerWrapper extends Object {
private String pattern;
private ApplicationDescriptor appDesc;
public SignerWrapper(String pattern) {
this.pattern = pattern;
}
SignerWrapper(ApplicationDescriptor appDesc) {
this.appDesc = appDesc;
}
public boolean equals(Object o) {
if (!(o instanceof SignerWrapper))
return false;
SignerWrapper other = (SignerWrapper) o;
ApplicationDescriptor matchAppDesc = (ApplicationDescriptor) (appDesc != null ? appDesc : other.appDesc);
String matchPattern = appDesc != null ? other.pattern : pattern;
return matchAppDesc.matchDNChain(matchPattern);
}
}
private void init() {
actionsVector = actionsVector( actions );
if ( actions.equals("*") )
actionsVector = actionsVector( LIFECYCLE_ACTION + "," + SCHEDULE_ACTION + "," + LOCK_ACTION );
else if (!ACTIONS.containsAll(actionsVector))
throw new IllegalArgumentException("Illegal action!");
applicationID = null;
}
private Filter getFilter() {
String transformedFilter = filter;
if (appliedFilter == null) {
try {
int pos = filter.indexOf("signer"); //$NON-NLS-1$
if (pos != -1){
//there may be a signer attribute
StringBuffer filterBuf = new StringBuffer(filter);
int numAsteriskFound = 0; //use as offset to replace in buffer
int walkbackPos; //temp pos
//find occurences of (signer= and escape out *'s
while (pos != -1) {
//walk back and look for '(' to see if this is an attr
walkbackPos = pos-1;
//consume whitespace
while(walkbackPos >= 0 && Character.isWhitespace(filter.charAt(walkbackPos))) {
walkbackPos--;
}
if (walkbackPos <0) {
//filter is invalid - FilterImpl will throw error
break;
}
//check to see if we have unescaped '('
if (filter.charAt(walkbackPos) != '(' || (walkbackPos > 0 && filter.charAt(walkbackPos-1) == '\\')) {
//'(' was escaped or not there
pos = filter.indexOf("signer",pos+6); //$NON-NLS-1$
continue;
}
pos+=6; //skip over 'signer'
//found signer - consume whitespace before '='
while (Character.isWhitespace(filter.charAt(pos))) {
pos++;
}
//look for '='
if (filter.charAt(pos) != '=') {
//attr was signerx - keep looking
pos = filter.indexOf("signer",pos); //$NON-NLS-1$
continue;
}
pos++; //skip over '='
//found signer value - escape '*'s
while (!(filter.charAt(pos) == ')' && filter.charAt(pos-1) != '\\')) {
if (filter.charAt(pos) == '*') {
filterBuf.insert(pos+numAsteriskFound,'\\');
numAsteriskFound++;
}
pos++;
}
//end of signer value - look for more?
pos = filter.indexOf("signer",pos); //$NON-NLS-1$
} //end while (pos != -1)
transformedFilter = filterBuf.toString();
} //end if (pos != -1)
appliedFilter = FrameworkUtil.createFilter( transformedFilter );
} catch (InvalidSyntaxException e) {
//we will return null
}
}
return appliedFilter;
}
}