blob: 29d298089d6780992ad09c4c5b6356da2f3f0999 [file] [log] [blame]
Richard S. Hall930fecc2005-08-16 18:33:34 +00001/*
2 * $Header: /cvshome/build/org.osgi.framework/src/org/osgi/framework/PackagePermission.java,v 1.10 2005/05/13 20:32:55 hargrave Exp $
3 *
4 * Copyright (c) OSGi Alliance (2000, 2005). All Rights Reserved.
5 *
6 * This program and the accompanying materials are made available under the
7 * terms of the Eclipse Public License v1.0 which accompanies this
8 * distribution, and is available at http://www.eclipse.org/legal/epl-v10.html.
9 */
10
11package org.osgi.framework;
12
13import java.io.IOException;
14import java.security.*;
15import java.util.Enumeration;
16import java.util.Hashtable;
17
18/**
19 * A bundle's authority to import or export a package.
20 *
21 * <p>
22 * A package is a dot-separated string that defines a fully qualified Java
23 * package.
24 * <p>
25 * For example:
26 *
27 * <pre>
28 * <code>
29 * org.osgi.service.http
30 * </code>
31 * </pre>
32 *
33 * <p>
34 * <code>PackagePermission</code> has two actions: <code>EXPORT</code> and
35 * <code>IMPORT</code>. The <code>EXPORT</code> action implies the <code>IMPORT</code>
36 * action.
37 *
38 * @version $Revision: 1.10 $
39 */
40
41public final class PackagePermission extends BasicPermission {
42 static final long serialVersionUID = -5107705877071099135L;
43 /**
44 * The action string <code>export</code>.
45 */
46 public final static String EXPORT = "export";
47
48 /**
49 * The action string <code>import</code>.
50 */
51 public final static String IMPORT = "import";
52
53 private final static int ACTION_EXPORT = 0x00000001;
54 private final static int ACTION_IMPORT = 0x00000002;
55 private final static int ACTION_ALL = ACTION_EXPORT
56 | ACTION_IMPORT;
57 private final static int ACTION_NONE = 0;
58 private final static int ACTION_ERROR = 0x80000000;
59
60 /**
61 * The actions mask.
62 */
63 private transient int action_mask = ACTION_NONE;
64
65 /**
66 * The actions in canonical form.
67 *
68 * @serial
69 */
70 private String actions = null;
71
72 /**
73 * Defines the authority to import and/or export a package within the OSGi
74 * environment.
75 * <p>
76 * The name is specified as a normal Java package name: a dot-separated
77 * string. Wildcards may be used. For example:
78 *
79 * <pre>
80 *
81 * org.osgi.service.http
82 * javax.servlet.*
83 * *
84 *
85 * </pre>
86 *
87 * <p>
88 * Package Permissions are granted over all possible versions of a package.
89 *
90 * A bundle that needs to export a package must have the appropriate
91 * <code>PackagePermission</code> for that package; similarly, a bundle that
92 * needs to import a package must have the appropriate
93 * <code>PackagePermssion</code> for that package.
94 * <p>
95 * Permission is granted for both classes and resources.
96 *
97 * @param name Package name.
98 * @param actions <code>EXPORT</code>,<code>IMPORT</code> (canonical order).
99 */
100
101 public PackagePermission(String name, String actions) {
102 this(name, getMask(actions));
103 }
104
105 /**
106 * Package private constructor used by PackagePermissionCollection.
107 *
108 * @param name class name
109 * @param mask action mask
110 */
111 PackagePermission(String name, int mask) {
112 super(name);
113 init(mask);
114 }
115
116 /**
117 * Called by constructors and when deserialized.
118 *
119 * @param mask action mask
120 */
121 private void init(int mask) {
122 if ((mask == ACTION_NONE) || ((mask & ACTION_ALL) != mask)) {
123 throw new IllegalArgumentException("invalid action string");
124 }
125
126 action_mask = mask;
127 }
128
129 /**
130 * Parse action string into action mask.
131 *
132 * @param actions Action string.
133 * @return action mask.
134 */
135 private static int getMask(String actions) {
136 boolean seencomma = false;
137
138 int mask = ACTION_NONE;
139
140 if (actions == null) {
141 return (mask);
142 }
143
144 char[] a = actions.toCharArray();
145
146 int i = a.length - 1;
147 if (i < 0)
148 return (mask);
149
150 while (i != -1) {
151 char c;
152
153 // skip whitespace
154 while ((i != -1)
155 && ((c = a[i]) == ' ' || c == '\r' || c == '\n'
156 || c == '\f' || c == '\t'))
157 i--;
158
159 // check for the known strings
160 int matchlen;
161
162 if (i >= 5 && (a[i - 5] == 'i' || a[i - 5] == 'I')
163 && (a[i - 4] == 'm' || a[i - 4] == 'M')
164 && (a[i - 3] == 'p' || a[i - 3] == 'P')
165 && (a[i - 2] == 'o' || a[i - 2] == 'O')
166 && (a[i - 1] == 'r' || a[i - 1] == 'R')
167 && (a[i] == 't' || a[i] == 'T')) {
168 matchlen = 6;
169 mask |= ACTION_IMPORT;
170
171 }
172 else
173 if (i >= 5 && (a[i - 5] == 'e' || a[i - 5] == 'E')
174 && (a[i - 4] == 'x' || a[i - 4] == 'X')
175 && (a[i - 3] == 'p' || a[i - 3] == 'P')
176 && (a[i - 2] == 'o' || a[i - 2] == 'O')
177 && (a[i - 1] == 'r' || a[i - 1] == 'R')
178 && (a[i] == 't' || a[i] == 'T')) {
179 matchlen = 6;
180 mask |= ACTION_EXPORT | ACTION_IMPORT;
181
182 }
183 else {
184 // parse error
185 throw new IllegalArgumentException("invalid permission: "
186 + actions);
187 }
188
189 // make sure we didn't just match the tail of a word
190 // like "ackbarfimport". Also, skip to the comma.
191 seencomma = false;
192 while (i >= matchlen && !seencomma) {
193 switch (a[i - matchlen]) {
194 case ',' :
195 seencomma = true;
196 /* FALLTHROUGH */
197 case ' ' :
198 case '\r' :
199 case '\n' :
200 case '\f' :
201 case '\t' :
202 break;
203 default :
204 throw new IllegalArgumentException(
205 "invalid permission: " + actions);
206 }
207 i--;
208 }
209
210 // point i at the location of the comma minus one (or -1).
211 i -= matchlen;
212 }
213
214 if (seencomma) {
215 throw new IllegalArgumentException("invalid permission: " + actions);
216 }
217
218 return (mask);
219 }
220
221 /**
222 * Determines if the specified permission is implied by this object.
223 *
224 * <p>
225 * This method checks that the package name of the target is implied by the
226 * package name of this object. The list of <code>PackagePermission</code>
227 * actions must either match or allow for the list of the target object to
228 * imply the target <code>PackagePermission</code> action.
229 * <p>
230 * The permission to export a package implies the permission to import the
231 * named package.
232 *
233 * <pre>
234 * x.y.*,&quot;export&quot; -&gt; x.y.z,&quot;export&quot; is true
235 * *,&quot;import&quot; -&gt; x.y, &quot;import&quot; is true
236 * *,&quot;export&quot; -&gt; x.y, &quot;import&quot; is true
237 * x.y,&quot;export&quot; -&gt; x.y.z, &quot;export&quot; is false
238 * </pre>
239 *
240 * @param p The target permission to interrogate.
241 * @return <code>true</code> if the specified <code>PackagePermission</code>
242 * action is implied by this object; <code>false</code> otherwise.
243 */
244
245 public boolean implies(Permission p) {
246 if (p instanceof PackagePermission) {
247 PackagePermission target = (PackagePermission) p;
248
249 return (((action_mask & target.action_mask) == target.action_mask) && super
250 .implies(p));
251 }
252
253 return (false);
254 }
255
256 /**
257 * Returns the canonical string representation of the
258 * <code>PackagePermission</code> actions.
259 *
260 * <p>
261 * Always returns present <code>PackagePermission</code> actions in the
262 * following order: <code>EXPORT</code>,<code>IMPORT</code>.
263 *
264 * @return Canonical string representation of the <code>PackagePermission</code>
265 * actions.
266 */
267
268 public String getActions() {
269 if (actions == null) {
270 StringBuffer sb = new StringBuffer();
271 boolean comma = false;
272
273 if ((action_mask & ACTION_EXPORT) == ACTION_EXPORT) {
274 sb.append(EXPORT);
275 comma = true;
276 }
277
278 if ((action_mask & ACTION_IMPORT) == ACTION_IMPORT) {
279 if (comma)
280 sb.append(',');
281 sb.append(IMPORT);
282 }
283
284 actions = sb.toString();
285 }
286
287 return (actions);
288 }
289
290 /**
291 * Returns a new <code>PermissionCollection</code> object suitable for storing
292 * <code>PackagePermission</code> objects.
293 *
294 * @return A new <code>PermissionCollection</code> object.
295 */
296 public PermissionCollection newPermissionCollection() {
297 return (new PackagePermissionCollection());
298 }
299
300 /**
301 * Determines the equality of two <code>PackagePermission</code> objects.
302 *
303 * This method checks that specified package has the same package name and
304 * <code>PackagePermission</code> actions as this <code>PackagePermission</code>
305 * object.
306 *
307 * @param obj The object to test for equality with this
308 * <code>PackagePermission</code> object.
309 * @return <code>true</code> if <code>obj</code> is a <code>PackagePermission</code>,
310 * and has the same package name and actions as this
311 * <code>PackagePermission</code> object; <code>false</code> otherwise.
312 */
313 public boolean equals(Object obj) {
314 if (obj == this) {
315 return (true);
316 }
317
318 if (!(obj instanceof PackagePermission)) {
319 return (false);
320 }
321
322 PackagePermission p = (PackagePermission) obj;
323
324 return ((action_mask == p.action_mask) && getName().equals(p.getName()));
325 }
326
327 /**
328 * Returns the hash code value for this object.
329 *
330 * @return A hash code value for this object.
331 */
332
333 public int hashCode() {
334 return (getName().hashCode() ^ getActions().hashCode());
335 }
336
337 /**
338 * Returns the current action mask.
339 * <p>
340 * Used by the PackagePermissionCollection class.
341 *
342 * @return Current action mask.
343 */
344 int getMask() {
345 return (action_mask);
346 }
347
348 /**
349 * WriteObject is called to save the state of this permission object to a
350 * stream. The actions are serialized, and the superclass takes care of the
351 * name.
352 */
353
354 private synchronized void writeObject(java.io.ObjectOutputStream s)
355 throws IOException {
356 // Write out the actions. The superclass takes care of the name
357 // call getActions to make sure actions field is initialized
358 if (actions == null)
359 getActions();
360 s.defaultWriteObject();
361 }
362
363 /**
364 * readObject is called to restore the state of this permission from a
365 * stream.
366 */
367 private synchronized void readObject(java.io.ObjectInputStream s)
368 throws IOException, ClassNotFoundException {
369 // Read in the action, then initialize the rest
370 s.defaultReadObject();
371 init(getMask(actions));
372 }
373}
374
375/**
376 * Stores a set of <code>PackagePermission</code> permissions.
377 *
378 * @see java.security.Permission
379 * @see java.security.Permissions
380 * @see java.security.PermissionCollection
381 */
382
383final class PackagePermissionCollection extends PermissionCollection {
384 static final long serialVersionUID = -3350758995234427603L;
385 /**
386 * Table of permissions.
387 *
388 * @serial
389 */
390 private Hashtable permissions;
391
392 /**
393 * Boolean saying if "*" is in the collection.
394 *
395 * @serial
396 */
397 private boolean all_allowed;
398
399 /**
400 * Create an empty PackagePermissions object.
401 *
402 */
403
404 public PackagePermissionCollection() {
405 permissions = new Hashtable();
406 all_allowed = false;
407 }
408
409 /**
410 * Adds a permission to the <code>PackagePermission</code> objects. The key
411 * for the hash is the name.
412 *
413 * @param permission The <code>PackagePermission</code> object to add.
414 *
415 * @exception IllegalArgumentException If the permission is not a
416 * <code>PackagePermission</code> instance.
417 *
418 * @exception SecurityException If this <code>PackagePermissionCollection</code>
419 * object has been marked read-only.
420 */
421
422 public void add(Permission permission) {
423 if (!(permission instanceof PackagePermission))
424 throw new IllegalArgumentException("invalid permission: "
425 + permission);
426 if (isReadOnly())
427 throw new SecurityException("attempt to add a Permission to a "
428 + "readonly PermissionCollection");
429
430 PackagePermission pp = (PackagePermission) permission;
431 String name = pp.getName();
432
433 PackagePermission existing = (PackagePermission) permissions.get(name);
434
435 if (existing != null) {
436 int oldMask = existing.getMask();
437 int newMask = pp.getMask();
438 if (oldMask != newMask) {
439 permissions.put(name, new PackagePermission(name, oldMask
440 | newMask));
441
442 }
443 }
444 else {
445 permissions.put(name, permission);
446 }
447
448 if (!all_allowed) {
449 if (name.equals("*"))
450 all_allowed = true;
451 }
452 }
453
454 /**
455 * Determines if the specified permissions implies the permissions expressed
456 * in <code>permission</code>.
457 *
458 * @param permission The Permission object to compare with this
459 * <code>PackagePermission</code> object.
460 *
461 * @return <code>true</code> if <code>permission</code> is a proper subset of a
462 * permission in the set; <code>false</code> otherwise.
463 */
464
465 public boolean implies(Permission permission) {
466 if (!(permission instanceof PackagePermission))
467 return (false);
468
469 PackagePermission pp = (PackagePermission) permission;
470 PackagePermission x;
471
472 int desired = pp.getMask();
473 int effective = 0;
474
475 // short circuit if the "*" Permission was added
476 if (all_allowed) {
477 x = (PackagePermission) permissions.get("*");
478 if (x != null) {
479 effective |= x.getMask();
480 if ((effective & desired) == desired)
481 return (true);
482 }
483 }
484
485 // strategy:
486 // Check for full match first. Then work our way up the
487 // name looking for matches on a.b.*
488
489 String name = pp.getName();
490
491 x = (PackagePermission) permissions.get(name);
492
493 if (x != null) {
494 // we have a direct hit!
495 effective |= x.getMask();
496 if ((effective & desired) == desired)
497 return (true);
498 }
499
500 // work our way up the tree...
501 int last, offset;
502
503 offset = name.length() - 1;
504
505 while ((last = name.lastIndexOf(".", offset)) != -1) {
506
507 name = name.substring(0, last + 1) + "*";
508 x = (PackagePermission) permissions.get(name);
509
510 if (x != null) {
511 effective |= x.getMask();
512 if ((effective & desired) == desired)
513 return (true);
514 }
515 offset = last - 1;
516 }
517
518 // we don't have to check for "*" as it was already checked
519 // at the top (all_allowed), so we just return false
520 return (false);
521 }
522
523 /**
524 * Returns an enumeration of all <code>PackagePermission</code> objects in the
525 * container.
526 *
527 * @return Enumeration of all <code>PackagePermission</code> objects.
528 */
529
530 public Enumeration elements() {
531 return (permissions.elements());
532 }
533}
534