Skip to content

Make ApplicationCertificateIdExtractor configurable via SPI#1280

Merged
minwoox merged 3 commits intoline:mainfrom
minwoox:extractor_spi
Mar 24, 2026
Merged

Make ApplicationCertificateIdExtractor configurable via SPI#1280
minwoox merged 3 commits intoline:mainfrom
minwoox:extractor_spi

Conversation

@minwoox
Copy link
Contributor

@minwoox minwoox commented Mar 19, 2026

Motivation:
The ApplicationCertificateIdExtractor was hardcoded to use CommonNameExtractor, which extracts the CN from the certificate's subject DN. Users who need to extract certificate IDs differently (e.g., from SANs or other DN fields) had no way to customize this behavior.

Modifications:

  • Modified ApplicationCertificateAuthorizer to load ApplicationCertificateIdExtractor via ServiceLoader.

Result:

  • You can now provide a custom ApplicationCertificateIdExtractor via SPI.

Motivation:
The `ApplicationCertificateIdExtractor` was hardcoded to use `CommonNameExtractor`,
which extracts the CN from the certificate's subject DN. Users who need to extract
certificate IDs differently (e.g., from SANs or other DN fields) had no way to
customize this behavior.

Modifications:
- Modified `ApplicationCertificateAuthorizer` to load `ApplicationCertificateIdExtractor`
  via `ServiceLoader`.

Result:
- You can now provide a custom `ApplicationCertificateIdExtractor` via SPI.
@minwoox minwoox added this to the 0.81.0 milestone Mar 19, 2026
@coderabbitai
Copy link

coderabbitai bot commented Mar 19, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 41fa5e34-60d9-4b39-bfa9-ec188fba3a98

📥 Commits

Reviewing files that changed from the base of the PR and between 7ea07a4 and 797a663.

📒 Files selected for processing (1)
  • it/server/src/test/java/com/linecorp/centraldogma/it/CustomCertificateIdExtractorTest.java
✅ Files skipped from review due to trivial changes (1)
  • it/server/src/test/java/com/linecorp/centraldogma/it/CustomCertificateIdExtractorTest.java

📝 Walkthrough

Walkthrough

Switches certificate-ID extraction to a pluggable SPI via ServiceLoader, adds a test extractor and its SPI registration, introduces an integration test exercising mTLS with the custom extractor, and updates an integration test dependency.

Changes

Cohort / File(s) Summary
Build
it/server/build.gradle
Added libs.armeria.junit5 to testImplementation.
SPI impl & registration
it/server/src/test/java/com/linecorp/centraldogma/it/TestCertificateIdExtractor.java, it/server/src/test/resources/META-INF/services/com.linecorp.centraldogma.server.auth.ApplicationCertificateIdExtractor
New test ApplicationCertificateIdExtractor implementation returning test-<CN>, registered via Java SPI.
Integration test
it/server/src/test/java/com/linecorp/centraldogma/it/CustomCertificateIdExtractorTest.java
New integration test that boots server with mTLS, verifies authorization lifecycle (401 → register app identity → 403 → grant role → 200).
Core auth wiring
server/src/main/java/com/linecorp/centraldogma/server/internal/api/auth/ApplicationCertificateAuthorizer.java
Initialize ID_EXTRACTOR via ServiceLoader: fallback to CommonNameExtractor if none, accept single provider, throw on multiple providers.

Sequence Diagram

sequenceDiagram
    participant Client as mTLS Client
    participant Server as CentralDogma Server
    participant Loader as ServiceLoader
    participant Extractor as TestCertificateIdExtractor
    participant Registry as AppIdentity Registry

    Client->>Server: HTTPS request with client X.509 cert
    rect rgba(100,150,200,0.5)
    Server->>Loader: load(ApplicationCertificateIdExtractor)
    Loader-->>Server: discover TestCertificateIdExtractor
    end
    rect rgba(150,180,220,0.5)
    Server->>Extractor: extractCertificateId(certificate)
    Extractor->>Extractor: parse subject CN -> "test-<CN>"
    Extractor-->>Server: "test-my-client"
    end
    rect rgba(200,150,100,0.5)
    Server->>Registry: lookup app identity "test-my-client"
    Registry-->>Server: no identity / then identity + roles (after registration/grant)
    Server-->>Client: 401 / 403 / 200 depending on registry state
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • trustin
  • jrhee17
  • ikhoon

Poem

🐰 I sniffed the CN and padded "test-" along,

A ServiceLoader hummed its discovery song,
Tests hopped through 401s, grants, and cheer,
A tiny rabbit squeaks: access is here!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Make ApplicationCertificateIdExtractor configurable via SPI' directly and clearly summarizes the main change: making a component configurable through the Service Provider Interface mechanism.
Description check ✅ Passed The description provides relevant context about the motivation for the change, the specific modifications made, and the resulting benefit, all of which relate directly to the changeset.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
server/src/main/java/com/linecorp/centraldogma/server/internal/api/auth/ApplicationCertificateAuthorizer.java (1)

77-79: Consider showing class names in the error message for easier debugging.

The current message will print object references. Showing class names would help operators identify which implementations are conflicting.

♻️ Optional improvement for clearer error message
         } else {
             throw new IllegalStateException(
                     "Only one ApplicationCertificateIdExtractor implementation must be provided. " +
-                    "found: " + extractors);
+                    "found: " + extractors.stream()
+                                          .map(e -> e.getClass().getName())
+                                          .collect(Collectors.toList()));
         }

Add the import:

import java.util.stream.Collectors;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@server/src/main/java/com/linecorp/centraldogma/server/internal/api/auth/ApplicationCertificateAuthorizer.java`
around lines 77 - 79, The IllegalStateException in
ApplicationCertificateAuthorizer currently prints the extractors list (object
references); update the throw to include the concrete class names of the
conflicting ApplicationCertificateIdExtractor implementations by mapping
extractors to their getClass().getName() (or similar) and joining them (e.g.,
using Collectors.joining) so the exception message shows the implementation
class names rather than raw object toString() values; keep the same exception
text but replace "found: " + extractors with the joined class-name string for
easier debugging.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@it/server/src/test/java/com/linecorp/centraldogma/it/CustomCertificateIdExtractorTest.java`:
- Around line 111-122: The ClientFactory created in configureWebClient(...) is
never closed; modify configureWebClient to assign the built ClientFactory to a
static field (e.g., private static ClientFactory clientFactory) instead of
passing an anonymous instance to builder.factory(...), import
org.junit.jupiter.api.AfterAll, and add an `@AfterAll` static teardown method that
checks for non-null clientFactory and calls clientFactory.close() (or
closeAsync().join() if async) to release native resources; update
WebClientBuilder usage to use the static clientFactory when calling
builder.factory(...).

---

Nitpick comments:
In
`@server/src/main/java/com/linecorp/centraldogma/server/internal/api/auth/ApplicationCertificateAuthorizer.java`:
- Around line 77-79: The IllegalStateException in
ApplicationCertificateAuthorizer currently prints the extractors list (object
references); update the throw to include the concrete class names of the
conflicting ApplicationCertificateIdExtractor implementations by mapping
extractors to their getClass().getName() (or similar) and joining them (e.g.,
using Collectors.joining) so the exception message shows the implementation
class names rather than raw object toString() values; keep the same exception
text but replace "found: " + extractors with the joined class-name string for
easier debugging.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 7e500efa-0863-4fcf-b75d-0f0fd4fe8592

📥 Commits

Reviewing files that changed from the base of the PR and between 6e561bc and c9236eb.

📒 Files selected for processing (5)
  • it/server/build.gradle
  • it/server/src/test/java/com/linecorp/centraldogma/it/CustomCertificateIdExtractorTest.java
  • it/server/src/test/java/com/linecorp/centraldogma/it/TestCertificateIdExtractor.java
  • it/server/src/test/resources/META-INF/services/com.linecorp.centraldogma.server.auth.ApplicationCertificateIdExtractor
  • server/src/main/java/com/linecorp/centraldogma/server/internal/api/auth/ApplicationCertificateAuthorizer.java

Copy link
Contributor

@jrhee17 jrhee17 left a comment

Choose a reason for hiding this comment

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

👍 👍

Copy link
Contributor

@ikhoon ikhoon left a comment

Choose a reason for hiding this comment

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

👍👍

@minwoox minwoox merged commit 8f4ec21 into line:main Mar 24, 2026
13 of 14 checks passed
@minwoox minwoox deleted the extractor_spi branch March 24, 2026 02:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants