1/* 2 * Copyright (c) 2003, 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 8058865 27 * @summary Checks various authentication behavior from remote jmx client 28 * @author Olivier Lagneau 29 * @modules java.management.rmi 30 * @library /lib/testlibrary 31 * @compile Simple.java 32 * @run main/othervm/timeout=300 -DDEBUG_STANDARD -Dusername=username1 -Dpassword=password1 AuthorizationTest -server -mapType x.access.file;x.password.file -populate -client -mapType credentials 33 * @run main/othervm/timeout=300 -DDEBUG_STANDARD -Dusername=username2 -Dpassword=password2 AuthorizationTest -server -mapType x.access.file;x.password.file -populate -client -mapType credentials -expectedCreateException -expectedSetException -expectedInvokeException 34 * @run main/othervm/timeout=300 -DDEBUG_STANDARD -Dusername=username6 -Dpassword=password6 AuthorizationTest -server -mapType x.access.file;x.password.file -populate -client -mapType credentials -expectedCreateException -expectedGetException -expectedSetException -expectedInvokeException 35 * @run main/othervm/timeout=300/policy=java.policy.authorization -DDEBUG_STANDARD -Dusername=username1 -Dpassword=password1 AuthorizationTest -server -mapType x.password.file -populate -client -mapType credentials 36 * @run main/othervm/timeout=300/policy=java.policy.authorization -DDEBUG_STANDARD -Dusername=username3 -Dpassword=password3 AuthorizationTest -server -mapType x.password.file -populate -client -mapType credentials -expectedGetException 37 * @run main/othervm/timeout=300/policy=java.policy.authorization -DDEBUG_STANDARD -Dusername=username5 -Dpassword=password5 AuthorizationTest -server -mapType x.password.file -populate -client -mapType credentials -expectedCreateException -expectedGetException -expectedSetException -expectedInvokeException 38 * @run main/othervm/timeout=300/policy=java.policy.authorization -DDEBUG_STANDARD -Dusername=username6 -Dpassword=password6 AuthorizationTest -server -mapType x.password.file -populate -client -mapType credentials -expectedCreateException -expectedGetException -expectedSetException -expectedInvokeException 39 * @run main/othervm/timeout=300/policy=java.policy.authorization -DDEBUG_STANDARD -Dusername=username1 -Dpassword=password1 AuthorizationTest -server -mapType x.access.file;x.password.file -populate -client -mapType credentials 40 * @run main/othervm/timeout=300/policy=java.policy.authorization -DDEBUG_STANDARD -Dusername=username2 -Dpassword=password2 AuthorizationTest -server -mapType x.access.file;x.password.file -populate -client -mapType credentials -expectedCreateException -expectedSetException -expectedInvokeException 41 * @run main/othervm/timeout=300/policy=java.policy.authorization -DDEBUG_STANDARD -Dusername=username3 -Dpassword=password3 AuthorizationTest -server -mapType x.access.file;x.password.file -populate -client -mapType credentials -expectedCreateException -expectedGetException -expectedSetException -expectedInvokeException 42 * @run main/othervm/timeout=300/policy=java.policy.authorization -DDEBUG_STANDARD -Dusername=username4 -Dpassword=password4 AuthorizationTest -server -mapType x.access.file;x.password.file -populate -client -mapType credentials -expectedGetException -expectedSetException 43 * @run main/othervm/timeout=300/policy=java.policy.authorization -DDEBUG_STANDARD -Dusername=username5 -Dpassword=password5 AuthorizationTest -server -mapType x.access.file;x.password.file -populate -client -mapType credentials -expectedCreateException -expectedGetException -expectedSetException -expectedInvokeException 44 */ 45 46import java.io.File; 47import java.util.Map ; 48import java.util.HashMap ; 49import java.util.List; 50import java.util.ArrayList; 51import java.util.Arrays; 52 53import java.lang.management.ManagementFactory; 54 55import javax.management.MBeanServer; 56import javax.management.MBeanServerFactory ; 57import javax.management.MBeanServerConnection; 58import javax.management.remote.JMXConnector; 59import javax.management.remote.JMXConnectorFactory; 60import javax.management.remote.JMXConnectorServer; 61import javax.management.remote.JMXConnectorServerFactory; 62import javax.management.remote.JMXServiceURL; 63 64import javax.management.Attribute ; 65import javax.management.ObjectName ; 66 67import jdk.testlibrary.ProcessTools; 68import jdk.testlibrary.JDKToolFinder; 69 70public class AuthorizationTest { 71 72 static final String SERVER_CLASS_NAME = "AuthorizationTest"; 73 static final String CLIENT_CLASS_NAME = "AuthorizationTest$ClientSide"; 74 static final String CLIENT_CLASS_MAIN = CLIENT_CLASS_NAME; 75 76 static final String USERNAME_PROPERTY = "username"; 77 static final String PASSWORD_PROPERTY = "password"; 78 79 private JMXConnectorServer cs; 80 81 /* 82 * First Debug properties and arguments are collect in expected 83 * map (argName, value) format, then calls original test's run method. 84 */ 85 public static void main(String args[]) throws Exception { 86 87 System.out.println("================================================="); 88 89 // Parses parameters 90 Utils.parseDebugProperties(); 91 92 // Supported parameters list format is : 93 // "MainClass [-server <param-spec> ...] [-client <param-spec> ...] 94 // with <param-spec> either "-parami valuei" or "-parami" 95 HashMap<String, Object> serverMap = new HashMap<>() ; 96 int clientArgsIndex = 97 Utils.parseServerParameters(args, SERVER_CLASS_NAME, serverMap); 98 99 // Extract and records client params 100 String[] clientParams = null; 101 if (clientArgsIndex < args.length) { 102 int clientParamsSize = args.length - clientArgsIndex; 103 clientParams = new String[clientParamsSize]; 104 System.arraycopy(args, clientArgsIndex, clientParams, 0, clientParamsSize); 105 } else { 106 clientParams = new String[0]; 107 } 108 109 // Run test 110 AuthorizationTest test = new AuthorizationTest(); 111 test.run(serverMap, clientParams); 112 113 } 114 115 /* 116 * Create the MBeansServer side of the test and returns its address 117 */ 118 private JMXServiceURL createServerSide(Map<String, Object> serverMap) 119 throws Exception { 120 final int NINETY_SECONDS = 90; 121 122 System.out.println("AuthorizationTest::createServerSide: Start") ; 123 124 MBeanServer mbs = MBeanServerFactory.newMBeanServer(); 125 JMXServiceURL url = new JMXServiceURL("rmi", null, 0); 126 127 // Creates connection environment from server side params 128 HashMap<String, Object> env = new HashMap<>(); 129 String value = null; 130 131 if ((value = (String)serverMap.get("-mapType")) != null) { 132 if (value.contains("x.access.file")) { 133 String accessFileStr = System.getProperty("test.src") + 134 File.separator + "access.properties"; 135 env.put("jmx.remote.x.access.file", accessFileStr); 136 System.out.println("Added " + accessFileStr + " file as jmx.remote.x.access.file"); 137 } 138 if (value.contains("x.password.file")) { 139 String passwordFileStr = System.getProperty("test.src") + 140 File.separator + "password.properties"; 141 env.put("jmx.remote.x.password.file", passwordFileStr); 142 System.out.println("Added " + passwordFileStr + " file as jmx.remote.x.password.file"); 143 } 144 } 145 146 if (serverMap.containsKey("-populate")) { 147 String populateClassName = "Simple"; 148 ObjectName on = 149 new ObjectName("defaultDomain:class=Simple"); 150 151 Utils.debug(Utils.DEBUG_STANDARD, "create and register Simple MBean") ; 152 mbs.createMBean(populateClassName, on); 153 } 154 155 cs = JMXConnectorServerFactory.newJMXConnectorServer(url, env, mbs); 156 cs.start(); 157 158 Utils.waitReady(cs, NINETY_SECONDS); 159 160 JMXServiceURL addr = cs.getAddress(); 161 162 System.out.println("AuthorizationTest::createServerSide: Done.") ; 163 164 return addr; 165 } 166 167 /* 168 * Creating command-line for running subprocess JVM: 169 * 170 * JVM command line is like: 171 * {test_jdk}/bin/java {defaultopts} -cp {test.class.path} {testopts} main 172 * 173 * {defaultopts} are the default java options set by the framework. 174 * 175 */ 176 private List<String> buildCommandLine(String args[]) { 177 List<String> opts = new ArrayList<>(); 178 opts.add(JDKToolFinder.getJDKTool("java")); 179 opts.addAll(Arrays.asList(jdk.testlibrary.Utils.getTestJavaOpts())); 180 181 String usernameValue = System.getProperty(USERNAME_PROPERTY); 182 if (usernameValue != null) { 183 opts.add("-D" + USERNAME_PROPERTY + "=" + usernameValue); 184 } 185 String passwordValue = System.getProperty(PASSWORD_PROPERTY); 186 if (passwordValue != null) { 187 opts.add("-D" + PASSWORD_PROPERTY + "=" + passwordValue); 188 } 189 190 opts.add("-cp"); 191 opts.add(System.getProperty("test.class.path", "test.class.path")); 192 opts.add(CLIENT_CLASS_MAIN); 193 opts.addAll(Arrays.asList(args)); 194 return opts; 195 } 196 197 /** 198 * Runs AuthorizationTest$ClientSide with the passed options and redirects 199 * subprocess standard I/O to the current (parent) process. This provides a 200 * trace of what happens in the subprocess while it is runnning (and before 201 * it terminates). 202 * 203 * @param serviceUrlStr string representing the JMX service Url to connect to. 204 */ 205 private int runClientSide(String args[], String serviceUrlStr) throws Exception { 206 207 // Building command-line 208 List<String> opts = buildCommandLine(args); 209 opts.add("-serviceUrl"); 210 opts.add(serviceUrlStr); 211 212 // Launch separate JVM subprocess 213 int exitCode = 0; 214 String[] optsArray = opts.toArray(new String[0]); 215 ProcessBuilder pb = new ProcessBuilder(optsArray); 216 Process p = ProcessTools.startProcess("AuthorizationTest$ClientSide", pb); 217 218 // Handling end of subprocess 219 try { 220 exitCode = p.waitFor(); 221 if (exitCode != 0) { 222 System.out.println( 223 "Subprocess unexpected exit value of [" + exitCode + 224 "]. Expected 0.\n"); 225 } 226 } catch (InterruptedException e) { 227 System.out.println("Parent process interrupted with exception : \n " + e + " :" ); 228 229 // Parent thread unknown state, killing subprocess. 230 p.destroyForcibly(); 231 232 throw new RuntimeException( 233 "Parent process interrupted with exception : \n " + e + " :" ); 234 235 } finally { 236 if (p.isAlive()) { 237 p.destroyForcibly(); 238 } 239 return exitCode; 240 } 241 242 } 243 244 public void run(Map<String, Object> serverArgs, String clientArgs[]) { 245 246 System.out.println("AuthorizationTest::run: Start") ; 247 int errorCount = 0; 248 249 try { 250 // Initialise the server side 251 JMXServiceURL urlToUse = createServerSide(serverArgs); 252 253 // Run client side 254 errorCount = runClientSide(clientArgs, urlToUse.toString()); 255 256 if ( errorCount == 0 ) { 257 System.out.println("AuthorizationTest::run: Done without any error") ; 258 } else { 259 System.out.println("AuthorizationTest::run: Done with " 260 + errorCount 261 + " error(s)") ; 262 throw new RuntimeException("errorCount = " + errorCount); 263 } 264 265 cs.stop(); 266 267 } catch(Exception e) { 268 throw new RuntimeException(e); 269 } 270 271 } 272 273 private static class ClientSide { 274 275 private JMXConnector cc = null; 276 private MBeanServerConnection mbsc = null; 277 278 public static void main(String args[]) throws Exception { 279 280 // Parses parameters 281 Utils.parseDebugProperties(); 282 283 // Supported parameters list format is : "MainClass [-client <param-spec> ...] 284 // with <param-spec> either "-parami valuei" or "-parami" 285 HashMap<String, Object> clientMap = new HashMap<>() ; 286 Utils.parseClientParameters(args, CLIENT_CLASS_NAME, clientMap); 287 288 // Run test 289 ClientSide test = new ClientSide(); 290 test.run(clientMap); 291 292 } 293 294 public void run(Map<String, Object> args) { 295 296 int errorCount = 0 ; 297 298 try { 299 boolean expectedCreateException = 300 (args.containsKey("-expectedCreateException")) ? true : false ; 301 boolean expectedGetException = 302 (args.containsKey("-expectedGetException")) ? true : false ; 303 boolean expectedSetException = 304 (args.containsKey("-expectedSetException")) ? true : false ; 305 boolean expectedInvokeException = 306 (args.containsKey("-expectedInvokeException")) ? true : false ; 307 // JSR262 (see bug 6440374) 308 // There is no special JSR262 protocol operation for connect. 309 // The first request sent initiate the connection. 310 // In the JSR262 current implementation, getDefaultDomain is sent to 311 // the server in order to get the server part of the connection ID. 312 // => the connection may fail if no access permission on get requests. 313 boolean expectedConnectException = 314 (args.containsKey("-expectedConnectException")) ? true : false ; 315 // Before connection, 316 // remove the element of the Map with null values (not supported by RMI) 317 // See bug 4982668 318 args.remove("-expectedCreateException"); 319 args.remove("-expectedGetException"); 320 args.remove("-expectedSetException"); 321 args.remove("-expectedInvokeException"); 322 args.remove("-expectedConnectException"); 323 324 325 // Here do connect to the JMX Server 326 String username = System.getProperty("username"); 327 Utils.debug(Utils.DEBUG_STANDARD, 328 "ClientSide::run: CONNECT on behalf of \"" + username + "\""); 329 doConnect(args, expectedConnectException); 330 331 // If the connection did not fail, perform some requests. 332 // At this stage the mbeanserver connection is up and running 333 if (mbsc != null) { 334 ObjectName on = new ObjectName("defaultDomain:class=Simple"); 335 336 // Create request 337 Utils.debug(Utils.DEBUG_STANDARD, 338 "ClientSide::run: CREATE on behalf of \"" + 339 username + "\""); 340 errorCount += doCreateRequest(mbsc, 341 new ObjectName("defaultDomain:class=Simple,user=" + username), 342 expectedCreateException); 343 344 // Get request 345 Utils.debug(Utils.DEBUG_STANDARD, 346 "ClientSide::run: GET on behalf of \"" + 347 username + "\""); 348 errorCount += doGetRequest(mbsc, on, expectedGetException); 349 350 // Set request 351 Utils.debug(Utils.DEBUG_STANDARD, 352 "ClientSide::run: SET on behalf of \"" + 353 username + "\""); 354 errorCount += doSetRequest(mbsc, on, expectedSetException); 355 356 // Invoke request 357 Utils.debug(Utils.DEBUG_STANDARD, 358 "ClientSide::run: INVOKE on behalf of \"" + 359 username + "\""); 360 errorCount += doInvokeRequest(mbsc, on, expectedInvokeException); 361 } 362 363 } catch(Exception e) { 364 Utils.printThrowable(e, true) ; 365 errorCount++; 366 } finally { 367 // Terminate the JMX Client 368 try { 369 cc.close(); 370 } catch (Exception e) { 371 Utils.printThrowable(e, true) ; 372 errorCount++; 373 } 374 } 375 376 System.out.println("ClientSide::run: Done") ; 377 378 // Handle result 379 if (errorCount == 0) { 380 System.out.println("ClientSide::run: (OK) authorization test succeeded."); 381 } else { 382 String message = "AuthorizationTest$ClientSide::run: (ERROR) " + 383 " authorization test failed with " + 384 errorCount + " error(s)"; 385 System.out.println(message); 386 throw new RuntimeException(message); 387 } 388 } 389 390 protected void doConnect(Map<String, Object> args, 391 boolean expectedException) { 392 393 String msgTag = "ClientSide::doConnect"; 394 boolean throwRuntimeException = false; 395 String message = ""; 396 397 try { 398 Utils.debug(Utils.DEBUG_STANDARD, 399 "ClientSide::doConnect: Connect the client"); 400 401 // Collect connection environment 402 HashMap<String, Object> env = new HashMap<>(); 403 404 Object value = args.get("-mapType"); 405 if (value != null) { 406 String username = System.getProperty("username"); 407 String password = System.getProperty("password"); 408 Utils.debug(Utils.DEBUG_STANDARD, 409 msgTag + "add \"jmx.remote.credentials\" = \"" + 410 username + "\", \"" + password + "\""); 411 env.put("jmx.remote.credentials", 412 new String[] { username , password }); 413 } 414 415 // Get a connection to remote mbean server 416 JMXServiceURL addr = new JMXServiceURL((String)args.get("-serviceUrl")); 417 cc = JMXConnectorFactory.connect(addr,env); 418 mbsc = cc.getMBeanServerConnection(); 419 420 if (expectedException) { 421 message = "ClientSide::doConnect: (ERROR) " + 422 "Connect did not fail with expected SecurityException"; 423 System.out.println(message); 424 throwRuntimeException = true; 425 } else { 426 System.out.println("ClientSide::doConnect: (OK) Connect succeed"); 427 } 428 } catch(Exception e) { 429 Utils.printThrowable(e, true); 430 if (expectedException) { 431 if (e instanceof java.lang.SecurityException) { 432 System.out.println("ClientSide::doConnect: (OK) " + 433 "Connect failed with expected SecurityException"); 434 } else { 435 message = "ClientSide::doConnect: (ERROR) " + 436 "Create failed with " + e.getClass() + 437 " instead of expected SecurityException"; 438 System.out.println(message); 439 throwRuntimeException = true; 440 } 441 } else { 442 message = "ClientSide::doConnect: (ERROR) " + 443 "Connect failed"; 444 System.out.println(message); 445 throwRuntimeException = true; 446 } 447 } 448 449 // If the connection failed, or if the connection succeeded but should not, 450 // no need to go further => throw RuntimeException and exit the test 451 if (throwRuntimeException) { 452 throw new RuntimeException(message); 453 } 454 } 455 456 protected int doCreateRequest(MBeanServerConnection mbsc, 457 ObjectName on, 458 boolean expectedException) { 459 int errorCount = 0; 460 461 try { 462 Utils.debug(Utils.DEBUG_STANDARD, 463 "ClientSide::doCreateRequest: Create and register the MBean") ; 464 465 mbsc.createMBean("Simple", on) ; 466 467 if (expectedException) { 468 System.out.println("ClientSide::doCreateRequest: " + 469 "(ERROR) Create did not fail with expected SecurityException"); 470 errorCount++; 471 } else { 472 System.out.println("ClientSide::doCreateRequest: (OK) Create succeed") ; 473 } 474 } catch(Exception e) { 475 Utils.printThrowable(e, true) ; 476 if (expectedException) { 477 if (e instanceof java.lang.SecurityException) { 478 System.out.println("ClientSide::doCreateRequest: " + 479 "(OK) Create failed with expected SecurityException") ; 480 } else { 481 System.out.println("ClientSide::doCreateRequest: " + 482 "(ERROR) Create failed with " + 483 e.getClass() + " instead of expected SecurityException"); 484 errorCount++; 485 } 486 } else { 487 System.out.println("ClientSide::doCreateRequest: " + 488 "(ERROR) Create failed"); 489 errorCount++; 490 } 491 } 492 return errorCount; 493 } 494 495 protected int doGetRequest(MBeanServerConnection mbsc, 496 ObjectName on, 497 boolean expectedException) { 498 int errorCount = 0; 499 500 try { 501 Utils.debug(Utils.DEBUG_STANDARD, 502 "ClientSide::doGetRequest: Get attributes of the MBean") ; 503 504 mbsc.getAttribute(on, "Attribute"); 505 506 if (expectedException) { 507 System.out.println("ClientSide::doGetRequest: " + 508 "(ERROR) Get did not fail with expected SecurityException"); 509 errorCount++; 510 } else { 511 System.out.println("ClientSide::doGetRequest: (OK) Get succeed") ; 512 } 513 } catch(Exception e) { 514 Utils.printThrowable(e, true) ; 515 if (expectedException) { 516 if (e instanceof java.lang.SecurityException) { 517 System.out.println("ClientSide::doGetRequest: " + 518 "(OK) Get failed with expected SecurityException") ; 519 } else { 520 System.out.println("ClientSide::doGetRequest: " + 521 "(ERROR) Get failed with " + 522 e.getClass() + " instead of expected SecurityException"); 523 errorCount++; 524 } 525 } else { 526 System.out.println("ClientSide::doGetRequest: (ERROR) Get failed"); 527 errorCount++; 528 } 529 } 530 531 return errorCount; 532 } 533 534 protected int doSetRequest(MBeanServerConnection mbsc, 535 ObjectName on, 536 boolean expectedException) { 537 int errorCount = 0; 538 539 try { 540 Utils.debug(Utils.DEBUG_STANDARD, 541 "ClientSide::doSetRequest: Set attributes of the MBean") ; 542 543 Attribute attribute = new Attribute("Attribute", "My value") ; 544 mbsc.setAttribute(on, attribute) ; 545 546 if (expectedException) { 547 System.out.println("ClientSide::doSetRequest: " + 548 "(ERROR) Set did not fail with expected SecurityException"); 549 errorCount++; 550 } else { 551 System.out.println("ClientSide::doSetRequest: (OK) Set succeed") ; 552 } 553 } catch(Exception e) { 554 Utils.printThrowable(e, true) ; 555 if (expectedException) { 556 if (e instanceof java.lang.SecurityException) { 557 System.out.println("ClientSide::doSetRequest: " + 558 "(OK) Set failed with expected SecurityException") ; 559 } else { 560 System.out.println("ClientSide::doSetRequest: " + 561 "(ERROR) Set failed with " + 562 e.getClass() + " instead of expected SecurityException"); 563 errorCount++; 564 } 565 } else { 566 System.out.println("ClientSide::doSetRequest: (ERROR) Set failed"); 567 errorCount++; 568 } 569 } 570 return errorCount; 571 } 572 573 protected int doInvokeRequest(MBeanServerConnection mbsc, 574 ObjectName on, 575 boolean expectedException) { 576 int errorCount = 0; 577 578 try { 579 Utils.debug(Utils.DEBUG_STANDARD, 580 "ClientSide::doInvokeRequest: Invoke operations on the MBean") ; 581 582 mbsc.invoke(on, "operation", null, null) ; 583 584 if (expectedException) { 585 System.out.println("ClientSide::doInvokeRequest: " + 586 "(ERROR) Invoke did not fail with expected SecurityException"); 587 errorCount++; 588 } else { 589 System.out.println("ClientSide::doInvokeRequest: (OK) Invoke succeed") ; 590 } 591 } catch(Exception e) { 592 Utils.printThrowable(e, true) ; 593 if (expectedException) { 594 if (e instanceof java.lang.SecurityException) { 595 System.out.println("ClientSide::doInvokeRequest: " + 596 "(OK) Invoke failed with expected SecurityException") ; 597 } else { 598 System.out.println("ClientSide::doInvokeRequest: " + 599 " (ERROR) Invoke failed with " + 600 e.getClass() + " instead of expected SecurityException"); 601 errorCount++; 602 } 603 } else { 604 System.out.println("ClientSide::doInvokeRequest: " + 605 "(ERROR) Invoke failed"); 606 errorCount++; 607 } 608 } 609 return errorCount; 610 } 611 612 } 613} 614