Hi I am having trouble understanding how to properly use
#Security.Authenticated(Secured.class)
statement within the PlayFrameWork.
I am trying to make sure that only authenticated users can access their accounts. Following from the example provided in the Play docs , it appears that their authentication allows a single user to access every users' account once logged in - rather than just their own.
Normally I would have assumed that you simply get the session value within the action say,
public static Result viewAccount(String account) {
//get session value and check against account name
}
However the Docs use another route:
They define a class
public class Secured extends Security.Authenticator {
#Override
public String getUsername(Context ctx) {
return ctx.session().get("username");
}
#Override
public Result onUnauthorized(Context ctx) {
return redirect(routes.Application.login());
}
}
Apparently now simply using the statement:
#Security.Authenticated(Secured.class)
before an action insures that it is authenticated. But by my understanding and testing this does not block users from logging in to anyones account as it passes simply if the a session value exists - and not if it matches.
How to fix this?
Should I just directly compare the session value? What is the purpose then of
#Security.Authenticated(Secured.class)?
Thanks
(Edit)
to clarify:
I want to allow users to be authorised only to see their own accounts and not others.
So when the statement
#Security.Authenticated(Secured.class)
is used, I would like it to not only check for the presence of a session id but check that it matches an account
Maybe I don't understand your question, but in the documentation you see how to create a login form and actually perform the authentication. Only after that happens does the email session value exist. If that value is in the session, then the user must have logged in with the proper credentials and must have been authenticated.
Of course, if User A knows the credentials of User B, nothing can help that.
If instead you are concerned about authorization, deciding who can see what upon authentication, then you can do a lot of things including leverage OAuth support in Play proper or a plugin like this one.
Related
I have a Spring Boot REST API that I'm building. Im slightly stuck on the correct way to design my API in a way that protects each individual users' data. For example, consider the following database relations:
User -> (Has Many) Projects -> (Has Many) Tasks. (A User has-many Projects, and a Project has-many tasks).
For example, if I design my endpoints in the following way:
GET /api/v1/projects/{projectId}
POST /api/v1/projects/{projectId}/tasks
Just as a simple example for the above, how can I make sure, when creating new tasks for a certain project, that the project belongs to the logged in user?
Currently, I am using JWT tokens via Spring Security as my authentication strategy, and included in the payload of the token I have my Users' id. So with every request I can retrieve the user, but surely that's incredibly inefficient to be making so many requests to the database and check if the user actually has a given project.
Some solution I was thinking about is to simply have endpoints designed like this:
/api/v1/users/{userId}/projects/{projectId}/tasks
And then I can use the user id in the JWT payload and compare it to the user id in the request parameter. But then that would mean with every new relation in my database, the length of the url is going to be massive :) Also I guess it would mean all the business logic would be inside the User service for the whole application, right? Which seems a little odd to me... but maybe I'm wrong.
Im not sure if thats an issue or not, but just trying to design the API to be as elegant as possible.
Thanks again!
Checking if the user has permissions to a project on every request is the correct solution. Consider cases when many other applications / users are calling your API. You want to make sure that your API is as secure as possible and cannot be manipulated from the frontend.
To make it more efficient you should have a way/query to check associations in your database like a simple query that returns true/false which should be quicker than retrieving all the data and comparing in Java code.
And when possible combine multiple database queries into one, like for one of your examples:
GET /api/v1/projects/{projectId}
in this case, don't run a query to get a user's information and a query for the requested project. Instead you could do a single query with a join between the user's table and the project table which should only return a project if the user is associated with it. The best way really depends on how your database is structured.
Adding a user id into the API URL is just redundant information. Just because the user id in the token matches the user id in the URL doesn't mean the user has any kind of permissions to any project.
Another solution to be avoided is to include the user's project ids in the JWT token which you can then compare without making a database request. This is bad for several reasons:
The token should only have required information for the user to access the API, it shouldn't have business logic
Depending on how much business logic you store in the token the token can become large in size. See this post for a discussion on size limits: What is the maximum size of JWT token?
If there is a way for the someone other than the user (like admin) to add/remove a user's association to a project then that change will not be reflected in the token until the token's data is refreshed
EDIT:
On the spring side I have used the #PreAuthorize annotation before to handle these types of method checks. Below is pseudo code as an example.
#RestController
public class MyController {
#PostMapping
#PreAuthorize("#mySecurityService.isAllowed(principal, #in)")
public SomeResponseType api1(SomeRequestType requestData) {
/* this is not reached unless mySecurityService.isAllowed
returns true, instead a user gets a 401/403 HTTP response
code (i don't remember the exact one) */
}
}
#Service
public class MySecurityService {
/*
Object principal - this is spring's UserDetails object that is
returned from the AuthenticationProvider. So basically a Java
representation of the JWT token which should have the
user's username.
SomeRequestType requestData - this is the request data that was
sent to the API. I'm sure there is a way to get the project ID
from the URL here as well.
*/
public boolean isAllowed(Object principal, SomeRequestType requestData) {
/*
take the user's username from the principal, take the
project ID from the request data and query the database
to check authorization, return true if authorized
make this check efficient
*/
return false;
}
}
The annotation and the security service can then be applied to multiple methods. You can have many different security services depending on what your are checking.
There are other ways available too https://www.baeldung.com/spring-security-method-security and this has to be enabled in spring's configuration (also explained in the link).
Hi so if I understood it correctly you want to automatically assign the task that is going to be created with "POST /api/v1/projects/{projectId}/tasks" to the current logged in user.
You could try to add a Parameter 'Principal principal' to your rest controller. The Principal is the user that is sending the request.
After you have your Prinicipal, you could write a simple convert method(for example: convertPrincipalToUser(Principal principal) which returns you the user. Finally you can add your user to the corresponding task)
Here is some more information about it:
https://www.baeldung.com/get-user-in-spring-security
Working with Spring / Spring security on a small project at the moment and having difficulty implementing this feature. Ideally I only want user1 to view the details of user1 and not those of 2,3 or 4 for example.
I've implemented Spring Security with Roles and understand that I can retrieve a UserDetails object or a principle, I'm not sure exactly but I know I can retrieve the details of the current logged in user using one of, what appears to be many methods.
This is what I'm currently using as a proof of concept when we go to the Admin/home page:
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
Gamer gamer = gamerService.findGamerByEmail(auth.getName());
System.out.println("Auth: " + auth.getName());
System.out.println("Gamer: " + gamer.getName() + gamer.getId() + gamer.getEmail());
The security config takes care of whether or not the current user can access because of the roles assigned to it.
I believe I should be able to go to the url of /mysite/viewUserDetails and have that page display information of the current user but I cannot find any examples of this, I've found plenty of example that prove a logged in user can view a page but none that specify checks in place to ensure user1 can only view user1's details.
On an older page I do this to display information for a particular user but I understand this to be bad practice-
<a th:href="#{/gamer/{gamerid}/games/excited (gamerid=${gamer.id}) }">
*Worth noting that this isn't using any form of login/registration to pull out this info, I'm simple using the id I pass in as part of the DB query.
It maps onto :
#RequestMapping("/gamer/{gamerid}/games/excited")
public String getExcited(#PathVariable final Long gamerid, Model model){
addGamerListAttributes(model, gamerid, "EXC");
return "games";
}
So my question becomes, and I hope you can point in the right direction, How can I implement a solution where a user can only view his/her details and how should this be represented via the form and connecting controllers as passing ids in the url is kinda ugly (I could use a guid but...)
Thanks so much in advance.
It's actually quite an easy choice. Either you have an entry point like:
#RequestMapping("/gamer/{gamerid}/games/excited")
and you manually check that the user in session can access the requested resource, or you have something like
#RequestMapping("/my-games")
that automatically reads the user id from the security context.
More than a security choice, I'd pick one depending on code reuse and future use-cases (for example the same page/set of pages can be seen by more than one user).
Have a look at #PreAuthorize annotation. It is possbile to annotate given endpoint with it and create custom logic in a bean. Then you can use custom method to allow or disallow the endpoint to proceed :
#Controller
public class HomeController {
#Autowired
private AuthenticationService authenticationService;
#RequestMapping("/gamer/{gamerid}/games/excited")
#PreAuthorize("#authenticationService.hasAccess(#gamerid)")
public String getExcited(#PathVariable final Long gamerid, Model model){
addGamerListAttributes(model, gamerid, "EXC");
return "games";
}
}
Service class :
#Service
public class AuthenticationService {
public boolean hasAccess(String tgamerid) {
//implement logic here
return true;
}
}
Method hasAccess in the AuthenticationService should return boolean. #PreAuthorize will be launched before controller handler method is invoked. The controller above is just an example. You can pass Authentication object in SPeL expression in #PreAuthorize annotation to service method or get it from security context inside service class, to implement logic which fits your needs. More information can be found here and in Spring Docs.
I have some JSF 2 applications that are currently working with a JSF Secutiry LoginModule (auth-method = FORM). But authentication will be done now through a new way, that means I'll have to manually program the authentication interactions.
That's fine, but now I have problems setting the roles. I couldn't find where I can set the Principals, or get subject to do it, or get shared state to put "javax.security.auth.principal" and "javax.security.auth.roles" variables.
Is there a way to do it? Here is a sample of my actual Bean code.
Thanks in advance!
#ManagedBean
#ViewScoped
public class PrincipalController extends AbstractController implements ExcluirRascunhoService.Presenter {
// has get and set
#ManagedProperty(value = "#{autenticacaoController}")
private AutenticacaoController autenticacaoController;
#PostConstruct
private void init() {
try {
// a previous application redirected the user here,
// giving two parameters, including a valid and calculated HASH
// to be passed to authentication
Map<String, String> requestMap = getContext().getRequestParameterMap();
String user = (String) requestMap.get("login");
String hash = (String) requestMap.get("hash");
// this will do the authentication, communicating with a
// webservice and passing these data so the webservice can
// authenticate the data, telling me if the user is Ok
autenticacaoController.authenticate(user, hash);
// do the other things if authentication doesn't throw an exception
// I should now fill all user's Roles accordingly to my database
// I get them correctly, but how to set them into the JSF Roles?
} catch (AuthenticationException e) {
// catch and quit the page
}
}
}
You can't do it with JSF alone. Basically, JSF only provide a utility to get the user and it's role directly from the JSF interface. So, if you want to access the principal user and it's roles from your application you must first authenticate the user.
To authenticate your user you can use an third party solution like JAAS or Apache Shiro to setup the realm, roles and control the authentications of your application.
You can also roll your own authentication layer, which may not give you the possibility to use some useful JSF utilities like getting the principal user or it's roles directly from the realm (note that your custom layer will probably provide other ways to get these values), but will also provide a custom way to do the authentication required.
I have found a pretty nice tutorial about JAAS authentication layer (in portuguese) that may help you setting up a authentication layer.
Wish you good luck and feel free to ask if you have any doubts about what I've said.
I'm trying to come up with the best way to do my own authentication in our Java REST API using the Jersey framework (v2.5.1) running on Tomcat 7.
The API will be accessed through our iOS application. In the iOS application we use Facebook authentication (using the Facebook SDK), and then we use the access token in every call to the REST API.
#Provider
#Priority(Priorities.AUTHENTICATION)
public class AuthenticationFilter implements ContainerRequestFilter
{
#Override
public void filter(ContainerRequestContext requestContext) throws IOException
{
// Extract the access token from the HTTP header
// Look up in the database to see if we have a user with that token
// If there is a user found, proceed
// If we can't find a user, we are going to send the token to Facebook to get the user details. If the token is invalid, we throw an exception. If it is valid, we look up if we can match the Facebook details with an existing user. When we can't match, we create a new user.
}
}
This filter will be executed in every API request.
My questions:
Is this a correct workflow?
Should we contact Facebook every time to validate the token? This will cause a lot of overhead.
This filter is executed for every request. How can we exclude certain urls (some resources won't require authentication)? I was thinking of holding a set of urls in the filter class and see if the requested url matches one of the defined public urls (if so, don't do the authentication).
Thanks!
I think it might be better if you can provide this option to your user:
login / login using facebook account.
So you dont have to contact Facebook unless user choose to login using their FB account.
also, the authentication should be session based. session information can includes user, session key, valid time range, maybe source IP too. once the user has successfully logged in, a session is generated. then for every request you only have to check if the session key is still valid.
How to get any http session by id or all currently active http sessions within web application (Java 2 EE) in an elegant way?
Currently I have a WebSessionListener and once session was created I put it in ConcurrentHashMap() (map.put(sessionId, sessionObj)), everything ok, I can retrieve HTTP session from that map in any time by session id, but it looks like the HttpSession objects will never finalize... Even session was invalidated the map still reference on invalidated session object... Also I have read this article and it looks like the WeakHashMap is not acceptable in my case...
In other words I need a possiblity to look in any HttpSession even get all currently active HttpSession and retrieve some attributes from there...
Please advice somebody :)
Update
I need to access HttpSession objects because of follwoing reason:
Sometimes user does some actions/requests that may impact the work of another concurrent user, for example admin should disable user account but this user currently working with the system, in this case I need to show a message to admin e.g. "user XXX is currently working with the system" hence I need to check if any HttpSession which holds credentials of user XXX already exists and active. So this is whay I need such possibility to get any http session or even all sessions.
My current implementation is: SessionManager which knows about all sessions (ConcurrentMap) and HttpSessionListener which put/remove session into SessionManager.
I was concerned about memory issues that may occure and I wanted to discusse this with someone, but currently I am clearly see that everything should works fine because all invalidated session will be removed from map when sessionDestroyed() method will be called...
Many thanks for your replays, but now I understood that problem was just imagination :)
As per your clarification:
Sometimes user does some actions/requests that may impact the work of another concurrent user, for example admin should disable user account but this user currently working with the system, in this case I need to show a message to admin e.g. "user XXX is currently working with the system" hence I need to check if any HttpSession which holds credentials of user XXX already exists and active. So this is whay I need such possibility to get any http session or even all sessions.
For this you actually don't need to know anything about the sessions. You just need to know which users are logged in. For that you can perfectly let the model object representing the logged in user implement HttpSessionBindingListener. I of course assume that you're following the normal idiom to login/logout user by setting/removing the User model as a session attribute.
public class User implements HttpSessionBindingListener {
#Override
public void valueBound(HttpSessionBindingEvent event) {
Set<User> logins = (Set<User>) event.getSession().getServletContext().getAttribute("logins");
logins.add(this);
}
#Override
public void valueUnbound(HttpSessionBindingEvent event) {
Set<User> logins = (Set<User>) event.getSession().getServletContext().getAttribute("logins");
logins.remove(this);
}
// #Override equals() and hashCode() as well!
}
Then somewhere in your admin app, just obtain the logins from ServletContext:
Set<User> logins = (Set<User>) servletContext.getAttribute("logins");
Generally speaking, your servlet container will have its own session manager, which is responsible both for maintaining the lifecycle of the sessions, and associating incoming requests with the appropriate session (via cookies, anchor parameters, whatever strategy it wants).
The elegant way to do this would be to hook into this session manager in whatever way it allows. You could subclass the default one, for example, to allow you to get access to arbitrary sessions.
However, it sounds like what you're doing belies an underlying problem with your architecture. The data contained within a session should be specific to that session, so in general you shouldn't need to look up an arbitrary one in order to provide the standard logic of your web application. And administrative/housekeeping tasks are usually handled for you by the container - so again, you shouldn't need to interfere with this.
If you gave an indication of why you want access to arbitrary sessions, chances are that an alternative approach is more suited to your goals.
Andrzej Doyle is very right. But if you really, really want to manage your own list of sessions, then the way to connect to your container is via the HttpSessionListener - example code.
The listener is called whenever a new session is created, and importantly, it's also called when a session is destroyed; this will allow you to mimic the container's session bookkeeping.
You use your web.xml to register your session listener as a lifecycle listener for your your app.
You can communicate your session list with other processes in the container using the ServletContext, or you can cook up a more dirty scheme using e.g. static class fields.