There are many good resources and literature on how to set up a a JAX-RS API. However, I have yet to find any resource that properly describes how to do security restrictions towards specific resources and methods. For example, given a resource PictureResource, only the uploader of the picture (and an admin) should be able to delete or change properties related to the picture, while anyone should be able to view the picture. The admin restriction is fine as it can be solved by roles, however the uploader would depend on the context of the call. A token identifying the user would then describe who is making the call. This can be solved through a ContainerRequestFilter.
#Path("pictures/{pictureId}")
public class PictureResource {
#GET
public Response getPicture(#PathParam("pictureId") final int pictureId) {
// Get the picture, available for all users.
}
#DELETE
public Response deletePicture(#PathParam("pictureId") final int pictureId) {
// Delete the picture, only available for the uploader of the picture and admins.
}
// ...
}
What would be the JAX-RS way of solving this issue? I'm assuming this can be solved by annotations, but is is rather vague to me how to do this. Would another approach be to dynamically assign the user a pictureOwnerRole depending on the context of the call?
The problem is discrete resource access control. You need a standard way to describe the resource being accessed in terms of ownership. Who owns the resource, and who has been granted scoped authority over it.
The problem is that this is very domain specific. Resource grouping and ownership requires the ability to lookup a resource instance or associated metadata and determine ownership/access requirements.
I don't know of any security frameworks that provide a standard framework or annotation for this.
You could place pictures into a directory structure and use directory access control to determine what users have what access to the resources.
Something like #Secured("ownerDecider=PictureInspector.class") would be how I would approach it. The AccessDecisionVoter or AfterInvocationProvider in spring security could then use the provided strategy for discerning ownership, and restrict access accordingly.
Related
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
}
I have a REST API, that should use Keycloak for authorizing incoming requests. I have problems with configuring desired scopes in my API.
In Keycloak I defined a client for my API, a client for the calling service and two users. Both users have some realm roles. The client for my API defines a resource, some scopes (eg read, delete), policies and permissions.
One permissions allows an user with the admin role to perform scopes read and delete on the resource. The other allows an user with the monitoring role to read the resource.
I understand that the path of my endpoint will be checked against the path defined in the Keycloak client resource.
My concrete problem here is that I don't know how to tell my endpoint, which scopes he needs.
In the end I want to have multiple endpoints with the same path but different scopes (for each HTTP method).
Here is my example config:
quarkus.http.port=8080
quarkus.resteasy.path=/api
quarkus.oidc.auth-server-url=https://myUrl:8443/auth/realms/enaq
quarkus.oidc.client-id=rest-api
quarkus.oidc.credentials.secret=secret-string
quarkus.oidc.enabled=true
quarkus.application.name=keycloak-test-api
quarkus.application.version=0.0.1
quarkus.keycloak.policy-enforcer.enable=true
Here is an example implementation:
#Path("/measurements")
public class MeasurementResource {
#Autowired
MeasurementService delegate;
#GET
#Path("/{id}/{from}/{to}/{resolution}")
#Produces(MediaType.APPLICATION_JSON)
public MeasurementHistory getMeasurementHistory(#PathParam("id") String deviceId, #PathParam("from") Long from,
#PathParam("to") Long to, #PathParam("resolution") Integer resolution) {
return delegate.getMeasurementHistory(from, to, resolution, deviceId);
}
}
In my example my permissions will be evaluated, but only the admin gets access. The monitoring user will be denied, although he should be able to read.
In order to define the exact scopes that should be protected and map them to the appropriate HTTP verb, you should configure the policy enforcer as documented here.
There is no documentation on Quarkus side yet that shows how to map that to properties (application.properties) but it should be quite trivial to do so. You can grab an example from here.
Another option is to set the quarkus.keycloak.policy-enforcer.http-method-as-scope=true. By doing that, your resources would be associated with scopes such as GET, POST, DELETE, etc. And the policy enforcer will enforce access accordingly without forcing you to map paths in your configuration file.
I have an application where single user can work in contexts of multiple companies. We call such a connection (user<->company) a permit. Every one of this permits can have different sets of permissions/roles. We want user to login just once and then he can simply change permits within application without need to enter password again.
Till now we had only one application and kept this whole permission model in our own DB. Unfortunately now we have to support second application which should inherit those permits. I was wondering wether is possible to move that model to keycloak so we don't have to replicate it to every single db and keep it in sync manually.
I have searched keycloak documentation regarding this topic but have found no information att all, which seems quite odd, because I don't think we are the first one working with multiple context application.
So now I'm asking is it possible to configure our model in keycloak and if so, how to do it? Eventually are there different options? I guess that I can provided that model as a claim with json structure but that doesn't feel right to me. I was thinking about custom IDP which could provide such claims based on DB so there no spelling errors and less repetition but I feel there should be a better way.
You could try to write your own Keycloak provider (SPI). There is a built in mechanism that allows you to expose REST endpoint on the Keycloak: https://github.com/keycloak/keycloak/tree/master/examples/providers/domain-extension
That REST could be called with authorized context only for example by passing Access-Token (Authorization header with Bearer value). On the provider level (through implementation of: org.keycloak.services.resource.RealmResourceProviderFactory and org.keycloak.services.resource.RealmResourceProvider) you have access to user's Keycloak session and object UserModel like in the following code:
AuthenticationManager.AuthResult authResult = new AppAuthManager().authenticateBearerToken(keycloakSession, keycloakSession.getContext().getRealm());
UserModel userModel = authResult.getUser();
UserModel class has methods for getting and setting attributes, so some information that indicates the current permit/company ID can be stored there. You can use REST methods exposed on the Keycloak to modify the model within the 'session' (represented by Access-Token).
The Github example shows also how to use another Keycloak provider (ex. built-in JPA provider) from you custom provider's level, so using that approach you could try to connect to the database with your permits/company informations. Of course the datasource representing you database should also be registered as Keycloak datasource.
I've a requirement to build a Java based web application where a resource should be available only when all the authorized users of that resource are logged in. Also, if any authorized user logs out, the resource should no longer be available to any of them.
The resource could be of any type(html pages, pdf documents, spread sheets etc.,)
Is there any existing authentication standards/protocols that supports this type of requirement or I've to build this from scratch?
the resource should be available only when all the authorized users of that resource are logged in. Also, if any authorized user logs out, the resource should no longer be available to any of them.
Once you have given access to the resource to an user, this user will be able to download / take screenshots / save / record the resource, no matter if it's a PDF document, an image, an audio file. I don't know the context and the goal of what you're trying to build, but you should know that it will be insecure in any case.
Even putting this consideration aside, you'll need a real-time solution. Once the user has loaded the page containing the resource, you need to be able to hide or deny modification rights to him. This means you have to use something like WebSockets or Ajax Polling on the client side to have the frontend know when your server considers that not all the required users are online, and that the access to the resource should be "denied". But once more since this is client-side code it can easily be changed or altered, the requests it is sending can easily be blocked by the user, so it is once again inherently insecure.
I'd suggest giving a little bit of context here and describing what is the problem you're trying to solve, because most likely there's a more reasonable solution to solve it.
If what you need to do is to deny modification rights if not all the "resource owners" are online, it is more easily doable since the modifications will happen on the server side. In this case, a solution using WebSockets could quite easily be implemented but I don't know a library or framework that does such a thing. Most likely you will have to build it yourself.
If you're not constrained to use a specific web framework, feel free to try the following filter based implementation for jersey. Note that you still need to add a fair amount of custom code for handling the logic of "Collective authentication" as jersey only provides the basic tools required for this, and it doesn't explicitly implement the whole concept. Here's how you could do it, on a high level:
class AuthorizationProvider {
public void authenticate(ContainerRequestContext requestContext) {
// Here you would need to query your database to get the Collection of Users belonging
// to the "Collective" Role. You would then check if they are all logged in.
// A really abstract version would look like this, assuming you've already queried the DB
// and have a reference to the above mentioned Collection.
if (collectiveUsers.size == collectiveUsers.stream().filter(User::isLoggedIn).count()) {
return true;
}
return false;
}
}
class AuthorizationRequestFilter implements ContainerRequestFilter {
private final AuthorizationProvider authorizationProvider;
#Override
public void filter(ContainerRequestContext requestContext) {
if (authorizationProvider.authenticate(requestContext)) {
// serve whatever it is you want to serve if all required users are logged in
} else {
// otherwise reject the request
requestContext.abortWith(Response
.status(Response.Status.UNAUTHORIZED)
.entity("Resource available only after collective login")
.build());
}
}
}
#ApplicationPath("/")
class MyApplication extends ResourceConfig {
public MyApplication() {
// Register the filter
register(AuthorizationRequestFilter.class);
}
}
Apart from this, you would also need to handle the Login part.
You would assign these specific users the Collective role, and you would mark them as logged in, whenever they successfully pass through login authentication.
If all the above conditions are met, you should be able to successfully serve your "Collective only" page, only when all "Collective" users are logged in.
This also covers the part where if either one of these users logs out, you store the state in your database (mark the Collective user with isLoggedIn = false). So from this point on, whenever somebody requests the page, it will return Unauthorized.
Conversely, you can also attempt to implement SSE (Server sent events) to actively update the frontend part, if somebody logs out. With this, the page will actively be disabled even if somebody has already managed to get it previously.
Container request filter source and example, for reference, jersey docs
I have implemented a REST application with some complicated authorization requirements.
Here's a summary
My customers purchase a proxy device called Collector that enables their home automation control to be centralized. My customers also purchase multiple home automation devices (let's call them HADevices) that report their metrics through the collector to my REST application.
An admin(who is my customer service rep), with role ROLE_ADMIN, should be able to look at any data from any Collector or HADevice. A customer, with role ROLE_USER role, should only be able to view data about the Collector or an HADevice that s/he owns.
The Collector, with role ROLE_COLLECTOR is the only role authorized to insert data i.e. create or update a resource in my REST service. Let's call this url /deviceMetrics (POST). A Collector can insert metrics for any HADevice associated with the customer. HADevices have no role and do not interact with my REST application. A Collector can only insert records to HADevices that have the same customer as the Collector.
I am using spring security 4.0 for authentication and #Secured annotation for authorization. However, I find that my code is cluttered with repetitive permission validations which take up a majority of my logic. The basic insertions and retrievals are pretty straightforward.
I want to use a PermissionEvaluator to centralize Access Control. I have to secure the following urls
GET /collectors/{id}/deviceMetrics - I retrieve the user from the
Principal and verify that the Collector with id={id} in my spring
data repository belongs to the Principal and if not I send a 403
GET/hadevices/{id}/deviceMetrics - I retrieve the user from the
Principal and verify that the HADevice with id={id} in my spring data
repository belongs to the Principal and if not I send a 403.
POST /collectors/{id}/deviceMetrics - I retrieve the Collector uniqueId
from the Principal and make sure that the Collector's id matches the
{id} in the URL
POST /hadevice/{id}/deviceMetrics - I retrieve the
Collector uniqueId from the Principal and the associated Customer. I
also pull the Customer associated with HADevice with id={id} and
compare the two. If they are unequal, I send a 403.
My application is littered with such complex authorization requirements for each REST resource, and I want to use a custom org.springframework.security.access.PermissionEvaluator, specifically by implementing the following method.
boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission)
I'm planning to use a combination of targetType and request.getUrl() to get a specialized Evaluator for each url and resource.
Is there a better way to do this?
Your question is quite broad, but I think you can get away with quite simple logic for most cases.
GET /collectors/{id}/deviceMetrics
Given that you have a DeviceMetrics class with suitable properties, you can annotate your data repository with something like:
#PostAuthorize("hasRole('ROLE_ADMIN') or (hasRole('ROLE_USER') and returnObject.collector.owner = authentication.name)")
public DeviceMetrics getDeviceMetrics(long deviceId);
(This assumes that DeviceMetrics class has a property collector which has a property owner which is the username.)
That doesn't need a PermissionEvaluator at all. Maybe you need one for more complex cases:
POST /collectors/{id}/deviceMetrics
#PreAuthorize("hasRole('ROLE_COLLECTOR') and hasPermission(#collectorId, 'com.example.Collector', 'WRITE')")
public void saveDeviceMetrics(long collectorId, DeviceMetrics deviceMetrics);
You only need one PermissionEvaluator since you get all the information you need as method arguments.
For those who are wondering what my solution looks like, I borrowed from this example.
It's old and it's based on xml configuration which I am not particularly fond of. But the idea is to create a Map and initialize the custom PermissionValidator and to store the authorization logic in the Permission interface implementations.
The biggest pain point was injecting an autowired HashMap of tuples, but that's an implementation detail that reasonably experienced spring users can figure out.