Skip to content

SEC-2098, SEC-2099 #24

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

Closed
wants to merge 12 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.domain;

import org.springframework.cache.Cache;
import org.springframework.security.acls.model.AclCache;
import org.springframework.security.acls.model.MutableAcl;
import org.springframework.security.acls.model.ObjectIdentity;
import org.springframework.security.acls.model.PermissionGrantingStrategy;
import org.springframework.security.util.FieldUtils;
import org.springframework.util.Assert;

import java.io.Serializable;


/**
* Simple implementation of {@link org.springframework.security.acls.model.AclCache} that delegates to {@link Cache} implementation.
* <p>
* Designed to handle the transient fields in {@link org.springframework.security.acls.domain.AclImpl}. Note that this implementation assumes all
* {@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}
* instances.
*
* @author Marten Deinum
* @since 3.2
*/
public class SpringCacheBasedAclCache implements AclCache {
//~ Instance fields ================================================================================================

private final Cache cache;
private PermissionGrantingStrategy permissionGrantingStrategy;
private AclAuthorizationStrategy aclAuthorizationStrategy;

//~ Constructors ===================================================================================================

public SpringCacheBasedAclCache(Cache cache, PermissionGrantingStrategy permissionGrantingStrategy,
AclAuthorizationStrategy aclAuthorizationStrategy) {
Assert.notNull(cache, "Cache required");
Assert.notNull(permissionGrantingStrategy, "PermissionGrantingStrategy required");
Assert.notNull(aclAuthorizationStrategy, "AclAuthorizationStrategy required");
this.cache = cache;
this.permissionGrantingStrategy = permissionGrantingStrategy;
this.aclAuthorizationStrategy = aclAuthorizationStrategy;
}

//~ Methods ========================================================================================================

public void evictFromCache(Serializable pk) {
Assert.notNull(pk, "Primary key (identifier) required");

MutableAcl acl = getFromCache(pk);

if (acl != null) {
cache.evict(acl.getId());
cache.evict(acl.getObjectIdentity());
}
}

public void evictFromCache(ObjectIdentity objectIdentity) {
Assert.notNull(objectIdentity, "ObjectIdentity required");

MutableAcl acl = getFromCache(objectIdentity);

if (acl != null) {
cache.evict(acl.getId());
cache.evict(acl.getObjectIdentity());
}
}

public MutableAcl getFromCache(ObjectIdentity objectIdentity) {
Assert.notNull(objectIdentity, "ObjectIdentity required");
return getFromCache((Object)objectIdentity);
}

public MutableAcl getFromCache(Serializable pk) {
Assert.notNull(pk, "Primary key (identifier) required");
return getFromCache((Object)pk);
}

public void putInCache(MutableAcl acl) {
Assert.notNull(acl, "Acl required");
Assert.notNull(acl.getObjectIdentity(), "ObjectIdentity required");
Assert.notNull(acl.getId(), "ID required");

if ((acl.getParentAcl() != null) && (acl.getParentAcl() instanceof MutableAcl)) {
putInCache((MutableAcl) acl.getParentAcl());
}

cache.put(acl.getObjectIdentity(), acl);
cache.put(acl.getId(), acl);
}

private MutableAcl getFromCache(Object key) {
Cache.ValueWrapper element = cache.get(key);

if (element == null) {
return null;
}

return initializeTransientFields((MutableAcl) element.get());
}

private MutableAcl initializeTransientFields(MutableAcl value) {
if (value instanceof AclImpl) {
FieldUtils.setProtectedFieldValue("aclAuthorizationStrategy", value, this.aclAuthorizationStrategy);
FieldUtils.setProtectedFieldValue("permissionGrantingStrategy", value, this.permissionGrantingStrategy);
}

if (value.getParentAcl() != null) {
initializeTransientFields((MutableAcl) value.getParentAcl());
}
return value;
}

public void clearCache() {
cache.clear();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
package org.springframework.security.acls.jdbc;

import org.junit.After;
import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.concurrent.ConcurrentMapCacheManager;
import org.springframework.security.acls.domain.*;
import org.springframework.security.acls.model.MutableAcl;
import org.springframework.security.acls.model.ObjectIdentity;
import org.springframework.security.acls.model.PermissionGrantingStrategy;
import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.util.FieldUtils;

import java.util.Map;

import static org.junit.Assert.*;

/**
* Tests {@link org.springframework.security.acls.domain.SpringCacheBasedAclCache}
*
* @author Marten Deinum
*/
public class SpringCacheBasedAclCacheTests {
private static final String TARGET_CLASS = "org.springframework.security.acls.TargetObject";

private static CacheManager cacheManager;

@BeforeClass
public static void initCacheManaer() {
cacheManager = new ConcurrentMapCacheManager();
// Use disk caching immediately (to test for serialization issue reported in SEC-527)
cacheManager.getCache("springcasebasedacltests");
}

@After
public void clearContext() {
SecurityContextHolder.clearContext();
}

private Cache getCache() {
Cache cache = cacheManager.getCache("springcasebasedacltests");
cache.clear();
return cache;
}

@Test(expected=IllegalArgumentException.class)
public void constructorRejectsNullParameters() throws Exception {
new SpringCacheBasedAclCache(null, null, null);
}

@SuppressWarnings("rawtypes")
@Test
public void cacheOperationsAclWithoutParent() throws Exception {
Cache cache = getCache();
Map realCache = (Map) cache.getNativeCache();
ObjectIdentity identity = new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(100));
AclAuthorizationStrategy aclAuthorizationStrategy = new AclAuthorizationStrategyImpl(
new SimpleGrantedAuthority("ROLE_OWNERSHIP"), new SimpleGrantedAuthority("ROLE_AUDITING"),
new SimpleGrantedAuthority("ROLE_GENERAL"));
AuditLogger auditLogger = new ConsoleAuditLogger();

PermissionGrantingStrategy permissionGrantingStrategy = new DefaultPermissionGrantingStrategy(auditLogger);
SpringCacheBasedAclCache myCache = new SpringCacheBasedAclCache(cache, permissionGrantingStrategy, aclAuthorizationStrategy);
MutableAcl acl = new AclImpl(identity, Long.valueOf(1), aclAuthorizationStrategy, auditLogger);

assertEquals(0, realCache.size());
myCache.putInCache(acl);

// Check we can get from cache the same objects we put in
assertEquals(myCache.getFromCache(Long.valueOf(1)), acl);
assertEquals(myCache.getFromCache(identity), acl);

// Put another object in cache
ObjectIdentity identity2 = new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(101));
MutableAcl acl2 = new AclImpl(identity2, Long.valueOf(2), aclAuthorizationStrategy, new ConsoleAuditLogger());

myCache.putInCache(acl2);

// Try to evict an entry that doesn't exist
myCache.evictFromCache(Long.valueOf(3));
myCache.evictFromCache(new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(102)));
assertEquals(realCache.size(), 4);

myCache.evictFromCache(Long.valueOf(1));
assertEquals(realCache.size(), 2);

// Check the second object inserted
assertEquals(myCache.getFromCache(Long.valueOf(2)), acl2);
assertEquals(myCache.getFromCache(identity2), acl2);

myCache.evictFromCache(identity2);
assertEquals(realCache.size(), 0);
}

@SuppressWarnings("rawtypes")
@Test
public void cacheOperationsAclWithParent() throws Exception {
Cache cache = getCache();
Map realCache = (Map) cache.getNativeCache();

Authentication auth = new TestingAuthenticationToken("user", "password", "ROLE_GENERAL");
auth.setAuthenticated(true);
SecurityContextHolder.getContext().setAuthentication(auth);

ObjectIdentity identity = new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(1));
ObjectIdentity identityParent = new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(2));
AclAuthorizationStrategy aclAuthorizationStrategy = new AclAuthorizationStrategyImpl(
new SimpleGrantedAuthority("ROLE_OWNERSHIP"), new SimpleGrantedAuthority("ROLE_AUDITING"),
new SimpleGrantedAuthority("ROLE_GENERAL"));
AuditLogger auditLogger = new ConsoleAuditLogger();

PermissionGrantingStrategy permissionGrantingStrategy = new DefaultPermissionGrantingStrategy(auditLogger);
SpringCacheBasedAclCache myCache = new SpringCacheBasedAclCache(cache, permissionGrantingStrategy, aclAuthorizationStrategy);

MutableAcl acl = new AclImpl(identity, Long.valueOf(1), aclAuthorizationStrategy, auditLogger);
MutableAcl parentAcl = new AclImpl(identityParent, Long.valueOf(2), aclAuthorizationStrategy, auditLogger);

acl.setParent(parentAcl);

assertEquals(0, realCache.size());
myCache.putInCache(acl);
assertEquals(realCache.size(), 4);

// Check we can get from cache the same objects we put in
AclImpl aclFromCache = (AclImpl) myCache.getFromCache(Long.valueOf(1));
assertEquals(acl, aclFromCache);
// SEC-951 check transient fields are set on parent
assertNotNull(FieldUtils.getFieldValue(aclFromCache.getParentAcl(), "aclAuthorizationStrategy"));
assertNotNull(FieldUtils.getFieldValue(aclFromCache.getParentAcl(), "permissionGrantingStrategy"));
assertEquals(acl, myCache.getFromCache(identity));
assertNotNull(FieldUtils.getFieldValue(aclFromCache, "aclAuthorizationStrategy"));
AclImpl parentAclFromCache = (AclImpl) myCache.getFromCache(Long.valueOf(2));
assertEquals(parentAcl, parentAclFromCache);
assertNotNull(FieldUtils.getFieldValue(parentAclFromCache, "aclAuthorizationStrategy"));
assertEquals(parentAcl, myCache.getFromCache(identityParent));
}
}
6 changes: 6 additions & 0 deletions buildSrc/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,12 @@ dependencies {
'com.springsource.bundlor:com.springsource.bundlor.blint:1.0.0.RELEASE'
}

// Trang
dependencies {
compile 'com.thaiopensource:trang:20091111',
'net.sourceforge.saxon:saxon:9.1.0.8'
}

task ide(type: Copy) {
from configurations.runtime
into 'ide'
Expand Down
59 changes: 59 additions & 0 deletions buildSrc/src/main/groovy/trang/TrangPlugin.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package trang;

import com.thaiopensource.relaxng.translate.Driver

import javax.xml.transform.Transformer
import javax.xml.transform.TransformerFactory
import javax.xml.transform.stream.StreamSource
import javax.xml.transform.stream.StreamResult

import org.gradle.api.*;
import org.gradle.api.tasks.*
import org.gradle.api.file.FileCollection

/**
* Used for converting .rnc files to .xsd files.
* @author Rob Winch
*/
class TrangPlugin implements Plugin<Project> {
public void apply(Project project) {
Task rncToXsd = project.tasks.add('rncToXsd', RncToXsd.class)
rncToXsd.description = 'Converts .rnc to .xsd'
rncToXsd.group = 'Build'
}
}

/**
* Converts .rnc files to .xsd files using trang and then applies an xsl file to cleanup the results.
*/
public class RncToXsd extends DefaultTask {
@InputDirectory
File rncDir

@InputFile
File xslFile

@OutputDirectory
File xsdDir

@TaskAction
public final void transform() {
String xslPath = xslFile.absolutePath
rncDir.listFiles( { dir, file -> file.endsWith('.rnc')} as FilenameFilter).each { rncFile ->
File xsdFile = new File(xsdDir, rncFile.name.replace('.rnc', '.xsd'))
String xsdOutputPath = xsdFile.absolutePath
new Driver().run([rncFile.absolutePath, xsdOutputPath] as String[]);

TransformerFactory tFactory = new net.sf.saxon.TransformerFactoryImpl()
Transformer transformer =
tFactory.newTransformer(new StreamSource(xslPath))
File temp = File.createTempFile("gradle-trang-" + xsdFile.name, ".xsd")
xsdFile.withInputStream { is ->
temp << is
}
StreamSource xmlSource = new StreamSource(temp)
transformer.transform(xmlSource, new StreamResult(xsdFile))
temp.delete()
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
implementation-class=trang.TrangPlugin
Loading