Example #1
0
def login(wwwAuth, repository, host=None, forceCredential=False):
    authresp = wwwAuth
    realm = parse_keqv_list(parse_http_list(authresp[authresp.index(' ') +
                                                     1:]))
    realmurl = urlparse(realm['realm'])
    credentialHost = host if host is not None else realmurl.netloc
    with closing(httplib.HTTPSConnection(realmurl.netloc)) as authhttps:
        authhttps.request(
            'GET', '%s?scope=repository:%s:pull&service=%s' %
            (realmurl.path, repository, realm['service']), None)
        resp = authhttps.getresponse()
        if forceCredential or resp.status == 401 or resp.status == 403:
            resp.read()
            basic = getCredential(credentialHost)
            if basic:
                account = base64.b64decode(basic).decode('utf-8').split(':')[0]
                authhttps.request(
                    'GET',
                    '%s?account=%s&scope=repository:%s:pull&service=%s' %
                    (realmurl.path, account, repository, realm['service']),
                    None, {'Authorization': 'Basic ' + basic})
                resp = authhttps.getresponse()
                if resp.status == 401:
                    raise Exception(
                        'Credential is wrong (used "%s"). Please relogin to %s.'
                        % (account, credentialHost))
            else:
                raise Exception('`docker login %s` is required.' %
                                (credentialHost))
        token = json.load(resp)['token']
        return {'Authorization': 'Bearer ' + token}
Example #2
0
 def authorization(self) -> Optional[Authorization]:
     header = self.headers.get("Authorization", "")
     try:
         type_, value = header.split(None, 1)
         type_ = type_.lower()
     except ValueError:
         return None
     else:
         if type_ == "basic":
             try:
                 username, password = b64decode(
                     value.encode()).decode().split(":", 1)
             except ValueError:
                 return None
             else:
                 return Authorization(username=username, password=password)
         elif type_ == "digest":
             items = parse_http_list(value)
             params = parse_keqv_list(items)
             for key in "username", "realm", "nonce", "uri", "response":
                 if key not in params:
                     return None
             if ("cnonce" in params
                     or "nc" in params) and "qop" not in params:
                 return None
             return Authorization(**params)
     return None
Example #3
0
 def process_request(self, url, body, headers):
     if not headers:
         headers = {}
     self.logger.debug("[%s] Process middleware on: %s", self.name, url)
     # First query - 401
     code, resp_headers, result = fetch_sync(
         url,
         headers=None,
         request_timeout=60,
         follow_redirects=True,
         allow_proxy=False,
         validate_cert=False,
         eof_mark=self.eof_mark,
     )
     self.logger.debug(
         "[%s] Response code %s, headers %s on: %s, body: %s",
         self.name,
         code,
         resp_headers,
         url,
         body,
     )
     if "WWW-Authenticate" in resp_headers and resp_headers["WWW-Authenticate"].startswith(
         "Digest"
     ):
         items = parse_http_list(resp_headers["WWW-Authenticate"][7:])
         digest_response = parse_keqv_list(items)
         headers["Authorization"] = self.build_digest_header(url, self.method, digest_response)
     self.logger.debug("[%s] Set headers, %s", self.name, headers)
     return url, body, headers
def parse_www_authenticate_header(header):
    """
    Convert a WWW-Authentication header into a dict that can be used
    in a JSON response.
    """
    items = parse_http_list(header)
    return parse_keqv_list(items)
Example #5
0
 def _get_auth_info(self, response):
     """Parse a 401 response and get the needed auth parameters."""
     www_auth = response.headers["Www-Authenticate"]
     if not www_auth.startswith("Bearer "):
         raise ValueError("Bearer not found")
     info = parse_keqv_list(parse_http_list(www_auth[7:]))
     return info
Example #6
0
    def _fetch_oauth2_token(self, host, response_headers):
        www_auth = response_headers['WWW-Authenticate']
        dockercfg = self.pull_secret.for_host(host)
        if not www_auth.startswith('Bearer ') or not dockercfg:
            return None

        items = parse_http_list(www_auth[7:])
        opts = parse_keqv_list(items)  # realm, scope, service

        realm = urlparse(opts['realm'])
        url = ('{path}'
               '?client_id=docker'
               '&offline_token=true'
               '&service={service}'
               '&scope={scope}').format(path=realm.path, **opts)
        c = http.client.HTTPSConnection(realm.netloc)
        c.request('GET', url, headers={
            'Authorization': "Basic {}".format(dockercfg['auth'])
        })
        r = c.getresponse()
        if r.status != 200:
            return False

        data = json.loads(r.read())
        self.headers['Authorization'] = 'Bearer {}'.format(data['token'])
        return True
 def authorization(self) -> Optional[Authorization]:
     header = self.headers.get('Authorization', '')
     try:
         type_, value = header.split(None, 1)
         type_ = type_.lower()
     except ValueError:
         return None
     else:
         if type_ == 'basic':
             try:
                 username, password = b64decode(
                     value.encode()).decode().split(':', 1)
             except ValueError:
                 return None
             else:
                 return Authorization(username=username, password=password)
         elif type_ == 'digest':
             items = parse_http_list(value)
             params = parse_keqv_list(items)
             for key in 'username', 'realm', 'nonce', 'uri', 'response':
                 if key not in params:
                     return None
             if ('cnonce' in params
                     or 'nc' in params) and 'qop' not in params:
                 return None
             return Authorization(**params)
     return None
Example #8
0
    def __init__(
        self, auth_header, http_method,
        debug=False, accept_charset=DEFAULT_CHARSET[:],
    ):
        self.http_method = http_method
        self.debug = debug

        if not self.matches(auth_header):
            raise ValueError('Authorization scheme is not "Digest"')

        self.auth_header = _try_decode_header(auth_header, accept_charset)

        scheme, params = self.auth_header.split(' ', 1)

        # make a dict of the params
        items = parse_http_list(params)
        paramsd = parse_keqv_list(items)

        self.realm = paramsd.get('realm')
        self.username = paramsd.get('username')
        self.nonce = paramsd.get('nonce')
        self.uri = paramsd.get('uri')
        self.method = paramsd.get('method')
        self.response = paramsd.get('response')  # the response digest
        self.algorithm = paramsd.get('algorithm', 'MD5').upper()
        self.cnonce = paramsd.get('cnonce')
        self.opaque = paramsd.get('opaque')
        self.qop = paramsd.get('qop')  # qop
        self.nc = paramsd.get('nc')  # nonce count

        # perform some correctness checks
        if self.algorithm not in valid_algorithms:
            raise ValueError(
                self.errmsg("Unsupported value for algorithm: '%s'" %
                            self.algorithm))

        has_reqd = (
            self.username and
            self.realm and
            self.nonce and
            self.uri and
            self.response
        )
        if not has_reqd:
            raise ValueError(
                self.errmsg('Not all required parameters are present.'))

        if self.qop:
            if self.qop not in valid_qops:
                raise ValueError(
                    self.errmsg("Unsupported value for qop: '%s'" % self.qop))
            if not (self.cnonce and self.nc):
                raise ValueError(
                    self.errmsg('If qop is sent then '
                                'cnonce and nc MUST be present'))
        else:
            if self.cnonce or self.nc:
                raise ValueError(
                    self.errmsg('If qop is not sent, '
                                'neither cnonce nor nc can be present'))
Example #9
0
    def wrapped(self):
        if not hasattr(self, 'authenticated'):
            self.authenticated = None

        auth = self.headers.get('Authorization')
        if not self.authenticated and auth is not None:
            token, fields = auth.split(' ', 1)
            if token == 'Digest':
                cred = parse_http_list(fields)
                cred = parse_keqv_list(cred)

                # The request must contain all these keys to
                # constitute a valid response.
                keys = 'realm username nonce uri response'.split()
                if not all(cred.get(key) for key in keys):
                    self.authenticated = False
                elif cred['realm'] != realm or cred['username'] not in accounts:
                    self.authenticated = False
                elif 'qop' in cred and ('nc' not in cred or 'cnonce' not in cred):
                    self.authenticated = False
                else:
                    location = '%s:%s' % (self.command, self.path)
                    location = location.encode('utf8')
                    location = md5hex(location)
                    password = accounts.get_password(cred['username'])
                    if 'qop' in cred:
                        info = (cred['nonce'],
                                cred['nc'],
                                cred['cnonce'],
                                cred['qop'],
                                location)
                    else:
                        info = cred['nonce'], location

                    expect = '%s:%s' % (password, ':'.join(info))
                    expect = md5hex(expect.encode('utf8'))
                    self.authenticated = (expect == cred['response'])
                    if self.authenticated:
                        self.crunchy_username = cred['username']

        if self.authenticated is None:
            msg = "You are not allowed to access this page. Please login first!"
        elif self.authenticated is False:
            msg = "Authenticated Failed"
        if not self.authenticated :
            self.send_response(401)
            nonce = ("%d:%s" % (time.time(), realm)).encode('utf8')
            self.send_header('WWW-Authenticate',
                             'Digest realm="%s",'
                             'qop="auth",'
                             'algorithm="MD5",'
                             'nonce="%s"' % (realm, nonce))
            self.end_headers()
            self.wfile.write(msg.encode('utf8'))
        else:
            return func(self)
Example #10
0
    def wrapped(self):
        if not hasattr(self, 'authenticated'):
            self.authenticated = None
        if self.authenticated:
            return func(self)

        auth = self.headers.get('Authorization')
        if auth is None:
            msg = "You are not allowed to access this page. Please login first!"
            return _error_401(self, msg)

        token, fields = auth.split(' ', 1)
        if token != 'Digest':
            return _error_401(self, 'Unsupported authentication type')

        # Check the header fields of the request.
        cred = parse_http_list(fields)
        cred = parse_keqv_list(cred)
        keys = 'realm', 'username', 'nonce', 'uri', 'response'
        if not all(cred.get(key) for key in keys):
            return _error_401(self, 'Incomplete authentication header')
        if cred['realm'] != self.server.realm:
            return _error_401(self, 'Incorrect realm')
        if 'qop' in cred and ('nc' not in cred or 'cnonce' not in cred):
            return _error_401(self, 'qop with missing nc or cnonce')

        # Check the username.
        username = cred['username']
        password = self.server.get_password(username)
        if not username or password is None:
            return _error_401(self, 'Invalid username or password')

        # Check the digest string.
        location = '%s:%s' % (self.command, self.path)
        location = md5hex(location.encode('utf8'))
        pwhash   = md5hex('%s:%s:%s' % (username, self.server.realm, password))

        if 'qop' in cred:
            info = (cred['nonce'],
                    cred['nc'],
                    cred['cnonce'],
                    cred['qop'],
                    location)
        else:
            info = cred['nonce'], location

        expect = '%s:%s' % (pwhash, ':'.join(info))
        expect = md5hex(expect.encode('utf8'))
        if expect != cred['response']:
            return _error_401(self, 'Invalid username or password')

        # Success!
        self.authenticated = True
        return func(self)
Example #11
0
    def authorized(self):
        tcs = self.server.test_case_server

        auth_header = self.headers.get(tcs.auth_header_recv, None)
        if auth_header is None:
            return False
        scheme, auth = auth_header.split(None, 1)
        if scheme.lower() == tcs.auth_scheme:
            auth_dict = parse_keqv_list(parse_http_list(auth))

            return tcs.digest_authorized(auth_dict, self.command)

        return False
Example #12
0
 def from_header(
     cls: Type['_CacheControl'],
     header: str,
     on_update: Optional[Callable] = None,
 ) -> '_CacheControl':
     cache_control = cls(on_update)
     for item in parse_http_list(header):
         if '=' in item:
             for key, value in parse_keqv_list([item]).items():
                 cache_control._directives[key] = value
         else:
             cache_control._directives[item] = True
     return cache_control
Example #13
0
    def wrapped(self):
        if not hasattr(self, 'authenticated'):
            self.authenticated = None
        if self.authenticated:
            return func(self)

        auth = self.headers.get(u'Authorization')
        if auth is None:
            msg = u"You are not allowed to access this page. Please login first!"
            return _error_401(self, msg)

        token, fields = auth.split(' ', 1)
        if token != 'Digest':
            return _error_401(self, 'Unsupported authentication type')

        # Check the header fields of the request.
        cred = parse_http_list(fields)
        cred = parse_keqv_list(cred)
        keys = u'realm', u'username', u'nonce', u'uri', u'response'
        if not all(cred.get(key) for key in keys):
            return _error_401(self, 'Incomplete authentication header')
        if cred['realm'] != self.server.realm:
            return _error_401(self, 'Incorrect realm')
        if 'qop' in cred and ('nc' not in cred or 'cnonce' not in cred):
            return _error_401(self, 'qop with missing nc or cnonce')

        # Check the username.
        username = cred['username']
        password = self.server.get_password(username)
        if not username or password is None:
            return _error_401(self, 'Invalid username or password')

        # Check the digest string.
        location = u'%s:%s' % (self.command, self.path)
        location = md5hex(location.encode('utf8'))
        pwhash = md5hex('%s:%s:%s' % (username, self.server.realm, password))

        if 'qop' in cred:
            info = (cred['nonce'], cred['nc'], cred['cnonce'], cred['qop'],
                    location)
        else:
            info = cred['nonce'], location

        expect = u'%s:%s' % (pwhash, ':'.join(info))
        expect = md5hex(expect.encode('utf8'))
        if expect != cred['response']:
            return _error_401(self, 'Invalid username or password')

        # Success!
        self.authenticated = True
        return func(self)
Example #14
0
    def compose(self,
                digest=None,
                basic=None,
                username=None,
                password=None,
                challenge=None,
                path=None,
                method=None):
        assert username and password
        if basic or not challenge:
            assert not digest
            userpass = "******" % (username.strip(), password.strip())
            return "Basic %s" % userpass.encode('base64').strip()
        assert challenge and not basic
        path = path or "/"
        (_, realm) = challenge.split('realm="')
        (realm, _) = realm.split('"', 1)
        auth = AbstractDigestAuthHandler()
        auth.add_password(realm, path, username, password)
        (token, challenge) = challenge.split(' ', 1)
        chal = parse_keqv_list(parse_http_list(challenge))

        class FakeRequest(object):
            if six.PY3:

                @property
                def full_url(self):
                    return path

                selector = full_url

                @property
                def data(self):
                    return None
            else:

                def get_full_url(self):
                    return path

                get_selector = get_full_url

                def has_data(self):
                    return False

            def get_method(self):
                return method or "GET"

        retval = "Digest %s" % auth.get_authorization(FakeRequest(), chal)
        return (retval, )
Example #15
0
    def get_opener(self):
        # The opener is yet build ?
        if self.opener is not None:
            return self.opener

        # Build a new opener
        opener = build_opener()
        headers = [ ('User-agent', 'restedit/%s' % __version__) ]

        # Add the "includes"
        for include in self.includes:
            headers.append(include)

        # An authentication ?
        auth_header = self.metadata.get('auth')
        if auth_header is not None:
            if auth_header.lower().startswith('basic'):
                cls_handler = HTTPBasicAuthHandler
                chal = auth_header[6:].strip()
                # Automatically find the username and the password
                username, password = decode_base64(chal).split(':', 1)
            elif auth_header.lower().startswith('digest'):
                cls_handler = HTTPDigestAuthHandler
                # Automatically find the username, but we must ask the password
                # XXX undocumented functions
                chal = parse_keqv_list(parse_http_list(auth_header[7:]))
                username = chal['username']
                password = askPassword(chal['realm'], username)
            else:
                raise NotImplemented

            password_mgr = HTTPPasswordMgrWithDefaultRealm()
            password_mgr.add_password(realm=None,
                                      uri=self.url,
                                      user=username,
                                      passwd=password)

            auth_handler = cls_handler(password_mgr)
            opener.add_handler(auth_handler)

        # A cookie ?
        if self.metadata.get('cookie'):
            headers.append( ('Cookie', self.metadata['cookie']) )

        # All OK
        opener.addheaders = headers
        self.opener = opener
        return opener
Example #16
0
 def parse_rtsp_header(data: bytes) -> Tuple[int, Dict[str, Any], bytes]:
     code, headers, msg = 200, {}, b""
     for line in data.splitlines():
         if line.startswith(b"RTSP/1.0"):
             _, code, msg = line.split(None, 2)
         elif b":" in line:
             h, v = line.split(b":", 1)
             h, v = smart_text(h), smart_text(v)
             if h in MULTIPLE_HEADER:
                 if h not in headers:
                     headers[h] = {}
                 auth, line = v.split(None, 1)
                 items = parse_http_list(line)
                 headers[h][auth] = parse_keqv_list(items)
                 continue
             headers[h] = v.strip()
     return int(code), headers, msg
Example #17
0
def _parse_authorization_header(headers):
    """Parse an OAuth authorization header into a list of 2-tuples"""
    authorization_header = headers.get('Authorization')
    if not authorization_header:
        return [], None

    auth_scheme = 'oauth '
    if authorization_header.lower().startswith(auth_scheme):
        items = parse_http_list(authorization_header[len(auth_scheme):])
        try:
            items = parse_keqv_list(items).items()
            auth_params = [(unescape(k), unescape(v)) for k, v in items]
            realm = dict(auth_params).get('realm')
            return auth_params, realm
        except (IndexError, ValueError):
            pass
    raise ValueError('Malformed authorization header')
Example #18
0
def parse_authorization_header(value):
    """Parse the Authenticate header.

    Returns nothing on failure, opts hash on success with type='basic' or 'digest'
    and other params.

    <http://nullege.com/codes/search/werkzeug.http.parse_authorization_header>
    <http://stackoverflow.com/questions/1349367/parse-an-http-request-authorization-header-with-python>
    <http://bugs.python.org/file34041/0001-Add-an-authorization-header-to-the-initial-request.patch>
    """
    try:
        (auth_type, auth_info) = value.split(' ', 1)
        auth_type = auth_type.lower()
    except ValueError:
        return
    if (auth_type == 'basic'):
        try:
            decoded = base64.b64decode(auth_info).decode(
                'utf-8')  # b64decode gives bytes in python3
            (username, password) = decoded.split(':', 1)
        except (ValueError, TypeError):  # py3, py2
            return
        return {'type': 'basic', 'username': username, 'password': password}
    elif (auth_type == 'digest'):
        try:
            auth_map = parse_keqv_list(parse_http_list(auth_info))
        except ValueError:
            return
        logging.debug(auth_map)
        for key in 'username', 'realm', 'nonce', 'uri', 'response':
            if key not in auth_map:
                return
        if 'qop' in auth_map and ('nc' not in auth_map
                                  or 'cnonce' not in auth_map):
            return
        auth_map['type'] = 'digest'
        return auth_map
    else:
        # unknown auth type
        return
Example #19
0
    def compose(self, digest=None, basic=None, username=None, password=None,
                challenge=None, path=None, method=None):
        assert username and password
        if basic or not challenge:
            assert not digest
            userpass = "******" % (username.strip(), password.strip())
            return "Basic %s" % userpass.encode('base64').strip()
        assert challenge and not basic
        path = path or "/"
        (_, realm) = challenge.split('realm="')
        (realm, _) = realm.split('"', 1)
        auth = AbstractDigestAuthHandler()
        auth.add_password(realm, path, username, password)
        (token, challenge) = challenge.split(' ', 1)
        chal = parse_keqv_list(parse_http_list(challenge))
        class FakeRequest(object):
            if six.PY3:
                @property
                def full_url(self):
                    return path

                selector = full_url

                @property
                def data(self):
                    return None
            else:
                def get_full_url(self):
                    return path

                get_selector = get_full_url

                def has_data(self):
                    return False

            def get_method(self):
                return method or "GET"

        retval = "Digest %s" % auth.get_authorization(FakeRequest(), chal)
        return (retval,)
Example #20
0
def _parseDigestAuthorization(auth_params):
    # Convert the auth params to a dict
    items = parse_http_list(auth_params)
    params = parse_keqv_list(items)

    # Now validate the params

    # Check for required parameters
    required = ["username", "realm", "nonce", "uri", "response"]
    for k in required:
        if k not in params:
            return None

    # If qop is sent then cnonce and nc MUST be present
    if "qop" in params and not ("cnonce" in params and "nc" in params):
        return None

    # If qop is not sent, neither cnonce nor nc can be present
    if ("cnonce" in params or "nc" in params) and "qop" not in params:
        return None

    return params
Example #21
0
def _parseDigestAuthorization(auth_params):
    # Convert the auth params to a dict
    items = parse_http_list(auth_params)
    params = parse_keqv_list(items)

    # Now validate the params

    # Check for required parameters
    required = ["username", "realm", "nonce", "uri", "response"]
    for k in required:
        if k not in params:
            return None

    # If qop is sent then cnonce and nc MUST be present
    if "qop" in params and not ("cnonce" in params and "nc" in params):
        return None

    # If qop is not sent, neither cnonce nor nc can be present
    if ("cnonce" in params or "nc" in params) and "qop" not in params:
        return None

    return params
Example #22
0
def parse_authorization_header(value):
    """Parse the Authenticate header.

    Returns nothing on failure, opts hash on success with type='basic' or 'digest'
    and other params.

    <http://nullege.com/codes/search/werkzeug.http.parse_authorization_header>
    <http://stackoverflow.com/questions/1349367/parse-an-http-request-authorization-header-with-python>
    <http://bugs.python.org/file34041/0001-Add-an-authorization-header-to-the-initial-request.patch>
    """
    try:
        (auth_type, auth_info) = value.split(' ', 1)
        auth_type = auth_type.lower()
    except ValueError:
        return
    if (auth_type == 'basic'):
        try:
            decoded = base64.b64decode(auth_info).decode(
                'utf-8')  # b64decode gives bytes in python3
            (username, password) = decoded.split(':', 1)
        except (ValueError, TypeError):  # py3, py2
            return
        return {'type': 'basic', 'username': username, 'password': password}
    elif (auth_type == 'digest'):
        try:
            auth_map = parse_keqv_list(parse_http_list(auth_info))
        except ValueError:
            return
        logging.debug(auth_map)
        for key in 'username', 'realm', 'nonce', 'uri', 'response':
            if key not in auth_map:
                return
        if 'qop' in auth_map and ('nc' not in auth_map or 'cnonce' not in auth_map):
            return
        auth_map['type'] = 'digest'
        return auth_map
    else:
        # unknown auth type
        return
Example #23
0
def parse_authorization_header(value):
    """Parse the Authenticate header.

    Returns nothing on failure, opts hash on success with type='basic' or 'digest'
    and other params.

    <http://nullege.com/codes/search/werkzeug.http.parse_authorization_header>
    <http://stackoverflow.com/questions/1349367/parse-an-http-request-authorization-header-with-python>
    <http://bugs.python.org/file34041/0001-Add-an-authorization-header-to-the-initial-request.patch>
    """
    try:
        (auth_type, auth_info) = value.split(" ", 1)
        auth_type = auth_type.lower()
    except ValueError as e:
        return
    if auth_type == "basic":
        try:
            decoded = base64.b64decode(auth_info).decode("utf-8")  # b64decode gives bytes in python3
            (username, password) = decoded.split(":", 1)
        except ValueError:  # Exception as e:
            return
        return {"type": "basic", "username": username, "password": password}
    elif auth_type == "digest":
        auth_map = parse_keqv_list(parse_http_list(auth_info))
        print(auth_map)
        for key in "username", "realm", "nonce", "uri", "response":
            if key not in auth_map:
                return
            if "qop" in auth_map:
                if not auth_map.get("nc") or not auth_map.get("cnonce"):
                    return
        auth_map["type"] = "digest"
        return auth_map
    else:
        # unknown auth type
        return
Example #24
0
def parse_dict_header(value):
    return parse_keqv_list(parse_http_list(value))
Example #25
0
 def get_components_from_bearer(header):
     _, _, value = header.partition("Bearer")
     opts = parse_keqv_list(parse_http_list(value)) if value else None
     return opts.get('realm',
                     None), opts.get('service',
                                     None), opts.get('scope', None)
Example #26
0
def parse_dict_header(value):
    return parse_keqv_list(parse_http_list(value))
Example #27
0
    def process_request(self, req, resp):
        if self.debug:
            return
        # CORS Pre-flight
        if req.method == 'OPTIONS':
            resp.status = falcon.HTTP_204
            return
        # http basic auth
        if self.config['server'].get('enable_basic_auth'):
            hdr_auth = req.get_header('AUTHORIZATION')
            if not hdr_auth:
                raise falcon.HTTPUnauthorized('Access denied',
                                              'No auth header', [])

            auth = re.sub('^Basic ', '', hdr_auth)
            usr, pwd = decodestring(auth).split(':')
            if not equals(self.basic_auth.get(usr, ''), pwd):
                logger.warning('basic auth failure: %s', usr)
                raise falcon.HTTPUnauthorized('Access denied',
                                              'Basic auth failure', [])

        segments = req.path.strip('/').split('/')
        if segments[0] == 'api':
            if len(segments) >= 3:
                # twilio validation
                if segments[2] == 'twilio':
                    sig = req.get_header('X_TWILIO_SIGNATURE')
                    if sig is None:
                        logger.warning("no twilio signature found!")
                        raise falcon.HTTPUnauthorized('Access denied',
                                                      'No Twilio signature',
                                                      [])
                    uri = [
                        req.protocol, '://',
                        req.get_header('HOST'),
                        self.config['server'].get('lb_routing_path',
                                                  ''), req.path
                    ]
                    if req.query_string:
                        uri.append('?')
                        uri.append(req.query_string)
                    post_body = req.context['body']
                    expected_sigs = [
                        compute_signature(t, ''.join(uri), post_body)
                        for t in self.twilio_auth_token
                    ]
                    sig = sig.encode('utf8')
                    if sig not in expected_sigs:
                        logger.warning(
                            'twilio validation failure: %s not in possible sigs: %s',
                            sig, expected_sigs)
                        raise falcon.HTTPUnauthorized('Access denied',
                                                      'Twilio auth failure',
                                                      [])
                    return
                elif self.mobile and (segments[2] == 'mobile'
                                      or segments[2] == 'oncall'):
                    # Only allow refresh tokens for /refresh, only access for all else
                    table = 'refresh_token' if segments[
                        3] == 'refresh' else 'access_token'
                    key_query = '''SELECT `key`, `target`.`name`
                                   FROM `%s` JOIN `target` ON `user_id` = `target`.`id`
                                   WHERE `%s`.`id` = %%s
                                   AND `expiration` > %%s''' % (table, table)
                    method = req.method
                    auth = req.get_header('Authorization', required=True)

                    items = urllib2.parse_http_list(auth)
                    parts = urllib2.parse_keqv_list(items)

                    if 'signature' not in parts or 'keyId' not in parts or 'timestamp' not in parts:
                        raise falcon.HTTPUnauthorized(
                            'Authentication failure: invalid header')

                    try:
                        window = int(parts['timestamp'])
                        time_diff = abs(time.time() - window)
                    except ValueError:
                        raise falcon.HTTPUnauthorized(
                            'Authentication failure: invalid header')
                    client_digest = parts['signature']
                    key_id = parts['keyId']
                    body = req.context['body'].decode('utf8')
                    path = req.env['PATH_INFO']
                    qs = req.env['QUERY_STRING']
                    if qs:
                        path = path + '?' + qs
                    text = '%s %s %s %s' % (window, method, path, body)
                    text = text.encode('utf8')

                    conn = db.connect()
                    cursor = conn.cursor()
                    cursor.execute(key_query, (key_id, time.time()))
                    row = cursor.fetchone()
                    conn.close()
                    # make sure that there exists a row for the corresponding username
                    if row is None:
                        raise falcon.HTTPUnauthorized(
                            'Authentication failure: server')
                    key = self.fernet.decrypt(str(row[0]).encode('utf8'))
                    key = key
                    req.context['user'] = row[1]

                    HMAC = hmac.new(key, text, hashlib.sha512)
                    digest = urlsafe_b64encode(HMAC.digest())

                    if hmac.compare_digest(
                            client_digest.encode('utf8'),
                            digest) and time_diff < self.time_window:
                        return
                    else:
                        raise falcon.HTTPUnauthorized(
                            'Authentication failure: server')
                elif segments[2] == 'gmail' or segments[
                        2] == 'gmail-oneclick' or segments[
                            2] == 'slack' or segments[2] == 'ical':
                    return
        elif len(segments) == 1:
            if segments[0] == 'health' or segments[0] == 'healthcheck':
                return
            elif segments[0] == self.config.get('gmail',
                                                {}).get('verification_code'):
                return

        elif segments[0] == 'saml':
            return
        raise falcon.HTTPUnauthorized('Access denied', 'Authentication failed',
                                      [])
def parse_dict_header(value):
    """
    Parses a HTTP dict header value -- i.e. ``"foo=bar, spam=eggs"`` is parsed
    into ``{'foo': 'bar', 'spam': 'eggs'}``.
    """
    return parse_keqv_list(parse_http_list(value))
Example #29
0
 def get_components_from_bearer(header):
     _, _, value = header.partition("Bearer")
     opts = parse_keqv_list(parse_http_list(value)) if value else None
     return opts.get('realm', None), opts.get('service', None), opts.get('scope', None)
        def build_digest_response(self, fields, username, password):
            """
            Takes a Proxy-Authenticate: Digest header and creates a response
            header

            :param fields:
                The string portion of the Proxy-Authenticate header after
                "Digest "

            :param username:
                The username to use for the response

            :param password:
                The password to use for the response

            :return:
                None if invalid Proxy-Authenticate header, otherwise the
                string of fields for the Proxy-Authorization: Digest header
            """

            fields = parse_keqv_list(parse_http_list(fields))

            realm = fields.get('realm')
            nonce = fields.get('nonce')
            qop = fields.get('qop')
            algorithm = fields.get('algorithm')
            if algorithm:
                algorithm = algorithm.lower()
            opaque = fields.get('opaque')

            if algorithm in ['md5', None]:

                def md5hash(string):
                    return hashlib.md5(string).hexdigest()

                hash = md5hash

            elif algorithm == 'sha':

                def sha1hash(string):
                    return hashlib.sha1(string).hexdigest()

                hash = sha1hash

            else:
                return None

            host_port = u"%s:%s" % (self.host, self.port)

            a1 = "%s:%s:%s" % (username, realm, password)
            a2 = "CONNECT:%s" % host_port
            ha1 = hash(a1)
            ha2 = hash(a2)

            if qop is None:
                response = hash(u"%s:%s:%s" % (ha1, nonce, ha2))
            elif qop == 'auth':
                nc = '00000001'
                cnonce = hash(os.urandom(8))[:8]
                response = hash(u"%s:%s:%s:%s:%s:%s" %
                                (ha1, nonce, nc, cnonce, qop, ha2))
            else:
                return None

            response_fields = {
                'username': username,
                'realm': realm,
                'nonce': nonce,
                'response': response,
                'uri': host_port
            }
            if algorithm:
                response_fields['algorithm'] = algorithm
            if qop == 'auth':
                response_fields['nc'] = nc
                response_fields['cnonce'] = cnonce
                response_fields['qop'] = qop
            if opaque:
                response_fields['opaque'] = opaque

            return ', '.join([
                u"%s=\"%s\"" % (field, response_fields[field])
                for field in response_fields
            ])
        def build_digest_response(self, fields, username, password):
            """
            Takes a Proxy-Authenticate: Digest header and creates a response
            header

            :param fields:
                The string portion of the Proxy-Authenticate header after
                "Digest "

            :param username:
                The username to use for the response

            :param password:
                The password to use for the response

            :return:
                None if invalid Proxy-Authenticate header, otherwise the
                string of fields for the Proxy-Authorization: Digest header
            """

            fields = parse_keqv_list(parse_http_list(fields))

            realm = fields.get('realm')
            nonce = fields.get('nonce')
            qop = fields.get('qop')
            algorithm = fields.get('algorithm')
            if algorithm:
                algorithm = algorithm.lower()
            opaque = fields.get('opaque')

            if algorithm in ['md5', None]:
                def md5hash(string):
                    return hashlib.md5(string).hexdigest()
                hash = md5hash

            elif algorithm == 'sha':
                def sha1hash(string):
                    return hashlib.sha1(string).hexdigest()
                hash = sha1hash

            else:
                return None

            host_port = u"%s:%s" % (self.host, self.port)

            a1 = "%s:%s:%s" % (username, realm, password)
            a2 = "CONNECT:%s" % host_port
            ha1 = hash(a1)
            ha2 = hash(a2)

            if qop == None:
                response = hash(u"%s:%s:%s" % (ha1, nonce, ha2))
            elif qop == 'auth':
                nc = '00000001'
                cnonce = hash(os.urandom(8))[:8]
                response = hash(u"%s:%s:%s:%s:%s:%s" % (ha1, nonce, nc, cnonce, qop, ha2))
            else:
                return None

            response_fields = {
                'username': username,
                'realm': realm,
                'nonce': nonce,
                'response': response,
                'uri': host_port
            }
            if algorithm:
                response_fields['algorithm'] = algorithm
            if qop == 'auth':
                response_fields['nc'] = nc
                response_fields['cnonce'] = cnonce
                response_fields['qop'] = qop
            if opaque:
                response_fields['opaque'] = opaque

            return ', '.join([u"%s=\"%s\"" % (field, response_fields[field]) for field in response_fields])
Example #32
0
 def __eq__(self, authorization_header_value):
     auth_type, auth_value = authorization_header_value.split(maxsplit=1)
     assert auth_type.lower() == "digest"
     assert parse_keqv_list(parse_http_list(auth_value)) == self._parameters
     return True
Example #33
0
def parse_keqv_list(l):
    """A unicode-safe version of urllib2.parse_keqv_list"""
    # With Python 2.6, parse_http_list handles unicode fine
    return urllib2.parse_keqv_list(l)
Example #34
0
def parse_dict_header(value):
    """
    Parses a HTTP dict header value -- i.e. ``"foo=bar, spam=eggs"`` is parsed
    into ``{'foo': 'bar', 'spam': 'eggs'}``.
    """
    return parse_keqv_list(parse_http_list(value))