Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 8c7941578972d6f61ed9aeffac374a0b8c294056 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
/*
 * Copyright (c) OSGi Alliance (2004, 2010). 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.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 schedule} and {@code lock}. The
 * permission {@code schedule} implies the permission
 * {@code lifecycle}.
 * 
 * @version $Id$
 */
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}
	 * implies {@code lifecycle}
	 */
	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}
	 * specifies the target application. The {@code filter} is an
	 * LDAP-style filter, the recognized properties are {@code signer}
	 * and {@code pid}. The pattern specified in the {@code signer}
	 * 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} is matched with the PID of the application according to
	 * the filter string rules. 
	 * <p>
	 * If the {@code filter} is {@code null} then it matches 
	 * {@code "*"}. If
	 * {@code actions} is {@code "*"} then it identifies all the
	 * possible actions.
	 * 
	 * @param filter
	 *            filter to identify application. The value {@code null}
	 *            is equivalent to {@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}. The order of the actions in the list is
	 *            not significant.
	 * @throws InvalidSyntaxException 
	 *            is thrown if the specified {@code filter} is not syntactically
	 *            correct.
	 * 
	 * @exception NullPointerException
	 *                is thrown if the actions parameter is {@code null}
	 * 
	 * @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}
	 * instance for {@code checkPermission} call. 
	 * @param application the tareget of the operation, it must not be {@code null}
	 * @param actions the required operation. it must not be {@code null}
	 * @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} 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;} 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(e); /* this can never happen */
			}
		}
		else	
		    newPerm = new ApplicationAdminPermission( this.applicationDescriptor, this.actions );
		
		newPerm.applicationID = applicationId;
		
		return newPerm;
	}

	/**
	 * Checks if the specified {@code permission} 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} was created for a particular {@link ApplicationDescriptor}
	 *      (see {@link #ApplicationAdminPermission(ApplicationDescriptor, String)})
	 * <LI> The {@code filter} of this permission mathes the {@code ApplicationDescriptor} specified
	 *      in the {@code otherPermission}. If the filter in this permission is the 
	 *      {@code &lt;&lt;SELF&gt;&gt;} pseudo target, then the currentApplicationId set in the 
	 *      {@code otherPermission} is compared to the application Id of the target 
	 *      {@code ApplicationDescriptor}.
	 * <LI> The list of permitted actions in this permission contains all actions required in the 
	 *      {@code otherPermission}  
	 * </UL> 
	 * Otherwise the method returns false.
	 * @param otherPermission the implied permission
	 * @return true if this permission implies the {@code otherPermission}, 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;
  	
  	/**
  	 * @param pattern
  	 */
  	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() {
  	if (appliedFilter == null) {
  		try {
  			appliedFilter = FrameworkUtil.createFilter(filter);
		} catch (InvalidSyntaxException e) {
			//we will return null
		}
  	}     		
  	return appliedFilter;
  }
}

Back to the top