diff --git a/core/src/main/java/org/infinispan/configuration/global/GlobalAuthorizationConfiguration.java b/core/src/main/java/org/infinispan/configuration/global/GlobalAuthorizationConfiguration.java index 0db76eefa6bc..0d304ed1485a 100644 --- a/core/src/main/java/org/infinispan/configuration/global/GlobalAuthorizationConfiguration.java +++ b/core/src/main/java/org/infinispan/configuration/global/GlobalAuthorizationConfiguration.java @@ -63,17 +63,23 @@ static AttributeSet attributeDefinitionSet() { AuthorizationPermission.ALL_READ, AuthorizationPermission.ALL_WRITE, AuthorizationPermission.LISTEN, - AuthorizationPermission.EXEC + AuthorizationPermission.EXEC, + AuthorizationPermission.MONITOR )); roles.put("deployer", new CacheRoleImpl("deployer", true, AuthorizationPermission.ALL_READ, AuthorizationPermission.ALL_WRITE, AuthorizationPermission.LISTEN, AuthorizationPermission.EXEC, - AuthorizationPermission.CREATE + AuthorizationPermission.CREATE, + AuthorizationPermission.MONITOR )); roles.put("observer", new CacheRoleImpl("observer", true, - AuthorizationPermission.ALL_READ + AuthorizationPermission.ALL_READ, + AuthorizationPermission.MONITOR + )); + roles.put("monitor", new CacheRoleImpl("monitor", true, + AuthorizationPermission.MONITOR )); // Deprecated roles. Will be removed in Infinispan 16.0 roles.put("___schema_manager", new CacheRoleImpl("___schema_manager", false, diff --git a/core/src/main/java/org/infinispan/manager/DefaultCacheManager.java b/core/src/main/java/org/infinispan/manager/DefaultCacheManager.java index 43fde21a8ec3..4edabbcb1815 100644 --- a/core/src/main/java/org/infinispan/manager/DefaultCacheManager.java +++ b/core/src/main/java/org/infinispan/manager/DefaultCacheManager.java @@ -1119,6 +1119,7 @@ public Properties getGlobalConfigurationAsProperties() { @Override public CacheContainerStats getStats() { + authorizer.checkPermission(getSubject(), AuthorizationPermission.MONITOR); return stats; } diff --git a/core/src/main/java/org/infinispan/security/AuthorizationPermission.java b/core/src/main/java/org/infinispan/security/AuthorizationPermission.java index 19e96c9ca615..6d8d599b9b48 100644 --- a/core/src/main/java/org/infinispan/security/AuthorizationPermission.java +++ b/core/src/main/java/org/infinispan/security/AuthorizationPermission.java @@ -46,6 +46,10 @@ public enum AuthorizationPermission { * Allows creation of resources (caches, counters, schemas, tasks) */ CREATE(1 << 8), + /** + * Allows retrieval of stats + */ + MONITOR(1 << 9), /** * Aggregate permission which implies all of the others */ diff --git a/core/src/main/java/org/infinispan/security/impl/SecureCacheImpl.java b/core/src/main/java/org/infinispan/security/impl/SecureCacheImpl.java index 373791a71e7b..ce22a41835db 100644 --- a/core/src/main/java/org/infinispan/security/impl/SecureCacheImpl.java +++ b/core/src/main/java/org/infinispan/security/impl/SecureCacheImpl.java @@ -883,7 +883,7 @@ public CompletableFuture replaceAsync(K key, V oldValue, V newValue) { @Override public Stats getStats() { - authzManager.checkPermission(subject, AuthorizationPermission.ADMIN); + authzManager.checkPermission(subject, AuthorizationPermission.MONITOR); return delegate.getStats(); } diff --git a/core/src/test/java/org/infinispan/security/CacheManagerAuthorizationTest.java b/core/src/test/java/org/infinispan/security/CacheManagerAuthorizationTest.java index f0e66f49c23a..447122335e0e 100644 --- a/core/src/test/java/org/infinispan/security/CacheManagerAuthorizationTest.java +++ b/core/src/test/java/org/infinispan/security/CacheManagerAuthorizationTest.java @@ -2,10 +2,9 @@ import static org.testng.Assert.assertTrue; +import java.lang.reflect.Field; import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; -import java.util.Arrays; -import java.util.List; import javax.security.auth.Subject; @@ -15,26 +14,33 @@ @Test(groups = {"functional", "smoke"}, testName = "security.CacheManagerAuthorizationTest") public class CacheManagerAuthorizationTest extends BaseAuthorizationTest { - public void testAdminCombinations() throws Exception { - List calls = Arrays.asList( - () -> cacheManager.getGlobalComponentRegistry(), - () -> cacheManager.getCacheManagerConfiguration()); + @TestCachePermission(AuthorizationPermission.ADMIN) + Runnable GET_GLOBAL_COMPONENT_REGISTRY = () -> cacheManager.getGlobalComponentRegistry(); - for (final AuthorizationPermission perm : AuthorizationPermission.values()) { - for (Runnable fn : calls) { + @TestCachePermission(AuthorizationPermission.ADMIN) + Runnable GET_CACHE_MANAGER_CONFIGURATION = () -> cacheManager.getCacheManagerConfiguration(); + @TestCachePermission(AuthorizationPermission.MONITOR) + Runnable GET_STATS = () -> cacheManager.getStats(); + + public void testCombinations() throws Exception { + Field[] fields = this.getClass().getDeclaredFields(); + for (Field f : fields) { + if (f.getType().equals(Runnable.class)) { + final Runnable fn = (Runnable) f.get(this); PrivilegedExceptionAction action = () -> { fn.run(); return true; }; - - // only admin must work - Subject subject = SUBJECTS.get(perm); - if (perm.implies(AuthorizationPermission.ADMIN)) { - assertTrue(Security.doAs(subject, action)); - } else { - Exceptions.expectException(PrivilegedActionException.class, SecurityException.class, - () -> Security.doAs(subject, action)); + TestCachePermission p = f.getAnnotation(TestCachePermission.class); + for (final AuthorizationPermission perm : AuthorizationPermission.values()) { + Subject subject = SUBJECTS.get(perm); + if (perm.implies(p.value())) { + assertTrue(Security.doAs(subject, action)); + } else { + Exceptions.expectException(PrivilegedActionException.class, SecurityException.class, + () -> Security.doAs(subject, action)); + } } } } diff --git a/core/src/test/java/org/infinispan/security/SecureCacheTestDriver.java b/core/src/test/java/org/infinispan/security/SecureCacheTestDriver.java index 948710df11a0..298dd96898a3 100644 --- a/core/src/test/java/org/infinispan/security/SecureCacheTestDriver.java +++ b/core/src/test/java/org/infinispan/security/SecureCacheTestDriver.java @@ -364,7 +364,7 @@ public void testGetBatchContainer(SecureCache cache) { cache.getBatchContainer(); } - @TestCachePermission(AuthorizationPermission.ADMIN) + @TestCachePermission(AuthorizationPermission.MONITOR) public void testGetStats(SecureCache cache) { cache.getStats(); } diff --git a/core/src/test/java/org/infinispan/security/TestCachePermission.java b/core/src/test/java/org/infinispan/security/TestCachePermission.java index 88f59258ef55..bb4f30ccee00 100644 --- a/core/src/test/java/org/infinispan/security/TestCachePermission.java +++ b/core/src/test/java/org/infinispan/security/TestCachePermission.java @@ -5,7 +5,7 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -@Target(ElementType.METHOD) +@Target({ElementType.METHOD, ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) public @interface TestCachePermission { AuthorizationPermission value(); diff --git a/documentation/src/main/asciidoc/topics/ref_authz_default_roles.adoc b/documentation/src/main/asciidoc/topics/ref_authz_default_roles.adoc index 7731da199db4..f0b0ac3e1675 100644 --- a/documentation/src/main/asciidoc/topics/ref_authz_default_roles.adoc +++ b/documentation/src/main/asciidoc/topics/ref_authz_default_roles.adoc @@ -27,6 +27,9 @@ Run [command]`help user` for command usage examples. |`observer` |Read-only access to the system. +|`monitor` +|Access to statistics + |=== .Reference diff --git a/documentation/src/main/asciidoc/topics/ref_authz_permissions.adoc b/documentation/src/main/asciidoc/topics/ref_authz_permissions.adoc index b39e282e1eae..208d3c4ef9e4 100644 --- a/documentation/src/main/asciidoc/topics/ref_authz_permissions.adoc +++ b/documentation/src/main/asciidoc/topics/ref_authz_permissions.adoc @@ -27,6 +27,10 @@ Permissions let you restrict user access to both Cache Managers and caches. |`createCache`, `removeCache` |Create and remove container resources such as caches, counters, schemas, and scripts. +| MONITOR +|`getStats` +|Allows access to statistics + |ALL |- |Includes all Cache Manager permissions. @@ -68,10 +72,14 @@ Permissions let you restrict user access to both Cache Managers and caches. |`start`, `stop` |Starts and stops a cache. -| ADMIN -| `getVersion`, `addInterceptor*`, `removeInterceptor`, `getInterceptorChain`, `getEvictionManager`, `getComponentRegistry`, `getDistributionManager`, `getAuthorizationManager`, `evict`, `getRpcManager`, `getCacheConfiguration`, `getCacheManager`, `getInvocationContextContainer`, `setAvailability`, `getDataContainer`, `getStats`, `getXAResource` +|ADMIN +|`getVersion`, `addInterceptor*`, `removeInterceptor`, `getInterceptorChain`, `getEvictionManager`, `getComponentRegistry`, `getDistributionManager`, `getAuthorizationManager`, `evict`, `getRpcManager`, `getCacheConfiguration`, `getCacheManager`, `getInvocationContextContainer`, `setAvailability`, `getDataContainer`, `getXAResource` |Allows access to underlying components and internal structures. +|MONITOR +|`getStats` +|Allows access to statistics + |ALL |- |Includes all cache permissions. diff --git a/server/testdriver/core/src/main/java/org/infinispan/server/test/api/TestUser.java b/server/testdriver/core/src/main/java/org/infinispan/server/test/api/TestUser.java index 416d8ae4d6fb..c57f122ddf8e 100644 --- a/server/testdriver/core/src/main/java/org/infinispan/server/test/api/TestUser.java +++ b/server/testdriver/core/src/main/java/org/infinispan/server/test/api/TestUser.java @@ -18,6 +18,7 @@ public enum TestUser { OBSERVER("observer", "password", Collections.singletonList("observer")), APPLICATION("application", "somePassword", Collections.singletonList("application")), DEPLOYER("deployer", "lessStrongPassword", Collections.singletonList("deployer")), + MONITOR("monitor", "weakPassword", Collections.singletonList("monitor")), READER("reader", "readerPassword", Collections.singletonList("reader")), WRITER("writer", "writerPassword", Collections.singletonList("writer")); diff --git a/server/tests/src/test/java/org/infinispan/server/security/authorization/AbstractAuthorization.java b/server/tests/src/test/java/org/infinispan/server/security/authorization/AbstractAuthorization.java index a4208c99dfc3..724c21362d54 100644 --- a/server/tests/src/test/java/org/infinispan/server/security/authorization/AbstractAuthorization.java +++ b/server/tests/src/test/java/org/infinispan/server/security/authorization/AbstractAuthorization.java @@ -118,7 +118,7 @@ public void testRestAdminCanDoEverything() { @Test public void testHotRodNonAdminsMustNotCreateCache() { - for (TestUser user : EnumSet.of(TestUser.APPLICATION, TestUser.OBSERVER)) { + for (TestUser user : EnumSet.of(TestUser.APPLICATION, TestUser.OBSERVER, TestUser.MONITOR)) { Exceptions.expectException(HotRodClientException.class, "(?s).*ISPN000287.*", () -> getServerTest().hotrod().withClientConfiguration(hotRodBuilders.get(user)).withCacheMode(CacheMode.DIST_SYNC).create() ); @@ -127,7 +127,7 @@ public void testHotRodNonAdminsMustNotCreateCache() { @Test public void testRestNonAdminsMustNotCreateCache() { - for (TestUser user : EnumSet.of(TestUser.APPLICATION, TestUser.OBSERVER)) { + for (TestUser user : EnumSet.of(TestUser.APPLICATION, TestUser.OBSERVER, TestUser.MONITOR)) { Exceptions.expectException(SecurityException.class, "(?s).*403.*", () -> getServerTest().rest().withClientConfiguration(restBuilders.get(user)).withCacheMode(CacheMode.DIST_SYNC).create() ); @@ -141,7 +141,7 @@ public void testHotRodWriterCannotReadImplicit() { @Test public void testHotRodWriterCannotReadExplicit() { - testHotRodWriterCannotRead("admin", "observer", "deployer", "application", "writer", "reader"); + testHotRodWriterCannotRead("admin", "observer", "deployer", "application", "writer", "reader", "monitor"); } private void testHotRodWriterCannotRead(String... explicitRoles) { @@ -151,7 +151,7 @@ private void testHotRodWriterCannotRead(String... explicitRoles) { Exceptions.expectException(HotRodClientException.class, "(?s).*ISPN000287.*", () -> writerCache.get("k1") ); - for (TestUser user : EnumSet.complementOf(EnumSet.of(TestUser.WRITER))) { + for (TestUser user : EnumSet.complementOf(EnumSet.of(TestUser.WRITER, TestUser.MONITOR))) { RemoteCache userCache = getServerTest().hotrod().withClientConfiguration(hotRodBuilders.get(user)).get(); assertEquals("v1", userCache.get("k1")); } @@ -320,7 +320,7 @@ public void testNonBulkReadUsersCannotQuery() { Exceptions.expectException(HotRodClientException.class, "(?s).*ISPN000287.*", () -> query.execute().list()); } // REST - for (TestUser user : EnumSet.of(TestUser.READER, TestUser.WRITER)) { + for (TestUser user : EnumSet.of(TestUser.READER, TestUser.WRITER, TestUser.MONITOR)) { RestCacheClient userCache = getServerTest().rest().withClientConfiguration(restBuilders.get(user)).get().cache(getServerTest().getMethodName()); assertStatus(FORBIDDEN, userCache.query("FROM sample_bank_account.User WHERE name = 'Tom'")); assertStatus(OK, userCache.searchStats()); diff --git a/server/tests/src/test/resources/ldif/infinispan-kerberos.ldif b/server/tests/src/test/resources/ldif/infinispan-kerberos.ldif index 3337e2b16d0f..9c68cd4c7e06 100644 --- a/server/tests/src/test/resources/ldif/infinispan-kerberos.ldif +++ b/server/tests/src/test/resources/ldif/infinispan-kerberos.ldif @@ -81,6 +81,19 @@ userPassword: password krb5PrincipalName: observer@INFINISPAN.ORG krb5KeyVersionNumber: 0 +dn: uid=monitor,ou=People,dc=infinispan,dc=org +objectClass: top +objectClass: person +objectClass: inetOrgPerson +objectClass: krb5principal +objectClass: krb5kdcentry +cn: ISPN Monitor +sn: monitor +uid: monitor +userPassword: weakPassword +krb5PrincipalName: monitor@INFINISPAN.ORG +krb5KeyVersionNumber: 0 + dn: uid=executor,ou=People,dc=infinispan,dc=org objectClass: top objectClass: person @@ -238,6 +251,13 @@ cn: observer description: the Infinispan observer group member: uid=observer,ou=People,dc=infinispan,dc=org +dn: cn=monitor,ou=Roles,dc=infinispan,dc=org +objectClass: top +objectClass: groupOfNames +cn: monitor +description: the Infinispan monitor group +member: uid=monitor,ou=People,dc=infinispan,dc=org + dn: cn=reader,ou=Roles,dc=infinispan,dc=org objectClass: top objectClass: groupOfNames diff --git a/server/tests/src/test/resources/ldif/infinispan.ldif b/server/tests/src/test/resources/ldif/infinispan.ldif index c33d4ebc469b..592d84691628 100644 --- a/server/tests/src/test/resources/ldif/infinispan.ldif +++ b/server/tests/src/test/resources/ldif/infinispan.ldif @@ -39,6 +39,15 @@ cn: ISPN Reader sn: observer userPassword: password +dn: uid=monitor,ou=People,dc=infinispan,dc=org +objectclass: top +objectclass: uidObject +objectclass: person +uid: monitor +cn: ISPN Monitor +sn: monitor +userPassword: weakPassword + dn: uid=unprivileged,ou=People,dc=infinispan,dc=org objectclass: top objectclass: uidObject @@ -94,6 +103,20 @@ cn: deployer description: the Infinispan deployer group member: uid=deployer,ou=People,dc=infinispan,dc=org +dn: cn=observer,ou=Roles,dc=infinispan,dc=org +objectClass: top +objectClass: groupOfNames +cn: observer +description: the Infinispan observer group +member: uid=observer,ou=People,dc=infinispan,dc=org + +dn: cn=monitor,ou=Roles,dc=infinispan,dc=org +objectClass: top +objectClass: groupOfNames +cn: monitor +description: the Infinispan monitor group +member: uid=monitor,ou=People,dc=infinispan,dc=org + dn: cn=executor,ou=Roles,dc=infinispan,dc=org objectClass: top objectClass: groupOfNames