diff --git a/modules/swagger-codegen-cli/pom.xml b/modules/swagger-codegen-cli/pom.xml
index 1e9dd434712..0389eae1d92 100644
--- a/modules/swagger-codegen-cli/pom.xml
+++ b/modules/swagger-codegen-cli/pom.xml
@@ -4,7 +4,7 @@
io.swagger.codegen.v3
swagger-codegen-project
3.0.79-SNAPSHOT
- ../..
+ ../../pom.xml
4.0.0
diff --git a/modules/swagger-codegen-maven-plugin/pom.xml b/modules/swagger-codegen-maven-plugin/pom.xml
index 5eb5618e84f..882a741d8c0 100644
--- a/modules/swagger-codegen-maven-plugin/pom.xml
+++ b/modules/swagger-codegen-maven-plugin/pom.xml
@@ -7,7 +7,7 @@
io.swagger.codegen.v3
swagger-codegen-project
3.0.79-SNAPSHOT
- ../..
+ ../../pom.xml
swagger-codegen-maven-plugin
swagger-codegen (maven-plugin)
diff --git a/modules/swagger-codegen/pom.xml b/modules/swagger-codegen/pom.xml
index d9e816e9853..f02e692f6e3 100644
--- a/modules/swagger-codegen/pom.xml
+++ b/modules/swagger-codegen/pom.xml
@@ -4,7 +4,7 @@
io.swagger.codegen.v3
swagger-codegen-project
3.0.79-SNAPSHOT
- ../..
+ ../../pom.xml
4.0.0
swagger-codegen
diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/v3/DefaultGenerator.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/v3/DefaultGenerator.java
index 94acb4bcbac..2ac2c4c8611 100644
--- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/v3/DefaultGenerator.java
+++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/v3/DefaultGenerator.java
@@ -17,8 +17,7 @@
import io.swagger.v3.oas.models.info.License;
import io.swagger.v3.oas.models.media.Schema;
import io.swagger.v3.oas.models.parameters.Parameter;
-import io.swagger.v3.oas.models.security.SecurityRequirement;
-import io.swagger.v3.oas.models.security.SecurityScheme;
+import io.swagger.v3.oas.models.security.*;
import io.swagger.v3.oas.models.tags.Tag;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.ObjectUtils;
@@ -1217,16 +1216,64 @@ private Map getAuthMethods(List sec
}
final Map authMethods = new HashMap<>();
for (SecurityRequirement requirement : securities) {
- for (String key : requirement.keySet()) {
- SecurityScheme securityScheme = securitySchemes.get(key);
+ requirement.forEach((securitySchemeName, endpointScopes) -> {
+ SecurityScheme securityScheme = securitySchemes.get(securitySchemeName);
if (securityScheme != null) {
- authMethods.put(key, securityScheme);
+ SecurityScheme endpointSecurityScheme = selectScopeSubsetForSecurityScheme(securityScheme, endpointScopes);
+ authMethods.put(securitySchemeName, endpointSecurityScheme);
}
- }
+ });
}
return authMethods;
}
+ private SecurityScheme selectScopeSubsetForSecurityScheme(SecurityScheme globalSecurityScheme, List scopesToSelect) {
+ OAuthFlows globalOAuthFlows = globalSecurityScheme.getFlows();
+ // the OAuth flows should only be set when security scheme's getType() is either OAUTH2 or OPENIDCONNECT
+ if (globalOAuthFlows == null) {
+ return globalSecurityScheme;
+ }
+ OAuthFlows oauthFlowsWithScopeSubset = new OAuthFlows()
+ .authorizationCode(selectScopeSubsetForOAuthFlow(globalOAuthFlows.getAuthorizationCode(), scopesToSelect))
+ .clientCredentials(selectScopeSubsetForOAuthFlow(globalOAuthFlows.getClientCredentials(), scopesToSelect))
+ .implicit(selectScopeSubsetForOAuthFlow(globalOAuthFlows.getImplicit(), scopesToSelect))
+ .password(selectScopeSubsetForOAuthFlow(globalOAuthFlows.getPassword(), scopesToSelect))
+ .extensions(globalOAuthFlows.getExtensions());
+ return new SecurityScheme()
+ .type(globalSecurityScheme.getType())
+ .$ref(globalSecurityScheme.get$ref())
+ .name(globalSecurityScheme.getName())
+ .description(globalSecurityScheme.getDescription())
+ .scheme(globalSecurityScheme.getScheme())
+ .in(globalSecurityScheme.getIn())
+ .flows(oauthFlowsWithScopeSubset)
+ .bearerFormat(globalSecurityScheme.getBearerFormat())
+ .openIdConnectUrl(globalSecurityScheme.getOpenIdConnectUrl())
+ .extensions(globalSecurityScheme.getExtensions());
+ }
+
+ private OAuthFlow selectScopeSubsetForOAuthFlow(OAuthFlow globalOAuthFlow, List scopesToSelect) {
+ if (globalOAuthFlow != null) {
+ Scopes globalScopes = globalOAuthFlow.getScopes();
+ Scopes scopeSubset = new Scopes();
+ for (String scope : scopesToSelect) {
+ String selectedScope = globalScopes.get(scope);
+ if (selectedScope != null) {
+ scopeSubset.addString(scope, selectedScope);
+ } else {
+ LOGGER.warn("Scope {} is not defined in the global security schemes!", scope);
+ }
+ }
+ return new OAuthFlow()
+ .authorizationUrl(globalOAuthFlow.getAuthorizationUrl())
+ .tokenUrl(globalOAuthFlow.getTokenUrl())
+ .refreshUrl(globalOAuthFlow.getRefreshUrl())
+ .scopes(scopeSubset)
+ .extensions(globalOAuthFlow.getExtensions());
+ }
+ return null;
+ }
+
private Boolean getCustomOptionBooleanValue(String option) {
List languageArguments = config.getLanguageArguments();
if (languageArguments == null || languageArguments.isEmpty()) {
diff --git a/modules/swagger-codegen/src/test/java/io/swagger/codegen/v3/service/GeneratorServiceTest.java b/modules/swagger-codegen/src/test/java/io/swagger/codegen/v3/service/GeneratorServiceTest.java
index 33d611e3444..c0de766c710 100644
--- a/modules/swagger-codegen/src/test/java/io/swagger/codegen/v3/service/GeneratorServiceTest.java
+++ b/modules/swagger-codegen/src/test/java/io/swagger/codegen/v3/service/GeneratorServiceTest.java
@@ -14,6 +14,7 @@
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
+import java.nio.charset.Charset;
import java.nio.file.Files;
import java.util.List;
@@ -1022,6 +1023,43 @@ public void testIssue613_605_612_non_resteasy() throws IOException {
Assert.assertFalse(files.isEmpty());
System.out.println("Generated server in:\n" + path);
}
+
+ @Test
+ public void testSecurityScopesAreProperlyApplied() throws IOException {
+
+ String path = getTmpFolder().getAbsolutePath();
+ GenerationRequest request = new GenerationRequest()
+ .codegenVersion(GenerationRequest.CodegenVersion.V3)
+ .type(GenerationRequest.Type.SERVER)
+ .lang("spring")
+ .spec(loadSpecAsNode("3_0_0/issue-9843.yaml", true, false))
+ .options(new Options().outputDir(path));
+
+ List files = new GeneratorService().generationRequest(request).generate();
+ boolean petFound = false, userFound = false;
+ Assert.assertFalse(files.isEmpty());
+ for (File f: files) {
+ String relPath = f.getAbsolutePath().substring(path.length()).replace("\\", "/");
+ if ("/src/main/java/io/swagger/api/PetApi.java".equals(relPath)) {
+ petFound = true;
+ String actualContents = FileUtils.readFileToString(f, Charset.defaultCharset());
+ Assert.assertTrue(actualContents.contains("@SecurityRequirement(name = \"petstore_auth\", scopes = {"));
+ Assert.assertTrue(actualContents.contains("\"read:pets\""));
+ Assert.assertFalse(actualContents.contains("\"write:pets\""));
+ Assert.assertFalse(actualContents.contains(":users\""));
+ } else if ("/src/main/java/io/swagger/api/UserApi.java".equals(relPath)) {
+ userFound = true;
+ String actualContents = FileUtils.readFileToString(f, Charset.defaultCharset());
+ Assert.assertTrue(actualContents.contains("@SecurityRequirement(name = \"petstore_auth\", scopes = {"));
+ Assert.assertTrue(actualContents.contains("\"write:users\""));
+ Assert.assertFalse(actualContents.contains(":pets\""));
+ }
+ }
+ final String fileExpectedFormat = "%s expected to be generated";
+ Assert.assertTrue(petFound, String.format(fileExpectedFormat, "PetApi.java"));
+ Assert.assertTrue(userFound, String.format(fileExpectedFormat, "UserApi.java"));
+ }
+
protected static File getTmpFolder() {
try {
File outputFolder = Files.createTempFile("codegentest-", "-tmp").toFile();
diff --git a/modules/swagger-codegen/src/test/resources/3_0_0/issue-9843.yaml b/modules/swagger-codegen/src/test/resources/3_0_0/issue-9843.yaml
new file mode 100644
index 00000000000..d44a097a57f
--- /dev/null
+++ b/modules/swagger-codegen/src/test/resources/3_0_0/issue-9843.yaml
@@ -0,0 +1,206 @@
+openapi: 3.0.1
+servers: []
+info:
+ description: |
+ This is a sample Petstore server. You can find
+ out more about Swagger at
+ [http://swagger.io](http://swagger.io) or on
+ [irc.freenode.net, #swagger](http://swagger.io/irc/).
+ version: "1.0.0"
+ title: Swagger Petstore
+ termsOfService: 'http://swagger.io/terms/'
+ contact:
+ email: apiteam@swagger.io
+ license:
+ name: Apache 2.0
+ url: 'http://www.apache.org/licenses/LICENSE-2.0.html'
+tags:
+ - name: pet
+ description: Everything about your Pets
+ externalDocs:
+ description: Find out more
+ url: 'http://swagger.io'
+ - name: user
+ description: Operations about user
+ externalDocs:
+ description: Find out more about our store
+ url: 'http://swagger.io'
+paths:
+ /pet/findByStatus:
+ get:
+ tags:
+ - pet
+ summary: Finds Pets by status
+ description: Multiple status values can be provided with comma separated strings
+ operationId: findPetsByStatus
+ parameters:
+ - name: status
+ in: query
+ description: Status values that need to be considered for filter
+ required: true
+ explode: true
+ schema:
+ type: array
+ items:
+ type: string
+ enum:
+ - available
+ - pending
+ - sold
+ default: available
+ responses:
+ '200':
+ description: successful operation
+ content:
+ application/json:
+ schema:
+ type: array
+ items:
+ $ref: '#/components/schemas/Pet'
+ application/xml:
+ schema:
+ type: array
+ items:
+ $ref: '#/components/schemas/Pet'
+ '400':
+ description: Invalid status value
+ security:
+ - petstore_auth:
+ - 'read:pets'
+ /user:
+ post:
+ tags:
+ - user
+ summary: Create user
+ description: This can only be done by the logged in user.
+ operationId: createUser
+ responses:
+ default:
+ description: successful operation
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/User'
+ description: Created user object
+ required: true
+ security:
+ - petstore_auth:
+ - 'write:users'
+externalDocs:
+ description: Find out more about Swagger
+ url: 'http://swagger.io'
+components:
+ schemas:
+ Category:
+ type: object
+ properties:
+ id:
+ type: integer
+ format: int64
+ name:
+ type: string
+ xml:
+ name: Category
+ User:
+ type: object
+ properties:
+ id:
+ type: integer
+ format: int64
+ username:
+ type: string
+ firstName:
+ type: string
+ lastName:
+ type: string
+ email:
+ type: string
+ password:
+ type: string
+ phone:
+ type: string
+ userStatus:
+ type: integer
+ format: int32
+ description: User Status
+ xml:
+ name: User
+ Tag:
+ type: object
+ properties:
+ id:
+ type: integer
+ format: int64
+ name:
+ type: string
+ xml:
+ name: Tag
+ Pet:
+ type: object
+ required:
+ - name
+ - photoUrls
+ properties:
+ id:
+ type: integer
+ format: int64
+ category:
+ $ref: '#/components/schemas/Category'
+ name:
+ type: string
+ example: doggie
+ photoUrls:
+ type: array
+ xml:
+ name: photoUrl
+ wrapped: true
+ items:
+ type: string
+ tags:
+ type: array
+ xml:
+ name: tag
+ wrapped: true
+ items:
+ $ref: '#/components/schemas/Tag'
+ status:
+ type: string
+ description: pet status in the store
+ enum:
+ - available
+ - pending
+ - sold
+ xml:
+ name: Pet
+
+ requestBodies:
+ Pet:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Pet'
+ application/xml:
+ schema:
+ $ref: '#/components/schemas/Pet'
+ description: Pet object that needs to be added to the store
+ required: true
+ UserArray:
+ content:
+ application/json:
+ schema:
+ type: array
+ items:
+ $ref: '#/components/schemas/User'
+ description: List of user object
+ required: true
+ securitySchemes:
+ petstore_auth:
+ type: oauth2
+ flows:
+ implicit:
+ authorizationUrl: 'http://petstore.swagger.io/oauth/dialog'
+ scopes:
+ 'write:pets': modify pets in your account
+ 'read:pets': read your pets
+ 'write:users': modify users
diff --git a/modules/swagger-generator/pom.xml b/modules/swagger-generator/pom.xml
index b36796c9f46..0c4ced472c4 100644
--- a/modules/swagger-generator/pom.xml
+++ b/modules/swagger-generator/pom.xml
@@ -5,7 +5,7 @@
io.swagger.codegen.v3
swagger-codegen-project
3.0.79-SNAPSHOT
- ../..
+ ../../pom.xml
swagger-generator
war