"Zachary Roadhouse":https://jira.spring.io/secure/ViewProfile.jspa?name=zroadhouse said:
I am integrating the Spring Security ACL module into our application which uses a mixture of UUID and numeric primary keys as our domain object identifiers. Based on reading the interface documentation fo ObjectIdentity, I had hoped that our UUID identifiers would work with the JdbcMutableAclService implementation without any customizations to the source. This is not currently true as the JdbcMutableAclService and related classes all assume that the identifier value can be converted to a Long. This assumption is limited to a small number of points in the implementation.
This is a request to change the implementation to allow the choice of Long vs. String to be made into a configuration setting (or alternate implementation).
["Zachary Roadhouse":https://jira.spring.io/secure/ViewProfile.jspa?name=zroadhouse] said:
Note that passing BigDecimal for the identity also doesn't work because the hashCode calculations for a Long (used when the ObjectIndentity objects are loaded from the DB) is different from BigDecimal for the same value. Therefore, you get an exception when using the following code:
Class type = ...;
BigDecimal id = ...;
ObjectIdentity oid = new ObjectIdentityImpl(type, id);
MutableAcl acl = aclService.createAcl(oid);
Thus I would like to expand the scope of this request to include use of any other types besides Long as the identity value.
["Ross Duncan":https://jira.spring.io/secure/ViewProfile.jspa?name=magicduncan] said:
I have a similar issue whereby we have domain objects already in the system which are uniquely identified already by a String. To work around the above issue, I have had to add a Long getId method to my domain object which returns the hash code of the object's unique name. This is not ideal, as this ends up being a large otherwise meaningless number. Ideally, any solution should allow any mixture of Serializable object identifiers to be used.
Joern Huxhorn said:
We have this exact issue right now with a CouchDB that is creating IDs looking like this: 744d71ed2536ca173d2968424803171c
It is absolutely beyond my comprehension why
This is not a request for an improvement in my books. This is a bug since the implementation simply isn't supporting the defined API since Identifier of ObjectIdentity is defined as Serializable.
Further, a NumberFormatException is thrown instead of something like UnsupportedOperationException indicating that this is a shortcoming of the implementation.
See also patch in issue SEC-1716.
(Yes, I'm annoyed. And quite disappointed. Sorry about that.)
Udi D said:
We have same issue, we want to create acls for uris (Strings).
we dont have numeric keys and we don't want to access a database to generate/retrieve them.
any idea how we can work around it?
any estimate for when 3.2 is going to be released?
AE said:
Facing the same issue. I have a object which is identifiable by String only and cannot easily use the acl permissions. Hope we could switch this to a string in the future.
nicholas deyoung said:
Same problem. We use UUIDs (Strings) for Object Identity. :(
Frank Henningsen said:
Yes, same problem. We would like to use UUID/GUID (Strings) for Object Identity :( any plans on allowing this in 2015? I have to find another way around this issue then....pretty annoying, seems like a little improvement :)
Has anyone found a workaround for this issue?
Frank Henningsen said:
I end up with the following stacktrace when trying to use a GUID (uniqueidentier in MSSQL) in the object_id_identity column of the object_id_identity table :
2015-01-22 12:30:37,931 [tomcat-http--6] DEBUG org.springframework.security.acls.AclPermissionEvaluator - Checking permission 'read' for object 'org.springframework.security.acls.domain.ObjectIdentityImpl[Type: dk.kmd.nemoekonomi.services.domain.Organisation; Identifier: 5F8C338B-CD4E-4CE0-B28A-9AE212727812]'
java.lang.NumberFormatException: For input string: "5F8C338B-CD4E-4CE0-B28A-9AE212727812"
at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
at java.lang.Long.parseLong(Long.java:441)
at java.lang.Long.valueOf(Long.java:540)
at org.springframework.security.acls.jdbc.BasicLookupStrategy$2.setValues(BasicLookupStrategy.java:351)
at org.springframework.jdbc.core.JdbcTemplate$1.doInPreparedStatement(JdbcTemplate.java:644)
at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:589)
at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:639)
at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:668)
at org.springframework.security.acls.jdbc.BasicLookupStrategy.lookupObjectIdentities(BasicLookupStrategy.java:341)
at org.springframework.security.acls.jdbc.BasicLookupStrategy.readAclsById(BasicLookupStrategy.java:303)
at org.springframework.security.acls.jdbc.JdbcAclService.readAclsById(JdbcAclService.java:109)
at org.springframework.security.acls.jdbc.JdbcAclService.readAclById(JdbcAclService.java:94)
at org.springframework.security.acls.AclPermissionEvaluator.checkPermission(AclPermissionEvaluator.java:81)
at org.springframework.security.acls.AclPermissionEvaluator.hasPermission(AclPermissionEvaluator.java:65)
at org.springframework.security.access.expression.SecurityExpressionRoot.hasPermission(SecurityExpressionRoot.java:140)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.springframework.expression.spel.support.ReflectiveMethodExecutor.execute(ReflectiveMethodExecutor.java:69)
at org.springframework.expression.spel.ast.MethodReference.getValueInternal(MethodReference.java:122)
at org.springframework.expression.spel.ast.MethodReference.getValueInternal(MethodReference.java:80)
at org.springframework.expression.spel.ast.SpelNodeImpl.getTypedValue(SpelNodeImpl.java:102)
at org.springframework.expression.spel.standard.SpelExpression.getValue(SpelExpression.java:98)
at org.springframework.security.access.expression.ExpressionUtils.evaluateAsBoolean(ExpressionUtils.java:11)
at org.springframework.security.access.expression.method.ExpressionBasedPreInvocationAdvice.before(ExpressionBasedPreInvocationAdvice.java:41)
at org.springframework.security.access.prepost.PreInvocationAuthorizationAdviceVoter.vote(PreInvocationAuthorizationAdviceVoter.java:54)
at org.springframework.security.access.prepost.PreInvocationAuthorizationAdviceVoter.vote(PreInvocationAuthorizationAdviceVoter.java:24)
at org.springframework.security.access.vote.AffirmativeBased.decide(AffirmativeBased.java:62)
at org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:206)
at org.springframework.security.access.intercept.aspectj.AspectJMethodSecurityInterceptor.invoke(AspectJMethodSecurityInterceptor.java:41)
at org.springframework.security.access.intercept.aspectj.aspect.AnnotationSecurityAspect.ajc$around$org_springframework_security_access_intercept_aspectj_aspect_AnnotationSecurityAspect$1$c4d57a2b(AnnotationSecurityAspect.aj:63)
at dk.kmd.nemoekonomi.services.controller.OrganisationController.listJson(OrganisationController.java:85)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:215)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:132)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:104)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:743)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:672)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:82)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:919)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:851)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:953)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:844)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:618)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:829)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:725)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:291)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:77)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at dk.kmd.nemoekonomi.services.filter.SetupMDCRequestFilter.doFilter(SetupMDCRequestFilter.java:36)
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:343)
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:260)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at dk.kmd.nemoekonomi.services.filter.ActivateUserPOIDFilter.doFilter(ActivateUserPOIDFilter.java:33)
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:343)
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:260)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter.doFilterInternal(OpenEntityManagerInViewFilter.java:180)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:88)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:118)
at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:84)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:113)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:103)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:113)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:154)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:45)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at dk.kmd.security.authentication.WaffleAuthenticationFilter.doFilter(WaffleAuthenticationFilter.java:87)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:50)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:87)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.access.channel.ChannelProcessingFilter.doFilter(ChannelProcessingFilter.java:144)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:192)
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:160)
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:343)
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:260)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at dk.kmd.nemoekonomi.services.filter.RequestFilter.doFilter(RequestFilter.java:38)
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:343)
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:260)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:219)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:501)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:142)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:610)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:537)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1085)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:658)
at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:146)
at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:279)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:724)
table
CREATE TABLE acl_object_identity (
id BIGINT NOT NULL IDENTITY PRIMARY KEY,
object_id_class BIGINT NOT NULL,
object_id_identity UNIQUEIDENTIFIER NOT NULL,
parent_object BIGINT,
owner_sid BIGINT,
entries_inheriting BIT NOT NULL,
CONSTRAINT uk_acl_object_identity UNIQUE (object_id_class, object_id_identity),
CONSTRAINT fk_acl_object_identity_parent FOREIGN KEY (parent_object) REFERENCES acl_object_identity (id),
CONSTRAINT fk_acl_object_identity_class FOREIGN KEY (object_id_class) REFERENCES acl_class (id),
CONSTRAINT fk_acl_object_identity_owner FOREIGN KEY (owner_sid) REFERENCES acl_sid (id)
);
Paul Wheeler said:
Hi Frank,
I have actually implemented this in a fork of Spring Security but I haven't had time to contribute it back to the community yet. You can have a look at it in this branch if you like, although be aware that there is also some config in there to upload artifacts to a private repo so that part will fail for you.
https://github.com/pwheel/spring-security/tree/feature/acl-uuid-strings
I'll kick off the process of contributing it back soon. There's no guarantee it will be accepted of course. Message me on github if you want more details.
Thanks,
Paul
Frank Henningsen said:
thx Paul,
just what i had hoped for, much appreciated even though its not yet accepted, im looking into the fork right away, thx for the info :)
br
Frank
Frank Henningsen said:
thx Paul, i was able to take your changes and got it to work with a few changes in ObjectIdentityImpl where i had to convert a UUID as String to ta real UUID to make the equals/hashcode methods work in collections
// convert string to UUID
if(identifier instanceof String) {
identifier = UUID.fromString((String) identifier);
}
Paul Wheeler said:
As per the contributing guidelines I've raised a Stack Overflow question to discuss the changes (as the Spring forums are now closed) - http://stackoverflow.com/questions/28421390/allowing-uuid-values-for-objectidentity-getidentifier-in-spring-security-acl.
Frank - I'll ping you directly as I haven't had to modify ObjectIdentityImpl.
This issue supersedes #740
I would much like to see broader extensibility in the org.springframework.security.acls.jdbc.BasicLookupStrategy class. Both JdbcAclService and JdbcMutableAclService can be easily extended via setters and/or protected methods, but the BasicLookupStratedy has all implementation details hidden in private methods.
I suggest updating this class at least such that:
ObjectIdentity extraction from ResultSet can be reimplemented, orProcessResultSet implementationWith these modifications, I believe extensions such as UUID fork by Paul Wheeler mentioned in previous comments would be possible more easily.
Is this issue still on the roadmap?
I am looking forward for this to be resolved. The only workaround I could find is to implement my own LookupStrategy to support a couple of non-primitives such as String.
Raised #4424 to fix this issue
Most helpful comment
I would much like to see broader extensibility in the
org.springframework.security.acls.jdbc.BasicLookupStrategyclass. BothJdbcAclServiceandJdbcMutableAclServicecan be easily extended via setters and/or protected methods, but theBasicLookupStratedyhas all implementation details hidden in private methods.I suggest updating this class at least such that:
ObjectIdentityextraction fromResultSetcan be reimplemented, orProcessResultSetimplementationWith these modifications, I believe extensions such as UUID fork by Paul Wheeler mentioned in previous comments would be possible more easily.