Skip to content

Commit

Permalink
Flask request user support (#432)
Browse files Browse the repository at this point in the history
* ConnexionRequest.context proxied to flask.request instance

* Add type annotations for FlaskRequestContextProxy

* Sort imports

* Sort imports

* Remove unnecessary code
  • Loading branch information
rafaelcaricio authored and hjacobs committed Apr 5, 2017
1 parent 4a66e0b commit 067ad1b
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 14 deletions.
42 changes: 31 additions & 11 deletions connexion/apis/flask_api.py
Expand Up @@ -225,7 +225,8 @@ def _get_flask_response(cls, response, mimetype):
return cls._build_flask_response(mimetype=mimetype, data=response)

@classmethod
def get_request(cls, **params):
def get_request(cls, *args, **params):
# type: (*Any, **Any) -> ConnexionRequest
"""Gets ConnexionRequest instance for the operation handler
result. Status Code and Headers for response. If only body
data is returned by the endpoint function, then the status
Expand All @@ -234,19 +235,20 @@ def get_request(cls, **params):
If the returned object is a flask.Response then it will just
pass the information needed to recreate it.
:type operation_handler_result: flask.Response | (flask.Response, int) | (flask.Response, int, dict)
:rtype: ConnexionRequest
"""
request = flask.request
flask_request = flask.request
request = ConnexionRequest(
request.url, request.method,
headers=request.headers,
form=request.form,
query=request.args,
body=request.get_data(),
json=request.get_json(silent=True),
files=request.files,
path_params=params
flask_request.url,
flask_request.method,
headers=flask_request.headers,
form=flask_request.form,
query=flask_request.args,
body=flask_request.get_data(),
json=flask_request.get_json(silent=True),
files=flask_request.files,
path_params=params,
context=FlaskRequestContextProxy()
)
logger.debug('Getting data and status code',
extra={
Expand All @@ -255,3 +257,21 @@ def get_request(cls, **params):
'url': request.url
})
return request


class FlaskRequestContextProxy(object):
""""Proxy assignments from `ConnexionRequest.context`
to `flask.request` instance.
"""
def __init__(self):
self.values = {}

def __setitem__(self, key, value):
# type: (str, Any) -> None
logger.debug('Setting "%s" attribute in flask.request', key)
setattr(flask.request, key, value)
self.values[key] = value

def items(self):
# type: () -> list
return self.values.items()
8 changes: 5 additions & 3 deletions connexion/decorators/parameter.py
Expand Up @@ -7,6 +7,7 @@
import inflection
import six

from ..request import ConnexionRequest # NOQA
from ..utils import all_json, boolean, is_null, is_nullable

try:
Expand Down Expand Up @@ -115,6 +116,7 @@ def sanitize_param(name):

@functools.wraps(function)
def wrapper(request):
# type: (ConnexionRequest) -> Any
logger.debug('Function Arguments: %s', arguments)
kwargs = {}

Expand Down Expand Up @@ -186,10 +188,10 @@ def wrapper(request):

# add context info (e.g. from security decorator)
for key, value in request.context.items():
if not has_kwargs and key not in arguments:
logger.debug("Context parameter '%s' not in function arguments", key)
else:
if has_kwargs or key in arguments:
kwargs[key] = value
else:
logger.debug("Context parameter '%s' not in function arguments", key)
return function(**kwargs)

return wrapper
4 changes: 4 additions & 0 deletions tests/api/test_secure_api.py
Expand Up @@ -78,6 +78,10 @@ def test_security(oauth_requests, secure_endpoint_app):
assert get_bye_good_auth.status_code == 200
assert get_bye_good_auth.data == b'Goodbye hjacobs (Secure!)'

headers = {"Authorization": "Bearer 100"}
get_bye_from_flask = app_client.get('/v1.0/byesecure-from-flask', headers=headers) # type: flask.Response
assert get_bye_from_flask.data == b'Goodbye test-user (Secure!)'


def test_checking_that_client_token_has_all_necessary_scopes(
oauth_requests, secure_endpoint_app):
Expand Down
6 changes: 6 additions & 0 deletions tests/fakeapi/hello.py
@@ -1,5 +1,6 @@
#!/usr/bin/env python3

import flask
from flask import jsonify, redirect

from connexion import NoContent, ProblemException, problem
Expand Down Expand Up @@ -69,6 +70,11 @@ def get_flask_response_tuple():
def get_bye_secure(name, user, token_info):
return 'Goodbye {name} (Secure: {user})'.format(name=name, user=user)


def get_bye_secure_from_flask():
return 'Goodbye {user} (Secure!)'.format(user=flask.request.user)


def get_bye_secure_ignoring_context(name):
return 'Goodbye {name} (Secure!)'.format(name=name)

Expand Down
16 changes: 16 additions & 0 deletions tests/fixtures/secure_endpoint/swagger.yaml
Expand Up @@ -44,6 +44,22 @@ paths:
required: true
type: string

/byesecure-from-flask:
get:
summary: Generate goodbye
description: ""
operationId: fakeapi.hello.get_bye_secure_from_flask
security:
- oauth:
- myscope
produces:
- text/plain
responses:
200:
description: goodbye response
schema:
type: string

/byesecure-ignoring-context/{name}:
get:
summary: Generate goodbye
Expand Down

0 comments on commit 067ad1b

Please sign in to comment.