Path Variables in Web Security Expressions and set /users/{userId:\\d+} - spring-mvc

spring security 4.1.3.RELEASE intercept-url request-matcher="mvc"
I try to use spring-security.xml to config my web security with request-matche="mvc", but the pattern "/users/{userId:id}" dosen't work.
My config just look like the following code:
<http entry-point-ref="myAuthenticationEntryPoint" auto-config="true" use-expressions="true" create-session="stateless" request-matcher="mvc"
access-decision-manager-ref="myAccessDecisionManager">
<intercept-url pattern="/users/{userId:\\d+}" method="GET" access="#webSecurity.isMe(authentication, #userId) or hasAnyRole('ADMIN')"/>
<intercept-url pattern="/users/management" method="GET" access="hasAnyRole('ADMIN')"/>
</http>
I expecte that the request for /users/12345 will match the pattern /users/{userId:\d+} which will match 12345 to the userId, and the request for /users/management will match the pattern /users/management. However, in fact, the pattern /users/{userId:\d+} dosen't work, it never match the request /users/12345. and if I change the setting to the following code:
<http entry-point-ref="myAuthenticationEntryPoint" auto-config="true" use-expressions="true" create-session="stateless" request-matcher="mvc"
access-decision-manager-ref="myAccessDecisionManager">
<intercept-url pattern="/users/{userId}" method="GET" access="#webSecurity.isMe(authentication, #userId) or hasAnyRole('ADMIN')"/>
<intercept-url pattern="/users/management" method="GET" access="hasAnyRole('ADMIN')"/>
</http>
Remove the :\d+ from /users/{userId:\d+}, it will match the request for /users/12345. But it will match the request for /users/management as well, which makes the value of userId equals to unexpected "management". I have try ant-matcher and regex-matcher, and I can't find the userId in a perfect solution.

I have solved the problem by myself. It's a stupid mistake. To solve the problem, I just need to replace /users/{userId:\\d+} with /users/{userId:\d+} or /users/{userId:[0-9]+} and use AntPathMatcher instead of using MvcPathMatcher. Configuring the security with spring-security.xml is defferent with using javaConfig or using annotation. In javaConfig or annotation #RequestMapping, the content in "" is String Object, so we need an annother \ to escape the \d+. But in xml, it's unnecessary. If using \\d+ here, spring security will append it to \\\\d+, which will never match number type string. I find this solution when I set a breakpoint in org.springframework.util.AntPathMatcher.matchStrings(String str, Map<String, String> uriTemplateVariables) and watch the request.

Related

What should #PreAuthorize do?

I have simple crud with spring-mcv and spring-security.
Everything its up and running
spring-security.xml
<b:bean id="handlerWeb1" class="org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler">
<b:property name="permissionEvaluator" ref="permissionEvaluator"/>
</b:bean>
<b:bean id="handlerMethod2" class="org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler">
<b:property name="permissionEvaluator" ref="permissionEvaluator"/>
</b:bean>
<global-method-security pre-post-annotations="enabled">
<expression-handler ref="handlerMethod2"/>
</global-method-security>
<http auto-config="true" use-expressions="true" >
<expression-handler ref="handlerWeb1" />
...
</http>
The logger
INFO: Using bean 'handlerWeb1' as web SecurityExpressionHandler implementation
INFO: Using bean 'handlerMethod2' as method ExpressionHandler implementation
When the fallow methodo is executed:
#RequestMapping("/page")
#PreAuthorize("hasPermission('page','list')")
public ModelAndView pages() {
return modelAndView( ... ));
}
If hasPermission('page','list')==true there is no problem.
If hasPermission('page','list')==false the methodo still render the view but the vatiables passed tto de view are empty.
Ask:
When hasPermission('page','list')==false, what should happen?
a) redirect to 403
b) render the view with variable empty.

Using AssignMessage policy to generate a formatted string in Apigee

I am doing a URL rewrite in my API End point. So, the querystring is not passed by default to the backend service.
I want to create a variable with name "querystring" and format that string with the input query params. How can I do that?
For eg: apigee api url = https://myapigee-prod.agigee.net/v1/test/resource1?p1=abc&p2=123
I want to create the querystring variable as querystring = param1=abc&param2=123
I tried
<AssignVariable>
<Name>myqs</Name>
<Value>param1={request.queryparam.p1}&param2={request.queryparam.p2}</Value>
</AssignVariable>
<Ref> tag can be used instead of <Value> tag only if I am getting value of one parameter. To form a string, it wouldn't work.
Unfortunately you can't use the curly braces method of variable expansion within an AssignVariable.
You can do this via a JavaScript policy. Here is the JavaScript code:
var myqs = "param1=" + context.getVariable("request.queryparam.p1") +
"&param2=" + context.getVariable("request.queryparam.p2");
context.setVariable("myqs", myqs);
Also, you can use an AssignMessage policy to do this, on the Proxy request or Target request flow.
http://apigee.com/docs/api-services/content/generate-or-modify-messages-using-assignmessage
e.g.
<AssignMessage name="GenerateRequest">
<AssignTo createNew="false" type="request">Request</AssignTo>
<Set>
<QueryParams>
<QueryParam name="p1">{request.queryparam.param1}</QueryParam>
<QueryParam name="p2">{request.queryparam.param2}</QueryParam>
</QueryParams>
</Set>
</AssignMessage>

How to add basic auth to a service callout policy

This is the service callout policy:
<ServiceCallout name="GeoCodeClient">
<Request clearPayload="false" variable="GeocodingRequest" />
<Response>GeocodingResponse</Response>
<Timeout>30000</Timeout>
<HTTPTargetConnection>
<URL>http://maps.googleapis.com/maps/api/geocode/json</URL>
</HTTPTargetConnection>
</ServiceCallout>
Let us say I have to access a resource that is username/password protected. How do I add that basic authorization to this policy to enable me to do that?
In our project a KeyValueMaps are used to store the basic auth info at org level. The authorisation information is retrieved using the KeyValueMap policy and added as the basic auth header to the request message.
See if this approach works for you.
To add Basic Authentication header for your service callout, you can use an 'AssignMessage' policy that sets the 'Authorization' header in the 'GeocodingRequest' as follows:
<AssignMessage enabled="true" continueOnError="true" async="false" name="AssignAuthorizationHeaderPolicy">
<IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
<AssignTo createNew="true" transport="http" type="request">GeocodingRequest</AssignTo>
<Add>
<Headers>
<Header name="Authorization">Basic YourAuthenticationHeader</Header>
</Headers>
</Add>
</AssignMessage>
Once you have created this policy, you will need to attach it in the request flow before the serviceCallout in the proxy.xml as flows:
<Step>
<FaultRules/>
<Name>AssignAuthorizationHeaderPolicy</Name>
</Step>
<Step>
<FaultRules/>
<Name>GeoCodeClient</Name>
</Step>
to add to what's already been said, if you need base64 encoding (and you probably will if you're using Basic Authorization), you'll need to do script callout. For instance, you can use the following Python:
import base64
if (client_secret is not None):
data = client_id + ":" + client_secret
header_value = base64.b64encode(data)
header_value = "Basic " + header_value
flow.setVariable("request.header.Authorization", header_value)
JS will be a little trickier since you need to include appropriate libraries, but I'm sure SO has plenty of more examples to follow for that.
Using Key Value Map to store sensitive data in a secure way
Step 1)Use below API to Create/Update the key Value maphttps://api.enterprise.apigee.com/v1/o/{orgname}/environments/{env}/keyvaluemaps Body:-{
"entry" : [ {
"name" : "basic_auth_system1",
"value" : "Basic XXXXXXXXXXX"
} ],
"name" : "system1_credentials"
}
Step 2) Policy used to lookup The key Value map
<KeyValueMapOperations enabled="true" continueOnError="false" async="false" name="keymap_get_credentials" mapIdentifier="system1_credentials">
<DisplayName>keymap_get_credentials</DisplayName>
<FaultRules/>
<Properties/>
<ExpiryTimeInSecs>-1</ExpiryTimeInSecs>
<Get assignTo="basic_auth_system1">
<Key>
<Parameter>basic_auth_system1</Parameter>
</Key>
</Get>
<Scope>environment</Scope>
</KeyValueMapOperations>

Spring Security: multiple security context but returns wrong authentication-failure-url

I am using Spring Security version 3.1.2.
Here is the configuration:
<http pattern="/embedded/**" auto-config="true" use-expressions="true" access-denied-page="/embedded/login.htm">
<intercept-url pattern="/embedded/login-embedded.html" access="hasRole('ROLE_AUTHENTICATED')"/>
<intercept-url pattern="/embedded/**" access="permitAll"/>
<form-login login-page="/embedded/login.htm"
authentication-failure-url="/embedded/login.htm?error=true"
default-target-url="/embedded/login-embedded.html" />
<logout logout-success-url="/embedded/index.html"/>
</http>
<http auto-config="true" use-expressions="true" access-denied-page="/login.htm">
<intercept-url pattern="/login-success.html" access="hasRole('ROLE_AUTHENTICATED')"/>
<intercept-url pattern="/**" access="permitAll"/>
<form-login login-page="/login.htm"
authentication-failure-url="/login.htm?error=true"
default-target-url="/login-success.html"/>
<logout logout-success-url="/index.html"/>
</http>
I POST data to a Spring MVC controller which calls a service to validate a captcha. If that passes it forwards it to the j_spring_security_check RequestDispatcher.
Here is the relevant part of the controller:
#RequestMapping(value ="/embedded/login.htm", method = RequestMethod.POST)
public String authenticateCaptcha(HttpServletRequest request,
HttpServletResponse response,
#RequestParam String verificationText) throws IOException, ServletException {
HttpSession session = request.getSession();
String sessionId = session.getId();
if (captchaService.validate(sessionId, verificationText)) {
request.getRequestDispatcher("/j_spring_security_check").forward(request, response);
return null;
}
return buildErrorRedirect(request);
}
My problem is that after captcha is validated and the request is forwarded to Spring Security and authentication fails there the error page it forwards to is /login.htm?error=true instead of /embedded/login.htm?error=true.
URL /j_spring_security_check doesn't match /embedded/** so authentication-failure-url="/login.htm?error=true" is used - the one from second configuration.
Similar question has been asked recently:
Spring security with two realms, first default-target-url is never invoked
And one of the creators of Spring Security answered it. I recommend reading it.
Another worthy piece of Stack Overflow:
Why does a forwarded request pass through filter chain again?

How to simulate the #PreAutorize tag in a integration test?

I have the following method in Spring MVC and using Spring Security:
#PreAuthorize("#phoneNumber == authentication.name")
#RequestMapping(value = "/{phoneNumber}/start", method = RequestMethod.POST)
public ModelAndView startUpgrading(#PathVariable("phoneNumber") String phoneNumber,
....
}
I manage to simulate authentication something like this:
public Authentication tryToAuthenticate(String accountName, String password) {
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(accountName, password);
return authenticationManager.authenticate(token);
}
But I dont know how to set up the authorization with #PreAutorize.
How can I set up my test context correctly such that I dont get access denied ?
org.springframework.security.access.AccessDeniedException: Access is denied
at org.springframework.security.access.vote.AffirmativeBased.decide(AffirmativeBased.java:83)
at org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:205)
The annotations ( #PreAuthorize, #PostAuthorize, #PreFilter, #PostFilter ) which support expression attributes to allow pre & post-invocation authorization checks are enabled through the global-method-authority namespace element.
You need to add following code in your application-servlet.xml or security xml file.
<security:global-method-security pre-post-annotations="enabled" >
<security:expression-handler ref="expressionHandler"/>
</security:global-method-security>
<beans:bean id="expressionHandler" class="org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler">
<beans:property name="permissionEvaluator" ref="permissionEvaluator"/>
</beans:bean>
Check spring-testcontext-framework and this post answering question very similar to yours.
Sounds like you want to declare a mock version of the bean that does the Authentication. You might need a test context.xml to declare it.
Maybe check this old post and the official documentation 16.3 Method Security Expressions for xml configuration.
I think you need to declare on your xml:
Also your method to authenticate token could be a hasPermisions(). Check 16.3.2 Built-In Expressions

Resources