Skip to content

Commit

Permalink
Tests
Browse files Browse the repository at this point in the history
  • Loading branch information
cziebuhr committed Sep 20, 2018
1 parent 04c5709 commit 0b49ed2
Show file tree
Hide file tree
Showing 6 changed files with 148 additions and 100 deletions.
6 changes: 4 additions & 2 deletions tests/api/test_secure_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,11 @@ def test_security(oauth_requests, secure_endpoint_app):
assert get_bye_bad_token_reponse['detail'] == "Provided oauth token is not valid"

response = app_client.get('/v1.0/more-than-one-security-definition') # type: flask.Response
assert response.status_code == 200
assert response.status_code == 401

response = app_client.get('/v1.0/user-handled-security') # type: flask.Response
# also tests case-insensitivity
headers = {"X-AUTH": "mykey"}
response = app_client.get('/v1.0/more-than-one-security-definition', headers=headers) # type: flask.Response
assert response.status_code == 200

headers = {"Authorization": "Bearer 100"}
Expand Down
129 changes: 86 additions & 43 deletions tests/decorators/test_security.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@
import requests

import pytest
from connexion.decorators.security import (get_tokeninfo_url,
verify_oauth_local,
verify_oauth_remote)
from connexion.decorators.security import (get_tokeninfo_func,
get_tokeninfo_remote,
validate_scope, verify_apikey,
verify_basic, verify_oauth)
from connexion.exceptions import (OAuthProblem, OAuthResponseProblem,
OAuthScopeProblem)
from mock import MagicMock
Expand All @@ -17,29 +18,31 @@ def test_get_tokeninfo_url(monkeypatch):
logger = MagicMock()
monkeypatch.setattr('connexion.decorators.security.logger', logger)
security_def = {}
assert get_tokeninfo_url(security_def) is None
assert get_tokeninfo_func(security_def) is None
logger.warn.assert_not_called()
env['TOKENINFO_URL'] = 'issue-146'
assert get_tokeninfo_url(security_def) == 'issue-146'
func = get_tokeninfo_func(security_def)
assert func.func is get_tokeninfo_remote
assert func.args == ('issue-146',)
logger.warn.assert_not_called()
logger.warn.reset_mock()
security_def = {'x-tokenInfoUrl': 'bar'}
assert get_tokeninfo_url(security_def) == 'bar'
func = get_tokeninfo_func(security_def)
assert func.func is get_tokeninfo_remote
assert func.args == ('bar',)
logger.warn.assert_not_called()


def test_verify_oauth_invalid_auth_header(monkeypatch):
def func():
pass
def test_verify_oauth_missing_auth_header():
def somefunc(token):
return None

wrapped_func = verify_oauth_remote('https://example.org/tokeninfo', set(['admin']), func)
wrapped_func = verify_oauth(somefunc, validate_scope)

request = MagicMock()
app = MagicMock()
monkeypatch.setattr('flask.current_app', app)
request.headers = {}

with pytest.raises(OAuthProblem):
wrapped_func(request)
assert wrapped_func(request, ['admin']) is None


def test_verify_oauth_scopes_remote(monkeypatch):
Expand All @@ -51,77 +54,117 @@ def get_tokeninfo_response(*args, **kwargs):
tokeninfo_response._content = json.dumps(tokeninfo).encode()
return tokeninfo_response

def func(request):
pass

wrapped_func = verify_oauth_remote('https://example.org/tokeninfo', set(['admin']), func)
token_info_func = get_tokeninfo_func({'x-tokenInfoUrl': 'https://example.org/tokeninfo'})
wrapped_func = verify_oauth(token_info_func, validate_scope)

request = MagicMock()
request.headers = {"Authorization": "Bearer 123"}
app = MagicMock()
monkeypatch.setattr('flask.current_app', app)

session = MagicMock()
session.get = get_tokeninfo_response
monkeypatch.setattr('connexion.decorators.security.session', session)

with pytest.raises(OAuthScopeProblem, message="Provided token doesn't have the required scope"):
wrapped_func(request)
wrapped_func(request, ['admin'])

tokeninfo["scope"] += " admin"
wrapped_func(request)
assert wrapped_func(request, ['admin']) is not None

tokeninfo["scope"] = ["foo", "bar"]
with pytest.raises(OAuthScopeProblem, message="Provided token doesn't have the required scope"):
wrapped_func(request)
wrapped_func(request, ['admin'])

tokeninfo["scope"].append("admin")
wrapped_func(request)
assert wrapped_func(request, ['admin']) is not None


def test_verify_oauth_invalid_local_token_response_none(monkeypatch):
def test_verify_oauth_invalid_local_token_response_none():
def somefunc(token):
return None

def func():
pass

wrapped_func = verify_oauth_local(somefunc, set(['admin']), func)
wrapped_func = verify_oauth(somefunc, validate_scope)

request = MagicMock()
request.headers = {"Authorization": "Bearer 123"}
app = MagicMock()
monkeypatch.setattr('flask.current_app', app)

with pytest.raises(OAuthResponseProblem):
wrapped_func(request)
wrapped_func(request, ['admin'])


def test_verify_oauth_scopes_local(monkeypatch):
def test_verify_oauth_scopes_local():
tokeninfo = dict(uid="foo", scope="scope1 scope2")

def func(request):
pass

def token_info(token):
return tokeninfo

wrapped_func = verify_oauth_local(token_info, set(['admin']), func)
wrapped_func = verify_oauth(token_info, validate_scope)

request = MagicMock()
request.headers = {"Authorization": "Bearer 123"}
app = MagicMock()
monkeypatch.setattr('flask.current_app', app)

with pytest.raises(OAuthScopeProblem, message="Provided token doesn't have the required scope"):
wrapped_func(request)
wrapped_func(request, ['admin'])

tokeninfo["scope"] += " admin"
wrapped_func(request)
assert wrapped_func(request, ['admin']) is not None

tokeninfo["scope"] = ["foo", "bar"]
with pytest.raises(OAuthScopeProblem, message="Provided token doesn't have the required scope"):
wrapped_func(request)
wrapped_func(request, ['admin'])

tokeninfo["scope"].append("admin")
wrapped_func(request)
assert wrapped_func(request, ['admin']) is not None


def test_verify_basic_missing_auth_header():
def somefunc(username, password, required_scopes=None):
return None

wrapped_func = verify_basic(somefunc)

request = MagicMock()
request.headers = {"Authorization": "Bearer 123"}

assert wrapped_func(request, ['admin']) is None


def test_verify_basic():
def basic_info(username, password, required_scopes=None):
if username == 'foo' and password == 'bar':
return {'sub': 'foo'}
return None

wrapped_func = verify_basic(basic_info)

request = MagicMock()
request.headers = {"Authorization": 'Basic Zm9vOmJhcg=='}

assert wrapped_func(request, ['admin']) is not None


def test_verify_apikey_query():
def apikey_info(apikey, required_scopes=None):
if apikey == 'foobar':
return {'sub': 'foo'}
return None

wrapped_func = verify_apikey(apikey_info, 'query', 'auth')

request = MagicMock()
request.query = {"auth": 'foobar'}

assert wrapped_func(request, ['admin']) is not None


def test_verify_apikey_header():
def apikey_info(apikey, required_scopes=None):
if apikey == 'foobar':
return {'sub': 'foo'}
return None

wrapped_func = verify_apikey(apikey_info, 'header', 'X-Auth')

request = MagicMock()
request.headers = {"X-Auth": 'foobar'}

assert wrapped_func(request, ['admin']) is not None
6 changes: 6 additions & 0 deletions tests/fakeapi/hello.py
Original file line number Diff line number Diff line change
Expand Up @@ -484,3 +484,9 @@ def get_httpstatus_response():

def get_bad_default_response(response_code):
return {}, response_code


def apikey_info(apikey, required_scopes=None):
if apikey == 'mykey':
return {'sub': 'admin'}
return None
13 changes: 2 additions & 11 deletions tests/fixtures/secure_endpoint/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -103,16 +103,6 @@ paths:
responses:
'200':
description: some response
/user-handled-security:
get:
summary: Some external call to API
description: External application call
operationId: fakeapi.hello.schema_map
security:
- api_key: []
responses:
'200':
description: OK
servers:
- url: /v1.0
components:
Expand All @@ -128,5 +118,6 @@ components:
otherscope: another scope
api_key:
type: apiKey
name: api_key
name: X-Auth
in: header
x-apikeyInfoFunc: fakeapi.hello.apikey_info
14 changes: 2 additions & 12 deletions tests/fixtures/secure_endpoint/swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@ securityDefinitions:

api_key:
type: apiKey
name: api_key
name: X-Auth
in: header
x-apikeyInfoFunc: fakeapi.hello.apikey_info

paths:
/byesecure/{name}:
Expand Down Expand Up @@ -127,14 +128,3 @@ paths:
responses:
200:
description: some response

/user-handled-security:
get:
summary: Some external call to API
description: External application call
operationId: fakeapi.hello.schema_map
security:
- api_key: []
responses:
200:
description: OK
Loading

0 comments on commit 0b49ed2

Please sign in to comment.