Skip to content

API key implementation#4478

Merged
demiankatz merged 118 commits intovufind-org:devfrom
LuomaJuha:api-key-implementation
Oct 24, 2025
Merged

API key implementation#4478
demiankatz merged 118 commits intovufind-org:devfrom
LuomaJuha:api-key-implementation

Conversation

@LuomaJuha
Copy link
Contributor

@LuomaJuha LuomaJuha commented Jul 10, 2025

Still a work in progress, as the database entities needs to be converted into doctrine versions. Easier to open this for now as a draft pull request and finish this way.

  • Adds an option to enable or enforce API keys for Rest API.
  • API key generation happens in the users profile under developer settings.
  • New database table api_key
  • User can have n amount of API keys active at once.
  • Each API key can be assigned a title for users clarity.
  • If any of the users API keys are revoked, then user is blocked from generating new API keys or removing the blocked API key. This is to prevent misbehavior and usually and blocking a key should not happen often.
  • API key usage is tracked by updating last_used column. The update interval is once every hour to avoid extra calls to the database.
  • Adds next new classes:
  • DeveloperSettingsController, general controller containing different actions for managing Developer settings related routing.
  • DeveloperSettingsService, works as a bridge for accessing different developer settings related database entities
  • ApiKeyService, service to manage api_key entities in the database
  • ApiKey, entity class for an API key.
  • HasEmailVerifiedAssertion, first assertion class which asserts that the user has a verified email in the database account.
  • AuthorizationServiceFactory, Overrides the AuthorizationServices factory to allow setting Permissions and their related Assertions into the AuthorizationService
  • New translation domain, Developer. Contains translations related to developer settings context.
  • New database migration, 010-add-api-key-table.sql
  • New configurations:
  • permissionBehavior.ini
  • new section for feature.Developer which contains option deniedTemplateBehavior to adjust the template rendered if user does not have proper permissions.
  • permissions.ini
  • New section for default.Developer. Default settings require the user must be logged in and have a verified Email in their account.
  • Each section now supports adding assertion[] options. These assertions are then checked with each permission check if set.
  • config.ini
  • New section API_Keys, which contains the next options:
  • mode: If set to 'disable' API keys are not used, default value. If set to 'enabled' API keys can be used and created but rest api requests do not require them. If set to 'enforced' using an API key with rest api is mandatory.
  • token_salt: A random string of salt to add when generating API key tokens. This setting is required.
  • header_field: In which header field should the API key token be provided in the request. This defaults to 'X-API-KEY'.
  • key_limit: Controls how many API keys can a user have at once. Default value is 5.

Still needs to be implemented:

  • Tests
  • Translations?
  • Simple API key usage logging?
  • Allow API keys only be generated for users with a verified email?
  • Wait until Doctrine has been merged and adjust pr
  • Reconcile with laminas-log to monolog migration #4429 (if that PR gets merged before this one)
  • Follow-up: add a console utility to remove expired tokens
  • Follow-up: RateLimiter support for valid API key

Requires: #2233

Copy link
Member

@demiankatz demiankatz left a comment

Choose a reason for hiding this comment

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

@LuomaJuha, I only had a few minutes to start looking at this today, so I haven't done anything resembling a full review, but see below for a few initial thoughts and suggestions! I'll try to look at more of the code as time permits.

@LuomaJuha
Copy link
Contributor Author

@demiankatz thanks for suggestions! I think i will add the ApiKeyController to avoid that. And this is still in progress at the moment as there is conversion to doctrine going and need tests + some small adjustments listed in the description :)

@LuomaJuha LuomaJuha force-pushed the api-key-implementation branch from 230f6e9 to 800ab92 Compare July 30, 2025 12:35
@LuomaJuha LuomaJuha force-pushed the api-key-implementation branch from 800ab92 to 78d900b Compare July 30, 2025 12:40
@LuomaJuha LuomaJuha marked this pull request as ready for review July 30, 2025 14:46
@LuomaJuha
Copy link
Contributor Author

I'm opening this now for reviewing even though the doctrine which this uses is not yet merged.

Copy link
Member

@demiankatz demiankatz left a comment

Choose a reason for hiding this comment

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

@LuomaJuha, I haven't had time to read through everything yet, but see below for some initial comments based on my first partial review.

Copy link
Member

@demiankatz demiankatz left a comment

Choose a reason for hiding this comment

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

Thanks, @LuomaJuha! I finished my review just now; see below for a few more comments.

Note that I haven't looked closely at the AccessTokenService yet, since it's full of warnings related to Doctrine not being merged yet. I'll review it more carefully after Doctrine is in place (and when I can actually start doing some hands-on experimentation).

Copy link
Member

@demiankatz demiankatz left a comment

Choose a reason for hiding this comment

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

Thanks for the progress here, @LuomaJuha! Now that #2233 has been merged, you should be able to get this PR into a runnable state. Once you've done that, please request a new review from me and I'll start doing some hands-on testing. Also, see below for a very trivial formatting suggestion.

@demiankatz
Copy link
Member

Here's the test failure -- once again, something to do with Doctrine entity annotations being mismatched with the actual database:

1) VuFindTest\Db\Service\DoctrineSchemaValidationTest::testSchemaValidation
Unexpected schema updates pending; first update: DROP INDEX api_key_token_idx ON api_key
Failed asserting that two arrays are equal.
--- Expected
+++ Actual
@@ @@
 Array (
+    0 => 'DROP INDEX api_key_token_idx ...pi_key'
+    1 => 'ALTER TABLE api_key CHANGE id...T NULL'
 )

/usr/local/vufind/module/VuFind/tests/integration-tests/src/VuFindTest/Db/Service/DoctrineSchemaValidationTest.php:89

@LuomaJuha
Copy link
Contributor Author

@demiankatz Seems like i forgot that orm requires indexeses in the class also. Added it now.

@demiankatz
Copy link
Member

Thanks for fixing the test. I'm just about out of time for today but will try to give this another look when I get online tomorrow. In the meantime, if you have a chance, please merge the dev branch into this PR -- since I recently added PHP 8.4 CI support, we need to merge that in to get the new set of required CI tasks to pass.

@LuomaJuha LuomaJuha requested a review from demiankatz October 23, 2025 09:21
Copy link
Member

@demiankatz demiankatz left a comment

Choose a reason for hiding this comment

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

I noticed a few problems during review, but they're all simple and can be fixed by applying the suggestions below.

@LuomaJuha LuomaJuha requested a review from demiankatz October 23, 2025 13:17
Copy link
Member

@demiankatz demiankatz left a comment

Choose a reason for hiding this comment

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

My hands-on testing revealed two minor things:

1.) I removed the confirm_delete translation string because it was unused elsewhere in VuFind... but it is needed here, so I have restored it.

2.) When I set this up, I forgot to generate salt and encountered an unintuitive error (had to look in logs to understand what was happening). I'm adding a "REQUIRED" label to the setting in config.ini to hopefully make it harder to overlook.

I'm going to apply my proposal and then approve and merge this. No further action required.

Thanks, @LuomaJuha!

@demiankatz
Copy link
Member

One more last-minute addition: the new permission was not included in the summary of permissions in permissions.ini; I've inserted it.

Copy link
Member

@demiankatz demiankatz left a comment

Choose a reason for hiding this comment

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

Victory at last!

@demiankatz demiankatz dismissed EreMaijala’s stale review October 24, 2025 12:56

Dismissing with Ere's permission.

@demiankatz demiankatz merged commit 8e146d9 into vufind-org:dev Oct 24, 2025
6 checks passed
@demiankatz demiankatz removed the request for review from EreMaijala October 24, 2025 12:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

database migrations Pull requests that add new DB migrations new feature new language strings adds new text in need of translation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants