Skip to content

Commit e7f1295

Browse files
committed
feat(spec): Add SecurityRequirement model + parser
1 parent 33c3ece commit e7f1295

3 files changed

Lines changed: 80 additions & 0 deletions

File tree

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.retailsvc.http.spec.security;
2+
3+
import java.util.List;
4+
import java.util.Map;
5+
6+
/**
7+
* One OR-branch in a {@code security} list. Each entry in {@link #schemes} is AND-ed: every scheme
8+
* name must be satisfied for the requirement to hold. Scopes are preserved but unused in v1.
9+
*/
10+
public record SecurityRequirement(Map<String, List<String>> schemes) {
11+
public SecurityRequirement {
12+
schemes = Map.copyOf(schemes);
13+
}
14+
}

src/main/java/com/retailsvc/http/spec/security/SecuritySchemeParser.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
import com.retailsvc.http.spec.security.SecurityScheme.HttpBasic;
66
import com.retailsvc.http.spec.security.SecurityScheme.HttpBearer;
77
import com.retailsvc.http.spec.security.SecurityScheme.Unsupported;
8+
import java.util.ArrayList;
9+
import java.util.LinkedHashMap;
10+
import java.util.List;
811
import java.util.Locale;
912
import java.util.Map;
1013
import java.util.Optional;
@@ -33,6 +36,30 @@ private static SecurityScheme parseApiKey(Map<String, Object> raw) {
3336
return new ApiKey(name, Location.valueOf(in.toUpperCase(Locale.ROOT)));
3437
}
3538

39+
@SuppressWarnings("unchecked")
40+
public static List<SecurityRequirement> parseRequirements(List<Object> raw) {
41+
if (raw == null || raw.isEmpty()) {
42+
return List.of();
43+
}
44+
List<SecurityRequirement> out = new ArrayList<>(raw.size());
45+
for (Object entry : raw) {
46+
if (!(entry instanceof Map<?, ?> map)) {
47+
throw new IllegalArgumentException("security requirement entries must be objects");
48+
}
49+
Map<String, List<String>> schemes = new LinkedHashMap<>();
50+
for (var e : map.entrySet()) {
51+
String name = (String) e.getKey();
52+
List<String> scopes =
53+
e.getValue() instanceof List<?> list
54+
? list.stream().map(Object::toString).toList()
55+
: List.of();
56+
schemes.put(name, scopes);
57+
}
58+
out.add(new SecurityRequirement(schemes));
59+
}
60+
return List.copyOf(out);
61+
}
62+
3663
private static SecurityScheme parseHttp(Map<String, Object> raw) {
3764
String scheme = (String) raw.get("scheme");
3865
if (scheme == null) {
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package com.retailsvc.http.spec.security;
2+
3+
import static org.assertj.core.api.Assertions.assertThat;
4+
5+
import java.util.List;
6+
import java.util.Map;
7+
import org.junit.jupiter.api.Test;
8+
9+
class SecurityRequirementParseTest {
10+
11+
@Test
12+
void singleRequirementParses() {
13+
List<Object> raw = List.of(Map.of("bearerAuth", List.of()));
14+
List<SecurityRequirement> req = SecuritySchemeParser.parseRequirements(raw);
15+
assertThat(req).containsExactly(new SecurityRequirement(Map.of("bearerAuth", List.of())));
16+
}
17+
18+
@Test
19+
void andGroupParses() {
20+
List<Object> raw = List.of(Map.of("apiKey", List.of(), "bearer", List.of("admin")));
21+
List<SecurityRequirement> req = SecuritySchemeParser.parseRequirements(raw);
22+
assertThat(req).hasSize(1);
23+
assertThat(req.get(0).schemes())
24+
.containsEntry("apiKey", List.of())
25+
.containsEntry("bearer", List.of("admin"));
26+
}
27+
28+
@Test
29+
void orGroupsParse() {
30+
List<Object> raw = List.of(Map.of("apiKey", List.of()), Map.of("bearer", List.of()));
31+
List<SecurityRequirement> req = SecuritySchemeParser.parseRequirements(raw);
32+
assertThat(req).hasSize(2);
33+
}
34+
35+
@Test
36+
void nullReturnsEmptyList() {
37+
assertThat(SecuritySchemeParser.parseRequirements(null)).isEmpty();
38+
}
39+
}

0 commit comments

Comments
 (0)