package org.infinispan.security; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.security.PrivilegedAction; import java.security.PrivilegedExceptionAction; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import javax.security.auth.Subject; import org.infinispan.Cache; import org.infinispan.configuration.cache.AuthorizationConfigurationBuilder; import org.infinispan.configuration.cache.ConfigurationBuilder; import org.infinispan.configuration.global.GlobalAuthorizationConfigurationBuilder; import org.infinispan.configuration.global.GlobalConfigurationBuilder; import org.infinispan.manager.EmbeddedCacheManager; import org.infinispan.security.impl.IdentityRoleMapper; import org.infinispan.test.SingleCacheManagerTest; import org.infinispan.test.TestingUtil; import org.infinispan.test.fwk.TestCacheManagerFactory; import org.infinispan.transaction.LockingMode; import org.infinispan.util.logging.Log; import org.infinispan.util.logging.LogFactory; import org.testng.annotations.Test; @Test(groups = {"functional", "smoke"}, testName = "security.CacheAuthorizationTest") public class CacheAuthorizationTest extends SingleCacheManagerTest { static final Log log = LogFactory.getLog(CacheAuthorizationTest.class); static final Subject ADMIN; static final Map<AuthorizationPermission, Subject> SUBJECTS; static { // Initialize one subject per permission SUBJECTS = new HashMap<>(AuthorizationPermission.values().length); for (AuthorizationPermission perm : AuthorizationPermission.values()) { SUBJECTS.put(perm, TestingUtil.makeSubject(perm.toString() + "_user", perm.toString())); } ADMIN = SUBJECTS.get(AuthorizationPermission.ALL); } @Override protected EmbeddedCacheManager createCacheManager() throws Exception { final GlobalConfigurationBuilder global = new GlobalConfigurationBuilder(); GlobalAuthorizationConfigurationBuilder globalRoles = global.security().authorization().enable() .principalRoleMapper(new IdentityRoleMapper()); final ConfigurationBuilder config = TestCacheManagerFactory.getDefaultCacheConfiguration(true); config.transaction().lockingMode(LockingMode.PESSIMISTIC); config.invocationBatching().enable(); AuthorizationConfigurationBuilder authConfig = config.security().authorization().enable(); for (AuthorizationPermission perm : AuthorizationPermission.values()) { globalRoles.role(perm.toString()).permission(perm); authConfig.role(perm.toString()); } return Security.doAs(ADMIN, new PrivilegedAction<EmbeddedCacheManager>() { @Override public EmbeddedCacheManager run() { return TestCacheManagerFactory.createCacheManager(global, config); } }); } @Override protected void setup() throws Exception { cacheManager = createCacheManager(); } @Override protected void teardown() { Security.doAs(ADMIN, new PrivilegedAction<Void>() { @Override public Void run() { CacheAuthorizationTest.super.teardown(); return null; } }); } @Override protected void clearContent() { Security.doAs(ADMIN, new PrivilegedAction<Void>() { @Override public Void run() { cacheManager.getCache().clear(); return null; } }); } public void testAllCombinations() throws Exception { Method[] allMethods = SecureCache.class.getMethods(); Set<String> methodNames = new HashSet<String>(); collectmethods: for (Method m : allMethods) { StringBuilder s = new StringBuilder("test"); String name = m.getName(); s.append(name.substring(0, 1).toUpperCase()); s.append(name.substring(1)); for (Class<?> p : m.getParameterTypes()) { Package pkg = p.getPackage(); if (pkg != null && pkg.getName().startsWith("java.util.function")) continue collectmethods; // Skip methods which use interfaces introduced in JDK8 s.append("_"); s.append(p.getSimpleName().replaceAll("\\[\\]", "Array")); } methodNames.add(s.toString()); } final SecureCacheTestDriver driver = new SecureCacheTestDriver(); final SecureCache<String, String> cache = (SecureCache<String, String>) Security.doAs( ADMIN, new PrivilegedAction<Cache<String, String>>() { @Override public Cache<String, String> run() { return cacheManager.getCache(); } }); for (final String methodName : methodNames) { Class<? extends SecureCacheTestDriver> driverClass = driver.getClass(); try { final Method method = driverClass.getMethod(methodName, SecureCache.class); TestCachePermission annotation = method.getAnnotation(TestCachePermission.class); if (annotation == null) { throw new Exception(String.format("Method %s on class %s is missing the TestCachePermission annotation", methodName, driver.getClass().getName())); } final AuthorizationPermission expectedPerm = annotation.value(); for (final AuthorizationPermission perm : AuthorizationPermission.values()) { if (perm == AuthorizationPermission.NONE) continue;// Skip if (annotation.needsSecurityManager() && System.getSecurityManager() == null) { log.debugf("Method %s (skipped, needs SecurityManager)", methodName); break; } log.debugf("Method %s > %s", methodName, perm.toString()); if (expectedPerm == AuthorizationPermission.NONE) { try { method.invoke(driver, cache); } catch (SecurityException e) { throw new Exception(String.format("Unexpected SecurityException while invoking %s with permission %s", methodName, perm.toString() ), e); } } else { Security.doAs(SUBJECTS.get(perm), new PrivilegedExceptionAction<Void>() { @Override public Void run() throws Exception { try { method.invoke(driver, cache); if (!perm.implies(expectedPerm)) { throw new Exception(String.format("Expected SecurityException while invoking %s with permission %s", methodName, perm.toString())); } return null; } catch (InvocationTargetException e) { Throwable cause = e.getCause(); if (cause instanceof SecurityException) { if (perm.implies(expectedPerm)) { throw new Exception(String.format("Unexpected SecurityException while invoking %s with permission %s", methodName, perm.toString() ), e); } else { // We were expecting a security exception return null; } } else throw new Exception("Unexpected non-SecurityException", e); } } }); } } } catch (NoSuchMethodException e) { throw new Exception( String.format( "Class %s needs to declare a method with the following signature: public void %s(SecureCache<String, String> cache) {}\n", driver.getClass().getName(), methodName), e); } } } }