Skip to content

Commit

Permalink
Merge pull request #58 from Cornices/view-security
Browse files Browse the repository at this point in the history
Add a way to document security properties from the view
  • Loading branch information
gabisurita authored Jan 25, 2017
2 parents 6b3d731 + f7a58c2 commit 3e96d11
Show file tree
Hide file tree
Showing 3 changed files with 108 additions and 2 deletions.
5 changes: 5 additions & 0 deletions cornice_swagger/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@


class CorniceSwaggerPredicate(object):
"""Predicate to add simple information to Cornice Swagger."""

def __init__(self, schema, config):
self.schema = schema

Expand All @@ -20,6 +22,9 @@ def __call__(self, context, request):


def includeme(config):

# Custom view parameters
config.add_view_predicate('response_schemas', CorniceSwaggerPredicate)
config.add_view_predicate('tags', CorniceSwaggerPredicate)
config.add_view_predicate('operation_id', CorniceSwaggerPredicate)
config.add_view_predicate('api_security', CorniceSwaggerPredicate)
23 changes: 21 additions & 2 deletions cornice_swagger/swagger.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ def __call__(self, title, version, base_path='/', info={}, swagger={},
return swagger

def _build_paths(self, ignore_methods=['head', 'options'], ignore_ctypes=[],
default_tags=None, default_op_ids=None, **kwargs):
default_tags=None, default_op_ids=None, default_security=None, **kwargs):
"""
Build the Swagger "paths" and "tags" attributes from cornice service
definitions.
Expand All @@ -128,7 +128,12 @@ def _build_paths(self, ignore_methods=['head', 'options'], ignore_ctypes=[],
tags to be used if not provided by the view.
:param default_op_ids:
Provide a callable that takes a cornice service and the method name
(e.g GET) and returns an operation Id that should be unique."""
(e.g GET) and returns an operation Id that should be unique.
:param default_security:
Provide a default list or a callable that takes a cornice
service and the method name (e.g GET) and returns a list of Swagger
security policies.
"""

paths = {}
tags = []
Expand Down Expand Up @@ -179,6 +184,16 @@ def _build_paths(self, ignore_methods=['head', 'options'], ignore_ctypes=[],
raise CorniceSwaggerException('default_op_id should be a callable.')
op['operationId'] = default_op_ids(service, method)

# If security options not defined and default is provided
if 'security' not in op and default_security:
if callable(default_security):
op['security'] = default_security(service, method)
else:
op['security'] = default_security

if not isinstance(op.get('security', []), list):
raise CorniceSwaggerException('security should be a list or callable')

path[method.lower()] = op
paths[service.path] = path

Expand Down Expand Up @@ -281,6 +296,10 @@ def _extract_operation_from_view(self, view, args={},
if 'operation_id' in args:
op['operationId'] = args['operation_id']

# Get security policies
if 'api_security' in args:
op['security'] = args['api_security']

return op

def _extract_transform_schema(self, args):
Expand Down
82 changes: 82 additions & 0 deletions tests/test_swagger.py
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,88 @@ def view_get(self, request):
default_op_ids="foo")


class ExtractSecurityTest(unittest.TestCase):

def test_view_defined_security(self):

service = Service("IceCream", "/icecream/{flavour}")

@service.get(api_security=[{'basicAuth': []}])
def view_get(self, request):
return service

swagger = CorniceSwagger([service])
base_spec = {'securityDefinitions': {'basicAuth': {'type': 'basic'}}}
spec = swagger('IceCreamAPI', '4.2', swagger=base_spec)
validate(spec)
security = spec['paths']['/icecream/{flavour}']['get']['security']
self.assertEquals(security, [{'basicAuth': []}])

def test_listed_default_security(self):

service = Service("IceCream", "/icecream/{flavour}")

@service.get()
def view_get(self, request):
return service

swagger = CorniceSwagger([service])
base_spec = {'securityDefinitions': {'basicAuth': {'type': 'basic'}}}
security = [{'basicAuth': []}]
spec = swagger('IceCreamAPI', '4.2', swagger=base_spec, default_security=security)
validate(spec)
security = spec['paths']['/icecream/{flavour}']['get']['security']
self.assertEquals(security, [{'basicAuth': []}])

def test_callable_default_security(self):

def get_security(service, method):
definitions = service.definitions
for definition in definitions:
met, view, args = definition
if met == method:
break

if 'security' in args:
return [{'basicAuth': []}]
else:
return []

service = Service("IceCream", "/icecream/{flavour}")

@service.get()
def view_get(self, request):
return service

@service.post(security='foo')
def view_post(self, request):
return service

swagger = CorniceSwagger([service])
base_spec = {'securityDefinitions': {'basicAuth': {'type': 'basic'}}}
security = [{'basicAuth': []}]
spec = swagger('IceCreamAPI', '4.2', swagger=base_spec,
default_security=get_security)
validate(spec)
security = spec['paths']['/icecream/{flavour}']['post']['security']
self.assertEquals(security, [{'basicAuth': []}])
security = spec['paths']['/icecream/{flavour}']['get']['security']
self.assertEquals(security, [])

def test_invalid_security_raises_exception(self):

service = Service("IceCream", "/icecream/{flavour}")

class IceCream(object):
@service.get(api_security='basicAuth')
def view_get(self, request):
return service

swagger = CorniceSwagger([service])
self.assertRaises(CorniceSwaggerException,
swagger, 'IceCreamAPI', '4.2')


class NotInstantiatedSchemaTest(unittest.TestCase):

def test_not_instantiated(self):
Expand Down

0 comments on commit 3e96d11

Please sign in to comment.