Skip to content

Commit

Permalink
[PLAT-14850]API Token authentication loops through the users and chec…
Browse files Browse the repository at this point in the history
…ks token against each of these.

Summary:
API calls are taking longer than 30 seconds per request because we iterate through the list of Users to authenticate using the API token due to the block of
```
List<Users> usersList = find.query().where().isNotNull("apiToken").findList();
for (Users user : usersList) {
  if (Users.hasher.isValid(apiToken, user.getApiToken())) {
    return user;
  }
}
```
This was introduced in https://phorge.dev.yugabyte.com/D35345 to store hash of the API Tokens in the YBA DB.
The proposal to speed up the look up is to return `userUUID$apiToken` as the new API token that can be used to extract the user and verify the request token with the hashed token from the DB.
The older look up is still supported, and clients wishing to increase performance can regenerate the API token to use the new workflow

Test Plan:
Tested with API calls, UI, CLI and Terraform for various auth methods that involve API tokens

Works with both the older API tokens generated by the customer before this change and the new format of the API tokens

Reviewers: #yba-api-review, sneelakantan, sanketh, nbhatia, amalyshev

Reviewed By: #yba-api-review, sneelakantan, amalyshev

Subscribers: yugaware

Differential Revision: https://phorge.dev.yugabyte.com/D37083
  • Loading branch information
Deepti-yb committed Aug 9, 2024
1 parent 9e7181f commit d56903c
Showing 1 changed file with 37 additions and 5 deletions.
42 changes: 37 additions & 5 deletions managed/src/main/java/com/yugabyte/yw/models/Users.java
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,7 @@ public String upsertApiToken() {
}

public String upsertApiToken(Long version) {
String apiTokenFormatVersion = "2";
UUID uuidToLock = uuid != null ? uuid : NULL_UUID;
usersLock.acquireLock(uuidToLock);
try {
Expand All @@ -420,7 +421,9 @@ public String upsertApiToken(Long version) {

apiTokenVersion = apiTokenVersion == null ? 1L : apiTokenVersion + 1;
save();
return apiTokenUnhashed;
// new format of api token = apiTokenFormatVersion$userUUID$apiTokenUnhashed
return apiTokenFormatVersion + "$" + uuid + "$" + apiTokenUnhashed;

} finally {
usersLock.releaseLock(uuidToLock);
}
Expand Down Expand Up @@ -472,12 +475,41 @@ public static Users authWithApiToken(String apiToken) {
return null;
}

// Supporting the 2 formats of api token
// 1. apiTokenFormatVersion$userUUID$apiToken (newer format)
// 2. apiToken (older format)

// The second format would lead to performance degradation in the case of more than 10 users
// Recommended to reissue the token (which will follow the first format)

try {
List<Users> usersList = find.query().where().isNotNull("apiToken").findList();
for (Users user : usersList) {
if (Users.hasher.isValid(apiToken, user.getApiToken())) {
return user;
if (apiToken.contains("$")) {
// to authenticate new format of api token = apiTokenFormatVersion$userUUID$apiTokenUnhashed
String[] parts = apiToken.split("\\$");
UUID userUUID = UUID.fromString(parts[1]);
String apiTokenUnhashed = parts[2];
Users userWithToken = find.query().where().eq("uuid", userUUID).findOne();
if (userWithToken != null) {
if (Users.hasher.isValid(apiTokenUnhashed, userWithToken.getApiToken())) {
return userWithToken;
}
}
} else {
// to authenticate old format of api token
LOG.warn("Using older API token format. Renew to improve performance.");
List<Users> usersList = find.query().where().isNotNull("apiToken").findList();
long startTime = System.currentTimeMillis();
for (Users user : usersList) {
if (Users.hasher.isValid(apiToken, user.getApiToken())) {
LOG.info(
"Authentication using API token. Completed time: {} ms",
System.currentTimeMillis() - startTime);
return user;
}
}
LOG.info(
"Authentication using API token. Completed time: {} ms",
System.currentTimeMillis() - startTime);
}
return null;
} catch (Exception e) {
Expand Down

0 comments on commit d56903c

Please sign in to comment.