diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenSecurity.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenSecurity.java index ea3549712027..ea51d6ff5593 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenSecurity.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenSecurity.java @@ -17,6 +17,7 @@ package org.openapitools.codegen; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Locale; @@ -38,7 +39,7 @@ public class CodegenSecurity { // Oauth specific public String flow, authorizationUrl, tokenUrl; public List> scopes; - public Boolean isCode, isPassword, isApplication, isImplicit, hasScopes; + public Boolean isCode, isPassword, isApplication, isImplicit; @Override public String toString() { @@ -100,4 +101,47 @@ public int hashCode() { isImplicit, scopes); } + + // Return a copy of the security object, filtering out any scopes from the passed-in list. + public CodegenSecurity filterByScopeNames(List filterScopes) { + CodegenSecurity filteredSecurity = new CodegenSecurity(); + // Copy all fields except the scopes. + filteredSecurity.name = name; + filteredSecurity.type = type; + filteredSecurity.hasMore = false; + filteredSecurity.isBasic = isBasic; + filteredSecurity.isBasicBasic = isBasicBasic; + filteredSecurity.isBasicBearer = isBasicBearer; + filteredSecurity.isApiKey = isApiKey; + filteredSecurity.isOAuth = isOAuth; + filteredSecurity.keyParamName = keyParamName; + filteredSecurity.isCode = isCode; + filteredSecurity.isImplicit = isImplicit; + filteredSecurity.isApplication = isApplication; + filteredSecurity.isPassword = isPassword; + filteredSecurity.isKeyInCookie = isKeyInCookie; + filteredSecurity.isKeyInHeader = isKeyInHeader; + filteredSecurity.isKeyInQuery = isKeyInQuery; + filteredSecurity.flow = flow; + filteredSecurity.tokenUrl = tokenUrl; + filteredSecurity.authorizationUrl = authorizationUrl; + // It is not possible to deep copy the extensions, as we have no idea what types they are. + // So the filtered method *will* refer to the original extensions, if any. + filteredSecurity.vendorExtensions = new HashMap(vendorExtensions); + List> returnedScopes = new ArrayList>(); + Map lastScope = null; + for (String filterScopeName : filterScopes) { + for (Map scope : scopes) { + String name = (String) scope.get("scope"); + if (filterScopeName.equals(name)) { + returnedScopes.add(scope); + lastScope = scope; + break; + } + } + } + filteredSecurity.scopes = returnedScopes; + + return filteredSecurity; + } } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java index 98a0226afa28..7f56122e52c4 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java @@ -1057,55 +1057,21 @@ private void processOperation(String resourcePath, String httpMethod, Operation } Map authMethods = getAuthMethods(securities, securitySchemes); - if (authMethods == null || authMethods.isEmpty()) { - authMethods = getAuthMethods(globalSecurities, securitySchemes); - } if (authMethods != null && !authMethods.isEmpty()) { - codegenOperation.authMethods = config.fromSecurity(authMethods); - List> scopes = new ArrayList>(); - if (codegenOperation.authMethods != null) { - for (CodegenSecurity security : codegenOperation.authMethods) { - if (security != null && security.isBasicBearer != null && security.isBasicBearer && - securities != null) { - for (SecurityRequirement req : securities) { - if (req == null) continue; - for (String key : req.keySet()) { - if (security.name != null && key.equals(security.name)) { - int count = 0; - for (String sc : req.get(key)) { - Map scope = new HashMap(); - scope.put("scope", sc); - scope.put("description", ""); - count++; - if (req.get(key) != null && count < req.get(key).size()) { - scope.put("hasMore", "true"); - } else { - scope.put("hasMore", null); - } - scopes.add(scope); - } - //end this inner for - break; - } - } - - } - security.hasScopes = scopes.size() > 0; - security.scopes = scopes; - } - } - } + List fullAuthMethods = config.fromSecurity(authMethods); + codegenOperation.authMethods = filterAuthMethods(fullAuthMethods, securities); codegenOperation.hasAuthMethods = true; - } + } else { + authMethods = getAuthMethods(globalSecurities, securitySchemes); - /* TODO need to revise the logic below - Map securitySchemeMap = openAPI.getComponents().getSecuritySchemes(); - if (securitySchemeMap != null && !securitySchemeMap.isEmpty()) { - codegenOperation.authMethods = config.fromSecurity(securitySchemeMap); - codegenOperation.hasAuthMethods = true; + if (authMethods != null && !authMethods.isEmpty()) { + List fullAuthMethods = config.fromSecurity(authMethods); + codegenOperation.authMethods = filterAuthMethods(fullAuthMethods, globalSecurities); + codegenOperation.hasAuthMethods = true; + } } - */ + } catch (Exception ex) { String msg = "Could not process operation:\n" // + " Tag: " + tag + "\n"// @@ -1311,6 +1277,40 @@ private static OAuthFlow cloneOAuthFlow(OAuthFlow originFlow, List opera .scopes(newScopes); } + private List filterAuthMethods(List authMethods, List securities) { + if (securities == null || securities.isEmpty() || authMethods == null) { + return authMethods; + } + + List result = new ArrayList(); + + for (CodegenSecurity security : authMethods) { + boolean filtered = false; + if (security != null && security.scopes != null) { + for (SecurityRequirement requirement : securities) { + List opScopes = requirement.get(security.name); + if (opScopes != null) { + // We have operation-level scopes for this method, so filter the auth method to + // describe the operation auth method with only the scopes that it requires. + // We have to create a new auth method instance because the original object must + // not be modified. + CodegenSecurity opSecurity = security.filterByScopeNames(opScopes); + result.add(opSecurity); + filtered = true; + break; + } + } + } + + // If we didn't get a filtered version, then we can keep the original auth method. + if (!filtered) { + result.add(security); + } + } + + return result; + } + private boolean hasOAuthMethods(List authMethods) { for (CodegenSecurity cs : authMethods) { if (Boolean.TRUE.equals(cs.isOAuth)) { diff --git a/modules/openapi-generator/src/main/resources/aspnetcore/2.1/controller.mustache b/modules/openapi-generator/src/main/resources/aspnetcore/2.1/controller.mustache index 99492c9a6040..19a6acbc53e0 100644 --- a/modules/openapi-generator/src/main/resources/aspnetcore/2.1/controller.mustache +++ b/modules/openapi-generator/src/main/resources/aspnetcore/2.1/controller.mustache @@ -33,9 +33,15 @@ namespace {{apiPackage}} /// {{description}}{{/allParams}}{{#responses}} /// {{message}}{{/responses}} [{{httpMethod}}] - [Route("{{{basePathWithoutHost}}}{{{path}}}")]{{#hasAuthMethods}}{{#authMethods}}{{#isApiKey}} - [Authorize(Policy = "{{name}}")]{{/isApiKey}}{{#isBasicBearer}} - [Authorize{{#hasScopes}}(Roles = "{{#scopes}}{{scope}}{{#hasMore}},{{/hasMore}}{{/scopes}}"){{/hasScopes}}]{{/isBasicBearer}}{{/authMethods}}{{/hasAuthMethods}} + [Route("{{{basePathWithoutHost}}}{{{path}}}")] +{{#authMethods}} +{{#isApiKey}} + [Authorize(Policy = "{{name}}")] +{{/isApiKey}} +{{#isBasicBearer}} + [Authorize{{#scopes}}{{#-first}}(Roles = "{{/-first}}{{scope}}{{^-last}},{{/-last}}{{#-last}}"){{/-last}}{{/scopes}}] +{{/isBasicBearer}} +{{/authMethods}} [ValidateModelState]{{#useSwashbuckle}} [SwaggerOperation("{{operationId}}")]{{#responses}}{{#dataType}} [SwaggerResponse(statusCode: {{code}}, type: typeof({{&dataType}}), description: "{{message}}")]{{/dataType}}{{^dataType}}{{/dataType}}{{/responses}}{{/useSwashbuckle}}{{^useSwashbuckle}}{{#responses}}{{#dataType}} diff --git a/modules/openapi-generator/src/test/resources/3_0/rust-server/openapi-v3.yaml b/modules/openapi-generator/src/test/resources/3_0/rust-server/openapi-v3.yaml index 661961cd9778..2e558056871a 100644 --- a/modules/openapi-generator/src/test/resources/3_0/rust-server/openapi-v3.yaml +++ b/modules/openapi-generator/src/test/resources/3_0/rust-server/openapi-v3.yaml @@ -101,6 +101,20 @@ paths: responses: '200': description: 'OK' + /readonly_auth_scheme: + get: + security: + - authScheme: ["test.read"] + responses: + 200: + description: Check that limiting to a single required auth scheme works + /multiple_auth_scheme: + get: + security: + - authScheme: ["test.read", "test.write"] + responses: + 200: + description: Check that limiting to multiple required auth schemes works /responses_with_headers: get: responses: @@ -123,7 +137,18 @@ paths: Failure-Info: schema: type: String + components: + securitySchemes: + authScheme: + type: oauth2 + flows: + authorizationCode: + authorizationUrl: 'http://example.org' + tokenUrl: 'http://example.org' + scopes: + test.read: Allowed to read state. + test.write: Allowed to change state. schemas: UuidObject: description: Test a model containing a UUID diff --git a/samples/server/petstore/aspnetcore/.openapi-generator/VERSION b/samples/server/petstore/aspnetcore/.openapi-generator/VERSION index 2f81801b7943..0e97bd19efbf 100644 --- a/samples/server/petstore/aspnetcore/.openapi-generator/VERSION +++ b/samples/server/petstore/aspnetcore/.openapi-generator/VERSION @@ -1 +1 @@ -4.1.1-SNAPSHOT \ No newline at end of file +4.1.3-SNAPSHOT \ No newline at end of file diff --git a/samples/server/petstore/aspnetcore/src/Org.OpenAPITools/wwwroot/openapi-original.json b/samples/server/petstore/aspnetcore/src/Org.OpenAPITools/wwwroot/openapi-original.json index a0a4803cd077..ec994f8fca35 100644 --- a/samples/server/petstore/aspnetcore/src/Org.OpenAPITools/wwwroot/openapi-original.json +++ b/samples/server/petstore/aspnetcore/src/Org.OpenAPITools/wwwroot/openapi-original.json @@ -1,35 +1,32 @@ { "openapi" : "3.0.1", "info" : { - "title" : "OpenAPI Petstore", "description" : "This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.", "license" : { "name" : "Apache-2.0", "url" : "http://www.apache.org/licenses/LICENSE-2.0.html" }, + "title" : "OpenAPI Petstore", "version" : "1.0.0" }, "servers" : [ { "url" : "http://petstore.swagger.io/v2" } ], "tags" : [ { - "name" : "pet", - "description" : "Everything about your Pets" + "description" : "Everything about your Pets", + "name" : "pet" }, { - "name" : "store", - "description" : "Access to Petstore orders" + "description" : "Access to Petstore orders", + "name" : "store" }, { - "name" : "user", - "description" : "Operations about user" + "description" : "Operations about user", + "name" : "user" } ], "paths" : { "/pet" : { - "put" : { - "tags" : [ "pet" ], - "summary" : "Update an existing pet", - "operationId" : "updatePet", + "post" : { + "operationId" : "addPet", "requestBody" : { - "description" : "Pet object that needs to be added to the store", "content" : { "application/json" : { "schema" : { @@ -42,33 +39,25 @@ } } }, + "description" : "Pet object that needs to be added to the store", "required" : true }, "responses" : { - "400" : { - "description" : "Invalid ID supplied", - "content" : { } - }, - "404" : { - "description" : "Pet not found", - "content" : { } - }, "405" : { - "description" : "Validation exception", - "content" : { } + "content" : { }, + "description" : "Invalid input" } }, "security" : [ { "petstore_auth" : [ "write:pets", "read:pets" ] } ], + "summary" : "Add a new pet to the store", + "tags" : [ "pet" ], "x-codegen-request-body-name" : "body" }, - "post" : { - "tags" : [ "pet" ], - "summary" : "Add a new pet to the store", - "operationId" : "addPet", + "put" : { + "operationId" : "updatePet", "requestBody" : { - "description" : "Pet object that needs to be added to the store", "content" : { "application/json" : { "schema" : { @@ -81,146 +70,184 @@ } } }, + "description" : "Pet object that needs to be added to the store", "required" : true }, "responses" : { + "400" : { + "content" : { }, + "description" : "Invalid ID supplied" + }, + "404" : { + "content" : { }, + "description" : "Pet not found" + }, "405" : { - "description" : "Invalid input", - "content" : { } + "content" : { }, + "description" : "Validation exception" } }, "security" : [ { "petstore_auth" : [ "write:pets", "read:pets" ] } ], + "summary" : "Update an existing pet", + "tags" : [ "pet" ], "x-codegen-request-body-name" : "body" } }, "/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, - "style" : "form", "explode" : false, + "in" : "query", + "name" : "status", + "required" : true, "schema" : { - "type" : "array", "items" : { - "type" : "string", "default" : "available", - "enum" : [ "available", "pending", "sold" ] - } - } + "enum" : [ "available", "pending", "sold" ], + "type" : "string" + }, + "type" : "array" + }, + "style" : "form" } ], "responses" : { "200" : { - "description" : "successful operation", "content" : { "application/xml" : { "schema" : { - "type" : "array", "items" : { "$ref" : "#/components/schemas/Pet" - } + }, + "type" : "array" } }, "application/json" : { "schema" : { - "type" : "array", "items" : { "$ref" : "#/components/schemas/Pet" - } + }, + "type" : "array" } } - } + }, + "description" : "successful operation" }, "400" : { - "description" : "Invalid status value", - "content" : { } + "content" : { }, + "description" : "Invalid status value" } }, "security" : [ { "petstore_auth" : [ "write:pets", "read:pets" ] - } ] + } ], + "summary" : "Finds Pets by status", + "tags" : [ "pet" ] } }, "/pet/findByTags" : { "get" : { - "tags" : [ "pet" ], - "summary" : "Finds Pets by tags", + "deprecated" : true, "description" : "Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.", "operationId" : "findPetsByTags", "parameters" : [ { - "name" : "tags", - "in" : "query", "description" : "Tags to filter by", - "required" : true, - "style" : "form", "explode" : false, + "in" : "query", + "name" : "tags", + "required" : true, "schema" : { - "type" : "array", "items" : { "type" : "string" - } - } + }, + "type" : "array" + }, + "style" : "form" } ], "responses" : { "200" : { - "description" : "successful operation", "content" : { "application/xml" : { "schema" : { - "type" : "array", "items" : { "$ref" : "#/components/schemas/Pet" - } + }, + "type" : "array" } }, "application/json" : { "schema" : { - "type" : "array", "items" : { "$ref" : "#/components/schemas/Pet" - } + }, + "type" : "array" } } - } + }, + "description" : "successful operation" }, "400" : { - "description" : "Invalid tag value", - "content" : { } + "content" : { }, + "description" : "Invalid tag value" } }, - "deprecated" : true, "security" : [ { "petstore_auth" : [ "write:pets", "read:pets" ] - } ] + } ], + "summary" : "Finds Pets by tags", + "tags" : [ "pet" ] } }, "/pet/{petId}" : { + "delete" : { + "operationId" : "deletePet", + "parameters" : [ { + "in" : "header", + "name" : "api_key", + "schema" : { + "type" : "string" + } + }, { + "description" : "Pet id to delete", + "in" : "path", + "name" : "petId", + "required" : true, + "schema" : { + "format" : "int64", + "type" : "integer" + } + } ], + "responses" : { + "400" : { + "content" : { }, + "description" : "Invalid pet value" + } + }, + "security" : [ { + "petstore_auth" : [ "write:pets", "read:pets" ] + } ], + "summary" : "Deletes a pet", + "tags" : [ "pet" ] + }, "get" : { - "tags" : [ "pet" ], - "summary" : "Find pet by ID", "description" : "Returns a single pet", "operationId" : "getPetById", "parameters" : [ { - "name" : "petId", - "in" : "path", "description" : "ID of pet to return", + "in" : "path", + "name" : "petId", "required" : true, "schema" : { - "type" : "integer", - "format" : "int64" + "format" : "int64", + "type" : "integer" } } ], "responses" : { "200" : { - "description" : "successful operation", "content" : { "application/xml" : { "schema" : { @@ -232,33 +259,34 @@ "$ref" : "#/components/schemas/Pet" } } - } + }, + "description" : "successful operation" }, "400" : { - "description" : "Invalid ID supplied", - "content" : { } + "content" : { }, + "description" : "Invalid ID supplied" }, "404" : { - "description" : "Pet not found", - "content" : { } + "content" : { }, + "description" : "Pet not found" } }, "security" : [ { "api_key" : [ ] - } ] + } ], + "summary" : "Find pet by ID", + "tags" : [ "pet" ] }, "post" : { - "tags" : [ "pet" ], - "summary" : "Updates a pet in the store with form data", "operationId" : "updatePetWithForm", "parameters" : [ { - "name" : "petId", - "in" : "path", "description" : "ID of pet that needs to be updated", + "in" : "path", + "name" : "petId", "required" : true, "schema" : { - "type" : "integer", - "format" : "int64" + "format" : "int64", + "type" : "integer" } } ], "requestBody" : { @@ -267,12 +295,12 @@ "schema" : { "properties" : { "name" : { - "type" : "string", - "description" : "Updated name of the pet" + "description" : "Updated name of the pet", + "type" : "string" }, "status" : { - "type" : "string", - "description" : "Updated status of the pet" + "description" : "Updated status of the pet", + "type" : "string" } } } @@ -281,58 +309,28 @@ }, "responses" : { "405" : { - "description" : "Invalid input", - "content" : { } + "content" : { }, + "description" : "Invalid input" } }, "security" : [ { "petstore_auth" : [ "write:pets", "read:pets" ] - } ] - }, - "delete" : { - "tags" : [ "pet" ], - "summary" : "Deletes a pet", - "operationId" : "deletePet", - "parameters" : [ { - "name" : "api_key", - "in" : "header", - "schema" : { - "type" : "string" - } - }, { - "name" : "petId", - "in" : "path", - "description" : "Pet id to delete", - "required" : true, - "schema" : { - "type" : "integer", - "format" : "int64" - } } ], - "responses" : { - "400" : { - "description" : "Invalid pet value", - "content" : { } - } - }, - "security" : [ { - "petstore_auth" : [ "write:pets", "read:pets" ] - } ] + "summary" : "Updates a pet in the store with form data", + "tags" : [ "pet" ] } }, "/pet/{petId}/uploadImage" : { "post" : { - "tags" : [ "pet" ], - "summary" : "uploads an image", "operationId" : "uploadFile", "parameters" : [ { - "name" : "petId", - "in" : "path", "description" : "ID of pet to update", + "in" : "path", + "name" : "petId", "required" : true, "schema" : { - "type" : "integer", - "format" : "int64" + "format" : "int64", + "type" : "integer" } } ], "requestBody" : { @@ -341,13 +339,13 @@ "schema" : { "properties" : { "additionalMetadata" : { - "type" : "string", - "description" : "Additional data to pass to server" + "description" : "Additional data to pass to server", + "type" : "string" }, "file" : { - "type" : "string", "description" : "file to upload", - "format" : "binary" + "format" : "binary", + "type" : "string" } } } @@ -356,55 +354,54 @@ }, "responses" : { "200" : { - "description" : "successful operation", "content" : { "application/json" : { "schema" : { "$ref" : "#/components/schemas/ApiResponse" } } - } + }, + "description" : "successful operation" } }, "security" : [ { "petstore_auth" : [ "write:pets", "read:pets" ] - } ] + } ], + "summary" : "uploads an image", + "tags" : [ "pet" ] } }, "/store/inventory" : { "get" : { - "tags" : [ "store" ], - "summary" : "Returns pet inventories by status", "description" : "Returns a map of status codes to quantities", "operationId" : "getInventory", "responses" : { "200" : { - "description" : "successful operation", "content" : { "application/json" : { "schema" : { - "type" : "object", "additionalProperties" : { - "type" : "integer", - "format" : "int32" - } + "format" : "int32", + "type" : "integer" + }, + "type" : "object" } } - } + }, + "description" : "successful operation" } }, "security" : [ { "api_key" : [ ] - } ] + } ], + "summary" : "Returns pet inventories by status", + "tags" : [ "store" ] } }, "/store/order" : { "post" : { - "tags" : [ "store" ], - "summary" : "Place an order for a pet", "operationId" : "placeOrder", "requestBody" : { - "description" : "order placed for purchasing the pet", "content" : { "*/*" : { "schema" : { @@ -412,11 +409,11 @@ } } }, + "description" : "order placed for purchasing the pet", "required" : true }, "responses" : { "200" : { - "description" : "successful operation", "content" : { "application/xml" : { "schema" : { @@ -428,37 +425,62 @@ "$ref" : "#/components/schemas/Order" } } - } + }, + "description" : "successful operation" }, "400" : { - "description" : "Invalid Order", - "content" : { } + "content" : { }, + "description" : "Invalid Order" } }, + "summary" : "Place an order for a pet", + "tags" : [ "store" ], "x-codegen-request-body-name" : "body" } }, "/store/order/{orderId}" : { + "delete" : { + "description" : "For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors", + "operationId" : "deleteOrder", + "parameters" : [ { + "description" : "ID of the order that needs to be deleted", + "in" : "path", + "name" : "orderId", + "required" : true, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "400" : { + "content" : { }, + "description" : "Invalid ID supplied" + }, + "404" : { + "content" : { }, + "description" : "Order not found" + } + }, + "summary" : "Delete purchase order by ID", + "tags" : [ "store" ] + }, "get" : { - "tags" : [ "store" ], - "summary" : "Find purchase order by ID", "description" : "For valid response try integer IDs with value <= 5 or > 10. Other values will generated exceptions", "operationId" : "getOrderById", "parameters" : [ { - "name" : "orderId", - "in" : "path", "description" : "ID of pet that needs to be fetched", + "in" : "path", + "name" : "orderId", "required" : true, "schema" : { + "format" : "int64", "maximum" : 5, "minimum" : 1, - "type" : "integer", - "format" : "int64" + "type" : "integer" } } ], "responses" : { "200" : { - "description" : "successful operation", "content" : { "application/xml" : { "schema" : { @@ -470,52 +492,27 @@ "$ref" : "#/components/schemas/Order" } } - } + }, + "description" : "successful operation" }, "400" : { - "description" : "Invalid ID supplied", - "content" : { } + "content" : { }, + "description" : "Invalid ID supplied" }, "404" : { - "description" : "Order not found", - "content" : { } - } - } - }, - "delete" : { - "tags" : [ "store" ], - "summary" : "Delete purchase order by ID", - "description" : "For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors", - "operationId" : "deleteOrder", - "parameters" : [ { - "name" : "orderId", - "in" : "path", - "description" : "ID of the order that needs to be deleted", - "required" : true, - "schema" : { - "type" : "string" + "content" : { }, + "description" : "Order not found" } - } ], - "responses" : { - "400" : { - "description" : "Invalid ID supplied", - "content" : { } - }, - "404" : { - "description" : "Order not found", - "content" : { } - } - } + }, + "summary" : "Find purchase order by ID", + "tags" : [ "store" ] } }, "/user" : { "post" : { - "tags" : [ "user" ], - "summary" : "Create user", "description" : "This can only be done by the logged in user.", "operationId" : "createUser", "requestBody" : { - "description" : "Created user object", "content" : { "*/*" : { "schema" : { @@ -523,90 +520,91 @@ } } }, + "description" : "Created user object", "required" : true }, "responses" : { "default" : { - "description" : "successful operation", - "content" : { } + "content" : { }, + "description" : "successful operation" } }, + "summary" : "Create user", + "tags" : [ "user" ], "x-codegen-request-body-name" : "body" } }, "/user/createWithArray" : { "post" : { - "tags" : [ "user" ], - "summary" : "Creates list of users with given input array", "operationId" : "createUsersWithArrayInput", "requestBody" : { - "description" : "List of user object", "content" : { "*/*" : { "schema" : { - "type" : "array", "items" : { "$ref" : "#/components/schemas/User" - } + }, + "type" : "array" } } }, + "description" : "List of user object", "required" : true }, "responses" : { "default" : { - "description" : "successful operation", - "content" : { } + "content" : { }, + "description" : "successful operation" } }, + "summary" : "Creates list of users with given input array", + "tags" : [ "user" ], "x-codegen-request-body-name" : "body" } }, "/user/createWithList" : { "post" : { - "tags" : [ "user" ], - "summary" : "Creates list of users with given input array", "operationId" : "createUsersWithListInput", "requestBody" : { - "description" : "List of user object", "content" : { "*/*" : { "schema" : { - "type" : "array", "items" : { "$ref" : "#/components/schemas/User" - } + }, + "type" : "array" } } }, + "description" : "List of user object", "required" : true }, "responses" : { "default" : { - "description" : "successful operation", - "content" : { } + "content" : { }, + "description" : "successful operation" } }, + "summary" : "Creates list of users with given input array", + "tags" : [ "user" ], "x-codegen-request-body-name" : "body" } }, "/user/login" : { "get" : { - "tags" : [ "user" ], - "summary" : "Logs user into the system", "operationId" : "loginUser", "parameters" : [ { - "name" : "username", - "in" : "query", "description" : "The user name for login", + "in" : "query", + "name" : "username", "required" : true, "schema" : { "type" : "string" } }, { - "name" : "password", - "in" : "query", "description" : "The password for login in clear text", + "in" : "query", + "name" : "password", "required" : true, "schema" : { "type" : "string" @@ -614,65 +612,90 @@ } ], "responses" : { "200" : { - "description" : "successful operation", - "headers" : { - "X-Rate-Limit" : { - "description" : "calls per hour allowed by the user", + "content" : { + "application/xml" : { "schema" : { - "type" : "integer", - "format" : "int32" + "type" : "string" } }, - "X-Expires-After" : { - "description" : "date in UTC when toekn expires", + "application/json" : { "schema" : { - "type" : "string", - "format" : "date-time" + "type" : "string" } } }, - "content" : { - "application/xml" : { + "description" : "successful operation", + "headers" : { + "X-Rate-Limit" : { + "description" : "calls per hour allowed by the user", "schema" : { - "type" : "string" + "format" : "int32", + "type" : "integer" } }, - "application/json" : { + "X-Expires-After" : { + "description" : "date in UTC when toekn expires", "schema" : { + "format" : "date-time", "type" : "string" } } } }, "400" : { - "description" : "Invalid username/password supplied", - "content" : { } + "content" : { }, + "description" : "Invalid username/password supplied" } - } + }, + "summary" : "Logs user into the system", + "tags" : [ "user" ] } }, "/user/logout" : { "get" : { - "tags" : [ "user" ], - "summary" : "Logs out current logged in user session", "operationId" : "logoutUser", "responses" : { "default" : { - "description" : "successful operation", - "content" : { } + "content" : { }, + "description" : "successful operation" } - } + }, + "summary" : "Logs out current logged in user session", + "tags" : [ "user" ] } }, "/user/{username}" : { + "delete" : { + "description" : "This can only be done by the logged in user.", + "operationId" : "deleteUser", + "parameters" : [ { + "description" : "The name that needs to be deleted", + "in" : "path", + "name" : "username", + "required" : true, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "400" : { + "content" : { }, + "description" : "Invalid username supplied" + }, + "404" : { + "content" : { }, + "description" : "User not found" + } + }, + "summary" : "Delete user", + "tags" : [ "user" ] + }, "get" : { - "tags" : [ "user" ], - "summary" : "Get user by user name", "operationId" : "getUserByName", "parameters" : [ { - "name" : "username", - "in" : "path", "description" : "The name that needs to be fetched. Use user1 for testing.", + "in" : "path", + "name" : "username", "required" : true, "schema" : { "type" : "string" @@ -680,7 +703,6 @@ } ], "responses" : { "200" : { - "description" : "successful operation", "content" : { "application/xml" : { "schema" : { @@ -692,34 +714,34 @@ "$ref" : "#/components/schemas/User" } } - } + }, + "description" : "successful operation" }, "400" : { - "description" : "Invalid username supplied", - "content" : { } + "content" : { }, + "description" : "Invalid username supplied" }, "404" : { - "description" : "User not found", - "content" : { } + "content" : { }, + "description" : "User not found" } - } + }, + "summary" : "Get user by user name", + "tags" : [ "user" ] }, "put" : { - "tags" : [ "user" ], - "summary" : "Updated user", "description" : "This can only be done by the logged in user.", "operationId" : "updateUser", "parameters" : [ { - "name" : "username", - "in" : "path", "description" : "name that need to be deleted", + "in" : "path", + "name" : "username", "required" : true, "schema" : { "type" : "string" } } ], "requestBody" : { - "description" : "Updated user object", "content" : { "*/*" : { "schema" : { @@ -727,120 +749,107 @@ } } }, + "description" : "Updated user object", "required" : true }, "responses" : { "400" : { - "description" : "Invalid user supplied", - "content" : { } + "content" : { }, + "description" : "Invalid user supplied" }, "404" : { - "description" : "User not found", - "content" : { } + "content" : { }, + "description" : "User not found" } }, - "x-codegen-request-body-name" : "body" - }, - "delete" : { + "summary" : "Updated user", "tags" : [ "user" ], - "summary" : "Delete user", - "description" : "This can only be done by the logged in user.", - "operationId" : "deleteUser", - "parameters" : [ { - "name" : "username", - "in" : "path", - "description" : "The name that needs to be deleted", - "required" : true, - "schema" : { - "type" : "string" - } - } ], - "responses" : { - "400" : { - "description" : "Invalid username supplied", - "content" : { } - }, - "404" : { - "description" : "User not found", - "content" : { } - } - } + "x-codegen-request-body-name" : "body" } } }, "components" : { "schemas" : { "Order" : { - "title" : "Pet Order", - "type" : "object", + "description" : "An order for a pets from the pet store", + "example" : { + "petId" : 6, + "quantity" : 1, + "id" : 0, + "shipDate" : "2000-01-23T04:56:07.000+00:00", + "complete" : false, + "status" : "placed" + }, "properties" : { "id" : { - "type" : "integer", - "format" : "int64" + "format" : "int64", + "type" : "integer" }, "petId" : { - "type" : "integer", - "format" : "int64" + "format" : "int64", + "type" : "integer" }, "quantity" : { - "type" : "integer", - "format" : "int32" + "format" : "int32", + "type" : "integer" }, "shipDate" : { - "type" : "string", - "format" : "date-time" + "format" : "date-time", + "type" : "string" }, "status" : { - "type" : "string", "description" : "Order Status", - "enum" : [ "placed", "approved", "delivered" ] + "enum" : [ "placed", "approved", "delivered" ], + "type" : "string" }, "complete" : { - "type" : "boolean", - "default" : false + "default" : false, + "type" : "boolean" } }, - "description" : "An order for a pets from the pet store", - "example" : { - "petId" : 6, - "quantity" : 1, - "id" : 0, - "shipDate" : "2000-01-23T04:56:07.000+00:00", - "complete" : false, - "status" : "placed" - }, + "title" : "Pet Order", + "type" : "object", "xml" : { "name" : "Order" } }, "Category" : { - "title" : "Pet category", - "type" : "object", + "description" : "A category for a pet", + "example" : { + "name" : "name", + "id" : 6 + }, "properties" : { "id" : { - "type" : "integer", - "format" : "int64" + "format" : "int64", + "type" : "integer" }, "name" : { "type" : "string" } }, - "description" : "A category for a pet", - "example" : { - "name" : "name", - "id" : 6 - }, + "title" : "Pet category", + "type" : "object", "xml" : { "name" : "Category" } }, "User" : { - "title" : "a User", - "type" : "object", + "description" : "A User who is purchasing from the pet store", + "example" : { + "firstName" : "firstName", + "lastName" : "lastName", + "password" : "password", + "userStatus" : 6, + "phone" : "phone", + "id" : 0, + "email" : "email", + "username" : "username" + }, "properties" : { "id" : { - "type" : "integer", - "format" : "int64" + "format" : "int64", + "type" : "integer" }, "username" : { "type" : "string" @@ -861,118 +870,113 @@ "type" : "string" }, "userStatus" : { - "type" : "integer", "description" : "User Status", - "format" : "int32" + "format" : "int32", + "type" : "integer" } }, - "description" : "A User who is purchasing from the pet store", - "example" : { - "firstName" : "firstName", - "lastName" : "lastName", - "password" : "password", - "userStatus" : 6, - "phone" : "phone", - "id" : 0, - "email" : "email", - "username" : "username" - }, + "title" : "a User", + "type" : "object", "xml" : { "name" : "User" } }, "Tag" : { - "title" : "Pet Tag", - "type" : "object", + "description" : "A tag for a pet", + "example" : { + "name" : "name", + "id" : 1 + }, "properties" : { "id" : { - "type" : "integer", - "format" : "int64" + "format" : "int64", + "type" : "integer" }, "name" : { "type" : "string" } }, - "description" : "A tag for a pet", - "example" : { - "name" : "name", - "id" : 1 - }, + "title" : "Pet Tag", + "type" : "object", "xml" : { "name" : "Tag" } }, "Pet" : { - "title" : "a Pet", - "required" : [ "name", "photoUrls" ], - "type" : "object", + "description" : "A pet for sale in the pet store", + "example" : { + "photoUrls" : [ "photoUrls", "photoUrls" ], + "name" : "doggie", + "id" : 0, + "category" : { + "name" : "name", + "id" : 6 + }, + "tags" : [ { + "name" : "name", + "id" : 1 + }, { + "name" : "name", + "id" : 1 + } ], + "status" : "available" + }, "properties" : { "id" : { - "type" : "integer", - "format" : "int64" + "format" : "int64", + "type" : "integer" }, "category" : { "$ref" : "#/components/schemas/Category" }, "name" : { - "type" : "string", - "example" : "doggie" + "example" : "doggie", + "type" : "string" }, "photoUrls" : { + "items" : { + "type" : "string" + }, "type" : "array", "xml" : { "name" : "photoUrl", "wrapped" : true - }, - "items" : { - "type" : "string" } }, "tags" : { + "items" : { + "$ref" : "#/components/schemas/Tag" + }, "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" ] + "enum" : [ "available", "pending", "sold" ], + "type" : "string" } }, - "description" : "A pet for sale in the pet store", - "example" : { - "photoUrls" : [ "photoUrls", "photoUrls" ], - "name" : "doggie", - "id" : 0, - "category" : { - "name" : "name", - "id" : 6 - }, - "tags" : [ { - "name" : "name", - "id" : 1 - }, { - "name" : "name", - "id" : 1 - } ], - "status" : "available" - }, + "required" : [ "name", "photoUrls" ], + "title" : "a Pet", + "type" : "object", "xml" : { "name" : "Pet" } }, "ApiResponse" : { - "title" : "An uploaded response", - "type" : "object", + "description" : "Describes the result of uploading an image resource", + "example" : { + "code" : 0, + "type" : "type", + "message" : "message" + }, "properties" : { "code" : { - "type" : "integer", - "format" : "int32" + "format" : "int32", + "type" : "integer" }, "type" : { "type" : "string" @@ -981,17 +985,12 @@ "type" : "string" } }, - "description" : "Describes the result of uploading an image resource", - "example" : { - "code" : 0, - "type" : "type", - "message" : "message" - } + "title" : "An uploaded response", + "type" : "object" } }, "securitySchemes" : { "petstore_auth" : { - "type" : "oauth2", "flows" : { "implicit" : { "authorizationUrl" : "http://petstore.swagger.io/api/oauth/dialog", @@ -1000,12 +999,13 @@ "read:pets" : "read your pets" } } - } + }, + "type" : "oauth2" }, "api_key" : { - "type" : "apiKey", + "in" : "header", "name" : "api_key", - "in" : "header" + "type" : "apiKey" } } } diff --git a/samples/server/petstore/rust-server/output/openapi-v3/README.md b/samples/server/petstore/rust-server/output/openapi-v3/README.md index 66109167f3b6..d51e1c48832c 100644 --- a/samples/server/petstore/rust-server/output/openapi-v3/README.md +++ b/samples/server/petstore/rust-server/output/openapi-v3/README.md @@ -61,6 +61,8 @@ cargo run --example server To run a client, follow one of the following simple steps: ``` +cargo run --example client MultipleAuthSchemeGet +cargo run --example client ReadonlyAuthSchemeGet cargo run --example client RequiredOctetStreamPut cargo run --example client ResponsesWithHeadersGet cargo run --example client UuidGet @@ -102,6 +104,8 @@ All URIs are relative to *http://localhost* Method | HTTP request | Description ------------- | ------------- | ------------- +[****](docs/default_api.md#) | **GET** /multiple_auth_scheme | +[****](docs/default_api.md#) | **GET** /readonly_auth_scheme | [****](docs/default_api.md#) | **PUT** /required_octet_stream | [****](docs/default_api.md#) | **GET** /responses_with_headers | [****](docs/default_api.md#) | **GET** /uuid | @@ -125,8 +129,22 @@ Method | HTTP request | Description ## Documentation For Authorization - Endpoints do not require authorization. +## authScheme +- **Type**: OAuth +- **Flow**: accessCode +- **Authorization URL**: http://example.org +- **Scopes**: + - **test.read**: Allowed to read state. + - **test.write**: Allowed to change state. + +Example +``` +``` + +Or via OAuth2 module to automatically refresh tokens and perform user authentication. +``` +``` ## Author diff --git a/samples/server/petstore/rust-server/output/openapi-v3/api/openapi.yaml b/samples/server/petstore/rust-server/output/openapi-v3/api/openapi.yaml index 552e1394d149..e903c1c2fe52 100644 --- a/samples/server/petstore/rust-server/output/openapi-v3/api/openapi.yaml +++ b/samples/server/petstore/rust-server/output/openapi-v3/api/openapi.yaml @@ -86,6 +86,23 @@ paths: responses: 200: description: OK + /readonly_auth_scheme: + get: + responses: + 200: + description: Check that limiting to a single required auth scheme works + security: + - authScheme: + - test.read + /multiple_auth_scheme: + get: + responses: + 200: + description: Check that limiting to multiple required auth schemes works + security: + - authScheme: + - test.read + - test.write /responses_with_headers: get: responses: @@ -176,4 +193,14 @@ components: xml: name: snake_another_xml_object namespace: http://foo.bar + securitySchemes: + authScheme: + flows: + authorizationCode: + authorizationUrl: http://example.org + scopes: + test.read: Allowed to read state. + test.write: Allowed to change state. + tokenUrl: http://example.org + type: oauth2 diff --git a/samples/server/petstore/rust-server/output/openapi-v3/docs/default_api.md b/samples/server/petstore/rust-server/output/openapi-v3/docs/default_api.md index d6115c696e8a..82b8c773ff40 100644 --- a/samples/server/petstore/rust-server/output/openapi-v3/docs/default_api.md +++ b/samples/server/petstore/rust-server/output/openapi-v3/docs/default_api.md @@ -4,6 +4,8 @@ All URIs are relative to *http://localhost* Method | HTTP request | Description ------------- | ------------- | ------------- +****](default_api.md#) | **GET** /multiple_auth_scheme | +****](default_api.md#) | **GET** /readonly_auth_scheme | ****](default_api.md#) | **PUT** /required_octet_stream | ****](default_api.md#) | **GET** /responses_with_headers | ****](default_api.md#) | **GET** /uuid | @@ -14,6 +16,50 @@ Method | HTTP request | Description ****](default_api.md#) | **PUT** /xml | +# **** +> (ctx, ) + + +### Required Parameters +This endpoint does not need any parameter. + +### Return type + + (empty response body) + +### Authorization + +[authScheme](../README.md#authScheme) + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **** +> (ctx, ) + + +### Required Parameters +This endpoint does not need any parameter. + +### Return type + + (empty response body) + +### Authorization + +[authScheme](../README.md#authScheme) + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + # **** > (body) diff --git a/samples/server/petstore/rust-server/output/openapi-v3/examples/client.rs b/samples/server/petstore/rust-server/output/openapi-v3/examples/client.rs index 6b4bbb646334..dcbe006993af 100644 --- a/samples/server/petstore/rust-server/output/openapi-v3/examples/client.rs +++ b/samples/server/petstore/rust-server/output/openapi-v3/examples/client.rs @@ -19,6 +19,8 @@ use tokio_core::reactor; #[allow(unused_imports)] use openapi_v3::{ApiNoContext, ContextWrapperExt, ApiError, + MultipleAuthSchemeGetResponse, + ReadonlyAuthSchemeGetResponse, RequiredOctetStreamPutResponse, ResponsesWithHeadersGetResponse, UuidGetResponse, @@ -35,6 +37,8 @@ fn main() { .arg(Arg::with_name("operation") .help("Sets the operation to run") .possible_values(&[ + "MultipleAuthSchemeGet", + "ReadonlyAuthSchemeGet", "RequiredOctetStreamPut", "ResponsesWithHeadersGet", "UuidGet", @@ -83,6 +87,16 @@ fn main() { match matches.value_of("operation") { + Some("MultipleAuthSchemeGet") => { + let result = core.run(client.multiple_auth_scheme_get()); + println!("{:?} (X-Span-ID: {:?})", result, (client.context() as &Has).get().clone()); + }, + + Some("ReadonlyAuthSchemeGet") => { + let result = core.run(client.readonly_auth_scheme_get()); + println!("{:?} (X-Span-ID: {:?})", result, (client.context() as &Has).get().clone()); + }, + Some("RequiredOctetStreamPut") => { let result = core.run(client.required_octet_stream_put(swagger::ByteArray(Vec::from("BYTE_ARRAY_DATA_HERE")))); println!("{:?} (X-Span-ID: {:?})", result, (client.context() as &Has).get().clone()); diff --git a/samples/server/petstore/rust-server/output/openapi-v3/examples/server_lib/mod.rs b/samples/server/petstore/rust-server/output/openapi-v3/examples/server_lib/mod.rs index ad8904ca16bd..2139af63155f 100644 --- a/samples/server/petstore/rust-server/output/openapi-v3/examples/server_lib/mod.rs +++ b/samples/server/petstore/rust-server/output/openapi-v3/examples/server_lib/mod.rs @@ -13,6 +13,7 @@ use std::marker::PhantomData; use hyper; use openapi_v3; use swagger::{Has, XSpanIdString}; +use swagger::auth::Authorization; pub struct NewService{ marker: PhantomData @@ -24,7 +25,7 @@ impl NewService{ } } -impl hyper::server::NewService for NewService where C: Has + Clone + 'static { +impl hyper::server::NewService for NewService where C: Has + Has> + Clone + 'static { type Request = (hyper::Request, C); type Response = hyper::Response; type Error = hyper::Error; diff --git a/samples/server/petstore/rust-server/output/openapi-v3/examples/server_lib/server.rs b/samples/server/petstore/rust-server/output/openapi-v3/examples/server_lib/server.rs index a66ac4e58d77..56c760181776 100644 --- a/samples/server/petstore/rust-server/output/openapi-v3/examples/server_lib/server.rs +++ b/samples/server/petstore/rust-server/output/openapi-v3/examples/server_lib/server.rs @@ -11,6 +11,8 @@ use swagger::{Has, XSpanIdString}; use uuid; use openapi_v3::{Api, ApiError, + MultipleAuthSchemeGetResponse, + ReadonlyAuthSchemeGetResponse, RequiredOctetStreamPutResponse, ResponsesWithHeadersGetResponse, UuidGetResponse, @@ -36,6 +38,20 @@ impl Server { impl Api for Server where C: Has{ + fn multiple_auth_scheme_get(&self, context: &C) -> Box> { + let context = context.clone(); + println!("multiple_auth_scheme_get() - X-Span-ID: {:?}", context.get().0.clone()); + Box::new(futures::failed("Generic failure".into())) + } + + + fn readonly_auth_scheme_get(&self, context: &C) -> Box> { + let context = context.clone(); + println!("readonly_auth_scheme_get() - X-Span-ID: {:?}", context.get().0.clone()); + Box::new(futures::failed("Generic failure".into())) + } + + fn required_octet_stream_put(&self, body: swagger::ByteArray, context: &C) -> Box> { let context = context.clone(); println!("required_octet_stream_put({:?}) - X-Span-ID: {:?}", body, context.get().0.clone()); diff --git a/samples/server/petstore/rust-server/output/openapi-v3/src/client/mod.rs b/samples/server/petstore/rust-server/output/openapi-v3/src/client/mod.rs index 3537ffbd9085..b67f0353b297 100644 --- a/samples/server/petstore/rust-server/output/openapi-v3/src/client/mod.rs +++ b/samples/server/petstore/rust-server/output/openapi-v3/src/client/mod.rs @@ -37,6 +37,8 @@ use swagger; use swagger::{ApiError, XSpanId, XSpanIdString, Has, AuthData}; use {Api, + MultipleAuthSchemeGetResponse, + ReadonlyAuthSchemeGetResponse, RequiredOctetStreamPutResponse, ResponsesWithHeadersGetResponse, UuidGetResponse, @@ -248,7 +250,153 @@ impl Client where impl Api for Client where F: Future + 'static, - C: Has { + C: Has + Has>{ + + fn multiple_auth_scheme_get(&self, context: &C) -> Box> { + let mut uri = format!( + "{}/multiple_auth_scheme", + self.base_path + ); + + let mut query_string = self::url::form_urlencoded::Serializer::new("".to_owned()); + + + let query_string_str = query_string.finish(); + if !query_string_str.is_empty() { + uri += "?"; + uri += &query_string_str; + } + + let uri = match Uri::from_str(&uri) { + Ok(uri) => uri, + Err(err) => return Box::new(futures::done(Err(ApiError(format!("Unable to build URI: {}", err))))), + }; + + let mut request = hyper::Request::new(hyper::Method::Get, uri); + + + request.headers_mut().set(XSpanId((context as &Has).get().0.clone())); + + (context as &Has>).get().as_ref().map(|auth_data| { + // Currently only authentication with Basic, API Key, and Bearer are supported + match auth_data { + &AuthData::Bearer(ref bearer_header) => { + request.headers_mut().set(hyper::header::Authorization( + bearer_header.clone(), + )) + }, + _ => {} + } + }); + Box::new(self.client_service.call(request) + .map_err(|e| ApiError(format!("No response received: {}", e))) + .and_then(|mut response| { + match response.status().as_u16() { + 200 => { + let body = response.body(); + Box::new( + + future::ok( + MultipleAuthSchemeGetResponse::CheckThatLimitingToMultipleRequiredAuthSchemesWorks + ) + ) as Box> + }, + code => { + let headers = response.headers().clone(); + Box::new(response.body() + .take(100) + .concat2() + .then(move |body| + future::err(ApiError(format!("Unexpected response code {}:\n{:?}\n\n{}", + code, + headers, + match body { + Ok(ref body) => match str::from_utf8(body) { + Ok(body) => Cow::from(body), + Err(e) => Cow::from(format!("", e)), + }, + Err(e) => Cow::from(format!("", e)), + }))) + ) + ) as Box> + } + } + })) + + } + + fn readonly_auth_scheme_get(&self, context: &C) -> Box> { + let mut uri = format!( + "{}/readonly_auth_scheme", + self.base_path + ); + + let mut query_string = self::url::form_urlencoded::Serializer::new("".to_owned()); + + + let query_string_str = query_string.finish(); + if !query_string_str.is_empty() { + uri += "?"; + uri += &query_string_str; + } + + let uri = match Uri::from_str(&uri) { + Ok(uri) => uri, + Err(err) => return Box::new(futures::done(Err(ApiError(format!("Unable to build URI: {}", err))))), + }; + + let mut request = hyper::Request::new(hyper::Method::Get, uri); + + + request.headers_mut().set(XSpanId((context as &Has).get().0.clone())); + + (context as &Has>).get().as_ref().map(|auth_data| { + // Currently only authentication with Basic, API Key, and Bearer are supported + match auth_data { + &AuthData::Bearer(ref bearer_header) => { + request.headers_mut().set(hyper::header::Authorization( + bearer_header.clone(), + )) + }, + _ => {} + } + }); + Box::new(self.client_service.call(request) + .map_err(|e| ApiError(format!("No response received: {}", e))) + .and_then(|mut response| { + match response.status().as_u16() { + 200 => { + let body = response.body(); + Box::new( + + future::ok( + ReadonlyAuthSchemeGetResponse::CheckThatLimitingToASingleRequiredAuthSchemeWorks + ) + ) as Box> + }, + code => { + let headers = response.headers().clone(); + Box::new(response.body() + .take(100) + .concat2() + .then(move |body| + future::err(ApiError(format!("Unexpected response code {}:\n{:?}\n\n{}", + code, + headers, + match body { + Ok(ref body) => match str::from_utf8(body) { + Ok(body) => Cow::from(body), + Err(e) => Cow::from(format!("", e)), + }, + Err(e) => Cow::from(format!("", e)), + }))) + ) + ) as Box> + } + } + })) + + } fn required_octet_stream_put(&self, param_body: swagger::ByteArray, context: &C) -> Box> { let mut uri = format!( @@ -272,7 +420,6 @@ impl Api for Client where let mut request = hyper::Request::new(hyper::Method::Put, uri); - // Body parameter let body = param_body.0; request.set_body(body); diff --git a/samples/server/petstore/rust-server/output/openapi-v3/src/lib.rs b/samples/server/petstore/rust-server/output/openapi-v3/src/lib.rs index 0ccacdce2fcd..255d779db328 100644 --- a/samples/server/petstore/rust-server/output/openapi-v3/src/lib.rs +++ b/samples/server/petstore/rust-server/output/openapi-v3/src/lib.rs @@ -51,6 +51,18 @@ pub const BASE_PATH: &'static str = ""; pub const API_VERSION: &'static str = "1.0.7"; +#[derive(Debug, PartialEq)] +pub enum MultipleAuthSchemeGetResponse { + /// Check that limiting to multiple required auth schemes works + CheckThatLimitingToMultipleRequiredAuthSchemesWorks +} + +#[derive(Debug, PartialEq)] +pub enum ReadonlyAuthSchemeGetResponse { + /// Check that limiting to a single required auth scheme works + CheckThatLimitingToASingleRequiredAuthSchemeWorks +} + #[derive(Debug, PartialEq)] pub enum RequiredOctetStreamPutResponse { /// OK @@ -131,6 +143,12 @@ pub enum XmlPutResponse { pub trait Api { + fn multiple_auth_scheme_get(&self, context: &C) -> Box>; + + + fn readonly_auth_scheme_get(&self, context: &C) -> Box>; + + fn required_octet_stream_put(&self, body: swagger::ByteArray, context: &C) -> Box>; @@ -160,6 +178,12 @@ pub trait Api { pub trait ApiNoContext { + fn multiple_auth_scheme_get(&self) -> Box>; + + + fn readonly_auth_scheme_get(&self) -> Box>; + + fn required_octet_stream_put(&self, body: swagger::ByteArray) -> Box>; @@ -200,6 +224,16 @@ impl<'a, T: Api + Sized, C> ContextWrapperExt<'a, C> for T { impl<'a, T: Api, C> ApiNoContext for ContextWrapper<'a, T, C> { + fn multiple_auth_scheme_get(&self) -> Box> { + self.api().multiple_auth_scheme_get(&self.context()) + } + + + fn readonly_auth_scheme_get(&self) -> Box> { + self.api().readonly_auth_scheme_get(&self.context()) + } + + fn required_octet_stream_put(&self, body: swagger::ByteArray) -> Box> { self.api().required_octet_stream_put(body, &self.context()) } diff --git a/samples/server/petstore/rust-server/output/openapi-v3/src/server/context.rs b/samples/server/petstore/rust-server/output/openapi-v3/src/server/context.rs index 6f2900b3d70c..a377b535b623 100644 --- a/samples/server/petstore/rust-server/output/openapi-v3/src/server/context.rs +++ b/samples/server/petstore/rust-server/output/openapi-v3/src/server/context.rs @@ -83,6 +83,16 @@ impl hyper::server::Service for AddContext fn call(&self, req: Self::Request) -> Self::Future { let context = A::default().push(XSpanIdString::get_or_generate(&req)); + { + use hyper::header::{Authorization as HyperAuth, Basic, Bearer}; + use std::ops::Deref; + if let Some(bearer) = req.headers().get::>().cloned() { + let auth_data = AuthData::Bearer(bearer.deref().clone()); + let context = context.push(Some(auth_data)); + let context = context.push(None::); + return self.inner.call((req, context)); + } + } let context = context.push(None::); let context = context.push(None::); diff --git a/samples/server/petstore/rust-server/output/openapi-v3/src/server/mod.rs b/samples/server/petstore/rust-server/output/openapi-v3/src/server/mod.rs index 72923bf8ee8c..2298f08c7076 100644 --- a/samples/server/petstore/rust-server/output/openapi-v3/src/server/mod.rs +++ b/samples/server/petstore/rust-server/output/openapi-v3/src/server/mod.rs @@ -35,6 +35,8 @@ use swagger::{ApiError, XSpanId, XSpanIdString, Has, RequestParser}; use swagger::auth::Scopes; use {Api, + MultipleAuthSchemeGetResponse, + ReadonlyAuthSchemeGetResponse, RequiredOctetStreamPutResponse, ResponsesWithHeadersGetResponse, UuidGetResponse, @@ -56,6 +58,8 @@ mod paths { lazy_static! { pub static ref GLOBAL_REGEX_SET: regex::RegexSet = regex::RegexSet::new(vec![ + r"^/multiple_auth_scheme$", + r"^/readonly_auth_scheme$", r"^/required_octet_stream$", r"^/responses_with_headers$", r"^/uuid$", @@ -64,12 +68,14 @@ mod paths { r"^/xml_other$" ]).unwrap(); } - pub static ID_REQUIRED_OCTET_STREAM: usize = 0; - pub static ID_RESPONSES_WITH_HEADERS: usize = 1; - pub static ID_UUID: usize = 2; - pub static ID_XML: usize = 3; - pub static ID_XML_EXTRA: usize = 4; - pub static ID_XML_OTHER: usize = 5; + pub static ID_MULTIPLE_AUTH_SCHEME: usize = 0; + pub static ID_READONLY_AUTH_SCHEME: usize = 1; + pub static ID_REQUIRED_OCTET_STREAM: usize = 2; + pub static ID_RESPONSES_WITH_HEADERS: usize = 3; + pub static ID_UUID: usize = 4; + pub static ID_XML: usize = 5; + pub static ID_XML_EXTRA: usize = 6; + pub static ID_XML_OTHER: usize = 7; } pub struct NewService { @@ -80,7 +86,7 @@ pub struct NewService { impl NewService where T: Api + Clone + 'static, - C: Has + 'static + C: Has + Has> + 'static { pub fn new>>(api_impl: U) -> NewService { NewService{api_impl: api_impl.into(), marker: PhantomData} @@ -90,7 +96,7 @@ where impl hyper::server::NewService for NewService where T: Api + Clone + 'static, - C: Has + 'static + C: Has + Has> + 'static { type Request = (Request, C); type Response = Response; @@ -110,7 +116,7 @@ pub struct Service { impl Service where T: Api + Clone + 'static, - C: Has + 'static { + C: Has + Has> + 'static { pub fn new>>(api_impl: U) -> Service { Service{api_impl: api_impl.into(), marker: PhantomData} } @@ -119,7 +125,7 @@ where impl hyper::server::Service for Service where T: Api + Clone + 'static, - C: Has + 'static + C: Has + Has> + 'static { type Request = (Request, C); type Response = Response; @@ -135,6 +141,127 @@ where // Please update both places if changing how this code is autogenerated. match &method { + // MultipleAuthSchemeGet - GET /multiple_auth_scheme + &hyper::Method::Get if path.matched(paths::ID_MULTIPLE_AUTH_SCHEME) => { + { + let authorization = match (&context as &Has>).get() { + &Some(ref authorization) => authorization, + &None => return Box::new(future::ok(Response::new() + .with_status(StatusCode::Forbidden) + .with_body("Unauthenticated"))), + }; + + // Authorization + if let Scopes::Some(ref scopes) = authorization.scopes { + let required_scopes: BTreeSet = vec![ + "test.read".to_string(), // Allowed to read state. + "test.write".to_string(), // Allowed to change state. + ].into_iter().collect(); + + if !required_scopes.is_subset(scopes) { + let missing_scopes = required_scopes.difference(scopes); + return Box::new(future::ok(Response::new() + .with_status(StatusCode::Forbidden) + .with_body(missing_scopes.fold( + "Insufficient authorization, missing scopes".to_string(), + |s, scope| format!("{} {}", s, scope) + )) + )); + } + } + } + Box::new({ + {{ + Box::new(api_impl.multiple_auth_scheme_get(&context) + .then(move |result| { + let mut response = Response::new(); + response.headers_mut().set(XSpanId((&context as &Has).get().0.to_string())); + + match result { + Ok(rsp) => match rsp { + MultipleAuthSchemeGetResponse::CheckThatLimitingToMultipleRequiredAuthSchemesWorks + + + => { + response.set_status(StatusCode::try_from(200).unwrap()); + + }, + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.set_status(StatusCode::InternalServerError); + response.set_body("An internal error occurred"); + }, + } + + future::ok(response) + } + )) + }} + }) as Box> + }, + + // ReadonlyAuthSchemeGet - GET /readonly_auth_scheme + &hyper::Method::Get if path.matched(paths::ID_READONLY_AUTH_SCHEME) => { + { + let authorization = match (&context as &Has>).get() { + &Some(ref authorization) => authorization, + &None => return Box::new(future::ok(Response::new() + .with_status(StatusCode::Forbidden) + .with_body("Unauthenticated"))), + }; + + // Authorization + if let Scopes::Some(ref scopes) = authorization.scopes { + let required_scopes: BTreeSet = vec![ + "test.read".to_string(), // Allowed to read state. + ].into_iter().collect(); + + if !required_scopes.is_subset(scopes) { + let missing_scopes = required_scopes.difference(scopes); + return Box::new(future::ok(Response::new() + .with_status(StatusCode::Forbidden) + .with_body(missing_scopes.fold( + "Insufficient authorization, missing scopes".to_string(), + |s, scope| format!("{} {}", s, scope) + )) + )); + } + } + } + Box::new({ + {{ + Box::new(api_impl.readonly_auth_scheme_get(&context) + .then(move |result| { + let mut response = Response::new(); + response.headers_mut().set(XSpanId((&context as &Has).get().0.to_string())); + + match result { + Ok(rsp) => match rsp { + ReadonlyAuthSchemeGetResponse::CheckThatLimitingToASingleRequiredAuthSchemeWorks + + + => { + response.set_status(StatusCode::try_from(200).unwrap()); + + }, + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.set_status(StatusCode::InternalServerError); + response.set_body("An internal error occurred"); + }, + } + + future::ok(response) + } + )) + }} + }) as Box> + }, + // RequiredOctetStreamPut - PUT /required_octet_stream &hyper::Method::Put if path.matched(paths::ID_REQUIRED_OCTET_STREAM) => { // Body parameters (note that non-required body parameters will ignore garbage @@ -644,6 +771,12 @@ impl RequestParser for ApiRequestParser { let path = paths::GLOBAL_REGEX_SET.matches(request.uri().path()); match request.method() { + // MultipleAuthSchemeGet - GET /multiple_auth_scheme + &hyper::Method::Get if path.matched(paths::ID_MULTIPLE_AUTH_SCHEME) => Ok("MultipleAuthSchemeGet"), + + // ReadonlyAuthSchemeGet - GET /readonly_auth_scheme + &hyper::Method::Get if path.matched(paths::ID_READONLY_AUTH_SCHEME) => Ok("ReadonlyAuthSchemeGet"), + // RequiredOctetStreamPut - PUT /required_octet_stream &hyper::Method::Put if path.matched(paths::ID_REQUIRED_OCTET_STREAM) => Ok("RequiredOctetStreamPut"),