Security
← Previous post Next post →Security
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:
- Authentication
- 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
- Authorization
- 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
API Keys
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
Direct access
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-Key
header 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
APIKey
query parameter to your call:
$ curl -s http://localhost:8080/sor/1/review:testcustomer/demo1?APIKey=<your_api_key>"
Java client
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 DataStore.class
to AuthDataStore.class
and wrap it using a DataStoreAuthenticator
.
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);
Both dataStore1
and dataStore2
use the same underlying client but calls to each will be authenticated using
their respective API keys.
Although not presented here BlobStore
, DataBus
, QueueService
, and DedupQueueService
all have a corresponding
usingCredentials()
method, Auth
and Authenticator
classes and can be used following the same pattern.