Skip to content

Commit 16a9e6f

Browse files
API Key YAML Example (#2535)
* refactor: update tests, API configurations, and router logic for API key support - Extract `X-Api-Key` into a constant for reusability in tests. - Improve router lifecycle handling with `DefaultRouter` adjustments. - Enhance YAML configuration for API key handling, including support for registry-based ApiKeyStores. - Minor refactoring and cleanup in `Apis.yaml` and related test files. * refactor: rename API key constant and improve related tests, YAML, and router logic - Renamed `API_KEY` to `API_KEY_HEADER` for better clarity and consistency. - Updated tests and flow configurations to reflect the rename. - Removed unused imports and streamlined logic in `ApiKeysInterceptor`. - Enhanced YAML setup for OpenAPI validation and API key extraction ordering. --------- Co-authored-by: Christian Gördes <118011644+christiangoerdes@users.noreply.github.com>
1 parent da8e3f0 commit 16a9e6f

5 files changed

Lines changed: 44 additions & 14 deletions

File tree

core/src/main/java/com/predic8/membrane/core/cli/RouterCLI.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,7 @@ private static void start(String[] args) {
7575
if (commandLine.getCommand().getName().equals("private-jwk-to-public")) {
7676
privateJwkToPublic(commandLine);
7777
}
78-
var router = getRouter(commandLine);
79-
if (router instanceof DefaultRouter dr)
78+
if (getRouter(commandLine) instanceof DefaultRouter dr)
8079
dr.waitFor();
8180
}
8281

core/src/main/java/com/predic8/membrane/core/interceptor/apikey/ApiKeysInterceptor.java

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
package com.predic8.membrane.core.interceptor.apikey;
1515

1616
import com.predic8.membrane.annot.*;
17-
import com.predic8.membrane.core.config.spring.*;
1817
import com.predic8.membrane.core.exchange.*;
1918
import com.predic8.membrane.core.interceptor.*;
2019
import com.predic8.membrane.core.interceptor.apikey.extractors.*;
@@ -23,11 +22,10 @@
2322
import org.slf4j.*;
2423

2524
import java.util.*;
26-
import java.util.stream.*;
2725

2826
import static com.predic8.membrane.core.exceptions.ProblemDetails.*;
2927
import static com.predic8.membrane.core.interceptor.Outcome.*;
30-
import static java.util.stream.Collectors.joining;
28+
import static java.util.stream.Collectors.*;
3129
import static java.util.stream.Stream.*;
3230

3331
/**
@@ -85,10 +83,17 @@ public String getLongDescription() {
8583
@Override
8684
public void init() {
8785
super.init();
88-
// At the moment the beanFactory is only there when the Membrane configuration was read from XML
86+
87+
// Todo: Move logic into the registry
88+
// The beanFactory is only there when the Membrane configuration was read from XML
8989
if (router.getBeanFactory() != null) {
9090
stores.addAll(router.getBeanFactory().getBeansOfType(ApiKeyStore.class).values());
9191
}
92+
// For YAML configuration
93+
if (router.getRegistry() != null) {
94+
this.stores.addAll(router.getRegistry().getBeans(ApiKeyStore.class));
95+
}
96+
9297
stores.forEach(s -> s.init(router));
9398

9499
// Add the default extractor if none is configured

core/src/main/java/com/predic8/membrane/core/interceptor/apikey/extractors/ApiKeyHeaderExtractor.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
import static com.predic8.membrane.core.security.ApiKeySecurityScheme.In.*;
2323

2424
/**
25-
* @deprecated Set an expression like ${header['api']} on apiKey
2625
* @description Extracts an API key from a specific HTTP request header. By default, the header name
2726
* is <code>X-Api-Key</code>. If the header is present, its first value is returned as the API key.
2827
* <p>
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# yaml-language-server: $schema=https://www.membrane-api.io/v7.0.5.json
2+
3+
components:
4+
store:
5+
apiKeyFileStore:
6+
location: ./demo-keys.txt
7+
---
8+
9+
api:
10+
port: 2000
11+
specs:
12+
- openapi:
13+
location: fruitshop-api-v2-openapi-3-security.yml
14+
validateSecurity: true
15+
flow:
16+
- apiKey:
17+
# API keys are validated in the OpenAPI validator with validateSecurity: true. See the OpenAPI document for details.
18+
required: false
19+
extractors:
20+
- headerExtractor:
21+
name: "X-Api-Key"
22+
# The API key must be extracted before the OpenAPI validator is called.
23+
# Normally, the OpenAPI is validated before the flow is executed. By explicitly
24+
# setting the openapiValidator to this position, the OpenAPI is validated after the apiKey plugin.
25+
- openapiValidator: {}

distribution/src/test/java/com/predic8/membrane/examples/withinternet/test/APIKeyWithOpenAPIExampleTest.java

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,15 @@
2626

2727
public class APIKeyWithOpenAPIExampleTest extends AbstractSampleMembraneStartStopTestcase {
2828

29+
public static final String API_KEY_HEADER = "X-Api-Key";
30+
2931
@Override
3032
protected String getExampleDirName() {
3133
return "security/api-key/apikey-openapi";
3234
}
3335

3436
@Test
35-
public void noApiKey() {
37+
void noApiKey() {
3638
when()
3739
.get("http://localhost:2000/shop/v2/products")
3840
.then().assertThat()
@@ -41,9 +43,9 @@ public void noApiKey() {
4143
}
4244

4345
@Test
44-
public void noScopesGet() {
46+
void noScopesGet() {
4547
given()
46-
.header("X-Api-Key", "111")
48+
.header(API_KEY_HEADER, "111")
4749
.when()
4850
.get("http://localhost:2000/shop/v2/products")
4951
.then().assertThat()
@@ -53,9 +55,9 @@ public void noScopesGet() {
5355
}
5456

5557
@Test
56-
public void noScopesPost() {
58+
void noScopesPost() {
5759
given()
58-
.header("X-Api-Key", "111")
60+
.header(API_KEY_HEADER, "111")
5961
.contentType(APPLICATION_JSON)
6062
.body("""
6163
{
@@ -78,9 +80,9 @@ public void noScopesPost() {
7880
}
7981

8082
@Test
81-
public void writeScopes() {
83+
void writeScopes() {
8284
given()
83-
.headers("X-Api-Key", "222")
85+
.headers(API_KEY_HEADER, "222")
8486
.contentType(APPLICATION_JSON)
8587
.body("{\"name\": \"Mango\", \"price\": 2.79}")
8688
.when()

0 commit comments

Comments
 (0)