001 /*
002 * Copyright (c) OSGi Alliance (2002, 2009). All Rights Reserved.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016 package org.osgi.service.wireadmin;
017
018 import java.io.IOException;
019 import java.security.BasicPermission;
020 import java.security.Permission;
021 import java.security.PermissionCollection;
022 import java.util.Enumeration;
023 import java.util.Hashtable;
024
025 /**
026 * Permission for the scope of a <code>Wire</code> object. When a
027 * <code>Envelope</code> object is used for communication with the
028 * <code>poll</code> or <code>update</code> method, and the scope is set, then
029 * the <code>Wire</code> object must verify that the Consumer service has
030 * <code>WirePermission[name,CONSUME]</code> and the Producer service has
031 * <code>WirePermission[name,PRODUCE]</code> for all names in the scope.
032 * <p>
033 * The names are compared with the normal rules for permission names. This means
034 * that they may end with a "*" to indicate wildcards. E.g. Door.* indicates all
035 * scope names starting with the string "Door". The last period is required due
036 * to the implementations of the <code>BasicPermission</code> class.
037 *
038 * @ThreadSafe
039 * @version $Revision: 6381 $
040 */
041 final public class WirePermission extends BasicPermission {
042 static final long serialVersionUID = -5583709391516569321L;
043 /**
044 * The action string for the <code>produce</code> action.
045 */
046 public static final String PRODUCE = "produce";
047 /**
048 * The action string for the <code>consume</code> action.
049 */
050 public static final String CONSUME = "consume";
051 private final static int ACTION_PRODUCE = 0x00000001;
052 private final static int ACTION_CONSUME = 0x00000002;
053 private final static int ACTION_ALL = ACTION_PRODUCE
054 | ACTION_CONSUME;
055 private final static int ACTION_NONE = 0;
056 /**
057 * The actions mask.
058 */
059 private transient int action_mask;
060 /**
061 * The actions in canonical form.
062 *
063 * @serial
064 */
065 private volatile String actions = null;
066
067 /**
068 * Create a new WirePermission with the given name (may be wildcard) and
069 * actions.
070 *
071 * @param name Wire name.
072 * @param actions <code>produce</code>, <code>consume</code> (canonical
073 * order).
074 */
075 public WirePermission(String name, String actions) {
076 this(name, parseActions(actions));
077 }
078
079 /**
080 * Package private constructor used by WirePermissionCollection.
081 *
082 * @param name class name
083 * @param mask action mask
084 */
085 WirePermission(String name, int mask) {
086 super(name);
087 setTransients(mask);
088 }
089
090 /**
091 * Called by constructors and when deserialized.
092 *
093 * @param mask action mask
094 */
095 private synchronized void setTransients(int mask) {
096 if ((mask == ACTION_NONE) || ((mask & ACTION_ALL) != mask)) {
097 throw new IllegalArgumentException("invalid action string");
098 }
099 action_mask = mask;
100 }
101
102 /**
103 * Returns the current action mask. Used by the WirePermissionCollection
104 * object.
105 *
106 * @return The actions mask.
107 */
108 synchronized int getActionsMask() {
109 return action_mask;
110 }
111
112 /**
113 * Parse action string into action mask.
114 *
115 * @param actions Action string.
116 * @return action mask.
117 */
118 private static int parseActions(String actions) {
119 boolean seencomma = false;
120 int mask = ACTION_NONE;
121 if (actions == null) {
122 return mask;
123 }
124 char[] a = actions.toCharArray();
125 int i = a.length - 1;
126 if (i < 0)
127 return mask;
128 while (i != -1) {
129 char c;
130 // skip whitespace
131 while ((i != -1)
132 && ((c = a[i]) == ' ' || c == '\r' || c == '\n'
133 || c == '\f' || c == '\t'))
134 i--;
135 // check for the known strings
136 int matchlen;
137 if (i >= 6 && (a[i - 6] == 'p' || a[i - 6] == 'P')
138 && (a[i - 5] == 'r' || a[i - 5] == 'R')
139 && (a[i - 4] == 'o' || a[i - 4] == 'O')
140 && (a[i - 3] == 'd' || a[i - 3] == 'D')
141 && (a[i - 2] == 'u' || a[i - 2] == 'U')
142 && (a[i - 1] == 'c' || a[i - 1] == 'C')
143 && (a[i] == 'e' || a[i] == 'E')) {
144 matchlen = 7;
145 mask |= ACTION_PRODUCE;
146 }
147 else
148 if (i >= 6 && (a[i - 6] == 'c' || a[i - 6] == 'C')
149 && (a[i - 5] == 'o' || a[i - 5] == 'O')
150 && (a[i - 4] == 'n' || a[i - 4] == 'N')
151 && (a[i - 3] == 's' || a[i - 3] == 'S')
152 && (a[i - 2] == 'u' || a[i - 2] == 'U')
153 && (a[i - 1] == 'm' || a[i - 1] == 'M')
154 && (a[i] == 'e' || a[i] == 'E')) {
155 matchlen = 7;
156 mask |= ACTION_CONSUME;
157 }
158 else {
159 // parse error
160 throw new IllegalArgumentException("invalid permission: "
161 + actions);
162 }
163 // make sure we didn't just match the tail of a word
164 // like "ackbarfregister". Also, skip to the comma.
165 seencomma = false;
166 while (i >= matchlen && !seencomma) {
167 switch (a[i - matchlen]) {
168 case ',' :
169 seencomma = true;
170 /* FALLTHROUGH */
171 case ' ' :
172 case '\r' :
173 case '\n' :
174 case '\f' :
175 case '\t' :
176 break;
177 default :
178 throw new IllegalArgumentException(
179 "invalid permission: " + actions);
180 }
181 i--;
182 }
183 // point i at the location of the comma minus one (or -1).
184 i -= matchlen;
185 }
186 if (seencomma) {
187 throw new IllegalArgumentException("invalid permission: " + actions);
188 }
189 return mask;
190 }
191
192 /**
193 * Checks if this <code>WirePermission</code> object <code>implies</code>
194 * the specified permission.
195 * <P>
196 * More specifically, this method returns <code>true</code> if:
197 * <p>
198 * <ul>
199 * <li><i>p </i> is an instanceof the <code>WirePermission</code> class,
200 * <li><i>p </i>'s actions are a proper subset of this object's actions, and
201 * <li><i>p </i>'s name is implied by this object's name. For example,
202 * <code>java.*</code> implies <code>java.home</code>.
203 * </ul>
204 *
205 * @param p The permission to check against.
206 *
207 * @return <code>true</code> if the specified permission is implied by this
208 * object; <code>false</code> otherwise.
209 */
210 public boolean implies(Permission p) {
211 if (p instanceof WirePermission) {
212 WirePermission requested = (WirePermission) p;
213 int requestedMask = requested.getActionsMask();
214 return ((getActionsMask() & requestedMask) == requestedMask)
215 && super.implies(p);
216 }
217 return false;
218 }
219
220 /**
221 * Returns the canonical string representation of the actions. Always
222 * returns present actions in the following order: <code>produce</code>,
223 * <code>consume</code>.
224 *
225 * @return The canonical string representation of the actions.
226 */
227 public String getActions() {
228 String result = actions;
229 if (result == null) {
230 StringBuffer sb = new StringBuffer();
231 boolean comma = false;
232 int mask = getActionsMask();
233 if ((mask & ACTION_PRODUCE) == ACTION_PRODUCE) {
234 sb.append(PRODUCE);
235 comma = true;
236 }
237 if ((mask & ACTION_CONSUME) == ACTION_CONSUME) {
238 if (comma)
239 sb.append(',');
240 sb.append(CONSUME);
241 }
242 actions = result = sb.toString();
243 }
244 return result;
245 }
246
247 /**
248 * Returns a new <code>PermissionCollection</code> object for storing
249 * <code>WirePermission</code> objects.
250 *
251 * @return A new <code>PermissionCollection</code> object suitable for
252 * storing <code>WirePermission</code> objects.
253 */
254 public PermissionCollection newPermissionCollection() {
255 return new WirePermissionCollection();
256 }
257
258 /**
259 * Determines the equalty of two <code>WirePermission</code> objects.
260 *
261 * Checks that specified object has the same name and actions as this
262 * <code>WirePermission</code> object.
263 *
264 * @param obj The object to test for equality.
265 * @return true if <code>obj</code> is a <code>WirePermission</code>, and
266 * has the same name and actions as this <code>WirePermission</code>
267 * object; <code>false</code> otherwise.
268 */
269 public boolean equals(Object obj) {
270 if (obj == this) {
271 return true;
272 }
273 if (!(obj instanceof WirePermission)) {
274 return false;
275 }
276 WirePermission wp = (WirePermission) obj;
277 return (getActionsMask() == wp.getActionsMask())
278 && getName().equals(wp.getName());
279 }
280
281 /**
282 * Returns the hash code value for this object.
283 *
284 * @return Hash code value for this object.
285 */
286 public int hashCode() {
287 int h = 31 * 17 + getName().hashCode();
288 h = 31 * h + getActions().hashCode();
289 return h;
290 }
291
292 /**
293 * Returns a string describing this <code>WirePermission</code>. The
294 * convention is to specify the class name, the permission name, and the
295 * actions in the following format:
296 * '(org.osgi.service.wireadmin.WirePermission "name"
297 * "actions")'.
298 *
299 * @return information about this <code>Permission</code> object.
300 */
301 public String toString() {
302 StringBuffer sb = new StringBuffer();
303 sb.append('(');
304 sb.append(getClass().getName());
305 sb.append(" \"");
306 sb.append(getName());
307 String a = getActions();
308 if (a.length() > 0) {
309 sb.append("\" \"");
310 sb.append(a);
311 }
312 sb.append("\")");
313 return sb.toString();
314 }
315
316 /**
317 * WriteObject is called to save the state of the ServicePermission to a
318 * stream. The actions are serialized, and the superclass takes care of the
319 * name.
320 */
321 private synchronized void writeObject(java.io.ObjectOutputStream s)
322 throws IOException {
323 // Write out the actions. The superclass takes care of the name
324 // call getActions to make sure actions field is initialized
325 if (actions == null)
326 getActions();
327 s.defaultWriteObject();
328 }
329
330 /**
331 * readObject is called to restore the state of the ServicePermission from a
332 * stream.
333 */
334 private synchronized void readObject(java.io.ObjectInputStream s)
335 throws IOException, ClassNotFoundException {
336 // Read in the action, then initialize the rest
337 s.defaultReadObject();
338 setTransients(parseActions(actions));
339 }
340 }
341
342 /**
343 * A <code>WirePermissionCollection</code> stores a set of
344 * <code>WirePermission</code> permissions.
345 */
346
347 final class WirePermissionCollection extends PermissionCollection {
348 static final long serialVersionUID = 2617521094909826016L;
349 /**
350 * Table of permissions.
351 *
352 * @GuardedBy this
353 * @serial
354 */
355 private final Hashtable permissions;
356 /**
357 * Boolean saying if "*" is in the collection.
358 *
359 * @GuardedBy this
360 * @serial
361 */
362 private boolean all_allowed;
363
364 /**
365 * Creates an empty WirePermissionCollection object.
366 *
367 */
368 public WirePermissionCollection() {
369 permissions = new Hashtable();
370 all_allowed = false;
371 }
372
373 /**
374 * Adds a permission to this PermissionCollection.
375 *
376 * @param permission The Permission object to add.
377 *
378 * @throws IllegalArgumentException If the permission is not a
379 * WirePermission object.
380 *
381 * @throws SecurityException If this PermissionCollection has been marked
382 * read-only.
383 */
384 public void add(Permission permission) {
385 if (!(permission instanceof WirePermission)) {
386 throw new IllegalArgumentException("invalid permission: "
387 + permission);
388 }
389 if (isReadOnly()) {
390 throw new SecurityException("attempt to add a Permission to a "
391 + "readonly PermissionCollection");
392 }
393 WirePermission wp = (WirePermission) permission;
394 String name = wp.getName();
395 synchronized (this) {
396 WirePermission existing = (WirePermission) permissions.get(name);
397 if (existing != null) {
398 int oldMask = existing.getActionsMask();
399 int newMask = wp.getActionsMask();
400 if (oldMask != newMask) {
401 permissions.put(name, new WirePermission(name, oldMask
402 | newMask));
403 }
404 }
405 else {
406 permissions.put(name, wp);
407 }
408 if (!all_allowed) {
409 if (name.equals("*")) {
410 all_allowed = true;
411 }
412 }
413 }
414 }
415
416 /**
417 * Determines if a set of permissions implies the permissions expressed in
418 * <code>permission</code>.
419 *
420 * @param permission The Permission object to compare.
421 *
422 * @return <code>true</code> if <code>permission</code> is a proper subset
423 * of a permission in the set; <code>false</code> otherwise.
424 */
425 public boolean implies(Permission permission) {
426 if (!(permission instanceof WirePermission)) {
427 return false;
428 }
429 WirePermission requested = (WirePermission) permission;
430 WirePermission x;
431 int desired = requested.getActionsMask();
432 int effective = 0;
433 String name = requested.getName();
434 synchronized (this) {
435 // short circuit if the "*" Permission was added
436 if (all_allowed) {
437 x = (WirePermission) permissions.get("*");
438 if (x != null) {
439 effective |= x.getActionsMask();
440 if ((effective & desired) == desired)
441 return true;
442 }
443 }
444 // strategy:
445 // Check for full match first. Then work our way up the
446 // name looking for matches on a.b.*
447 x = (WirePermission) permissions.get(name);
448 }
449 if (x != null) {
450 // we have a direct hit!
451 effective |= x.getActionsMask();
452 if ((effective & desired) == desired) {
453 return true;
454 }
455 }
456 // work our way up the tree...
457 int last;
458 int offset = name.length() - 1;
459 while ((last = name.lastIndexOf(".", offset)) != -1) {
460 name = name.substring(0, last + 1) + "*";
461 synchronized (this) {
462 x = (WirePermission) permissions.get(name);
463 }
464 if (x != null) {
465 effective |= x.getActionsMask();
466 if ((effective & desired) == desired) {
467 return (true);
468 }
469 }
470 offset = last - 1;
471 }
472 // we don't have to check for "*" as it was already checked
473 // at the top (all_allowed), so we just return false
474 return false;
475 }
476
477 /**
478 * Returns an enumeration of all the Permission objects in the container.
479 *
480 * @return Enumeration of all the Permission objects.
481 */
482 public Enumeration elements() {
483 return permissions.elements();
484 }
485 }