-
Notifications
You must be signed in to change notification settings - Fork 92
initial work on security schema (only jwt bearer) #143
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 12 commits
5ffec5c
ef238a1
57c9be1
eb9c334
93e0732
a8edef7
6b7b75a
2cb7f30
fa56cee
fea000d
8a778a8
f48001b
3c7cf65
3ff37b3
616f0bc
89b7c08
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,11 +5,11 @@ class << self | |
| # @param [OpenAPIParser::Config] config | ||
| # @param [OpenAPIParser::PathItemFinder] path_item_finder | ||
| # @return [OpenAPIParser::RequestOperation, nil] | ||
| def create(http_method, request_path, path_item_finder, config) | ||
| def create(http_method, request_path, path_item_finder, config, security_schemes = {}) | ||
| result = path_item_finder.operation_object(http_method, request_path) | ||
| return nil unless result | ||
|
|
||
| self.new(http_method, result, config) | ||
| self.new(http_method, result, config, security_schemes) | ||
| end | ||
| end | ||
|
|
||
|
|
@@ -25,18 +25,23 @@ def create(http_method, request_path, path_item_finder, config) | |
| # @return [String] | ||
| # @!attribute [r] path_item | ||
| # @return [OpenAPIParser::Schemas::PathItem] | ||
| attr_reader :operation_object, :path_params, :config, :http_method, :original_path, :path_item | ||
| attr_reader :operation_object, :path_params, :config, :http_method, :original_path, :path_item, :security_schemes | ||
|
|
||
| # @param [String] http_method | ||
| # @param [OpenAPIParser::PathItemFinder::Result] result | ||
| # @param [OpenAPIParser::Config] config | ||
| def initialize(http_method, result, config) | ||
| def initialize(http_method, result, config, security_schemes) | ||
| @http_method = http_method.to_s | ||
| @original_path = result.original_path | ||
| @operation_object = result.operation_object | ||
| @path_params = result.path_params || {} | ||
| @path_item = result.path_item_object | ||
| @config = config | ||
| @security_schemes = security_schemes | ||
| end | ||
|
|
||
| def validate_security(security_schemes, headers) | ||
| operation_object.validate_security_schemes(security_schemes, headers) | ||
| end | ||
|
|
||
| def validate_path_params(options = nil) | ||
|
|
@@ -65,6 +70,7 @@ def validate_response_body(response_body, response_validate_options = nil) | |
| # @param [OpenAPIParser::SchemaValidator::Options] options request validator options | ||
| def validate_request_parameter(params, headers, options = nil) | ||
| options ||= config.request_validator_options | ||
| validate_security(security_schemes, headers) | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Now users are using code under the assumption that this validation will not take place, and it may suddenly break at update time. |
||
| operation_object&.validate_request_parameter(params, headers, options) | ||
| end | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,10 +7,10 @@ module OpenAPIParser::Schemas | |
| class Operation < Base | ||
| include OpenAPIParser::ParameterValidatable | ||
|
|
||
| openapi_attr_values :tags, :summary, :description, :deprecated | ||
| openapi_attr_values :tags, :summary, :description, :deprecated, :security | ||
|
|
||
| openapi_attr_value :operation_id, schema_key: :operationId | ||
|
|
||
| openapi_attr_list_object :parameters, Parameter, reference: true | ||
|
|
||
| # @!attribute [r] request_body | ||
|
|
@@ -30,5 +30,21 @@ def validate_request_body(content_type, params, options) | |
| def validate_response(response_body, response_validate_options) | ||
| responses&.validate(response_body, response_validate_options) | ||
| end | ||
|
|
||
| def validate_security_schemes(securitySchemes, headers) | ||
| securitySchemes&.each do |securityScheme| | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We must checks for a Security Scheme Object with a corresponding name if there is a SecurityRequriment Object so we don't need check all securitySchemes. def validate_security_schemes(securitySchemes, headers)
validate_results = security.map do |s| # s is security requirement object
# check all security
s.validate_security_schemes(securitySchemes, headers)
end
if validate_results.count(true) == 1
return true # accept
end
return ValidateSecurityError()
endI have determined that the validate passes if only one security scheme is valid, and if two or more are valid, the validate does not pass.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @ota42y So this code below cannot be used in # this code cannot be used in security.rb (security requirement object)
def validate_security_schemes(securityScheme, headers)
if self.type == "http" && self.scheme == "bearer" && self.bearer_format == "JWT"
end
end
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @ota42y Hi. any updates? I'm thinking of doing my own fork because i cannot find the solution with: def validate_security_schemes(securitySchemes, headers)
validate_results = security.map do |s| # s is security requirement object
# check all security
s.validate_security_schemes(securitySchemes, headers) <-- this needs to be object, cannot use "self" with `securitySchemes
end
if validate_results.count(true) == 1
return true # accept
end
return ValidateSecurityError()
end
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You are right. I confused security scheme object and secuirty requirement object 🙇 |
||
| # check if the endpoint has security in schema | ||
| if security | ||
| # if security exists, check what securitySchemas used for enforcing | ||
| security.each do |s| | ||
| # securityScheme[0] is the securitySchema name | ||
| if s == securityScheme[0] | ||
| # securitySchema[1] is the values like "type", "scheme" and bearerFormat | ||
| securityScheme[1].validate_security_schemes(securityScheme[1], headers) | ||
| end | ||
| end | ||
| end | ||
| end | ||
| end | ||
| end | ||
| end | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| require 'jwt' | ||
|
muhammadn marked this conversation as resolved.
|
||
| module OpenAPIParser::Schemas | ||
| class SecuritySchemes < Base | ||
|
|
||
| openapi_attr_values :type, :description, :scheme | ||
| openapi_attr_value :bearer_format, schema_key: :bearerFormat | ||
|
|
||
| def validate_security_schemes(securityScheme, headers) | ||
| if self.type == "http" && self.scheme == "bearer" && self.bearer_format == "JWT" | ||
| raise "need authorization" unless headers["AUTHORIZATION"] | ||
| raise "not bearer" unless headers["AUTHORIZATION"].split[0] == "Bearer" | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please define new error class. |
||
|
|
||
| # check if the JWT token is being sent and try to decode. | ||
| # if JWT token does not exist or token cannot decode, then deny access | ||
| token = headers["AUTHORIZATION"].split[1] | ||
| value = JWT.decode token, nil, false | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How much detail to verify here is a difficult question. One solution would be to have the user pass in a callback what validation is to be performed, and this library would only call the callback. In this case, instead of limiting the bearerFormat=JWT, I think it would be possible to pass a Security Scheme Object to the callback so that users can validate non-JWT formats as well, thus allowing for a wider range of applications. |
||
| end | ||
| end | ||
| end | ||
| end | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The OpenAPI Specification define that it is OK if either one of OpenAPI Object or Operation Object's security requirement is satisfied, so need to check both security fields.
https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#securityRequirementObject
Now RequestOperation Object doesn't have OpenAPI's security field so we need pass it too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it is good to create validator which require Security Scheme Object and Security Requriement Object and validate seurity schemes specified by requirement objects for validating OpenAPI Object and Operation Object.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ota42y I don't understand this part:
I think it is good to create validator which require Security Scheme Object and Security Requriement ObjectThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you are adding the validation method to the Security Requriement Object, that's fine. I was thinking of using Security Scheme Object for validation and thought that would be a problem. Ignore it.