Add request parameter to SAML request using Spring Security SAML - spring-saml

I need to add a request parameter (e.g. locale=en) to the SAML request in order to let the login page display correct language. How do I do that?
I tried to add the attribute to the HttpServletRequest sent as an argument to the commence method (SamlEntryPoint), but that doesn't seem to work.
Any suggestions?

SAML provides a standard mechanism for extending content sent in authentication requests - an Extensions element.
In order to use it you will need to coordinate with your IDP on what data you send and in what format. In Spring SAML you can customize its content by extending class WebSSOProfileImpl, for example like this:
package com.v7security.saml;
import org.opensaml.common.SAMLException;
import org.opensaml.saml2.common.Extensions;
import org.opensaml.saml2.common.impl.ExtensionsBuilder;
import org.opensaml.saml2.core.AuthnRequest;
import org.opensaml.saml2.metadata.AssertionConsumerService;
import org.opensaml.saml2.metadata.SingleSignOnService;
import org.opensaml.saml2.metadata.provider.MetadataProviderException;
import org.opensaml.xml.schema.XSAny;
import org.opensaml.xml.schema.impl.XSAnyBuilder;
import org.springframework.security.saml.context.SAMLMessageContext;
import org.springframework.security.saml.websso.WebSSOProfileImpl;
import org.springframework.security.saml.websso.WebSSOProfileOptions;
/**
* Class adds additional extensions element to the AuthnRequest sent to IDP.
*/
public class WebSSOProfile extends WebSSOProfileImpl {
#Override
protected AuthnRequest getAuthnRequest(SAMLMessageContext context, WebSSOProfileOptions options, AssertionConsumerService assertionConsumer, SingleSignOnService bindingService) throws SAMLException, MetadataProviderException {
AuthnRequest authnRequest = super.getAuthnRequest(context, options, assertionConsumer, bindingService);
authnRequest.setExtensions(buildExtensions());
return authnRequest;
}
protected Extensions buildExtensions() {
XSAny languageClass = new XSAnyBuilder().buildObject("http://www.v7security.com/schema/2015/04/request", "RequestLanguage", "req");
languageClass.setTextContent("urn:v7security:request:lang:english");
Extensions extensions = new ExtensionsBuilder().buildObject();
extensions.getUnknownXMLObjects().add(languageClass);
return extensions;
}
}
Another option is to send data in relayState, which is a piece of information SP can send to IDP and expect it to be bounced back (typically SP's state). The value is supposed to be opaque to the IDP, but of course it could process it for example in the way you intend. For details on setting the relay state see chapter on SP initialized SSO in the manual.
Setting request parameters on HttpRequest object is not expected to produce any result, Spring SAML doesn't automatically communicate these in any way.
It is possible to add an HTTP parameter to request sent with HTTP Redirect binding by extending class HTTPRedirectDeflateEncoder and overriding method buildRedirectURL. The new class can then be supplied into constructor of HTTPRedirectDeflateBinding and replaced in bean redirectBinding of securityContext.xml, in the following way:
<bean id="redirectBinding" class="org.springframework.security.saml.processor.HTTPRedirectDeflateBinding">
<constructor-arg>
<bean class="org.opensaml.saml2.binding.decoding.HTTPRedirectDeflateDecoder">
<constructor-arg name="pool" ref="parserPool"/>
</bean>
</constructor-arg>
<constructor-arg>
<bean class="com.custom.HTTPRedirectDeflateEncoder"/>
</constructor-arg>
</bean>

Related

Swagger ignore functions from resource

when I'm trying to configure everything for swagger, it's working.. somehow partially, it ignores functions from resource and consider only #Api adnotation of the class
I'm using apache cxf + spring
Generated json contains only class description, no methods here
..."apis":[{"path":"/","description":"Entities operations"}]...
The controller adnotations
#Path("/")
#Api(value = "/", description = "Entities operations")
#Controller
public class RepositoryResource
....
The function adnotation
#GET
#ApiOperation(value = "Method to check if this resources is up and running")
public String hello()
...
Log message spoted
Could not find a definition for bean with id {http://listing.jaxrs.swagger.wordnik.com/}ApiListingResourceJSON.http-destination
spring configuration
<jaxrs:server id="restAPI" address="/" staticSubresourceResolution="true">
<jaxrs:serviceBeans>
<ref bean="swaggerResourceJSON"/>
<ref bean="repositoryResource"/>
</jaxrs:serviceBeans>
....
Don't use a root ("/") for your #Api value - that would break the documentation. Even if you have #Path("/"), you should have something like #Api("/root").
This does not affect the API itself, only where the documentation is served.
Once you make that modification, your /api-docs would look like:
..."apis":[{"path":"/root","description":"Entities operations"}]...
And that resource would be available on /api-docs/root. Running it with swagger-ui will produce the proper API calls.

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

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.

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.

How to forward to a specific annotated handler from a spring interceptor?

I wrote an Spring request interceptor for authentication purposes, it extends the HandlerInterceptorAdapter. I've set it with this line in my servlet-context:
<mvc:interceptors>
<bean class = "it.jsoftware.jacciseweb.controllers.AuthInterceptor">
<property name="manServ" ref = "acciseService"></property>
</bean>
</mvc:interceptors>
and the pre handle method is
#Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
HttpSession sess = request.getSession();
String path = request.getPathTranslated();
boolean autenticated = maincont.isAuthenticated(sess);
if (!autenticated){
response.sendRedirect("accise?action=login");
return false;
}
return super.preHandle(request, response, handler);
}
like this anyway it will generate a redirect loop, because it will never reach the redirect page due to the interception and redirect.
There is many ways to solve this, but I don't know how to achieve them:
Detect the url of the request (but I don't know how) and don't check for authentication for the login page. Moreover I'd like to make this solution more flexible.
Select the login handler directly on the controller. How do I do that? Is it possible?
I've seen that in examples people specifies interceptor mapping using org.springframework.web.servlet.handler.SimpleUrlHandlerMapping, anyway I'm using annotations. Is there a way, using annotations, to specify a different mapping for the interceptor so that it doesn't fire with the above address (accise?action=login)? Or maybe to chain different mapping schemes?
Is there a specific reason for not using spring-security?
IMHO is simple, powerful and deeply tested.
You can simply implement and inject your custom authenticator, spring-security will handle the redirect.

Resources