async def test_basic_failure(setup: SetupTest) -> None: basic_b64 = base64.b64encode(b"bogus-string").decode() r = await setup.client.get( "/auth", params={"scope": "exec:admin"}, headers={"Authorization": f"Basic {basic_b64}"}, ) assert r.status_code == 400 authenticate = parse_www_authenticate(r.headers["WWW-Authenticate"]) assert isinstance(authenticate, AuthErrorChallenge) assert authenticate.auth_type == AuthType.Bearer assert authenticate.realm == setup.config.realm assert authenticate.error == AuthError.invalid_request for basic in (b"foo:foo", b"x-oauth-basic:foo", b"foo:x-oauth-basic"): basic_b64 = base64.b64encode(basic).decode() r = await setup.client.get( "/auth", params={ "scope": "exec:admin", "auth_type": "basic" }, headers={"Authorization": f"Basic {basic_b64}"}, ) assert r.status_code == 401 authenticate = parse_www_authenticate(r.headers["WWW-Authenticate"]) assert not isinstance(authenticate, AuthErrorChallenge) assert authenticate.auth_type == AuthType.Basic assert authenticate.realm == setup.config.realm
async def test_auth_forbidden(setup: SetupTest) -> None: r = await setup.client.get( "/auth/forbidden", params=[("scope", "exec:test"), ("scope", "exec:admin")], ) assert r.status_code == 403 assert r.headers["Cache-Control"] == "no-cache, must-revalidate" authenticate = parse_www_authenticate(r.headers["WWW-Authenticate"]) assert isinstance(authenticate, AuthErrorChallenge) assert authenticate.auth_type == AuthType.Bearer assert authenticate.realm == setup.config.realm assert authenticate.error == AuthError.insufficient_scope assert authenticate.scope == "exec:admin exec:test" assert "Token missing required scope" in r.text r = await setup.client.get("/auth/forbidden", params={ "scope": "exec:admin", "auth_type": "basic" }) assert r.status_code == 403 assert r.headers["Cache-Control"] == "no-cache, must-revalidate" authenticate = parse_www_authenticate(r.headers["WWW-Authenticate"]) assert not isinstance(authenticate, AuthErrorChallenge) assert authenticate.auth_type == AuthType.Basic assert authenticate.realm == setup.config.realm assert "Token missing required scope" in r.text
async def test_invalid_auth(setup: SetupTest) -> None: r = await setup.client.get( "/auth", params={"scope": "exec:admin"}, headers={"Authorization": "Bearer"}, ) assert r.status_code == 400 authenticate = parse_www_authenticate(r.headers["WWW-Authenticate"]) assert isinstance(authenticate, AuthErrorChallenge) assert authenticate.auth_type == AuthType.Bearer assert authenticate.realm == setup.config.realm assert authenticate.error == AuthError.invalid_request r = await setup.client.get( "/auth", params={"scope": "exec:admin"}, headers={"Authorization": "token foo"}, ) assert r.status_code == 400 authenticate = parse_www_authenticate(r.headers["WWW-Authenticate"]) assert isinstance(authenticate, AuthErrorChallenge) assert authenticate.auth_type == AuthType.Bearer assert authenticate.realm == setup.config.realm assert authenticate.error == AuthError.invalid_request r = await setup.client.get( "/auth", params={"scope": "exec:admin"}, headers={"Authorization": "Bearer token"}, ) assert r.status_code == 401 assert r.headers["Cache-Control"] == "no-cache, must-revalidate" authenticate = parse_www_authenticate(r.headers["WWW-Authenticate"]) assert isinstance(authenticate, AuthErrorChallenge) assert authenticate.auth_type == AuthType.Bearer assert authenticate.realm == setup.config.realm assert authenticate.error == AuthError.invalid_token # Create a nonexistent token. token = Token() r = await setup.client.get( "/auth", params={"scope": "exec:admin"}, headers={"Authorization": f"Bearer {token}"}, ) assert r.status_code == 401 assert r.headers["Cache-Control"] == "no-cache, must-revalidate" authenticate = parse_www_authenticate(r.headers["WWW-Authenticate"]) assert isinstance(authenticate, AuthErrorChallenge) assert authenticate.auth_type == AuthType.Bearer assert authenticate.realm == setup.config.realm assert authenticate.error == AuthError.invalid_token
async def test_internal_errors(setup: SetupTest) -> None: token_data = await setup.create_session_token(scopes=["read:some"]) # Delegating a token with a scope the original doesn't have will fail. r = await setup.client.get( "/auth", params={ "scope": "read:some", "delegate_to": "a-service", "delegate_scope": "read:all", }, headers={"Authorization": f"Bearer {token_data.token}"}, ) assert r.status_code == 403 assert r.headers["Cache-Control"] == "no-cache, must-revalidate" authenticate = parse_www_authenticate(r.headers["WWW-Authenticate"]) assert isinstance(authenticate, AuthErrorChallenge) assert authenticate.error == AuthError.insufficient_scope assert authenticate.scope == "read:all read:some" # Cannot request a notebook token and an internal token at the same time. r = await setup.client.get( "/auth", params={ "scope": "read:some", "notebook": "true", "delegate_to": "a-service", "delegate_scope": "read:some", }, headers={"Authorization": f"Bearer {token_data.token}"}, ) assert r.status_code == 422
async def test_no_auth(setup: SetupTest) -> None: r = await setup.client.get("/auth/tokens/influxdb/new") assert r.status_code == 401 authenticate = parse_www_authenticate(r.headers["WWW-Authenticate"]) assert not isinstance(authenticate, AuthErrorChallenge) assert authenticate.auth_type == AuthType.Bearer assert authenticate.realm == setup.config.realm
async def test_ajax_unauthorized(setup: SetupTest) -> None: """Test that AJAX requests without auth get 403, not 401.""" r = await setup.client.get( "/auth", params={"scope": "exec:admin"}, headers={"X-Requested-With": "XMLHttpRequest"}, ) assert r.status_code == 403 authenticate = parse_www_authenticate(r.headers["WWW-Authenticate"]) assert not isinstance(authenticate, AuthErrorChallenge) assert authenticate.auth_type == AuthType.Bearer assert authenticate.realm == setup.config.realm
async def test_no_auth(setup: SetupTest) -> None: r = await setup.client.get("/auth", params={"scope": "exec:admin"}) assert r.status_code == 401 assert r.headers["Cache-Control"] == "no-cache, must-revalidate" authenticate = parse_www_authenticate(r.headers["WWW-Authenticate"]) assert not isinstance(authenticate, AuthErrorChallenge) assert authenticate.auth_type == AuthType.Bearer assert authenticate.realm == setup.config.realm r = await setup.client.get("/auth", params={ "scope": "exec:admin", "auth_type": "bearer" }) assert r.status_code == 401 assert r.headers["Cache-Control"] == "no-cache, must-revalidate" authenticate = parse_www_authenticate(r.headers["WWW-Authenticate"]) assert not isinstance(authenticate, AuthErrorChallenge) assert authenticate.auth_type == AuthType.Bearer assert authenticate.realm == setup.config.realm r = await setup.client.get("/auth", params={ "scope": "exec:admin", "auth_type": "bogus" }) assert r.status_code == 422 r = await setup.client.get("/auth", params={ "scope": "exec:admin", "auth_type": "basic" }) assert r.status_code == 401 assert r.headers["Cache-Control"] == "no-cache, must-revalidate" authenticate = parse_www_authenticate(r.headers["WWW-Authenticate"]) assert not isinstance(authenticate, AuthErrorChallenge) assert authenticate.auth_type == AuthType.Basic assert authenticate.realm == setup.config.realm
async def test_satisfy_all(setup: SetupTest) -> None: token_data = await setup.create_session_token(scopes=["exec:test"]) r = await setup.client.get( "/auth", params=[("scope", "exec:test"), ("scope", "exec:admin")], headers={"Authorization": f"Bearer {token_data.token}"}, ) assert r.status_code == 403 assert r.headers["Cache-Control"] == "no-cache, must-revalidate" authenticate = parse_www_authenticate(r.headers["WWW-Authenticate"]) assert isinstance(authenticate, AuthErrorChallenge) assert authenticate.auth_type == AuthType.Bearer assert authenticate.realm == setup.config.realm assert authenticate.error == AuthError.insufficient_scope assert authenticate.scope == "exec:admin exec:test" assert "Token missing required scope" in r.text
async def test_invalid(setup: SetupTest, caplog: LogCaptureFixture) -> None: token_data = await setup.create_session_token() issuer = setup.factory.create_token_issuer() oidc_token = issuer.issue_token(token_data, jti="some-jti") caplog.clear() r = await setup.client.get( "/auth/userinfo", headers={"Authorization": f"token {oidc_token.encoded}"}, ) assert r.status_code == 400 authenticate = parse_www_authenticate(r.headers["WWW-Authenticate"]) assert isinstance(authenticate, AuthErrorChallenge) assert authenticate.auth_type == AuthType.Bearer assert authenticate.realm == setup.config.realm assert authenticate.error == AuthError.invalid_request assert authenticate.error_description == "Unknown Authorization type token" log = json.loads(caplog.record_tuples[0][2]) assert log == { "error": "Unknown Authorization type token", "event": "Invalid request", "level": "warning", "logger": "gafaelfawr", "method": "GET", "path": "/auth/userinfo", "remote": "127.0.0.1", "request_id": ANY, "user_agent": ANY, } r = await setup.client.get( "/auth/userinfo", headers={"Authorization": f"bearer{oidc_token.encoded}"}, ) assert r.status_code == 400 authenticate = parse_www_authenticate(r.headers["WWW-Authenticate"]) assert isinstance(authenticate, AuthErrorChallenge) assert authenticate.auth_type == AuthType.Bearer assert authenticate.realm == setup.config.realm assert authenticate.error == AuthError.invalid_request assert authenticate.error_description == "Malformed Authorization header" caplog.clear() r = await setup.client.get( "/auth/userinfo", headers={"Authorization": f"bearer XXX{oidc_token.encoded}"}, ) assert r.status_code == 401 authenticate = parse_www_authenticate(r.headers["WWW-Authenticate"]) assert isinstance(authenticate, AuthErrorChallenge) assert authenticate.auth_type == AuthType.Bearer assert authenticate.realm == setup.config.realm assert authenticate.error == AuthError.invalid_token assert authenticate.error_description log = json.loads(caplog.record_tuples[0][2]) assert log == { "error": ANY, "event": "Invalid token", "level": "warning", "logger": "gafaelfawr", "method": "GET", "path": "/auth/userinfo", "remote": "127.0.0.1", "request_id": ANY, "token_source": "bearer", "user_agent": ANY, }
def assert_unauthorized_is_correct(r: Response, config: Config) -> None: assert r.status_code == 401 challenge = parse_www_authenticate(r.headers["WWW-Authenticate"]) assert not isinstance(challenge, AuthErrorChallenge) assert challenge.auth_type == AuthType.Bearer assert challenge.realm == config.realm