Add worker to clean up dynamic OAuth apps when DCR is disabled

Summary

Adds Authn::OauthApplications::CleanupDynamicApplicationsWorker, a Sidekiq worker that destroys all dynamic OAuth applications (those created via the DCR endpoint, where dynamic = true) and revokes their tokens and grants.

Part of the reason why an instance admin would disable DCR is that it generates a lot of OAuth applications at the instance level. When the setting is disabled, we also want to get rid of all of that cruft. There could be thousands of these records, so deleting them in a job makes the most sense.

This worker is intentionally inert on its own — it is designed to be enqueued by an after_commit hook on ApplicationSetting when dynamic_client_registration_enabled is set to false. That hook will be added in a follow-on MR (which also introduces the setting itself). This ordering ensures the worker exists before anything can enqueue it.

References

Queries

The worker issues the following queries:

-- each_batch boundary query (uses idx_oauth_applications_dynamic_and_id)
SELECT "oauth_applications"."id" FROM "oauth_applications"
WHERE "oauth_applications"."dynamic" = TRUE
AND "oauth_applications"."id" >= 2346545
ORDER BY "oauth_applications"."id" ASC LIMIT 1 OFFSET 1000;

-- each_batch fetch
SELECT "oauth_applications".* FROM "oauth_applications"
WHERE "oauth_applications"."dynamic" = TRUE
AND "oauth_applications"."id" >= 2346545
AND "oauth_applications"."id" < 2349074;

-- Token revocation
UPDATE "oauth_access_tokens" SET revoked_at = NOW()
WHERE application_id = $1 AND revoked_at IS NULL;

-- Grant revocation
UPDATE "oauth_access_grants" SET revoked_at = NOW()
WHERE application_id = $1 AND revoked_at IS NULL;

-- Application delete
DELETE FROM "oauth_applications" WHERE "oauth_applications"."id" = $1;

-- Consent revocation (when iam_svc_oauth FF is enabled)
UPDATE "oauth_consents" SET status = 2, updated_at = NOW()
WHERE "oauth_consents"."status" = 0
AND "oauth_consents"."client_id" IN ($1, ..., $1000);

A composite index idx_oauth_applications_dynamic_and_id on (dynamic, id) was added to support efficient each_batch iteration with the dynamic = true filter, per the iteration with filters guide.

Query plan for the each_batch iteration after adding the index: https://console.postgres.ai/gitlab/projects/gitlab-production-main/sessions/54147/commands/156624

Checklist

  • Worker is idempotent!
  • Worker uses each_batch for safe batching
  • all_queues.yml and config/sidekiq_queues.yml regenerated
  • Spec covers destroy path and non-dynamic app preservation
Edited by Jessie Young

Merge request reports

Loading