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

Add a DELETE endpoint for API Keys #37051

Merged
merged 1 commit into from
Jan 2, 2024
Merged

Conversation

johnswanson
Copy link
Contributor

Don't actually delete them from the database (to keep the invariant that a user with an :api-key type will always have an associated ApiKey). Just mark them as :archived, and:

  • make them invisible to the count endpoint

  • make them invisible to the list endpoint

  • make them not work for authentication

@metabase-bot metabase-bot bot added the .Team/AdminWebapp Admin and Webapp team label Dec 21, 2023
Copy link

cypress bot commented Dec 21, 2023

Passing run #1266 ↗︎

0 2201 155 0 Flakiness 0
⚠️ You've recorded test results over your free plan limit.
Upgrade your plan to view test results.

Details:

Add a DELETE endpoint for API Keys
Project: Metabase e2e Commit: 53c26af6e5
Status: Passed Duration: 15:33 💡
Started: Jan 2, 2024 6:50 PM Ended: Jan 2, 2024 7:05 PM

Review all test suite changes for PR #37051 ↗︎

@johnswanson johnswanson force-pushed the add-updated-by-to-api-keys branch 2 times, most recently from 45993e7 to ecce8b4 Compare December 22, 2023 14:37
@johnswanson johnswanson added the no-backport Do not backport this PR to any branch label Dec 22, 2023
Base automatically changed from add-updated-by-to-api-keys to master December 22, 2023 15:38
Copy link
Member

What's the advantage of archiving API keys instead of deleting the API Key and the associated user?

Normal users can only be archived, not deleted, but api-key users don't necessarily have to have the same constraint since they're more of an internal mechanism than actual users.

And there's the slight risk that a bug in the future could allow deactivated API keys to still be used for auth. I know you've added checks to prevent that but it's still a potential risk if we're not allowing API keys to be fully deleted.

Don't have a strong opinion necessarily, but just want to make sure we have a good justification for the approach here.

@johnswanson
Copy link
Contributor Author

What's the advantage of archiving API keys instead of deleting the API Key and the associated user?

Normal users can only be archived, not deleted, but api-key users don't necessarily have to have the same constraint since they're more of an internal mechanism than actual users.

Deleting a user would cause other issues - for example, if an API key user creates a dashboard, then deleting that user would break the creator_id of that dashboard (I think there's a foreign key constraint to prevent that).

And there's the slight risk that a bug in the future could allow deactivated API keys to still be used for auth. I know you've added checks to prevent that but it's still a potential risk if we're not allowing API keys to be fully deleted.

This is a good point to consider. What would you think of regenerating the API key as part of "deletion"? That way any existing key becomes unusable even if there's a bug that allows auth with a "deleted" key.

@noahmoss
Copy link
Member

Gotcha. Yeah I'd be in favor of either:

  • Overwriting the hashed key in the API Key with a dummy value or just set it to nil (maybe nil would be clearer)
  • Don't necessarily require that API Key users have an associated API Key, and delete the API Key but leave the user as archived

@johnswanson
Copy link
Contributor Author

johnswanson commented Dec 22, 2023

Gotcha. Yeah I'd be in favor of either:

  • Overwriting the hashed key in the API Key with a dummy value or just set it to nil (maybe nil would be clearer)
  • Don't necessarily require that API Key users have an associated API Key, and delete the API Key but leave the user as archived

One additional point I want to consider is auditability. Say

  • someone uses an API key to do something malicious
  • later, for unrelated reasons, that ApiKey is deleted
  • later, the malicious activity is noticed

We'd have an audit log that showed that User 123, with type :api_key, performed some malicious activity. But we wouldn't have any way of figuring out which ApiKey did it.

I think this makes me lean towards the first option - overwriting the ApiKey with a nil hashed key. That way, if you have a user ID that performed a malicious action, you can always get the associated ApiKey.

Does that sound reasonable? Another option would be to add user_id to the audited fields for ApiKeys, so the deletion audit log event contains the user ID. But this feels a bit less obvious.

EDIT: I'm going with option 2, deleting the ApiKey but leaving the User: https://metaboat.slack.com/archives/C064EB1UE5P/p1703284432369899?thread_ts=1703183375.589429&cid=C064EB1UE5P

Comment on lines 138 to 139
(let [api-key (-> (t2/select-one :model/ApiKey id)
(t2/hydrate :group_name))]
Copy link
Member

Choose a reason for hiding this comment

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

Not a huge deal, but might be worth wrapping this in check-404. Even though it's a deletion endpoint, we should probably return a 404 if the caller is trying to delete something that already doesn't exist.

The User update call below would also error in this case anyway, so probably better to catch that at the start and error.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good call. I'm also going to add this check to the PUT endpoints.

Comment on lines 142 to 144
(events/publish-event! :event/api-key-delete
{:object api-key
:user-id api/*current-user-id*})
Copy link
Member

Choose a reason for hiding this comment

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

Here's another case where I don't think the publish-event! call should be in the transaction, since we don't know what downstream DB calls are generated

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oops, thanks for noticing this - sorry about that!

(events/publish-event! :event/api-key-delete
{:object api-key
:user-id api/*current-user-id*})
(present-api-key api-key))))
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 a specific reason we need to return the API key after it's been deleted? If not, you could just return a 204 (api/generic-204-no-content)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good point, done!

@johnswanson johnswanson force-pushed the delete-endpoint-for-api-keys branch 2 times, most recently from 9a63671 to 265e21e Compare January 2, 2024 14:44
When the endpoint is hit, we delete the ApiKey from the database. The
user is left behind to ensure that we don't break foreign keys, and to
enhance auditability.
@johnswanson johnswanson merged commit fbb1202 into master Jan 2, 2024
105 checks passed
@johnswanson johnswanson deleted the delete-endpoint-for-api-keys branch January 2, 2024 19:58
Copy link

github-actions bot commented Jan 2, 2024

@johnswanson Did you forget to add a milestone to the issue for this PR? When and where should I add a milestone?

@johnswanson johnswanson added this to the 0.49 milestone Jan 4, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
no-backport Do not backport this PR to any branch .Team/AdminWebapp Admin and Webapp team
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants