How to parse spring security expressions programmatically (e.g. in some controller) - spring

How do you parse spring (web) security expressions like hasRole('admin') programmatically (without using tags, annotations or ...)? (reference doc)
I've found Spring: What parser to use to parse security expressions - but I don't know how to find or build the EvaluationContext e.g. inside a spring controller.
Without providing an EvaluationContext gives
org.springframework.expression.spel.SpelEvaluationException: EL1011E:(pos 0): Method call: Attempted to call method hasRole(java.lang.String) on null context object

you need to add several things in order to get this thing working. You have to plug into the Spring's security API. Here's how I did it and it is working fine with Spring 3.2.
First as it was stated before you must have similar configuration in your spring-context.xml:
<security:http access-decision-manager-ref="customAccessDecisionManagerBean">
<security:http/>
<bean id="customWebSecurityExpressionHandler"
class="com.boyan.security.CustomWebSecurityExpressionHandler"/>
<bean id="customAccessDecisionManagerBean"
class="org.springframework.security.access.vote.AffirmativeBased">
<property name="decisionVoters">
<list>
<bean class="org.springframework.security.web.access.expression.WebExpressionVoter">
<property name="expressionHandler" ref="customWebSecurityExpressionHandler" />
</bean>
</list>
</property>
</bean>
This defines a new expressionHandler to override the default one for the WebExpressionVoter. Then we add this new decision voter to the decision manager. CustomWebSecurityExpressionHandler's purpose it to control the creation of SecurityExpressionRoot. So far so good. The question is why do you need a CustomWebSecurityExpressionRoot and the answer is simple as that - you define your custom security methods there. Having this in mind we can write the following classes:
public class CustomWebSecurityExpressionHandler extends DefaultWebSecurityExpressionHandler {
#Override
protected SecurityExpressionOperations createSecurityExpressionRoot(
Authentication authentication, FilterInvocation fi) {
CustomWebSecurityExpressionRoot expressionRoot =
new CustomWebSecurityExpressionRoot(authentication, delegationEvaluator);
return expressionRoot;
}
}
}
public class CustomWebSecurityExpressionRoot extends WebSecurityExpressionRoot {
public CustomWebSecurityExpressionRoot(Authentication auth, FilterInvocation fi) {
super(auth, fi);
}
// in here you must define all of the methods you are going to invoke in #PreAuthorize
// for example if you have an expression with #PreAuthorize('isBoyan(John)')
// then you must have the following method defined here:
public boolean isBoyan(String value) {
//your logic goes in here
return "Boyan".equalsIgnoreCase(value);
}
}
If you want to get a reference to the ExpressionParser you can use the following method AbstractSecurityExpressionHandler.getExpressionParser(). It is accessible through CustomWebSecurityExpressionHandler. Also you can take a look at its API if you want to do something more specific.
I hope this answers you question.

Related

How do you create a subclass of PropertySourcesPropertyResolver in spring 4?

I'm trying to figure out a way to store certain properties in an encrypted form while they are at rest, and have them transparently decrypted before the property is injected into any beans, whether they are using #Value or they are defined in xml by setting properties. We're not using spring-boot yet. The property file would look like this:
db_password={aes}some_encrypted_value
I can see in the logs where the PropertySourcesPropertyResolver gets the value for my property. It should be pretty simple to create my own implementation of the PropertySourcesPropertyResolver.getProperty method that looks for the "{aes}" prefix, decrypting it if necessary, but I can't figure out how to use my subclass in my application.
Does anyone have any idea how I can get spring to use my implementation instead of Springs?
I initially tried to use the PropertySourcesPlaceholderConfigurer that worked for me in Spring 3, but I couldn't figure out how to make it work in spring 4. I also couldn't get the newer PropertySourcesPlaceholderConfigurer to work either.
Can anyone point me in the right direction?
We did it as follows with Spring 4.0.3 RELEASE
public class MyPropertyConfigurer extends PropertyPlaceHolderConfigurer{
protected void convertProperties(Properties props){
Enumeration<?> propertyNames = props.propertyNames();
while(propertyNames.hasMoreElements()){
String propName = (String)propertyNames.nextElement();
String propValue = (String)props.getProperty(propName);
if(propName.indexOf("db_password") != -1){
setPropertyValue(props,propName,propValue);
}
}
}
private void setPropertyValue(Properties props,String propName,String propValue){
String decryptedValue = PasswordUtility.decrypt(propValue);
props.setProperty(propName,decryptedValue);
}
}
In xml, it was configured as below
<bean id="dbPropertyPlaceholder" class="mypackage.MyPropertyConfigurer">
<property name="locations">
<list>
<value>file:myProp.properties</value>
<list>
</property>
</bean>

reading values from XML and displaying them in JSP

I have some experience with JSF but I want to learn some Spring MVC now. I wish to display the options to the user to change the language my website is displayed in. To accomplish this I want to define the languages in XML and set them in a bean, then in a JSP iterate over that list to show the languages options to the user.
This is what my XML looks like:
<bean id="languagesSupportedBean" class="be.maxcorp.Util.LanguageBean">
<property name="languagesSupported">
<array>
<value>en</value>
<value>nl</value>
</array>
</property>
</bean>
This is my LanguagesSupportedBean class:
#Component
public class LanguageBean {
public String[] languagesSupported;
public String[] getLanguagesSupported() {
return languagesSupported;
}
public void setLanguagesSupported(String[] languagesSupported) {
this.languagesSupported = languagesSupported;
}
}
In my JSP I'd like to do something like this:
<c:forEach items="${languageBean.LanguagesSupported}" var="language">
${language}
</c:forEach>
Because Spring MVC is request-based and not component-based I suppose this approach won't work unless I add the LanguageBean as attribute to every Model param in every controller method?
I'd greatly appreciate any tips on accomplishing this.
If you're using an InternalResourceViewResolver you should be able to set a property called exposeContextBeansAsAttributes that will expose your beans as attributes that JSPs can access directly:
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="exposeContextBeansAsAttributes" value="true"/>
</bean>
So if your LanguageBean is specified as id="languagesSupportedBean" in your XML, you can reference it directly in your JSP using its id:
<c:forEach items="${languagesSupportedBean.languagesSupported}" var="language">
${language}
</c:forEach>
So no controller or model modifications needed.
If you are declaring your LanguageBean in XML, then you won't need to annotate it #Component
Alternatively, if you're not using InternalResourceViewResolver you could inject your LanguageBean into your controller and then expose it using a method annotated #ModelAttribute:
#ModelAttribute("languagesSupportedBean")
public LanguageBean getLanguageBean() {
return languageBean;
}
That would then be accessible in your JSP using the name languagesSupportedBean and would alleviate the need to set the bean on every model in every controller method.
Store the bean in the session. You can access it the same way from the JSP. Otherwise you can extend Model and #Autowire your been there(not sure that it will work).

Change security-context for method call with spring-security

Currently I'm using spring security and #PreAuthorize annotations to secure method calls. Now I want to change the authentication token for a method call like the run-as authentication replacement of spring security allows me to do.
Can I configure the replacement on a per method base? Per annotation, SpEL expression....
If not, would it be possible do figure out in the runAsManager what method is called?
How would I configure the security config attributes for a secured object, at all?
I've posted a detailed article on implementing Run-As in conjunction with #PreAuthorize.
1) Implement your own RunAsManager that creates the Authentication to use during method execution based on any custom logic. The example below uses a custom annotation that provides the extra role:
public class AnnotationDrivenRunAsManager extends RunAsManagerImpl {
#Override
public Authentication buildRunAs(Authentication authentication, Object object, Collection<ConfigAttribute> attributes) {
if(!(object instanceof ReflectiveMethodInvocation) || ((ReflectiveMethodInvocation)object).getMethod().getAnnotation(RunAsRole.class) == null) {
return super.buildRunAs(authentication, object, attributes);
}
String roleName = ((ReflectiveMethodInvocation)object).getMethod().getAnnotation(RunAsRole.class).value();
if (roleName == null || roleName.isEmpty()) {
return null;
}
GrantedAuthority runAsAuthority = new SimpleGrantedAuthority(roleName);
List<GrantedAuthority> newAuthorities = new ArrayList<GrantedAuthority>();
// Add existing authorities
newAuthorities.addAll(authentication.getAuthorities());
// Add the new run-as authority
newAuthorities.add(runAsAuthority);
return new RunAsUserToken(getKey(), authentication.getPrincipal(), authentication.getCredentials(),
newAuthorities, authentication.getClass());
}
}
This implementation will look for a custom #RunAsRole annotation on a protected method (e.g. #RunAsRole("ROLE_AUDITOR")) and, if found, will add the given authority (ROLE_AUDITOR in this case) to the list of granted authorities. RunAsRole itself is just a simple custom annotation.
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface RunAsRole {
String value();
}
2) Instantiate the manager:
<bean id="runAsManager"
class="org.springframework.security.access.intercept.RunAsManagerImpl">
<property name="key" value="my_run_as_key"/>
</bean>
3) Register it:
<global-method-security pre-post-annotations="enabled" run-as-manager-ref="runAsManager">
<expression-handler ref="expressionHandler"/>
</global-method-security>
4) Example usage in a Controller:
#Controller
public class TransactionLogController {
#PreAuthorize("hasRole('ROLE_REGISTERED_USER')") //Authority needed to access the method
#RunAsRole("ROLE_AUDITOR") //Authority added by RunAsManager
#RequestMapping(value = "/transactions", method = RequestMethod.GET) //Spring MVC configuration. Not related to security
#ResponseBody //Spring MVC configuration. Not related to security
public List<Transaction> getTransactionLog(...) {
... //Invoke something in the backend requiring ROLE_AUDITOR
{
... //User does not have ROLE_AUDITOR here
}
EDIT:
The value of key in RunAsManagerImpl can be anything you want. Here's the excerpt from Spring docs on its use:
To ensure malicious code does not create a RunAsUserToken and present
it for guaranteed acceptance by the RunAsImplAuthenticationProvider,
the hash of a key is stored in all generated tokens. The
RunAsManagerImpl and RunAsImplAuthenticationProvider is created in the
bean context with the same key:
<bean id="runAsManager"
class="org.springframework.security.access.intercept.RunAsManagerImpl">
<bean id="runAsAuthenticationProvider"
class="org.springframework.security.access.intercept.RunAsImplAuthenticationProvider">
By using the
same key, each RunAsUserToken can be validated it was created by an
approved RunAsManagerImpl. The RunAsUserToken is immutable after
creation for security reasons.
I solved this by implementing my own RunAsManager that checks for a custom annotation on the invoked method and returns the appropriate Token.
works great.

Adding custom RequestCondition's in Spring mvc 3.1

I have a Spring mvc (3.1.1) app, and I want to define conditions beyond what's available in RequestMapping. I have a couple of things I want to use it for.
First, it would be nice if I could show a different home page for different user types:
#Controller
public class HomepageController {
#RequestMapping(value = "/")
#CustomCondition(roles = Guest.class)
public String guestHome() { /*...*/ }
#RequestMapping(value = "/")
#CustomCondition(roles = Admin.class)
public String adminHome() { /*...*/ }
}
Second, I want the app to function both as a web site and as a REST service (e.g. for mobile apps), so I'd want to let the website access both html and json actions, and let the service (different subdomain) only access json actions (some kind of #CustomCondition(web = true) which only matches website urls)
Can this work for any of the two uses I'm planning?
I found very little documentation about custom conditions, but I did find one example that implements custom conditions which might be what I want, but it uses a #Configuration class instead of the XML configuration which I'm using and I don't want to move my entire spring xml definitions to a #Configuration class.
Can I define a customMethodCondition for RequestMappingHandlerMapping in the XML?
I tried subclassing RequestMappingHandlerMapping and override getCustomMethodCondition, to return my custom RequestCondition, but it didn't work - getMatchingCondition() in my condition didn't fire.
Any help would be greatly appreciated!
UPDATE
I read a little more, and it looks like RequestMappingHandlerMapping is a new class (since ver 3.1).
What happens in my app is that the #Configuration that tries to override and thereby redefine the requestMappingHandlerMapping bean actually works, but the url mappings (#RequestMapping methods in #Controllers) seem to get processed twice, once by the subclass ExtendedRequestMappingHandlerMapping and once by the original RequestMappingHandlerMapping --first with a custom condition, and then again without it.
Bottom line is my custom conditions are simply ignored.
This is supposed to be an advanced pattern, but IMO it should be quite common...
Comments anyone?
Spring MVC already provides a mechanism for distinguishing between json and html, the RequestMapping annotation takes a consumes attribute which looks at the content type of the request...
// REST version, Content-type is "application/json"
#RequestMapping(value = "/", consumes = "application/json")
public void myRestService() {
...
// HTML version, Content-type is not "application/json"
#RequestMapping(value = "/", consumes = "!application/json")
public void myHtmlService() {
...
Another way to use the same url but have distinct methods is with the param or headers attribute...
// the url is /?role=guest
#RequestMapping(value = "/", param = "role=guest")
public void guestService() {
// the url is / with header role=admin
#RequestMapping(value = "/", headers = "role=admin")
public void adminService() {
I would think you would want distinct urls for security. Typically, with something like Spring Security, you would put all of the admin functionality under /admin and let the framework manage it all...
<http auto-config="true">
<intercept-url pattern="/admin/**" access="ROLE_ADMIN" />
...
Would this be sufficient for your use case(s)?
If you have extended RequestMappingHandlerMapping(say ExtendedRequestMappingHandlerMapping) you have to register this new mapping a little differently in application context xml.
You cannot use <mvc:annotation-driven/> to configure the Spring MVC as that defines it's own handlerMapping internally, you can instead do something along these lines(or follow the approach in the link with #Configuration that you have provided):
<bean name="handlerAdapter" class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="webBindingInitializer">
<bean class="org.springframework.web.bind.support.ConfigurableWebBindingInitializer">
<property name="conversionService" ref="conversionService"></property>
<property name="validator">
<bean class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/>
</property>
</bean>
</property>
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter"></bean>
<bean class="org.springframework.http.converter.StringHttpMessageConverter"></bean>
<bean class="org.springframework.http.converter.ResourceHttpMessageConverter"></bean>
<bean class="org.springframework.http.converter.xml.SourceHttpMessageConverter"></bean>
<bean class="org.springframework.http.converter.xml.XmlAwareFormHttpMessageConverter"></bean>
<bean class="org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter"></bean>
<bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"></bean>
</list>
</property>
</bean>
<bean name="handlerMapping" class="..ExtendedRequestMappingHandlerMapping">
</bean>
This should ensure that your mapping takes effect and will ensure that the appropriate handler method is found by the handlerAdapter component.

Spring HTTP cache management

I've seen that you can control cache http headers with the AnnotationMethodHandlerAdapter bean.
My problem is that I need to have a fine grane control on the cache (at method level).
The best think would be to have something like an annotation like "#RequestCache(expire=60)".
Is there anything like this?
What is the best way to accomplish this task?
Thanks,
Andrea
Update:
pap suggest to use an HandlerInterceptor, but I've seen multiple forum's post saying that it's not possible to get the target method inside an HandlerInterceptor and suggest to use regular AOP instead (not specifically for caching).
The problem is that I don't want to add the request parameter to all my methods, only to make it accessible to the aspect. Is there a way to avoid this?
You can use the following approach described in
Spring mvc reference manual
Support for the 'Last-Modified' Response Header To Facilitate Content Caching
#RequestMapping(value = "/modified")
#ResponseBody
public String getLastModified(WebRequest request) {
if (request.checkNotModified(lastModified.getTime())) {
logger.error("Was not modified.");
return null;
}
logger.error("Was modified.");
//processing
return "viewName";
}
One way (that I have used myself) is to create your own HandlerInterceptor.
public class CacheInterceptor implements HandlerInterceptor {
#Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
Class<?> o = AopUtils.getTargetClass(handler);
if (o.isAnnotationPresent(RequestCache.class)) {
response.setDateHeader("Expires", o.getAnnotation(RequestCache.class).expire());
}
return true;
}
...
}
and then
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
<property name="interceptors">
<array>
<bean class="bla.bla.CacheInterceptor " />
</array>
</property>
</bean>

Resources