I have the following setup - the Spring SAAS REST service, which allows different companies to manage different events. And there is a rest client (a mobile app) also, shipped separately for each company.
I want to use keycloak for security stuff, and I have a question of how to separate one company from another.
I need companyA to not be able to access companyB event, and also need different roles within the company - some can create events, some can only read it.
First I thought each company will have own realm created in keycloak, but I learned that realm actually specified in the spring boot REST service parameters like
keycloak.realm=demo-realm
Which means it is only one realm per REST application. And I don't want to configure REST service instance per client. I only want one REST rule them all.
Am I trying to use something which really doesn't fit my use case?
Will it be right way to have a keycloack Group configured for each company, and make a logic in such a way that users of one group won't have access to what is created by other group. But then it actually feels wrong, since as I understand group are supposed to be used in a different way - to have admin group and user group, etc, segregating users "vertically" by "privileges", and not "horizontally".
Can you please suggest a right approach for this problem?
I would implement a custom protocol mapper which loads extra user permissions for your application and stores them in a token. This way, you use a single realm and if there are more companies in the future it scales well. Here you've got an example of how to implement it.
Basically, the otherClaims field of the access token is a JSON field that allows a map of properties to be set. You could add a field here such as:
userAccessibleCompanyIds: [1,3,4]
How to load the company ids for the concrete user? You can access your application database from the mapper or get them using the REST API.
Then in your application you need to have a control of what the user accesses. What I do is decode the token and see if the user request suits. If not, return a 403 response.
Related
I'm exploring the Spring framework, and in particular I am working on a Cinema Management Application that will be connected to a React.JS SPA (Single Page Application).
The problem is the following. On my database I do have three different tables representing three different types of users, namely Admin, Customer, and Cinema_Employee.
For each type of user, I created a #RestController with a list of RequestMethods that a particular user is able to perform:
"/admin"
"/customer"
"/employee"
What I am trying to achieve now, it's to secure each endpoint offering three different login pages that will handle the authentication the respective type of user.
How can I set up three AuthenticationManager that handle different Authentication objects within a SecurityConfig class given these requirements, and most importantly, how can I override the Authorisation mindful that each user once has logged in, will have access only to the respective endpoint?
I looked carefully at other examples online, and most of them are radical different, following a pattern where the database has another additional 'Authorities' table aside the 'user' one that stores the credential. In my case this solution cannot be applied, not only because the whole design would become redundant, but also because the name of the table where the application will perform the authentication check against, explicitly imply the authorisation that a given user has inside the system.
Your design sounds strange to me.
A user should have a role, e.g. Admin, Customer, Employee and based on the user's role he gets access to methods or not. Have a look at role based access control concepts. For Spring Security there is for example this tutorial:
https://www.baeldung.com/role-and-privilege-for-spring-security-registration
Assume I have a database composed of user and projects. A user has a one to many relationship with projects. The user can do operations using rest endpoints.
The problem is:
how can I verify that this user owns this resource?
I don't want a malicious user to change an id then suddenly he views another person's project details/images/etc. Think of it like a social media (my app is not a social media but trying to illustrate the issue): where you can view only your images but not another person's images despite having the same "status".
Most spring-security blogs online is using a role based approach. I have no idea what to even search for in this case (tried multiple search queries to no avail).
One option is to run some sort of multijoin query on every resource request/operation till I reach that resource id and check it's owning user to check if it is the logged in user. However, I am not sure if this way is efficient since there are multiple tables in a chain in the real app (could have a lot of joins if I go this way manually; example: user -> project -> tasklist-> ... -> Note; deleting a note would trigger a large chain) or how to make the "authorizer" use it.
what are my options?
In case it matters:
I am using spring-boot + hibernate/JPA + spring-security
Spring Security has the following concepts:
Authentication:
Proving the an actor is who it vouches to be. Authentication is done using credentials, where credentials can take any number of forms - username/password, token, hardware key, etc.
You can set up Spring Security with a set of acceptable authentication providers.
Authorization:
Given an authenticated user, deciding if that user has access to a given resource. Where the resource can be:
An HTTP endpoint.
An Java Method.
A property of an object.
What you want to do here is provide a custom authorization scheme.
You can read about Spring Security's authorization architecture here, including how to set up custom authorization. Once you're ready you might ask specific questions as you go.
First, I'm well aware of the multi-realm approach to multi-tenancy in Keycloak. I've taken over a legacy project where nobody thought of multi-tenancy. Now, two years later, suddenly, the customer needs this feature. Actually, the microservices are prepared for this scenario out-of-the-box.
The customer develops a mobile app that authenticates users via API on our keycloak instance with an account number (as username) and a password. Now, he'd like to add an tenant id to the login information.
The customer wants to avoid using several endpoints as a multi-realm solution would demand.
One first idea was to just concatenate tenant-id and account-id on registration. But that's a smelly approach.
So, my thought was that there may be a way to configure Keycloak in a way that I add a custom tenantid field together with username that acts just like a composite primary key in the database world.
Is such a configuration possible? Is there another way to achieve multi-tenancy behaviour using a single realm?
I can't say for sure, but after some research, I found this:
This website lists all of this together with more information:
https://lists.jboss.org/pipermail/keycloak-user/2017-June/010854.html
Check it out, it may help with your data organization in key-cloak.
Late to the party. But maybe for others who are interested. You could try the keycloak extension keycloak-orgs. I am currently building a test stack with it and I am pleased.
A tenant in keycloak-orgs is an organization. You can map organizations and their roles to token claims with a built-in mapper.
"organizations": {
"5aeb9aeb-97a3-4deb-af9f-516615b59a2d" : {
"name": "foo",
"roles": [ "admin", "viewer" ]
}
}
The extension comes w/ an admin interface. From there you can create organizations and assign users to it. There is also a well-documented REST API on the Phase Two homepage (the company who open-sourced the project).
The maintainers provide a keycloak docker image that has the relevant keycloak extensions installed.
If you want a single realm and singe client that serves many tenants, you can just use custom user attribute and e.g. add key(s) "tenant=MyTenant" and then add a client scope and a mapper to include user attributes that has key=tenant
Then the token will carry the user's tenant(s) and you can use that to filter data, add to newly created data etc.
It's only like 4 steps in Keycloak:
Add User attributes using a key-convention.
Add a Client scope that will represent tenants.
Add a mapper to extract the User attributes.
Add Client scope to the Client in use.
Wrote about it here: https://danielwertheim.se/keycloak-and-multi-tenancy-using-single-realm/
I have a requirement where I need to use role based access to service methods. I have restful services and i use spring-data to interact with MongoDB.
There are some of the restrictions that I have. I deal with a document in DB called "Organization". In each organization, I know who are the Admins. I do not have a repository of users who can access the services.
So the only way I can enforce some access based rules is to check if the logged in user is one of the admin's configured for each organization and then allow the user to access the methods.
Should I think of applying Spring security in this case? Otherwise will a simple check on user against the configured admins in the database document help? Can I make this check at a single point so that I can apply it to service methods based on my use case needs.
Please provide your suggestions / thoughts on how to go about this.
If you use Spring Security your rest methods can take advantage of a passed-in authenticated Principal object (example here) whereupon you can do whatever extra validation desired (such as checking if the admin is good for the given organization requested, etc.) There are many other parameters also available, perhaps allowing for this org checking to be done once and stored in the session object.
I'm currently working on a iphone/android project where the mobile talk ta java backend server through REST API calls.
The Java backend is done using Spring and its Authentication system (with a JSESSION ID token)
I'm not an expert in security but I can see that if not implemented correctly there could be quite a lot of issues.
One of my biggest concern would be user creation for example.
When the app creates a user it simply makes a POST request to (url.com/rest/create)
How can I avoid, server side, that a malicious user puts this url in a loop and create thousands of users ?
What are common best practices to secure API calls ?
Is the Spring Authentication token enough ?
Thank you!
It's not really possible to prevent a client from making many calls to your server. A malicious user can create a script or application firing requests to your server.
The solution is to authenticate and authorize the calls to the server. You give certain users (for example administrators) the privilege to create users. You trust those users to behave in a correct manner. You have your users authenticate before they call the APIs on your server. Then, on the server side your check who the user is and what he/she is allowed to do.
If you are still concerned about privileged users not behaving, you can assign quota to each user on the actions they are allowed to perform.
The hightech solution (with as much framework fuctions as possible) would be
first: have a created-by and created-date field at the entity you want to protect (I recommend to use Spring-Data-JPA Auditing for that).
second: create a custom spring method (or web) expression method that is able to check how many items the current user has created in the (for example) last 10minutes and if this are more then (for examle) 20, then return false (or make them parameters of the method).
Then you can protect your method (or url) with that expression (#PreAuthorize("createsNotExeced(10, 20)"))
But this is the high tech solution - it would be quite intresstion implementing them when one wants to learn spring security. (and you would need to add some caching, but this is also a Spring feature).
The lowtech solution would be: put an list of timestamp in the users session, and add an new item to that array whenever the user creates an new item. When the last (for example) 20 timestamp enties are within the last (for example) 10 minutes, then throw an TooMuchHeavyUseRuntimeException or somthing else.