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        @Override
284        public void add(Permission p)
285        {
286          if (isReadOnly())
287            throw new SecurityException("This collection is read only.");
288          if (p instanceof MBeanServerPermission)
289            {
290              MBeanServerPermission msp = (MBeanServerPermission) p;
291              if (collectionPermission == null)
292                collectionPermission = msp;
293              else
294                {
295                  String finalString = collectionPermission.getName();
296                  String[] cp = finalString.split(",");
297                  String[] np = msp.getName().split(",");
298                  int createms = finalString.indexOf("createMBeanServer");
299                  int newms = finalString.indexOf("newMBeanServer");
300                  for (int a = 0; a < np.length; ++a)
301                    {
302                      boolean found = false;
303                      String nps = np[a].trim();
304                      for (int b = 0; b < cp.length; ++b)
305                        {
306                          String cps = cp[b].trim();
307                          if (cps.equals(nps))
308                            found = true;
309                          if (nps.equals("newMBeanServer")
310                              && createms != -1)
311                            found = true;
312                          if (nps.equals("createMBeanServer")
313                              && newms != -1)
314                            finalString =
315                              finalString.replace("newMBeanServer",
316                                                  "createMBeanServer");
317                        }
318                      if (!found)
319                        finalString += "," + nps;
320                    }
321                  collectionPermission =
322                    new MBeanServerPermission(finalString);
323                }
324            }
325        }
326    
327        /**
328         * Returns an enumeration over the single permission.
329         *
330         * @return an enumeration over the collection permission.
331         */
332        @Override
333        public Enumeration<Permission> elements()
334        {
335          return new
336            MBeanServerPermissionEnumeration(collectionPermission);
337        }
338    
339        /**
340         * Provides an enumeration over a comma-separated list
341         * of capabilities.
342         *
343         * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
344         * @since 1.5
345         */
346        private class MBeanServerPermissionEnumeration
347          implements Enumeration<Permission>
348        {
349    
350          /**
351           * The collected permission.
352           */
353          private MBeanServerPermission p;
354    
355          /**
356           * True if we have returned the permission.
357           */
358          private boolean done;
359    
360          /**
361           * Constructs a new {@link MBeanServerPermissionEnumeration}
362           * using the given collected permission.
363           *
364           * @param p the collected permission.
365           */
366          public MBeanServerPermissionEnumeration(MBeanServerPermission p)
367          {
368            this.p = p;
369            done = false;
370          }
371    
372          /**
373           * Returns true if there are more capabilities to return.
374           *
375           * @return true if there are more capabilities available.
376           */
377          public boolean hasMoreElements()
378          {
379            return !done;
380          }
381    
382          /**
383           * Returns the next capability.
384           *
385           * @return the next capability.
386           */
387          public Permission nextElement()
388          {
389            if (hasMoreElements())
390              {
391                done = true;
392                return p;
393              }
394            else
395              throw new NoSuchElementException("No more elements are available.");
396          }
397    
398        }
399    
400        /**
401         * Returns true if the collected {@link MBeanServerPermission}
402         * implies the given permission.  This occurs if the given permission
403         * is also an {@link MBeanServerPermission} and its target names
404         * are a subset of the target names of this permission.  Note that
405         * the name <code>createMBeanServer</code> implies
406         * <code>newMBeanServer</code>.
407         *
408         * @param p the permission to check for implication.
409         * @return true if this permission implies <code>p</code>.
410         */
411        @Override
412        public boolean implies(Permission p)
413        {
414          return collectionPermission.implies(p);
415        }
416      }
417    
418      /**
419       * Checks the name is valid, including removing
420       * the <code>newMBeanServer</code> permission when
421       * <code>createMBeanServer</code> is present.
422       *
423       * @param name the name to check.
424       * @throws NullPointerException if <code>name</code>
425       *                              is <code>null</code>.
426       * @throws IllegalArgumentException if <code>name</code>
427       *                                  is not either equal to
428       *                                  <code>"*"</code> or forms
429       *                                  a comma-separated list of
430       *                                  valid constraints.
431       */
432      private static String checkName(String name)
433      {
434        if (!(name.equals("*")))
435          {
436            String[] constraints = name.split(",");
437            name = "";
438            boolean seenCreate = false;
439            boolean seenNew = false;
440            boolean start = true;
441            for (int a = 0; a < constraints.length; ++a)
442              {
443                String next = constraints[a].trim();
444                if (!(next.equals("createMBeanServer") ||
445                      next.equals("findMBeanServer") ||
446                      next.equals("newMBeanServer") ||
447                      next.equals("releaseMBeanServer")))
448                  throw new IllegalArgumentException("An invalid constraint, " +
449                                                     next + ", was specified.");
450                if (next.equals("newMBeanServer"))
451                  seenNew = true;
452                else if (next.equals("createMBeanServer"))
453                  seenCreate = true;
454                else
455                  {
456                    if (!start)
457                      name += ",";
458                    name += next;
459                    start = false;
460                  }
461              }
462            if (seenNew && !seenCreate)
463              name += (start ? "" : ",") + "newMBeanServer";
464            else if (seenCreate)
465              name += (start ? "" : ",") + "createMBeanServer";
466          }
467        return name;
468      }
469    
470    }