Skip to content

Commit 01ea39c

Browse files
mdeinumrwinch
authored andcommitted
SEC-2114: Provide Spring Cache Abstraction based cache implementations
As of Spring 3.1 spring has its own cache abstraction. This commit adds cache imlpementations based on that abstraction.
1 parent 89c63fd commit 01ea39c

File tree

6 files changed

+649
-0
lines changed

6 files changed

+649
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
/*
2+
* Copyright 2002-2013 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.security.acls.domain;
17+
18+
import net.sf.ehcache.CacheException;
19+
import net.sf.ehcache.Ehcache;
20+
import net.sf.ehcache.Element;
21+
import org.springframework.cache.Cache;
22+
import org.springframework.security.acls.model.AclCache;
23+
import org.springframework.security.acls.model.MutableAcl;
24+
import org.springframework.security.acls.model.ObjectIdentity;
25+
import org.springframework.security.acls.model.PermissionGrantingStrategy;
26+
import org.springframework.security.util.FieldUtils;
27+
import org.springframework.util.Assert;
28+
29+
import java.io.Serializable;
30+
31+
32+
/**
33+
* Simple implementation of {@link org.springframework.security.acls.model.AclCache} that delegates to {@link Cache} implementation.
34+
* <p>
35+
* Designed to handle the transient fields in {@link org.springframework.security.acls.domain.AclImpl}. Note that this implementation assumes all
36+
* {@link org.springframework.security.acls.domain.AclImpl} instances share the same {@link org.springframework.security.acls.model.PermissionGrantingStrategy} and {@link org.springframework.security.acls.domain.AclAuthorizationStrategy}
37+
* instances.
38+
*
39+
* @author Marten Deinum
40+
* @since 3.2
41+
*/
42+
public class SpringCacheBasedAclCache implements AclCache {
43+
//~ Instance fields ================================================================================================
44+
45+
private final Cache cache;
46+
private PermissionGrantingStrategy permissionGrantingStrategy;
47+
private AclAuthorizationStrategy aclAuthorizationStrategy;
48+
49+
//~ Constructors ===================================================================================================
50+
51+
public SpringCacheBasedAclCache(Cache cache, PermissionGrantingStrategy permissionGrantingStrategy,
52+
AclAuthorizationStrategy aclAuthorizationStrategy) {
53+
Assert.notNull(cache, "Cache required");
54+
Assert.notNull(permissionGrantingStrategy, "PermissionGrantingStrategy required");
55+
Assert.notNull(aclAuthorizationStrategy, "AclAuthorizationStrategy required");
56+
this.cache = cache;
57+
this.permissionGrantingStrategy = permissionGrantingStrategy;
58+
this.aclAuthorizationStrategy = aclAuthorizationStrategy;
59+
}
60+
61+
//~ Methods ========================================================================================================
62+
63+
public void evictFromCache(Serializable pk) {
64+
Assert.notNull(pk, "Primary key (identifier) required");
65+
66+
MutableAcl acl = getFromCache(pk);
67+
68+
if (acl != null) {
69+
cache.evict(acl.getId());
70+
cache.evict(acl.getObjectIdentity());
71+
}
72+
}
73+
74+
public void evictFromCache(ObjectIdentity objectIdentity) {
75+
Assert.notNull(objectIdentity, "ObjectIdentity required");
76+
77+
MutableAcl acl = getFromCache(objectIdentity);
78+
79+
if (acl != null) {
80+
cache.evict(acl.getId());
81+
cache.evict(acl.getObjectIdentity());
82+
}
83+
}
84+
85+
public MutableAcl getFromCache(ObjectIdentity objectIdentity) {
86+
Assert.notNull(objectIdentity, "ObjectIdentity required");
87+
88+
Cache.ValueWrapper element = null;
89+
90+
try {
91+
element = cache.get(objectIdentity);
92+
} catch (CacheException ignored) {}
93+
94+
if (element == null) {
95+
return null;
96+
}
97+
98+
return initializeTransientFields((MutableAcl)element.get());
99+
}
100+
101+
public MutableAcl getFromCache(Serializable pk) {
102+
Assert.notNull(pk, "Primary key (identifier) required");
103+
104+
Cache.ValueWrapper element = null;
105+
106+
try {
107+
element = cache.get(pk);
108+
} catch (CacheException ignored) {}
109+
110+
if (element == null) {
111+
return null;
112+
}
113+
114+
return initializeTransientFields((MutableAcl) element.get());
115+
}
116+
117+
public void putInCache(MutableAcl acl) {
118+
Assert.notNull(acl, "Acl required");
119+
Assert.notNull(acl.getObjectIdentity(), "ObjectIdentity required");
120+
Assert.notNull(acl.getId(), "ID required");
121+
122+
if ((acl.getParentAcl() != null) && (acl.getParentAcl() instanceof MutableAcl)) {
123+
putInCache((MutableAcl) acl.getParentAcl());
124+
}
125+
126+
cache.put(acl.getObjectIdentity(), acl);
127+
cache.put(acl.getId(), acl);
128+
}
129+
130+
private MutableAcl initializeTransientFields(MutableAcl value) {
131+
if (value instanceof AclImpl) {
132+
FieldUtils.setProtectedFieldValue("aclAuthorizationStrategy", value, this.aclAuthorizationStrategy);
133+
FieldUtils.setProtectedFieldValue("permissionGrantingStrategy", value, this.permissionGrantingStrategy);
134+
}
135+
136+
if (value.getParentAcl() != null) {
137+
initializeTransientFields((MutableAcl) value.getParentAcl());
138+
}
139+
return value;
140+
}
141+
142+
public void clearCache() {
143+
cache.clear();
144+
}
145+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
package org.springframework.security.acls.jdbc;
2+
3+
import org.junit.After;
4+
import org.junit.AfterClass;
5+
import org.junit.BeforeClass;
6+
import org.junit.Test;
7+
import org.springframework.cache.Cache;
8+
import org.springframework.cache.CacheManager;
9+
import org.springframework.cache.concurrent.ConcurrentMapCacheManager;
10+
import org.springframework.security.acls.domain.*;
11+
import org.springframework.security.acls.model.MutableAcl;
12+
import org.springframework.security.acls.model.ObjectIdentity;
13+
import org.springframework.security.acls.model.PermissionGrantingStrategy;
14+
import org.springframework.security.authentication.TestingAuthenticationToken;
15+
import org.springframework.security.core.Authentication;
16+
import org.springframework.security.core.authority.SimpleGrantedAuthority;
17+
import org.springframework.security.core.context.SecurityContextHolder;
18+
import org.springframework.security.util.FieldUtils;
19+
20+
import java.io.*;
21+
import java.util.Map;
22+
23+
import static org.junit.Assert.*;
24+
25+
/**
26+
* Tests {@link org.springframework.security.acls.domain.EhCacheBasedAclCache}
27+
*
28+
* @author Andrei Stefan
29+
*/
30+
public class SpringCacheBasedAclCacheTests {
31+
private static final String TARGET_CLASS = "org.springframework.security.acls.TargetObject";
32+
33+
private static CacheManager cacheManager;
34+
35+
@BeforeClass
36+
public static void initCacheManaer() {
37+
cacheManager = new ConcurrentMapCacheManager();
38+
// Use disk caching immediately (to test for serialization issue reported in SEC-527)
39+
cacheManager.getCache("springcasebasedacltests");
40+
}
41+
42+
@After
43+
public void clearContext() {
44+
SecurityContextHolder.clearContext();
45+
}
46+
47+
private Cache getCache() {
48+
Cache cache = cacheManager.getCache("springcasebasedacltests");
49+
cache.clear();
50+
return cache;
51+
}
52+
53+
@Test(expected=IllegalArgumentException.class)
54+
public void constructorRejectsNullParameters() throws Exception {
55+
new SpringCacheBasedAclCache(null, null, null);
56+
}
57+
58+
@Test
59+
public void cacheOperationsAclWithoutParent() throws Exception {
60+
Cache cache = getCache();
61+
Map realCache = (Map) cache.getNativeCache();
62+
ObjectIdentity identity = new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(100));
63+
AclAuthorizationStrategy aclAuthorizationStrategy = new AclAuthorizationStrategyImpl(
64+
new SimpleGrantedAuthority("ROLE_OWNERSHIP"), new SimpleGrantedAuthority("ROLE_AUDITING"),
65+
new SimpleGrantedAuthority("ROLE_GENERAL"));
66+
AuditLogger auditLogger = new ConsoleAuditLogger();
67+
68+
PermissionGrantingStrategy permissionGrantingStrategy = new DefaultPermissionGrantingStrategy(auditLogger);
69+
SpringCacheBasedAclCache myCache = new SpringCacheBasedAclCache(cache, permissionGrantingStrategy, aclAuthorizationStrategy);
70+
MutableAcl acl = new AclImpl(identity, Long.valueOf(1), aclAuthorizationStrategy, auditLogger);
71+
72+
assertEquals(0, realCache.size());
73+
myCache.putInCache(acl);
74+
75+
// Check we can get from cache the same objects we put in
76+
assertEquals(myCache.getFromCache(Long.valueOf(1)), acl);
77+
assertEquals(myCache.getFromCache(identity), acl);
78+
79+
// Put another object in cache
80+
ObjectIdentity identity2 = new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(101));
81+
MutableAcl acl2 = new AclImpl(identity2, Long.valueOf(2), aclAuthorizationStrategy, new ConsoleAuditLogger());
82+
83+
myCache.putInCache(acl2);
84+
85+
// Try to evict an entry that doesn't exist
86+
myCache.evictFromCache(Long.valueOf(3));
87+
myCache.evictFromCache(new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(102)));
88+
assertEquals(realCache.size(), 4);
89+
90+
myCache.evictFromCache(Long.valueOf(1));
91+
assertEquals(realCache.size(), 2);
92+
93+
// Check the second object inserted
94+
assertEquals(myCache.getFromCache(Long.valueOf(2)), acl2);
95+
assertEquals(myCache.getFromCache(identity2), acl2);
96+
97+
myCache.evictFromCache(identity2);
98+
assertEquals(realCache.size(), 0);
99+
}
100+
101+
@SuppressWarnings("unchecked")
102+
@Test
103+
public void cacheOperationsAclWithParent() throws Exception {
104+
Cache cache = getCache();
105+
Map realCache = (Map) cache.getNativeCache();
106+
107+
Authentication auth = new TestingAuthenticationToken("user", "password", "ROLE_GENERAL");
108+
auth.setAuthenticated(true);
109+
SecurityContextHolder.getContext().setAuthentication(auth);
110+
111+
ObjectIdentity identity = new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(1));
112+
ObjectIdentity identityParent = new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(2));
113+
AclAuthorizationStrategy aclAuthorizationStrategy = new AclAuthorizationStrategyImpl(
114+
new SimpleGrantedAuthority("ROLE_OWNERSHIP"), new SimpleGrantedAuthority("ROLE_AUDITING"),
115+
new SimpleGrantedAuthority("ROLE_GENERAL"));
116+
AuditLogger auditLogger = new ConsoleAuditLogger();
117+
118+
PermissionGrantingStrategy permissionGrantingStrategy = new DefaultPermissionGrantingStrategy(auditLogger);
119+
SpringCacheBasedAclCache myCache = new SpringCacheBasedAclCache(cache, permissionGrantingStrategy, aclAuthorizationStrategy);
120+
121+
MutableAcl acl = new AclImpl(identity, Long.valueOf(1), aclAuthorizationStrategy, auditLogger);
122+
MutableAcl parentAcl = new AclImpl(identityParent, Long.valueOf(2), aclAuthorizationStrategy, auditLogger);
123+
124+
acl.setParent(parentAcl);
125+
126+
assertEquals(0, realCache.size());
127+
myCache.putInCache(acl);
128+
assertEquals(realCache.size(), 4);
129+
130+
// Check we can get from cache the same objects we put in
131+
AclImpl aclFromCache = (AclImpl) myCache.getFromCache(Long.valueOf(1));
132+
assertEquals(acl, aclFromCache);
133+
// SEC-951 check transient fields are set on parent
134+
assertNotNull(FieldUtils.getFieldValue(aclFromCache.getParentAcl(), "aclAuthorizationStrategy"));
135+
assertNotNull(FieldUtils.getFieldValue(aclFromCache.getParentAcl(), "permissionGrantingStrategy"));
136+
assertEquals(acl, myCache.getFromCache(identity));
137+
assertNotNull(FieldUtils.getFieldValue(aclFromCache, "aclAuthorizationStrategy"));
138+
AclImpl parentAclFromCache = (AclImpl) myCache.getFromCache(Long.valueOf(2));
139+
assertEquals(parentAcl, parentAclFromCache);
140+
assertNotNull(FieldUtils.getFieldValue(parentAclFromCache, "aclAuthorizationStrategy"));
141+
assertEquals(parentAcl, myCache.getFromCache(identityParent));
142+
}
143+
144+
//~ Inner Classes ==================================================================================================
145+
146+
private class MockCache implements Cache {
147+
148+
@Override
149+
public String getName() {
150+
return "mockcache";
151+
}
152+
153+
@Override
154+
public Object getNativeCache() {
155+
return null;
156+
}
157+
158+
@Override
159+
public ValueWrapper get(Object key) {
160+
return null;
161+
}
162+
163+
@Override
164+
public void put(Object key, Object value) {}
165+
166+
@Override
167+
public void evict(Object key) {}
168+
169+
@Override
170+
public void clear() {}
171+
}
172+
}

0 commit comments

Comments
 (0)