Skip to content

Commit d72887b

Browse files
committed
[#457] Embedded security example
1 parent 4da3db2 commit d72887b

File tree

5 files changed

+257
-0
lines changed

5 files changed

+257
-0
lines changed

documentation/asciidoc/topics/ref_embedded_tutorials.adoc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ $ {clean_exec}
2828
|link:{repository}/infinispan-embedded/transactions[Transactions]
2929
|Demonstrates how transactions work.
3030

31+
|link:{repository}/infinispan-embedded/security[Cache security]
32+
|Demonstrates how to integrate an application with {brandname} security features.
33+
3134
|link:{repository}/infinispan-embedded/streams[Streams]
3235
|Demonstrates how Distributed Streams work.
3336

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<modelVersion>4.0.0</modelVersion>
6+
<parent>
7+
<groupId>org.infinispan.tutorial.simple</groupId>
8+
<artifactId>infinispan-simple-tutorials</artifactId>
9+
<version>1.0.0-SNAPSHOT</version>
10+
<relativePath>../../pom.xml</relativePath>
11+
</parent>
12+
13+
<name>Infinispan Simple Tutorials: Embedded Cache Security</name>
14+
<artifactId>infinispan-simple-tutorials-cache-security</artifactId>
15+
16+
<build>
17+
<plugins>
18+
<plugin>
19+
<groupId>org.codehaus.mojo</groupId>
20+
<artifactId>exec-maven-plugin</artifactId>
21+
<executions>
22+
<execution>
23+
<goals>
24+
<goal>exec</goal>
25+
</goals>
26+
</execution>
27+
</executions>
28+
<configuration>
29+
<executable>java</executable>
30+
<arguments>
31+
<!--
32+
Assumes a cluster formed within the same node.
33+
Adjust JGroups bind address if creating cluster between different node.
34+
-->
35+
<argument>-Djgroups.bind_addr=127.0.0.1</argument>
36+
<argument>-Djava.net.preferIPv4Stack=true</argument>
37+
<argument>-Djava.util.logging.config.file=src/main/resources/logging.properties</argument>
38+
<argument>-classpath</argument>
39+
<classpath />
40+
<argument>org.infinispan.tutorial.simple.security.InfinispanCacheSecurity</argument>
41+
</arguments>
42+
</configuration>
43+
</plugin>
44+
</plugins>
45+
</build>
46+
47+
<dependencies>
48+
<dependency>
49+
<groupId>org.infinispan</groupId>
50+
<artifactId>infinispan-core</artifactId>
51+
</dependency>
52+
53+
<dependency>
54+
<groupId>org.junit.jupiter</groupId>
55+
<artifactId>junit-jupiter</artifactId>
56+
<scope>test</scope>
57+
</dependency>
58+
</dependencies>
59+
</project>
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
package org.infinispan.tutorial.simple.security;
2+
3+
import java.io.Serializable;
4+
import java.security.Principal;
5+
import java.util.Collections;
6+
import java.util.Set;
7+
import java.util.UUID;
8+
9+
import javax.security.auth.Subject;
10+
11+
import org.infinispan.Cache;
12+
import org.infinispan.commons.api.CacheContainerAdmin;
13+
import org.infinispan.configuration.cache.CacheMode;
14+
import org.infinispan.configuration.cache.ConfigurationBuilder;
15+
import org.infinispan.configuration.global.GlobalConfigurationBuilder;
16+
import org.infinispan.manager.DefaultCacheManager;
17+
import org.infinispan.security.AuthorizationPermission;
18+
import org.infinispan.security.Security;
19+
import org.infinispan.security.mappers.IdentityRoleMapper;
20+
21+
public class InfinispanCacheSecurity {
22+
23+
public static final String CACHE_NAME = "my-cache";
24+
public static final String SECRET_CACHE_NAME = "my-secret-cache";
25+
public static final String READ_ONLY_ROLE = "read-only";
26+
public static final String WRITE_ONLY_ROLE = "write-only";
27+
public static final String ADMIN_ROLE = "admin";
28+
public static final String SECRET_ROLE = "secret";
29+
30+
// These are the users authenticated by the application.
31+
// The subjects are provided by the application utilizing the caches.
32+
static final Subject ADMIN_USER = createSubject(ADMIN_ROLE);
33+
static final Subject SECRET_USER = createSubject(SECRET_ROLE);
34+
static final Subject READ_ONLY_USER = createSubject(READ_ONLY_ROLE);
35+
static final Subject WRITE_ONLY_USER = createSubject(WRITE_ONLY_ROLE);
36+
37+
public DefaultCacheManager dcm;
38+
Cache<String, String> cache;
39+
Cache<String, String> secretCache;
40+
41+
public static void main(String[] args) {
42+
InfinispanCacheSecurity security = new InfinispanCacheSecurity();
43+
44+
// First, create the caches and insert the entries.
45+
// Each operation is wrapped with the user with the correct permissions.
46+
security.createDefaultCacheManager();
47+
security.createAllCachesAndPopulate(3);
48+
49+
System.out.println("Entries for normal cache:");
50+
security.showAllEntriesWithUser(READ_ONLY_USER, security.cache);
51+
52+
System.out.println("\nEntries for secret cache:");
53+
security.showAllEntriesWithUser(SECRET_USER, security.secretCache);
54+
55+
// Now, we try to access the secret cache with a user that doesn't have the permission.
56+
// This operation should throw the SecurityException.
57+
System.out.println("\nFail to read with incorrect user:");
58+
try {
59+
security.showAllEntriesWithUser(WRITE_ONLY_USER, security.secretCache);
60+
throw new AssertionError("Should have failed to read with incorrect user");
61+
} catch (SecurityException ignore) {
62+
System.out.println("The user doesn't have permission to operate the secret cache");
63+
}
64+
65+
security.stop();
66+
}
67+
68+
public void createDefaultCacheManager() {
69+
// Setup up a clustered cache manager
70+
GlobalConfigurationBuilder global = GlobalConfigurationBuilder.defaultClusteredBuilder();
71+
72+
// Enable authorization and define the roles and their permissions.
73+
global.security().authorization().enable()
74+
// Defines how to map a principal to a role.
75+
// The identity will simply map the principal name to the role name.
76+
// For example, user "admin" will map to role "admin", and so on.
77+
.principalRoleMapper(new IdentityRoleMapper())
78+
.groupOnlyMapping(false)
79+
// Define different roles and associate the permissions.
80+
.role(ADMIN_ROLE).permission(AuthorizationPermission.ALL)
81+
.role(SECRET_ROLE).permission(AuthorizationPermission.ALL)
82+
.role(READ_ONLY_ROLE).permission(AuthorizationPermission.ALL_READ)
83+
.role(WRITE_ONLY_ROLE).permission(AuthorizationPermission.ALL_WRITE);
84+
85+
// Initialize the cache manager
86+
// We pass false to not start automatically.
87+
// Then we call start using the admin user. The user needs LIFECYCLE permission.
88+
dcm = new DefaultCacheManager(global.build(), false);
89+
Security.doAs(ADMIN_USER, dcm::start);
90+
}
91+
92+
public void createAllCachesAndPopulate(int size) {
93+
// First, create the first cache.
94+
// This cache is accessible to any role.
95+
ConfigurationBuilder builder1 = new ConfigurationBuilder();
96+
builder1.clustering().cacheMode(CacheMode.DIST_SYNC);
97+
builder1.security().authorization().enable();
98+
99+
// We utilize an admin to create the cache.
100+
cache = Security.doAs(ADMIN_USER, () -> dcm.administration().withFlags(CacheContainerAdmin.AdminFlag.VOLATILE)
101+
.getOrCreateCache(CACHE_NAME, builder1.build()));
102+
103+
104+
// Now we create a cache only accessible to users with the secret role.
105+
ConfigurationBuilder builder2 = new ConfigurationBuilder();
106+
builder2.clustering().cacheMode(CacheMode.DIST_SYNC);
107+
// The cache must include both roles so it can be stopped by the admin user as well.
108+
builder2.security().authorization().enable().roles(ADMIN_ROLE, SECRET_ROLE);
109+
110+
// We utilize an admin to create the cache.
111+
secretCache = Security.doAs(ADMIN_USER, () -> dcm.administration().withFlags(CacheContainerAdmin.AdminFlag.VOLATILE)
112+
.getOrCreateCache(SECRET_CACHE_NAME, builder2.build()));
113+
114+
// Now populate the caches with the correct user.
115+
Security.doAs(WRITE_ONLY_USER, () -> {
116+
for (int i = 0; i < size; i++) {
117+
cache.put(UUID.randomUUID().toString(), dcm.getNodeAddress());
118+
}
119+
});
120+
121+
Security.doAs(SECRET_USER, () -> {
122+
for (int i = 0; i < size; i++) {
123+
secretCache.put(UUID.randomUUID().toString(), dcm.getNodeAddress());
124+
}
125+
});
126+
}
127+
128+
public void showAllEntriesWithUser(Subject user, Cache<String, String> c) {
129+
Security.doAs(user, () -> c.entrySet().forEach(e -> System.out.println(e.getKey() + " -> " + e.getValue())));
130+
}
131+
132+
public int getCacheSize(Subject user, Cache<String, String> c) {
133+
return Security.doAs(user, c::size);
134+
}
135+
136+
public void stop() {
137+
if (dcm != null) {
138+
Security.doAs(ADMIN_USER, dcm::stop);
139+
dcm = null;
140+
}
141+
}
142+
143+
public static Subject createSubject(String principal) {
144+
return new Subject(true, Set.of(new ExamplePrincipal(principal)), Collections.emptySet(), Collections.emptySet());
145+
}
146+
147+
private record ExamplePrincipal(String name) implements Principal, Serializable {
148+
149+
@Override
150+
public String getName() {
151+
return name;
152+
}
153+
}
154+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package org.infinispan.tutorial.simple.security;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
import static org.junit.jupiter.api.Assertions.assertNotNull;
5+
import static org.junit.jupiter.api.Assertions.assertThrows;
6+
7+
import org.junit.jupiter.api.AfterEach;
8+
import org.junit.jupiter.api.BeforeEach;
9+
import org.junit.jupiter.api.Test;
10+
11+
public class InfinispanCacheSecurityTest {
12+
13+
InfinispanCacheSecurity security = new InfinispanCacheSecurity();
14+
15+
@BeforeEach
16+
protected void start() {
17+
security.createDefaultCacheManager();
18+
}
19+
20+
@AfterEach
21+
protected void stop() {
22+
security.stop();
23+
}
24+
25+
@Test
26+
public void testCacheSecurity() {
27+
assertNotNull(security.dcm);
28+
29+
security.createAllCachesAndPopulate(5);
30+
31+
assertEquals(5, security.getCacheSize(InfinispanCacheSecurity.READ_ONLY_USER, security.cache));
32+
assertEquals(5, security.getCacheSize(InfinispanCacheSecurity.SECRET_USER, security.secretCache));
33+
34+
// Only the secret user can access the secret cache.
35+
assertThrows(SecurityException.class, () -> security.getCacheSize(InfinispanCacheSecurity.READ_ONLY_USER, security.secretCache));
36+
37+
// Write-only user can not perform bulk read operations.
38+
assertThrows(SecurityException.class, () -> security.getCacheSize(InfinispanCacheSecurity.WRITE_ONLY_USER, security.cache));
39+
}
40+
}

pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,5 +147,6 @@
147147
<module>integrations/spring-boot/session-remote</module>
148148
<module>integrations/spring-boot/cache-embedded</module>
149149
<module>integrations/spring-boot/session-embedded</module>
150+
<module>infinispan-embedded/security</module>
150151
</modules>
151152
</project>

0 commit comments

Comments
 (0)