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