I’m trying to understand why Vaughn Vernon (in the Github Sample code for the red book) includes tenantId in every repository get or find method. Particularly those that are doing a basic getById.
In one example he uses username to provide the identity of a User, and the business rule that usernames must be unique within a tenancy, so:
class UserRepository:
public User userWithUsername(TenantId aTenantId, String aUsername)
Makes perfect sense.
But in his other BCs he is using an identity value object based on a GUID, but still combines that with a tenantId when retrieving from a repository:
class ProductRepository:
public Product productOfId(TenantId aTenantId, ProductId aProductId)
Even the CQRS example, uses a combination of tenantId and entityId for its repositories single get method.
Its ubiquitous throughout, but I can’t understand why it would be necessary. If a product, backlog item, forum, calendar etc all have a globally unique identifier, why would you need anything more to query them?
If using this to ensure only entities for a particular tenant can be retrieved - this doesn’t seem to be a responsibility expected of a repository, but rather authentication etc, and so why include in every repositories query method?
Note, I understand why having tenantId as an attribute of the entity is required, and how it could be used to enforce some unique constraint. When retrieving a product however, wouldn’t productOfId(ProductId aProductId) suffice?
He does touch on this with “In the case of a multitenancy environment, the TenantId instance is also considered part of unique identity.” What value does combining two GUIDs to determine identity have over just the one?
I don't think this is specific to DDD, but to multitenancy. Regardless of how you design your application, you need to be able to tell if a record or entity belongs to a tenant or another. So, even if the IDs are globally unique, therefore don't need another ID part for deduplication, you still need the tenant Id.
On a more practical note, you can find the utility of the tenant Id on every entity if you need to provide a restful API on top of that data. Most likely, your API structure will look like the following:
api/{tenantId}/entity-type/{entityId}
You will have to validate that the user doing the request has access to the given 'tenantId' based, for example, on the claims in the authentication token. If the user has access, then you'll read from the database. But, if your repository only accepts 'entityId', then it will return that entity regardless of the tenant it belongs to and a user from tenant1 could get data from any other tenant only knowing the Id. You can, of course, add a check of the tenant id after loading the entity, but sooner or later you will forget to add it. If instead, you follow the practice of always adding the 'tenantId' to your repositories, then this check is built into the queries themselves, making the whole process more consistent and efficient.
On a side note, there are other ways to add a tenant Id check on all your queries, which will achieve the same goal without having to manually pass it to every repository method call and query implementation. For example, you could put the tenantId on a context and inject it using DI resolving your Repository. With SQL Server, you could use row-level security, so that the tenantId check is part of a table policy, instead of adding it to all the queries on that table.
Related
I have a web-application secured with Keycloak. To keep the description of the service short, we have Users and Documents as entities in the service. The users may have access to none or more documents and may edit or read the document.
Currently we have roles such as Admin, EndUser, Developer etc. We then keep a database table outside of Keycloak that maps the documents to users and what user has what access level to what document. All our end-users have the EndUser role in Keycloak. Every single time an EndUser tries to read/edit a Document, we have to make a lookup in the database table for authorization.
We would like to migrate that table to Keycloak. As I understand it I basically have two options:
Create a lot of roles, two for each document with names such as doc_read_[DOCUMENT-ID] and doc_edit_[DOCUMENT-ID] and so on. Then assign the correct role to the correct user. The downside here is that the number of roles will grow A LOT. Also, the number of roles attached to a user will be very large.
Create a group for each document, with the name of the document id. Have different sub-groups for read/write and then add the users in the correct groups. The downside is that the number of groups will be very large. Also, I will rely Authorization on group names, so the list of group names has to be mapped to the token.
I do not want to add a user-attribute with the document-ids to each user. With this approach I can not get an overview of a document and see what users have access to a given Document.
What is the best practice here? Are there any other solutions to solve this issue? This must be a very common setup.
This is just my opinion.
From what I understand both solutions are suboptimal, adding a role per document is unnatural and too finer grain. And as you already mention this would lead to too many roles that probably you will have to add them into the token.
I would personally use Keycloak just for the authentication part and do the authorization part in the backend. I would also try to group the documents in a way that reflect which user roles are allowed to manipulate them.
Alternatively you might try to use Keycloak's Authorization features to handle that use-case, however I have never used it, so there is not much that I can say about this option.
In my opinion what you want to achieve is something that is very tied to your business logic, I wouldn't recomend depending on keycloak to do it. Your token would constantly grow and management would be a nightmare really.
I see no problem in having a service with good cache to lookup permissions, the bulk of the data won't change much over time.
In the systems, there may be data that is restricted in nature.
Sometimes access to specific entities should be easily restricted or granted based on user or group membership.
What is the best way to implement this in the microservice architecture?
#1
Should access control, managing permissions etc. be the responsibility of the microserive itself? Developers will have to implement access control, store, and update permissions for every service. Seems like not very robust and error-prone approach.
#2
Create dedicated microservice handling permission management? This service will be called by other microserives to check access permissions for each entity and filtering entities before returning results. Centralized permissions storage and management is an advantage but microservice will have to make a call to "Permission Service" for each entity to check access rights what may have a negative influence on performance. And developers still have to integrate access checks into their services what leaves space for an error.
#3
Make access control responsibility of the API Gateway or Service Mesh. It is possible to think of an implementation that will automatically filter responses of all services. But in the case when the microservice returns list of entities permissions should be checked for each entity. Still a potential performance problem.
Example
Consider the following synthetic example.
Healthcare system dealing with test results, X-Ray images etc. Health information is very sensitive and should not be disclosed.
Test results should be available only to:
patient
doctor
laboratory
Attending doctor may send the patient to another specialist. A new doctor should have access to test results too. So access can be granted dynamically.
So each entity (e.g. test results, X-Ray image) has a set of rules what users and groups are allowed to access it.
Imagine there is a microservice called "Test Results Service" dealing with test results. Should it be responsible for access control, manage permissions etc.? Or permissions management should be extracted to separate microservice?
Healthcare system may also handle visits to a doctor. Information about patient's visit to the doctor should be available to:
patient
doctor
clinic receptionist
This is the example of a different entity type that requires entity level access restriction based on user or group membership.
It is easy to imagine even more examples when entity level access control is required.
I came to the following generic solution.
ACL security model is used. Each object in the system has associated set of permissions. Permissions defines who and what actions can perform on the object.
Microservices are responsible for entity-level authorization and filter objects in responses based on permissions of the objects.
Central Access Control Service is responsible for the creation, update, and deletion of permissions for all objects in the system. Access Control Service database is the primary store of objects' permissions.
Permissions stored in microservices databases are synchronized with Access Control Service database using event-carried state transfer. Every time, permissions are changed an event is sent to the message broker. Microservices can subscribe to these events to synchronize permissions.
API Gateway can be used as the additional protection layer. API Gateway can call Access Control Service directly (RPC) to check response objects' permissions or load recently revoked permissions.
Design features:
A way to uniquely identify each object in the system is required (e.g. UUID).
Permissions synchronization in microservices are eventual consistent. In case of partitioning between message broker and microservice permissions will not be synchronized. It may be a problem with revocation of the permissions. The solution to this problem is a separate topic.
Looks like security is a part of business logic here. In both examples.
Then security could be a part of data scheme.
For example,
Patient can see his tests:
select * from test_result where patient_id=*patient_id*
Doctor can see all test from his medical department:
select * from test_result where branch_id=*doctor_branch*
I believe that to have separate MS for access control is a really bad idea and could lead serious performance problems. Just imagine situation that somebody with zero entity access tries to fetch all entities each time :) You will always need to handle larger result sets than actually needed.
Firstly, this is very bad idea to have a separate (per microservice) security model. It should be single always cross-cutting all application, because it can lead to a hell with access management, permissions granting and mapping between entities in different microservices.
In second, I assume that you are wrong with understanding how to organize microservices..? You should dedicate the principle of splitting functionality into microservices: by features, by domain, etc. Look at Single Responsibility, DDD and other approaches which helps you to achieve clear behavior of your MS.
So, in best case, you should have to:
Choose right security model ABAC or RBAC - there are a lot of other options, but looking at your example I guess the ABAC is the best one
Create separate MS for access management - the main responsibility of this MS is a CRUD and assignment of groups/roles/permissions/attributes to the people accounts.
Create separate MS for providing only permitted health information.
In third, how it works?:
With ABAC you can setup hierarchical roles/permissions (based on groups/attributes) - it helps you to resolve a delegation path of who is permitted to the data
Setup authorization (via auth-MS) and store the list of permissions (in session, cookies, etc)
Check access for a given user for a needed data in health-info-MS. Here we have several options how to do this:
If you use memory-grids (hazelcast, coherence), you can easily create filters with predicates based on security attributes.
If you're using SQL (hibernate, plain SQL, etc.) you should generate queries to return only permitted data - add security specific criteria to the where clause
Few more details about SQL queries with security check in where: before the SQL execution (if hibernate & spring is easy to do with spring-method-auth hook) you should resolve all permissions assigned to a user - you can do this with call to auth-MS.
Example
We created CRUD permissions for TestResult entity - VIEW, EDIT, DELETE.
The role DOCTOR can see any TestResults - so, it has VIEW permission
The role PATIENT can see only his/her TestResults
So, you create a business rules which provide the correct where clause for each business role (DOCTOR, PATIENT, LAB, etc.) and at the end the SQL request would be like:
For patient who has assigned VIEW permission:
select * from test_result where id=*patient_id* and 1=1
For patient who hasn't assigned VIEW permission:
select * from test_result where id=*patient_id* and 1!=1
NOTE: In business rules we can add 1=1 or 1!=1 to permit/restrict query result
I have an application where a user can be linked to several companies.
The manyToMany relationship with the company is a distinguished entity called Associate.
I'd like to give to this Associate entity the exact same role functionnality as my FOSUserBundle User entity has. Important : if a user has a role_manager for one company, it should not be given the rights to access specific features of another company he belongs to too.
Is there a clean way to do this?
I'd like to check for instance if $this->getUser->getAssociate->hasRole('ROLE_MANAGER') is true.
What if I give a role array to my entity Associate? I've read it's not secure enough? Why? What could someone do to break that security if anyway my users have to pass through FOS security login checks?
I've found an article where using a voter is suggested. But I don't want to filter routes, I really want to check the condition against the link between a user and a company, so if a voter is the solution, how would I use it?
EDIT: if a better solution not involving roles or with different logic exists, I am interested in learning about it!!
So in my case, I actually one user can actually be only linked to a maximum of 4 companies, each of a different kind defined by its category.
The official doc would suggest using ACL, defining a role for every company or store the data in the entity. cf first paragraphs of :
http://symfony.com/doc/current/cookbook/security/acl.html
I used a combination of roles and business logic. I've created roles for every type of company and since one user can only have one company per type, I just had to check for the type and the role-manager associated to the type.
See my voter here:
symfony2 call is_granted in voter : how to avoid an infinite loop?
I would like to find out all of the user which are associated in some roles. I have seen the UserLocalService that provides the method to find the users in a particular role. But I want a method to which I can pass an array of roleIds and it shall return me the list of users in those roles.
One way is to write custom SQL, but I would like to get it done by using the API only.
Is that possible with Liferay API??
Call the API multiple times? Create a new service that does this for you so that you only have a single call to (your) API?
I know, this is probably not the answer that you expected. Note that roles in Liferay can be scoped globally (to the whole portal, called 'regular') or to an individual site or organization. Thus just giving a roleId would limit you to the global roles (as the others would require the site's or organization's groupId).
Stepping back and looking at what you want to achieve, my first guess is that you have some semantics in being associated with a specific role - kind of like... a usergroup? A role is designed to give permissions to users, while a usergroup is designed to group users. UserLocalService also has getUserGroupUsers - this also only takes a single usergroup id, but at least it's a single (global) scope by definition and not ambiguous like roles.
I store user data in a MSSQL table called Users. What I want to is to have accessible all user's data for actually logged user (email, address, phone, if the user is subscriber etc.).
I don't want to use profiles so I decided to use custom MembershipProvider (or do you know some better, less painful way?).
What I don't understand is MembershipUser and Membership.
If I inherite from MembershipProvider, in overriden methods I control access data from and to database.
But how do I use inherited class from MembershipProvider?
If I want to authenticate user by using membership, I should do:
if(Membership.ValidateUser(string username, string password))
{
FormsAuthentication.RedirectFromLoginPage(string username, string password);
}
But where is class inherited from MembershipProvider? And when to use a class inherited from MembershipUser? And what is relation between Membership and MembershipProvider?
While it's not crystal clear on MSDN, it's not all that complicated. There's a trio of classes:
Membership: provides utility methods and a point of entry -- basically a Singleton (static class).
MembershipProvider: acts as a data accessor and factory for MembershipUser objects.
MembershipUser: represents an individual user.
A custom MembershipProvider is selected (by code in Membership) based on your application's configuration: configuration/system.web/membership. Here's where you bring your provider into play. Your MembershipProvider implementation must be written to access whatever data store you prefer for users: your User table in this case.
MembershipUser objects are only created through your MembershipProvider. The MembershipProvider.ValidateUser() method should check against your data store that the user/password combination is valid. The MembershipProvider.GetUser() retrieves user information -- use it within an access protected page and pass in System.Web.HttpContext.Current.User.Identity.Name as the current authenticated user.
This said, I hope you are sure you don't want to use Profiles, and really want to have a separate User table. If you are writing an internal application, using an existing Active Directory or LDAP-enabled data store would reduce administration costs and probably security risks. There are hundreds of things you can easily do wrong when going the MembershipProvider route. Do you use salted hashes? How are you protecting the User table against manipulation? MSDN covers only a fraction of the security issues you may face.
The specific provider used is controlled on the web.config. You can actually set more than 1 provider, and have a default one. Check: http://msdn.microsoft.com/en-us/library/6e9y4s5t.aspx.
When called like that, membership just uses the default provider. You would inherit MembershipUser, if you wanted to provide extra info for the user, but that will tie the rest of your code to your specific provider.