001/* MBeanServerPermission.java -- Permissions controlling server creation.
002   Copyright (C) 2006 Free Software Foundation, Inc.
003
004This file is part of GNU Classpath.
005
006GNU Classpath is free software; you can redistribute it and/or modify
007it under the terms of the GNU General Public License as published by
008the Free Software Foundation; either version 2, or (at your option)
009any later version.
010
011GNU Classpath is distributed in the hope that it will be useful, but
012WITHOUT ANY WARRANTY; without even the implied warranty of
013MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014General Public License for more details.
015
016You should have received a copy of the GNU General Public License
017along with GNU Classpath; see the file COPYING.  If not, write to the
018Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
01902110-1301 USA.
020
021Linking this library statically or dynamically with other modules is
022making a combined work based on this library.  Thus, the terms and
023conditions of the GNU General Public License cover the whole
024combination.
025
026As a special exception, the copyright holders of this library give you
027permission to link this library with independent modules to produce an
028executable, regardless of the license terms of these independent
029modules, and to copy and distribute the resulting executable under
030terms of your choice, provided that you also meet, for each linked
031independent module, the terms and conditions of the license of that
032module.  An independent module is a module which is not derived from
033or based on this library.  If you modify this library, you may extend
034this exception to your version of the library, but you are not
035obligated to do so.  If you do not wish to do so, delete this
036exception statement from your version. */
037
038package javax.management;
039
040import java.security.BasicPermission;
041import java.security.Permission;
042import java.security.PermissionCollection;
043
044import java.util.Enumeration;
045import 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 */
082public 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}
471
472
473
474