1 /*
   2  * Copyright (c) 2004, 2015, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 /*
  25  * @test
  26  * @bug 5025141
  27  * @summary Tests that MBeanServerFileAccessController supports
  28  *          principals other than JMXPrincipal.
  29  * @author Luis-Miguel Alventosa
  30  * @modules java.management.rmi
  31  * @run clean NonJMXPrincipalsTest SimpleStandard SimpleStandardMBean
  32  * @run build NonJMXPrincipalsTest SimpleStandard SimpleStandardMBean
  33  * @run main NonJMXPrincipalsTest
  34  */
  35 
  36 import java.io.File;
  37 import java.io.Serializable;
  38 import java.security.Principal;
  39 import java.util.HashMap;
  40 import javax.management.Attribute;
  41 import javax.management.MBeanServer;
  42 import javax.management.MBeanServerConnection;
  43 import javax.management.MBeanServerFactory;
  44 import javax.management.MBeanServerInvocationHandler;
  45 import javax.management.Notification;
  46 import javax.management.NotificationListener;
  47 import javax.management.ObjectName;
  48 import javax.management.remote.JMXAuthenticator;
  49 import javax.management.remote.JMXConnector;
  50 import javax.management.remote.JMXConnectorFactory;
  51 import javax.management.remote.JMXConnectorServer;
  52 import javax.management.remote.JMXConnectorServerFactory;
  53 import javax.management.remote.JMXPrincipal;
  54 import javax.management.remote.JMXServiceURL;
  55 import javax.security.auth.Subject;
  56 
  57 public class NonJMXPrincipalsTest {
  58 
  59     private static class OtherPrincipal implements Principal, Serializable {
  60 
  61         private String name;
  62 
  63         public OtherPrincipal(String name) {
  64             if (name == null)
  65                 throw new NullPointerException("illegal null input");
  66             this.name = name;
  67         }
  68 
  69         public String getName() {
  70             return name;
  71         }
  72 
  73         public String toString() {
  74             return("OtherPrincipal:  " + name);
  75         }
  76 
  77         public boolean equals(Object o) {
  78             if (o == null)
  79                 return false;
  80             if (this == o)
  81                 return true;
  82             if (!(o instanceof OtherPrincipal))
  83                 return false;
  84             OtherPrincipal that = (OtherPrincipal)o;
  85             return (this.getName().equals(that.getName()));
  86         }
  87 
  88         public int hashCode() {
  89             return name.hashCode();
  90         }
  91     }
  92 
  93     private static class OtherPrincipalAuthenticator
  94         implements JMXAuthenticator {
  95         public Subject authenticate(Object credentials) {
  96             final String[] aCredentials = (String[]) credentials;
  97             final String username = (String) aCredentials[0];
  98             final Subject subject = new Subject();
  99             subject.getPrincipals().add(new JMXPrincipal("dummy"));
 100             subject.getPrincipals().add(new OtherPrincipal(username));
 101             return subject;
 102         }
 103     }
 104 
 105     private static class NoPrincipalAuthenticator
 106         implements JMXAuthenticator {
 107         public Subject authenticate(Object credentials) {
 108             return new Subject();
 109         }
 110     }
 111 
 112     public static void runTest(JMXAuthenticator authenticator)
 113         throws Exception {
 114         //------------------------------------------------------------------
 115         // SERVER
 116         //------------------------------------------------------------------
 117 
 118         // Instantiate the MBean server
 119         //
 120         System.out.println("Create the MBean server");
 121         MBeanServer mbs = MBeanServerFactory.createMBeanServer();
 122 
 123         // Create SimpleStandard MBean
 124         //
 125         ObjectName mbeanName = new ObjectName("MBeans:type=SimpleStandard");
 126         System.out.println("Create SimpleStandard MBean...");
 127         mbs.createMBean("SimpleStandard", mbeanName, null, null);
 128 
 129         // Server's environment map
 130         //
 131         System.out.println(">>> Initialize the server's environment map");
 132         HashMap sEnv = new HashMap();
 133 
 134         // Provide a JMX Authenticator
 135         //
 136         sEnv.put("jmx.remote.authenticator", authenticator);
 137 
 138         // Provide the access level file used by the connector server to
 139         // perform user authorization. The access level file is a properties
 140         // based text file specifying username/access level pairs where
 141         // access level is either "readonly" or "readwrite" access to the
 142         // MBeanServer operations. This properties based access control
 143         // checker has been implemented using the MBeanServerForwarder
 144         // interface which wraps the real MBean server inside an access
 145         // controller MBean server which performs the access control checks
 146         // before forwarding the requests to the real MBean server.
 147         //
 148         // This property is implementation-dependent and might not be
 149         // supported by all implementations of the JMX Remote API.
 150         //
 151         sEnv.put("jmx.remote.x.access.file",
 152                  System.getProperty("test.src") +
 153                  File.separator +
 154                  "access.properties");
 155 
 156         // Create an RMI connector server
 157         //
 158         System.out.println("Create an RMI connector server");
 159         JMXServiceURL url = new JMXServiceURL("service:jmx:rmi://");
 160         JMXConnectorServer cs =
 161             JMXConnectorServerFactory.newJMXConnectorServer(url, sEnv, mbs);
 162 
 163         // Start the RMI connector server
 164         //
 165         System.out.println("Start the RMI connector server");
 166         cs.start();
 167         System.out.println("RMI connector server successfully started");
 168         System.out.println("Waiting for incoming connections...");
 169 
 170         //------------------------------------------------------------------
 171         // CLIENT (admin)
 172         //------------------------------------------------------------------
 173 
 174         // Admin client environment map
 175         //
 176         String[] adminCreds = new String[] { "admin" , "adminPassword" };
 177         System.out.println(">>> Initialize the client environment map for" +
 178                            " user [" + adminCreds[0] + "] with " +
 179                            "password [" + adminCreds[1] + "]");
 180         HashMap adminEnv = new HashMap();
 181         adminEnv.put("jmx.remote.credentials", adminCreds);
 182 
 183         // Create an RMI connector client and
 184         // connect it to the RMI connector server
 185         //
 186         System.out.println("Create an RMI connector client and " +
 187                            "connect it to the RMI connector server");
 188         JMXConnector adminConnector =
 189             JMXConnectorFactory.connect(cs.getAddress(), adminEnv);
 190 
 191         // Get an MBeanServerConnection
 192         //
 193         System.out.println("Get an MBeanServerConnection");
 194         MBeanServerConnection adminConnection =
 195             adminConnector.getMBeanServerConnection();
 196 
 197         // Get the proxy for the Simple MBean
 198         //
 199         SimpleStandardMBean adminProxy = (SimpleStandardMBean)
 200             MBeanServerInvocationHandler.newProxyInstance(
 201                                                  adminConnection,
 202                                                  mbeanName,
 203                                                  SimpleStandardMBean.class,
 204                                                  false);
 205 
 206         // Get State attribute
 207         //
 208         System.out.println("State = " + adminProxy.getState());
 209 
 210         // Set State attribute
 211         //
 212         adminProxy.setState("changed state");
 213 
 214         // Get State attribute
 215         //
 216         System.out.println("State = " + adminProxy.getState());
 217 
 218         // Invoke "reset" in SimpleStandard MBean
 219         //
 220         System.out.println("Invoke reset() in SimpleStandard MBean...");
 221         adminProxy.reset();
 222 
 223         // Close MBeanServer connection
 224         //
 225         System.out.println("Close the admin connection to the server");
 226         adminConnector.close();
 227 
 228         //------------------------------------------------------------------
 229         // CLIENT (user)
 230         //------------------------------------------------------------------
 231 
 232         // User client environment map
 233         //
 234         String[] userCreds = new String[] { "user" , "userPassword" };
 235         System.out.println(">>> Initialize the client environment map for" +
 236                            " user [" + userCreds[0] + "] with " +
 237                            "password [" + userCreds[1] + "]");
 238         HashMap userEnv = new HashMap();
 239         userEnv.put("jmx.remote.credentials", userCreds);
 240 
 241         // Create an RMI connector client and
 242         // connect it to the RMI connector server
 243         //
 244         System.out.println("Create an RMI connector client and " +
 245                            "connect it to the RMI connector server");
 246         JMXConnector userConnector =
 247             JMXConnectorFactory.connect(cs.getAddress(), userEnv);
 248 
 249         // Get an MBeanServerConnection
 250         //
 251         System.out.println("Get an MBeanServerConnection");
 252         MBeanServerConnection userConnection =
 253             userConnector.getMBeanServerConnection();
 254 
 255         // Get the proxy for the Simple MBean
 256         //
 257         SimpleStandardMBean userProxy = (SimpleStandardMBean)
 258             MBeanServerInvocationHandler.newProxyInstance(
 259                                                  userConnection,
 260                                                  mbeanName,
 261                                                  SimpleStandardMBean.class,
 262                                                  false);
 263 
 264         // Get State attribute
 265         //
 266         System.out.println("State = " + userProxy.getState());
 267 
 268         // Set State attribute
 269         //
 270         try {
 271             userProxy.setState("changed state");
 272         } catch (SecurityException e) {
 273             System.out.println("Got expected security exception: " + e);
 274         } catch (Exception e) {
 275             System.out.println("Got unexpected exception: " + e);
 276             e.printStackTrace(System.out);
 277         }
 278 
 279         // Get State attribute
 280         //
 281         System.out.println("State = " + userProxy.getState());
 282 
 283         // Invoke "reset" in SimpleStandard MBean
 284         //
 285         try {
 286             System.out.println("Invoke reset() in SimpleStandard MBean...");
 287             userProxy.reset();
 288         } catch (SecurityException e) {
 289             System.out.println("Got expected security exception: " + e);
 290         } catch (Exception e) {
 291             System.out.println("Got unexpected exception: " + e);
 292             e.printStackTrace(System.out);
 293         }
 294 
 295         // Close MBeanServer connection
 296         //
 297         System.out.println("Close the user connection to the server");
 298         userConnector.close();
 299 
 300         //------------------------------------------------------------------
 301         // SERVER
 302         //------------------------------------------------------------------
 303 
 304         // Stop the connector server
 305         //
 306         System.out.println(">>> Stop the connector server");
 307         cs.stop();
 308     }
 309 
 310     public static void main(String[] args) {
 311         int errorCount = 0;
 312         // Runt tests
 313         //
 314         System.out.println("\n>>> Run NoPrincipalAuthenticator test...");
 315         try {
 316             NonJMXPrincipalsTest.runTest(new NoPrincipalAuthenticator());
 317             System.out.println("Did not get expected SecurityException");
 318             errorCount++;
 319         } catch (Exception e) {
 320             if (e instanceof SecurityException) {
 321                 System.out.println("Got expected exception: " + e);
 322             } else {
 323                 System.out.println("Got unexpected exception: " + e);
 324                 errorCount++;
 325             }
 326             e.printStackTrace(System.out);
 327         }
 328         System.out.println("\n>>> Run OtherPrincipalAuthenticator test...");
 329         try {
 330             NonJMXPrincipalsTest.runTest(new OtherPrincipalAuthenticator());
 331         } catch (Exception e) {
 332             errorCount++;
 333             System.out.println("Got unexpected exception: " + e);
 334             e.printStackTrace(System.out);
 335         }
 336 
 337         if (errorCount > 0) {
 338             System.out.println("\nTEST FAILED! Error count = " + errorCount);
 339             System.exit(1);
 340         }
 341 
 342         System.out.println("\nTEST PASSED!");
 343         System.out.println("\nBye! Bye!");
 344     }
 345 }