Spring Security SAML: Extract Attributes from a saml2p:Response as user attributes - java

I have been digging into spring security yaml a little bit yesterday to make it work with Okta SAML. Logging in works, but the response XML contains user attributes that apparently cannot be extracted automatically into an attribute map. The response contains a fields like this
<saml2:Attribute Name="user.lastName" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified">
<saml2:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">
Surname
</saml2:AttributeValue>
</saml2:Attribute>
Once an authentication is successful, I would like to put those in the authentication information. When logging in via github/oauth, the OAuth2AuthenticatedPrincipal class has an attributes map, however the Saml2AuthenticatedPrincipal only features a name.
What would be the correct way to solve this?
Right now I am thinking of a custom AuthenticationSuccessHandler that populates a custom Saml2AuthenticatedPrincipalWithAttributes class which contains all the attributes by parsing the provided XML response (via .getDetails()) a second time (or put them into the session).
I have a hunch that this is probably not the spring way to do things and would love to get a second opinion. When googling around you mainly find examples of spring security saml, before it got merged into spring security, which seems to handle things a little bit different, as the mentioned classes do not exist anymore.
Thanks for helping everyone!

In the next release of Spring Security (5.4.0) you should be able to do something like this:
#GetMapping("/")
public String index(Model model,
#AuthenticationPrincipal Saml2AuthenticatedPrincipal principal) {
String emailAddress = principal.getFirstAttribute("emailAddress");
model.addAttribute("emailAddress", emailAddress);
model.addAttribute("userAttributes", principal.getAttributes());
return "index";
}
For now, I don't know a better workaround than yours.

Related

Spring Thymeleaf Html form + Crud Operations

I'm new to Spring Framework and currently trying develop an simple application were restaurant owners can add a dish to a main database through logging in and imputing the dish data into a Html form (with Thymeleaf). (dish name, price, gluten free = true etc.)
App users should then be able to search the main database based on their location and dietary requirements etc.
I'm trying to learn through Youtube tutorials however, all of them seem to use #RestController and #Requestbody and use Postman to send JSON requests. I'm having difficulty adapting this to my Html form.
I have used the #controller annotation for simple 'save' methods but when I research 'how to include a foreign key in the data added to the data base' (i.e. include 'restaurant id' as foreign key in the 'dish' class), all of the tutorials use #RequestController and JSON requests in Postman.
As I'm trying to build a usable application sending requests in Postman doesn't seem like a good solution.
I think the problem I'm according to other posts is the following:
"If you use template engine like Thymeleaf it will not work with #RestController because of #ResponseBody which included in this annotation" – Sonique
"#ResponseBody makes the returned objects to something that could be in the body, e.g. JSON or XML" – Martin Thoma
I've tried removing #RequestBody and changing it to #RequestParam but I get errors.
Are there any work-arounds to my issue? E.g. using a different file format for the 'add new dish' form or not using Thymeleaf?
Any help/advice would be very much appreciated!
If you want to use HTML with Thymeleaf and the class should annotate by #Controller and the method parameter that handle the object from the FORM you want to control should be #ModelAttribute("").

Spring Boot authorisation in services

I have a Spring Boot application which uses Spring Security to Authenticate and Authorise requests using a JWT. However, some requests should only be able to be executed by a particular user. For example:
GET /users/{id}/orders should only return the list of orders if {id} is the current user or the current user is ADMIN
PUT /orders/{id} should only edit the order if the its payer is the current user
PUT /representation-requests/{id}/accept should only work if the current user is the target of the representation request
Because of the usage of JWTs, the way I get the current user's ID is by
String userId = ((DecodedJWT) SecurityContextHolder.getContext().getAuthentication().getDetails()).getSubject();
I've implemented this in the various methods of the services responsible for handling each API call. My question is if there is a more general way to do this using Spring Boot and Spring Security? Or is this not a standard use case?
I have looked at #PreAuthorize annotation in controllers, but it does not suite my needs as the URL parameters are not enough to check the nested entities. #PostAuthorize in controllers seems closer, but because of the JWT I couldn't get it to work (and it also seems a bit clunky to do long code in annotations, not sure it is better than doing it in the service itself). I appreciate a pointer in the right direction.
You'll have to play around with it a bit (I can't give you 100% specifics for JWT), but you basically could use SpEL to achieve what you want. (I'm unsure why you think #PostAuthorize could be a better fit, though)
Either like so, for simple checks (# denotes a method parameter)
#PreAuthorize("principal?.id == #id")
public List<Order> ordersForUsers(Integer id) {
// do someting
}
Or like so, delegating to a custom bean (# denotes a bean name, hash the method param, 'admin' the role you want, or any other parameter, really)
#PreAuthorize("#yourBean.hasPermission(principal, #id, 'admin')")
public List<Order> ordersForUsers(Integer id) {
// do someting
}

CAS redirect to URL on succesfull login

Here a solution is described to handle redirects to a custom URL based on a condition via use of AccessStrategy.
This however is part of the unauthorized login logical flow therefore results into a still not-logged in user arriving at the end url we redirect to. (via getUnauthorizedUrl)
If we want to redirect the user based on a condition, say via injecting an action to the webflow, how can we manipulate the return URL to be changed into a custom one?
WebUtils.getService(requestContext) include getters of the source/originalUrl but no obvious way to set/manipulate said value through an action bean.
p.s. Currently using CAS version 5.3.x
Responses for normal web applications from CAS are built using WebApplicationServiceResponseBuilder.
If you examine this block you will find that the final response is built using WebApplicationServiceResponseBuilder bean. It is only created conditionally, if an existing bean is not already found in the context by the same name. So to provide your own, you just need to register a bean with the same name using your own #Configuration class.
#Bean
public ResponseBuilder<WebApplicationService> webApplicationServiceResponseBuilder() {
return new MyOwnWebApplicationServiceResponseBuilder(...);
}
...and then proceed to design your own MyOwnWebApplicationServiceResponseBuilder, perhaps even by extending WebApplicationServiceResponseBuilder and overriding what you need where necessary to build the final redirect logic conditionally.
To learn about how #Configuration classes work in general, you can:
Review this post
or this post
or consult the documentation for Spring and/or Spring Boot.

Java Spring - check if user is allowed to view the page or not

i have some pages that can be viewed only if the user is logged in.
So, in every controller that i want to page that i want to be restricted i put this code:
Boolean checkLogin = (Boolean) request.getSession().getAttribute(adminSesName);
if(!checkLogin) return "redirect:/";
All my controllers extends a Base Controller, and here i was thinking to put the code that makes the ckeck if a user is logged in or not, but i don't know where to put it.
Do you have some suggestions how to do it?
Thanks!
Spring security would be the best option, he is right. A much easier alternative ( not recommended though) is to implement a filter to do the check for you and you can map it on all url with a wanted structure:
https://www.mkyong.com/spring-mvc/how-to-register-a-servlet-filter-in-spring-mvc/
Use spring security for authentication check. You can start from here spring security tutorial

Dynamic URL from database and MVC in JSP

I am learning JAVA and Spring Framework. I wanted to know that is it possible in java to create Dynamic URL in spring framework using values from url and fetching from database.
I am trying to make URL Shortner in Java and I will need to lookup for url's short code in my database and as we all know, url shortner will look like "url/ShorTCode" and my script will look for "ShorTCode" keyword in database and will redirect to associated weblink.
So I wanted to know that is it even possible in JAVA and Spring? And one more thing, if I make something like this "url/yt/VIdeoCode" or "url/fb/UserProfile"
So it will look at yt object which will redirect to youtube link only and fb object which will redirect to facebook user profile.
I want to clarify that I am still learning JAVA, JSP and Spring but I want to keep this thing in my mind while I am learning so I can focus on some particular things.
Thank you all fro helping me.
If you're asking how your controller could respond with a dynamic redirect, the answer is either:
(1) Have the controller return a "redirect:" result instead of view name. It must be followed with an absolute url, and behavior might depend on your spring version and configuration, but basically it looks like this:
#RequestMapping(...)
public String myMethod(){
String url=... // database lookup, e.g. "http://myUrl"
return "redirect:"+url;
}
(2) Less elegant but sometimes useful: get direct access to the response. If your controller method has a parameter of type HttpServletResponse spring will automatically inject it. So:
#RequestMapping(...)
public String myMethod(HttpServletResponse resp){
...
response.sendRedirect(...)
}

Categories