Skip to content

Conversation

@Jyhess
Copy link
Contributor

@Jyhess Jyhess commented Feb 5, 2019

Fixes #868 .

Allow to use coroutines with Aiohttp in security handlers.

Changes proposed in this pull request:

  • Move all function from connexion.operations.security to a class in connexion.security.SecurityHandlerFactory
  • Specialize this class for AioHttp and Flask, and instantiate them in dedicated apis
  • AioHttp specialization now support coroutines
  • pass_context_arg_name option is supported for security handlers

@Jyhess Jyhess force-pushed the async_security_check branch 2 times, most recently from 89bfff0 to 3fb9249 Compare February 6, 2019 15:26
@spec-first spec-first deleted a comment Feb 6, 2019
@Jyhess Jyhess changed the title WIP Async security check Async security check Feb 12, 2019
@Jyhess Jyhess force-pushed the async_security_check branch 3 times, most recently from a5876e4 to 8912e28 Compare May 9, 2019 16:31
@Jyhess
Copy link
Contributor Author

Jyhess commented May 9, 2019

I rebased the branch on top of master.
Is there something else I can do to move further on this pull request?

@elsnoon
Copy link

elsnoon commented May 21, 2019

Hi guys, when we can expect this to be merged?

It would be great to have async security check. At the moment we have some workaround solution with middleware.

Thanks 🙂

@ghost
Copy link

ghost commented Jun 25, 2019

Is there any progress on this?

@hjacobs
Copy link
Contributor

hjacobs commented Oct 15, 2019

Sorry for the delay. Could you rebase the PR and fix the conflicts? We will take a look afterwards.

@cognifloyd
Copy link
Contributor

I will rebase this one once #849 is merged. (Thanks @Jyhess for giving me access so I can move the PRs forward)


class OAuthResponseProblem(OAuthProblem):
def __init__(self, token_response, **kwargs):
self.token_response = token_response
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what are the implications of removing this?

This comment was marked as resolved.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some history here - every security handler used to raise OAuthResponseProblem in case of an error, so including it (or inheriting from it) was for backwards compatibility.
Hopefully we can drop that requirement at some point.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh. Well, I just got rid of it everywhere where it was in use, and added it where it made sense.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we need to adjust the PR to split that out into another PR then fine. This PR wore me out, so if changes need to occur I'll wait a bit to make them.

logger = logging.getLogger('connexion.api.security')


class AioHttpSecurityHandlerFactory(SecurityHandlerFactory):
Copy link
Collaborator

@dtkav dtkav Dec 5, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are we going to have to write one of these for every framework we support?
Is there a more generic version of this like AsyncSecurityHandlerFactory?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right now, yes, each framework would need its own SecurityHandlerFactory.
As far as making it more generic - I'm of two minds:

  1. I think we should assume this is AioHttp-specific until we implement another async framework. At that time, refactoring this into a reusable AsyncSecurityHandler that can be reused by both of them would make sense.
  2. The only thing that uses aiohttp in this class is get_token_info_remote in how it uses self.client_session. If request is a ConnexionRequest (vs a framework specific request) then the other functions are very generic, so it should be simple to have an AbstractAsyncSecurityHandlerFactory that the AioHttpSecurityHandlerFactory extends to implement the get_token_info_remote method.

After I rebase this, I can add the AbstractAsyncSecurityHandlerFactory if you think that's worth it to help adding future frameworks. The risk there is that there actually is something specific to aiohttp here than I'm not seeing by reading, so we'll have to refactor again at that point to only include the generic stuff.

And now that I've typed all that, I think that I will go ahead and do that. I'll leave the rest of my comments here for future reference on the thinking.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

# Must be created in a coroutine
self.client_session = aiohttp.ClientSession()
headers = {'Authorization': 'Bearer {}'.format(token)}
token_request = yield from self.client_session.get(token_info_url, headers=headers, timeout=5)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

5 is a magic number here - it might make sense to add as a field on the base class in case someone wants to change it.

This comment was marked as resolved.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Copy link
Contributor

@cognifloyd cognifloyd left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wow. good catches. Here are my follow up comments as I try to understand the code :)

logger = logging.getLogger('connexion.api.security')


class AioHttpSecurityHandlerFactory(SecurityHandlerFactory):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right now, yes, each framework would need its own SecurityHandlerFactory.
As far as making it more generic - I'm of two minds:

  1. I think we should assume this is AioHttp-specific until we implement another async framework. At that time, refactoring this into a reusable AsyncSecurityHandler that can be reused by both of them would make sense.
  2. The only thing that uses aiohttp in this class is get_token_info_remote in how it uses self.client_session. If request is a ConnexionRequest (vs a framework specific request) then the other functions are very generic, so it should be simple to have an AbstractAsyncSecurityHandlerFactory that the AioHttpSecurityHandlerFactory extends to implement the get_token_info_remote method.

After I rebase this, I can add the AbstractAsyncSecurityHandlerFactory if you think that's worth it to help adding future frameworks. The risk there is that there actually is something specific to aiohttp here than I'm not seeing by reading, so we'll have to refactor again at that point to only include the generic stuff.

And now that I've typed all that, I think that I will go ahead and do that. I'll leave the rest of my comments here for future reference on the thinking.

# Must be created in a coroutine
self.client_session = aiohttp.ClientSession()
headers = {'Authorization': 'Bearer {}'.format(token)}
token_request = yield from self.client_session.get(token_info_url, headers=headers, timeout=5)

This comment was marked as resolved.

logger.debug('pass_context_arg_name: %s', pass_context_arg_name)
self.pass_context_arg_name = pass_context_arg_name

self.security_handler_factory = self.make_security_handler_factory(pass_context_arg_name)

This comment was marked as resolved.

:type token: str
:rtype: dict
"""
token_request = session.get(token_info_url, headers={'Authorization': 'Bearer {}'.format(token)}, timeout=5)

This comment was marked as resolved.


class OAuthResponseProblem(OAuthProblem):
def __init__(self, token_response, **kwargs):
self.token_response = token_response

This comment was marked as resolved.

if token_info is self.no_value:
return self.no_value
if token_info is None:
raise OAuthResponseProblem(description=exception_msg)

This comment was marked as resolved.

Comment on lines 257 to 276
if token_info is None:
raise OAuthResponseProblem(description=exception_msg)

This comment was marked as resolved.

@cognifloyd
Copy link
Contributor

K. I'm not entirely confident about my fixes for generic auth issues.

@cognifloyd cognifloyd force-pushed the async_security_check branch 12 times, most recently from 25d5448 to 4795d7b Compare December 13, 2019 09:45
@cognifloyd cognifloyd force-pushed the async_security_check branch from e1ca702 to 07d20e6 Compare May 30, 2020 08:35
@cognifloyd
Copy link
Contributor

cognifloyd commented May 30, 2020

K. I just rebased this on master, removing the OAuthResponseProblem changes. Those exception changes/cleanups are in my unauthorized_problem branch.

Next steps:

  • satisfy travis
  • adjust to use async/await instead of coroutine decorator

@cognifloyd cognifloyd force-pushed the async_security_check branch 4 times, most recently from 1b058bc to 0252ff7 Compare June 3, 2020 21:00
@coveralls
Copy link

coveralls commented Jun 4, 2020

Coverage Status

Coverage increased (+0.6%) to 96.875% when pulling caacc78 on Jyhess:async_security_check into bed4b95 on zalando:master.

@cognifloyd cognifloyd force-pushed the async_security_check branch from e75b210 to 6bb75db Compare June 4, 2020 01:04
@cognifloyd cognifloyd force-pushed the async_security_check branch from 6bb75db to caacc78 Compare June 4, 2020 09:09
@cognifloyd
Copy link
Contributor

@dtkav could you review when you get a chance?

@cognifloyd
Copy link
Contributor

@hjacobs this is ready for review

@chbndrhnns
Copy link
Contributor

@jmcs I saw you did some work recently on this project. This PR seems to be ready for quite some time. Any chance to have a look and approve/review?

I am in a position where I would need such functionality for performance reasons and I would prefer the changes in the official repo instead of forking my own version of connexion just to get async security handlers.

@jmcs jmcs self-requested a review July 20, 2020 16:00
@jmcs
Copy link
Contributor

jmcs commented Jul 20, 2020

@chbndrhnns @cognifloyd, I'll review this PR in the next few days.

@jmcs
Copy link
Contributor

jmcs commented Jul 22, 2020

👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Security handlers should be async when using AioHttp

8 participants