Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #112 from macisamuele/security-objects-handling
Security Objects handling and parameter validation
- Loading branch information
Showing
8 changed files
with
320 additions
and
18 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
import pytest | ||
from six import iteritems | ||
|
||
SECURITY_DEFINITIONS = { | ||
'basic': { | ||
'basic': { | ||
'type': 'basic', | ||
}, | ||
}, | ||
'apiKey': { | ||
'apiKey': { | ||
'type': 'apiKey', | ||
'name': 'api_key', | ||
'in': 'header', | ||
}, | ||
}, | ||
'oauth2': { | ||
'oauth2': { | ||
'type': 'oauth2', | ||
'authorizationUrl': 'http://petstore.swagger.io/api/oauth/dialog', | ||
'flow': 'implicit', | ||
'scopes': { | ||
'write:pets': 'modify pets in your account', | ||
'read:pets': 'read your pets', | ||
}, | ||
}, | ||
} | ||
} | ||
SECURITY_OBJECTS = { | ||
'basic': [{'basic': []}], | ||
'apiKey': [{'apiKey': []}], | ||
'oauth2': [{'oauth2': []}], | ||
} | ||
|
||
|
||
def test_security_object_and_definition_constants(): | ||
assert SECURITY_OBJECTS.keys() == SECURITY_DEFINITIONS.keys() | ||
|
||
|
||
@pytest.fixture | ||
def definitions_spec(): | ||
return { | ||
'Pet': { | ||
'type': 'object', | ||
'required': ['name'], | ||
'properties': { | ||
'name': {'type': 'string'}, | ||
'age': {'type': 'integer'}, | ||
'breed': {'type': 'string'} | ||
} | ||
} | ||
} | ||
|
||
|
||
@pytest.fixture | ||
def _paths_spec(): | ||
# The '#/paths' dict from a spec | ||
return { | ||
'/pet/findByStatus': { | ||
'get': { | ||
'tags': [ | ||
'pet' | ||
], | ||
'summary': 'Finds Pets by status', | ||
'description': 'Multiple status values can be provided with comma seperated strings', # noqa | ||
'operationId': 'findPetsByStatus', | ||
'produces': [ | ||
'application/json', | ||
'application/xml' | ||
], | ||
'parameters': [ | ||
{ | ||
'name': 'status', | ||
'in': 'query', | ||
'description': 'Status values that need to be considered for filter', # noqa | ||
'required': False, | ||
'type': 'array', | ||
'items': { | ||
'type': 'string' | ||
}, | ||
'collectionFormat': 'multi', | ||
'default': 'available' | ||
} | ||
], | ||
'responses': { | ||
'200': { | ||
'description': 'successful operation', | ||
'schema': { | ||
'type': 'array', | ||
'items': { | ||
'$ref': '#/definitions/Pet' | ||
} | ||
} | ||
}, | ||
'400': { | ||
'description': 'Invalid status value' | ||
} | ||
}, | ||
} | ||
}, | ||
} | ||
|
||
|
||
@pytest.fixture( | ||
params=SECURITY_OBJECTS.keys(), | ||
) | ||
def specs_with_security_obj_in_op_and_security_specs(request, _paths_spec, | ||
definitions_spec): | ||
security_object = SECURITY_OBJECTS[request.param] | ||
|
||
for path, path_item in iteritems(_paths_spec): | ||
for http_method in path_item.keys(): | ||
path_item[http_method]['security'] = security_object | ||
|
||
return { | ||
'paths': _paths_spec, | ||
'definitions': definitions_spec, | ||
'securityDefinitions': SECURITY_DEFINITIONS[request.param], | ||
} | ||
|
||
|
||
@pytest.fixture | ||
def specs_with_security_obj_in_op_and_no_security_specs( | ||
specs_with_security_obj_in_op_and_security_specs | ||
): | ||
del specs_with_security_obj_in_op_and_security_specs['securityDefinitions'] | ||
return specs_with_security_obj_in_op_and_security_specs | ||
|
||
|
||
@pytest.fixture( | ||
params=SECURITY_OBJECTS.keys(), | ||
) | ||
def specs_with_security_obj_in_root_and_security_specs(request, _paths_spec, | ||
definitions_spec): | ||
return { | ||
'paths': _paths_spec, | ||
'definitions': definitions_spec, | ||
'security': SECURITY_OBJECTS[request.param], | ||
'securityDefinitions': SECURITY_DEFINITIONS[request.param], | ||
} | ||
|
||
|
||
@pytest.fixture | ||
def specs_with_security_obj_in_root_and_no_security_specs( | ||
specs_with_security_obj_in_root_and_security_specs | ||
): | ||
del specs_with_security_obj_in_root_and_security_specs['securityDefinitions'] # noqa | ||
return specs_with_security_obj_in_root_and_security_specs |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
from mock import Mock | ||
import pytest | ||
from six import iteritems | ||
|
||
from bravado_core.exception import SwaggerSchemaError | ||
from bravado_core.request import IncomingRequest, unmarshal_request | ||
from bravado_core.resource import build_resources | ||
from bravado_core.spec import Spec | ||
from jsonschema import ValidationError | ||
|
||
|
||
def test_op_with_security_in_op_without_security_defs( | ||
specs_with_security_obj_in_op_and_no_security_specs, | ||
): | ||
with pytest.raises(SwaggerSchemaError): | ||
build_resources(Spec( | ||
specs_with_security_obj_in_op_and_no_security_specs, | ||
)) | ||
|
||
|
||
def test_op_with_security_in_root_without_security_defs( | ||
specs_with_security_obj_in_root_and_no_security_specs, | ||
): | ||
with pytest.raises(SwaggerSchemaError): | ||
build_resources(Spec( | ||
specs_with_security_obj_in_root_and_no_security_specs, | ||
)) | ||
|
||
|
||
def _validate_resources(resources, security_definitions_spec): | ||
resource = resources.get('pet') | ||
assert resource is not None | ||
for security_option, security_obj in iteritems(security_definitions_spec): | ||
operation = getattr(resource, 'findPetsByStatus') | ||
assert operation is not None | ||
assert security_obj in operation.security_objects | ||
if security_option == 'apiKey': | ||
assert len(operation.params) == 2 | ||
assert security_obj['name'] in operation.params | ||
else: | ||
assert len(operation.params) == 1 | ||
|
||
|
||
def test_op_with_security_in_op_with_security_defs( | ||
specs_with_security_obj_in_op_and_security_specs, | ||
): | ||
security_definitions_spec = \ | ||
specs_with_security_obj_in_op_and_security_specs['securityDefinitions'] | ||
_validate_resources( | ||
resources=build_resources(Spec( | ||
specs_with_security_obj_in_op_and_security_specs, | ||
)), | ||
security_definitions_spec=security_definitions_spec, | ||
) | ||
|
||
|
||
def test_op_with_security_in_root_with_security_defs( | ||
specs_with_security_obj_in_root_and_security_specs, | ||
): | ||
security_definitions_spec = \ | ||
specs_with_security_obj_in_root_and_security_specs['securityDefinitions'] # noqa | ||
_validate_resources( | ||
resources=build_resources(Spec( | ||
specs_with_security_obj_in_root_and_security_specs, | ||
)), | ||
security_definitions_spec=security_definitions_spec, | ||
) | ||
|
||
|
||
def test_correct_request_with_apiKey_security(petstore_spec): | ||
request = Mock( | ||
spec=IncomingRequest, | ||
path={'petId': '1234'}, | ||
headers={'api_key': 'key1'}, | ||
) | ||
op = petstore_spec.resources['pet'].operations['getPetById'] | ||
unmarshal_request(request, op) | ||
|
||
|
||
def test_wrong_request_with_apiKey_security(petstore_spec): | ||
request = Mock( | ||
spec=IncomingRequest, | ||
path={'petId': '1234'}, | ||
headers={}, | ||
) | ||
op = petstore_spec.resources['pet'].operations['getPetById'] | ||
with pytest.raises(ValidationError): | ||
unmarshal_request(request, op) |
Oops, something went wrong.