Skip to content

Commit e11ce37

Browse files
committed
test(internal): Cover OR-of-AND evaluation in SecurityFilter
1 parent 480bc43 commit e11ce37

1 file changed

Lines changed: 106 additions & 0 deletions

File tree

src/test/java/com/retailsvc/http/internal/SecurityFilterTest.java

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,112 @@ void apiKeyMissingReturnsApiKeyChallengeHeader() throws Exception {
213213
.isEqualTo("ApiKey location=header, name=\"X-API-Key\"");
214214
}
215215

216+
@Test
217+
void andGroupRequiresAllSchemesToSucceed() throws Exception {
218+
// Group requires both apiKeyAuth AND bearerAuth.
219+
Operation op =
220+
new Operation(
221+
"getX",
222+
HttpMethod.GET,
223+
null,
224+
Optional.empty(),
225+
List.of(),
226+
Map.of(),
227+
Map.of(),
228+
Optional.of(
229+
List.of(
230+
new SecurityRequirement(
231+
Map.of("apiKeyAuth", List.of(), "bearerAuth", List.of())))));
232+
233+
Map<String, SecurityScheme> schemes =
234+
Map.of(
235+
"apiKeyAuth", new ApiKey("X-API-Key", Location.HEADER),
236+
"bearerAuth", new HttpBearer(Optional.empty()));
237+
238+
Map<String, SchemeValidator> validators =
239+
Map.of(
240+
"apiKeyAuth", (req, cred) -> Optional.of("api-principal"),
241+
"bearerAuth", (req, cred) -> Optional.of("bearer-principal"));
242+
243+
SecurityFilter filter =
244+
new SecurityFilter(Map.of("getX", op), schemes, List.of(), validators, false);
245+
246+
HttpExchange ex = mock(HttpExchange.class);
247+
Headers headers = new Headers();
248+
headers.add("X-API-Key", "abc");
249+
headers.add("Authorization", "Bearer token");
250+
when(ex.getRequestHeaders()).thenReturn(headers);
251+
when(ex.getRequestURI()).thenReturn(URI.create("http://h/getX"));
252+
253+
Map<String, Object> captured = new java.util.HashMap<>();
254+
Chain chain = mock(Chain.class);
255+
doAnswer(
256+
inv -> {
257+
captured.putAll(DispatchHandler.CURRENT.get().principals());
258+
return null;
259+
})
260+
.when(chain)
261+
.doFilter(ex);
262+
263+
ScopedValueHarness.runWith(newMinimalRequest("getX"), () -> filter.doFilter(ex, chain));
264+
265+
assertThat(captured)
266+
.containsEntry("apiKeyAuth", "api-principal")
267+
.containsEntry("bearerAuth", "bearer-principal");
268+
}
269+
270+
@Test
271+
void orFallsBackToSecondGroupWhenFirstDenied() throws Exception {
272+
// [{apiKeyAuth}, {bearerAuth}] — first group denied, second allowed.
273+
Operation op =
274+
new Operation(
275+
"getX",
276+
HttpMethod.GET,
277+
null,
278+
Optional.empty(),
279+
List.of(),
280+
Map.of(),
281+
Map.of(),
282+
Optional.of(
283+
List.of(
284+
new SecurityRequirement(Map.of("apiKeyAuth", List.of())),
285+
new SecurityRequirement(Map.of("bearerAuth", List.of())))));
286+
287+
Map<String, SecurityScheme> schemes =
288+
Map.of(
289+
"apiKeyAuth", new ApiKey("X-API-Key", Location.HEADER),
290+
"bearerAuth", new HttpBearer(Optional.empty()));
291+
292+
Map<String, SchemeValidator> validators =
293+
Map.of(
294+
"apiKeyAuth", (req, cred) -> Optional.empty(),
295+
"bearerAuth", (req, cred) -> Optional.of("bearer-ok"));
296+
297+
SecurityFilter filter =
298+
new SecurityFilter(Map.of("getX", op), schemes, List.of(), validators, false);
299+
300+
HttpExchange ex = mock(HttpExchange.class);
301+
Headers headers = new Headers();
302+
headers.add("X-API-Key", "bad");
303+
headers.add("Authorization", "Bearer token");
304+
when(ex.getRequestHeaders()).thenReturn(headers);
305+
when(ex.getRequestURI()).thenReturn(URI.create("http://h/getX"));
306+
307+
Map<String, Object> captured = new java.util.HashMap<>();
308+
Chain chain = mock(Chain.class);
309+
doAnswer(
310+
inv -> {
311+
captured.putAll(DispatchHandler.CURRENT.get().principals());
312+
return null;
313+
})
314+
.when(chain)
315+
.doFilter(ex);
316+
317+
ScopedValueHarness.runWith(newMinimalRequest("getX"), () -> filter.doFilter(ex, chain));
318+
319+
assertThat(captured).containsEntry("bearerAuth", "bearer-ok").doesNotContainKey("apiKeyAuth");
320+
}
321+
216322
private static Request newMinimalRequest(String operationId) {
217323
return new Request(new byte[0], null, null, operationId, Map.of(), null, h -> null);
218324
}

0 commit comments

Comments
 (0)