Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ISPN-12874 MONITOR permission and "monitor" role #9169

Merged
merged 1 commit into from Mar 24, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -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,
Expand Down
Expand Up @@ -1119,6 +1119,7 @@ public Properties getGlobalConfigurationAsProperties() {

@Override
public CacheContainerStats getStats() {
authorizer.checkPermission(getSubject(), AuthorizationPermission.MONITOR);
return stats;
}

Expand Down
Expand Up @@ -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
*/
Expand Down
Expand Up @@ -883,7 +883,7 @@ public CompletableFuture<Boolean> replaceAsync(K key, V oldValue, V newValue) {

@Override
public Stats getStats() {
authzManager.checkPermission(subject, AuthorizationPermission.ADMIN);
authzManager.checkPermission(subject, AuthorizationPermission.MONITOR);
return delegate.getStats();
}

Expand Down
Expand Up @@ -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;

Expand All @@ -15,26 +14,33 @@
@Test(groups = {"functional", "smoke"}, testName = "security.CacheManagerAuthorizationTest")
public class CacheManagerAuthorizationTest extends BaseAuthorizationTest {

public void testAdminCombinations() throws Exception {
List<Runnable> 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<Boolean> 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));
}
}
}
}
Expand Down
Expand Up @@ -364,7 +364,7 @@ public void testGetBatchContainer(SecureCache<String, String> cache) {
cache.getBatchContainer();
}

@TestCachePermission(AuthorizationPermission.ADMIN)
@TestCachePermission(AuthorizationPermission.MONITOR)
public void testGetStats(SecureCache<String, String> cache) {
cache.getStats();
}
Expand Down
Expand Up @@ -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();
Expand Down
Expand Up @@ -27,6 +27,9 @@ Run [command]`help user` for command usage examples.
|`observer`
|Read-only access to the system.

|`monitor`
|Access to statistics

|===

.Reference
Expand Down
12 changes: 10 additions & 2 deletions documentation/src/main/asciidoc/topics/ref_authz_permissions.adoc
Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down
Expand Up @@ -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"));

Expand Down
Expand Up @@ -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()
);
Expand All @@ -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()
);
Expand All @@ -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) {
Expand All @@ -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<String, String> userCache = getServerTest().hotrod().withClientConfiguration(hotRodBuilders.get(user)).get();
assertEquals("v1", userCache.get("k1"));
}
Expand Down Expand Up @@ -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());
Expand Down
20 changes: 20 additions & 0 deletions server/tests/src/test/resources/ldif/infinispan-kerberos.ldif
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
23 changes: 23 additions & 0 deletions server/tests/src/test/resources/ldif/infinispan.ldif
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down