rovome / spring-security-acl-mongodb Goto Github PK
View Code? Open in Web Editor NEWSpring Security MongoDB based access control list (ACL) implementation
Spring Security MongoDB based access control list (ACL) implementation
Unit Test:
@Test
@WithMockUser
public void testReadAclsById() throws Exception {
// Arrange
TestDomainObject domainObject = new TestDomainObject();
TestDomainObject firstObject = new TestDomainObject();
TestDomainObject secondObject = new TestDomainObject();
TestDomainObject thirdObject = new TestDomainObject();
TestDomainObject unrelatedObject = new TestDomainObject();
ObjectIdentity objectIdentity = new ObjectIdentityImpl(Class.forName(domainObject.getClass().getName()), domainObject.getId());
MongoAcl parent = new MongoAcl(domainObject.getId(), domainObject.getClass().getName(), UUID.randomUUID().toString(),"owner", null, true);
MongoAcl child1 = new MongoAcl(firstObject.getId(), firstObject.getClass().getName(), UUID.randomUUID().toString(), "Tim Test", parent.getId(), true);
MongoAcl child2 = new MongoAcl(secondObject.getId(), secondObject.getClass().getName(), UUID.randomUUID().toString(), "Petty Pattern", parent.getId(), true);
MongoAcl child3 = new MongoAcl(thirdObject.getId(), thirdObject.getClass().getName(), UUID.randomUUID().toString(), "Sam Sample", parent.getId(), true);
MongoAcl nonChild = new MongoAcl(unrelatedObject.getId(), unrelatedObject.getClass().getName(), UUID.randomUUID().toString());
DomainObjectPermission permission = new DomainObjectPermission(UUID.randomUUID().toString(),
SecurityContextHolder.getContext().getAuthentication().getName(),
BasePermission.READ.getMask() | BasePermission.WRITE.getMask(),
true, true, true);
parent.getPermissions().add(permission);
child1.getPermissions().add(permission);
child2.getPermissions().add(permission);
// MongoAcl must has owner
aclRepository.save(parent);
aclRepository.save(child1);
aclRepository.save(child2);
aclRepository.save(child3);
aclRepository.save(nonChild);
// Act
List<Sid> sids = new LinkedList<>();
sids.add(new PrincipalSid(SecurityContextHolder.getContext().getAuthentication().getName()));
//List<ObjectIdentity> childObjects = aclService.findChildren(objectIdentity);
List<ObjectIdentity> childObjects = aclService.findChildren(objectIdentity);
Map<ObjectIdentity, Acl> resultUser = aclService.readAclsById(childObjects, sids);
// Assert
assertThat(childObjects.size(), is(equalTo(3)));
assertThat(resultUser.keySet().size(), is(equalTo(3)));
resultUser.values().removeIf(Objects::isNull);
resultUser.keySet().forEach(objectIdentity1 -> {
Acl acl = resultUser.get(objectIdentity1);
System.out.println("ACE Size found: " + acl.getEntries().size());
});
assertThat(resultUser.keySet().size(), is(equalTo(3)));
}
the result is:
ACE Size found: 0
ACE Size found: 1
ACE Size found: 1
but it should be 1,1,1, because parent,child1,child2 should have permission!
Hi, this project looks really interesting. I'm always on the lookout of a Spring ACL implementation that provides row level security. The downfall I see in many of them is that the data that comes back from the database is post filtered with only the rows the authenticated principal has access to. The issue I have had in the past with this is that it ruins database paging.
Does this library augment mongo queries with the desired permission checks when querying collections before the call is made to retrieve a collection, or is this done as a post filter mechanism?
Thanks
unit test:
@Test
@WithMockUser
public void testReadAclsById() throws Exception {
// Arrange
TestDomainObject domainObject = new TestDomainObject();
TestDomainObject firstObject = new TestDomainObject();
TestDomainObject secondObject = new TestDomainObject();
TestDomainObject thirdObject = new TestDomainObject();
TestDomainObject unrelatedObject = new TestDomainObject();
ObjectIdentity objectIdentity = new ObjectIdentityImpl(Class.forName(domainObject.getClass().getName()), domainObject.getId());
MongoAcl parent = new MongoAcl(domainObject.getId(), domainObject.getClass().getName(), UUID.randomUUID().toString());
MongoAcl child1 = new MongoAcl(firstObject.getId(), firstObject.getClass().getName(), UUID.randomUUID().toString(), "Tim Test", parent.getId(), true);
MongoAcl child2 = new MongoAcl(secondObject.getId(), secondObject.getClass().getName(), UUID.randomUUID().toString(), "Petty Pattern", parent.getId(), true);
MongoAcl child3 = new MongoAcl(thirdObject.getId(), thirdObject.getClass().getName(), UUID.randomUUID().toString(), "Sam Sample", parent.getId(), true);
MongoAcl nonChild = new MongoAcl(unrelatedObject.getId(), unrelatedObject.getClass().getName(), UUID.randomUUID().toString());
DomainObjectPermission permission = new DomainObjectPermission(UUID.randomUUID().toString(),
SecurityContextHolder.getContext().getAuthentication().getName(),
BasePermission.READ.getMask() | BasePermission.WRITE.getMask(),
true, true, true);
parent.getPermissions().add(permission);
child1.getPermissions().add(permission);
child2.getPermissions().add(permission);
// MongoAcl must has owner
aclRepository.save(parent);
aclRepository.save(child1);
aclRepository.save(child2);
aclRepository.save(child3);
aclRepository.save(nonChild);
// Act
List<Sid> sids = new LinkedList<>();
sids.add(new PrincipalSid(SecurityContextHolder.getContext().getAuthentication().getName()));
List<ObjectIdentity> childObjects = aclService.findChildren(objectIdentity);
//List<ObjectIdentity> childObjects = aclService.findChildren(new ObjectIdentityImpl(Class.forName(firstObject.getClass().getName()), firstObject.getId()));
Map<ObjectIdentity, Acl> resultUser = aclService.readAclsById(childObjects, sids);
// Assert
assertThat(resultUser.keySet().size(), is(equalTo(1)));
}
and aclService.findChildren has such exception:
java.lang.IllegalArgumentException: Principal required
at org.springframework.util.Assert.hasText(Assert.java:181)
at org.springframework.security.acls.domain.PrincipalSid.<init>(PrincipalSid.java:44)
at org.springframework.security.acls.mongodb.BasicLookupStrategy.convertToAcl(BasicLookupStrategy.java:213)
unit test is a little modification from another test, I just want to compare result of two readAclsById, but get the exception.
@Test
@WithMockUser
public void testReadAclsByIdTwice() throws Exception {
// Arrange
TestDomainObject domainObject = new TestDomainObject();
TestDomainObject firstObject = new TestDomainObject();
TestDomainObject secondObject = new TestDomainObject();
TestDomainObject thirdObject = new TestDomainObject();
TestDomainObject unrelatedObject = new TestDomainObject();
ObjectIdentity objectIdentity = new ObjectIdentityImpl(Class.forName(domainObject.getClass().getName()), domainObject.getId());
MongoAcl parent = new MongoAcl(domainObject.getId(), domainObject.getClass().getName(), UUID.randomUUID().toString(),"owner", null, true);
MongoAcl child1 = new MongoAcl(firstObject.getId(), firstObject.getClass().getName(), UUID.randomUUID().toString(), "Tim Test", parent.getId(), true);
MongoAcl child2 = new MongoAcl(secondObject.getId(), secondObject.getClass().getName(), UUID.randomUUID().toString(), "Petty Pattern", parent.getId(), true);
MongoAcl child3 = new MongoAcl(thirdObject.getId(), thirdObject.getClass().getName(), UUID.randomUUID().toString(), "Sam Sample", parent.getId(), true);
MongoAcl nonChild = new MongoAcl(unrelatedObject.getId(), unrelatedObject.getClass().getName(), UUID.randomUUID().toString());
DomainObjectPermission permission = new DomainObjectPermission(UUID.randomUUID().toString(),
"user-0",
BasePermission.READ.getMask() | BasePermission.WRITE.getMask(),
true, true, true);
parent.getPermissions().add(permission);
child1.getPermissions().add(permission);
child2.getPermissions().add(permission);
// MongoAcl must has owner
aclRepository.save(parent);
aclRepository.save(child1);
aclRepository.save(child2);
aclRepository.save(child3);
aclRepository.save(nonChild);
// Act
List<Sid> sids = new LinkedList<>();
sids.add(new PrincipalSid("user-0"));
List<Sid> sids1 = new LinkedList<>();
sids.add(new PrincipalSid("owner"));
//List<ObjectIdentity> childObjects = aclService.findChildren(objectIdentity);
List<ObjectIdentity> childObjects = aclService.findChildren(objectIdentity);
//Map<ObjectIdentity, Acl> resultOwner = aclService.readAclsById(childObjects, sids1);
Map<ObjectIdentity, Acl> resultUser = aclService.readAclsById(childObjects, sids);
// Assert
assertThat(childObjects.size(), is(equalTo(3)));
assertThat(resultUser.keySet().size(), is(equalTo(3)));
resultUser.values().removeIf(Objects::isNull);
resultUser.keySet().forEach(objectIdentity1 -> {
Acl acl = resultUser.get(objectIdentity1);
System.out.println("ACE Size found: " + acl.getEntries().size());
});
assertThat(resultUser.keySet().size(), is(equalTo(3)));
}
The Exception is:
java.lang.IllegalStateException: Error: SID-filtered element detected when implementation does not perform SID filtering - have you added something to the cache manually?
at org.springframework.security.acls.mongodb.BasicLookupStrategy.readAclsById(BasicLookupStrategy.java:122)
at org.springframework.security.acls.mongodb.MongoDBAclService.readAclsById(MongoDBAclService.java:96)
at io.pinzi.security.AclUnitTest.testReadAclsByIdTwice(AclUnitTest.java:458)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:252)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:94)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:191)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:51)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
The strange thing is , if I comment anyone of next two lines, the code works ok:
//Map<ObjectIdentity, Acl> resultOwner = aclService.readAclsById(childObjects, sids1);
Map<ObjectIdentity, Acl> resultUser = aclService.readAclsById(childObjects, sids);
How come this is not yet part of the official Spring Security release? Have you contacted anybody in the Spring community about it?
In case they are not willing to embrace this for whatever reason, can I suggest you release this independently, so that we can pull a dependency rather than building it locally?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.