Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow custom authorization mechanisms. #139

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions docs/examples/custom_auth.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
from __future__ import print_function
from twisted.internet.task import react
from twisted.web.iweb import IAgent
from _utils import print_response
from zope.interface import implementer

import treq


@implementer(IAgent)
class CustomAuth(object):
"""
I implement a custom authorization scheme.
"""

def __init__(self, agent):
self._agent = agent

def request(self, method, uri, headers=None, bodyProducer=None):
print("Some authorization occurs here")
return self._agent.request(method, uri, headers, bodyProducer)


def main(reactor, *args):
d = treq.get(
'http://httpbin.org/get',
auth=CustomAuth,
)
d.addCallback(print_response)
return d

react(main, [])
12 changes: 12 additions & 0 deletions docs/howto.rst
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,18 @@ Full example: :download:`basic_auth.py <examples/basic_auth.py>`

.. _RFC 2617: http://www.ietf.org/rfc/rfc2617.txt

The ``auth`` keyword argument also supports custom authorization
implementations. ``auth`` should be a callable that accepts as its
only argument the currently configured ``IAgent`` and should return an
``IAgent`` that implements the desired authorization mechanism.

.. literalinclude:: examples/custom_auth.py
:linenos:
:lines: 10-33

Full example: :download:`basic_auth.py <examples/custom_auth.py>`


Redirects
---------

Expand Down
2 changes: 2 additions & 0 deletions treq/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,7 @@ def add_basic_auth(agent, username, password):
def add_auth(agent, auth_config):
if isinstance(auth_config, tuple):
return add_basic_auth(agent, auth_config[0], auth_config[1])
elif callable(auth_config):
return auth_config(agent)

raise UnknownAuthConfig(auth_config)
21 changes: 20 additions & 1 deletion treq/test/test_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@

from twisted.web.client import Agent
from twisted.web.http_headers import Headers
from twisted.web.iweb import IAgent

from treq.test.util import TestCase
from treq.auth import _RequestHeaderSettingAgent, add_auth, UnknownAuthConfig

from zope.interface import implementer


class RequestHeaderSettingAgentTests(TestCase):
def setUp(self):
Expand Down Expand Up @@ -70,6 +73,22 @@ def test_add_basic_auth_huge(self):
agent,
Headers({b'authorization': [auth]}))

def test_auth_callable(self):
agent = mock.Mock()

@implementer(IAgent)
class AuthorizingAgent(object):

def __init__(self, agent):
self.wrapped_agent = agent

def request(self, method, uri, headers=None, bodyProducer=None):
"""Not called by this test"""

wrapping_agent = add_auth(agent, AuthorizingAgent)
self.assertIsInstance(wrapping_agent, AuthorizingAgent)
self.assertIs(wrapping_agent.wrapped_agent, agent)

def test_add_unknown_auth(self):
agent = mock.Mock()
self.assertRaises(UnknownAuthConfig, add_auth, agent, mock.Mock())
self.assertRaises(UnknownAuthConfig, add_auth, agent, object())