EmoDB is a shared service that is designed for multi-tenancy. While this is a work in progress (for example, all EmoDB tables currently share a common namespace) there need to be security protections to ensure the integrity of the system.
These protections include:
- Prevent system access by unknown entities
- Prevent a team from accidentally performing updates intended for one environment (e.g.; QA) in another environment (e.g; production) by assigning different credentials per environment
- Provide an audit trail for all operations
- Prevent a team from accessing, mutating, or otherwise disturbing resources managed by another team
- Allow a team to define the rules for protecting and sharing their resources
To provide these protections EmoDB uses API keys. Each individual or team can be granted an API key which can be narrowly permitted to perform only the actions required by the grantee. This includes restricting access to specific resources (e.g.; permitting access only to tables matching “my_team_prefix:*”) as well as restricting access levels to those resources (e.g.; read-only vs. read-write).
How to get an API key
We recommend creating a protocol for the creation and distribution of API keys. Each API key request should include the following:
- The individual or team email address
- A description of who will own the key (typically your team name)
- What type of access the key needs (specific resources, read-only vs. read-write, and so on)
- What environments will be accessed (QA, integration, production). If the requester needs access to multiple environments they should get a separate key for each environment.
For more information on API Key administration, see Security Management.
Using your API key
If you are making API calls directly against the EmoDB API, such as through
curl, then you can pass your API key
in either of the following ways:
- Create a
X-BV-API-Keyheader with your key’s value:
$ curl -s -H "X-BV-API-Key: <your_api_key>" http://localhost:8080/sor/1/review:testcustomer/demo1"
- Add a
APIKeyquery parameter to your call:
$ curl -s http://localhost:8080/sor/1/review:testcustomer/demo1?APIKey=<your_api_key>"
For the simple case where your entire application uses a single API key use the
usingCredentials() method in the
client factory. For example, the following creates a
DataStore client using a single API key:
String emodbHost = "localhost:8080"; // Adjust to point to the EmoDB server. String apiKey = "<your_api_key>"; DataStore dataStore = ServicePoolBuilder.create(DataStore.class) .withHostDiscoverySource(new DataStoreFixedHostDiscoverySource(emodbHost)) .withServiceFactory(DataStoreClientFactory.forCluster("local_default").usingCredentials(apiKey)) .buildProxy(new ExponentialBackoffRetry(5, 50, 1000, TimeUnit.MILLISECONDS));
If you application uses multiple API keys then it would be inefficient to create a separate service pool for each data
store. Instead you can use a
DataStoreAuthenticator to re-use a single data store pool for multiple API keys.
To do this change
AuthDataStore.class and wrap it using a
This time the previous example would be updated to the following:
String emodbHost = "localhost:8080"; // Adjust to point to the EmoDB server. // Use AuthDataStore AuthDataStore authDataStore = ServicePoolBuilder.create(AuthDataStore.class) .withHostDiscoverySource(new DataStoreFixedHostDiscoverySource(emodbHost)) .withServiceFactory(DataStoreClientFactory.forCluster("local_default")) .buildProxy(new ExponentialBackoffRetry(5, 50, 1000, TimeUnit.MILLISECONDS)); // Create a DataStoreAuthenticator from the AuthDataStore service pool DataStoreAuthenticator dataStoreAuthenticator = DataStoreAuthenticator.proxied(authDataStore) String apiKey1 = "<your_first_api_key>"; String apiKey2 = "<your_second_api_key>"; // Convert back to a DataStore view DataStore dataStore1 = dataStoreAuthenticator.usingCredentials(apiKey1); DataStore dataStore2 = dataStoreAuthenticator.usingCredentials(apiKey2);
dataStore2 use the same underlying client but calls to each will be authenticated using
their respective API keys.
Although not presented here
DedupQueueService all have a corresponding
Authenticator classes and can be used following the same pattern.