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

Implement BasicAuth decode classmethod. #744

Merged
merged 12 commits into from
May 23, 2016
36 changes: 36 additions & 0 deletions aiohttp/helpers.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""Various helper functions"""
import base64
import binascii
import datetime
import functools
import io
Expand Down Expand Up @@ -31,6 +32,41 @@ def __new__(cls, login, password='', encoding='latin1'):

return super().__new__(cls, login, password, encoding)

@classmethod
def decode(cls, auth_header, encoding='latin1'):
"""Create a :class:`BasicAuth` object from an ``Authorization`` HTTP
header.

:param auth_header: The value of the ``Authorization`` header.
:type auth_header: str
Copy link
Member

@kxepal kxepal May 23, 2016

Choose a reason for hiding this comment

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

You can use :param str auth_header: ... today, but aiohttp doesn't uses docstring in code, but separate files under doc/ directory.

:param encoding: The character encoding used on the password.
:type encoding: str

:returns: The decoded authentication.
:rtype: :class:`BasicAuth`

:raises ValueError: if the headers are unable to be decoded.

"""
split = auth_header.strip().split(' ')
if len(split) == 2:
if split[0].strip().lower() != 'basic':
raise ValueError('Unknown authorization method %s' % split[0])
to_decode = split[1]
elif len(split) == 1:
to_decode = split[0]
Copy link
Member

Choose a reason for hiding this comment

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

Why this case exists?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Because https://github.com/rdegges/python-basicauth implements it. Happy to remove it if you want.

Copy link
Member

Choose a reason for hiding this comment

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

I would be better strict to the specification where auth scheme is mandatory. Unlikely Authorization: Zm9vOmJhcg== will be a valid basic auth header.

else:
raise ValueError('Could not parse authorization header.')

try:
username, _, password = base64.b64decode(
to_decode.encode('ascii')
).decode(encoding).partition(':')
except binascii.Error:
raise ValueError('Invalid base64 encoding.')

return cls(username, password)
Copy link
Member

Choose a reason for hiding this comment

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

Shouldn't encoding be passed here as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Whoops. Fixed.


def encode(self):
"""Encode credentials."""
creds = ('%s:%s' % (self.login, self.password)).encode(self.encoding)
Expand Down
16 changes: 16 additions & 0 deletions tests/test_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,22 @@ def test_basic_auth4():
assert auth.encode() == 'Basic bmtpbTpwd2Q='


def test_basic_auth_decode():
auth = helpers.BasicAuth.decode('Basic bmtpbTpwd2Q=')
assert auth.login == 'nkim'
assert auth.password == 'pwd'


def test_basic_auth_decode_not_basic():
with pytest.raises(ValueError):
auth = helpers.BasicAuth.decode('Complex bmtpbTpwd2Q=')


def test_basic_auth_decode_bad_base64():
with pytest.raises(ValueError):
auth = helpers.BasicAuth.decode('Basic bmtpbTpwd2Q')


def test_invalid_formdata_params():
with pytest.raises(TypeError):
helpers.FormData('asdasf')
Expand Down