#1043 - [Ada] Operation security scopes are ignored when generating the server (#1044)

* [Ada] Operation security scopes are ignored when generating the server (#1043)
- Update fromOperation() to keep the operation required scopes for each auth
  method and store that information in the x-scopes vendor extensions attribute
- Update postProcessOperationsWithModels() to process the operation required
  scopes and build a list of authMethods which only contain the required scopes
  for the operation and store these authMethods in the x-auth-scopes attribute.
- Update postProcessAuthMethod() to handle the logic of filtering and building
  the operation authMethod (new instances are created because we must not
  modify the global authMethod definitions)
- Update the Ada server templates to use the x-auth-scopes instead of authMethods
  Add a URL prefix parameter for the Ada server instantiation

* Fix the Ada server template to use the x-auth-scopes for operation scopes
This commit is contained in:
Stephane Carrez 2018-09-20 13:31:11 +02:00 committed by William Cheng
parent 0e045bee1b
commit 3cacbcb965
3 changed files with 83 additions and 9 deletions

View File

@ -24,6 +24,8 @@ import io.swagger.v3.oas.models.Operation;
import io.swagger.v3.oas.models.media.ArraySchema;
import io.swagger.v3.oas.models.media.Schema;
import io.swagger.v3.oas.models.responses.ApiResponse;
import io.swagger.v3.oas.models.security.SecurityRequirement;
import io.swagger.v3.oas.models.security.SecurityScheme;
import org.openapitools.codegen.CodegenConfig;
import org.openapitools.codegen.CodegenConstants;
import org.openapitools.codegen.CodegenModel;
@ -414,9 +416,38 @@ abstract public class AbstractAdaCodegen extends DefaultCodegen implements Codeg
}
}
}
// Add a vendor extension attribute that provides a map of auth methods and the scopes
// which are expected by the operation. This map is then used by postProcessOperationsWithModels
// to build another vendor extension that provides a subset of the auth methods with only
// the scopes required by the operation.
final List<SecurityRequirement> securities = operation.getSecurity();
if (securities != null && securities.size() > 0) {
final Map<String, SecurityScheme> securitySchemes = openAPI.getComponents() != null ? openAPI.getComponents().getSecuritySchemes() : null;
final List<SecurityRequirement> globalSecurities = openAPI.getSecurity();
Map<String, List<String>> scopes = getAuthScopes(securities, securitySchemes);
if (scopes.isEmpty() && globalSecurities != null) {
scopes = getAuthScopes(globalSecurities, securitySchemes);
}
op.vendorExtensions.put("x-scopes", scopes);
}
return op;
}
private Map<String, List<String>> getAuthScopes(List<SecurityRequirement> securities, Map<String, SecurityScheme> securitySchemes) {
final Map<String, List<String>> scopes = new HashMap<>();
for (SecurityRequirement requirement : securities) {
for (String key : requirement.keySet()) {
SecurityScheme securityScheme = securitySchemes.get(key);
if (securityScheme != null) {
scopes.put(key, requirement.get(key));
}
}
}
return scopes;
}
@SuppressWarnings("unchecked")
@Override
public Map<String, Object> postProcessOperationsWithModels(Map<String, Object> objs, List<Object> allModels) {
@ -449,7 +480,14 @@ abstract public class AbstractAdaCodegen extends DefaultCodegen implements Codeg
p.dataType = "Swagger.File_Part_Type";
}
}
postProcessAuthMethod(op1.authMethods);
// Given the operation scopes and the auth methods, build a list of auth methods that only
// describe the auth methods and scopes required by the operation.
final Map<String, List<String>> scopes = (Map<String, List<String>>) op1.vendorExtensions.get("x-scopes");
List<CodegenSecurity> opScopes = postProcessAuthMethod(op1.authMethods, scopes);
if (opScopes != null) {
op1.vendorExtensions.put("x-auth-scopes", opScopes);
}
/*
* Scan the path parameter to construct a x-path-index that tells the index of
@ -584,7 +622,7 @@ abstract public class AbstractAdaCodegen extends DefaultCodegen implements Codeg
* Collect the scopes to generate unique identifiers for each of them.
*/
List<CodegenSecurity> authMethods = (List<CodegenSecurity>) objs.get("authMethods");
postProcessAuthMethod(authMethods);
postProcessAuthMethod(authMethods, null);
return super.postProcessSupportingFileData(objs);
}
@ -593,8 +631,11 @@ abstract public class AbstractAdaCodegen extends DefaultCodegen implements Codeg
* Collect the scopes to generate a unique identifier for each of them.
*
* @param authMethods the auth methods with their scopes.
* @param scopes the optional auth methods and scopes required by an operation
* @return the authMethods to be used by the operation with its required scopes.
*/
private void postProcessAuthMethod(List<CodegenSecurity> authMethods) {
private List<CodegenSecurity> postProcessAuthMethod(List<CodegenSecurity> authMethods, Map<String, List<String>> scopes) {
List<CodegenSecurity> result = (scopes == null) ? null : new ArrayList<CodegenSecurity>();
if (authMethods != null) {
for (CodegenSecurity authMethod : authMethods) {
if (authMethod.scopes != null) {
@ -620,8 +661,39 @@ abstract public class AbstractAdaCodegen extends DefaultCodegen implements Codeg
}
}
}
// If we have operation scopes, filter the auth method to describe the operation auth
// method with only the scope that it requires. We have to create a new auth method
// instance because the original object must not be modified.
List<String> opScopes = (scopes == null) ? null : scopes.get(authMethod.name);
authMethod.name = org.openapitools.codegen.utils.StringUtils.camelize(sanitizeName(authMethod.name), true);
if (opScopes != null) {
CodegenSecurity opSecurity = new CodegenSecurity();
opSecurity.name = authMethod.name;
opSecurity.type = authMethod.type;
opSecurity.hasMore = false;
opSecurity.isBasic = authMethod.isBasic;
opSecurity.isApiKey = authMethod.isApiKey;
opSecurity.isKeyInCookie = authMethod.isKeyInCookie;
opSecurity.isKeyInHeader = authMethod.isKeyInHeader;
opSecurity.isKeyInQuery = authMethod.isKeyInQuery;
opSecurity.flow = authMethod.flow;
opSecurity.tokenUrl = authMethod.tokenUrl;
List<Map<String, Object>> opAuthScopes = new ArrayList<Map<String, Object>>();
for (String opScopeName : opScopes) {
for (Map<String, Object> scope : authMethod.scopes) {
String name = (String) scope.get("scope");
if (opScopeName.equals(name)) {
opAuthScopes.add(scope);
break;
}
}
}
opSecurity.scopes = opAuthScopes;
result.add(opSecurity);
}
}
}
return result;
}
}

View File

@ -13,7 +13,7 @@ package body {{package}}.Skeletons is
package API_{{operationId}} is
new Swagger.Servers.Operation (Handler => {{operationId}},
Method => Swagger.Servers.{{httpMethod}},
URI => "{{path}}");
URI => URI_Prefix & "{{path}}");
-- {{summary}}
procedure {{operationId}}
@ -32,7 +32,7 @@ package body {{package}}.Skeletons is
Result : {{returnType}};
{{/returnType}}
begin
{{#authMethods}}
{{#vendorExtensions.x-auth-scopes}}
if not Context.Is_Authenticated then
Context.Set_Error (401, "Not authenticated");
return;
@ -43,7 +43,7 @@ package body {{package}}.Skeletons is
return;
end if;
{{/scopes}}
{{/authMethods}}
{{/vendorExtensions.x-auth-scopes}}
{{#queryParams}}
Swagger.Servers.Get_Query_Parameter (Req, "{{baseName}}", {{paramName}});
{{/queryParams}}
@ -128,7 +128,7 @@ package body {{package}}.Skeletons is
Result : {{returnType}};
{{/returnType}}
begin
{{#authMethods}}
{{#vendorExtensions.x-auth-scopes}}
if not Context.Is_Authenticated then
Context.Set_Error (401, "Not authenticated");
return;
@ -139,7 +139,7 @@ package body {{package}}.Skeletons is
return;
end if;
{{/scopes}}
{{/authMethods}}
{{/vendorExtensions.x-auth-scopes}}
{{#queryParams}}
Swagger.Servers.Get_Query_Parameter (Req, "{{baseName}}", {{paramName}});
{{/queryParams}}
@ -185,7 +185,7 @@ package body {{package}}.Skeletons is
package API_{{operationId}} is
new Swagger.Servers.Operation (Handler => {{operationId}},
Method => Swagger.Servers.{{httpMethod}},
URI => "{{path}}");
URI => URI_Prefix & "{{path}}");
{{/operation}}
{{/operations}}
{{/apis}}

View File

@ -32,6 +32,7 @@ package {{package}}.Skeletons is
generic
type Implementation_Type is limited new Server_Type with private;
URI_Prefix : String := "";
package Skeleton is
procedure Register (Server : in out Swagger.Servers.Application_Type'Class);
@ -56,6 +57,7 @@ package {{package}}.Skeletons is
generic
type Implementation_Type is limited new Server_Type with private;
URI_Prefix : String := "";
package Shared_Instance is
procedure Register (Server : in out Swagger.Servers.Application_Type'Class);