3

Our team has decided to move to a microservice approach for newly developed features. We've decided to keep a database/datastore per service and to split the services along the business function they perform. As we move this direction we're creating the services as necessary. This means that not only have we created a new service for this new functionality but we may be creating a new service for functionality and data that already exist within the system (within the monolithic application that is being deconstructed).

After working on our first service we've come up with an interesting dilemma. How do we secure data based on users' relationships? This is not security based on authentication or authorization. We're currently using a token passed from our SSO provider to insure authentication and are developing JWTs to add authorization.

Quick example

I'm going to describe a basic scenario:

Suppose we have a business object which is owned by a user (or group). A lot of microservice architecture examples use orders, etc. Say we have a user, U1, which has created an order object and we have another user which is a manager, M1, which oversees this user. Also, we have other users, U2, U3, and U4, which are part of group, G1. User U1 and manager M1 are not members of this group.

Here are some questions we would want answered:

  1. Manager, M1, logs in and should see his downline employee's order objects, but no other manager's downline information.
  2. Members of G1 should be able to see each others order objects.

Clarifying the scenario some:

The goal is to be able to have a user identified (the logged in user or given id) and filter any data within the service to what that user is permissible to see. i.e. A user may have the role "orders" and is able to utilize the service (orders_read, orders_write), but shouldn't have access to objects outside of his relationship to other users (can see his and those he manages). Even if there were a permission called "orders_supervise" the user inheriting this role needs to have context as to whom he is supervising. This is important when we're talking about scenarios involving data that is sensitive nature, a manager viewing their employee's salary or performance review.

Monolithic Answer

Assume that our monolithic application structure has defined groups of users and relationships between users. Without going in depth on the data structures, it could be assumed that there are linking tables for these structures. For groups a linking table would have a group identifier and a person identifier. For relationships the table could be between person identifiers that make up some type of relationship.

Another method of handling it, and this is the way it is done in one of our legacy applications, is to have a column on the person for each relationship. For U1 they would have M1's id in their ManagerId column.

Depending on the question above the monolithic application could use a query that joined across these tables. The query would produce a projection of data which was filtered via the join to either linking tables in order to answer the above questions. The database in the application contains all relevant data to answer these questions.

This approach is how we currently function. It produces results very quickly since the data is filtered at the database level. The relational database allows the normalized data to be updated in a quick fashion which would filter through to allow any changes to be reflected pretty quickly in the answers to the questions.

Framing this in a microservice architecture

When our team is looking at this scenario in a microservice architecture we're considering different business capabilities: relationship management, group membership, and orders (not to mention users or authentication, etc).

If we were to place these concerns into two services (or three) with the order information separated from the relationship or membership data how do we handle security based on these relationships?

Is this where we would be duplicating data between services and data stores? As in, should we be including group data within the orders microservice? This doesn't seem correct as it violates a separation of concerns. However, it does support having a reactive services where this one service isn't dependent on another.

Thinking of the solution where duplication of data is the way to go, this could get messy for this common data structure. If our system is dealing with business objects that are mainly subjected to security based on relationships between users and groups it would seem that almost every microservice would have this data by default. How does this duplicated data get sync'd? Aside from a technical solution (redis) is there a service design pattern we should be following?

Additional Thoughts

The question of how to perform data security based on relationships between users and groups seems to not be answered in a well defined manner in any pattern we've researched. Topics such as authentication and authorization are fairly well covered. It seems that we can't be the first to ask this question. Are we framing the question wrong, coming at it from a bad perspective? Does a microservice architecture pattern already address this concern?

  • Are you trying to restrict the data that is returned from withing the microservice API's based on logged in User? or are you trying to create separate API "endpoints" which are restricted based scopes and different user claims / roles? – hanzolo Dec 5 '17 at 19:23
  • @hanzolo, I added some additional clarity there to say that the data should be filtered within the microservice. One thought was to have an orchestration layer which handed requests to services and handled their responses. That layer could introduce the security but we felt that it wasn't the best approach. – Mike G Dec 5 '17 at 19:41
2

There is no need to duplicate data, instead each service needs to have the proper data context.

Let's say one has users and orders. Each order at minimum is going to be tagged with a user Id that placed the order. The order data context has no idea of the relationships of users or any addition information, only the User Id is needed.

The user service would know details about the user and the relations between other users.

So, one would have two services, user and order. The user service would have a method like:

GetMyUsers(UserId)

This would return the list of users that user can "see".

Then the order service has a method like:

GetOrders(List<UserIds>)

And Potentially...

GetMyOrders(UserId)

One could have a 3rd service that could orchestrate the events if necessary, although in this case it may not be necessary since it seems sequential.

  • We considered this pattern. Our concern here is that you could have a scenario where a user that was high enough in the "hierarchy" could see hundreds or thousands of users. We've avoided this in the past due to technical limitations (like passing thousands of ids into a prepared sql query). Currently we have the expectation on staying with MongoDB, does executing queries make since in this manner? – Mike G Dec 6 '17 at 3:14
  • One additional thought: We also wanted to avoid having the microservice doing the filtering in memory. Our thought (untested) is that offloading filtering to the datastore would be better perfoming. – Mike G Dec 6 '17 at 3:26
  • The concern is valid, normally I would try and do the filtering or limiting in the database so that a large result set is not passed through the layers or cached in memory. For example SQL Server has keyset and offset paging which can be implemented right in the query/stored proc to limit or page the results back. I am not well versed in MongoDB, but perhaps there are similar ways to page data internally. – Jon Raynor Dec 6 '17 at 15:23
  • MongoDB paging link...codementor.io/arpitbhayani/… – Jon Raynor Dec 6 '17 at 20:19
2

I think you're asking about implementation details for your specific scenario and environment. However, here's my input based on my experience and from what I've gathered from your question.

In the web services world, your routes would have filters:

/managers/{id}/orders (M1) - a single manager and their orders. Using some sort of Resource Authorization scheme, only a manager would be able to access that endpoint.

and then either same endpoint or another:

/managers/orders (for G1), all managers and their orders

Again, only managers could access that route. and you could again use some kind of Resource Authorization (roles / claims) to determine if the user has access to that route (a member of G1 or not).

You'd still have to filter your data response using the users identity and membership configurations, but that's all how you build your app services and such. With the User Claims info and the route filters, you should be able to make decisions on how to return the proper data, and in my opinion, that's when the implementation specific details come into play.

just off the top of my head ..hope that helps.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.