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 &quot;name&quot;
297             * &quot;actions&quot;)'.
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    }