Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reworked API key management to use internal IDs instead of API keys #99

Merged
merged 4 commits into from
Apr 20, 2017

Conversation

billkalter
Copy link
Contributor

Github Issue

50 (partial)

What Are We Doing Here?

The current API administration task uses the API key itself as input. However, API keys are supposed to be secret, and in our experience user's can't be trusted not to communicate their API key over a public channel when they need new permissions, such as by posting the key in a chat client or an email.

Additionally, as Emo moves toward distributed API key and permission management there will be a separation between individuals who grant specific roles. For example, you may go to team A to get permission to read from tables "team_a:*" while you may go to team B to get permission to read from tables "team_b:*". In both cases you may not trust giving your secret credential to anyone on either of those teams, but with the current API there is no alternative but to do so in order to for them to grant your API key a role with the new permissions.

This PR changes the API administration task to operate on the immutable internal IDs instead of the keys themselves, now generically referred to as "authentication IDs" or, for API keys in particular, "keys". So as not to break existing code or data the IDs are still referred to as internalId, even though it is used both internally and externally as a non-secret public identifier.

This PR consists of two commits. The first is the actual work for this PR. The second is simply a rename of RoleUpdateRequest to RoleModification. This was done for two reasons. The first is simply to mirror the class name AuthIdentityModification. The second is that RoleUpdateRequest was being used to both create and update roles, so it was a bit of a misnomer. The new name is more generic and less confusingly applies to both operations, though if there's a better name for it please make a comment.

A few notes:

  • Even though the effect of the change is relatively minor as a side effect it required rewriting the API for AuthIdentityManager to work on internal IDs instead of authentication IDs. As such this PR encompasses more changes than I would have thought necessary at the outset, but ultimately I think it flows well and closes a possible vulnerability in that it no longer stores the unencrypted API key as an attribute of the ApiKey class.
  • Another side effect of this change is that the logic for migrating API keys needed to be pushed down into AuthIdentityManager. This is actually a change for the better since it allows for an atomic migrate operation, and arguably it should have been implemented this way in the first place.
  • As with role management, a new DataCenterSynchronizedAuthIdentityManager was introduced to ensure each mutative API key operation is serializable within the local data center. For now this can be managed by only running API key tasks within one data center; in the future more changes could be added to enforce this.

How to Test and Verify

  1. Check out this PR
  2. Create roles and API keys using the respective tasks
  3. Verify that all operations behave as expected.

Risk

Risk is relatively low. The format of the data actually stored in the authentication tables __auth:keys and __auth:internal_ids is unchanged, so there shouldn't be a migration issue. The biggest risk is regression, that a change in the API key manager breaks permission checks.

Level

Medium

Required Testing

Regression

Code Review Checklist

  • Tests are included. If not, make sure you leave us a line or two for the reason.

  • Pulled down the PR and performed verification of at least being able to
    build and run.

  • Well documented, including updates to any necessary markdown files. When
    we inevitably come back to this code it will only take hours to figure out, not
    days.

  • Consistent/Clear/Thoughtful? We are better with this code. We also aren't
    a victim of rampaging consistency, and should be using this course of action.
    We don't have coding standards out yet for this project, so please make sure to address any feedback regarding STYLE so the codebase remains consistent.

  • PR has a valid summary, and a good description.

@bv-jenkins
Copy link

This PR was tested by custom branch build on jenkins

@bv-jenkins
Copy link

This PR was tested by custom branch build on jenkins

Copy link
Contributor

@sujithvaddi sujithvaddi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@billkalter Change looks good to me 👍 minor comments posted, otherwise it's all good.

  • Verified the following:
  • change to APIKey class.
  • change to the AuthIdentityManager interface.
  • Implementations of AuthIdentityManager. Dao impl - Identity table -> <hash_auth_id, Identity Object> & index table -> <Internal_id, hash_auth_id>

@@ -418,13 +418,13 @@ private void cacheAuthorizationInfoByInternalId(String internalId, Authorization
*/
private AuthorizationInfo getUncachedAuthorizationInfoByInternalId(String internalId) {
// Retrieve the roles by internal ID
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minor: missed the comment update.

@@ -17,14 +17,14 @@
private final PrincipalCollection _principals;
private final String _credentials;

public ApiKeyAuthenticationInfo(ApiKey apiKey, String realm) {
public ApiKeyAuthenticationInfo(String authenticationId, ApiKey apiKey, String realm) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minor: null check for authenticationId.

*
* In the original design this ID was only used internally, but over time it has become an accepted public-facing
* identifier for identities. This is why for historical reasons this field is called "internalId" even though
* it is used as both the unique internal and external identifier for this identity.
*/
private final String _internalId;

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've seen your note about the reason for keeping the InternalID naming as is, but if it's only about changing the name at various places, I'm more biased in changing it, as it's now used as an external identifier as well. But it's your call though.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason I didn't change it is because the change cascades down. Notably, to be consistent I'd have to change Subject.java and InternalAuthorizer.java, as well as any classes which use them. I agree with your statement and don't mind doing it, but it makes this PR all the larger.

* .withOwner("[email protected]")
* .addRoles("role1")
* .buildFrom("123");
*
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minor: I think it's buildNew("123") here.

@billkalter
Copy link
Contributor Author

billkalter commented Mar 28, 2017

@sujithvaddi I addressed your feedback. There's two commits; the first is small and contains the smaller updates from your feedback. The second commit is much larger, but at its core it only contains the following changes:

  • Renamed AuthIdentity (parent class of ApiKey) "internalId" attribute to "id"
  • Renamed Subject "id" and "internalId" attributes to "authenticationId" and "id", respectively
  • Renamed all methods in InternalAuthorizer to use "id" instead of "internalId"
  • Renamed SystemInternalId annotation to SystemIdentity

All other changes stem from one of those changes.

One thing I didn't change is that the default table for storing the ID index is still __auth:internal_ids. This was done intentionally to prevent any possible issues in rolling out the new version. Arguably the default could be changed to something more natural like __auth:ids and we could just update our config files to use the old names.

@bv-jenkins
Copy link

This PR was tested by custom branch build on jenkins

@bv-jenkins
Copy link

This PR was tested by custom branch build on jenkins

@sujithvaddi
Copy link
Contributor

@billkalter Looks good to me. Please go ahead and merge.

  • verified the changes as listed in the comment above.
  • checked for any missed references of InternalID, but found none except at a few places where we needed backward compatibility and was intentional.
  • Emo started fine & all integ tests passed.

@billkalter billkalter merged commit ab63c62 into bazaarvoice:master Apr 20, 2017
@billkalter billkalter deleted the emo-6146 branch April 20, 2017 14:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants