I'm confusing about design restful for my API.
I have 2 roles are end-user and backend(admin). With backend role, i can access any resources of user
An user can have many orders.
So I defined base on users resource
GET /users/{userID} -- get User Information
GET /users/{userID}/orders -- list user order list
POST /users/{userID}/orders -- user make an order
But uhm.. after reference some online documents, implicit userID will be identified after authentication phase, so here are the different designs by using order resource:
GET /orders/ --list user order list by user account(backend can get all)
GET /orders/{orderID} --get orderID by userID
POST /orders/ -- user make an order.
with this define, when backend user want to list orders by user. Which method should I use?
GET /orders?user={userID} (user as query parameter) -- List order with userID
Or
GET /users/{userID}/orders
Pls advise me which one (users or orders resource) design is better, and why?
Tks,
Which design is better depends on the use cases.
For example, let's assume two users:
Alice (userID=1)
Bob (userID=2)
Let's also say Alice has already authenticated so the backend has userID=1 available somewhere.
If Alice wants to list her own orders, the shortest way would be
GET /orders
And have the backend user the available userID.
Let's say the request is suppose to retrieve Orders for the authenticated user. What if Alice tries /users/2/orders - is she allowed to see Bob's orders? What if Alice mistypes a lot and issues a query for /users/2/?
Final thing to consider - what if there are more attributes to search on in the future? What if userID is removed from Orders from the future (let's say there's a new relation in the future like User --> Cart --> Order)? Which URI scheme is easier to update?
So, there's no simple answer and it depends on your use cases. In the case where it's just retrieving Orders, I'd recommend the going with {GET|POST} /orders for the most flexibility and simplicity.
Related
I am using keycloak-admin-client 13.0.0 in my Spring Boot Application. I can get all the users from KeyCloak. But I want to get users by attributes like mobile or em_no or dept.
User attribute:
key - value
mobile - 9876543210
em_no - 12334
dept - IT
I'm afraid that searching by attribute value would require fullscan over user entities and would be not very efficient if you will have huge number of users. Consider this feature apart from keycloak. How would you implement search by phone in standalone relational DB schema? I guess you would add index on this field. In this case you can add custom table to keycloak schema that will provide phone -> user mapping. See here (https://www.keycloak.org/docs/latest/server_development/#_extensions_jpa)
Considering dept attribute i would recommend to switch from attribute to groups. So you will have set of departments groups (IT,ACCOUNTING, ..) and join user to appropriate group. Then you can easily query members of desired group.
Similar approach works for roles. E.g. if you have finite numbers of values you can define them as roles and grant appropriate role to user instead of attaching this value as attribute. Querying by granted role also available right out of the box.
Note that there are available a lot of different OIDC mappers that will help you to add described mappings (no matter is this a attribute, group or role) to access token structure.
I'm very new to Spring. I'm trying to create a REST API using Spring Boot and I'm stuck whether to expose my user's primary key or not which also happens to be their email. Something like api/user/example#gmail.com. A big part of me says it's okay since it would sensible to expose it as it is the identifier for that specific record when viewing, deleting, and updating. Is there a security risk for this? What is the best practice for such implementation? Right now I'm combining the #PathVariable and #RequestBody. I didn't like the idea of putting my primary key in the RequestBody thinking that it might pose a risk...or is there?
#RequestMapping(value = "/updateUser/{customerEmail}", method = RequestMethod.POST)
public ApiResult updateCustomer(#RequestBody UserDetailsDto userDetailsDto, #PathVariable String customerEmail) {
//service call...
}
First of all, user e-mail is often considered to be PII (Personally Identifiable Information). As such it would be unwise to put it into a URL, because you should not put any sensitive information into the URL. Header - ok, body - too. But not into the URL. The reason is, that all the proxies/load balancers/other infrastructure you have or might have in the future will always be allowed to log URLs for debug reasons. And you don't want your sensitive data to leak across the components like this. No company policy would ever allow that.
Spring is a good framework of choice, usually as long as the identifier is unique it should be fine, the problem with using an email is you are exposing your users data more easily which could be problematic to the users, I would suggest you rather use a string of unique characters as an identifier in the form of:
http://api.example.com/user-management/users/{id} as an example http://api.example.com/user-management/users/22
in this case identifier of user 22 has the email example#gmail.com in this way you are not exposing sensitive data when doing an update here is a link that gives guidance on best naming practice https://restfulapi.net/resource-naming/.
Another tip given in the link provided is to avoid using URI's as CRUD (Create, Read, Update, Delete) functionality "URIs should be used to uniquely identify resources and not any action upon them".
Any sensitive information (in this case email but in other case that could also be your database autoincremented primary key field ID in your table) should not be exposed.
Once way to go around that that I know and I use is to have 2 fields. For example, I have table USER {ID, USERID, NAME, ...}
Above, ID is autoincremented Long field representing PK.
USERID on the other hand, is a field generated of random characters or GUID which I use to pass back and fort in REST calls.
So, I might have record in USER table as:
USER {1, "a23asf60asdaare998700asdfasr70po097", "Mike", ...}
If I were to pass ID=1 back and forth, a malicious user could easily deduce what it is and how to query next user. For that reason, i pass USERID which represent a public and safe version of ID that can be passed and no one can know what would be the USERID of next user.
So, your response model, dto model etc should have these fields and response model should return USERID instead of returning ID. And you can use JPA to find the user by the USERID (so, based on that, that method must be called in this case findByUserId).
The same would apply for your case where you use email instead of ID if you want dont want to expose user emails which make sense to me.
Hope that helps.
I think it's more a matter of taste and personal beliefs rather than objective aspects.
Since HTTPS is more or less mandatory today, it's a lot harder to obtain the e-mail address by just sniffing with a tool like Wireshark.
So what's the possible risk? Since users have to be authorized to call this endpoint, they know at least their own e-mail address and most likely used it to authenticate. So a user can't modify or acquire the data of another user, if properly implemented.
A problem which may be of concern is that it might be possible to check for a registered e-mail during the registration process. Depending on what kind of application you're developing, this might be an issue. To give a brief example of such a case: Imagine a catholic priest registered on a porn site or the e-mail address of your husband/wife registered on a dating platform.
So may advice: Force HTTPS and you are pretty fine to use them as a primary key. However, if you have the possibility to abstract this, I'd do so. A numerical key or username may be a better choice and also easier to handle - but it makes no difference. Imagine if you have an endpoint to acquire the user's data, including e-mail address. It just doesn't matter if you acquire this data by a numerical key or by the e-mail address. In the end, you end up with the e-mail address in the response's body. And if this body is accessible by someone, he can also access the username and password, thus rendering any security measurement you've taken useless.
Is it possible to search for an unknown collection that contains a document with identifying fields like an email?
My structure is like this:
Each user gets their own collection based on a unique ID. Each collection contains a user doc and a accounts doc. User doc contains the information about the user that I would like to search for. Accounts doc contains a collection of bank accounts that I want to transfer money between users.
My problem is that I don't want users to type in the long unique id to enter the collection but to type the email of the user that is inside the collection\user document. The email is unique.
Have I just made a bad structure for my project or is there something I can do?
UPDATE
Thanks, Alex and Frank for the feedback.
I went on and changed my structure to as shown:
/users/$uid/accounts/$accountid.
Did a java Query collectionReference = db.collection("users").whereEqualTo("uEmail", userEmail); and saving the document.getId() as a String userId.
I Then use the UserId in a spinner to enable the user to pick an account from the userId accounts collection.
As Alex said, there is no way to load data from a collection (with the client-side SDKs) unless you know the collection name.
But in this case, it seems like your collections are named after the user's UID.
That means that if the user is signed in, you can know their collection by:
String uid = FirebaseAuth.getInstance().getCurrentUser().getUid();
CollectionReference userCollection = FirebaseFirestore.getInstance().collection(uid);
A few notes:
It is much more idiomatic to store your structure with a top-level collection of users, and then a document for each user under that, and then subcollections for the other data under that. So for example: /users/$uid/accounts/$accountid.
The server-side SDKs do have a method to get a list of collections, for example like this listCollections method in the Node.js SDK. But these SDKs are only to be used in a trusted environment, such as your development machine, a server you control, or Cloud Functions, and not directly on the client. Even with this SDK though, you'll have to iterate the list of collections and check each in turn, because as said before: you can only read data from a collection of which you know the name.
If you're trying to look up the collection/UID for another user than the one who's signed in to the app, you may need to way to map an email address to a UID. Such functionality is not available in the client-side SDKs. But similar as the point above, there is a method getUserByEmail in the Admin SDKs.
Is it possible to search for an unknown collection that contains a document with identifying fields like an email?
No, you should know the name of your collection in order to be able to use it in your reference. There are no wildcards in Cloud Firestore paths to collections/documents. You have to identify every collection and every document by their specific ids.
I received a somewhat unusual request (imo) for a transactional web site.
I have to implement the possibility of having multiple shopping cart for the same user. Those really are shopping carts, not order templates.
I.E: A store with several departments ordering under the same account, but with a different person placing orders for a specific department only.
Having more than one user per account is not an option since it would involve 'too much' management from the stores owner and the admins.
Anyone had to deal with this before?
The option so far is to have names for shopping cart, and a dropdown list or something alike after login to choose the cart with some kind of 'busy flag' to lock the cart if it's in use in another session.
This is indeed a strange request. It would make much more sense if all the different departments used separate accounts, which belonged to some common group for the org. Anyhow, for this case something along the lines of what you described is probably fine. You could have a single shopping cart, but all the items added to it could have some sort of dept attribute that is used to demarcate.
One suggestion I have is that instead of having the user select the group/department after login, you make the department a required part of the login params. That way, you can know and inform the user right away is someone else is already logged in with the same account for that dept. It's pretty restrictive, but sometimes it's better to let the user know ahead of time rather than have them select items and then learn that the particular dept. cart is locked! Just my 2 cents...
I got similar domain model
1) User. Every user got many cities. #OneToMany(targetEntity=adv.domain.City.class...)
2) City. Every city got many districts #OneToMany(targetEntity=adv.domain.Distinct.class)
3) Distintc
My goal is to delete distinct when user press delete button in browser. After that controller get id of distinct and pass it to bussiness layer. Where method DistinctService.deleteDistinct(Long distinctId) should delegate deliting to
DAO layer.
So my question is where to put security restrictions and what is the best way to accomplish it. I want to be sure that i delete distinct of the real user, that is the real owner of city, and city is the real owner of distinct.
So nobody exept the owner can't delete ditinct using simple url like localhost/deleteDistinct/5.
I can get user from httpSession in my controller and pass it to bussiness layer.
After that i can get all cities of this user and itrate over them to be sure, that of the citie.id == distinct.city_id and then delete distinct.
But it's rather ridiculous in my opinion.
Also i can write sql query like this ...
delete from
t_distinct
where
t_distinct.city_id in (
select
t_city.id
from
t_city
left join t_user on t_user.id = t_city.owner_id
where
t_user.id = ?
)
and t_distinct.id = ?
So what is the best practice to add restrictions like this.
I'm using Hibernate, Spring, Spring MVC by the way..
Thank you
What you're asking for is not SQL Injection prevention. You need to ensure the user attempting the deletion is authorized.
As long as you check that the user accessing the page has the rights to delete the row your trying to delete (this would be checked in the Business layer), and ONLY allow the delete command if the user is authenticated and authorized to perform the action.
With hibernate you don't have to worry about sql injection. It always uses prepared statements, so you are safe.
As for your concrete case, this is not an sql injection. But to prevent it, make validation in the controller - whether the currently logged user owns the desired ID.
Depending on the size of the application, you can implement some general security scheme, with ownership settings, and apply it (using AOP).
I understand that i want to be sure, the the user is real owner of Book The question was how to accomplish it. And yes, i know that user is authenticated and authorized. But another authorized user can easy delete pages of another user.
This can be done like this...
User userFromHttpSession ...
Long bookId = load page, get bookId, load book, get bookId
List books = userFromHttpSession.getBooks();
... iterate over books and find out if one of the book.id == bookId
... then if book owner is owner of httpSession, then proceed Delete
It's like too many sql queries, and too many code, probably there are better solution. Anyway thank you for your answers
Just use your head, quote-escape* everything from an outside (or inside for that matter) source before it gets put in an SQL statement, and check data as it goes in. Or, use prepared statements.
*Edit: By "quote-escape" I meant functions like PHP's mysql_escape_string()