How can a session variable be read in a Spring annotation expression, specifically #PreAuthorize - spring

I'm using the Spring Security #PreAuthorize annotation and it works when I use "known good" expressions that I've seen in the documentation or in other online examples. The example code isn't a real use case it's contrived.
The following expressions are all variations of ones that worked on a method or methods similar in format to the example method below them. These expressions aren't being used at the same time or even the same method they're just displayed here together for simplicity.
#PreAuthorize("hasRole('ROLE_ADMIN')")
#PreAuthorize("hasAuthority('PERM_XYZ') or authentication.principal.id == #ownerId")
#PreAuthorize("hasRole('ROLE_USER') and #someValue == 'testValue'")
public List<Item> getSomeItems(Integer ownerId, String someValue ) {
// code goes here
}
What I would like to be able to do is test a method argument against a session variable in the expression like this:
#PreAuthorize("hasAuthority('PERM_XYZ') or #someValue == session.someOtherValue")
public List<Item> getSomeItems(Integer someValue) {
// code goes here
}
I would have thought that accessing the session in an expression would be a basic task but I haven't found a single example online or even anyone asking how to do it.
I've tried all of the following and many more but they all generate exceptions:
#PreAuthorize("#someValue == #session.someValue")
#PreAuthorize("#someValue == session.someValue")
#PreAuthorize("#someValue == session.getAttribute('someValue')")
#PreAuthorize("#someValue == request.session.someValue")
#PreAuthorize("#someValue == request.session.getAttribute('someValue')")
Everything above is related to Spring Security and the #PreAuthorize annotation but those things really aren't central to question.
I'm aware of many alternatives to accessing the session and have already solved my use case but I'm still wondering if it's possible to access the session via expression in any annotation.
So... can the user session be accessed in a Spring annotation expression and if so how? Thanks.

The spring EL expression for the current is #session.
So you could use
#PreAuthorize("#userId == session.userId")
But this session is the current HttpSession and it has no property userId. According to spring el access you can use authentication or principal
Try
#PreAuthorize("#userId == principal.username")
presuming that the username is the userId...

Related

Spring security match Principal info with request param

So I'm trying to validate my endpoint without IFs and such things, only with PreFilter and I basically have the following
#GetMapping(value = "/books/{userId}")
#PreFilter("hasRole('ADMIN') or principal.userId == pathVar.userId")
public List<Books> getBooks(#PathVariable("userId" String userId) {
//do sth
}
My problem is getting the PreFilter to work, or actually to match the userId from the authenticated principal with the path variable so that each user can request only his own books.
Yes I know that I can remove the path variable and just do principal.getUserId() inside the method, but that means that I will need to have an if user is admin.
Okay after quite a few tries I have finally found out how to do it. In case anyone is interested:
#PreAuthorize("hasRole('ADMIN') or #userId == principal.userId")
This way if you are the admin you can get the books of any user, and if you are a user you can get only your own.

Spring Security SAML One Login Global Single Logout LogoutRequest Parsing Issue

I am implementing Spring Security SAML with One Login. I have set all the configuration files and meta data is set.
I am able to get login work and logout is working if I logoff from the same application I logged in. In this scenario from SAML IDP I get LogoutResponse and Spring Security is able to parse and process it.
http://localhost:8080/web/saml/SingleLogout?SAMLResponse=..............
Problem is when I login in two applications, currently I login to One Login admin console, there is a link to my app, I click on it and I am able to login directly in my application, now when I log off from One Login admin console, my application gets LogoutRequest.
http://localhost:8080/web/saml/SingleLogout?SAMLRequest=.........
Spring Security parses it fine and passes the object to a validation check logic.
org.springframework.security.saml.websso.processLogoutRequest(SAMLMessageContext context, SAMLCredential credential)
This method has following check.
// Make sure request was authenticated if required, authentication is done as part of the binding processing
if (!context.isInboundSAMLMessageAuthenticated() && context.getLocalExtendedMetadata().isRequireLogoutRequestSigned()) {
throw new SAMLStatusException(StatusCode.REQUEST_DENIED_URI, "LogoutRequest is required to be signed by the entity policy");
}
I tried to follow the trace but the context object's field inboundSAMLMessageAuthenticated is never set to true. The above check fails and exception is thrown.
In debug mode I explicitly chencged the value to true, it went ahead but there is one more issue.
In the same method there is another check.
try {
// Fail if NameId doesn't correspond to the currently logged user
NameID nameID = getNameID(context, logoutRequest);
if (nameID == null || !equalsNameID(credential.getNameID(), nameID)) {
throw new SAMLStatusException(StatusCode.UNKNOWN_PRINCIPAL_URI, "The requested NameID is invalid");
}
} catch (DecryptionException e) {
throw new SAMLStatusException(StatusCode.RESPONDER_URI, "The NameID can't be decrypted", e);
}
The method equalsNameId is as follows.
private boolean equalsNameID(NameID a, NameID b) {
boolean equals = !differ(a.getSPProvidedID(), b.getSPProvidedID());
equals = equals && !differ(a.getValue(), b.getValue());
equals = equals && !differ(a.getFormat(), b.getFormat());
equals = equals && !differ(a.getNameQualifier(), b.getNameQualifier());
equals = equals && !differ(a.getSPNameQualifier(), b.getSPNameQualifier());
equals = equals && !differ(a.getSPProvidedID(), b.getSPProvidedID());
return equals;
}
Here it fails on differ(a.getFormat(), b.getFormat())
Question
I am not sure is there something I am missing, kind of lost where exactly to check to tackle this issue.
My binding for Single Logout is HTTP-Redirect.
Would appreciate if pointers are provided. Let me know if more information is required.
Thanks for time.
Stack (Legacy Application):
Spring 3.0.6
Spring Security 3.1.2
Spring Security SAML 1.0.0
Tomcat 7.x
Having encountered this two years after the original post, I had to do some further research. I still have some reading to do regarding the SAML specifications, but I think I found an entry in SAML 2.0 Errata on the structure of the NameIDType which underlies the NameID and Issuer. All four elements in this type are optional. OneLogin appears to be following this document and does not send a NameID.Format in the SingleLogout request.
So, the inboundMessage has a nameID with a null format while the credential's nameID has a format of "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress". This is what causes the getFormat line to return false and fail the entire check.

How to enforce Grails command objects have been validated?

We use the following general pattern with Grails controllers and command objects
SomeController {
def someAction() {
SomeCommandObject co = SomeCommandObject.valueOf(params)
if(!co.validate()) {
// return bad request
}
someService.doWork(co)
// return ok
}
SomeService {
def doWork(SomeCommandObject co) {
notTrue(!co.hasErrors(), 'cant have errors') // Commons validation
// do actual work
}
}
Apparently, if co.validate() has not been called, .hasErrors() will always return false. Is there a better way to enforce that .validate() has been called before a command object is passed between application layers? We don't want to pass around invalid command objects but we don't want to force every new method to re-validate the command object either.
Note: We aren't using the default controller/command object creation pattern because we need to do some custom parameter map checking, we use a static valueOf method instead to create the command object. Answers that change that practice are also welcome.
EDIT: A little more info on why we aren't using the 'default' controller/command object creation. Specifically why we aren't doing ..
def someAction(SomeCommandObject co) {
}
We have a requirement to disallow random query parameters, eg. endpoint/object?color=blue. To do that we need access to the parameter map in the command object to verify that it doesn't contain any 'unexpected' parameter keys. As I understand it, the default way would just create a member on the CO named color, and I don't see how to prevent arbitrary members using even custom validators. I'd happily entertain suggestions for doing so, thereby allowing us to use this default means.
Yes; what you can do is pass the command object as a parameter to the controller, and then the command will always be validated automatically.
Also, what you can do, is to make a filter or similar, so that you don't have to check for the hasErrors() each time, but handle all the cases in the same way (for example, by throwing an error, or returning with a specific response).
In an application we created, we had something like:
withValidCommand(cmd) {
// do work
}
Which worked pretty well. But maybe you can come up something even more elegant.
You should be doing this:
def someAction(SomeCommandObject co) {
if (!co.hasErrors()) {
someService.doWork(co)
}
}
By passing SomeCommandObject in as the argument grails will automatically populate it from params and validate. No need to do it manually.

Spring Security #PreAuthorize - Restrict certain roles by using Spring EL

Using Spring Security 3.1.3.RELEASE
So if there are a list of roles (over 10) and there is a need to block just ONE from accessing a Spring Controller method. Can this be done using Spring Expression Language, and avoid listing each and very accepted role?
For example, by including the Not sign.
#PreAuthorize("!hasRole('ROLE_FREE_USER')")
over listing all the roles like this
#PreAuthorize("hasAnyRole('ROLE_ADMIN','ROLE_PAID_USER','ROLE_PREM_USER',...)
I've looked at the documentation over here: http://static.springsource.org/spring-security/site/docs/3.0.x/reference/el-access.html
But there seems to be nothing on the NOT EQUAL to cases. Anyone face similar issue?
I'm pretty sure that NOT-sign (!) is supported in Spring Expression Language (SPEL). Naturally, it returns a boolean result.
An Example from the official documentation:
// evaluates to false
boolean falseValue = parser.parseExpression("!true").getValue(Boolean.class);
// -- AND and NOT --
String expression = "isMember('Nikola Tesla') and !isMember('Mihajlo Pupin')";
boolean falseValue = parser.parseExpression(expression).getValue(societyContext, Boolean.class);
Spring Expression Language didn't work for me in this case. Initially I tried with the following,
#RequestMapping("/post/edit")
#PreAuthorize("hasRole('ROLE_OWNER') AND !hasRole('ROLE_ADMIN')")
public String editPost(Model model, Principal principal, HttpServletRequest request, #RequestParam("postId") String postId) {
}
However, I had to recheck the Role from inside the method and redirect the page in case user has Admin Privileges.
#RequestMapping("/post/edit")
#PreAuthorize("hasRole('ROLE_OWNER')")
public String editPost(Model model, Principal principal, HttpServletRequest request{
//Admin Users does not have access to post edit page
if (request.isUserInRole("ROLE_ADMIN")) {
return TemplateNamesConstants.POST_WALL;
}
}
Do update this thread in case you found a better/alternate solution.

Spring MVC 3.0: Avoiding explicit JAXBElement<> wrapper in method arg

I have the following method and want to avoid having to explicitly show the JAXBElement<> syntax. Is there some sort of annotation that would allow the method to appear to accept raw MessageResponse objects but in actuality work the same as shown below? I'm not sure how clear that was so I'll say this: I'm looking for some syntactic sugar :)
#ServiceActivator
public void handleMessageResponse(JAXBElement<MessageResponse> jaxbResponse) {
MessageResponse response = jaxbResponse.getValue();
MessageStatus status = messageStatusDao.getByStoreIdAndMessageId(response.getStoreId(), response.getMessageId());
status.setStatusTimestamp(response.getDate());
status.setStatus("Complete");
}
You can use the unmarshalling transformer described in the reference guide here.
Adapted from the documentation (usable in a chain):
<si-xml:unmarshalling-transformer unmarshaller="unmarshaller" />
This should give you a domain object as the message payload.

Resources