Suppose I am writing software for an insurance company. I use DDD and have a bounded context with entities related to the customer account, address, and related information. If a customer logs in and makes changes to these entities it is straightforward.
But as it is, there is another portal, which the internal support staff uses. Now if a customer calls in to update their account information (not a policy but phone number, address etc.). What is the best and clean way to do it?
Make a call to bounded context used by the customers (HTTP etc).
Allow making changes from the internal portal (probably modifying data across the database/schema boundaries).
Raise a domain event about changed object, and handle it on the customer side application (again this is an event related to a domain object in another context and we are not writing to database from the internal portal).
Bounded Contexts exist because they have a meaning in the domain and a reason to exist in your system. They don't exist to serve a specific type of user or a specific client application. In fact, it's most common that a Bounded Context serves multiple types of users and multiple client applications, especially in software systems with a customer-facing application and a back-office.
In your Insurance domain, both the customer and back-office users will directly interact with the CustomerManagement BC as this BC is there to serve both, not only the Customer. The same way that both the customers and back-office users will be able to see the customer's policies, their coverage, etc.
Technically, the implementation will depend on your architecture. Both client applications could talk to the same API and even share endpoints. Or you could provide an API per client, and these APIs would directly talk to the same BC (either calling the same code or by making remote calls to the same remote service).
You already answered your questions if your business allows user to directly change info about customers directly by phone call this is the case your system should allow to do it. This is what about DDD to focus on the domain and domain is business logic from your example I see the context customers management and some other contexts that use this info but if some one requires to change info about customers this is the business case and system should allow to do it!
Related
This question is really two-fold with emphasis on the most important question.
Aggregates define the consistency and transactional boundaries of a system.
Say your company provides some service through an app and you are about to add a new feature through an integration with a third party. The third party has its own API and you need to create the user in their system when they choose to use the new feature. Now, you're not a monster, so you allow users to toggle off the feature if they had previously started using it. This means you must also be able to delete the user in the third party API.
If we were to design an Aggregate for such a feature (lets say it is a subscription), where should we put the interaction with the third party API?
What if the third party we integrate with allow the users to remove themselves from their system bypassing our app completely? Now our aggregate would be out of sync.
Generally, an aggregate enforces consistency not on a system level, but over some data. The concept was introduced in the DDD blue book, and at a time where most solutions relied on traditional large-scale SQL db's, and it intended to remove responsibility for consistency from the storage layer, and into business logic. It did so because that's what consistency is: business logic. If you have to decrease stock when you sell something, this is a consistency rule, and one which should live in application code, with other business logic instead of being delegated to a stored procedure.
With that in mind, any aggregate you'd have wouldn't (indeed couldn't) enforce consistency with the outside world (it's not data you own and hold internally). However, what you could do is introduce two (or one combined) process managers:
One process manager that would enforce the policy of "when a user asks to get deleted, delete them from the external system". This would observe internal requests and call out to the 3rd party service to delete a user when the user requested to be deleted
One process manager that would enforce the policy of "when a user gets deleted from the third party service, mark them as deleted in our system too" which would do pretty much what the policy says
You may or may not have a subscription aggregate. What would determine that is if you need to enforce some consistency around data you own and are responsible for.
Please consider the following scenario.
The system has a set of business owners (i.e. users of the system)
Each business owner is mapped to a set of customers
The business owners login to the system in order to manage their customers.
The business owners will select a single customer first, and from there onward his session is bound that selected customer.
I have a set of microservices, each requires a Customer-ID for processing. i.e.
Microservice A exposes GET /resource-a/{customer-id}
Microservice B exposes POST /resource-b/{customer-id}
etc.
The Customer-Id is considered to be a sensitive information - so that it is in encrypted form.
However it's still vulnerable to privilege-escalation. i.e. one business owner may mistakenly share the encrypted customer-id through bookmarks etc.
I want to avoid server-side authorization of customer-id against business owner because it is known to be a slow operation and and 90% of the APIs will have to repeat this authorization process
All the microservices are stateless, so that it's not possible to encrypt the Customer-Id with the session-id as the key (since no stateful microservices/ session)
Also I don't think it's a good idea to do this authorization at the gateway since it's not intended to perform such business logic.
How can I prevent privilege escalation in this situation?
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
currently I develop a backend based on the microservice architecture.
Unfortunately, I have a problem how to realize the authorization.
So let me explaine my system - there are the following services:
OAuth 2.0 Service (issuing JWT)
Group Service
Some Ressource Services (e.g. ToDos Service)
Every user is in one or many groups.
Each resource (like a ToDo list) also belongs to a group.
That means if some user creates a todo list, that list gets stored in the name of the group.
Szenario:
User A is in group A
User B is in group A and B
User C is in group C
User A creats a ToDo list in group A.
User B modifies this ToDo list (he is allowed to do this since he is also in group A)
User C also tries to modify this ToDo list, but he shouldn't allowed to do this since he is only in group C.
Has any body a great idea how I could realize this in a microservice architecture and keep the dependencies between the services on a minimum?
Certainly, I could ask on every request the Group Service if the user is in the group to which the resource belongs to. But so I get a really hard dependency between the Resource Services and the existence of a Group Service - I like to avoid this dependency.
Another option would be to store all groups, to which the user belongs to, in the access token. But with this option the client has to ask every time the OAuth Service for a new token when the user gets a member of a new group.
So is there any other option how I could realize this szenario?
So, you have three domains:
Authentication: responsible for identifying the user
Authorization: responsible for restricting access to resources
Todos: your core domain
You have done well identifying three bounded contexts, one for each domain and implemented in three microservices (MS). You are conforming to the best practices regarding DDD.
No, your question is how could you integrate those three microservices in such a way that the system is resilient, i.e. its microservices continue to work even if some of the other microservices fail.
You have two options regarding integration (communication between microservices):
Synchronous communication: every time the Todos MS receive a request, it queries the Authorization MS to check if the user is permitted to do what it wants. This has the advantage that is simple to implement but it has the disadvantage that is susceptible to cascade failure: if the Authorization MS fails then this MS also fails. So, this option is not good for you.
Asynchronous communication: somehow in the background, there is some data that is replicated from the Authorization MS to the Todos MS. You have at least two options for this replication to be done: a) in cron or scheduled tasks or similar and b) using a event driven architecture. This has the advantage that provides more resilience but it has the disadvantage that is more complex, harder to implement. This option seems to fit your need.
Further reading:
Building microservices
I would suggest to put the authorisation handling into a API gateway. When there is an incoming request the following steps are executed
1.The API gateway hits the OAuth server to get a transparent token which contains the access roles and groups of the user
2.The API gateway then calls the to do services with the access token , which the to do services use to decide if a particular user is authorised.
The advantage of this integration pattern is that the individual services don’t have to call the group service to get the roles.
I have a client that has chosen to use Business Catalyst for their public facing services, and they want to access roughly four different servers for various activities. The design team has put forth a requirement to be able to log into these various servers using unique login forms on Business Catalyst for each destination.
The first issue is in having a login form within an https page. Business Catalyst has "secure zones" which can be exposed to users that have already logged into Business Catalyst, and I believe there is a way to do so without login by opening up the secure zone to a range of IP addresses. That doesn't feel like a good faith move by any developer (the secure zone is an oxymoron if it has to be exposed to everybody), so let me know if that passes the insanity check. Having the user login to Business Catalyst just so they can login to one of the secure servers is not going to work from a UX perspective.
The second issue is that Business Catalyst states that it must be within a secure zone before it can do any work with the external tools I need it to work with. This might be solved by resolving the first issue, but this has more to do with form queries in general. I have content modules that need to query these servers, without login, to pull non-critical information down as a response.
I have performed a non-exhaustive search over this weekend to try and find a graceful solution to this challenge, but it doesn't appear to be something that Business Catalyst was designed to handle.
For those of you who TLDR;
I need a secure way to login to 1 of 4 servers from Business Catalyst without login to Business Catalyst (Current implementation theory noted above).
I need a way to query non-critical information responses from 1 of 4 servers, again without login to Business Catalyst (Such as returning cost estimate results).
It is not acceptable to have the user login to Business Catalyst, just to pull queries or login to 1 of 4 servers.
It may not be possible to allow a user to access the other servers using their Business Catalyst session handles.
When user logs in to BC, he will get cookie in form VSVxxxxx, where xxxxxx is BC site ID. Content of cookie is hashed active session ID. Then BC exports two web service API - CRM and eCommerce. In CRM web service there's method Contact_IsLoggedIn, which take two parameters - user ID and session ID. Session ID is one from user VSVxxxxx cookie. It returns true/false, whether user is really logged in BC.
Note that BC have bit strange session handling... it lasts for 30min. no matter whether user clicks on site, or no.