Check if resource belongs to user before deleting/updating it - java

Here's my spring endpoint:
#DeleteMapping("/{id}")
public ResponseEntity<Package> deletePackage(JwtAuthenticationToken principal,
#PathVariable Long id) {
Package parcel = packageService.retrievePackage(id);
checkResourceBelongsToUser(principal, parcel);
return new ResponseEntity<>(packageService.deletePackage(parcel), HttpStatus.ACCEPTED);
}
And here's my current way of checking if the current resource the user is trying to update/delete belongs to the user:
private void checkResourceBelongsToUser(Address address, AppUser user) {
if (!address.getUser().getId().equals(user.getId())) {
throw new ResponseStatusException(HttpStatus.FORBIDDEN, "Not allowed to access/modify resource");
}
}
This method will check if the Address belongs to the user.
The address was gotten from a id in the path variable
The user was gotten from the id in the JWT
Update and delete endpoint call the checkResourceBelongsToUser method before doing any processing (updating or deleting). I'm comparing ids. Is my method enough to check if the user truly has access?

Related

Why spring boot update method create new record

I have created update method in spring boot. It creates new record instead of update the record.
Tried code:
controller:
#PutMapping("update/{id}")
public Users updateUser(#PathVariable Integer id, #RequestBody Users user) {
user.setFname(user.getFname());
user.setLname(user.getLname());
user.setAddress(user.getAddress());
user.setTelno(user.getTelno());
return serveiceClass.updateUserbyId(id, user);
}
service:
public Users updateUserbyId(Integer id, Users users) {
return repositoryInterface.save(users);
}
How can I solve this problem?
You are not checking if user is present or not and you creating new record by calling save method. save method always insert new row if id in given object is not present in DB, in your case user is having id 0 I guess, thats why it is inserting new record. You need to fetch the User from given id and update the existing record in DB.
Create one more method in service class
public Users getUserById(Integer id) {
return repositoryInterface.findById(id).orElse(null);
}
And then..
#PutMapping("update/{id}")
public Users updateUser(#PathVariable Integer id, #RequestBody Users user) {
Users userExisting = serveiceClass.getUserById(id);
if(userExisting == null){
throw Exception("User Not Found");
}
userExisting.setFname(user.getFname());
userExisting.setLname(user.getLname());
userExisting.setAddress(user.getAddress());
userExisting.setTelno(user.getTelno());
return serveiceClass.updateUserbyId(userExisting);
}

Third-party API wrapper in Java: how to design

Suppose, there's a site that provides an API, such as this:
Users post questions, answers on that site
You can make GET and
POST calls
There are two types of authentication: weak (only gives
read rights) and strong (gives both read and write rights)
Right now, I'd like to read users' questions and answers (only need weak access) and send them messages or post my own questions in the future (would need strong access)
The site's API has actions both to do with users (e.g. send a message) and with site in general (see about, see most popular questions)
What I have right now looks like this:
public class Wrapper {
private AccessToken accessToken;
public Wrapper(...) {
//does some stuff
//gets access token:
getAccessToken(...);
}
public AccessToken getAccessToken(...) {
AccessToken result;
//gets access token, given the auth info provided as arguments
accessToken = result;
return result;
}
public ArrayList<Question> getQuestions(User user) {
//gets user's questions, using the accessToken field
//set's a user's questions field to the result and returns the result
}
public ArrayList<Answer> getAnswers(User user) {
//same as previous
}
public boolean sendMessage(User user) {
//sends a message, if current accessToken is strong
}
}
and User class:
class User {
private String username;
private ArrayList<Question> questions;
private ArrayList<Answer> answers;
public User(String username) {this.username=username;}
//getters and setters
}
So, to use it you would use something like this:
public class Main {
public static void main(String[] args) {
Wrapper wrapper = new Wrapper(...);
ArrayList<Question> questions = wrapper.getQuestions(new User("username"));
wrapper.sendMessage(new User("username2"));
}
}
I have issues with this.
First of all, class User feels superfluous, since all the functionality is inside the Wrapper class.
Second, I wonder if what my methods do is wright - from the design's perspective: in getAccessToken I both return AccessToken and set a Wrapper's field accessToken to the result. Is this the right approach? Or should the method only return access token and then that result should be assigned to a class' field explicitly? Same goes for the getQuestions and getAnswers methods: they both get the ArrayLists, return them and assign a User's field to the result - all inside the single method.
I would like for a User class to have some meaning. I thought of doing it something like that:
Wrapper wrapper = new Wrapper(...);
User user = new User("username");
user.getQuestions(wrapper.getAccessToken());
user.sendMessage(wrapper.getAccessToken());
So, the Wrapper class would only serve as a place to get an access token from, which doesn't feel right as well. I could place the access token functionality inside the User class and use it like this:
User user = new User("username", ...);
user.getQuestions();
user.sendMessage();
The User's constructor would take both username and auth data, would get access token and store it inside a user and then use it when getting questions/answers or sending messages. I could make the accessToken field inside User class static so that all users shared the same token.
However, there are actions the site API provides, that aren't obviously connected with users: for instance, getting the site's most popular questions. It feels right to use a generic Wrapper class for that purpose which contradicts with the previous approach.
I'm new to this and only know a couple design patterns. Perhaps, there are widespread patterns that are to be used for this type of problem? Any help/advice is appreciated.
There are a few alternatives that you can do to solve your problem, but there is likely not one that is better than all others. The solution you choose will depend on the trade-offs and how you want your system to operate. The following are two common solutions to this type of problem.
Have the Wrapper generate a User: Instead of generating a User object separate from the Wrapper, you can have the Wrapper generate the User object. This allows the Wrapper to embed the AccessToken within the User without any outside client knowing that a user has an AccessToken. For example, you can use the following Wrapper and User definitions:
public class Wrapper {
public Wrapper(...) {
// ... does some stuff, but DOES NOT get an access token ...
}
private AccessToken getAccessToken(...) {
AccessToken result;
// ... gets access token, given the auth info provided as arguments ...
return result;
}
public User findUser(String username, ...) {
return new User(username, getAccessToken(...));
}
}
class User {
private String username;
private final AccessToken token;
public User(String username, AccessToken token) {
this.user = user;
this.token = token;
}
// ... getters and setters ...
}
Note that getAccessToken is now private, as no other client needs to access this method. All of the methods of Wrapper continue to accept a User argument, but they now should obtain the access token by calling getToken on the User object, rather than using a stored AccessToken in Wrapper.
Also note that the token field is final, since the access token associated with a User should not change over the life of a User object.
Embed the Wrapper in User: This technique is similar to (1), but it also embeds the Wrapper object in the User object. This allows the User class to act as a live object, which can be queried for questions and answers and can be used to send messages. Since all of the methods of Wrapper accept a User argument, this is a good sign that the methods should be moved to User. The following is a halfway point to refactor the Wrapper methods into User:
public class Wrapper {
public Wrapper(...) {
// ... does some stuff, but DOES NOT get an access token ...
}
private AccessToken getAccessToken(...) {
AccessToken result;
// ... gets access token, given the auth info provided as arguments ...
return result;
}
public User findUser(String username, ...) {
return new User(username, getAccessToken(...));
}
public ArrayList<Question> getQuestions(User user) {
//gets user's questions, using the accessToken field
//set's a user's questions field to the result and returns the result
}
public ArrayList<Answer> getAnswers(User user) {
//same as previous
}
public boolean sendMessage(User user) {
//sends a message, if current accessToken is strong
}
}
class User {
private String username;
private final AccessToken token;
private final Wrapper wrapper;
public User(String username, AccessToken token, Wrapper wrapper) {
this.user = user;
this.token = token;
this.wrapper = wrapper;
}
public List<Question> findQuestions() {
return wrapper.getQuestions(this);
}
public ArrayList<Answer> findAnswers() {
return wrapper.getAnswers(this);
}
public boolean sendMessage() {
return wrapper.sendMessage(this);
}
// ... getters and setters ...
}
Using this technique, clients can now directly get questions and answers from a User object. Note that the findQuestions and findAnswers methods start with find. This tips off clients that this call may be a long call (as opposed to getQuestions or getAnswers, which would make a client assume that it is a simple getter and the method would return nearly-instantly). The fact that these methods execute a remote call should also be documented in the Java-docs for the methods. If the call takes a long time, the methods should return a Future (or a similar object) and be made asynchronously.
If you want to go all-in on the refactor, you can move all of the implementation details from the Wrapper class to the User class:
public class Wrapper {
public Wrapper(...) {
// ... does some stuff, but DOES NOT get an access token ...
}
private AccessToken getAccessToken(...) {
AccessToken result;
// ... gets access token, given the auth info provided as arguments ...
return result;
}
public User findUser(String username, ...) {
return new User(username, getAccessToken(...));
}
}
class User {
private String username;
private final AccessToken token;
private final Wrapper wrapper;
public User(String username, AccessToken token, Wrapper wrapper) {
this.user = user;
this.token = token;
this.wrapper = wrapper;
}
public List<Question> findQuestions() {
// ... find the questions remotely ...
}
public ArrayList<Answer> findAnswers() {
// ... find the answers remotely ...
}
public boolean sendMessage() {
// ... send message remotely ...
}
// ... getters and setters ...
}
This may not be the best approach, as it may be a better idea to keep the details of accessing the remote API abstracted in the Wrapper class. This is a judgment call that will depend on the nature of your specific application.
There are numerous other techniques that you can do, but the above two are common approaches to the problem you are trying to solve.

Spring Web MVC Post request multiple object

I have a method which inserts an object. But this object mapped with another object as one to many.
#Entity
public class FutureMessage {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private int id;
private String message;
#ManyToOne
private User user;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
This FutureMessage object has mapped to User object but there is a problem with post request.
{
"message":"qwerasdf",
"user":{"id":15,"username":"XXX"}
}
This request Returns 200 but in database XXX is not the right user name for id 15.
At the same time if user not exists in this request, it returns 200(OK) too.
My problem is this, I do not want to send user id. I just want to send message and username.(There are more than 2 attributes in FutureMessage objects)
#RequestMapping(path="message/new",method=RequestMethod.POST)
public FutureMessage newMessage(#RequestBody FutureMessage newMsg){// how to add user name here ?
FutureMessage msg = null;
if(newMsg==null)
return null;
if(newMsg.getUser()==null){
return null;
}
try{
msg = messageRepository.save(newMsg);
}catch(Exception ex){
ex.printStackTrace();
}
return msg;
}
EDIT : I only want to send FutureMessage object and username as ResponseBody. This is my question not returning 404 or anything else.
Spring has parameterized object named ResponseEntity that you should return from your controller methods. It allows you to programmatically send different HTTP responses based on the request. Look at this post for an example of returning a 400 response when the request is bad, in your case when the User is null.
As for the mismatch between user XXX and id 15 I don't fully understand what your problem is. It sounds like you just want to send in the message and username? if that is the case your repository needs to be smart enough to check on username and the usernames better be unique or you'll get collisions.
Responding to your first problem. Even though you return null, it will be SUCCESS (Status code - 200) http request. If you specifically want to send the request as bad request when you find your logic returns null, then return like
return new ResponseEntity<String>(HttpStatus.BAD_REQUEST);
How to respond with HTTP 400 error in a Spring MVC #ResponseBody method returning String?
Second, when you don't want to send the user id. The make sure you have configured the correct relationship between your models (message, user ) and you have placed the user Id's generated type to any of your Id generation strategy such as auto increment

foreign keys in objectify / google app engine

I'm trying to write an application for the google app engine using Objectify and am having some trouble. I'm new to noSQL datastores so it's probably a conceptual problem.
I have an entity called a message, and each message has a from User - the user who created the message.
#Entity
public class Message {
#Index private Key<User> fromUserKey;
#IgnoreSave private User fromUser;
Annoyingly, I have to have both a User and a Key field in the message. JSON needs the User object to populate the response with useful fields, and the google app engine needs the Key to be able to store the reference to the user. The #IgnoreSave annotation is used to stop Objectify asking the google app engine to try to store the User object (which will fail).
When fetching messages, the from user key is populated, but the from User object is not. Here's what the DAO code looks like for my "getMessages" operation:
public static List<Message> getSentMessages(long userId) {
List<Message> result;
result= ofy().load().type(Message.class).filter("from", Key.create(User.class, userId)).limit(1000).list();
return result;
}
But the fromUser object is not populated on each Message, only the fromUserKey. How do I get the actual User object populated in the response? I need to access such fields as forename, surname, etc - not just the ID of the User. I could get the from User from the DB, loop through the results, then call setFromUSer on each message, but it's ugly. And it also causes a ConcurrentModificationException...
I'm sure I'm doing something wrong here, but I can't work out what.
What you can do is have a property Ref<User> on the Message entity and annotate it with '#Parent'. This means that each Message entity will become part of the user's entity group.
The Ref<?> works like a key but allows you to directly access the actual entity object; that way you can easily get the forename, surname etc.
Change your class as follows:
#Entity
#Cache
public class Message
{
#Id Long id;
#Load #Parent private Ref<User> user;
public User getUser() { return this.user.get(); }
public void setUser(User value) { this.user = Ref.Create(value); }
}
Having done that, you will be able to perform ancestor queries to retrieve all the message entities associated with a particular user:
public static List<Message> getSentMessages(long userId)
{
User parent = ofy().load().type(User.class).id(userId).now();
List<Message> results = ofy().load().type(Message.class).ancestor(parent).limit(1000).list();
return results;
}
You should now be able to do what you wanted, and hopefully not get any more errors.

Why is this giving me AssertationError?

I have the following test :
#Test
public void deleteUser(){
User user = new User("admin","admin");
service.createUser(user);
service.deleteUser(1);
assertTrue("Check that user is deleted: ", user.getId() < 1);
}
But it gives me AssertationError.
Why can I not do like that?
I create my user and then wanna test that I am able to delete the newly created user.
My delete method is just deleting from a hashmap the user.
Map<Long, User> users = new HashMap<Long, User>();
protected static long nextId = 0;
#Override
public long createUser(User user) {
user.setId(getNextId());
users.put(user.getId(), user);
return user.getId();
}
#Override
public void deleteUser(long id) {
users.remove(id);
}
Can somebody explain this to me?
Instead of accessing the User object, that you still have the reference to (independent of whether it's deleted), you should implement a method, that checks, whether a user is present in the class of your service variable:
public boolean containsUser(long userId) {
return users.containsKey(userId);
}
And then assert like so:
assertFalse("Check that user is deleted: ", service.containsUser(1L));
When you delete user from Map, you do nothing with user.id. That's why your assertion fails.
One way to change id when deleting
#Override
public void deleteUser(long id) {
User deletedUser = users.remove(id);
deletedUser.setId(-1);
}
Another (and better) way is to change test:
#Test
public void deleteUser(){
User user = new User("admin","admin");
long id = service.createUser(user);
service.deleteUser(id);
assertFalse("Check that user is deleted: ", service.hasUser(id));
}
Your test does not make any sense - you want to check if the user is deleted from the service, but instead you are checking the value of the Id property of the user object (which is null, or set to some value - you did not provide any code). As some users already suggested, you need to check if the user is actually contained within your service. Also, are you doing something inside your getNextId() method? If that is simply getter for your nextId variable, every single user you create will have Id set to 0 (but maybe you wanted it that way, who knows). Is there some code within getNextId that actually sets the value to something else?

Categories