Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

When token invalid user must log back in #750

Merged
merged 5 commits into from
Feb 3, 2020

Conversation

sssoleileraaa
Copy link
Contributor

@sssoleileraaa sssoleileraaa commented Jan 30, 2020

Description

Fixes #662
Fixes #736

Related: #420 remains open

Test Plan for #662

  1. With a valid token, log out of the client when there are pending jobs and no longer see error message reported in Network error after signing out #662: https://user-images.githubusercontent.com/213636/70762572-d5f70880-1d05-11ea-97c0-f7e2c9b086a5.png

Test Plan for #736

  1. Update /securedrop/journalist_app/api.py in the securedrop repo to return AuthError when making a get_sources or get_all_replies or get_submissions request and see the client log you out and open a login window, you can use this diff if you'd like, see how this results in an AuthError:
     def get_all_sources():
+        return jsonify({'error':'test', 'message':'test'})

Checklist

  • These changes should not need testing in Qubes but as always please test in qubes if you'd like and want bonus points

@sssoleileraaa sssoleileraaa force-pushed the when-token-invalid-user-must-log-back-in branch from 4a0f7d2 to 47c04fc Compare January 30, 2020 04:54
@sssoleileraaa sssoleileraaa marked this pull request as ready for review January 30, 2020 04:56
@sssoleileraaa
Copy link
Contributor Author

This is what the user will see now when the token is invalid:

Screenshot from 2020-01-29 20-09-07

Copy link
Contributor

@kushaldas kushaldas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While clicking on logout (just after logging in, and I can see the source names in the source list) causes a crash.

Traceback (most recent call last):
  File "/home/kdas/code/securedrop-client/securedrop_client/logic.py", line 288, in on_queue_paused
    self.logout()
  File "/home/kdas/code/securedrop-client/securedrop_client/logic.py", line 495, in logout
    self.call_api(self.api.logout,
AttributeError: 'NoneType' object has no attribute 'logout'

@sssoleileraaa
Copy link
Contributor Author

While clicking on logout (just after logging in, and I can see the source names in the source list) causes a crash.

@kushaldas what is the STR? There shouldn't be any log out buttons showing when the token is invalid and we open the login window

@sssoleileraaa
Copy link
Contributor Author

also, this doesn't sound related to this PR

@kushaldas
Copy link
Contributor

@kushaldas what is the STR? There shouldn't be any log out buttons showing when the token is invalid and we open the login window

  • NUM_SOURCES=200 make dev
  • login to the client
  • click on signout

@ninavizz
Copy link
Member

This isn't quite right. The bang-message on the Login window is to communicate an error from that screen, in response to something the user did—not an error that lands the user on the Login page. Also, this window was never designed to scale with all the needs of an in-app login (such as being logged-out do to session inactivity, etc). At some point, the in-app login just needs to get implemented.

To get the messaging right so this can go out for Beta: what did the user do to get here, and what are the conditions around this screen?

@sssoleileraaa
Copy link
Contributor Author

Thanks @kushaldas I'll try to repro on master and this branch... will report back

@sssoleileraaa
Copy link
Contributor Author

To get the messaging right so this can go out for Beta: what did the user do to get here, and what are the conditions around this screen?

This is for an edge case where the user is logged in 8+ hours and the token expires

This isn't quite right. The bang-message on the Login window is to communicate an error from that screen, in response to something the user did—not an error that lands the user on the Login page.

So, for this first iteration we have two choices: (1) show an error message with the current way we show error messages in the login dialog, or (2) show no error message

The problem I see with number 2 is that the user might not understand why we logged them out when their token expires.

Also, this window was never designed to scale with all the needs of an in-app login (such as being logged-out do to session inactivity, etc). At some point, the in-app login just needs to get implemented.

I think we have an issue for creating a different log in screen for the sign in button: https://app.zeplin.io/project/5c807ea562f734bd2756b243/screen/5cf1b9bda9ebc81e5009b540:
Screenshot from 2020-01-30 08-47-25

But we haven't yet collaborated on a design for token expiration yet. Let's discuss further in standup.

@eloquence
Copy link
Member

eloquence commented Jan 30, 2020

For this edge case of session expiry, I like @creviera's solution a lot @ninavizz. I'd rather have a message than no message. And I don't think we're likely to prioritize in-client login soon, even post-beta -- there'll just be too many other requests and issues hitting us.

I also don't know that it'd be a good idea to use here, even if we had it, given that the user may have stepped away, and may not expect content to be showing on the screen when they unlock the screensaver.

I understand your point that this error style is intended for login errors. If you feel strongly that it should not be used here, one change we might still be able to make now is to style this message slightly differently (e.g., different color, icon).

@sssoleileraaa sssoleileraaa force-pushed the when-token-invalid-user-must-log-back-in branch from 58b7ba2 to 7263eb3 Compare January 30, 2020 17:40
@sssoleileraaa
Copy link
Contributor Author

@kushaldas, what you reported is indeed a bug on master as well as in this pr branch. It happens when the token becomes invalid before a logout. I just pushed a fix, and you can confirm by following this PR's Test Plan and making sure you test with an invalid token. You should see... #750 (comment)

@redshiftzero
Copy link
Contributor

in case I missed it: for the fix in 7263eb3 - on master how does the api attribute on the controller become None prior to logout? (my expectation is that AuthError would get raised, but then logout would need to be called to set the api attribute to None)

@sssoleileraaa
Copy link
Contributor Author

in widgets.py, when a user clicks on the logout button the controller logout function gets triggered which make an api.logout request after the api is set to None because an AuthError was seen.

@sssoleileraaa
Copy link
Contributor Author

sssoleileraaa commented Jan 30, 2020

That was an explanation for the current code. For the code on master:

in widgets.py, when a user clicks on the logout button the controller logout function gets triggered which make an api.logout request which wouldn't check whether or not the api is None, but it looks like we only set a copy of the api to None for the queues, so we carefully avoid ever setting the token to None in the Controller, BUT the token would still be invalid so requests would never succeed.

@sssoleileraaa sssoleileraaa force-pushed the when-token-invalid-user-must-log-back-in branch from 7263eb3 to a2550c2 Compare January 31, 2020 08:21
@sssoleileraaa sssoleileraaa dismissed kushaldas’s stale review February 1, 2020 00:47

addressed requested changes

Copy link
Contributor

@redshiftzero redshiftzero left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looking good, some thoughts inline

securedrop_client/queue.py Show resolved Hide resolved
self.on_logout_failure)
self.api = None
if self.api is not None:
self.call_api(self.api.logout, self.on_logout_success, self.on_logout_failure)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

gotcha, so if self.api is already None, we don't need to invalidate the token, because the token is already invalid/expired? (from inspecting the logic in on_sync_failure and on_queue_paused).

i think it would be good to write that this is what's going on in a comment somewhere (as it's only obvious from tracing the logic and could be easily missed in future modifications). alternatively we could move the setting of self.api = None into this method and make it more explicit that this is what's happening via an invalidate_token kwarg or something. Let me know if you have a better idea here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think only setting api to none in one place is a good idea. i was thinking about how I'd like to refactor how we check whether or not we're in offline_mode vs just offline (no token) but i think make a small change to only set api to None in one place would be a good addition to this pr.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

more on the whole offline-mode vs no-token: this is relevant if we want to provide a way for users to regenerate a new token without losing jobs in the api queue (so just pause and wait for a new token), but this is definitely a post-pilot issue

@sssoleileraaa sssoleileraaa force-pushed the when-token-invalid-user-must-log-back-in branch from e0a39b1 to ebc8df7 Compare February 3, 2020 22:10
redshiftzero
redshiftzero previously approved these changes Feb 3, 2020
@@ -33,6 +33,9 @@ def call_api(self, api_client: API, session: Session) -> str:
source = session.query(Source).filter_by(uuid=self.source_uuid).one()
session.commit()

import time
time.sleep(5)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

snip out

retry=True)

if isinstance(result, ApiInaccessibleError):
self.invalidate_token()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

very minor nit: maybe invalidate_token_locally or something? (just because otherwise the dev might think this method calls Api.logout)

@sssoleileraaa
Copy link
Contributor Author

Addressed PR comments and rebased on current master branch.

@redshiftzero
Copy link
Contributor

one line uncovered so test job is failing, but otherwise lgtm from me. opened #761 to unpack the auth state tracking a bit more

@redshiftzero redshiftzero merged commit 6b30050 into master Feb 3, 2020
@redshiftzero redshiftzero deleted the when-token-invalid-user-must-log-back-in branch February 3, 2020 22:47
cfm added a commit that referenced this pull request May 10, 2022
… state

Since #750, application-level state transitions (logging in, logging
out, and switching into offline mode) call
securedrop_client.storage.mark_all_pending_draft_replies().  However,
one or more SendReplyJobs (and their underlying POSTs requests to
"/sources/<source_uuid/replies") may be in flight on the network at that
time, whether or not the application is connected (or even running) to
receive their responses.  It's up to a given SendReplyJob to raise an
exception if it's failed; the state of a given DraftReply can't be
determined, or meaningfully overriden, by an application-level event.
cfm added a commit that referenced this pull request May 11, 2022
… state

Since #750, application-level state transitions (logging in, logging
out, and switching into offline mode) call
securedrop_client.storage.mark_all_pending_draft_replies().  However,
one or more SendReplyJobs (and their underlying POST requests to
"/sources/<source_uuid/replies") may be in flight on the network at that
time, whether or not the application is connected (or even running) to
receive their responses.  Until we have better (ideally generalized)
logic around upload jobs that have not yet been confirmed by the server,
these application-level events should not make assumptions about the
results of jobs that have already been dispatched.
cfm added a commit that referenced this pull request May 11, 2022
… state

Since #750, application-level state transitions (logging in, logging
out, and switching into offline mode) call
securedrop_client.storage.mark_all_pending_draft_replies().  However,
one or more SendReplyJobs (and their underlying POST requests to
"/sources/<source_uuid/replies>") may be in flight on the network at
that time, whether or not the application is connected (or even running)
to receive their responses.  Until we have better (ideally generalized)
logic around upload jobs that have not yet been confirmed by the server,
these application-level events should not make assumptions about the
results of jobs that have already been dispatched.
cfm added a commit that referenced this pull request May 11, 2022
… state

Since #750, application-level state transitions (logging in, logging
out, and switching into offline mode) call
securedrop_client.storage.mark_all_pending_draft_replies_as_failed().
However, one or more SendReplyJobs (and their underlying POST requests
to "/sources/<source_uuid/replies>") may be in flight on the network at
that time, whether or not the application is connected (or even running)
to receive their responses.  Until we have better (ideally generalized)
logic around upload jobs that have not yet been confirmed by the server,
these application-level events should not make assumptions about the
results of jobs that have already been dispatched.
cfm added a commit that referenced this pull request May 11, 2022
… state

Since #750, application-level state transitions (logging in, logging
out, and switching into offline mode) call
securedrop_client.storage.mark_all_pending_draft_replies_as_failed().
However, one or more SendReplyJobs (and their underlying POST requests
to "/sources/<source_uuid/replies>") may be in flight on the network at
that time, whether or not the application is connected (or even running)
to receive their responses.  Until we have better (ideally generalized)
logic around upload jobs that have not yet been confirmed by the server,
these application-level events should not make assumptions about the
results of jobs that have already been dispatched.

Individual SendReplyJobs still call their own _set_status_to_failed()
method on non-timeout exceptions.
cfm added a commit that referenced this pull request May 25, 2022
…-level state

Since #750, application-level state transitions (logging in, logging
out, and switching into offline mode) call
securedrop_client.storage.mark_all_pending_draft_replies_as_failed().
However, one or more SendReplyJobs (and their underlying POST requests
to "/sources/<source_uuid/replies>") may be in flight on the network at
that time, whether or not the application is connected (or even running)
to receive their responses.  Until we have better (ideally generalized)
logic around upload jobs that have not yet been confirmed by the server,
these application-level events should not make assumptions about the
results of jobs that have already been dispatched.

Individual SendReplyJobs still call their own _set_status_to_failed()
method on non-timeout exceptions.

[WIP] refactor: consolidate mark_all_pending_drafts_as_failed() calls

1. when queue is cleared
2. when local storage updates after sync
cfm added a commit that referenced this pull request May 27, 2022
…-level state

Since #750, application-level state transitions (logging in, logging
out, and switching into offline mode) call
securedrop_client.storage.mark_all_pending_draft_replies_as_failed().
However, one or more SendReplyJobs (and their underlying POST requests
to "/sources/<source_uuid/replies>") may be in flight on the network at
that time, whether or not the application is connected (or even running)
to receive their responses.  Until we have better (ideally generalized)
logic around upload jobs that have not yet been confirmed by the server,
these application-level events should not make assumptions about the
results of jobs that have already been dispatched.

Individual SendReplyJobs still call their own _set_status_to_failed()
method on non-timeout exceptions.

[WIP] refactor: consolidate mark_all_pending_drafts_as_failed() calls

1. when queue is cleared
2. when local storage updates after sync
cfm added a commit that referenced this pull request May 27, 2022
…-level state

Since #750, application-level state transitions (logging in, logging
out, and switching into offline mode) call
securedrop_client.storage.mark_all_pending_draft_replies_as_failed().
However, one or more SendReplyJobs (and their underlying POST requests
to "/sources/<source_uuid/replies>") may be in flight on the network at
that time, whether or not the application is connected (or even running)
to receive their responses.  Until we have better (ideally generalized)
logic around upload jobs that have not yet been confirmed by the server,
these application-level events should not make assumptions about the
results of jobs that have already been dispatched.

Individual SendReplyJobs still call their own _set_status_to_failed()
method on non-timeout exceptions.

[WIP] refactor: consolidate mark_all_pending_drafts_as_failed() calls

1. when queue is cleared
2. when local storage updates after sync
cfm added a commit that referenced this pull request Nov 8, 2022
… state

Since #750, application-level state transitions (logging in, logging
out, and switching into offline mode) call
securedrop_client.storage.mark_all_pending_draft_replies_as_failed().
However, one or more SendReplyJobs (and their underlying POST requests
to "/sources/<source_uuid/replies>") may be in flight on the network at
that time, whether or not the application is connected (or even running)
to receive their responses.  Until we have better (ideally generalized)
logic around upload jobs that have not yet been confirmed by the server,
these application-level events should not make assumptions about the
results of jobs that have already been dispatched.

Individual SendReplyJobs still call their own _set_status_to_failed()
method on non-timeout exceptions.
cfm added a commit that referenced this pull request Nov 14, 2022
… state

Since #750, application-level state transitions (logging in, logging
out, and switching into offline mode) call
securedrop_client.storage.mark_all_pending_draft_replies_as_failed().
However, one or more SendReplyJobs (and their underlying POST requests
to "/sources/<source_uuid/replies>") may be in flight on the network at
that time, whether or not the application is connected (or even running)
to receive their responses.  Until we have better (ideally generalized)
logic around upload jobs that have not yet been confirmed by the server,
these application-level events should not make assumptions about the
results of jobs that have already been dispatched.

Individual SendReplyJobs still call their own _set_status_to_failed()
method on non-timeout exceptions.
cfm added a commit that referenced this pull request Dec 9, 2022
… state

Since #750, application-level state transitions (logging in, logging
out, and switching into offline mode) call
securedrop_client.storage.mark_all_pending_draft_replies_as_failed().
However, one or more SendReplyJobs (and their underlying POST requests
to "/sources/<source_uuid/replies>") may be in flight on the network at
that time, whether or not the application is connected (or even running)
to receive their responses.  Until we have better (ideally generalized)
logic around upload jobs that have not yet been confirmed by the server,
these application-level events should not make assumptions about the
results of jobs that have already been dispatched.

Individual SendReplyJobs still call their own _set_status_to_failed()
method on non-timeout exceptions.
cfm added a commit that referenced this pull request Dec 22, 2022
… state

Since #750, application-level state transitions (logging in, logging
out, and switching into offline mode) call
securedrop_client.storage.mark_all_pending_draft_replies_as_failed().
However, one or more SendReplyJobs (and their underlying POST requests
to "/sources/<source_uuid/replies>") may be in flight on the network at
that time, whether or not the application is connected (or even running)
to receive their responses.  Until we have better (ideally generalized)
logic around upload jobs that have not yet been confirmed by the server,
these application-level events should not make assumptions about the
results of jobs that have already been dispatched.

Individual SendReplyJobs still call their own _set_status_to_failed()
method on non-timeout exceptions.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
5 participants