From 05c9e73d97aa7ed10a8d033867563f23a51111d5 Mon Sep 17 00:00:00 2001 From: rathnapandi Date: Thu, 21 Nov 2024 21:30:53 -0700 Subject: [PATCH 1/6] Enhance template creation to include api methods and inbound per method override --- .../java/com/axway/apim/config/APIConfig.java | 22 +- .../axway/apim/config/GenerateTemplate.java | 253 ++++++++++++++---- .../config/GenerateTemplateCLIOptions.java | 11 + .../axway/apim/config/model/APISecurity.java | 46 ---- .../model/GenerateTemplateParameters.java | 18 ++ .../apim/config/GenerateTemplateTest.java | 74 ++++- .../src/test/resources/methods.yaml | 106 ++++++++ 7 files changed, 420 insertions(+), 110 deletions(-) delete mode 100644 modules/spectoconfig/src/main/java/com/axway/apim/config/model/APISecurity.java create mode 100644 modules/spectoconfig/src/test/resources/methods.yaml diff --git a/modules/spectoconfig/src/main/java/com/axway/apim/config/APIConfig.java b/modules/spectoconfig/src/main/java/com/axway/apim/config/APIConfig.java index 075df3313..3e6313b69 100644 --- a/modules/spectoconfig/src/main/java/com/axway/apim/config/APIConfig.java +++ b/modules/spectoconfig/src/main/java/com/axway/apim/config/APIConfig.java @@ -2,11 +2,13 @@ import com.axway.apim.api.API; import com.axway.apim.api.model.*; -import com.axway.apim.config.model.APISecurity; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonPropertyOrder; -import java.util.*; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; @JsonPropertyOrder({"name", "path", "state", "version", "organization", "apiSpecification", "summary", "descriptionType", "descriptionManual", "vhost", "remoteHost", "backendBasepath", "image", "inboundProfiles", "outboundProfiles", "securityProfiles", "authenticationProfiles", "tags", "customProperties", @@ -16,12 +18,10 @@ public class APIConfig { public static final String DEFAULT = "_default"; private final API api; private final String apiDefinition; - private final Map securityProfiles; - public APIConfig(API api, String apiDefinition, Map securityProfiles) { + public APIConfig(API api, String apiDefinition) { this.api = api; this.apiDefinition = apiDefinition; - this.securityProfiles = securityProfiles; } public Map getOutboundProfiles() { @@ -46,15 +46,11 @@ public Map getOutboundProfiles() { } - public List> getSecurityProfiles() { - if (securityProfiles.size() == 1) { - List apiSecurities = (List) securityProfiles.get("devices"); - if (apiSecurities.get(0).getType().equals(DeviceType.passThrough.getName())) - return Collections.emptyList(); + public List getSecurityProfiles() { + if (api.getSecurityProfiles().size() == 1) { + return Collections.emptyList(); } - List> list = new ArrayList<>(); - list.add(securityProfiles); - return list; + return api.getSecurityProfiles(); } public String getPath() { diff --git a/modules/spectoconfig/src/main/java/com/axway/apim/config/GenerateTemplate.java b/modules/spectoconfig/src/main/java/com/axway/apim/config/GenerateTemplate.java index 358989253..15247a1a8 100644 --- a/modules/spectoconfig/src/main/java/com/axway/apim/config/GenerateTemplate.java +++ b/modules/spectoconfig/src/main/java/com/axway/apim/config/GenerateTemplate.java @@ -5,7 +5,6 @@ import com.axway.apim.api.model.*; import com.axway.apim.cli.APIMCLIServiceProvider; import com.axway.apim.cli.CLIServiceMethod; -import com.axway.apim.config.model.APISecurity; import com.axway.apim.config.model.GenerateTemplateParameters; import com.axway.apim.lib.StandardExportParams; import com.axway.apim.lib.error.AppException; @@ -19,7 +18,12 @@ import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter; import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider; import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.Operation; +import io.swagger.v3.oas.models.PathItem; +import io.swagger.v3.oas.models.Paths; import io.swagger.v3.oas.models.info.Info; +import io.swagger.v3.oas.models.security.SecurityRequirement; +import io.swagger.v3.oas.models.security.SecurityScheme; import io.swagger.v3.oas.models.servers.Server; import io.swagger.v3.oas.models.tags.Tag; import io.swagger.v3.parser.OpenAPIV3Parser; @@ -51,6 +55,10 @@ public class GenerateTemplate implements APIMCLIServiceProvider { private static final Logger LOG = LoggerFactory.getLogger(GenerateTemplate.class); public static final String DEFAULT = "_default"; + public static final String ORIGINAL = "original"; + public static final String PASS_THROUGH = "Pass Through"; + public static final String REMOVE_CREDENTIALS_ON_SUCCESS = "removeCredentialsOnSuccess"; + public static final String TOKEN_STORE = "tokenStore"; @Override public String getName() { @@ -89,8 +97,8 @@ public static int generate(String[] args) { GenerateTemplate app = new GenerateTemplate(); try { File file = new File(params.getConfig()); - if(file.getParentFile() != null && !file.getParentFile().exists() && (file.getParentFile().mkdirs())){ - LOG.info("Created new Directory : {}", file.getParentFile()); + if (file.getParentFile() != null && !file.getParentFile().exists() && (file.getParentFile().mkdirs())) { + LOG.info("Created new Directory : {}", file.getParentFile()); } APIConfig apiConfig = app.generateTemplate(params); try (FileWriter fileWriter = new FileWriter(params.getConfig())) { @@ -110,7 +118,7 @@ public static int generate(String[] args) { } catch (AppException e) { LOG.error("{} : Error code {}", e.getError().getDescription(), e.getError().getCode()); return e.getError().getCode(); - }catch (Exception e) { + } catch (Exception e) { LOG.error("Error in processing :", e); return 1; } @@ -171,7 +179,7 @@ public APIConfig generateTemplate(GenerateTemplateParameters parameters) throws api.setPath(basePath); api.setName(info.getTitle()); api.setVersion(info.getVersion()); - api.setDescriptionType("original"); + api.setDescriptionType(ORIGINAL); CorsProfile corsProfile = new CorsProfile(); corsProfile.setName("Custom CORS"); corsProfile.setIsDefault(false); @@ -205,7 +213,7 @@ public APIConfig generateTemplate(GenerateTemplateParameters parameters) throws api.setInboundProfiles(inboundProfiles); String frontendAuthType = parameters.getFrontendAuthType(); // If frontendAuthType is null, use authentication from openapi spec. If none found, set it as pass through - Map securityProfiles = addInboundSecurityToAPI(frontendAuthType); + List securityProfiles = addInboundSecurityToAPI(frontendAuthType); String backendAuthType = parameters.getBackendAuthType(); addOutboundSecurityToAPI(api, backendAuthType); String apiSpecLocation; @@ -216,19 +224,168 @@ public APIConfig generateTemplate(GenerateTemplateParameters parameters) throws } else { apiSpecLocation = parameters.getApiDefinition(); } + if (parameters.isIncludeMethods()) { + List methods = addMethods(openAPI); + api.setApiMethods(methods); + } + + if (parameters.isInboundPerMethodOverride()) { + Map inboundProfileMap = addInboundPerMethodOverride(openAPI, api, securityProfiles); + api.setInboundProfiles(inboundProfileMap); + } - return new APIConfig(api, apiSpecLocation, securityProfiles); + return new APIConfig(api, apiSpecLocation); } - public void addTags(API api, OpenAPI openAPI){ - List tags = openAPI.getTags(); - if(tags != null) { - TagMap apiManagerTags = new TagMap(); - for (Tag tag : tags) { - String[] value = new String[1]; - value[0] = tag.getName(); - apiManagerTags.put(tag.getName(), value); + public List addMethods(OpenAPI openAPI) { + List methods = new ArrayList<>(); + Paths paths = openAPI.getPaths(); + for (Map.Entry pathItem : paths.entrySet()) { + APIMethod method = new APIMethod(); + String key = pathItem.getKey(); + PathItem item = pathItem.getValue(); + Map operationsMap = item.readOperationsMap(); + for (Map.Entry operationEntry : operationsMap.entrySet()) { + PathItem.HttpMethod httpMethod = operationEntry.getKey(); + Operation operation = operationEntry.getValue(); + List tags = operation.getTags(); + List globalTags = openAPI.getTags(); + TagMap apiManagerTags = new TagMap(); + for (String tag : tags) { + Tag globalTag = findTag(tag, globalTags); + if (globalTag == null) { + continue; + } + String[] value = new String[1]; + value[0] = globalTag.getDescription(); + apiManagerTags.put(globalTag.getName(), value); + } + String operationId = operation.getOperationId(); + if (operationId == null) { + operationId = httpMethod.name() + " " + key; + } + method.setName(operationId); + method.setTags(apiManagerTags); + method.setDescriptionType(ORIGINAL); + methods.add(method); + } + } + return methods; + } + + public Map addInboundPerMethodOverride(OpenAPI openAPI, API api, List securityProfiles) { + Map inboundProfiles = new LinkedHashMap<>(); + inboundProfiles.put(DEFAULT, InboundProfile.getDefaultInboundProfile()); + Paths paths = openAPI.getPaths(); + for (Map.Entry pathItem : paths.entrySet()) { + InboundProfile inboundProfile = new InboundProfile(); + inboundProfile.setMonitorAPI(true); + inboundProfile.setQueryStringPassThrough(false); + String key = pathItem.getKey(); + PathItem item = pathItem.getValue(); + Map operationsMap = item.readOperationsMap(); + for (Map.Entry operationEntry : operationsMap.entrySet()) { + PathItem.HttpMethod httpMethod = operationEntry.getKey(); + Operation operation = operationEntry.getValue(); + String operationId = operation.getOperationId(); + if (operationId == null) { + operationId = httpMethod.name() + " " + key; + } + List securityRequirements = operation.getSecurity(); + if (securityRequirements == null) { + SecurityProfile passThroughProfile = createPassThroughSecurityProfile(); + inboundProfile.setSecurityProfile(passThroughProfile.getName()); + inboundProfiles.put(operationId, inboundProfile); + securityProfiles.add(passThroughProfile); + } else { + + for (SecurityRequirement securityRequirement : securityRequirements) { + Set keys = securityRequirement.keySet(); + for (String securityKey : keys) { + SecurityScheme securityScheme = openAPI.getComponents().getSecuritySchemes().get(securityKey); + SecurityScheme.Type type = securityScheme.getType(); + + if (type == SecurityScheme.Type.OAUTH2) { + List scopes = securityRequirement.get(securityKey); + SecurityProfile oauth2SecurityProfile = createOauthSecurityProfile(operationId, scopes); + inboundProfile.setSecurityProfile(oauth2SecurityProfile.getName()); + inboundProfiles.put(operationId, inboundProfile); + securityProfiles.add(oauth2SecurityProfile); + } else if (type == SecurityScheme.Type.APIKEY) { + LOG.warn("API key is not handled"); + } else if (type == SecurityScheme.Type.MUTUALTLS) { + LOG.warn("Mutual auth is not handled"); + } + } + } + } + } + } + api.setSecurityProfiles(securityProfiles); + return inboundProfiles; + } + + public SecurityProfile createPassThroughSecurityProfile() { + SecurityProfile profile = new SecurityProfile(); + profile.setName(PASS_THROUGH); + profile.setIsDefault(false); + SecurityDevice securityDevice = new SecurityDevice(); + securityDevice.setName(PASS_THROUGH); + securityDevice.setType(DeviceType.passThrough); + securityDevice.setOrder(0); + Map properties = new HashMap<>(); + properties.put("subjectIdFieldName", PASS_THROUGH); + properties.put(REMOVE_CREDENTIALS_ON_SUCCESS, "true"); + securityDevice.setProperties(properties); + List securityDevices = new ArrayList<>(); + securityDevices.add(securityDevice); + profile.setDevices(securityDevices); + return profile; + } + + public SecurityProfile createOauthSecurityProfile(String operationId, List scopes) { + SecurityProfile profile = new SecurityProfile(); + profile.setName("Oauth2"); + profile.setIsDefault(false); + SecurityDevice securityDevice = new SecurityDevice(); + securityDevice.setName("Oauth2 " + operationId); + securityDevice.setType(DeviceType.oauth); + securityDevice.setOrder(0); + Map properties = new HashMap<>(); + properties.put(TOKEN_STORE, "OAuth Access Token Store"); + String scope = String.join(" ", scopes); + setupOauthProperties(properties, scope); + securityDevice.setProperties(properties); + List securityDevices = new ArrayList<>(); + securityDevices.add(securityDevice); + profile.setDevices(securityDevices); + return profile; + } + + public Tag findTag(String tagName, List tags) { + if (tags == null || tags.isEmpty()) return null; + for (Tag tag : tags) { + if (tag.getName().equals(tagName)) { + return tag; } + } + return null; + } + + public TagMap parseTags(List tags) { + TagMap apiManagerTags = new TagMap(); + for (Tag tag : tags) { + String[] value = new String[1]; + value[0] = tag.getDescription(); + apiManagerTags.put(tag.getName(), value); + } + return apiManagerTags; + } + + public void addTags(API api, OpenAPI openAPI) { + List tags = openAPI.getTags(); + if (tags != null) { + TagMap apiManagerTags = parseTags(tags); api.setTags(apiManagerTags); } } @@ -319,64 +476,69 @@ public DeviceType matchDeviceType(String frontendAuthType) { return deviceType; } - private Map addInboundSecurityToAPI(String frontendAuthType) throws AppException { + private List addInboundSecurityToAPI(String frontendAuthType) throws AppException { + List securityProfiles = new ArrayList<>(); DeviceType deviceType = matchDeviceType(frontendAuthType); LOG.info("Frontend Authentication type : {}", frontendAuthType); if (deviceType == null) { throw new AppException("frontendAuthType : " + frontendAuthType + " is invalid", ErrorCode.INVALID_PARAMETER); } - APISecurity apiSecurity = new APISecurity(); - apiSecurity.setType(deviceType.toString()); - apiSecurity.setName(deviceType.getName()); - Map properties = new HashMap<>(); + SecurityProfile securityProfile = new SecurityProfile(); + securityProfile.setIsDefault(true); + securityProfile.setName(DEFAULT); + SecurityDevice securityDevice = new SecurityDevice(); + Map properties = new HashMap<>(); if (deviceType.equals(DeviceType.apiKey)) { properties.put("apiKeyFieldName", "KeyId"); properties.put("takeFrom", "HEADER"); - properties.put("removeCredentialsOnSuccess", "true"); + properties.put(REMOVE_CREDENTIALS_ON_SUCCESS, "true"); } else if (deviceType.equals(DeviceType.oauth)) { - properties.put("tokenStore", "OAuth Access Token Store"); - properties.put("scopes", "resource.WRITE, resource.READ"); - setupOauthProperties(properties); + properties.put(TOKEN_STORE, "OAuth Access Token Store"); + setupOauthProperties(properties, "resource.WRITE, resource.READ"); } else if (deviceType.equals(DeviceType.oauthExternal)) { - properties.put("tokenStore", "Tokeninfo policy 1"); - properties.put("useClientRegistry", true); + properties.put(TOKEN_STORE, "Tokeninfo policy 1"); + properties.put("useClientRegistry", "true"); properties.put("subjectSelector", "${oauth.token.client_id}"); - setupOauthProperties(properties); + setupOauthProperties(properties, "resource.WRITE, resource.READ"); } else if (deviceType.equals(DeviceType.authPolicy)) { properties.put("authenticationPolicy", "Custom authentication policy"); - properties.put("useClientRegistry", true); + properties.put("useClientRegistry", "true"); properties.put("subjectSelector", "authentication.subject.id"); - properties.put("descriptionType", "original"); + properties.put("descriptionType", ORIGINAL); properties.put("descriptionUrl", ""); properties.put("descriptionMarkdown", ""); properties.put("description", ""); } - apiSecurity.setProperties(properties); - Map securityProfile = new LinkedHashMap<>(); - securityProfile.put("name", DEFAULT); - securityProfile.put("isDefault", true); - List apiSecurities = new ArrayList<>(); - apiSecurities.add(apiSecurity); - securityProfile.put("devices", apiSecurities); - return securityProfile; + securityDevice.setProperties(properties); + securityDevice.setOrder(1); + securityDevice.setName(DEFAULT); + securityDevice.setType(deviceType); + List securityDevices = new ArrayList<>(); + securityDevices.add(securityDevice); + securityProfile.setDevices(securityDevices); + securityProfiles.add(securityProfile); + return securityProfiles; } - private void setupOauthProperties(Map properties) { + private void setupOauthProperties(Map properties, String scopes) { properties.put("accessTokenLocation", "HEADER"); properties.put("authorizationHeaderPrefix", "Bearer"); properties.put("accessTokenLocationQueryString", ""); - properties.put("scopesMustMatch", "Any"); - properties.put("scopes", "resource.WRITE, resource.READ"); - properties.put("removeCredentialsOnSuccess", true); - properties.put("implicitGrantEnabled", true); + properties.put("scopesMustMatch", "All"); + properties.put("scopes", scopes); + properties.put(REMOVE_CREDENTIALS_ON_SUCCESS, "true"); + properties.put("implicitGrantEnabled", "true"); properties.put("implicitGrantLoginEndpointUrl", "https://localhost:8089/api/oauth/authorize"); properties.put("implicitGrantLoginTokenName", "access_token"); - properties.put("authCodeGrantTypeEnabled", true); + properties.put("authCodeGrantTypeEnabled", "true"); properties.put("authCodeGrantTypeRequestEndpointUrl", "https://localhost:8089/api/oauth/authorize"); properties.put("authCodeGrantTypeRequestClientIdName", "client_id"); properties.put("authCodeGrantTypeRequestSecretName", "client_secret"); properties.put("authCodeGrantTypeTokenEndpointUrl", "https://localhost:8089/api/oauth/token"); properties.put("authCodeGrantTypeTokenEndpointTokenName", "access_code"); + properties.put("clientCredentialsGrantTypeEnabled", "true"); + properties.put("clientCredentialsGrantTypeTokenEndpointUrl", "https://localhost:8089/api/oauth/token"); + properties.put("clientCredentialsGrantTypeTokenName", "access_token"); } public String writeAPISpecification(String url, String configPath, InputStream inputStream) throws IOException { @@ -408,7 +570,7 @@ public String downloadContent(String configPath, String url) throws IOException HttpURLConnection httpURLConnection = (HttpURLConnection) httpURL.openConnection(); int responseCode = httpURLConnection.getResponseCode(); String filePath = null; - LOG.debug("Response Code : {}", responseCode); + LOG.debug("Http Response Code : {}", responseCode); if (responseCode == HttpURLConnection.HTTP_OK) { filePath = writeAPISpecification(url, configPath, httpURLConnection.getInputStream()); } @@ -416,7 +578,8 @@ public String downloadContent(String configPath, String url) throws IOException } - public String downloadCertificatesAndContent(API api, String configPath, String url) throws IOException, CertificateEncodingException, NoSuchAlgorithmException, KeyManagementException { + public String downloadCertificatesAndContent(API api, String configPath, String url) throws + IOException, CertificateEncodingException, NoSuchAlgorithmException, KeyManagementException { TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() { public java.security.cert.X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; diff --git a/modules/spectoconfig/src/main/java/com/axway/apim/config/GenerateTemplateCLIOptions.java b/modules/spectoconfig/src/main/java/com/axway/apim/config/GenerateTemplateCLIOptions.java index bf6500b52..15709f818 100644 --- a/modules/spectoconfig/src/main/java/com/axway/apim/config/GenerateTemplateCLIOptions.java +++ b/modules/spectoconfig/src/main/java/com/axway/apim/config/GenerateTemplateCLIOptions.java @@ -55,6 +55,15 @@ public void addOptions() { addOption(option); + option = new Option("includeMethods", false, "Include API Methods"); + option.setRequired(false); + addOption(option); + + option = new Option("inboundPerMethodOverride", false, "Include Inbound per Methods overrides"); + option.setRequired(false); + addOption(option); + + } @Override @@ -90,6 +99,8 @@ public Parameters getParams() { frontendAuthType = "passThrough"; } params.setFrontendAuthType(frontendAuthType); + params.setInboundPerMethodOverride(hasOption("inboundPerMethodOverride")); + params.setIncludeMethods(hasOption("includeMethods")); return params; } } diff --git a/modules/spectoconfig/src/main/java/com/axway/apim/config/model/APISecurity.java b/modules/spectoconfig/src/main/java/com/axway/apim/config/model/APISecurity.java deleted file mode 100644 index 7ac0e76d9..000000000 --- a/modules/spectoconfig/src/main/java/com/axway/apim/config/model/APISecurity.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.axway.apim.config.model; - -import com.fasterxml.jackson.annotation.JsonPropertyOrder; - -import java.util.Map; -@JsonPropertyOrder({"type","name","order","properties"}) -public class APISecurity { - - private String name; - private String type; - private int order = 1; - - private Map properties; - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getType() { - return type; - } - - public void setType(String type) { - this.type = type; - } - - public int getOrder() { - return order; - } - - public void setOrder(int order) { - this.order = order; - } - - public Map getProperties() { - return properties; - } - - public void setProperties(Map properties) { - this.properties = properties; - } -} \ No newline at end of file diff --git a/modules/spectoconfig/src/main/java/com/axway/apim/config/model/GenerateTemplateParameters.java b/modules/spectoconfig/src/main/java/com/axway/apim/config/model/GenerateTemplateParameters.java index 03f75c37e..ea38e4ce3 100644 --- a/modules/spectoconfig/src/main/java/com/axway/apim/config/model/GenerateTemplateParameters.java +++ b/modules/spectoconfig/src/main/java/com/axway/apim/config/model/GenerateTemplateParameters.java @@ -9,6 +9,8 @@ public class GenerateTemplateParameters extends StandardExportParams implements private String config; private String backendAuthType; private String frontendAuthType; + private boolean includeMethods; + private boolean inboundPerMethodOverride; public String getApiDefinition() { return apiDefinition; @@ -41,4 +43,20 @@ public String getFrontendAuthType() { public void setFrontendAuthType(String frontendAuthType) { this.frontendAuthType = frontendAuthType; } + + public boolean isIncludeMethods() { + return includeMethods; + } + + public void setIncludeMethods(boolean includeMethods) { + this.includeMethods = includeMethods; + } + + public boolean isInboundPerMethodOverride() { + return inboundPerMethodOverride; + } + + public void setInboundPerMethodOverride(boolean inboundPerMethodOverride) { + this.inboundPerMethodOverride = inboundPerMethodOverride; + } } diff --git a/modules/spectoconfig/src/test/java/com/axway/apim/config/GenerateTemplateTest.java b/modules/spectoconfig/src/test/java/com/axway/apim/config/GenerateTemplateTest.java index 785738d17..2804e5f18 100644 --- a/modules/spectoconfig/src/test/java/com/axway/apim/config/GenerateTemplateTest.java +++ b/modules/spectoconfig/src/test/java/com/axway/apim/config/GenerateTemplateTest.java @@ -2,10 +2,22 @@ import com.axway.apim.adapter.jackson.CustomYamlFactory; import com.axway.apim.api.API; +import com.axway.apim.api.model.APIMethod; +import com.axway.apim.api.model.InboundProfile; +import com.axway.apim.api.model.SecurityProfile; +import com.axway.apim.lib.error.AppException; +import com.axway.apim.lib.error.ErrorCode; +import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ser.FilterProvider; +import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter; +import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider; import com.jayway.jsonpath.DocumentContext; import com.jayway.jsonpath.JsonPath; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.parser.OpenAPIV3Parser; +import io.swagger.v3.parser.core.models.SwaggerParseResult; import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.server.*; import org.eclipse.jetty.servlet.ServletHandler; @@ -25,6 +37,10 @@ import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; import java.security.cert.CertificateEncodingException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import java.util.concurrent.Executors; public class GenerateTemplateTest { @@ -36,15 +52,16 @@ public class GenerateTemplateTest { private void init() throws URISyntaxException { URI uri = this.getClass().getProtectionDomain().getCodeSource().getLocation().toURI(); resourcePath = Paths.get(uri).toString(); - apimCliHome = resourcePath + File.separator + "apimcli"; + apimCliHome = resourcePath + File.separator + "apimcli"; } Server server = new Server(); + @BeforeClass public void start() throws InterruptedException { Executors.newSingleThreadExecutor().execute(() -> { - try(ServerConnector connector = new ServerConnector(server)) { + try (ServerConnector connector = new ServerConnector(server)) { connector.setPort(7070); HttpConfiguration https_config = getHttpConfiguration(); SslContextFactory.Server sslContextFactory = new SslContextFactory.Server(); @@ -52,8 +69,8 @@ public void start() throws InterruptedException { sslContextFactory.setKeyStorePassword("changeit"); sslContextFactory.setKeyManagerPassword("changeit"); try (ServerConnector https = new ServerConnector(server, - new SslConnectionFactory(sslContextFactory, HttpVersion.HTTP_1_1.asString()), - new HttpConnectionFactory(https_config))) { + new SslConnectionFactory(sslContextFactory, HttpVersion.HTTP_1_1.asString()), + new HttpConnectionFactory(https_config))) { https.setPort(8443); https.setHost("0.0.0.0"); @@ -91,9 +108,10 @@ private static HttpConfiguration getHttpConfiguration() { public void stop() throws Exception { server.stop(); } + @Test public void testDownloadCertificates() throws IOException, CertificateEncodingException, NoSuchAlgorithmException, KeyManagementException { - HttpsURLConnection.setDefaultHostnameVerifier ((hostname, session) -> true); + HttpsURLConnection.setDefaultHostnameVerifier((hostname, session) -> true); GenerateTemplate generateTemplate = new GenerateTemplate(); API api = new API(); String filePath = generateTemplate.downloadCertificatesAndContent(api, "config.json", "https://localhost:8443/openapi.json"); @@ -104,7 +122,7 @@ public void testDownloadCertificates() throws IOException, CertificateEncodingEx @Test public void testDownloadContent() throws IOException { GenerateTemplate generateTemplate = new GenerateTemplate(); - String filePath = generateTemplate.downloadContent( "config.json", "http://localhost:7070/openapi.json"); + String filePath = generateTemplate.downloadContent("config.json", "http://localhost:7070/openapi.json"); Assert.assertNotNull(filePath); } @@ -179,5 +197,49 @@ public void testWithFrontendAuthAlternateName() throws IOException { Assert.assertEquals("passThrough", documentContext.read("$.securityProfiles[0].devices[0].type")); } + @Test + public void generateApiMethods() throws IOException { + + OpenAPI openAPI = new OpenAPIV3Parser().read("src/test/resources/methods.yaml"); + GenerateTemplate generateTemplate = new GenerateTemplate(); + List apiMethods = generateTemplate.addMethods(openAPI); + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + System.out.println(objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(apiMethods)); + // System.out.println(openAPI); + + } + + @Test + public void includeInboundPerMethodOverride() throws IOException { + + OpenAPI openAPI = new OpenAPIV3Parser().read("src/test/resources/methods.yaml"); + GenerateTemplate generateTemplate = new GenerateTemplate(); + List securityProfiles = new ArrayList<>(); + API api = new API(); + generateTemplate.addInboundPerMethodOverride(openAPI, api, securityProfiles); + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + FilterProvider filters = new SimpleFilterProvider() + + .addFilter("ProfileFilter", + SimpleBeanPropertyFilter.serializeAllExcept("apiMethodId")) + .setDefaultFilter(SimpleBeanPropertyFilter.serializeAllExcept()); + objectMapper.setFilterProvider(filters); + System.out.println(objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(api.getInboundProfiles())); + } + + + @Test + public void testInboundOverride() throws IOException { + String[] args = {"template", "generate", "-c", "api-config.yaml", "-a", "src/test/resources/methods.yaml", "-frontendAuthType", "apiKey", "-inboundPerMethodOverride", "-o", "yaml"}; + GenerateTemplate.generate(args); +// DocumentContext documentContext = JsonPath.parse(Files.newInputStream(Paths.get("api-config.json"))); +// Assert.assertEquals("Swagger Petstore - OpenAPI 3.0", documentContext.read("$.name")); +// Assert.assertEquals("published", documentContext.read("$.state")); +// Assert.assertEquals("/api/v3", documentContext.read("$.path")); +// Assert.assertEquals("passThrough", documentContext.read("$.securityProfiles[0].devices[0].type")); + } + } diff --git a/modules/spectoconfig/src/test/resources/methods.yaml b/modules/spectoconfig/src/test/resources/methods.yaml new file mode 100644 index 000000000..ff383abe9 --- /dev/null +++ b/modules/spectoconfig/src/test/resources/methods.yaml @@ -0,0 +1,106 @@ +openapi: 3.0.3 +info: + title: Sample API + description: This is a sample API demonstrating OAuth 2.0 client credentials flow with scopes defined at the service and path levels. + version: 1.0.0 + +servers: + - url: https://api.example.com/v1 + +components: + securitySchemes: + oauth2ClientCredentials: + type: oauth2 + flows: + clientCredentials: + tokenUrl: https://auth.example.com/oauth2/token + scopes: + read: Grants read access + write: Grants write access + admin: Grants admin access + + schemas: + SampleResponse: + type: object + properties: + message: + type: string + +security: + - oauth2ClientCredentials: + - read + - write +tags: + - name: public + description: public desc + externalDocs: + url: http://docs.my-api.com/pet-operations.htm + - name: data + description: data desc + externalDocs: + url: http://docs.my-api.com/store-orders.htm + - name: admin + description: admin desc + externalDocs: + url: http://docs.my-api.com/store-orders.htm + + +paths: + /public: + get: + summary: Public endpoint + tags: + - public + description: This endpoint is public and does not require authentication. + responses: + '200': + description: Success + content: + application/json: + schema: + $ref: '#/components/schemas/SampleResponse' + + /secure-data: + get: + summary: Secure data endpoint + tags: + - data + description: This endpoint requires OAuth with 'read' scope. + security: + - oauth2ClientCredentials: + - read + responses: + '200': + description: Secured data retrieved successfully + content: + application/json: + schema: + $ref: '#/components/schemas/SampleResponse' + + /admin: + post: + summary: Admin-only endpoint + tags: + - admin + description: This endpoint requires OAuth with 'admin' scope. + security: + - oauth2ClientCredentials: + - admin + requestBody: + description: Request payload for admin operation + required: true + content: + application/json: + schema: + type: object + properties: + action: + type: string + example: "update" + responses: + '201': + description: Admin operation successful + content: + application/json: + schema: + $ref: '#/components/schemas/SampleResponse' From 53f67379ed09bc106038a39724972ee00b2b0531 Mon Sep 17 00:00:00 2001 From: rathnapandi Date: Thu, 21 Nov 2024 21:53:13 -0700 Subject: [PATCH 2/6] Enhance template creation to include api methods and inbound per method override --- .../src/main/java/com/axway/apim/config/APIConfig.java | 3 --- .../src/main/java/com/axway/apim/config/GenerateTemplate.java | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/modules/spectoconfig/src/main/java/com/axway/apim/config/APIConfig.java b/modules/spectoconfig/src/main/java/com/axway/apim/config/APIConfig.java index 3e6313b69..7bfd571d3 100644 --- a/modules/spectoconfig/src/main/java/com/axway/apim/config/APIConfig.java +++ b/modules/spectoconfig/src/main/java/com/axway/apim/config/APIConfig.java @@ -47,9 +47,6 @@ public Map getOutboundProfiles() { public List getSecurityProfiles() { - if (api.getSecurityProfiles().size() == 1) { - return Collections.emptyList(); - } return api.getSecurityProfiles(); } diff --git a/modules/spectoconfig/src/main/java/com/axway/apim/config/GenerateTemplate.java b/modules/spectoconfig/src/main/java/com/axway/apim/config/GenerateTemplate.java index 15247a1a8..c2bf21082 100644 --- a/modules/spectoconfig/src/main/java/com/axway/apim/config/GenerateTemplate.java +++ b/modules/spectoconfig/src/main/java/com/axway/apim/config/GenerateTemplate.java @@ -214,6 +214,7 @@ public APIConfig generateTemplate(GenerateTemplateParameters parameters) throws String frontendAuthType = parameters.getFrontendAuthType(); // If frontendAuthType is null, use authentication from openapi spec. If none found, set it as pass through List securityProfiles = addInboundSecurityToAPI(frontendAuthType); + api.setSecurityProfiles(securityProfiles); String backendAuthType = parameters.getBackendAuthType(); addOutboundSecurityToAPI(api, backendAuthType); String apiSpecLocation; @@ -228,7 +229,6 @@ public APIConfig generateTemplate(GenerateTemplateParameters parameters) throws List methods = addMethods(openAPI); api.setApiMethods(methods); } - if (parameters.isInboundPerMethodOverride()) { Map inboundProfileMap = addInboundPerMethodOverride(openAPI, api, securityProfiles); api.setInboundProfiles(inboundProfileMap); From 96007df3392f1af1e054cdd72d55985bfc391b0a Mon Sep 17 00:00:00 2001 From: rathnapandi Date: Thu, 21 Nov 2024 21:55:18 -0700 Subject: [PATCH 3/6] Enhance template creation to include api methods and inbound per method override --- .../src/main/java/com/axway/apim/config/GenerateTemplate.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/spectoconfig/src/main/java/com/axway/apim/config/GenerateTemplate.java b/modules/spectoconfig/src/main/java/com/axway/apim/config/GenerateTemplate.java index c2bf21082..d80716b7f 100644 --- a/modules/spectoconfig/src/main/java/com/axway/apim/config/GenerateTemplate.java +++ b/modules/spectoconfig/src/main/java/com/axway/apim/config/GenerateTemplate.java @@ -345,10 +345,10 @@ public SecurityProfile createPassThroughSecurityProfile() { public SecurityProfile createOauthSecurityProfile(String operationId, List scopes) { SecurityProfile profile = new SecurityProfile(); - profile.setName("Oauth2"); + profile.setName("Oauth2 " + operationId); profile.setIsDefault(false); SecurityDevice securityDevice = new SecurityDevice(); - securityDevice.setName("Oauth2 " + operationId); + securityDevice.setName("Oauth2"); securityDevice.setType(DeviceType.oauth); securityDevice.setOrder(0); Map properties = new HashMap<>(); From 201e2ded75f323e6abfda722779e1fed395a0136 Mon Sep 17 00:00:00 2001 From: rathnapandi Date: Fri, 22 Nov 2024 16:47:39 -0700 Subject: [PATCH 4/6] Fix junit and write open api format based on file type. --- .../api/specification/APISpecification.java | 4 + .../apim/api/export/impl/ExportHelper.java | 15 +- .../axway/apim/config/GenerateTemplate.java | 162 ++++++++++++++---- .../apim/config/GenerateTemplateTest.java | 31 +--- 4 files changed, 148 insertions(+), 64 deletions(-) diff --git a/modules/apim-adapter/src/main/java/com/axway/apim/api/specification/APISpecification.java b/modules/apim-adapter/src/main/java/com/axway/apim/api/specification/APISpecification.java index 7fd5ff25d..6b707c1c2 100644 --- a/modules/apim-adapter/src/main/java/com/axway/apim/api/specification/APISpecification.java +++ b/modules/apim-adapter/src/main/java/com/axway/apim/api/specification/APISpecification.java @@ -182,4 +182,8 @@ public APISpecification setFilterConfig(APISpecificationFilter filterConfig) { this.filterConfig = filterConfig; return this; } + + public ObjectMapper getMapper() { + return mapper; + } } diff --git a/modules/apis/src/main/java/com/axway/apim/api/export/impl/ExportHelper.java b/modules/apis/src/main/java/com/axway/apim/api/export/impl/ExportHelper.java index c9b661f64..85f80eea3 100644 --- a/modules/apis/src/main/java/com/axway/apim/api/export/impl/ExportHelper.java +++ b/modules/apis/src/main/java/com/axway/apim/api/export/impl/ExportHelper.java @@ -62,7 +62,7 @@ public void saveAPILocally(ObjectMapper mapper, ExportAPI exportAPI, String conf throw new AppException("Backend API Definition is not available for the API : " + exportAPI.getName() + ", hence use the option -useFEAPIDefinition to export API", ErrorCode.BACKEND_API_DEF_NA); return; } - writeSpec(mapper, apiDef, exportAPI, localFolder); + writeSpec(apiDef, exportAPI, localFolder); Image image = exportAPI.getAPIImage(); if (image != null && (!EnvironmentProperties.PRINT_CONFIG_CONSOLE)) { writeBytesToFile(image.getImageContent(), localFolder + File.separator + image.getBaseFilename()); @@ -143,22 +143,23 @@ private void storePrivateCerts(File localFolder, List aut } } - public void writeSpec(ObjectMapper mapper, APISpecification apiDef, ExportAPI exportAPI, File localFolder) throws AppException { + public void writeSpec(APISpecification apiDef, ExportAPI exportAPI, File localFolder) throws AppException { String targetFile = null; try { if (!(apiDef instanceof WSDLSpecification && EnvironmentProperties.RETAIN_BACKEND_URL) && (!EnvironmentProperties.PRINT_CONFIG_CONSOLE)) { String fileName = Utils.replaceSpecialChars(exportAPI.getName()); String fileExtension = apiDef.getAPIDefinitionType().getFileExtension(); - if(apiDef instanceof Swagger2xSpecification || apiDef instanceof OAS3xSpecification){ + if (apiDef instanceof Swagger2xSpecification || apiDef instanceof OAS3xSpecification) { + ObjectMapper mapper = apiDef.getMapper(); if (mapper.getFactory() instanceof YAMLFactory) { - fileExtension = APISpecification.APISpecType.SWAGGER_API_20_YAML.getFileExtension(); - }else { - fileExtension = APISpecification.APISpecType.SWAGGER_API_20.getFileExtension(); + fileExtension = APISpecification.APISpecType.SWAGGER_API_20_YAML.getFileExtension(); + } else { + fileExtension = APISpecification.APISpecType.SWAGGER_API_20.getFileExtension(); } targetFile = localFolder.getCanonicalPath() + "/" + fileName + fileExtension; Object spec = mapper.readValue(apiDef.getApiSpecificationContent(), Object.class); mapper.writerWithDefaultPrettyPrinter().writeValue(new File(targetFile), spec); - }else { + } else { targetFile = localFolder.getCanonicalPath() + "/" + fileName + fileExtension; writeBytesToFile(apiDef.getApiSpecificationContent(), targetFile); } diff --git a/modules/spectoconfig/src/main/java/com/axway/apim/config/GenerateTemplate.java b/modules/spectoconfig/src/main/java/com/axway/apim/config/GenerateTemplate.java index d80716b7f..20ebae3ae 100644 --- a/modules/spectoconfig/src/main/java/com/axway/apim/config/GenerateTemplate.java +++ b/modules/spectoconfig/src/main/java/com/axway/apim/config/GenerateTemplate.java @@ -50,6 +50,10 @@ import java.security.cert.X509Certificate; import java.util.*; +import static io.swagger.v3.oas.models.security.SecurityScheme.In.QUERY; +import static io.swagger.v3.oas.models.security.SecurityScheme.In.HEADER; + + public class GenerateTemplate implements APIMCLIServiceProvider { @@ -59,6 +63,11 @@ public class GenerateTemplate implements APIMCLIServiceProvider { public static final String PASS_THROUGH = "Pass Through"; public static final String REMOVE_CREDENTIALS_ON_SUCCESS = "removeCredentialsOnSuccess"; public static final String TOKEN_STORE = "tokenStore"; + public static final String TAKE_FROM = "takeFrom"; + public static final String OAUTH_TOKEN_CLIENT_ID = "${oauth.token.client_id}"; + public static final String USE_CLIENT_REGISTRY = "useClientRegistry"; + public static final String SUBJECT_SELECTOR = "subjectSelector"; + public static final String HEADER_STR = "HEADER"; @Override public String getName() { @@ -292,42 +301,71 @@ public Map addInboundPerMethodOverride(OpenAPI openAPI, operationId = httpMethod.name() + " " + key; } List securityRequirements = operation.getSecurity(); - if (securityRequirements == null) { - SecurityProfile passThroughProfile = createPassThroughSecurityProfile(); - inboundProfile.setSecurityProfile(passThroughProfile.getName()); - inboundProfiles.put(operationId, inboundProfile); - securityProfiles.add(passThroughProfile); - } else { - - for (SecurityRequirement securityRequirement : securityRequirements) { - Set keys = securityRequirement.keySet(); - for (String securityKey : keys) { - SecurityScheme securityScheme = openAPI.getComponents().getSecuritySchemes().get(securityKey); - SecurityScheme.Type type = securityScheme.getType(); - - if (type == SecurityScheme.Type.OAUTH2) { - List scopes = securityRequirement.get(securityKey); - SecurityProfile oauth2SecurityProfile = createOauthSecurityProfile(operationId, scopes); - inboundProfile.setSecurityProfile(oauth2SecurityProfile.getName()); - inboundProfiles.put(operationId, inboundProfile); - securityProfiles.add(oauth2SecurityProfile); - } else if (type == SecurityScheme.Type.APIKEY) { - LOG.warn("API key is not handled"); - } else if (type == SecurityScheme.Type.MUTUALTLS) { - LOG.warn("Mutual auth is not handled"); - } - } - } - } + handleSecurity(openAPI, inboundProfiles, securityRequirements, securityProfiles, inboundProfile, operationId); } } api.setSecurityProfiles(securityProfiles); return inboundProfiles; } - public SecurityProfile createPassThroughSecurityProfile() { + + public void handleSecurity(OpenAPI openAPI, Map inboundProfiles, List securityRequirements, List securityProfiles, InboundProfile inboundProfile, String operationId) { + if (securityRequirements == null || securityRequirements.isEmpty()) { + SecurityProfile passThroughProfile = createPassThroughSecurityProfile(operationId); + inboundProfile.setSecurityProfile(passThroughProfile.getName()); + inboundProfiles.put(operationId, inboundProfile); + securityProfiles.add(passThroughProfile); + } else { + for (SecurityRequirement securityRequirement : securityRequirements) { + Set keys = securityRequirement.keySet(); + for (String securityKey : keys) { + SecurityScheme securityScheme = openAPI.getComponents().getSecuritySchemes().get(securityKey); + mapAPIMSecurity(securityRequirement, securityScheme, inboundProfiles, inboundProfile, securityProfiles, operationId, securityKey); + } + } + } + } + + public void mapAPIMSecurity(SecurityRequirement securityRequirement, SecurityScheme securityScheme, Map inboundProfiles, InboundProfile inboundProfile, List securityProfiles, String operationId, String securityKey) { + SecurityScheme.Type type = securityScheme.getType(); + if (type == SecurityScheme.Type.OAUTH2) { + LOG.info("mapping oauth2 profile"); + List scopes = securityRequirement.get(securityKey); + SecurityProfile oauth2SecurityProfile = createOauthSecurityProfile(operationId, scopes); + inboundProfile.setSecurityProfile(oauth2SecurityProfile.getName()); + inboundProfiles.put(operationId, inboundProfile); + securityProfiles.add(oauth2SecurityProfile); + } else if (type == SecurityScheme.Type.APIKEY) { + LOG.info("mapping API key profile"); + List scopes = securityRequirement.get(securityKey); + SecurityScheme.In in = securityScheme.getIn(); + if (in == SecurityScheme.In.COOKIE) { + LOG.warn("API key in cookie not supported"); + return; + } + String apikeyLocation = in.name(); + String fieldName = securityScheme.getName(); + SecurityProfile apiKeySecurityProfile = createApiKeySecurityProfile(operationId, apikeyLocation, fieldName, scopes); + inboundProfile.setSecurityProfile(apiKeySecurityProfile.getName()); + inboundProfiles.put(operationId, inboundProfile); + securityProfiles.add(apiKeySecurityProfile); + } else if (type == SecurityScheme.Type.MUTUALTLS) { + LOG.warn("Mutual auth is not handled"); + } else if (type == SecurityScheme.Type.OPENIDCONNECT || type == SecurityScheme.Type.HTTP && securityScheme.getScheme().equalsIgnoreCase("bearer")) { + LOG.info("External auth / openid connect is not handled"); + List scopes = securityRequirement.get(securityKey); + SecurityProfile oauth2ExternalSecurityProfile = createOauthExternalSecurityProfile(operationId, scopes); + inboundProfile.setSecurityProfile(oauth2ExternalSecurityProfile.getName()); + inboundProfiles.put(operationId, inboundProfile); + securityProfiles.add(oauth2ExternalSecurityProfile); + } else if (type == SecurityScheme.Type.HTTP && securityScheme.getScheme().equalsIgnoreCase("basic")) { + LOG.warn("Basic Auth is not handled"); + } + } + + public SecurityProfile createPassThroughSecurityProfile(String operationId) { SecurityProfile profile = new SecurityProfile(); - profile.setName(PASS_THROUGH); + profile.setName(PASS_THROUGH + " " + operationId); profile.setIsDefault(false); SecurityDevice securityDevice = new SecurityDevice(); securityDevice.setName(PASS_THROUGH); @@ -343,6 +381,60 @@ public SecurityProfile createPassThroughSecurityProfile() { return profile; } + public SecurityProfile createApiKeySecurityProfile(String operationId, String apikeyLocation, String fieldName, List scopes) { + SecurityProfile profile = new SecurityProfile(); + profile.setName("apikey " + operationId); + profile.setIsDefault(false); + SecurityDevice securityDevice = new SecurityDevice(); + securityDevice.setName("API Key"); + securityDevice.setType(DeviceType.apiKey); + securityDevice.setOrder(0); + Map properties = new HashMap<>(); + properties.put(REMOVE_CREDENTIALS_ON_SUCCESS, "true"); + if (apikeyLocation.equals(HEADER.name())) { + properties.put(TAKE_FROM, HEADER_STR); + } else if (apikeyLocation.equals(QUERY.name())) { + properties.put(TAKE_FROM, "QUERY"); + } + properties.put("apiKeyFieldName", fieldName); + if (scopes != null && !scopes.isEmpty()) { + String scope = String.join(" ", scopes); + properties.put("scopes", scope); + properties.put("scopesMustMatch", "All"); + } + securityDevice.setProperties(properties); + List securityDevices = new ArrayList<>(); + securityDevices.add(securityDevice); + profile.setDevices(securityDevices); + return profile; + } + + + public SecurityProfile createOauthExternalSecurityProfile(String operationId, List scopes) { + SecurityProfile profile = new SecurityProfile(); + profile.setName("External Oauth2 " + operationId); + profile.setIsDefault(false); + SecurityDevice securityDevice = new SecurityDevice(); + securityDevice.setName("OAuth (External)"); + securityDevice.setType(DeviceType.oauthExternal); + securityDevice.setOrder(0); + Map properties = new HashMap<>(); + properties.put(TOKEN_STORE, "Tokeninfo policy 1"); + properties.put(USE_CLIENT_REGISTRY, "true"); + properties.put(SUBJECT_SELECTOR, OAUTH_TOKEN_CLIENT_ID); + properties.put("oauth.token.client_id", OAUTH_TOKEN_CLIENT_ID); + properties.put("oauth.token.scopes", "${oauth.token.scopes}"); + properties.put("oauth.token.valid", "${oauth.token.valid}"); + String scope = String.join(" ", scopes); + setupOauthProperties(properties, scope); + securityDevice.setProperties(properties); + List securityDevices = new ArrayList<>(); + securityDevices.add(securityDevice); + profile.setDevices(securityDevices); + return profile; + } + + public SecurityProfile createOauthSecurityProfile(String operationId, List scopes) { SecurityProfile profile = new SecurityProfile(); profile.setName("Oauth2 " + operationId); @@ -490,20 +582,20 @@ private List addInboundSecurityToAPI(String frontendAuthType) t Map properties = new HashMap<>(); if (deviceType.equals(DeviceType.apiKey)) { properties.put("apiKeyFieldName", "KeyId"); - properties.put("takeFrom", "HEADER"); + properties.put(TAKE_FROM, HEADER_STR); properties.put(REMOVE_CREDENTIALS_ON_SUCCESS, "true"); } else if (deviceType.equals(DeviceType.oauth)) { properties.put(TOKEN_STORE, "OAuth Access Token Store"); setupOauthProperties(properties, "resource.WRITE, resource.READ"); } else if (deviceType.equals(DeviceType.oauthExternal)) { properties.put(TOKEN_STORE, "Tokeninfo policy 1"); - properties.put("useClientRegistry", "true"); - properties.put("subjectSelector", "${oauth.token.client_id}"); + properties.put(USE_CLIENT_REGISTRY, "true"); + properties.put(SUBJECT_SELECTOR, OAUTH_TOKEN_CLIENT_ID); setupOauthProperties(properties, "resource.WRITE, resource.READ"); } else if (deviceType.equals(DeviceType.authPolicy)) { properties.put("authenticationPolicy", "Custom authentication policy"); - properties.put("useClientRegistry", "true"); - properties.put("subjectSelector", "authentication.subject.id"); + properties.put(USE_CLIENT_REGISTRY, "true"); + properties.put(SUBJECT_SELECTOR, "authentication.subject.id"); properties.put("descriptionType", ORIGINAL); properties.put("descriptionUrl", ""); properties.put("descriptionMarkdown", ""); @@ -521,7 +613,7 @@ private List addInboundSecurityToAPI(String frontendAuthType) t } private void setupOauthProperties(Map properties, String scopes) { - properties.put("accessTokenLocation", "HEADER"); + properties.put("accessTokenLocation", HEADER_STR); properties.put("authorizationHeaderPrefix", "Bearer"); properties.put("accessTokenLocationQueryString", ""); properties.put("scopesMustMatch", "All"); diff --git a/modules/spectoconfig/src/test/java/com/axway/apim/config/GenerateTemplateTest.java b/modules/spectoconfig/src/test/java/com/axway/apim/config/GenerateTemplateTest.java index 2804e5f18..ac28c0619 100644 --- a/modules/spectoconfig/src/test/java/com/axway/apim/config/GenerateTemplateTest.java +++ b/modules/spectoconfig/src/test/java/com/axway/apim/config/GenerateTemplateTest.java @@ -198,41 +198,28 @@ public void testWithFrontendAuthAlternateName() throws IOException { } @Test - public void generateApiMethods() throws IOException { - - OpenAPI openAPI = new OpenAPIV3Parser().read("src/test/resources/methods.yaml"); + public void generateApiMethods() { + OpenAPI openAPI = new OpenAPIV3Parser().read("methods.yaml"); GenerateTemplate generateTemplate = new GenerateTemplate(); List apiMethods = generateTemplate.addMethods(openAPI); - ObjectMapper objectMapper = new ObjectMapper(); - objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); - System.out.println(objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(apiMethods)); - // System.out.println(openAPI); - + Assert.assertEquals(3, apiMethods.size()); + Assert.assertNotNull(apiMethods.get(0).getTags().get("public")); } @Test - public void includeInboundPerMethodOverride() throws IOException { - - OpenAPI openAPI = new OpenAPIV3Parser().read("src/test/resources/methods.yaml"); + public void includeInboundPerMethodOverride() { + OpenAPI openAPI = new OpenAPIV3Parser().read("methods.yaml"); GenerateTemplate generateTemplate = new GenerateTemplate(); List securityProfiles = new ArrayList<>(); API api = new API(); - generateTemplate.addInboundPerMethodOverride(openAPI, api, securityProfiles); - ObjectMapper objectMapper = new ObjectMapper(); - objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); - FilterProvider filters = new SimpleFilterProvider() - - .addFilter("ProfileFilter", - SimpleBeanPropertyFilter.serializeAllExcept("apiMethodId")) - .setDefaultFilter(SimpleBeanPropertyFilter.serializeAllExcept()); - objectMapper.setFilterProvider(filters); - System.out.println(objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(api.getInboundProfiles())); + Map inboundProfileMap = generateTemplate.addInboundPerMethodOverride(openAPI, api, securityProfiles); + Assert.assertNotNull(inboundProfileMap); } @Test public void testInboundOverride() throws IOException { - String[] args = {"template", "generate", "-c", "api-config.yaml", "-a", "src/test/resources/methods.yaml", "-frontendAuthType", "apiKey", "-inboundPerMethodOverride", "-o", "yaml"}; + String[] args = {"template", "generate", "-c", "api-config.json", "-a", "src/test/resources/methods.yaml", "-frontendAuthType", "apiKey", "-inboundPerMethodOverride", "-o", "json"}; GenerateTemplate.generate(args); // DocumentContext documentContext = JsonPath.parse(Files.newInputStream(Paths.get("api-config.json"))); // Assert.assertEquals("Swagger Petstore - OpenAPI 3.0", documentContext.read("$.name")); From 44790e91359ad8279ad165c40e75f8c06b02b170 Mon Sep 17 00:00:00 2001 From: rathnapandi Date: Fri, 22 Nov 2024 16:59:44 -0700 Subject: [PATCH 5/6] Fix junit tests --- .../GenerateTemplateCLIOptionsTest.java | 30 +++++++++---------- .../apim/config/GenerateTemplateTest.java | 2 +- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/modules/spectoconfig/src/test/java/com/axway/apim/config/GenerateTemplateCLIOptionsTest.java b/modules/spectoconfig/src/test/java/com/axway/apim/config/GenerateTemplateCLIOptionsTest.java index 9df0676ca..8be001921 100644 --- a/modules/spectoconfig/src/test/java/com/axway/apim/config/GenerateTemplateCLIOptionsTest.java +++ b/modules/spectoconfig/src/test/java/com/axway/apim/config/GenerateTemplateCLIOptionsTest.java @@ -61,9 +61,9 @@ public void testGenerateAPIConfig() throws IOException { Assert.assertEquals(openApiLocation, documentContext.read("$.apiSpecification.resource")); Assert.assertEquals("https://localhost", documentContext.read("$.backendBasepath")); - Assert.assertEquals("pet", documentContext.read("$.tags.pet[0]")); - Assert.assertEquals("store", documentContext.read("$.tags.store[0]")); - Assert.assertEquals("user", documentContext.read("$.tags.user[0]")); + Assert.assertEquals("Everything about your Pets", documentContext.read("$.tags.pet[0]")); + Assert.assertEquals("Access to Petstore orders", documentContext.read("$.tags.store[0]")); + Assert.assertEquals("Operations about user", documentContext.read("$.tags.user[0]")); Assert.assertEquals("_default", documentContext.read("$.corsProfiles[0].name")); Assert.assertEquals("*", documentContext.read("$.corsProfiles[0].origins[0]")); @@ -82,7 +82,7 @@ public void testGenerateAPIConfigWithFrontendApikey() throws IOException { DocumentContext documentContext = JsonPath.parse(Files.newInputStream(Paths.get("test/api-config.json"))); Assert.assertEquals("apiKey", documentContext.read("$.securityProfiles[0].devices[0].type")); - Assert.assertEquals("API Key", documentContext.read("$.securityProfiles[0].devices[0].name")); + Assert.assertEquals("_default", documentContext.read("$.securityProfiles[0].devices[0].name")); Assert.assertEquals(1, documentContext.read("$.securityProfiles[0].devices[0].order", Integer.class).intValue()); Assert.assertEquals("KeyId", documentContext.read("$.securityProfiles[0].devices[0].properties.apiKeyFieldName")); Assert.assertEquals("HEADER", documentContext.read("$.securityProfiles[0].devices[0].properties.takeFrom")); @@ -96,19 +96,19 @@ public void testGenerateAPIConfigWithFrontendOauth() throws IOException { GenerateTemplate.generate(args); DocumentContext documentContext = JsonPath.parse(Files.newInputStream(Paths.get("test/api-config.json"))); Assert.assertEquals("oauth", documentContext.read("$.securityProfiles[0].devices[0].type")); - Assert.assertEquals("OAuth", documentContext.read("$.securityProfiles[0].devices[0].name")); + Assert.assertEquals("_default", documentContext.read("$.securityProfiles[0].devices[0].name")); Assert.assertEquals(1, documentContext.read("$.securityProfiles[0].devices[0].order", Integer.class).intValue()); Assert.assertEquals("OAuth Access Token Store", documentContext.read("$.securityProfiles[0].devices[0].properties.tokenStore")); Assert.assertEquals("HEADER", documentContext.read("$.securityProfiles[0].devices[0].properties.accessTokenLocation")); Assert.assertEquals("Bearer", documentContext.read("$.securityProfiles[0].devices[0].properties.authorizationHeaderPrefix")); //Assert.assertEquals("", documentContext.read("$.securityProfiles.devices[0].properties.accessTokenLocationQueryString")); - Assert.assertEquals("Any", documentContext.read("$.securityProfiles[0].devices[0].properties.scopesMustMatch")); + Assert.assertEquals("All", documentContext.read("$.securityProfiles[0].devices[0].properties.scopesMustMatch")); Assert.assertEquals("resource.WRITE, resource.READ", documentContext.read("$.securityProfiles[0].devices[0].properties.scopes")); - Assert.assertTrue(documentContext.read("$.securityProfiles[0].devices[0].properties.removeCredentialsOnSuccess", Boolean.class)); - Assert.assertTrue(documentContext.read("$.securityProfiles[0].devices[0].properties.implicitGrantEnabled", Boolean.class)); + Assert.assertEquals("true", documentContext.read("$.securityProfiles[0].devices[0].properties.removeCredentialsOnSuccess")); + Assert.assertEquals("true", documentContext.read("$.securityProfiles[0].devices[0].properties.implicitGrantEnabled")); Assert.assertEquals("https://localhost:8089/api/oauth/authorize", documentContext.read("$.securityProfiles[0].devices[0].properties.implicitGrantLoginEndpointUrl")); Assert.assertEquals("access_token", documentContext.read("$.securityProfiles[0].devices[0].properties.implicitGrantLoginTokenName")); - Assert.assertTrue(documentContext.read("$.securityProfiles[0].devices[0].properties.authCodeGrantTypeEnabled", Boolean.class)); + Assert.assertEquals("true", documentContext.read("$.securityProfiles[0].devices[0].properties.authCodeGrantTypeEnabled")); Assert.assertEquals("https://localhost:8089/api/oauth/authorize", documentContext.read("$.securityProfiles[0].devices[0].properties.authCodeGrantTypeRequestEndpointUrl")); Assert.assertEquals("client_id", documentContext.read("$.securityProfiles[0].devices[0].properties.authCodeGrantTypeRequestClientIdName")); Assert.assertEquals("client_secret", documentContext.read("$.securityProfiles[0].devices[0].properties.authCodeGrantTypeRequestSecretName")); @@ -124,22 +124,22 @@ public void testGenerateAPIConfigWithFrontendExternalOauth() throws IOException GenerateTemplate.generate(args); DocumentContext documentContext = JsonPath.parse(Files.newInputStream(Paths.get("test/api-config.json"))); Assert.assertEquals("oauthExternal", documentContext.read("$.securityProfiles[0].devices[0].type")); - Assert.assertEquals("OAuth (External)", documentContext.read("$.securityProfiles[0].devices[0].name")); + Assert.assertEquals("_default", documentContext.read("$.securityProfiles[0].devices[0].name")); Assert.assertEquals(1, documentContext.read("$.securityProfiles[0].devices[0].order", Integer.class).intValue()); Assert.assertEquals("Tokeninfo policy 1", documentContext.read("$.securityProfiles[0].devices[0].properties.tokenStore")); Assert.assertEquals("HEADER", documentContext.read("$.securityProfiles[0].devices[0].properties.accessTokenLocation")); Assert.assertEquals("Bearer", documentContext.read("$.securityProfiles[0].devices[0].properties.authorizationHeaderPrefix")); //Assert.assertEquals("", documentContext.read("$.securityProfiles.devices[0].properties.accessTokenLocationQueryString")); - Assert.assertEquals("Any", documentContext.read("$.securityProfiles[0].devices[0].properties.scopesMustMatch")); + Assert.assertEquals("All", documentContext.read("$.securityProfiles[0].devices[0].properties.scopesMustMatch")); Assert.assertEquals("resource.WRITE, resource.READ", documentContext.read("$.securityProfiles[0].devices[0].properties.scopes")); - Assert.assertTrue(documentContext.read("$.securityProfiles[0].devices[0].properties.removeCredentialsOnSuccess", Boolean.class)); - Assert.assertTrue(documentContext.read("$.securityProfiles[0].devices[0].properties.implicitGrantEnabled", Boolean.class)); - Assert.assertTrue(documentContext.read("$.securityProfiles[0].devices[0].properties.useClientRegistry", Boolean.class)); + Assert.assertEquals("true", documentContext.read("$.securityProfiles[0].devices[0].properties.removeCredentialsOnSuccess")); + Assert.assertEquals("true", documentContext.read("$.securityProfiles[0].devices[0].properties.implicitGrantEnabled")); + Assert.assertEquals("true", documentContext.read("$.securityProfiles[0].devices[0].properties.useClientRegistry")); Assert.assertEquals("${oauth.token.client_id}", documentContext.read("$.securityProfiles[0].devices[0].properties.subjectSelector")); Assert.assertEquals("https://localhost:8089/api/oauth/authorize", documentContext.read("$.securityProfiles[0].devices[0].properties.implicitGrantLoginEndpointUrl")); Assert.assertEquals("access_token", documentContext.read("$.securityProfiles[0].devices[0].properties.implicitGrantLoginTokenName")); - Assert.assertTrue(documentContext.read("$.securityProfiles[0].devices[0].properties.authCodeGrantTypeEnabled", Boolean.class)); + Assert.assertEquals("true", documentContext.read("$.securityProfiles[0].devices[0].properties.authCodeGrantTypeEnabled")); Assert.assertEquals("https://localhost:8089/api/oauth/authorize", documentContext.read("$.securityProfiles[0].devices[0].properties.authCodeGrantTypeRequestEndpointUrl")); Assert.assertEquals("client_id", documentContext.read("$.securityProfiles[0].devices[0].properties.authCodeGrantTypeRequestClientIdName")); diff --git a/modules/spectoconfig/src/test/java/com/axway/apim/config/GenerateTemplateTest.java b/modules/spectoconfig/src/test/java/com/axway/apim/config/GenerateTemplateTest.java index ac28c0619..569dd23ff 100644 --- a/modules/spectoconfig/src/test/java/com/axway/apim/config/GenerateTemplateTest.java +++ b/modules/spectoconfig/src/test/java/com/axway/apim/config/GenerateTemplateTest.java @@ -219,7 +219,7 @@ public void includeInboundPerMethodOverride() { @Test public void testInboundOverride() throws IOException { - String[] args = {"template", "generate", "-c", "api-config.json", "-a", "src/test/resources/methods.yaml", "-frontendAuthType", "apiKey", "-inboundPerMethodOverride", "-o", "json"}; + String[] args = {"template", "generate", "-c", "api-config.json", "-a", "methods.yaml", "-frontendAuthType", "apiKey", "-inboundPerMethodOverride", "-o", "json"}; GenerateTemplate.generate(args); // DocumentContext documentContext = JsonPath.parse(Files.newInputStream(Paths.get("api-config.json"))); // Assert.assertEquals("Swagger Petstore - OpenAPI 3.0", documentContext.read("$.name")); From 615bf80dbbf66ea52c4f92cae0f599b293d19582 Mon Sep 17 00:00:00 2001 From: rathnapandi Date: Fri, 22 Nov 2024 20:19:29 -0700 Subject: [PATCH 6/6] Format api config file --- .../src/main/java/com/axway/apim/config/GenerateTemplate.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/spectoconfig/src/main/java/com/axway/apim/config/GenerateTemplate.java b/modules/spectoconfig/src/main/java/com/axway/apim/config/GenerateTemplate.java index 20ebae3ae..53546f7b0 100644 --- a/modules/spectoconfig/src/main/java/com/axway/apim/config/GenerateTemplate.java +++ b/modules/spectoconfig/src/main/java/com/axway/apim/config/GenerateTemplate.java @@ -121,7 +121,7 @@ public static int generate(String[] args) { objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); objectMapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY); JsonNode jsonNode = objectMapper.convertValue(apiConfig, JsonNode.class); - objectMapper.writeValue(fileWriter, jsonNode); + objectMapper.writerWithDefaultPrettyPrinter().writeValue(fileWriter, jsonNode); LOG.info("Writing APIM CLI configuration file to : {}", params.getConfig()); } } catch (AppException e) {