Skip to content

Commit 33c3ece

Browse files
committed
feat(spec): Add SecurityScheme sealed model + parser
1 parent 8767a93 commit 33c3ece

3 files changed

Lines changed: 111 additions & 0 deletions

File tree

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package com.retailsvc.http.spec.security;
2+
3+
import java.util.Optional;
4+
5+
public sealed interface SecurityScheme
6+
permits SecurityScheme.ApiKey,
7+
SecurityScheme.HttpBearer,
8+
SecurityScheme.HttpBasic,
9+
SecurityScheme.Unsupported {
10+
11+
record ApiKey(String name, Location location) implements SecurityScheme {
12+
public enum Location {
13+
HEADER,
14+
QUERY,
15+
COOKIE
16+
}
17+
}
18+
19+
record HttpBearer(Optional<String> bearerFormat) implements SecurityScheme {}
20+
21+
record HttpBasic() implements SecurityScheme {}
22+
23+
/** Parsed but unsupported in v1 (oauth2, openIdConnect, mutualTLS). */
24+
record Unsupported(String type) implements SecurityScheme {}
25+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package com.retailsvc.http.spec.security;
2+
3+
import com.retailsvc.http.spec.security.SecurityScheme.ApiKey;
4+
import com.retailsvc.http.spec.security.SecurityScheme.ApiKey.Location;
5+
import com.retailsvc.http.spec.security.SecurityScheme.HttpBasic;
6+
import com.retailsvc.http.spec.security.SecurityScheme.HttpBearer;
7+
import com.retailsvc.http.spec.security.SecurityScheme.Unsupported;
8+
import java.util.Locale;
9+
import java.util.Map;
10+
import java.util.Optional;
11+
12+
public final class SecuritySchemeParser {
13+
private SecuritySchemeParser() {}
14+
15+
public static SecurityScheme parse(Map<String, Object> raw) {
16+
String type = (String) raw.get("type");
17+
if (type == null) {
18+
throw new IllegalArgumentException("securityScheme missing required 'type'");
19+
}
20+
return switch (type) {
21+
case "apiKey" -> parseApiKey(raw);
22+
case "http" -> parseHttp(raw);
23+
default -> new Unsupported(type);
24+
};
25+
}
26+
27+
private static SecurityScheme parseApiKey(Map<String, Object> raw) {
28+
String name = (String) raw.get("name");
29+
String in = (String) raw.get("in");
30+
if (name == null || in == null) {
31+
throw new IllegalArgumentException("apiKey scheme requires 'name' and 'in'");
32+
}
33+
return new ApiKey(name, Location.valueOf(in.toUpperCase(Locale.ROOT)));
34+
}
35+
36+
private static SecurityScheme parseHttp(Map<String, Object> raw) {
37+
String scheme = (String) raw.get("scheme");
38+
if (scheme == null) {
39+
throw new IllegalArgumentException("http securityScheme requires 'scheme'");
40+
}
41+
return switch (scheme.toLowerCase(Locale.ROOT)) {
42+
case "bearer" -> new HttpBearer(Optional.ofNullable((String) raw.get("bearerFormat")));
43+
case "basic" -> new HttpBasic();
44+
default -> new Unsupported("http:" + scheme);
45+
};
46+
}
47+
}
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 com.retailsvc.http.spec.security.SecurityScheme.ApiKey;
6+
import com.retailsvc.http.spec.security.SecurityScheme.ApiKey.Location;
7+
import java.util.Map;
8+
import java.util.Optional;
9+
import org.junit.jupiter.api.Test;
10+
11+
class SchemeParserTest {
12+
13+
@Test
14+
void apiKeyHeaderParses() {
15+
var scheme =
16+
SecuritySchemeParser.parse(Map.of("type", "apiKey", "name", "X-API-Key", "in", "header"));
17+
assertThat(scheme).isEqualTo(new ApiKey("X-API-Key", Location.HEADER));
18+
}
19+
20+
@Test
21+
void httpBearerParses() {
22+
var scheme =
23+
SecuritySchemeParser.parse(
24+
Map.of("type", "http", "scheme", "bearer", "bearerFormat", "JWT"));
25+
assertThat(scheme).isEqualTo(new SecurityScheme.HttpBearer(Optional.of("JWT")));
26+
}
27+
28+
@Test
29+
void httpBasicParses() {
30+
var scheme = SecuritySchemeParser.parse(Map.of("type", "http", "scheme", "basic"));
31+
assertThat(scheme).isEqualTo(new SecurityScheme.HttpBasic());
32+
}
33+
34+
@Test
35+
void unknownTypeMapsToUnsupported() {
36+
var scheme = SecuritySchemeParser.parse(Map.of("type", "oauth2"));
37+
assertThat(scheme).isEqualTo(new SecurityScheme.Unsupported("oauth2"));
38+
}
39+
}

0 commit comments

Comments
 (0)