1 /* 2 * Copyright (c) 2016, 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 import member.MemberFactory; 24 25 import java.lang.reflect.AccessibleObject; 26 import java.lang.reflect.Constructor; 27 import java.lang.reflect.Field; 28 import java.lang.reflect.InvocationTargetException; 29 import java.lang.reflect.Method; 30 import java.lang.reflect.Modifier; 31 import java.util.EnumSet; 32 import java.util.Iterator; 33 import java.util.Map; 34 import java.util.Optional; 35 import java.util.stream.Stream; 36 37 import static java.util.stream.Collectors.groupingBy; 38 import static java.util.stream.Collectors.joining; 39 import static java.util.stream.Collectors.mapping; 40 import static java.util.stream.Collectors.toCollection; 41 import static member.MemberFactory.*; 42 import static member.MemberFactory.Group.*; 43 44 /** 45 * @test 46 * @summary An exhaustive test of reflective access controls 47 * @bug 6378384 48 * @build a.PublicSuper a.Package b.PublicSub b.Package member.MemberFactory 49 * @run main AccessControlTest 50 */ 51 public class AccessControlTest { 52 53 public static void main(String[] args) throws Exception { 54 boolean ok = true; 55 56 // use this for generating an exhaustive set of test cases on stdout: 57 58 // String[] classNames = new String[]{ 59 // "a.PublicSuper", "a.Package", "b.PublicSub", "b.Package" 60 // }; 61 // for (String current : classNames) { 62 // for (String member : classNames) { 63 // for (String target : classNames) { 64 // if (Class.forName(member).isAssignableFrom(Class.forName(target))) { 65 // ok &= new Test() 66 // .current(current).member(member).target(target) 67 // .allowed(MemberFactory.values()) 68 // .perform(true); 69 // } 70 // } 71 // } 72 // } 73 74 ok &= new Test() 75 .current("a.PublicSuper").member("a.PublicSuper").target("a.PublicSuper") 76 .allowedAll() 77 .perform(); 78 ok &= new Test() 79 .current("a.PublicSuper").member("a.PublicSuper").target("b.PublicSub") 80 .allowedAll() 81 .perform(); 82 ok &= new Test() 83 .current("a.PublicSuper").member("a.Package").target("a.Package") 84 .allowed(PACKAGE_INSTANCE_F_M, PROTECTED_INSTANCE_F_M, PUBLIC_INSTANCE_F_M, 85 PACKAGE_STATIC_F_M, PROTECTED_STATIC_F_M, PUBLIC_STATIC_F_M, 86 PACKAGE_C, PROTECTED_C, PUBLIC_C) 87 .denied (PRIVATE_INSTANCE_F_M, PRIVATE_STATIC_F_M, PRIVATE_C) 88 .perform(); 89 ok &= new Test() 90 .current("a.PublicSuper").member("b.PublicSub").target("b.PublicSub") 91 .allowed(PUBLIC_INSTANCE_F_M, PUBLIC_STATIC_F_M, PUBLIC_C) 92 .denied (PRIVATE_INSTANCE_F_M, PACKAGE_INSTANCE_F_M, PROTECTED_INSTANCE_F_M, 93 PRIVATE_STATIC_F_M, PACKAGE_STATIC_F_M, PROTECTED_STATIC_F_M, 94 PRIVATE_C, PACKAGE_C, PROTECTED_C) 95 .perform(); 96 ok &= new Test() 97 .current("a.PublicSuper").member("b.Package").target("b.Package") 98 .deniedAll() 99 .perform(); 100 ok &= new Test() 101 .current("a.Package").member("a.PublicSuper").target("a.PublicSuper") 102 .allowed(PACKAGE_INSTANCE_F_M, PROTECTED_INSTANCE_F_M, PUBLIC_INSTANCE_F_M, 103 PACKAGE_STATIC_F_M, PROTECTED_STATIC_F_M, PUBLIC_STATIC_F_M, 104 PACKAGE_C, PROTECTED_C, PUBLIC_C) 105 .denied (PRIVATE_INSTANCE_F_M, PRIVATE_STATIC_F_M, PRIVATE_C) 106 .perform(); 107 ok &= new Test() 108 .current("a.Package").member("a.PublicSuper").target("b.PublicSub") 109 .allowed(PACKAGE_INSTANCE_F_M, PROTECTED_INSTANCE_F_M, PUBLIC_INSTANCE_F_M, 110 PACKAGE_STATIC_F_M, PROTECTED_STATIC_F_M, PUBLIC_STATIC_F_M, 111 PACKAGE_C, PROTECTED_C, PUBLIC_C) 112 .denied (PRIVATE_INSTANCE_F_M, PRIVATE_STATIC_F_M, PRIVATE_C) 113 .perform(); 114 ok &= new Test() 115 .current("a.Package").member("a.Package").target("a.Package") 116 .allowedAll() 117 .perform(); 118 ok &= new Test() 119 .current("a.Package").member("b.PublicSub").target("b.PublicSub") 120 .allowed(PUBLIC_INSTANCE_F_M, PUBLIC_STATIC_F_M, PUBLIC_C) 121 .denied (PRIVATE_INSTANCE_F_M, PACKAGE_INSTANCE_F_M, PROTECTED_INSTANCE_F_M, 122 PRIVATE_STATIC_F_M, PACKAGE_STATIC_F_M, PROTECTED_STATIC_F_M, 123 PRIVATE_C, PACKAGE_C, PROTECTED_C) 124 .perform(); 125 ok &= new Test() 126 .current("a.Package").member("b.Package").target("b.Package") 127 .deniedAll() 128 .perform(); 129 ok &= new Test() 130 .current("b.PublicSub").member("a.PublicSuper").target("a.PublicSuper") 131 .allowed(PUBLIC_INSTANCE_F_M, PROTECTED_STATIC_F_M, PUBLIC_STATIC_F_M, 132 PUBLIC_C) 133 .denied (PRIVATE_INSTANCE_F_M, PACKAGE_INSTANCE_F_M, PROTECTED_INSTANCE_F_M, 134 PRIVATE_STATIC_F_M, PACKAGE_STATIC_F_M, PRIVATE_C, 135 PACKAGE_C, PROTECTED_C) 136 .perform(); 137 ok &= new Test() 138 .current("b.PublicSub").member("a.PublicSuper").target("b.PublicSub") 139 .allowed(PROTECTED_INSTANCE_F_M, PUBLIC_INSTANCE_F_M, PROTECTED_STATIC_F_M, 140 PUBLIC_STATIC_F_M, PUBLIC_C) 141 .denied (PRIVATE_INSTANCE_F_M, PACKAGE_INSTANCE_F_M, PRIVATE_STATIC_F_M, 142 PACKAGE_STATIC_F_M, PRIVATE_C, PACKAGE_C, 143 PROTECTED_C) 144 .perform(); 145 ok &= new Test() 146 .current("b.PublicSub").member("a.Package").target("a.Package") 147 .deniedAll() 148 .perform(); 149 ok &= new Test() 150 .current("b.PublicSub").member("b.PublicSub").target("b.PublicSub") 151 .allowedAll() 152 .perform(); 153 ok &= new Test() 154 .current("b.PublicSub").member("b.Package").target("b.Package") 155 .allowed(PACKAGE_INSTANCE_F_M, PROTECTED_INSTANCE_F_M, PUBLIC_INSTANCE_F_M, 156 PACKAGE_STATIC_F_M, PROTECTED_STATIC_F_M, PUBLIC_STATIC_F_M, 157 PACKAGE_C, PROTECTED_C, PUBLIC_C) 158 .denied (PRIVATE_INSTANCE_F_M, PRIVATE_STATIC_F_M, PRIVATE_C) 159 .perform(); 160 ok &= new Test() 161 .current("b.Package").member("a.PublicSuper").target("a.PublicSuper") 162 .allowed(PUBLIC_INSTANCE_F_M, PUBLIC_STATIC_F_M, PUBLIC_C) 163 .denied (PRIVATE_INSTANCE_F_M, PACKAGE_INSTANCE_F_M, PROTECTED_INSTANCE_F_M, 164 PRIVATE_STATIC_F_M, PACKAGE_STATIC_F_M, PROTECTED_STATIC_F_M, 165 PRIVATE_C, PACKAGE_C, PROTECTED_C) 166 .perform(); 167 ok &= new Test() 168 .current("b.Package").member("a.PublicSuper").target("b.PublicSub") 169 .allowed(PUBLIC_INSTANCE_F_M, PUBLIC_STATIC_F_M, PUBLIC_C) 170 .denied (PRIVATE_INSTANCE_F_M, PACKAGE_INSTANCE_F_M, PROTECTED_INSTANCE_F_M, 171 PRIVATE_STATIC_F_M, PACKAGE_STATIC_F_M, PROTECTED_STATIC_F_M, 172 PRIVATE_C, PACKAGE_C, PROTECTED_C) 173 .perform(); 174 ok &= new Test() 175 .current("b.Package").member("a.Package").target("a.Package") 176 .deniedAll() 177 .perform(); 178 ok &= new Test() 179 .current("b.Package").member("b.PublicSub").target("b.PublicSub") 180 .allowed(PACKAGE_INSTANCE_F_M, PROTECTED_INSTANCE_F_M, PUBLIC_INSTANCE_F_M, 181 PACKAGE_STATIC_F_M, PROTECTED_STATIC_F_M, PUBLIC_STATIC_F_M, 182 PACKAGE_C, PROTECTED_C, PUBLIC_C) 183 .denied (PRIVATE_INSTANCE_F_M, PRIVATE_STATIC_F_M, PRIVATE_C) 184 .perform(); 185 ok &= new Test() 186 .current("b.Package").member("b.Package").target("b.Package") 187 .allowedAll() 188 .perform(); 189 190 if (ok) { 191 System.err.println("\nAll cases passed."); 192 } else { 193 System.err.println(); 194 throw new RuntimeException("Some cases failed - see log."); 195 } 196 } 197 198 static class Test { 199 200 Class<?> currentClass, memberClass, targetClass; 201 EnumSet<MemberFactory> expectAllowedMembers = EnumSet.noneOf(MemberFactory.class); 202 EnumSet<MemberFactory> expectDeniedMembers = EnumSet.noneOf(MemberFactory.class); 203 204 Test current(String current) { 205 try { 206 currentClass = Class.forName(current); 207 } catch (ClassNotFoundException e) { 208 throw new RuntimeException(e); 209 } 210 return this; 211 } 212 213 Test member(String member) { 214 try { 215 memberClass = Class.forName(member); 216 } catch (ClassNotFoundException e) { 217 throw new RuntimeException(e); 218 } 219 return this; 220 } 221 222 Test target(String target) { 223 try { 224 targetClass = Class.forName(target); 225 } catch (ClassNotFoundException e) { 226 throw new RuntimeException(e); 227 } 228 return this; 229 } 230 231 Test allowed(MemberFactory... allowed) { 232 expectAllowedMembers = MemberFactory.asSet(allowed); 233 return this; 234 } 235 236 Test allowed(MemberFactory.Group... allowedGroups) { 237 expectAllowedMembers = MemberFactory.groupsToMembers( 238 MemberFactory.Group.asSet(allowedGroups)); 239 return this; 240 } 241 242 Test allowedAll() { 243 expectAllowedMembers = EnumSet.allOf(MemberFactory.class); 244 return this; 245 } 246 247 Test denied(MemberFactory... denied) { 248 expectDeniedMembers = MemberFactory.asSet(denied); 249 return this; 250 } 251 252 Test denied(MemberFactory.Group... deniedGroups) { 253 expectDeniedMembers = MemberFactory.groupsToMembers( 254 MemberFactory.Group.asSet(deniedGroups)); 255 return this; 256 } 257 258 Test deniedAll() { 259 expectDeniedMembers = EnumSet.allOf(MemberFactory.class); 260 return this; 261 } 262 263 boolean perform() { 264 return perform(false); 265 } 266 267 boolean perform(boolean printCases) { 268 269 // some validation 1st 270 EnumSet<MemberFactory> intersection = EnumSet.copyOf(expectAllowedMembers); 271 intersection.retainAll(expectDeniedMembers); 272 if (!intersection.isEmpty()) { 273 throw new IllegalArgumentException( 274 "Expected allowed and denied MemberFactories have non-empty intersection: " + 275 intersection); 276 } 277 278 EnumSet<MemberFactory> missing = EnumSet.allOf(MemberFactory.class); 279 missing.removeAll(expectAllowedMembers); 280 missing.removeAll(expectDeniedMembers); 281 if (!missing.isEmpty()) { 282 throw new IllegalArgumentException( 283 "Union of expected allowed and denied MemberFactories is missing elements: " + 284 missing); 285 } 286 287 // retrieve method that will perform reflective access 288 Method checkAccessMethod; 289 try { 290 checkAccessMethod = currentClass.getDeclaredMethod( 291 "checkAccess", AccessibleObject.class, Object.class); 292 // in case of inaccessible currentClass 293 checkAccessMethod.setAccessible(true); 294 } catch (NoSuchMethodException e) { 295 throw new RuntimeException(e); 296 } 297 298 // construct a target object (for instance field/method) 299 Object target; 300 if (targetClass == null) { 301 target = null; 302 } else { 303 Constructor<?> targetConstructor = 304 (Constructor<?>) PUBLIC_CONSTRUCTOR.apply(targetClass); 305 // in case of inaccessible targetClass 306 targetConstructor.setAccessible(true); 307 try { 308 target = targetConstructor.newInstance( 309 new Object[targetConstructor.getParameterCount()]); 310 } catch (ReflectiveOperationException e) { 311 throw new RuntimeException(e); 312 } 313 } 314 315 System.err.println(); 316 317 Map<Boolean, EnumSet<MemberFactory>> actualMembers = Stream.concat( 318 319 expectAllowedMembers.stream().map(member -> new Trial(member, true)), 320 expectDeniedMembers.stream().map(member -> new Trial(member, false)) 321 322 ).map(trial -> { 323 324 // obtain AccessibleObject to be used to perform reflective access 325 AccessibleObject accessibleObject = trial.member.apply(memberClass); 326 327 // only need target 'obj' for instance fields and methods 328 Object obj = 329 (accessibleObject instanceof Field && 330 !Modifier.isStatic(((Field) accessibleObject).getModifiers()) 331 || 332 accessibleObject instanceof Method && 333 !Modifier.isStatic(((Method) accessibleObject).getModifiers()) 334 ) 335 ? target : null; 336 337 // invoke checkAccess method and let it perform the reflective access 338 try { 339 checkAccessMethod.invoke(null, accessibleObject, obj); 340 trial.actualAllowed = true; 341 } catch (IllegalAccessException e) { 342 // should not happen as checkAccessMethod.isAccessible() 343 throw new RuntimeException(e); 344 } catch (InvocationTargetException e) { 345 if (e.getTargetException() instanceof IllegalAccessException) { 346 trial.actualAllowed = false; 347 } else { 348 // any other Exception is a fault in test or infrastructure - fail fast 349 throw new RuntimeException(e.getTargetException()); 350 } 351 } 352 353 System.err.printf( 354 "%-18s accessing %18s's %-25s %-36s - expected %s, actual %s: %s\n", 355 currentClass.getName(), memberClass.getName(), trial.member.name(), 356 (obj == null ? "" : "with instance of " + targetClass.getName()), 357 (trial.expectAllowed ? "allowed" : "denied "), 358 (trial.actualAllowed ? "allowed" : "denied "), 359 (trial.expectAllowed == trial.actualAllowed ? "OK" : "FAILURE") 360 ); 361 362 return trial; 363 364 }).collect( 365 groupingBy( 366 Trial::isActualAllowed, 367 mapping( 368 Trial::getMember, 369 toCollection(() -> EnumSet.noneOf(MemberFactory.class)))) 370 ); 371 372 EnumSet<MemberFactory> actualAllowedMembers = 373 Optional.ofNullable(actualMembers.get(true)) 374 .orElse(EnumSet.noneOf(MemberFactory.class)); 375 EnumSet<MemberFactory> actualDeniedMembers = 376 Optional.ofNullable(actualMembers.get(false)) 377 .orElse(EnumSet.noneOf(MemberFactory.class)); 378 379 if (printCases) { 380 System.out.printf( 381 " ok &= new Test()\n" + 382 " .current(\"%s\").member(\"%s\").target(\"%s\")\n", 383 currentClass.getName(), memberClass.getName(), targetClass.getName() 384 ); 385 386 if (actualAllowedMembers.equals(EnumSet.allOf(MemberFactory.class))) { 387 System.out.print(" .allowedAll()\n"); 388 } else if (!actualAllowedMembers.isEmpty()) { 389 EnumSet<? extends Enum> actualAllowed = 390 MemberFactory.membersToGroupsOrNull(actualAllowedMembers); 391 if (actualAllowed == null) 392 actualAllowed = actualAllowedMembers; 393 System.out.print( 394 chunkBy(3, actualAllowed.stream().map(Enum::name)) 395 .map(chunk -> chunk.collect(joining(", "))) 396 .collect(joining(",\n" + 397 " ", 398 " .allowed(", 399 ")\n")) 400 ); 401 } 402 403 if (actualDeniedMembers.equals(EnumSet.allOf(MemberFactory.class))) { 404 System.out.print(" .deniedAll()\n"); 405 } else if (!actualDeniedMembers.isEmpty()) { 406 EnumSet<? extends Enum> actualDenied = 407 MemberFactory.membersToGroupsOrNull(actualDeniedMembers); 408 if (actualDenied == null) 409 actualDenied = actualAllowedMembers; 410 System.out.print( 411 chunkBy(3, actualDenied.stream().map(Enum::name)) 412 .map(chunk -> chunk.collect(joining(", "))) 413 .collect(joining(",\n" + 414 " ", 415 " .denied (", 416 ")\n")) 417 ); 418 } 419 420 System.out.print( 421 " .perform();\n" 422 ); 423 } 424 425 return expectAllowedMembers.equals(actualAllowedMembers) && 426 expectDeniedMembers.equals(actualDeniedMembers); 427 } 428 } 429 430 static <T> Stream<Stream<T>> chunkBy(int chunkSize, Stream<T> stream) { 431 Iterator<T> elements = stream.iterator(); 432 Stream.Builder<Stream<T>> b1 = Stream.builder(); 433 while (elements.hasNext()) { 434 Stream.Builder<T> b2 = Stream.builder(); 435 for (int i = 0; i < chunkSize && elements.hasNext(); i++) { 436 b2.accept(elements.next()); 437 } 438 b1.accept(b2.build()); 439 } 440 return b1.build(); 441 } 442 443 static class Trial { 444 final MemberFactory member; 445 final boolean expectAllowed; 446 boolean actualAllowed; 447 448 Trial(MemberFactory member, boolean expectAllowed) { 449 this.member = member; 450 this.expectAllowed = expectAllowed; 451 } 452 453 MemberFactory getMember() { 454 return member; 455 } 456 457 boolean isActualAllowed() { 458 return actualAllowed; 459 } 460 } 461 }