blob: d2f3c31672117ee71d8fcf84e314f0a064457de0 [file] [log] [blame]
Richard S. Hallc88fca32006-10-18 22:01:22 +00001/*
Richard S. Hallf28d6762009-06-08 19:31:06 +00002 * Copyright (c) OSGi Alliance (2000, 2009). All Rights Reserved.
Richard S. Hallc88fca32006-10-18 22:01:22 +00003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package org.osgi.framework;
18
19import java.io.IOException;
Richard S. Hallf28d6762009-06-08 19:31:06 +000020import java.io.NotSerializableException;
21import java.io.ObjectInputStream;
22import java.io.ObjectOutputStream;
23import java.io.ObjectStreamField;
24import java.security.AccessController;
25import java.security.BasicPermission;
26import java.security.Permission;
27import java.security.PermissionCollection;
28import java.security.PrivilegedAction;
29import java.util.ArrayList;
30import java.util.Collection;
31import java.util.Collections;
32import java.util.Dictionary;
Richard S. Hallc88fca32006-10-18 22:01:22 +000033import java.util.Enumeration;
Richard S. Hallf28d6762009-06-08 19:31:06 +000034import java.util.HashMap;
Richard S. Hallc88fca32006-10-18 22:01:22 +000035import java.util.Hashtable;
Richard S. Hallf28d6762009-06-08 19:31:06 +000036import java.util.Iterator;
37import java.util.List;
38import java.util.Map;
Richard S. Hallc88fca32006-10-18 22:01:22 +000039
40/**
Richard S. Hall53e70d32008-08-01 19:31:32 +000041 * A bundle's authority to register or get a service.
Richard S. Hallc88fca32006-10-18 22:01:22 +000042 * <ul>
Richard S. Hallf28d6762009-06-08 19:31:06 +000043 * <li>The <code>register</code> action allows a bundle to register a service on
44 * the specified names.
45 * <li>The <code>get</code> action allows a bundle to detect a service and get
46 * it.
Richard S. Hallc88fca32006-10-18 22:01:22 +000047 * </ul>
48 * Permission to get a service is required in order to detect events regarding
49 * the service. Untrusted bundles should not be able to detect the presence of
50 * certain services unless they have the appropriate
51 * <code>ServicePermission</code> to get the specific service.
52 *
Richard S. Hallf28d6762009-06-08 19:31:06 +000053 * @ThreadSafe
Richard S. Hall2ff3db92009-08-25 14:31:32 +000054 * @version $Revision: 7189 $
Richard S. Hallc88fca32006-10-18 22:01:22 +000055 */
56
Richard S. Hallf28d6762009-06-08 19:31:06 +000057public final class ServicePermission extends BasicPermission {
Richard S. Hallc88fca32006-10-18 22:01:22 +000058 static final long serialVersionUID = -7662148639076511574L;
59 /**
Richard S. Hallf28d6762009-06-08 19:31:06 +000060 * The action string <code>get</code>.
Richard S. Hallc88fca32006-10-18 22:01:22 +000061 */
62 public final static String GET = "get";
63 /**
Richard S. Hallf28d6762009-06-08 19:31:06 +000064 * The action string <code>register</code>.
Richard S. Hallc88fca32006-10-18 22:01:22 +000065 */
66 public final static String REGISTER = "register";
67
68 private final static int ACTION_GET = 0x00000001;
69 private final static int ACTION_REGISTER = 0x00000002;
70 private final static int ACTION_ALL = ACTION_GET
71 | ACTION_REGISTER;
Richard S. Hallf28d6762009-06-08 19:31:06 +000072 final static int ACTION_NONE = 0;
73
Richard S. Hallc88fca32006-10-18 22:01:22 +000074 /**
75 * The actions mask.
76 */
Richard S. Hallf28d6762009-06-08 19:31:06 +000077 transient int action_mask;
Richard S. Hallc88fca32006-10-18 22:01:22 +000078
79 /**
80 * The actions in canonical form.
81 *
82 * @serial
83 */
Richard S. Hallf28d6762009-06-08 19:31:06 +000084 private volatile String actions = null;
85
86 /**
87 * The service used by this ServicePermission. Must be null if not
88 * constructed with a service.
89 */
90 transient final ServiceReference service;
91
92 /**
93 * The object classes for this ServicePermission. Must be null if not
94 * constructed with a service.
95 */
96 transient final String[] objectClass;
97
98 /**
99 * If this ServicePermission was constructed with a filter, this holds a
100 * Filter matching object used to evaluate the filter in implies.
101 */
102 transient Filter filter;
103
104 /**
105 * This dictionary holds the properties of the permission, used to match a
106 * filter in implies. This is not initialized until necessary, and then
107 * cached in this object.
108 */
109 private transient volatile Dictionary properties;
110
111 /**
112 * True if constructed with a name and the name is "*" or ends with ".*".
113 */
114 private transient boolean wildcard;
115
116 /**
117 * If constructed with a name and the name ends with ".*", this contains the
118 * name without the final "*".
119 */
120 private transient String prefix;
Richard S. Hallc88fca32006-10-18 22:01:22 +0000121
122 /**
123 * Create a new ServicePermission.
124 *
125 * <p>
126 * The name of the service is specified as a fully qualified class name.
Richard S. Hallf28d6762009-06-08 19:31:06 +0000127 * Wildcards may be used.
Richard S. Hallc88fca32006-10-18 22:01:22 +0000128 *
129 * <pre>
Richard S. Hallf28d6762009-06-08 19:31:06 +0000130 * name ::= &lt;class name&gt; | &lt;class name ending in &quot;.*&quot;&gt; | *
Richard S. Hallc88fca32006-10-18 22:01:22 +0000131 * </pre>
132 *
133 * Examples:
134 *
135 * <pre>
136 * org.osgi.service.http.HttpService
137 * org.osgi.service.http.*
Richard S. Hallf28d6762009-06-08 19:31:06 +0000138 * *
Richard S. Hallc88fca32006-10-18 22:01:22 +0000139 * </pre>
140 *
Richard S. Hallf28d6762009-06-08 19:31:06 +0000141 * For the <code>get</code> action, the name can also be a filter
142 * expression. The filter gives access to the service properties as well as
143 * the following attributes:
144 * <ul>
145 * <li>signer - A Distinguished Name chain used to sign the bundle
146 * publishing the service. Wildcards in a DN are not matched according to
147 * the filter string rules, but according to the rules defined for a DN
148 * chain.</li>
149 * <li>location - The location of the bundle publishing the service.</li>
150 * <li>id - The bundle ID of the bundle publishing the service.</li>
151 * <li>name - The symbolic name of the bundle publishing the service.</li>
152 * </ul>
153 * Since the above attribute names may conflict with service property names
154 * used by a service, you can prefix an attribute name with '@' in the
155 * filter expression to match against the service property and not one of
156 * the above attributes. Filter attribute names are processed in a case
157 * sensitive manner unless the attribute references a service property.
158 * Service properties names are case insensitive.
159 *
Richard S. Hallc88fca32006-10-18 22:01:22 +0000160 * <p>
161 * There are two possible actions: <code>get</code> and
Richard S. Hallf28d6762009-06-08 19:31:06 +0000162 * <code>register</code>. The <code>get</code> permission allows the owner
163 * of this permission to obtain a service with this name. The
164 * <code>register</code> permission allows the bundle to register a service
165 * under that name.
Richard S. Hallc88fca32006-10-18 22:01:22 +0000166 *
Richard S. Hallf28d6762009-06-08 19:31:06 +0000167 * @param name The service class name
168 * @param actions <code>get</code>,<code>register</code> (canonical order)
Richard S. Hall2ff3db92009-08-25 14:31:32 +0000169 * @throws IllegalArgumentException If the specified name is a filter
170 * expression and either the specified action is not
171 * <code>get</code> or the filter has an invalid syntax.
Richard S. Hallc88fca32006-10-18 22:01:22 +0000172 */
Richard S. Hallc88fca32006-10-18 22:01:22 +0000173 public ServicePermission(String name, String actions) {
Richard S. Hallf28d6762009-06-08 19:31:06 +0000174 this(name, parseActions(actions));
175 if ((filter != null)
176 && ((action_mask & ACTION_ALL) != ACTION_GET)) {
177 throw new IllegalArgumentException(
178 "invalid action string for filter expression");
179 }
180 }
181
182 /**
183 * Creates a new requested <code>ServicePermission</code> object to be used
184 * by code that must perform <code>checkPermission</code> for the
185 * <code>get</code> action. <code>ServicePermission</code> objects created
186 * with this constructor cannot be added to a <code>ServicePermission</code>
187 * permission collection.
188 *
189 * @param reference The requested service.
190 * @param actions The action <code>get</code>.
Richard S. Hall2ff3db92009-08-25 14:31:32 +0000191 * @throws IllegalArgumentException If the specified action is not
192 * <code>get</code> or reference is <code>null</code>.
Richard S. Hallf28d6762009-06-08 19:31:06 +0000193 * @since 1.5
194 */
195 public ServicePermission(ServiceReference reference, String actions) {
196 super(createName(reference));
197 setTransients(null, parseActions(actions));
198 this.service = reference;
199 this.objectClass = (String[]) reference
200 .getProperty(Constants.OBJECTCLASS);
201 if ((action_mask & ACTION_ALL) != ACTION_GET) {
202 throw new IllegalArgumentException("invalid action string");
203 }
204 }
205
206 /**
207 * Create a permission name from a ServiceReference
208 *
209 * @param reference ServiceReference to use to create permission name.
210 * @return permission name.
211 */
212 private static String createName(ServiceReference reference) {
213 if (reference == null) {
214 throw new IllegalArgumentException("reference must not be null");
215 }
216 StringBuffer sb = new StringBuffer("(service.id=");
217 sb.append(reference.getProperty(Constants.SERVICE_ID));
218 sb.append(")");
219 return sb.toString();
Richard S. Hallc88fca32006-10-18 22:01:22 +0000220 }
221
222 /**
223 * Package private constructor used by ServicePermissionCollection.
224 *
225 * @param name class name
226 * @param mask action mask
227 */
228 ServicePermission(String name, int mask) {
229 super(name);
Richard S. Hallf28d6762009-06-08 19:31:06 +0000230 setTransients(parseFilter(name), mask);
231 this.service = null;
232 this.objectClass = null;
Richard S. Hallc88fca32006-10-18 22:01:22 +0000233 }
234
235 /**
236 * Called by constructors and when deserialized.
237 *
238 * @param mask action mask
239 */
Richard S. Hallf28d6762009-06-08 19:31:06 +0000240 private void setTransients(Filter f, int mask) {
Richard S. Hallc88fca32006-10-18 22:01:22 +0000241 if ((mask == ACTION_NONE) || ((mask & ACTION_ALL) != mask)) {
242 throw new IllegalArgumentException("invalid action string");
243 }
Richard S. Hallc88fca32006-10-18 22:01:22 +0000244 action_mask = mask;
Richard S. Hallf28d6762009-06-08 19:31:06 +0000245 filter = f;
246 if (f == null) {
247 String name = getName();
248 int l = name.length();
249 /* if "*" or endsWith ".*" */
250 wildcard = ((name.charAt(l - 1) == '*') && ((l == 1) || (name
251 .charAt(l - 2) == '.')));
252 if (wildcard && (l > 1)) {
253 prefix = name.substring(0, l - 1);
254 }
255 }
Richard S. Hallc88fca32006-10-18 22:01:22 +0000256 }
257
258 /**
259 * Parse action string into action mask.
260 *
261 * @param actions Action string.
262 * @return action mask.
263 */
Richard S. Hallf28d6762009-06-08 19:31:06 +0000264 private static int parseActions(String actions) {
Richard S. Hallc88fca32006-10-18 22:01:22 +0000265 boolean seencomma = false;
266
267 int mask = ACTION_NONE;
268
269 if (actions == null) {
270 return mask;
271 }
272
273 char[] a = actions.toCharArray();
274
275 int i = a.length - 1;
276 if (i < 0)
277 return mask;
278
279 while (i != -1) {
280 char c;
281
282 // skip whitespace
283 while ((i != -1)
284 && ((c = a[i]) == ' ' || c == '\r' || c == '\n'
285 || c == '\f' || c == '\t'))
286 i--;
287
288 // check for the known strings
289 int matchlen;
290
291 if (i >= 2 && (a[i - 2] == 'g' || a[i - 2] == 'G')
292 && (a[i - 1] == 'e' || a[i - 1] == 'E')
293 && (a[i] == 't' || a[i] == 'T')) {
294 matchlen = 3;
295 mask |= ACTION_GET;
296
297 }
298 else
299 if (i >= 7 && (a[i - 7] == 'r' || a[i - 7] == 'R')
300 && (a[i - 6] == 'e' || a[i - 6] == 'E')
301 && (a[i - 5] == 'g' || a[i - 5] == 'G')
302 && (a[i - 4] == 'i' || a[i - 4] == 'I')
303 && (a[i - 3] == 's' || a[i - 3] == 'S')
304 && (a[i - 2] == 't' || a[i - 2] == 'T')
305 && (a[i - 1] == 'e' || a[i - 1] == 'E')
306 && (a[i] == 'r' || a[i] == 'R')) {
307 matchlen = 8;
308 mask |= ACTION_REGISTER;
309
310 }
311 else {
312 // parse error
313 throw new IllegalArgumentException("invalid permission: "
314 + actions);
315 }
316
317 // make sure we didn't just match the tail of a word
318 // like "ackbarfregister". Also, skip to the comma.
319 seencomma = false;
320 while (i >= matchlen && !seencomma) {
321 switch (a[i - matchlen]) {
322 case ',' :
323 seencomma = true;
324 /* FALLTHROUGH */
325 case ' ' :
326 case '\r' :
327 case '\n' :
328 case '\f' :
329 case '\t' :
330 break;
331 default :
332 throw new IllegalArgumentException(
333 "invalid permission: " + actions);
334 }
335 i--;
336 }
337
338 // point i at the location of the comma minus one (or -1).
339 i -= matchlen;
340 }
341
342 if (seencomma) {
343 throw new IllegalArgumentException("invalid permission: " + actions);
344 }
345
346 return mask;
347 }
348
349 /**
Richard S. Hallf28d6762009-06-08 19:31:06 +0000350 * Parse filter string into a Filter object.
351 *
352 * @param filterString The filter string to parse.
353 * @return a Filter for this bundle. If the specified filterString is not a
354 * filter expression, then <code>null</code> is returned.
355 * @throws IllegalArgumentException If the filter syntax is invalid.
356 */
357 private static Filter parseFilter(String filterString) {
358 filterString = filterString.trim();
359 if (filterString.charAt(0) != '(') {
360 return null;
361 }
362
363 try {
364 return FrameworkUtil.createFilter(filterString);
365 }
366 catch (InvalidSyntaxException e) {
367 IllegalArgumentException iae = new IllegalArgumentException(
368 "invalid filter");
369 iae.initCause(e);
370 throw iae;
371 }
372 }
373
374 /**
Richard S. Hallc88fca32006-10-18 22:01:22 +0000375 * Determines if a <code>ServicePermission</code> object "implies" the
376 * specified permission.
377 *
378 * @param p The target permission to check.
Richard S. Hallf28d6762009-06-08 19:31:06 +0000379 * @return <code>true</code> if the specified permission is implied by this
380 * object; <code>false</code> otherwise.
Richard S. Hallc88fca32006-10-18 22:01:22 +0000381 */
Richard S. Hallc88fca32006-10-18 22:01:22 +0000382 public boolean implies(Permission p) {
Richard S. Hallf28d6762009-06-08 19:31:06 +0000383 if (!(p instanceof ServicePermission)) {
384 return false;
Richard S. Hallc88fca32006-10-18 22:01:22 +0000385 }
Richard S. Hallf28d6762009-06-08 19:31:06 +0000386 ServicePermission requested = (ServicePermission) p;
387 if (service != null) {
388 return false;
389 }
390 // if requested permission has a filter, then it is an invalid argument
391 if (requested.filter != null) {
392 return false;
393 }
394 return implies0(requested, ACTION_NONE);
395 }
Richard S. Hallc88fca32006-10-18 22:01:22 +0000396
Richard S. Hallf28d6762009-06-08 19:31:06 +0000397 /**
398 * Internal implies method. Used by the implies and the permission
399 * collection implies methods.
400 *
401 * @param requested The requested ServicePermission which has already be
402 * validated as a proper argument. The requested ServicePermission
403 * must not have a filter expression.
404 * @param effective The effective actions with which to start.
405 * @return <code>true</code> if the specified permission is implied by this
406 * object; <code>false</code> otherwise.
407 */
408 boolean implies0(ServicePermission requested, int effective) {
409 /* check actions first - much faster */
410 effective |= action_mask;
411 final int desired = requested.action_mask;
412 if ((effective & desired) != desired) {
413 return false;
414 }
415 /* we have name of "*" */
416 if (wildcard && (prefix == null)) {
417 return true;
418 }
419 /* if we have a filter */
420 Filter f = filter;
421 if (f != null) {
422 return f.matchCase(requested.getProperties());
423 }
424 /* if requested permission not created with ServiceReference */
425 String[] requestedNames = requested.objectClass;
426 if (requestedNames == null) {
427 return super.implies(requested);
428 }
429 /* requested permission created with ServiceReference */
430 if (wildcard) {
431 int pl = prefix.length();
432 for (int i = 0, l = requestedNames.length; i < l; i++) {
433 String requestedName = requestedNames[i];
434 if ((requestedName.length() > pl)
435 && requestedName.startsWith(prefix)) {
436 return true;
437 }
438 }
439 }
440 else {
441 String name = getName();
442 for (int i = 0, l = requestedNames.length; i < l; i++) {
443 if (requestedNames[i].equals(name)) {
444 return true;
445 }
446 }
447 }
448 return false;
Richard S. Hallc88fca32006-10-18 22:01:22 +0000449 }
450
451 /**
452 * Returns the canonical string representation of the actions. Always
453 * returns present actions in the following order: <code>get</code>,
454 * <code>register</code>.
455 *
456 * @return The canonical string representation of the actions.
457 */
458 public String getActions() {
Richard S. Hallf28d6762009-06-08 19:31:06 +0000459 String result = actions;
460 if (result == null) {
Richard S. Hallc88fca32006-10-18 22:01:22 +0000461 StringBuffer sb = new StringBuffer();
462 boolean comma = false;
463
Richard S. Hallf28d6762009-06-08 19:31:06 +0000464 int mask = action_mask;
465 if ((mask & ACTION_GET) == ACTION_GET) {
Richard S. Hallc88fca32006-10-18 22:01:22 +0000466 sb.append(GET);
467 comma = true;
468 }
469
Richard S. Hallf28d6762009-06-08 19:31:06 +0000470 if ((mask & ACTION_REGISTER) == ACTION_REGISTER) {
Richard S. Hallc88fca32006-10-18 22:01:22 +0000471 if (comma)
472 sb.append(',');
473 sb.append(REGISTER);
474 }
475
Richard S. Hallf28d6762009-06-08 19:31:06 +0000476 actions = result = sb.toString();
Richard S. Hallc88fca32006-10-18 22:01:22 +0000477 }
478
Richard S. Hallf28d6762009-06-08 19:31:06 +0000479 return result;
Richard S. Hallc88fca32006-10-18 22:01:22 +0000480 }
481
482 /**
483 * Returns a new <code>PermissionCollection</code> object for storing
484 * <code>ServicePermission<code> objects.
485 *
486 * @return A new <code>PermissionCollection</code> object suitable for storing
487 * <code>ServicePermission</code> objects.
488 */
489 public PermissionCollection newPermissionCollection() {
Richard S. Hallf28d6762009-06-08 19:31:06 +0000490 return new ServicePermissionCollection();
Richard S. Hallc88fca32006-10-18 22:01:22 +0000491 }
492
493 /**
Richard S. Hallf28d6762009-06-08 19:31:06 +0000494 * Determines the equality of two ServicePermission objects.
Richard S. Hallc88fca32006-10-18 22:01:22 +0000495 *
496 * Checks that specified object has the same class name and action as this
497 * <code>ServicePermission</code>.
498 *
499 * @param obj The object to test for equality.
Richard S. Hallf28d6762009-06-08 19:31:06 +0000500 * @return true if obj is a <code>ServicePermission</code>, and has the same
501 * class name and actions as this <code>ServicePermission</code>
502 * object; <code>false</code> otherwise.
Richard S. Hallc88fca32006-10-18 22:01:22 +0000503 */
504 public boolean equals(Object obj) {
505 if (obj == this) {
Richard S. Hallf28d6762009-06-08 19:31:06 +0000506 return true;
Richard S. Hallc88fca32006-10-18 22:01:22 +0000507 }
508
509 if (!(obj instanceof ServicePermission)) {
Richard S. Hallf28d6762009-06-08 19:31:06 +0000510 return false;
Richard S. Hallc88fca32006-10-18 22:01:22 +0000511 }
512
Richard S. Hallf28d6762009-06-08 19:31:06 +0000513 ServicePermission sp = (ServicePermission) obj;
Richard S. Hallc88fca32006-10-18 22:01:22 +0000514
Richard S. Hallf28d6762009-06-08 19:31:06 +0000515 return (action_mask == sp.action_mask)
516 && getName().equals(sp.getName())
517 && ((service == sp.service) || ((service != null) && (service
518 .compareTo(sp.service) == 0)));
Richard S. Hallc88fca32006-10-18 22:01:22 +0000519 }
520
521 /**
522 * Returns the hash code value for this object.
523 *
524 * @return Hash code value for this object.
525 */
Richard S. Hallc88fca32006-10-18 22:01:22 +0000526 public int hashCode() {
Richard S. Hallf28d6762009-06-08 19:31:06 +0000527 int h = 31 * 17 + getName().hashCode();
528 h = 31 * h + getActions().hashCode();
529 if (service != null) {
530 h = 31 * h + service.hashCode();
531 }
532 return h;
Richard S. Hallc88fca32006-10-18 22:01:22 +0000533 }
534
535 /**
536 * WriteObject is called to save the state of this permission to a stream.
537 * The actions are serialized, and the superclass takes care of the name.
538 */
Richard S. Hallc88fca32006-10-18 22:01:22 +0000539 private synchronized void writeObject(java.io.ObjectOutputStream s)
540 throws IOException {
Richard S. Hallf28d6762009-06-08 19:31:06 +0000541 if (service != null) {
542 throw new NotSerializableException("cannot serialize");
543 }
Richard S. Hallc88fca32006-10-18 22:01:22 +0000544 // Write out the actions. The superclass takes care of the name
545 // call getActions to make sure actions field is initialized
546 if (actions == null)
547 getActions();
548 s.defaultWriteObject();
549 }
550
551 /**
552 * readObject is called to restore the state of this permission from a
553 * stream.
554 */
555 private synchronized void readObject(java.io.ObjectInputStream s)
556 throws IOException, ClassNotFoundException {
557 // Read in the action, then initialize the rest
558 s.defaultReadObject();
Richard S. Hallf28d6762009-06-08 19:31:06 +0000559 setTransients(parseFilter(getName()), parseActions(actions));
560 }
561 /**
562 * Called by <code><@link ServicePermission#implies(Permission)></code>.
563 *
564 * @return a dictionary of properties for this permission.
565 */
566 private Dictionary getProperties() {
567 Dictionary result = properties;
568 if (result != null) {
569 return result;
570 }
571 if (service == null) {
572 result = new Hashtable(1);
573 if (filter == null) {
574 result.put(Constants.OBJECTCLASS, new String[] {getName()});
575 }
576 return properties = result;
577 }
578 final Map props = new HashMap(4);
579 final Bundle bundle = service.getBundle();
580 if (bundle != null) {
581 AccessController.doPrivileged(new PrivilegedAction() {
582 public Object run() {
583 props.put("id", new Long(bundle.getBundleId()));
584 props.put("location", bundle.getLocation());
585 String name = bundle.getSymbolicName();
586 if (name != null) {
587 props.put("name", name);
588 }
589 SignerProperty signer = new SignerProperty(bundle);
590 if (signer.isBundleSigned()) {
591 props.put("signer", signer);
592 }
593 return null;
594 }
595 });
596 }
597 return properties = new Properties(props, service);
598 }
599
600 private static class Properties extends Dictionary {
601 private final Map properties;
602 private final ServiceReference service;
603
604 Properties(Map properties, ServiceReference service) {
605 this.properties = properties;
606 this.service = service;
607 }
608
609 public Object get(Object k) {
610 if (!(k instanceof String)) {
611 return null;
612 }
613 String key = (String) k;
614 if (key.charAt(0) == '@') {
615 return service.getProperty(key.substring(1));
616 }
617 Object value = properties.get(key);
618 if (value != null) { // fall back to service properties
619 return value;
620 }
621 return service.getProperty(key);
622 }
623
624 public int size() {
625 return properties.size() + service.getPropertyKeys().length;
626 }
627
628 public boolean isEmpty() {
629 // we can return false because this must never be empty
630 return false;
631 }
632
633 public Enumeration keys() {
634 Collection pk = properties.keySet();
635 String spk[] = service.getPropertyKeys();
636 List all = new ArrayList(pk.size() + spk.length);
637 all.addAll(pk);
638 add:
639 for (int i = 0, length = spk.length; i < length; i++) {
640 String key = spk[i];
641 for (Iterator iter = pk.iterator(); iter.hasNext();) {
642 if (key.equalsIgnoreCase((String) iter.next())) {
643 continue add;
644 }
645 }
646 all.add(key);
647 }
648 return Collections.enumeration(all);
649 }
650
651 public Enumeration elements() {
652 Collection pk = properties.keySet();
653 String spk[] = service.getPropertyKeys();
654 List all = new ArrayList(pk.size() + spk.length);
655 all.addAll(properties.values());
656 add:
657 for (int i = 0, length = spk.length; i < length; i++) {
658 String key = spk[i];
659 for (Iterator iter = pk.iterator(); iter.hasNext();) {
660 if (key.equalsIgnoreCase((String) iter.next())) {
661 continue add;
662 }
663 }
664 all.add(service.getProperty(key));
665 }
666 return Collections.enumeration(all);
667 }
668
669 public Object put(Object key, Object value) {
670 throw new UnsupportedOperationException();
671 }
672
673 public Object remove(Object key) {
674 throw new UnsupportedOperationException();
675 }
Richard S. Hallc88fca32006-10-18 22:01:22 +0000676 }
677}
678
679/**
680 * Stores a set of ServicePermission permissions.
681 *
682 * @see java.security.Permission
683 * @see java.security.Permissions
684 * @see java.security.PermissionCollection
685 */
Richard S. Hallc88fca32006-10-18 22:01:22 +0000686final class ServicePermissionCollection extends PermissionCollection {
687 static final long serialVersionUID = 662615640374640621L;
688 /**
689 * Table of permissions.
690 *
Richard S. Hallf28d6762009-06-08 19:31:06 +0000691 * @GuardedBy this
Richard S. Hallc88fca32006-10-18 22:01:22 +0000692 */
Richard S. Hallf28d6762009-06-08 19:31:06 +0000693 private transient Map permissions;
Richard S. Hallc88fca32006-10-18 22:01:22 +0000694
695 /**
696 * Boolean saying if "*" is in the collection.
697 *
698 * @serial
Richard S. Hallf28d6762009-06-08 19:31:06 +0000699 * @GuardedBy this
Richard S. Hallc88fca32006-10-18 22:01:22 +0000700 */
701 private boolean all_allowed;
702
703 /**
Richard S. Hallf28d6762009-06-08 19:31:06 +0000704 * Table of permissions with filter expressions.
705 *
706 * @serial
707 * @GuardedBy this
708 */
709 private Map filterPermissions;
710
711 /**
Richard S. Hallc88fca32006-10-18 22:01:22 +0000712 * Creates an empty ServicePermissions object.
713 */
Richard S. Hallc88fca32006-10-18 22:01:22 +0000714 public ServicePermissionCollection() {
Richard S. Hallf28d6762009-06-08 19:31:06 +0000715 permissions = new HashMap();
Richard S. Hallc88fca32006-10-18 22:01:22 +0000716 all_allowed = false;
717 }
718
719 /**
Richard S. Hallf28d6762009-06-08 19:31:06 +0000720 * Adds a permission to this permission collection.
Richard S. Hallc88fca32006-10-18 22:01:22 +0000721 *
722 * @param permission The Permission object to add.
Richard S. Hallf28d6762009-06-08 19:31:06 +0000723 * @throws IllegalArgumentException If the specified permission is not a
Richard S. Hallc88fca32006-10-18 22:01:22 +0000724 * ServicePermission object.
725 * @throws SecurityException If this
726 * <code>ServicePermissionCollection</code> object has been marked
727 * read-only.
728 */
Richard S. Hallf28d6762009-06-08 19:31:06 +0000729 public void add(final Permission permission) {
730 if (!(permission instanceof ServicePermission)) {
Richard S. Hallc88fca32006-10-18 22:01:22 +0000731 throw new IllegalArgumentException("invalid permission: "
732 + permission);
Richard S. Hallf28d6762009-06-08 19:31:06 +0000733 }
734 if (isReadOnly()) {
Richard S. Hallc88fca32006-10-18 22:01:22 +0000735 throw new SecurityException("attempt to add a Permission to a "
736 + "readonly PermissionCollection");
Richard S. Hallf28d6762009-06-08 19:31:06 +0000737 }
Richard S. Hallc88fca32006-10-18 22:01:22 +0000738
Richard S. Hallf28d6762009-06-08 19:31:06 +0000739 final ServicePermission sp = (ServicePermission) permission;
740 if (sp.service != null) {
741 throw new IllegalArgumentException("cannot add to collection: "
742 + sp);
743 }
744
745 final String name = sp.getName();
746 final Filter f = sp.filter;
747 synchronized (this) {
748 /* select the bucket for the permission */
749 Map pc;
750 if (f != null) {
751 pc = filterPermissions;
752 if (pc == null) {
753 filterPermissions = pc = new HashMap();
754 }
Richard S. Hallc88fca32006-10-18 22:01:22 +0000755 }
Richard S. Hallf28d6762009-06-08 19:31:06 +0000756 else {
757 pc = permissions;
758 }
759 final ServicePermission existing = (ServicePermission) pc.get(name);
760
761 if (existing != null) {
762 final int oldMask = existing.action_mask;
763 final int newMask = sp.action_mask;
764 if (oldMask != newMask) {
765 pc
766 .put(name, new ServicePermission(name, oldMask
767 | newMask));
768 }
769 }
770 else {
771 pc.put(name, sp);
772 }
773
774 if (!all_allowed) {
775 if (name.equals("*")) {
776 all_allowed = true;
777 }
778 }
Richard S. Hallc88fca32006-10-18 22:01:22 +0000779 }
780 }
781
782 /**
783 * Determines if a set of permissions implies the permissions expressed in
784 * <code>permission</code>.
785 *
786 * @param permission The Permission object to compare.
Richard S. Hallc88fca32006-10-18 22:01:22 +0000787 * @return <code>true</code> if <code>permission</code> is a proper
788 * subset of a permission in the set; <code>false</code>
789 * otherwise.
790 */
Richard S. Hallf28d6762009-06-08 19:31:06 +0000791 public boolean implies(final Permission permission) {
792 if (!(permission instanceof ServicePermission)) {
793 return false;
794 }
795 final ServicePermission requested = (ServicePermission) permission;
796 /* if requested permission has a filter, then it is an invalid argument */
797 if (requested.filter != null) {
798 return false;
Richard S. Hallc88fca32006-10-18 22:01:22 +0000799 }
800
Richard S. Hallf28d6762009-06-08 19:31:06 +0000801 int effective = ServicePermission.ACTION_NONE;
802 Collection perms;
803 synchronized (this) {
804 final int desired = requested.action_mask;
805 /* short circuit if the "*" Permission was added */
806 if (all_allowed) {
807 ServicePermission sp = (ServicePermission) permissions.get("*");
808 if (sp != null) {
809 effective |= sp.action_mask;
810 if ((effective & desired) == desired) {
811 return true;
812 }
813 }
814 }
815
816 String[] requestedNames = requested.objectClass;
817 /* if requested permission not created with ServiceReference */
818 if (requestedNames == null) {
819 effective |= effective(requested.getName(), desired, effective);
820 if ((effective & desired) == desired) {
821 return true;
822 }
823 }
824 /* requested permission created with ServiceReference */
825 else {
826 for (int i = 0, l = requestedNames.length; i < l; i++) {
827 if ((effective(requestedNames[i], desired, effective) & desired) == desired) {
828 return true;
829 }
830 }
831 }
832 Map pc = filterPermissions;
833 if (pc == null) {
834 return false;
835 }
836 perms = pc.values();
837 }
838
839 /* iterate one by one over filteredPermissions */
840 for (Iterator iter = perms.iterator(); iter.hasNext();) {
841 if (((ServicePermission) iter.next())
842 .implies0(requested, effective)) {
843 return true;
844 }
845 }
846 return false;
847 }
848
849 /**
850 * Consult permissions map to compute the effective permission for the
851 * requested permission name.
852 *
853 * @param requestedName The requested service name.
854 * @param desired The desired actions.
855 * @param effective The effective actions.
856 * @return The new effective actions.
857 */
858 private int effective(String requestedName, final int desired,
859 int effective) {
860 final Map pc = permissions;
861 ServicePermission sp = (ServicePermission) pc.get(requestedName);
Richard S. Hallc88fca32006-10-18 22:01:22 +0000862 // strategy:
863 // Check for full match first. Then work our way up the
864 // name looking for matches on a.b.*
Richard S. Hallf28d6762009-06-08 19:31:06 +0000865 if (sp != null) {
Richard S. Hallc88fca32006-10-18 22:01:22 +0000866 // we have a direct hit!
Richard S. Hallf28d6762009-06-08 19:31:06 +0000867 effective |= sp.action_mask;
868 if ((effective & desired) == desired) {
869 return effective;
870 }
Richard S. Hallc88fca32006-10-18 22:01:22 +0000871 }
Richard S. Hallc88fca32006-10-18 22:01:22 +0000872 // work our way up the tree...
Richard S. Hallf28d6762009-06-08 19:31:06 +0000873 int last;
874 int offset = requestedName.length() - 1;
875 while ((last = requestedName.lastIndexOf(".", offset)) != -1) {
876 requestedName = requestedName.substring(0, last + 1) + "*";
877 sp = (ServicePermission) pc.get(requestedName);
878 if (sp != null) {
879 effective |= sp.action_mask;
880 if ((effective & desired) == desired) {
881 return effective;
882 }
Richard S. Hallc88fca32006-10-18 22:01:22 +0000883 }
884 offset = last - 1;
885 }
Richard S. Hallf28d6762009-06-08 19:31:06 +0000886 /*
887 * we don't have to check for "*" as it was already checked before we
888 * were called.
889 */
890 return effective;
Richard S. Hallc88fca32006-10-18 22:01:22 +0000891 }
Richard S. Hallf28d6762009-06-08 19:31:06 +0000892
Richard S. Hallc88fca32006-10-18 22:01:22 +0000893 /**
894 * Returns an enumeration of all the <code>ServicePermission</code>
895 * objects in the container.
896 *
897 * @return Enumeration of all the ServicePermission objects.
898 */
Richard S. Hallf28d6762009-06-08 19:31:06 +0000899 public synchronized Enumeration elements() {
900 List all = new ArrayList(permissions.values());
901 Map pc = filterPermissions;
902 if (pc != null) {
903 all.addAll(pc.values());
904 }
905 return Collections.enumeration(all);
906 }
907
908 /* serialization logic */
909 private static final ObjectStreamField[] serialPersistentFields = {
910 new ObjectStreamField("permissions", Hashtable.class),
911 new ObjectStreamField("all_allowed", Boolean.TYPE),
912 new ObjectStreamField("filterPermissions", HashMap.class) };
Richard S. Hallc88fca32006-10-18 22:01:22 +0000913
Richard S. Hallf28d6762009-06-08 19:31:06 +0000914 private synchronized void writeObject(ObjectOutputStream out)
915 throws IOException {
916 Hashtable hashtable = new Hashtable(permissions);
917 ObjectOutputStream.PutField pfields = out.putFields();
918 pfields.put("permissions", hashtable);
919 pfields.put("all_allowed", all_allowed);
920 pfields.put("filterPermissions", filterPermissions);
921 out.writeFields();
922 }
923
924 private synchronized void readObject(java.io.ObjectInputStream in)
925 throws IOException, ClassNotFoundException {
926 ObjectInputStream.GetField gfields = in.readFields();
927 Hashtable hashtable = (Hashtable) gfields.get("permissions", null);
928 permissions = new HashMap(hashtable);
929 all_allowed = gfields.get("all_allowed", false);
930 filterPermissions = (HashMap) gfields.get("filterPermissions", null);
Richard S. Hallc88fca32006-10-18 22:01:22 +0000931 }
932}