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 }