fix(auth): report rejected org switches and tolerate a null active_organization#1720
Draft
aucahuasi wants to merge 1 commit into
Draft
fix(auth): report rejected org switches and tolerate a null active_organization#1720aucahuasi wants to merge 1 commit into
aucahuasi wants to merge 1 commit into
Conversation
…ganization
Two defects on the login path, both of which turn a server response the
client should act on into silence.
1. _switch_org posted to /api/v2/o/<slug>/switch/, handed the response to
log_requests_error (which only logs and never raises), then cached the
(org, token) pair unconditionally. A server that rejected the switch was
recorded as having accepted it, and the early return at the top of the
method made every later attempt a silent no-op for the rest of the
session. The client could not distinguish a 200 from a 403.
The pair is now cached only on a 2xx response. The method returns a bool
so callers can tell acceptance from rejection, and logs a warning naming
the status code. A rejected switch is retried on the next call. Login
still succeeds when the switch fails, because not every deployment
exposes the endpoint, so callers must not treat False as fatal.
2. _finalize_login read json_response.get('active_organization', {}) and
then called .get('slug') on the result. The dict default only covers a
missing key, so a server sending "active_organization": null, or any
non-dict value, raised AttributeError rather than reading as "no
organization bound". The username/password and personal-key paths now
apply the same isinstance guard the SSO path already used.
Tests: nine cases in graphistry/tests/test_arrow_uploader.py covering
accepted, rejected, retried, transport-failed, already-cached and no-op
switches, plus login against a null, missing and non-dict
active_organization. Existing coverage mocked _switch_org itself, so it
asserted only that the client calls switch, never that the server accepted
it. All nine fail on the prior implementation.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two defects on the login path. Both take a server response the client should act on and turn it into silence.
1. A rejected organization switch was recorded as a success
ArrowUploader._switch_orgposts to/api/v2/o/<slug>/switch/, passes the response tolog_requests_error, and then cached the(org, token)pair unconditionally.log_requests_erroronly logs. It never raises. So a server that rejected the switch with a 403 was recorded as having accepted it, and the early return at the top of the method turned every later attempt into a silent no-op for the rest of the session. The client had no way to distinguish a 200 from a 403.Now the pair is cached only on a 2xx response. The method returns a bool so callers can tell acceptance from rejection, and logs a warning naming the status code. A rejected switch is retried on the next call instead of being skipped forever.
Login still succeeds when a switch fails, because not every deployment exposes the endpoint. Callers must not treat a
Falsereturn as fatal, and that contract is stated in the docstring.2. Login crashed on a null
active_organization_finalize_loginread:The dict default only covers a missing key. A server sending
"active_organization": null, or any non-dict value, made the second line raiseAttributeErrorrather than reading as "no organization bound".The username/password and personal-key paths now apply the same
isinstanceguard that the SSO path insso_get_tokenalready used.Tests
Nine new cases in
graphistry/tests/test_arrow_uploader.py:_switch_org: accepted, rejected, retried after rejection, transport failure, already cached, and the no-op when org or token is absent.login: against a null, a missing, and a non-dictactive_organization.All nine fail on the prior implementation. The null case fails with exactly
AttributeError: 'NoneType' object has no attribute 'get'.Existing coverage mocked
_switch_orgitself (test_login_invokes_switch_organd friends), so it asserted that the client calls switch, never that the server accepted it. That is why the suite stayed green while the behavior was wrong. The new tests mock the HTTP layer instead.Verified locally:
test_arrow_uploader.pyandtest_client_session.pypass at 71 tests,bin/lint.shpasses, andmypyreports no errors inarrow_uploader.py.Notes for review
_switch_orgchanges its return type fromNonetobool. Every current caller ignores the return value, so nothing breaks, but it is a signature change on a private method and callers may now want to branch on it.Behavior on a failed switch is deliberately unchanged in one respect: login proceeds. The only difference is that the failure is now visible in the logs and not remembered as a success.