Created a ticket:
http://jira.springsource.org/browse/SEC-1185
Also attached a project containing the Spring specific parts of what we have done so far (No DWR Dependencies).
Created a ticket:
http://jira.springsource.org/browse/SEC-1185
Also attached a project containing the Spring specific parts of what we have done so far (No DWR Dependencies).
OK - getting a little closer - I think the root cause may be me misunderstanding how to configure the ObjectDefinitionSource property...
I added this part to the ticket:
Nicolas - Just reading over what you said again:Actually maybe this is not a bug, but just me being confused about how to configure the ObjectDefinitionSource property on the MethodSecurityInterceptor instance in service-context.xml.
Here's what happens (attrs is null and securityInterceptor.isRejectPublicInvocations() is true) :
ConfigAttributeDefinition attrs = securityInterceptor.obtainObjectDefinitionSource() .getAttributes(mi);
if (attrs == null)
{
if (securityInterceptor.isRejectPublicInvocations())
{
return false;
}
return true;
So true is returned.
The reason this confuses me is that Spring knows that the method is protected like this:
<bean
id="needsAuthorizedMethod"
class="com.example.protect.NeedsAuthorizedMethod">
<security:intercept-methods>
<securityrotect method="returnString" access="ROLE_ADMIN" />
</security:intercept-methods>
</bean>
So it seems like it has the information it needs to provide the ConfigAttributeDefinition being retrieved start of the MethodInvocationPrivilegeEvaluator.isAllowed() code. Do I need to do something so that this information is used to create the ConfigAttributeDefinition instance?
OK - So I could have two instances of com.example.protect.NeedsAuthorizedMethod. One with protected methods and one not. So the the ObjectDefinitionSource is being used to figure out which instance it is that is being passed?I think that the it is because what you are securing is not method invocation plainly, but from an specific object.
You usually specify which instance of the class you are referring (with the bean name).
Currently I have the objectDefinitionSource property configured like this:
Do you know what I should put in there?Code:<property name="objectDefinitionSource"> <value> com.example.security.authentication.impl.AuthenticationServiceImpl.login=ROLE_ADMIN com.example.protect.NeedsAuthorizedMethod.returnString=ROLE_ADMIN </value> </property>
Hi Ole,
If you reread what i've written, it starts with "I think", because i'm not a master in Spring.
I don't really understand well enougth all this, so i can't really explain to you if your analysis is right or wrong :S
For what i'm reading from your post, your objectDefinitionSource is well formed, i don't have any ideas about this.
I'm sorry,
Regards,
No worries- I got a lot of "I Thinks" in here too
.
I'm going to write out my unverified understanding of what's happening (And this might be helpful in figuring out how to query for what method invocation has what security attributes on the client side as well):
The first thing the isAllowed method tries to do is obtain the security attributes for the method. If the security attributes can't be found, then the method is not secured, so it then checks to see whether the securityInterceptor should reject public invocations. If public invocations are allowed, it just returns true.
So the SecurityInterceptor.obtainObjectDefinitionSource() should return an ObjectDefinitionSource instance that contains the security attributes for all the methods.
The ObjectDefinitionSource instance probably has map<MethodInvocation, ConfigAttributeDefinition> that is used by the getAttributes(MethodInvocation) call.
So the ConfigAttributeDefinition instance returned would contain the roles that are allowed to invoke the method. Then these are compared to the roles held by the Authentication instance by the RoleVoter, and if there's a match the RoleVoter votes yes.
So I think the issue goes away once I can figure out how to get the right ObjectDefinitionSource. I'm sure there's another one available in Spring that has the ConfigAttributeDefinition with the security attribute holding the roles configured for the method.
I'll ask about it in another post.
I asked about the ObjectDefinitionSource handle here:
http://forum.springsource.org/showth...736#post246736
I'm going back to the drawing board. I think we need the following:
The ability to lookup security annotations that are class based.
The ability to lookup security annotations that are instance based.
A unique key that can be used to lookup class based security annotations.
A unique key that can be used to lookup instance based security annotations.
Then once we have this we can:
- First look to see whether the method has class based security roles
- If the invocation is allowed, then look to see if there are instance restrictions
- If it's still allowed, then look to see whether public invocations are allowed.
So that's what I'm doing ATM. Let me know if this is silly for some reason....
We can separate each thing in different interfaces, and implement each of them if we want.
I'm not sure which would be the best way.
Regards,
OK - Here's a different implementation of isCallable that only works on classes that are secured with the @Secure annotation (Still need to test):
This is simplistic. I just wanted to start with something that answers the question on how to see whether classes that are secured using annotation are callable.Code:/* * Searches the class and implemented interfaces for Secured annotations * and compares the result with the grantedAuthorities argument. */ public boolean isCallable(List<String> grantedAuthorities, Object object, String methodName) { List<Class> classList = new ArrayList<Class>(); classList.add(object.getClass()); Class<Class>[] interfaces = (Class<Class>[]) object.getClass().getInterfaces(); Set<String> securedMethodAuthorities = new HashSet<String>(); for (Class clazz : interfaces) { classList.add(object.getClass()); } for (Class klass : classList) { Method method = this.findMethod( klass, methodName); if (method != null) { Secured securedAnnotation = method.getAnnotation(Secured.class); if (securedAnnotation != null) { for (String role : securedAnnotation.value()) { securedMethodAuthorities.add(role); } } } } if (securedMethodAuthorities.size() == 0) { return true; } else { if (grantedAuthorities.size() == 0) { return false; } else { for (String grantedAuthority : grantedAuthorities) { for (String securedMethodAuthority : securedMethodAuthorities) { if ( grantedAuthority.equals(securedMethodAuthority)) { return true; } } } } } return false; } public Method findMethod(Class klass, String methodName) { for(Method method: klass.getMethods()) { if (methodName == method.getName()) { return method; } } return null; }
I going to attempt to create something similar for methods secured declaratively per object instance, but this is a little trickier, since the the configuration context has to be searched to obtain the roles / authorities required for access.
I'm going to look into extending the NamespaceHandler and BeanDefinitionParser for the <intercept-methods> elements so that a structure like:
Map(Object object, Map<String methodName, String[] roles>) becomes available after the configuration context has been processed.
Then the isCallable method can then use that structure to lookup which roles are required for access.
Still need to think about how to make this elegantly work with Spring. For example the implementation I did above does not use a AccessDecisionManager and Voters. It just checks to see whether the Authentication authorities and roles required per the @Secured annotations overlap.
I think that this implementation reinvents the wheel!!!
Why do you want to do that?
I know that you where having problems with the other implmenentation, but it seems that it is a much more complicated solution.
Regards,
The main reason I implemented this is because it only requires:
Authentication (Specifically List<String> that is the GrantedAuthorities)
Object
methodName
And then it figures out whether the method is authorized or not.
With what we had before it seems we have to respecify the security attributes for the methodName...or did I miss something?