001    /* MBeanServerPermission.java -- Permissions controlling server creation.
002       Copyright (C) 2006 Free Software Foundation, Inc.
003    
004    This file is part of GNU Classpath.
005    
006    GNU Classpath is free software; you can redistribute it and/or modify
007    it under the terms of the GNU General Public License as published by
008    the Free Software Foundation; either version 2, or (at your option)
009    any later version.
010    
011    GNU Classpath is distributed in the hope that it will be useful, but
012    WITHOUT ANY WARRANTY; without even the implied warranty of
013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014    General Public License for more details.
015    
016    You should have received a copy of the GNU General Public License
017    along with GNU Classpath; see the file COPYING.  If not, write to the
018    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019    02110-1301 USA.
020    
021    Linking this library statically or dynamically with other modules is
022    making a combined work based on this library.  Thus, the terms and
023    conditions of the GNU General Public License cover the whole
024    combination.
025    
026    As a special exception, the copyright holders of this library give you
027    permission to link this library with independent modules to produce an
028    executable, regardless of the license terms of these independent
029    modules, and to copy and distribute the resulting executable under
030    terms of your choice, provided that you also meet, for each linked
031    independent module, the terms and conditions of the license of that
032    module.  An independent module is a module which is not derived from
033    or based on this library.  If you modify this library, you may extend
034    this exception to your version of the library, but you are not
035    obligated to do so.  If you do not wish to do so, delete this
036    exception statement from your version. */
037    
038    package javax.management;
039    
040    import java.security.BasicPermission;
041    import java.security.Permission;
042    import java.security.PermissionCollection;
043    
044    import java.util.Enumeration;
045    import java.util.NoSuchElementException;
046    
047    /**
048     * <p>
049     * Represents the permissions required to perform
050     * operations provided by the {@link MBeanServerFactory}.
051     * As with all {@link java.security.Permission} objects, an
052     * instance of this class either represents a permission
053     * already held or one that is required to access a
054     * particular service.  In the case of {@link MBeanServerPermission}s,
055     * implication checks are made using an instance of this class
056     * when a user requests an operation from the factory, and a
057     * {@link SecurityManager} is in place.
058     * </p>
059     * <p>
060     * The permission is defined by its name, which may be
061     * either a <code>'*'</code> (to allow all) or one or
062     * more of the following, separated by a <code>','</code>:
063     * </p>
064     * <ul>
065     * <li><code>createMBeanServer</code> -- allows a registered
066     * instance of a server to be obtained from the factory.</li>
067     * <li><code>findMBeanServer</code> -- allows all or one
068     * particular server instance to be retrieved from the factory.</li>
069     * <li><code>newMBeanServer</code> -- allows an unregistered
070     * instance of a server to be obtained from the factory.</li>
071     * <li><code>releaseMBeanServer</code> -- allows a reference to
072     * a server instance to be removed from the factory.</li>
073     * </ul>
074     * <p>
075     * The names may be surrounded by arbitrary amounts of whitespace.
076     * <code>createMBeanServer</code> implies <code>newMBeanServer</code>.
077     * </p>
078     *
079     * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
080     * @since 1.5
081     */
082    public class MBeanServerPermission
083      extends BasicPermission
084    {
085    
086      /**
087       * Compatible with JDK 1.5
088       */
089      private static final long serialVersionUID = -5661980843569388590L;
090    
091      /**
092       * <p>
093       * Constructs a new {@link MBeanServerPermission} with
094       * the given name.  The name must not be <code>null</code>
095       * and must be equal to either <code>"*"</code> or a
096       * comma-separated list of valid permissions.  The four
097       * valid constraints are:
098       * </p>
099       * <ol>
100       * <li><code>createMBeanServer</code></li>
101       * <li><code>findMBeanServer</code></li>
102       * <li><code>newMBeanServer</code></li>
103       * <li><code>releaseMBeanServer</code></li>
104       * </ol>
105       * <p>
106       * Calling this constructor is equivalent to calling
107       * <code>MBeanPermission(name, null)</code>.
108       * </p>
109       *
110       * @param name the name of this permission.
111       * @throws NullPointerException if <code>name</code>
112       *                              is <code>null</code>.
113       * @throws IllegalArgumentException if <code>name</code>
114       *                                  is not either equal to
115       *                                  <code>"*"</code> or forms
116       *                                  a comma-separated list of
117       *                                  valid constraints.
118       * @see #MBeanServerPermission(String,String)
119       */
120      public MBeanServerPermission(String name)
121      {
122        this(name, null);
123      }
124    
125      /**
126       * <p>
127       * Constructs a new {@link MBeanServerPermission} with
128       * the given name and actions.  The actions are unused,
129       * and must be either <code>null</code> or the empty
130       * string.  The name must not be <code>null</code>
131       * and must be equal to either <code>"*"</code> or a
132       * comma-separated list of valid permissions.  The four
133       * valid constraints are:
134       * </p>
135       * <ol>
136       * <li><code>createMBeanServer</code></li>
137       * <li><code>findMBeanServer</code></li>
138       * <li><code>newMBeanServer</code></li>
139       * <li><code>releaseMBeanServer</code></li>
140       * </ol>
141       * <p>
142       * Calling this constructor is equivalent to calling
143       * <code>MBeanPermission(name, null)</code>.
144       * </p>
145       *
146       * @param name the name of this permission.
147       * @throws NullPointerException if <code>name</code>
148       *                              is <code>null</code>.
149       * @throws IllegalArgumentException if <code>name</code>
150       *                                  is not either equal to
151       *                                  <code>"*"</code> or forms
152       *                                  a comma-separated list of
153       *                                  valid constraints, or if
154       *                                  <code>actions</code> is not
155       *                                  <code>null</code> or the
156       *                                  empty string.
157       * @see #MBeanServerPermission(String,String)
158       */
159      public MBeanServerPermission(String name, String actions)
160      {
161        super(checkName(name), actions);
162        if (actions != null && actions.length() > 0)
163          throw new IllegalArgumentException("The supplied action list " +
164                                             "was not equal to null or the " +
165                                             "empty string.");
166      }
167      
168      /**
169       * Returns true if the given object is also an {@link MBeanServerPermission}
170       * with the same name.
171       *
172       * @param obj the object to compare with this one.
173       * @return true if the object is an {@link MBeanPermission}
174       *         with the same name.
175       */
176      public boolean equals(Object obj)
177      {
178        if (obj instanceof MBeanServerPermission)
179          {
180            MBeanServerPermission o = (MBeanServerPermission) obj;
181            return o.getName().equals(getName());
182          }
183        return false;
184      }
185    
186      /**
187       * Returns a unique hash code for this permission.
188       * This is simply the hashcode of {@link BasicPermission#getName()}.
189       *
190       * @return the hashcode of this permission.
191       */
192      public int hashCode()
193      {
194        return getName().hashCode();
195      }
196    
197      /**
198       * Returns true if this {@link MBeanServerPermission} implies
199       * the given permission.  This occurs if the given permission
200       * is also an {@link MBeanServerPermission} and its target names
201       * are a subset of the target names of this permission.  Note that
202       * the name <code>createMBeanServer</code> implies
203       * <code>newMBeanServer</code>.
204       *
205       * @param p the permission to check for implication.
206       * @return true if this permission implies <code>p</code>.
207       */
208      public boolean implies(Permission p)
209      {
210        if (p instanceof MBeanServerPermission)
211          {
212            if (getName().equals("*"))
213              return true;
214            MBeanServerPermission msp = (MBeanServerPermission) p;
215            String[] thisCaps = getName().split(",");
216            String[] mspCaps = msp.getName().split(",");
217            for (int a = 0; a < mspCaps.length; ++a)
218              {
219                boolean found = false;
220                String mc = mspCaps[a].trim();
221                for (int b = 0; b < thisCaps.length; ++b)
222                  {
223                    String tc = thisCaps[b].trim();
224                    if (tc.equals(mc))
225                      found = true;
226                    if (tc.equals("createMBeanServer") &&
227                        mc.equals("newMBeanServer"))
228                      found = true;
229                  }
230                if (!found)
231                  return false;
232              }
233            return true;
234          }
235        return false;
236      }
237    
238      /**
239       * Returns a {@link PermissionCollection} which stores
240       * a series of {@link MBeanServerPermission}s as the union
241       * of their capabilities.
242       *
243       * @return a collection for {@link MBeanServerPermission}s.
244       */
245      public PermissionCollection newPermissionCollection()
246      {
247        return new MBeanServerPermissionCollection();
248      }
249      
250      /**
251       * A collection of {@link MBeanServerPermission}s, stored
252       * as a single permission with the union of the capabilities
253       * as its capabilities.
254       *
255       * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
256       * @since 1.5
257       */
258      private class MBeanServerPermissionCollection
259        extends PermissionCollection
260      {
261    
262        /**
263         * Compatible with JDK 1.5
264         */
265        private static final long serialVersionUID = -5661980843569388590L;
266    
267        /**
268         * The collected permission.  This is <code>null</code> or
269         * the union of the permissions held by all the collected
270         * permissions.
271         */
272        private MBeanServerPermission collectionPermission;
273    
274        /**
275         * Adds a new permission by unifying it with the existing
276         * collection permission.
277         *
278         * @param p the permission to add.
279         * @throws SecurityException if the collection is read only.
280         * @see #isReadOnly()
281         * @see #setReadOnly(boolean)
282         */
283        public void add(Permission p)
284        {
285          if (isReadOnly())
286            throw new SecurityException("This collection is read only.");
287          if (p instanceof MBeanServerPermission)
288            {
289              MBeanServerPermission msp = (MBeanServerPermission) p;
290              if (collectionPermission == null)
291                collectionPermission = msp;
292              else
293                {
294                  String finalString = collectionPermission.getName();
295                  String[] cp = finalString.split(",");
296                  String[] np = msp.getName().split(",");
297                  int createms = finalString.indexOf("createMBeanServer");
298                  int newms = finalString.indexOf("newMBeanServer");
299                  for (int a = 0; a < np.length; ++a)
300                    {
301                      boolean found = false;
302                      String nps = np[a].trim();
303                      for (int b = 0; b < cp.length; ++b)
304                        {
305                          String cps = cp[b].trim();
306                          if (cps.equals(nps))
307                            found = true;
308                          if (np.equals("newMBeanServer") 
309                              && createms != -1)
310                            found = true;
311                          if (np.equals("createMBeanServer")
312                              && newms != -1)
313                            finalString.replace("newMBeanServer",
314                                                "createMBeanServer");
315                        }
316                      if (!found)
317                        finalString += "," + nps;
318                    }
319                  collectionPermission =
320                    new MBeanServerPermission(finalString);
321                }
322            }
323        }
324        
325        /**
326         * Returns an enumeration over the single permission.
327         *
328         * @return an enumeration over the collection permission.
329         */
330        public Enumeration elements()
331        {
332          return new
333            MBeanServerPermissionEnumeration(collectionPermission);
334        }
335    
336        /**
337         * Provides an enumeration over a comma-separated list
338         * of capabilities.
339         *
340         * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
341         * @since 1.5
342         */
343        private class MBeanServerPermissionEnumeration
344          implements Enumeration
345        {
346    
347          /**
348           * The collected permission.
349           */
350          private MBeanServerPermission p;
351    
352          /**
353           * True if we have returned the permission.
354           */
355          private boolean done;
356    
357          /**
358           * Constructs a new {@link MBeanServerPermissionEnumeration}
359           * using the given collected permission.
360           *
361           * @param p the collected permission.
362           */
363          public MBeanServerPermissionEnumeration(MBeanServerPermission p)
364          {
365            this.p = p;
366            done = false;
367          }
368    
369          /**
370           * Returns true if there are more capabilities to return.
371           *
372           * @return true if there are more capabilities available.
373           */
374          public boolean hasMoreElements()
375          {
376            return !done;
377          }
378    
379          /**
380           * Returns the next capability.
381           *
382           * @return the next capability.
383           */
384          public Object nextElement()
385          {
386            if (hasMoreElements())
387              {
388                done = true;
389                return p;
390              }
391            else
392              throw new NoSuchElementException("No more elements are available.");
393          }
394    
395        }
396    
397        /**
398         * Returns true if the collected {@link MBeanServerPermission}
399         * implies the given permission.  This occurs if the given permission
400         * is also an {@link MBeanServerPermission} and its target names
401         * are a subset of the target names of this permission.  Note that
402         * the name <code>createMBeanServer</code> implies
403         * <code>newMBeanServer</code>.
404         *
405         * @param p the permission to check for implication.
406         * @return true if this permission implies <code>p</code>.
407         */
408        public boolean implies(Permission p)
409        {
410          return collectionPermission.implies(p);
411        }
412      }
413    
414      /**
415       * Checks the name is valid, including removing
416       * the <code>newMBeanServer</code> permission when
417       * <code>createMBeanServer</code> is present.
418       *
419       * @param name the name to check.
420       * @throws NullPointerException if <code>name</code>
421       *                              is <code>null</code>.
422       * @throws IllegalArgumentException if <code>name</code>
423       *                                  is not either equal to
424       *                                  <code>"*"</code> or forms
425       *                                  a comma-separated list of
426       *                                  valid constraints.
427       */
428      private static String checkName(String name)
429      {
430        if (!(name.equals("*")))
431          {
432            String[] constraints = name.split(",");
433            name = "";
434            boolean seenCreate = false;
435            boolean seenNew = false;
436            boolean start = true;
437            for (int a = 0; a < constraints.length; ++a)
438              {
439                String next = constraints[a].trim();
440                if (!(next.equals("createMBeanServer") ||
441                      next.equals("findMBeanServer") ||
442                      next.equals("newMBeanServer") ||
443                      next.equals("releaseMBeanServer")))
444                  throw new IllegalArgumentException("An invalid constraint, " +
445                                                     next + ", was specified.");
446                if (next.equals("newMBeanServer"))
447                  seenNew = true;
448                else if (next.equals("createMBeanServer"))
449                  seenCreate = true;
450                else
451                  {
452                    if (!start)
453                      name += ",";
454                    name += next;
455                    start = false;
456                  }
457              }
458            if (seenNew && !seenCreate)
459              name += (start ? "" : ",") + "newMBeanServer";
460            else if (seenCreate)
461              name += (start ? "" : ",") + "createMBeanServer";
462          }
463        return name;
464      }
465    
466    }
467    
468    
469    
470