adityamparikh opened a new pull request, #187:
URL: https://github.com/apache/solr-mcp/pull/187

   The security configuration currently has **no test that exercises it**. This 
adds two, both
   validated by mutation.
   
   ## The gap
   
   `McpToolRegistrationTest#everyMcpEndpointIsPreAuthorized` reflects over the 
service classes and
   asserts `@PreAuthorize` is *present* on every MCP entry point. That is a 
good guard against
   forgetting it on a new tool — but it is static, and cannot tell whether the 
annotation has any
   runtime effect.
   
   Demonstrated by commenting out `@EnableMethodSecurity` in 
`MethodSecurityConfiguration`, which
   neuters all 24 `@PreAuthorize` annotations and makes every MCP tool callable 
unauthenticated:
   
   | | existing guard | this PR |
   |---|---|---|
   | `@EnableMethodSecurity` disabled | **BUILD SUCCESSFUL** | **FAILS** |
   | `/actuator/**` → `permitAll()` | **BUILD SUCCESSFUL** | **FAILS** |
   
   So today, if the profile gate or the `http.security.enabled` property 
condition ever stopped
   matching, every tool would be open and CI would stay green.
   
   ## What's added
   
   **`MethodSecurityEnforcementTest`** — calls a secured tool through the 
Spring proxy with an
   empty `SecurityContext` and asserts rejection. Note the exception type: with 
no `Authentication`
   at all, Spring raises `AuthenticationCredentialsNotFoundException`, not 
`AccessDeniedException`
   (the latter is for an authenticated principal lacking authority). I got that 
wrong on the first
   run and the failure output corrected it.
   
   **`HttpSecurityFilterChainTest`** — pins the anonymous-access boundary: 
`/actuator/health` open
   for probes, `/actuator/sbom/application` and `/actuator/metrics` closed. 
That split is a single
   `requestMatchers` rule whose justification currently lives only in a code 
comment — widening it
   to `permitAll()` would expose the dependency tree and the metrics that map 
the tool surface, and
   would break no existing test.
   
   Denial is asserted as **401-or-403 rather than a fixed code**. With no 
issuer configured there
   is no authentication entry point, so Spring rejects with 403; wiring an 
issuer turns the same
   request into a 401 with `WWW-Authenticate: Bearer`. Both are correct denials 
— the property
   worth pinning is that neither is a 200. Asserting 401 exactly would fail the 
day someone
   configures an issuer, which is a *good* change.
   
   ## Why this went unnoticed
   
   `OtlpExportIntegrationTest` is the only test that activates the `http` 
profile without setting
   `http.security.enabled=false` — and it is `@Disabled` over an unrelated 
Jetty/LGTM container
   issue. Every other http-profile test disables security. So no executing test 
has ever run with
   the security configuration active.
   
   ## Notes
   
   - Applies to `main` as-is; the security classes are identical on the `sb4` 
branch, so this
     flows there on the next rebase.
   - 376 tests, 0 failures (baseline 372).
   - Both tests are `@Tag("integration")` + `@DisabledInNativeImage`, matching 
the convention for
     Testcontainers-backed and proxy-dependent tests.
   
   ## Not covered here
   
   Deliberately out of scope, worth separate issues if wanted: OAuth2 wiring 
when an issuer *is*
   configured (the Nimbus decoder is eager, so it needs a reachable issuer or a 
mock), the
   `validateAudienceClaim(true)` behaviour the MCP Authorization spec requires, 
and the CORS
   configuration.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to