diff --git a/core/src/main/java/com/predic8/membrane/core/cloud/etcd/EtcdBasedConfigurator.java b/core/src/main/java/com/predic8/membrane/core/cloud/etcd/EtcdBasedConfigurator.java index 5862d6a18f..4d62885a38 100644 --- a/core/src/main/java/com/predic8/membrane/core/cloud/etcd/EtcdBasedConfigurator.java +++ b/core/src/main/java/com/predic8/membrane/core/cloud/etcd/EtcdBasedConfigurator.java @@ -192,12 +192,9 @@ private void cleanUpNotRunningNodes(HashSet newRunningNodes shutdownRunningClusterNode(node); } - HashSet modules = new HashSet<>(); - for (String module : runningNodesForModule.keySet()) { - modules.add(module); - } + HashSet modules = new HashSet<>(runningNodesForModule.keySet()); for (String module : modules) { - if (runningNodesForModule.get(module).size() == 0) { + if (runningNodesForModule.get(module).isEmpty()) { runningNodesForModule.remove(module); shutDownRunningModuleServiceProxy(module); } diff --git a/core/src/main/java/com/predic8/membrane/core/graphql/GraphQLProtectionInterceptor.java b/core/src/main/java/com/predic8/membrane/core/graphql/GraphQLProtectionInterceptor.java index 439bc6f158..76130e74e6 100644 --- a/core/src/main/java/com/predic8/membrane/core/graphql/GraphQLProtectionInterceptor.java +++ b/core/src/main/java/com/predic8/membrane/core/graphql/GraphQLProtectionInterceptor.java @@ -66,6 +66,7 @@ public class GraphQLProtectionInterceptor extends AbstractInterceptor { private List allowedMethods = Lists.newArrayList("GET", "POST"); private int maxRecursion = 3; private int maxDepth = 7; + private int maxMutations = 5; public GraphQLProtectionInterceptor() { name = "GraphQL protection"; @@ -159,6 +160,9 @@ public Outcome handleRequest(Exchange exc) throws Exception { ExecutableDocument ed = graphQLParser.parseRequest(new ByteArrayInputStream(((String) query).getBytes(UTF_8))); + if (countMutations(ed.getExecutableDefinitions()) > maxMutations) + return error(exc, 400, "Too many mutations defined in document."); + // so far, this ensures uniqueness of global names List e1 = new GraphQLValidator().validate(ed); if (e1 != null && e1.size() > 0) @@ -191,8 +195,6 @@ public Outcome handleRequest(Exchange exc) throws Exception { .map(exd -> (OperationDefinition) exd).toList(); if (ods.size() == 0) return error(exc, "Could not find an OperationDefinition in the GraphQL document."); - if (ods.size() > 1) - return error(exc, "Multiple OperationDefinitions with the same name in the GraphQL document."); operationToExecute = ods.get(0); } @@ -203,6 +205,15 @@ public Outcome handleRequest(Exchange exc) throws Exception { return Outcome.CONTINUE; } + public int countMutations(List definitions) { + return (int) definitions.stream() + .filter(definition -> definition instanceof OperationDefinition) + .map(definition -> (OperationDefinition) definition) + .filter(operation -> operation.getOperationType() != null) + .filter(operation -> operation.getOperationType().getOperation().equals("mutation")) + .count(); + } + private String getDepthOrRecursionError(ExecutableDocument ed, OperationDefinition od) { String err = checkSelections(ed, od, od.getSelections(), new ArrayList<>(), new HashSet<>()); if (err != null) @@ -292,6 +303,20 @@ public boolean isAllowExtensions() { return allowExtensions; } + /** + * Limit how many mutations can be defined in a document query. + * @default 5 + * @example 2 + */ + @MCAttribute + public void setMaxMutations(int maxMutations) { + this.maxMutations = maxMutations; + } + + public int getMaxMutations() { + return maxMutations; + } + /** * Whether to allow GraphQL "extensions". * @default false diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/authentication/session/StaticUserDataProvider.java b/core/src/main/java/com/predic8/membrane/core/interceptor/authentication/session/StaticUserDataProvider.java index f7b7edd898..0dea8dac06 100644 --- a/core/src/main/java/com/predic8/membrane/core/interceptor/authentication/session/StaticUserDataProvider.java +++ b/core/src/main/java/com/predic8/membrane/core/interceptor/authentication/session/StaticUserDataProvider.java @@ -169,8 +169,7 @@ public Map getAttributes() { */ @MCOtherAttributes public void setAttributes(Map attributes) { - for (Map.Entry e : attributes.entrySet()) - this.attributes.put(e.getKey(), e.getValue()); + this.attributes.putAll(attributes); } } diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/flow/ConditionalInterceptor.java b/core/src/main/java/com/predic8/membrane/core/interceptor/flow/ConditionalInterceptor.java index 834baa7ed2..7996567ab9 100644 --- a/core/src/main/java/com/predic8/membrane/core/interceptor/flow/ConditionalInterceptor.java +++ b/core/src/main/java/com/predic8/membrane/core/interceptor/flow/ConditionalInterceptor.java @@ -25,8 +25,10 @@ import java.util.*; import java.util.function.*; +import static com.predic8.membrane.core.interceptor.Interceptor.Flow.REQUEST; import static com.predic8.membrane.core.interceptor.Outcome.*; import static com.predic8.membrane.core.interceptor.flow.ConditionalInterceptor.LanguageType.*; +import static com.predic8.membrane.core.lang.ScriptingUtils.createParameterBindings; /** * @description

@@ -69,8 +71,15 @@ public void init(Router router) throws Exception { } private boolean testCondition(Exchange exc) { - HashMap parameters = new HashMap<>(); - parameters.put("exc", exc); + HashMap parameters = new HashMap<>() {{ + put("Outcome", Outcome.class); + put("RETURN", RETURN); + put("CONTINUE", CONTINUE); + put("ABORT", Outcome.ABORT); + put("spring", router.getBeanFactory()); + put("exc", exc); + putAll(createParameterBindings(router.getUriFactory(), exc, exc.getRequest(), REQUEST, false)); + }}; return condition.apply(parameters); } diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/oauth2/parameter/ClaimsParameter.java b/core/src/main/java/com/predic8/membrane/core/interceptor/oauth2/parameter/ClaimsParameter.java index 6bbced9dfd..eeb2abf8b9 100644 --- a/core/src/main/java/com/predic8/membrane/core/interceptor/oauth2/parameter/ClaimsParameter.java +++ b/core/src/main/java/com/predic8/membrane/core/interceptor/oauth2/parameter/ClaimsParameter.java @@ -121,11 +121,7 @@ private Map getObject(String objectName){ } private HashSet getClaimsFromJsonObject(String objectName){ - HashSet claims = new HashSet<>(); - for(String claimName : getObject(objectName).keySet()) - claims.add(claimName); - - return claims; + return new HashSet<>(getObject(objectName).keySet()); } public HashSet getUserinfoClaims(){ diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/rest/REST2SOAPInterceptor.java b/core/src/main/java/com/predic8/membrane/core/interceptor/rest/REST2SOAPInterceptor.java index 1b2ed8f55d..3198075b64 100644 --- a/core/src/main/java/com/predic8/membrane/core/interceptor/rest/REST2SOAPInterceptor.java +++ b/core/src/main/java/com/predic8/membrane/core/interceptor/rest/REST2SOAPInterceptor.java @@ -280,11 +280,18 @@ private void setServiceEndpoint(AbstractExchange exc, Mapping mapping) { } private String getNewDestination(AbstractExchange exc) { - return "http://" + ((AbstractServiceProxy) exc.getRule()).getTargetHost() + ":" + return getProtocol(exc) + "://" + ((AbstractServiceProxy) exc.getRule()).getTargetHost() + ":" + ((AbstractServiceProxy) exc.getRule()).getTargetPort() + exc.getRequest().getUri(); } + private String getProtocol(AbstractExchange exc) { + if(exc.getRule().getSslOutboundContext() != null) { + return "https"; + } + return "http"; + } + private String getURI(AbstractExchange exc) { return exc.getRequest().getUri(); } @@ -305,5 +312,4 @@ public void setMappings(List mappings) { public String getShortDescription() { return "Transforms REST requests into SOAP and responses vice versa."; } - -} +} \ No newline at end of file diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/rewrite/RewriteInterceptor.java b/core/src/main/java/com/predic8/membrane/core/interceptor/rewrite/RewriteInterceptor.java index a05b5817a7..cc2b985750 100644 --- a/core/src/main/java/com/predic8/membrane/core/interceptor/rewrite/RewriteInterceptor.java +++ b/core/src/main/java/com/predic8/membrane/core/interceptor/rewrite/RewriteInterceptor.java @@ -23,9 +23,11 @@ import org.slf4j.*; import java.io.*; +import java.net.URISyntaxException; import java.util.*; import java.util.regex.*; +import static com.predic8.membrane.core.exceptions.ProblemDetails.createProblemDetails; import static com.predic8.membrane.core.interceptor.Interceptor.Flow.Set.*; import static com.predic8.membrane.core.interceptor.Outcome.*; import static com.predic8.membrane.core.interceptor.rewrite.RewriteInterceptor.Type.*; @@ -147,8 +149,10 @@ public Outcome handleRequest(Exchange exc) throws Exception { ListIterator it = exc.getDestinations().listIterator(); while ( it.hasNext() ) { String dest = it.next(); + String pathQuery = getPathQueryOrSetError(router.getUriFactory(), dest, exc); + if (pathQuery == null) + return RETURN; - String pathQuery = URLUtil.getPathQuery(router.getUriFactory(), dest); int pathBegin = -1; int authorityBegin = dest.indexOf("//"); if (authorityBegin != -1) @@ -187,7 +191,9 @@ public Outcome handleRequest(Exchange exc) throws Exception { if (mapping != null && mapping.do_ == REWRITE) { String newDest = replace(exc.getRequest().getUri(), mapping); if (newDest.contains("://")) { - newDest = URLUtil.getPathQuery(router.getUriFactory(), newDest); + newDest = getPathQueryOrSetError(router.getUriFactory(), newDest, exc); + if (newDest == null) + return RETURN; } exc.getRequest().setUri(newDest); } @@ -195,6 +201,22 @@ public Outcome handleRequest(Exchange exc) throws Exception { return CONTINUE; } + private String getPathQueryOrSetError(URIFactory factory, String destination, Exchange exc) throws URISyntaxException { + String pathQuery; + try { + pathQuery = URLUtil.getPathQuery(factory, destination); + } catch(URISyntaxException ignore) { + exc.setResponse( + createProblemDetails( + 400, + "/uri-parser", + "This URL does not follow the URI specification. Confirm the validity of the provided URL.") + ); + return null; + } + return pathQuery; + } + private void logMappings() { for (Mapping m : mappings) { log.debug("[from:"+m.from+"],[to:"+m.to+"],[do:"+m.do_+"]"); diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/xmlcontentfilter/SimpleXPathAnalyzer.java b/core/src/main/java/com/predic8/membrane/core/interceptor/xmlcontentfilter/SimpleXPathAnalyzer.java index f0db691a79..698f1aa1ef 100644 --- a/core/src/main/java/com/predic8/membrane/core/interceptor/xmlcontentfilter/SimpleXPathAnalyzer.java +++ b/core/src/main/java/com/predic8/membrane/core/interceptor/xmlcontentfilter/SimpleXPathAnalyzer.java @@ -198,8 +198,7 @@ private List splitUnionExprIntoIntersectExceptExprs(ContainerNode if (n instanceof UnparsedStringNode) { List parts = new ArrayList<>(); for (String part : splitOnOperand(((UnparsedStringNode)n).s, "|")) - for (String part2 : splitOnOperand(part, "union")) - parts.add(part2); + parts.addAll(splitOnOperand(part, "union")); for (int i = 0; i < parts.size(); i++) { if (i >= 1) { // next IntersectExceptExpr diff --git a/core/src/main/java/com/predic8/membrane/core/resolver/FileSchemaResolver.java b/core/src/main/java/com/predic8/membrane/core/resolver/FileSchemaResolver.java index 21d805fe92..c6ee87e9f8 100644 --- a/core/src/main/java/com/predic8/membrane/core/resolver/FileSchemaResolver.java +++ b/core/src/main/java/com/predic8/membrane/core/resolver/FileSchemaResolver.java @@ -21,6 +21,7 @@ import java.net.URLDecoder; import java.nio.file.*; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.concurrent.ConcurrentHashMap; @@ -127,10 +128,8 @@ public List getChildren(String url) { String[] children = new File(normalize(url)).list(); if (children == null) return null; - ArrayList res = new ArrayList<>(children.length); - for (String child : children) - res.add(child); - return res; + + return Arrays.asList(children); } @Override diff --git a/core/src/test/java/com/predic8/membrane/core/UnitTests.java b/core/src/test/java/com/predic8/membrane/core/UnitTests.java index 6052d63848..6c1c1daa03 100644 --- a/core/src/test/java/com/predic8/membrane/core/UnitTests.java +++ b/core/src/test/java/com/predic8/membrane/core/UnitTests.java @@ -18,6 +18,7 @@ import com.predic8.membrane.core.config.ReadRulesConfigurationTest; import com.predic8.membrane.core.config.ReadRulesWithInterceptorsConfigurationTest; import com.predic8.membrane.core.exchangestore.*; +import com.predic8.membrane.core.graphql.GraphQLProtectionInterceptorTest; import com.predic8.membrane.core.http.*; import com.predic8.membrane.core.http.cookie.*; import com.predic8.membrane.core.interceptor.*; @@ -130,6 +131,7 @@ XMLProtectorTest.class, AbstractExchangeStoreTest.class, JsonProtectionInterceptorTest.class, + GraphQLProtectionInterceptorTest.class, BeautifierInterceptorTest.class, ExchangeEvaluationContextTest.class, PaddingHeaderInterceptorTest.class, diff --git a/core/src/test/java/com/predic8/membrane/core/graphql/GraphQLProtectionInterceptorTest.java b/core/src/test/java/com/predic8/membrane/core/graphql/GraphQLProtectionInterceptorTest.java index 43b6f0212e..220c896812 100644 --- a/core/src/test/java/com/predic8/membrane/core/graphql/GraphQLProtectionInterceptorTest.java +++ b/core/src/test/java/com/predic8/membrane/core/graphql/GraphQLProtectionInterceptorTest.java @@ -14,16 +14,23 @@ package com.predic8.membrane.core.graphql; -import com.predic8.membrane.core.*; -import com.predic8.membrane.core.exchange.*; -import com.predic8.membrane.core.http.*; -import com.predic8.membrane.core.interceptor.*; -import org.junit.jupiter.api.*; - -import static com.predic8.membrane.core.http.MimeType.*; -import static java.net.URLEncoder.*; -import static java.nio.charset.StandardCharsets.*; -import static org.junit.jupiter.api.Assertions.*; +import com.predic8.membrane.core.HttpRouter; +import com.predic8.membrane.core.exchange.Exchange; +import com.predic8.membrane.core.graphql.model.*; +import com.predic8.membrane.core.http.Request; +import com.predic8.membrane.core.interceptor.Outcome; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import javax.security.auth.login.Configuration; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import static com.predic8.membrane.core.http.MimeType.APPLICATION_JSON; +import static java.net.URLEncoder.encode; +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.junit.jupiter.api.Assertions.assertEquals; public class GraphQLProtectionInterceptorTest { @@ -339,6 +346,42 @@ public void recursionNotOK() throws Exception { Outcome.RETURN); } + @Test + public void mutationsCountNotOK() throws Exception { + verifyPost("/", + APPLICATION_JSON, + """ + {"query":"mutation abc{} mutation abcd{} mutation abcde{} mutation abcdef{} mutation abcdefg{} mutation abcdefgh{}", + "operationName": ""}""", + Outcome.RETURN); + } + + @Test + public void mutationsCountOK() throws Exception { + verifyPost("/", + APPLICATION_JSON, + """ + {"query":"mutation abc{} mutation abcd{} mutation abcde{} mutation abcdef{} mutation abcdefg{}", + "operationName": ""}""", + Outcome.CONTINUE); + } + + @Test + public void countThreeMutations() { + List definitions = Arrays.asList( + opDefOfType("mutation"), + opDefOfType("mutation"), + opDefOfType("mutation"), + opDefOfType("query") + ); + + assertEquals(3, i.countMutations(definitions)); + } + + private OperationDefinition opDefOfType(String type) { + return new OperationDefinition(new OperationType(type), "", new ArrayList<>(), new ArrayList<>(), new ArrayList<>()); + } + @Test public void invalidMethod() throws Exception { Exchange e = new Request.Builder().put("/").contentType(APPLICATION_JSON).body("{}").buildExchange(); diff --git a/core/src/test/java/com/predic8/membrane/core/interceptor/rewrite/RewriteInterceptorTest.java b/core/src/test/java/com/predic8/membrane/core/interceptor/rewrite/RewriteInterceptorTest.java index c9cea64ab0..b103593a51 100644 --- a/core/src/test/java/com/predic8/membrane/core/interceptor/rewrite/RewriteInterceptorTest.java +++ b/core/src/test/java/com/predic8/membrane/core/interceptor/rewrite/RewriteInterceptorTest.java @@ -14,11 +14,13 @@ package com.predic8.membrane.core.interceptor.rewrite; import static com.predic8.membrane.core.interceptor.Outcome.CONTINUE; +import static com.predic8.membrane.core.interceptor.Outcome.RETURN; import static org.junit.jupiter.api.Assertions.*; import java.util.ArrayList; import java.util.List; +import com.predic8.membrane.core.interceptor.Outcome; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -77,11 +79,27 @@ void testRewrite() throws Exception { } @Test - void storeSample() throws Exception { + void storeSample() throws Exception { exc.setRequest(MessageUtil.getGetRequest("https://api.predic8.de/store/products/")); assertEquals(CONTINUE, di.handleRequest(exc)); assertEquals(CONTINUE, rewriter.handleRequest(exc)); assertEquals("https://api.predic8.de/shop/v2/products/", exc.getDestinations().get(0)); } + @Test + void invalidURI() throws Exception { + exc.setRequest(MessageUtil.getGetRequest("/buy/banana/%")); + exc.setRule(sp); + + assertEquals(CONTINUE, di.handleRequest(exc)); + assertEquals(RETURN, rewriter.handleRequest(exc)); + assertEquals( + """ + { + "type" : "http://membrane-api.io/error/uri-parser", + "title" : "This URL does not follow the URI specification. Confirm the validity of the provided URL." + }""", + exc.getResponse().getBodyAsStringDecoded() + ); + } } diff --git a/core/src/test/java/com/predic8/membrane/core/interceptor/rewrite/RewriterTest.java b/core/src/test/java/com/predic8/membrane/core/interceptor/rewrite/RewriterTest.java new file mode 100644 index 0000000000..b6fa4ce464 --- /dev/null +++ b/core/src/test/java/com/predic8/membrane/core/interceptor/rewrite/RewriterTest.java @@ -0,0 +1,90 @@ +/* Copyright 2023 predic8 GmbH, www.predic8.com + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. */ +package com.predic8.membrane.core.interceptor.rewrite; + +import com.predic8.membrane.core.HttpRouter; +import com.predic8.membrane.core.exchange.Exchange; +import com.predic8.membrane.core.http.Request; +import com.predic8.membrane.core.interceptor.rewrite.RewriteInterceptor.Mapping; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class RewriterTest { + + private RewriteInterceptor rewriter; + + @BeforeEach + public void setUp() throws Exception { + rewriter = new RewriteInterceptor(); + List mappings = new ArrayList<>(); + mappings.add( new Mapping("/hello/(.*)", "/$1", null)); + rewriter.setMappings(mappings); + + rewriter.init(new HttpRouter()); + } + + @Test + void testRewriteWithoutTarget() throws Exception { + assertThrows(URISyntaxException.class, () -> { + Exchange exc = new Exchange(null); + Request req = new Request(); + req.setUri("/%"); + exc.setRequest(req); + + exc.getDestinations().add("/%"); + + rewriter.handleRequest(exc); + + System.out.println("uri: " + exc.getRequest().getUri()); + System.out.println("dest: " + exc.getDestinations().get(0)); + }); + } + + @Test + void testRewriteWithoutTarget2() throws Exception { + Exchange exc = new Exchange(null); + Request req = new Request(); + req.setUri("/%25"); + exc.setRequest(req); + + exc.getDestinations().add("/%25"); + + rewriter.handleRequest(exc); + + assertEquals("/%25", exc.getRequest().getUri()); + assertEquals("/%25", exc.getDestinations().get(0)); + } + + @Test + void testRewriteWithoutTarget3() throws Exception { + Exchange exc = new Exchange(null); + Request req = new Request(); + req.setUri("/hello/%25"); + exc.setRequest(req); + + exc.getDestinations().add("/hello/%25"); + + rewriter.handleRequest(exc); + + assertEquals("/%25", exc.getRequest().getUri()); + assertEquals("/%25", exc.getDestinations().get(0)); + } +} \ No newline at end of file diff --git a/distribution/examples/openapi/README.md b/distribution/examples/openapi/README.md index 67e49df496..d95de682a7 100644 --- a/distribution/examples/openapi/README.md +++ b/distribution/examples/openapi/README.md @@ -9,49 +9,49 @@ Membrane's **openAPIProxy** offers support for [OpenAPI](https://github.com/OAI/ This page serves as a reference for the functions and configuration. See also the examples: -- [openAPIProxy, UI and Swagger UI](openapi-proxy) +- [api, UI and Swagger UI](openapi-proxy) - [OpenAPI validation](validation-simple) - [OpenAPI validation extended sample](validation) -The **openAPIProxy** is featured in Membrane version 5 and newer and supports OpenAPI from version 2. +The __api__ is featured in Membrane version 5 and newer and supports OpenAPI version 2 and up. # Configuration -An _openAPIProxy_ can be added to the _proxies.xml_ configuration file. See the example in the [openapi-proxy/](openapi-proxy/) folder. +An _api_ can be added to the _proxies.xml_ configuration file. See the example in the [openapi-proxy/](openapi-proxy) folder. ```xml - + - + ``` The _spec_ element adds OpenAPI documents to the configuration. You can add _*.json_, _*.yml_ and _*.yaml_ files in a folder using the _dir_ attribute or single local or remote files using the _location_ attribute. -The addresses of the backend servers and the basepaths are taken from the OpenAPI specification. Suppose an OpenAPI document contains the _servers_ field below. The proxy will match requests against the basepath ```/shop``` and in case of a match it will forward the request to the backend server ```api.predic8.de``` using TLS. +The addresses of the backend servers and the base-paths are taken from the OpenAPI specification. Suppose an OpenAPI document contains the _servers_ field below. The proxy will match requests against the base-path ```/shop/v2``` and in case of a match it will forward the request to the backend server ```api.predic8.de``` using TLS. ```yaml servers: - - url: https://api.predic8.de/shop + - url: https://api.predic8.de/shop/v2 ``` If the basepath does not match, the next API is checked. -It is also possible to configure the backend address using a [target](https://www.membrane-soa.org/service-proxy-doc/4.8/configuration/reference/target.htm) in the configuration. Then the addresses in the ```server``` field of the OpenAPI are ignored and the request is sent to the address from the _target_ element. +It is also possible to configure the backend address using a [target](https://www.membrane-soa.org/api-gateway-doc/current/configuration/reference/target.htm) in the configuration. Then the addresses in the ```server``` field of the OpenAPI are ignored and the request is sent to the address from the _target_ element. ```xml - + - + ``` @@ -85,14 +85,14 @@ x-membrane-validation: # Overview and Swagger UI -The __openAPIProxy__ has a UI that can be reached on its port e.g. [http://localhost:2000/api-doc](http://localhost:2000/api-doc). Follow the links on the left to access the Swagger UI or the link on the right to download the OpenAPI document. +The __api__ has a UI that can be reached on its port e.g. [http://localhost:2000/api-docs](http://localhost:2000/api-docs). Follow the links on the left to access the Swagger UI or the link on the right to download the OpenAPI document. ![Overview UI](openapi-proxy/api-overview.png) -To get a JSON description of the deployed OpenAPI documents call the same URL outside the browser e.g. in curl or Postman: +To get a JSON description of the deployed OpenAPI documents call the same `URL` outside the browser e.g. in curl or Postman: ``` -curl http://localhost:2000/api-doc +curl http://localhost:2000/api-docs ``` @@ -100,42 +100,42 @@ curl http://localhost:2000/api-doc When an API is published on a gateway the OpenAPI must point to the gateway instead of pointing to the backend server. Rewriting changes the backend addresses of an OpenAPI document to the address of the gateway. -The _openAPIProxy_ exposes the OpenAPI specifications in the UI and over an endpoint: +The _api_ exposes the OpenAPI specifications in the UI and over an endpoint: ``` -/api_docs/<> +/api-docs/<> ``` The addresses in the OpenAPI's _/servers_ field are rewritten to the address the endpoint is called on the gateway. Suppose the OpenAPI has the following servers field: ```yaml servers: -- url: "http://fruit-shop.api-demos.svc.ack.predic8.de:8080/shop" +- url: "http://fruit-shop.api-demos.svc.ack.predic8.de:8080/shop/v2" ``` And you send a request to Membrane like this: ```http request -GET /api-doc/fruit-shop-api-v1-0 +GET /api-docs/fruit-shop-api-v1-0 Host: api.predic8.de:443 ``` -Then the rewriter of the _openAPIProxy_ will turn the _servers_ field into: +Then the rewriter of the _api_ will turn the _servers_ field into: ```yaml servers: -- url: "https://api.predic8.de:443/shop" +- url: "https://api.predic8.de:443/shop/v2" ``` -If you use the rewritten OpenAPI-document for your client, then requests will be sent to Membrane at _https://api.predic8.de:443/shop_ and then forwarded to the destination _http://fruit-shop.api-demos.svc.ack.predic8.de:8080/shop_. +If you use the rewritten OpenAPI-document for your client, then requests will be sent to Membrane at _https://api.predic8.de:443/shop/v2_ and then forwarded to the destination _http://fruit-shop.api-demos.svc.ack.predic8.de:8080/shop/v2_. # SSL/TLS -TLS for incoming and outgoing connections can be configured in the same way as for the _serviceProxy_. See the documentation for the [ssl](https://www.membrane-soa.org/service-proxy-doc/4.8/configuration/reference/ssl.htm) element. +TLS for incoming and outgoing connections can be configured in the same way as for the _serviceProxy_. See the documentation for the [ssl](https://www.membrane-soa.org/api-gateway-doc/current/configuration/reference/ssl.htm) element. ```xml - + @@ -147,13 +147,13 @@ TLS for incoming and outgoing connections can be configured in the same way as f - + ``` # Plugins / Interceptors -The behaviour of the _openAPIProxy_ can be modified like other proxies with plugins and interceptors. See the [examples](..) and the [configuration reference](http://membrane-soa.org/service-proxy-doc/4.8/configuration/reference/). +The behaviour of the _api_ can be modified like other proxies with plugins and interceptors. See the [examples](..) and the [configuration reference](http://membrane-soa.org/api-gateway-doc/current/configuration/reference/). ```xml @@ -170,7 +170,7 @@ Membrane needs unique ids for each OpenAPI document to provide a Swagger UI and ```yaml info: title: "Fruit Shop API" - version: "1.0" + version: "2.0" ``` To give an API a custom id the _x-membrane-id_ field can be used. @@ -178,7 +178,7 @@ To give an API a custom id the _x-membrane-id_ field can be used. ```yaml info: title: Fruit Shop API - version: '1.0' + version: '2.0' x-membrane-id: fruitshop ``` diff --git a/distribution/examples/openapi/openapi-proxy/fruitshop-api.yml b/distribution/examples/openapi/openapi-proxy/fruitshop-api.yml index fec60276eb..4e3e41401b 100644 --- a/distribution/examples/openapi/openapi-proxy/fruitshop-api.yml +++ b/distribution/examples/openapi/openapi-proxy/fruitshop-api.yml @@ -1,7 +1,7 @@ openapi: '3.0.2' info: title: Fruit Shop API - version: '1.0' + version: '2.0' x-membrane-id: fruitshop servers: - url: https://api.predic8.de/shop/v2