How should a user-specific resource be protected using spring boot security? - java

Let's consider basic authentication flow. User A should be able to access entities only with IDs 1 and 2, and user B should be able to access only IDs 3 and 4.
Put it another way, I want user A to be able to access /foos/1 and foos/2, but get a 401 if trying to call foos/3.
The way I consider implementing it is getting the current user ID in the endpoint, checking which IDs are mapped to it, and if the requested resource ID is not in that list, throw an unauthorized exception. Something like this:
#GetMapping("/foo/{fooId}")
public Foo home(#AuthenticationPrincipal User user, #PathVariable String fooId) {
// Get Foo IDs mapped to User
// If fooId is not in the result, throw an unauthorized exception
}
It feels like there should be a more streamlined way to do this. Is there a better way?
How should I protect a user-specific resource from being retrieved by another user?

The way you consider to do it is fine...
Another way is to use SpEL directly in the FooRepository ref: jpa.query.spel-expressions
#Query("select foo from Foo foo where foo.id = ?1 and foo.user.id=?#{principal.id}")
Foo findFooById(String fooId);

Related

How to validate rest path (spring boot)

How to validate path variables (storeId,customerId,accountId) given from the following URL or anything similar ?
/store/{storeId}/customers/{customerId}/accounts/{accountId}
In case a user starts writing random storeIds/customerIds, and try to create resources like
POST /store/478489/customers/56423/accounts (supposing 478489 and 56423 don't point to a valid resource) in the URL.
I want to return the correct error code e.g. HttpStatus.NOT_FOUND, HttpStatus.BAD_REQUEST.
I am using Java with spring boot.
Following question explains my problem more detailed, however it does not have much responses.
Validating path to nested resource
From the provided URL /store/{storeId}/customers/{customerId}/accounts/{accountId},
It is evident that store has customers and those customers have accounts.
Following approach includes extra database calls for validating store by ID and customer by ID, but that would be the appropriate approach because if we use query with joins on STORE and CUSTOMER tables then you might not be able to exactly tell if given storeId or customerId is incorrect/not in database.
If you go step by step you can show appropriate error messages,
If storeId is incorrect - There exists no store with given storeId: XYZ
If customerId is incorrect - There exists no customer with customerID: XYZ
Since, you mentioned that you are using Spring Boot, your code should be looking something like this:
#RequestMapping(value = "/store/{storeId}/customers/{customerId}/accounts",
method = RequestMethod.POST)
public ResponseEntity<Account> persistAccount(#RequestBody Account account, #PathVariable("storeId") Integer storeId,
#PathVariable("customerId") Integer customerId) {
// Assuming you have some service class #Autowired that will query store by ID.
// Assuming you have classes like Store, Customer, Account defined
Store store = service.getStoreById(storeId);
if(store==null){
//Throw your exception / Use some Exception handling mechanism like #ExceptionHandler etc.
//Along with proper Http Status code.
//Message will be something like: *There exists no store with given storeId: XYZ*
}
Customer customer = service.getAccountById(storeId, customerId);
if(customer==null){
//Throw your exception with proper message.
//Message will be something like: *There exists no store with given customerID: XYZ*
}
// Assuming you already have some code to save account info in database.
// for convenience I am naming it as saveAccountAgainstStoreAndCustomer
Account account = service.saveAccountAgainstStoreAndCustomer(storeId, customerId, account);
ResponseEntity<Account> responseEntity = new ResponseEntity<Account>(account, HttpStatus.CREATED);
}
Above code snippet is just a skeleton of what your code should look like, you make structure it in better way than it is given above by following some good coding practices.
I hope it helps.

What's best practice in Spring Data JPA Error handling

I'm new in the world of spring and Spring Data.
I've build a user- and permission management system where a user can grant and remove permissions for another user.
I've digged in the docs but I'm not shure how to handle querys where I just want to insert or manipulate Data in a Table.
In the docs query return type table I couldn't find something like a status or a status reporting object.
What I'm looking for is a pattern that allows me to controle is an operation was successfull. Right now I'm using primitives. When everything worked out I get "1" returned but when I query an empty table I get "0". So I need something with more details.
Thank you for guidance and sharing your experience.
EDIT:
Here is a code example:
#Transactional
#Modifying
#Query(value = "DELETE FROM permissions WHERE producerId=:producerId AND comsumerId=:consumerId", nativeQuery = true)
void clearAllPermissions(#Param("producerId") Long producerId,#Param("consumerId") Long consumerId);
The Method is provided by my repository class.
#Repository
public interface PermissionsRepository extends JpaRepository<ProducerConsumerPermissions, Integer>{
.
.
.
}
I call the class from my service layer which is used by my Controller layer.
I guess it'd be nice to know if this operation was successfull so that I can transport the exeption throught the layer till my frontend and throw a message to the user. But when the Table is empty I get a value of false back when I use Integer as return type.
Make your method return int then you know if any records are deleted.
#Transactional
#Modifying
#Query(value = "DELETE FROM permissions WHERE producerId=:producerId AND comsumerId=:consumerId", nativeQuery = true)
int clearAllPermissions(#Param("producerId") Long producerId,#Param("consumerId") Long consumerId);
If there is a problem it will throw and exception anyway. Spring Data throws DataAccessException that you can catch to see what went wrong.

Better way to secure REST resources in Spring?

I have a RESTful service that exposes resources like /user/{id}
Now, the user can provide the credentials, get the token and access the resource. However, once authenticated, the user can access the resources for any id.
Meaning, user1 can access the URIs like /user/1 as well as user/2 and so on. I ended up using a Principal in the controller methods and started checking the id of the Principal with the id the user is trying to access.
Further, the user has multiple resources associated with it. Say, user1 owns res1 and res2, user2 owns res3 and res4. These can be accessed via /user/1/res/2. I need a way where I can prevent /user/1/res/3 as res3 is owned by user1 and not user2.
But I believe that this problem is very common and I am not really convinced with my solution.
Is there a better way to deal with this problem?
Thanks
You should not be exposing resourse /user/{id} at all if you all user can do is access only their own ID.
If I understand correctly, just exposing /user is enough, find ID of user from Principal or session etc and return result.
If you really want to do it, you can have custom implementation of #PreAuthorize. Got this code from a blog.
#PreAuthorize("isUsersRes(#id)")
#RequestMapping(method = RequestMethod.GET, value = "/users/{id}")
public UsersfindById(#PathVariable long id) {
return Users.findOne(id);
}
public class CustomMethodSecurityExpressionRoot
extends SecurityExpressionRoot implements MethodSecurityExpressionOperations {
public CustomMethodSecurityExpressionRoot(Authentication authentication) {
super(authentication);
}
And implemenation of isUsersRes
public class CustomMethodSecurityExpressionRoot
extends SecurityExpressionRoot implements MethodSecurityExpressionOperations {
public boolean isMember(Long OrganizationId) {
//user logic
}
Check complete blog here
This is a common problem with varied solutions. Also its not a problem related to REST alone. We have had this ever since apps exist. Employee can see his salary slip, leave records, etc but not another employee's.
One solution I like the most is 'security in depth'. This idea comes from how I have seen this work in banking systems for decades. This needs to get supported in the DB layer first.
You would need a table design like this example (or whatever your app's entity hierarchical structure is):
Organisation
-Dept
--user
And all non-master tables need to have a relation to one of these entities. Example:
Payslip -> user
Leave record -> user
Manager -> dept
HR Manager -> org
etc...
You would need another table to map out the basic access levels (This can get complex if we need to implement different sub access levels)
user1:dept2:org1
user2:dept2:org1
(I have seen some implementations that send this table's info as part of an encrypted access token that is used on every access request if the access has to be sessionless.)
You have not mentioned a framework/language but most languages have a database layer. For example if the DB layer is hibernate-java. There are interceptors (https://docs.jboss.org/hibernate/core/3.6/javadocs/org/hibernate/Interceptor.html#onPrepareStatement(java.lang.String)) that can be used to modify the query thats being executed.
Every query to DB will go with additional where-clause for these relationship keys.
We can get clever with Spring AOP, REST interceptors and a lot of other techniques on top of this basic architecture to enforce this security.
Idea will be that DB layer does not return data thats not accessible to the logged in user principal irrespective of what queries higher layer code uses.
if this is in place, a REST GET call for
/payslip/user1/Jan-2017
will end up with a 404 and not a 403.
Expecting this to be solved by a framework or a superficial set of interceptors is both risky and not future proof. We end up continuously tweaking the interceptors as the url patterns evolve.
Addition to show table examples:
ACL table
user, uid, dept, org
--------------------
jhon, 1 , 1 , 1
mary, 2 , 2 , 1
will, 3 , 2 , 1
Payslip table
--------------
month, net, deductions,..., uid
-------------------------------------
Jan , 200, 15.5 ,..., 3
Feb , 200, 15.5 ,..., 3
Project table
-------------
pname, pstart, pbudget, dept
------------------------------------
mark1, 12/21 , 20000 , 2
markx, 12/31 , 40000 , 2
What you want is user roles and permissions + cross user control. To find out user roles and permissions refer this
Also additionally you may want to cross check their user ID to the resource ID. Since you cannot let user1's resource ID 1 to view by user2, you will need to add userID as part of the resource id ex:- /user/user_id_1.
Otherwise we don't have a logical way to separate which resources are applicable to which users.

PUT method (RESTful) doesn't work as a way to update resources

According to this article(http://restcookbook.com/HTTP%20Methods/put-vs-post/), PUT is supposed to work as a method to update resources.
However, practicing RESTful with JAX_RS 2.0 and Jersey 2.0, I don't think it updates a particular resource.
(I.e. I'm studying RESTful with JAX_RS 2.0 and Jersey 2.0)
Here is a resouce like this.
<customer>
<name>Before</name>
<postcode>111</postcode>
</customer>
What I'm trying to do is to update (perhaps I should say "replace") this resource.
ClientConfig config = new ClientConfig();
Client client = ClientBuilder.newClient(config);
WebTarget target = client.target("http://xxx/yyy/zzz/end.cust");
Customer cust = new Customer();
cust.setName("After");
cust.setPostcode(222);
target.path("Before").request().put(Entity.xml(cust));
#Id annotation is set to "Name" in the "Customer" class, so the path "Before" is supposed to work as the ID and the first resource (named "Before") should be replaced with the second resource (named "After").
However, after the coding above is executed, the "Before" resource still remains, and there is a new "After" resrouce.
It seems that the PUT method worked to create a new resource, instead of updating something.
(i.e. There are both "Before" and "After" resources, and nothing has been updated)
I tested a POST method in order to create a new resource, and it created a new resource as I expected.
If you see anything I'm doing wrong or what needs to be done, could you please give some advice?
edit
I'll add the server side code. The method annotated with #PUT is like this.
#PUT
#Path("{id}")
#Consumes({"application/xml", "application/json"})
public void edit(#PathParam("id") String id, Customer entity) {
super.edit(entity);
}
This is inside a class called CustomerFacadeREST.java, automatically created after I created a "RESTful service from Database".
According to NetBeans' document, super.edit() method is originally like this.
public void edit(T entity) {
getEntityManager().merge(entity);
}
In the "Customer" class, #Id is set to the "name" value in this way.
public class Customer implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Basic(optional = false)
#NotNull
#Size(min = 1, max = 80)
#Column(name = "Name")
private String name;
// Other fields, such as Postcode...
public Customer() {
}
// Other constructors and methods...
}
The idea behind "HTTP Verbs" like PUT, GET, POST, DELETE are just a matter of protocol semantics. Just performing an HTTP PUT operation doesn't do anything magical. It's just proper semantics we as developers should understand, while developing, as these semantics are known to all (that's why protocols exist). If no one followed these semantics, the world would be somewhere between the Great Depression and the Apocalypse.
That being said, these verbs (semantics) are a sort of guarantee (or maybe assurance is a better word) to the client performing the request with a certain verb will have some know semantics to it. One major factor is the idea of idempotence. Idempotence is the idea that no matter how many times I make a request, the result will be the same (or have the same effect).
Certain HTTP verbs are said to be idempotent, such as PUT, DELETE, GET. No matter how many times be make the exact same request, the general idea is that the result/effect should be the same. POST on the other hand is said to not be idempotent, as the exact same POST request may produce different results, for example submit an order, wrongfully, again, or creating a new customer twice.
If we want to make the world a better place, and do our part in saving the world from a complete meltdown, we should learn these semantics and be good citizens by following them. There's a lot more to learn about the verb semantics, than just idempotence, but understanding that much, is a good start. I'd suggest maybe picking up a good book on REST to learn some good practices. Or if you want you want to be a cool kid, take time to read the bible (actually the Fielding Dissertation).
All that being said, it's our job as developers to create the code to follow these semantics. The reason your method is creating a new resource, is probably because you are creating a new resource with your code. Maybe something like this would seem more appropriate:
#PUT
#Path("/customers/{id}")
#Consumes(MediaType.APPLICATION_JSON)
public Response updateCustomer(#PathParam("id") long id,
Customer updateCustomer) {
Customer customer = customerService.getCustomerById(id);
if (customer == null) {
throw new WebApplicationException("Can't find it", 404);
}
customer.setFirstName(updateCustomer.getFirstName());
customer.setLastName(updateCustomer.getLastName());
...
return Response.noContent().build();
}
So we are just update the customer that already exists in our database. Normally with a PUT request to update, the particular customer resource URI should be known. So say the client makes a request to http://blah.com/api/customers/1234, our service will look up the customer with the id 1234. If it can't be found, we return a 404 status code, as the resource doesn't exist. If it does exist, then we update the customer with the customer data provided in the request. If you wanted to create a new customer, where the URI is not known, then POST would be correct, and you'd send a customer representation to http://blah.com/api/customers.
Also keep just an FYI: in many cases a case like this, what happens is that the client requests (GET) a resource, say a customer, and updates that customer representation, then send it back as PUT request with the updated customer. On the sever it should use that information to update the particular customer's data, as you can see from the example above.
UPDATE
Per your edit. You are completely missing the point of how this is supposed to work.
Customer cust = new Customer();
cust.setName("After");
cust.setPostcode(222);
target.path("Before").request().put(Entity.xml(cust));
What's wrong with this is that with the new Customer, you are setting the identifier to "After", which is different from the identifier in the request path, you are using "Before". So the path variable {id} is "Before". With this request URI you are saying that you want to access the customer with id "Before". As seen in my code, it's your duty to check if a customer with the id "Before" exists in the database. If not, you should return back a 404 Not Found. The name (id) you set for the new Customer should be the id expected in the database. So if you want to update the customer with id in the databse "After". then you should put "After" in the path, instead of "Before". We should not try and change the identifier.
Like I said, when we want to update a resource, we normally, GET the resource, update some field (but not the identifier), and send it back. A sequence might look something like
final String PATH = "http://hello.com/api/customers"
WebTarget target = client.target(PATH);
Customer customer = target.path("1234").request().get(Customer.class);
// where 1234 is the id (or in your case `name` of the customer.
// I would avoid using the name as the DB id, that's why my example uses numbers
customer.setPostalCode(...);
target = client.target(PATH).path(customer.getName()); // getName should be 1234
Response response = target.request().put(Entity.xml(customer));
We are using the same id as we were provided with, in the path, because that is the how the resource is identified in the server.

Submitting / binding partial objects with spring mvc

The Spring MVC binding mechanism is powerful, but I'm now confronted with a trivial issue that I wonder how to resolve:
User JPA entity, that is used for the binding and validation as well (i.e. throughout all layers)
"Edit profile" page, that is not supposed to change the password or some other entity properties
Two ways that I can think of:
Using the same object
use #InitBinder to configure a list of disallowed properties
obtain the target user (by id)
then use a reflection utility (BeanUtils) to copy the submitted object to the target object, but ignore null values - i.e. fields that are not submitted
Introduce a new object that has the needed subset of fields, and use BeanUtils.copyProperties(..) to merge it to the entity.
Alternatives?
I've found that as soon as your web model starts to diverge from your business layer in function, it's best to use a view layer object (a model object) to collect, or display the data
the entity:
public class com.myapp.domain.UserEntity {
}
the model object:
public class com.myapp.somesite.web.SomeSiteUserModel {
public static SomeSiteUserModel from(UserEntity userEntity) {
... initialize model ...
}
public UserEntity getModelObject() {
... get entity back ...
}
}
now all view based operations can hand off processing to the internal model object if that makes sense, otherwise it can customize them itself. Of course the problem with this is you have to re-write all the getters and setters you want for the entity (an issue that I've had to deal with, that is annoying) unfortunately that is a bit of a Java language issue
I just checked up with two of the last Spring projects I have worked on and in both places the following approach is taken:
In the JSP page for the form the change password field has a name that does not match the name of the password field in the User bean, so that it doesn't get mapped to the bean. Then in the onSubmit method there is a separate check whether a new password has been submitted, and if it has been, the change is reflected explicitly.
Поздрави,
Vassil
You can read the object from the database first and bind then the request. You can find an example at FuWeSta-Sample.
It uses a helper-bean which must be initialized by Spring.

Categories