Repository navigation
Return 401 from CAS v2 login when the CAS response is not valid - #381
Conversation
32d36a1 to
4f608d0
Compare
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #381 +/- ##
==========================================
+ Coverage 82.16% 82.22% +0.06%
==========================================
Files 113 113
Lines 10009 10015 +6
==========================================
+ Hits 8224 8235 +11
+ Misses 1785 1780 -5
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
ntorqulu
left a comment
There was a problem hiding this comment.
Hi @DavidAmsellem, just some minor comments about exception handling of the verify_ticket call.
| user, attributes, pgtiou = cas_client.verify_ticket(ticket) | ||
| try: | ||
| user, attributes, pgtiou = cas_client.verify_ticket(ticket) | ||
| except ET.ParseError: |
There was a problem hiding this comment.
verify_ticket calls cas.CASClientV2.verify_response. This endpoint can return ParseError, (None, None, None), IndexError, AttributeError or ValueError.
I would do something like this, adding a logger.warning to be able to check the error raised in the logs:
| except ET.ParseError: | |
| except (ET.ParseError, IndexError, AttributeError, ValueError) as exc: | |
| logger.warning("Unexpected CAS ticket validation response: %s", exc) | |
| user = attributes = pgtiou = None |
And bound all user, attributes, pgtiou to prevent any future error from a refactor.
WDYT?
|
Thanks @ntorqulu, good call. I checked each case against python-cas's
Done in 35e188d: the One deviation from the suggestion: Thanks for rebasing the branch and linking #383. I put the change on top of your commit and only reworded the v4 CHANGELOG line to "not a valid CAS document", since it now covers more than malformed XML. The full suite passes locally (749 tests), and |
|
Perfect, thanks @DavidAmsellem. Will do a quick look at the new changes, but I agree on avoid setting unused variables to |
ntorqulu
left a comment
There was a problem hiding this comment.
Hi @DavidAmsellem, all perfect from my side.
While reviewing it I noticed that the function is mixing sync calls inside an async function, and that there's no timeout for the function call, which can make workers stall here.
Not a huge bug, but thanks for fixing the exception handling here so we can pay attention to this part of the code!
I'll write another issue to check with more detail the "/cas/v2/login" endpoint and merge this pr!
python-cas parses the serviceValidate response with ElementTree and does not catch ParseError, so a CAS server answering with something other than XML (an HTML error page, an empty body) made /v4/auth/cas/v2/login fail with a 500. Treat it as an unverified ticket and return 401 "Can't verify user", as /v3/login does. Add tests for the ticket path: a successful validation, an authenticationFailure and a response that is not XML. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Besides ParseError, CASClientV2.verify_response raises IndexError for an empty serviceResponse, AttributeError for an authenticationSuccess with no user and ValueError for a document without the CAS namespace. Treat them as an unverified ticket too, and log the response problem as a warning so it can be found in the logs. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
35e188d to
9dc9bfe
Compare
Follow-up to @ntorqulu's note in #378:
/v4/auth/cas/v2/loginhas the same problem as/v3/loginhad.What
When the request carries a
ticket, the endpoint callsCASClient.verify_ticket(). python-cas parses theserviceValidateresponse without catching parse errors, so if the CAS server answers with something that isn't a valid CAS document, the exception reaches FastAPI and the endpoint returns a 500:ParseErrorfor a body that isn't XML (an HTML error page, an empty body),IndexErrorfor an emptyserviceResponse,AttributeErrorfor anauthenticationSuccesswith nouser, andValueErrorfor a document without the CAS namespace.This catches those four exceptions around
verify_ticket(), logs the problem as a warning, and treats it as an unverified ticket: the endpoint returns the existing 401Can't verify userresponse, as/v3/logindoes after #378. Other errors, such as a network failure reaching the CAS server, are not caught and still surface as before. Tracked in #383.Tests
The ticket path had no tests.
TestCASV2Login.test_ticket_validationpatches onlyCASClientV2.get_verification_response(the HTTP call to the CAS server), so python-cas's own parsing runs:authenticationSuccessresponse: 200 with the user and a tokenauthenticationFailureresponse: 401serviceResponse, anauthenticationSuccesswith nouser, and a document without the CAS namespace: 401 (500 before this change)The last four cases fail without the change, on both SQLite and PostgreSQL. The full suite passes locally (749 tests, rebased onto
mainafter #378), andruff checkpasses on the changed files. I added a line toCHANGELOG.mdunder Unreleased.AI use: I used Claude Code (Opus 5.5) to help analyse the code, plan the change and write the tests. I reviewed every line, ran the full test suite locally against SQLite and PostgreSQL, and checked that the new test cases fail without the change.
🤖 Generated with Claude Code