diff --git a/core/src/main/java/com/predic8/membrane/core/cli/RouterCLI.java b/core/src/main/java/com/predic8/membrane/core/cli/RouterCLI.java index cb6e2f054e..de4bc97fd5 100644 --- a/core/src/main/java/com/predic8/membrane/core/cli/RouterCLI.java +++ b/core/src/main/java/com/predic8/membrane/core/cli/RouterCLI.java @@ -75,8 +75,7 @@ private static void start(String[] args) { if (commandLine.getCommand().getName().equals("private-jwk-to-public")) { privateJwkToPublic(commandLine); } - var router = getRouter(commandLine); - if (router instanceof DefaultRouter dr) + if (getRouter(commandLine) instanceof DefaultRouter dr) dr.waitFor(); } diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/apikey/ApiKeysInterceptor.java b/core/src/main/java/com/predic8/membrane/core/interceptor/apikey/ApiKeysInterceptor.java index d6cc29902e..c598b02ce6 100644 --- a/core/src/main/java/com/predic8/membrane/core/interceptor/apikey/ApiKeysInterceptor.java +++ b/core/src/main/java/com/predic8/membrane/core/interceptor/apikey/ApiKeysInterceptor.java @@ -14,7 +14,6 @@ package com.predic8.membrane.core.interceptor.apikey; import com.predic8.membrane.annot.*; -import com.predic8.membrane.core.config.spring.*; import com.predic8.membrane.core.exchange.*; import com.predic8.membrane.core.interceptor.*; import com.predic8.membrane.core.interceptor.apikey.extractors.*; @@ -23,11 +22,10 @@ import org.slf4j.*; import java.util.*; -import java.util.stream.*; import static com.predic8.membrane.core.exceptions.ProblemDetails.*; import static com.predic8.membrane.core.interceptor.Outcome.*; -import static java.util.stream.Collectors.joining; +import static java.util.stream.Collectors.*; import static java.util.stream.Stream.*; /** @@ -85,10 +83,17 @@ public String getLongDescription() { @Override public void init() { super.init(); - // At the moment the beanFactory is only there when the Membrane configuration was read from XML + + // Todo: Move logic into the registry + // The beanFactory is only there when the Membrane configuration was read from XML if (router.getBeanFactory() != null) { stores.addAll(router.getBeanFactory().getBeansOfType(ApiKeyStore.class).values()); } + // For YAML configuration + if (router.getRegistry() != null) { + this.stores.addAll(router.getRegistry().getBeans(ApiKeyStore.class)); + } + stores.forEach(s -> s.init(router)); // Add the default extractor if none is configured diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/apikey/extractors/ApiKeyHeaderExtractor.java b/core/src/main/java/com/predic8/membrane/core/interceptor/apikey/extractors/ApiKeyHeaderExtractor.java index 1f2a82bd94..26b3c65315 100644 --- a/core/src/main/java/com/predic8/membrane/core/interceptor/apikey/extractors/ApiKeyHeaderExtractor.java +++ b/core/src/main/java/com/predic8/membrane/core/interceptor/apikey/extractors/ApiKeyHeaderExtractor.java @@ -22,7 +22,6 @@ import static com.predic8.membrane.core.security.ApiKeySecurityScheme.In.*; /** - * @deprecated Set an expression like ${header['api']} on apiKey * @description Extracts an API key from a specific HTTP request header. By default, the header name * is X-Api-Key. If the header is present, its first value is returned as the API key. *

diff --git a/distribution/examples/security/api-key/apikey-openapi/apis.yaml b/distribution/examples/security/api-key/apikey-openapi/apis.yaml new file mode 100644 index 0000000000..3227b1e5ee --- /dev/null +++ b/distribution/examples/security/api-key/apikey-openapi/apis.yaml @@ -0,0 +1,25 @@ +# yaml-language-server: $schema=https://www.membrane-api.io/v7.0.5.json + +components: + store: + apiKeyFileStore: + location: ./demo-keys.txt +--- + +api: + port: 2000 + specs: + - openapi: + location: fruitshop-api-v2-openapi-3-security.yml + validateSecurity: true + flow: + - apiKey: + # API keys are validated in the OpenAPI validator with validateSecurity: true. See the OpenAPI document for details. + required: false + extractors: + - headerExtractor: + name: "X-Api-Key" + # The API key must be extracted before the OpenAPI validator is called. + # Normally, the OpenAPI is validated before the flow is executed. By explicitly + # setting the openapiValidator to this position, the OpenAPI is validated after the apiKey plugin. + - openapiValidator: {} \ No newline at end of file diff --git a/distribution/src/test/java/com/predic8/membrane/examples/withinternet/test/APIKeyWithOpenAPIExampleTest.java b/distribution/src/test/java/com/predic8/membrane/examples/withinternet/test/APIKeyWithOpenAPIExampleTest.java index f51c2b045a..50bc4ee199 100644 --- a/distribution/src/test/java/com/predic8/membrane/examples/withinternet/test/APIKeyWithOpenAPIExampleTest.java +++ b/distribution/src/test/java/com/predic8/membrane/examples/withinternet/test/APIKeyWithOpenAPIExampleTest.java @@ -26,13 +26,15 @@ public class APIKeyWithOpenAPIExampleTest extends AbstractSampleMembraneStartStopTestcase { + public static final String API_KEY_HEADER = "X-Api-Key"; + @Override protected String getExampleDirName() { return "security/api-key/apikey-openapi"; } @Test - public void noApiKey() { + void noApiKey() { when() .get("http://localhost:2000/shop/v2/products") .then().assertThat() @@ -41,9 +43,9 @@ public void noApiKey() { } @Test - public void noScopesGet() { + void noScopesGet() { given() - .header("X-Api-Key", "111") + .header(API_KEY_HEADER, "111") .when() .get("http://localhost:2000/shop/v2/products") .then().assertThat() @@ -53,9 +55,9 @@ public void noScopesGet() { } @Test - public void noScopesPost() { + void noScopesPost() { given() - .header("X-Api-Key", "111") + .header(API_KEY_HEADER, "111") .contentType(APPLICATION_JSON) .body(""" { @@ -78,9 +80,9 @@ public void noScopesPost() { } @Test - public void writeScopes() { + void writeScopes() { given() - .headers("X-Api-Key", "222") + .headers(API_KEY_HEADER, "222") .contentType(APPLICATION_JSON) .body("{\"name\": \"Mango\", \"price\": 2.79}") .when()