Skip to content

Commit

Permalink
Included realm as option for basic auth; use auth parser to extract
Browse files Browse the repository at this point in the history
Resolves #8
  • Loading branch information
Xophmeister committed Sep 5, 2017
1 parent ca05c3b commit 2d2ab9f
Show file tree
Hide file tree
Showing 5 changed files with 39 additions and 20 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,9 @@ This is only needed if using HTTP basic authentication.
either `s` (`sec` or `second`) or `m` (`min` or `minute`), where spelt
units may be pluralised.

* **`realm`** Optional free text input for the basic authentication
realm parameter.

### Arvados Authentication

This is only needed if using Arvados authentication.
Expand Down
4 changes: 4 additions & 0 deletions irobot.conf.sample
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,10 @@ url = http://example.com
# of the following units: s(ec(ond)(s)), m(in(ute)(s))
cache = 10 min

# The basic authentication realm; this can be omitted or set to any text
# string
realm = iRobot authentication realm

[arvados_auth] #########################################################
# This section must appear if using the Arvados authentication handler

Expand Down
38 changes: 19 additions & 19 deletions irobot/authentication/basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,35 +18,28 @@
"""

import logging
import re
from base64 import b64decode
from typing import Optional, Tuple

from requests import Response, Request

from irobot.authentication._http import HTTPAuthHandler
from irobot.authentication.parser import ParseError, auth_parser
from irobot.config import BasicAuthConfig


_BASIC_AUTH_RE = re.compile(r"""
^Basic \s
(
(?: [a-z0-9+/]{4} )*
(?: [a-z0-9+/]{2}== | [a-z0-9+/]{3}= )?
)$
""", re.VERBOSE | re.IGNORECASE)


class HTTPBasicAuthHandler(HTTPAuthHandler):
""" HTTP basic authentication handler """
def __init__(self, config:BasicAuthConfig, logger:Optional[logging.Logger] = None) -> None:
super().__init__(config=config, logger=logger)
self._auth_re = _BASIC_AUTH_RE

@property
def www_authenticate(self) -> str:
# TODO Support Basic authentication params (e.g., realm, etc.)
return "Basic"
challenge = "Basic"
if self._config.realm:
challenge += f" realm=\"{self._config.realm}\""

return challenge

def parse_auth_header(self, auth_header:str) -> Tuple[str, str]:
"""
Expand All @@ -55,12 +48,19 @@ def parse_auth_header(self, auth_header:str) -> Tuple[str, str]:
@param auth_header Contents of the "Authorization" header (string)
@return Tuple of username (string) and password (string)
"""
match = self._auth_re.match(auth_header)

if not match:
raise ValueError("Invalid HTTP basic authentication header")

return tuple(b64decode(match.group(1)).decode().split(":"))
try:
auth_handlers = auth_parser(auth_header)
except ParseError:
raise ValueError("Couldn't parse authentication header")

for handler in auth_handlers:
if handler.auth_method == "Basic" and handler.payload:
basic_handler = handler
break
else:
raise ValueError("No HTTP basic authentication response found")

return tuple(b64decode(basic_handler.payload).decode().split(":"))

def auth_request(self, user:str, password:str) -> Request:
"""
Expand Down
11 changes: 11 additions & 0 deletions irobot/common/canon.py
Original file line number Diff line number Diff line change
Expand Up @@ -233,3 +233,14 @@ def domain_name(n:str) -> str:
return n.lower()

raise ValueError("Invalid domain name")


def free_text(t:str) -> str:
"""
Canonicalise free text so special characters are escaped
@param t Free text (string)
@return Escaped text (string)
"""
# TODO This could probably be better!!
return re.sub(r"\"", r"\"", t)
3 changes: 2 additions & 1 deletion irobot/config/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@ def __call__(self, section:str, config:ConfigParser) -> Configuration:

_factories.add("basic_auth", auth.BasicAuthConfig,
RequiredKey("url", auth.url),
RequiredKey("cache", canon.duration)
RequiredKey("cache", canon.duration),
OptionalKey("realm", canon.free_text)
)

_factories.add("arvados_auth", auth.ArvadosAuthConfig,
Expand Down

0 comments on commit 2d2ab9f

Please sign in to comment.