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

Auth Token feature #1952

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open

Auth Token feature #1952

wants to merge 1 commit into from

Conversation

barreiro
Copy link
Collaborator

as discussed in #1922

@barreiro barreiro force-pushed the auth-tokens branch 6 times, most recently from dd6925e to 636d6ac Compare August 30, 2024 04:54
@barreiro
Copy link
Collaborator Author

This feature is now ready for review

@lampajr
Copy link
Member

lampajr commented Sep 19, 2024

Hey Luis, sorry for delay! I am planning to review this one by today/tomorrow.. I'll come back to you asap 🚀

Copy link
Member

@lampajr lampajr left a comment

Choose a reason for hiding this comment

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

Hey again,

Here a first round of review, overall looks good and looks working. I tried the use case where I try to upload a new run and it looks working using the generated API key.

curl -s --url-query "test=optaplanner-jmh-benchmarks-8x" -X POST -H "content-type: application/json" -H 'X-Horreum-API-Key: <MY_KEY>' 'http://localhost:8080/api/run/data' -d @/tmp/op-run.json

There are still a couple of comments:

  1. @barreiro could you please rebase this on top of master? those changes shouldn't affect this feature but just to be sure.
  2. Return error msg when the API Key validation failed, I tried to run the same upload command with either a fake key or a revoked one and in both cases no error is returned to the user even if the request failed.

Here an example:
If don't send any auth token, the rest will return a generic error msg:

$ curl -s --url-query "test=optaplanner-jmh-benchmarks-8x" --url-query "owner=optaplanner-team" --url-query "start=2024-07-09T09:45:38" --url-query "stop=2024-07-09T09:45:38" -X POST -H "content-type: application/json"  'http://localhost:8080/api/run/data' -d @/tmp/op-run.json

Cannot upload to test optaplanner-jmh-benchmarks-8x% 

and in the logs I see (in DEBUG)

DEBUG [io.hyp.too.hor.svc.TestServiceImpl] (executor-thread-1) Failed to retrieve test optaplanner-jmh-benchmarks-8x as this user ( = []) is not uploader for optaplanner-team and token null does not match

If I send, instead, a revoked token:

$ curl -s --url-query "test=optaplanner-jmh-benchmarks-8x" --url-query "owner=optaplanner-team" --url-query "start=2024-07-09T09:45:38" --url-query "stop=2024-07-09T09:45:38" -X POST -H "content-type: application/json" -H 'X-Horreum-API-Key: HUSR_0A1CE383_38D9_4AFB_B094_99B0D6D6BE73' 'http://localhost:8080/api/run/data' -d @/tmp/op-run.json

there is no response at all and no logs as well, I think it could be very useful to return at least an error msg, even a generic "cannot upload to test ..."

  1. In the UI, when there is no API keys set the webview still shows the table columns:
    Screenshot from 2024-09-20 11-33-54

I would suggest to show just the button when there is no API keys set similarly to what we are doing in other places - or showing a message like "No API keys set" and below the button (that's actually just a minor).

Copy link
Member

@johnaohara johnaohara left a comment

Choose a reason for hiding this comment

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

@barreiro this looks great!

For this PR, we need user docs adding

For future PR's i think we need to open issues to;

  • add global management of keys at admin level
  • remove "tokens" and "machine accounts" features

Comment on lines +198 to +199
public LocalDate creation;
public LocalDate access;
Copy link
Member

Choose a reason for hiding this comment

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

For all other timestamps in the API, we have returned an Instant object, and configured the object mapper to use the jackson JSR310 datatype. We have validated to work across all the supported clients.
IDK how LocalDate serializes / deserializes to json and if it is portable across the different clients.

I think we should stick to returning Instant in the REST API unless there is a compelling reason not to

@Transient
private final UUID randomnessSource;

private final String hash;
Copy link
Member

Choose a reason for hiding this comment

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

Do we need a unique constraint on user & hash? If there is a hash collision of API keys, what happens?

If in the future, we create 2 keys with different authorization levels, could we have 2 keys in the db with the same hash? in that case could we distinguish between the keys?

@Enumerated
public final UserService.KeyType type;

public LocalDate creation, access;
Copy link
Member

Choose a reason for hiding this comment

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

Similar comment as in API about Instant we probably want to standarize unless compelling reason not to.

Comment on lines +161 to +163
public int compareTo(UserApiKey other) {
return Comparator.<UserApiKey, LocalDate> comparing(a -> a.creation).thenComparing(a -> a.id).compare(this, other);
}
Copy link
Member

Choose a reason for hiding this comment

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

just out of curiosity, when do we need to sort the API Keys?

Comment on lines +91 to +101
public UserService.ApiKeyResponse toResponse() {
UserService.ApiKeyResponse response = new UserService.ApiKeyResponse();
response.id = id;
response.name = name;
response.type = type;
response.creation = creation;
response.access = access;
response.isRevoked = revoked;
response.toExpiration = toExpiration(LocalDate.now());
return response;
}
Copy link
Member

Choose a reason for hiding this comment

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

For all other entities, mapping has been decoupled from the API via Mappers defined in the io.hyperfoil.tools.horreum.mapper package

// update last access
userKey.access = timeService.today();

// create identity with just the principal, roles will be populated in RolesAugmentor
Copy link
Member

Choose a reason for hiding this comment

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

Is there strict ordering between ApiKeyIdentityProvider and RolesAugmentor?

newKey.persist();
userInfo.persist();

Log.infov("{0} created API key \"{1}\"", getUsername(), request.name == null ? "" : request.name);
Copy link
Member

Choose a reason for hiding this comment

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

I think this should be debug level

}
String oldName = key.name;
key.name = newName == null ? "" : newName;
Log.infov("{0} renamed API key \"{1}\" to \"{2}\"", getUsername(), oldName, newName == null ? "" : newName);
Copy link
Member

Choose a reason for hiding this comment

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

I think this should be debug level

UserApiKey key = UserApiKey.<UserApiKey> findByIdOptional(keyId)
.orElseThrow(() -> ServiceException.notFound(format("Key with id {0} not found", keyId)));
key.revoked = true;
Log.infov("{0} revoked API key \"{1}\"", getUsername(), key.name);
Copy link
Member

Choose a reason for hiding this comment

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

I think this should be debug level

}
// revoke expired keys -- could be done directly in the DB but iterate instead to be able to log
UserApiKey.<UserApiKey> stream("#UserApiKey.pastExpiration", timeService.today()).forEach(key -> {
Log.infov("Revoked idle API key \"{0}\"", key.name);
Copy link
Member

Choose a reason for hiding this comment

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

I think this should be debug level

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