def _on_xpath(self, unused_env, url_config, unused_client_cert, data): """ Handles the authentication based on XPath expressions. """ if not data: return AuthResult(False, AUTH_XPATH_NO_DATA) request = etree.fromstring(data) prefix = 'xpath-' expressions = [url_config[header] for header in url_config if header.startswith(prefix)] if not expressions: # It's clearly an error. We've been requested to use XPath yet no # expressions have been defined in the config. raise SecWallException('No XPath expressions were found in the config') for expr in expressions: if not expr(request): return AuthResult(False, AUTH_XPATH_EXPR_MISMATCH) else: auth_result = AuthResult(True, '0') auth_result.auth_info = map(str, expressions) return auth_result
def on_ssl_cert(url_config, client_cert, field_prefix, needs_auth_info=True): """ Visit _RequestApp._on_ssl_cert method's docstring. """ if client_cert: config_fields = {} for field, value in url_config.items(): if field.startswith(field_prefix): config_fields[field.split(field_prefix)[1]] = value # There are no fields so the user just wants the connection be # encrypted and the client use client certificate however they're # not interested in the cert's fields - so as long as the CA is # OK (and we know it is because otherwise we wouldn't have gotten # so far), we let the client in. if not config_fields: return True else: subject = client_cert.get('subject') if not subject: return AuthResult(False, AUTH_CERT_NO_SUBJECT) cert_fields = dict((elem[0][0].encode('utf-8'), elem[0][1].encode('utf-8')) for elem in subject) for config_field, config_value in config_fields.items(): cert_value = cert_fields.get(config_field) if not cert_value: return AuthResult(False, AUTH_CERT_NO_VALUE) if cert_value != config_value: return AuthResult(False, AUTH_CERT_VALUE_MISMATCH) else: auth_result = AuthResult(True, '0') if needs_auth_info: auth_result.auth_info = dict((quote_plus(k), quote_plus(v)) for k, v in cert_fields.iteritems()) return auth_result
def _on_custom_http(self, env, url_config, *ignored): """ Handles the authentication based on custom HTTP headers. """ prefix = 'custom-http-' expected_headers = {} expected_headers_keys = (header for header in url_config if header.startswith(prefix)) for key in expected_headers_keys: # This set of operations (.split, .upper, .replace) could be done once # when the config's read, well, it's a room for improvement. expected_headers[str(key)] = str(env.get('HTTP_' + key.split(prefix)[1].upper().replace('-', '_'), '')) if not expected_headers: # It's clearly an error. We've been requested to use custom HTTP # headers but none are in the config. raise SecWallException('No custom HTTP headers were found in the config') for key, value in expected_headers.iteritems(): if not value: return AuthResult(False, AUTH_CUSTOM_HTTP_NO_HEADER) if value != url_config[key]: return AuthResult(False, AUTH_CUSTOM_HTTP_HEADER_MISMATCH) else: auth_result = AuthResult(True, '0') auth_result.auth_info = expected_headers return auth_result
def test_auth_result_repr(): """ Tests the AuthResult's __repr__ output. """ at_pattern = '\w*' status, code, description = [uuid4().hex for x in range(3)] auth_info = {b'abc':b'def'} a1 = AuthResult(status, code, description) a1.auth_info = auth_info r = repr(a1) pattern = '<AuthResult at {0} status={1} code={2} description={3} auth_info={{abc: def}}\n>' pattern = pattern.format(at_pattern, status, code, description) regexp = re.compile(pattern) assert_true(regexp.match(r) is not None, (pattern, r))
def on_basic_auth(env, url_config, needs_auth_info=True): """ Visit _RequestApp._on_basic_auth method's docstring. """ username = url_config['basic-auth-username'] result = _on_basic_auth(env.get('HTTP_AUTHORIZATION', ''), username, url_config['basic-auth-password']) is_success = result is True # Yes, need to check for True auth_result = AuthResult(is_success) if is_success: if needs_auth_info: auth_result.auth_info = {b'basic-auth-username': quote_plus(username).encode('utf-8')} else: auth_result.code = result return auth_result
def test_auth_result_repr(): """ Tests the AuthResult's __repr__ output. """ at_pattern = '\w*' status, code, description = [uuid4().hex for x in range(3)] auth_info = {b'abc': b'def'} a1 = AuthResult(status, code, description) a1.auth_info = auth_info r = repr(a1) pattern = '<AuthResult at {0} status={1} code={2} description={3} auth_info={{abc: def}}\n>' pattern = pattern.format(at_pattern, status, code, description) regexp = re.compile(pattern) assert_true(regexp.match(r) is not None, (pattern, r))
def on_wsse_pwd(wsse, url_config, data, needs_auth_info=True): """ Visit _RequestApp._on_wsse_pwd method's docstring. """ if not data: return AuthResult(False, AUTH_WSSE_NO_DATA) request = etree.fromstring(data) try: ok, wsse_username = wsse.validate(request, url_config) except SecurityException as e: return AuthResult(False, AUTH_WSSE_VALIDATION_ERROR, e.description) else: auth_result = AuthResult(True, '0') if needs_auth_info: auth_result.auth_info = {b'wsse-pwd-username': str(wsse_username)} return auth_result
def _on_basic_auth(self, env, url_config, *ignored): """ Handles HTTP Basic Authentication. """ auth = env.get('HTTP_AUTHORIZATION') if not auth: return AuthResult(False, AUTH_BASIC_NO_AUTH) prefix = 'Basic ' if not auth.startswith(prefix): return AuthResult(False, AUTH_BASIC_INVALID_PREFIX) _, auth = auth.split(prefix) auth = auth.strip().decode('base64') username, password = auth.split(':', 1) if username == url_config['basic-auth-username'] and \ password == url_config['basic-auth-password']: auth_result = AuthResult(True, '0') auth_result.auth_info = {b'basic-auth-username': quote_plus(username)} return auth_result else: return AuthResult(False, AUTH_BASIC_USERNAME_OR_PASSWORD_MISMATCH)
def _on_ssl_cert(self, env, url_config, client_cert, data): """ Validates the client SSL/TLS certificates, its very existence and the values of its fields (commonName, organizationName etc.) """ if client_cert: field_prefix = 'ssl-cert-' config_fields = {} for field, value in url_config.items(): if field.startswith(field_prefix): config_fields[field.split(field_prefix)[1]] = value # There are no fields so the user just wants the connection be # encrypted and the client use client certificate however they're # not interested in the cert's fields - so as long as the CA is # OK (and we know it is because otherwise we wouldn't have gotten # so far), we let the client in. if not config_fields: return True else: subject = client_cert.get('subject') if not subject: return AuthResult(False, AUTH_CERT_NO_SUBJECT) cert_fields = dict(elem[0] for elem in subject) for config_field, config_value in config_fields.items(): cert_value = cert_fields.get(config_field) if not cert_value: return AuthResult(False, AUTH_CERT_NO_VALUE) if cert_value != config_value: return AuthResult(False, AUTH_CERT_VALUE_MISMATCH) else: auth_result = AuthResult(True, '0') auth_result.auth_info = dict((quote_plus(k), quote_plus(v)) for k, v in cert_fields.iteritems()) return auth_result
def on_wsse_pwd(wsse, url_config, data, needs_auth_info=True): """ Visit _RequestApp._on_wsse_pwd method's docstring. """ if not data: return AuthResult(False, AUTH_WSSE_NO_DATA) request = etree.fromstring(data) try: ok, wsse_username = wsse.validate(request, url_config) except SecurityException, e: return AuthResult(False, AUTH_WSSE_VALIDATION_ERROR, e.description) else: auth_result = AuthResult(True, '0') if needs_auth_info: auth_result.auth_info = {b'wsse-pwd-username': str(wsse_username)} return auth_result def _on_basic_auth(auth, expected_username, expected_password): """ A low-level call for checking the HTTP Basic Auth credentials. """ if not auth: return AUTH_BASIC_NO_AUTH prefix = 'Basic ' if not auth.startswith(prefix): return AUTH_BASIC_INVALID_PREFIX _, auth = auth.split(prefix) auth = auth.strip().decode('base64')