Service-level Rights and Protected Endpoints
(Related to - OLMIS-1456Getting issue details... STATUS )
Problem Description
There is a need for a service's API endpoints to allow special access that cannot be sufficiently described using rights. A couple of examples:
- Services need to access data from other services on behalf of a user where the user would not have permission to access on their own.
- Example: a user needs to view a list of facilities and their details for a UI screen. This would normally require a right like FACILITIES_MANAGE (managing facilities) if accessing the endpoint directly, but since a service is calling the endpoint, it should be allowed on behalf of the user.
- Some endpoints should not be "public" at all, but should only be called by another service. ("Public" is in quotes because all endpoints could theoretically be discovered, even if not published.)
- Example: creating, updating or deleting rights in the system. Users should never be allowed to do this; only services would manage rights in the system. Services would create/update/delete rights relevant to themselves that could be assigned to roles and users and then verified when their endpoints are called.
- Example: access to a notification service. This service would handle processing notification events and routing them to different places to notify (email, text, message in OpenLMIS UI screen). It does not make sense for a user to call these endpoints, nor is it clear what rights would need to be checked to allow access to the endpoint.
The main impetus in particular for this research is how to allow access to reference data in the Reference Data Service.
Possible Approaches
- Allow read-only access to only authenticated users - users would be able to view (reference) data, but would not be able to change it. Viewing data would be allowed for any logged in user.
- For the system rights example, the mechanism to disallow changing other reference data would have to disallow access to all rights endpoints.
- Con: there seems to be a security issue. While the endpoints are not public to the world (which would be a non-starter) so the endpoints require some authentication, allowing anyone in the system to view all of the data seems like a security breach.
- Con: this doesn't address the notification service example–how would a user be able to create a new notification to be processed?
- Add private versions of all endpoints that would be used by services
- Pro: this does address all examples. Users who would need to access endpoints could access the public one, while services would access the private ones. Any endpoints that should not be called by a user would not have a public version of the endpoint.
- Con: this seems like it would double the number of endpoints to maintain.
- How would we ensure these endpoints are "private"?
- Would we really want to create private versions of all endpoints for all services? If not, we would still need to determine which endpoints to have private versions
- Differentiate access tokens between service and user - any access token passed to an endpoint would be checked with the auth service to see if it's a service-based token or a user-based token
- Pro: this does address all examples. A service would have the credentials to generate a service-based token. Any service that needs to check if a token is service-based (or user-based) could consult the auth service to check and allow or deny accordingly.
- There is some complexity here to have different token types and for services to consult the auth service to check the type. But this might not be much complex than the current auth implementation.
Chosen Approach and Design
Option number 3 was chosen as it seems to have the fewest drawbacks while satisfying the requirements.
A small spike with some proof-of-concept code was done in the Auth and Reference Data Services to see how it could be implemented for different scenarios. (This code can be found in the feature/OLMIS-1456-service-level-rights branches of the two repositories.) It was found that the main OAuth client credentials used to generate access tokens could be used to generate only service-level access tokens once the password grant_type is removed. Then, an additional OAuth client with its own credentials was created to generate user-based access tokens.
- Allowing only service-level access to manage rights in the system - This was successfully done by adding a matcher to the PUT /api/rights endpoint to the security configuration to require the token to have a TRUSTED_CLIENT authority. Based on how the auth service credentials are set up, only the service-level access tokens have the TRUSTED_CLIENT authority, so only those tokens can successfully use the endpoint. User-based access tokens get an "access denied" error message.
- Note: this would also be the way to implement special access to something like a notification service. Only service-based tokens could use the Notification Service endpoints and it would be enforced using the same approach.
- A service accessing an endpoint from another service - This looks like it is mostly implemented in the Requisition Service. All calls to the Reference Data Service have a method obtainAccessToken() which makes a call to the Auth Service to get an access token, using the main OAuth client credentials. As a result, the method automatically generates only service-based access tokens. The only thing that needs to be changed is for the method not to use the access token in the original HTTP request.
OpenLMIS: the global initiative for powerful LMIS software