FEATURE: detect when client thinks user is logged on but is not
This cleans up an error condition where UI thinks a user is logged on but the user is not. If this happens user will be prompted to refresh.
This commit is contained in:
parent
f0d5f83424
commit
0134e41286
|
@ -11,6 +11,7 @@
|
|||
// Stuff we need to load first
|
||||
//= require ./discourse/lib/utilities
|
||||
//= require ./discourse/lib/page-visible
|
||||
//= require ./discourse/lib/logout
|
||||
//= require ./discourse/lib/ajax
|
||||
//= require ./discourse/lib/text
|
||||
//= require ./discourse/lib/hash
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import logout from 'discourse/lib/logout';
|
||||
|
||||
let _showingLogout = false;
|
||||
|
||||
// Subscribe to "logout" change events via the Message Bus
|
||||
export default {
|
||||
name: "logout",
|
||||
|
@ -7,14 +9,22 @@ export default {
|
|||
|
||||
initialize: function (container) {
|
||||
const messageBus = container.lookup('message-bus:main');
|
||||
const siteSettings = container.lookup('site-settings:main');
|
||||
const keyValueStore = container.lookup('key-value-store:main');
|
||||
|
||||
if (!messageBus) { return; }
|
||||
const callback = () => logout(siteSettings, keyValueStore);
|
||||
|
||||
messageBus.subscribe("/logout", function () {
|
||||
bootbox.dialog(I18n.t("logout"), {label: I18n.t("refresh"), callback}, {onEscape: callback, backdrop: 'static'});
|
||||
if (!_showingLogout) {
|
||||
|
||||
_showingLogout = true;
|
||||
|
||||
bootbox.dialog(I18n.t("logout"), {
|
||||
label: I18n.t("refresh"),
|
||||
callback: logout
|
||||
}, {
|
||||
onEscape: logout,
|
||||
backdrop: 'static'
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
import pageVisible from 'discourse/lib/page-visible';
|
||||
import logout from 'discourse/lib/logout';
|
||||
|
||||
let _trackView = false;
|
||||
let _transientHeader = null;
|
||||
let _showingLogout = false;
|
||||
|
||||
export function setTransientHeader(key, value) {
|
||||
_transientHeader = {key, value};
|
||||
|
@ -39,6 +41,10 @@ export function ajax() {
|
|||
|
||||
args.headers = args.headers || {};
|
||||
|
||||
if (Discourse.__container__.lookup('current-user:main')) {
|
||||
args.headers['Discourse-Logged-In'] = "true";
|
||||
}
|
||||
|
||||
if (_transientHeader) {
|
||||
args.headers[_transientHeader.key] = _transientHeader.value;
|
||||
_transientHeader = null;
|
||||
|
@ -54,7 +60,22 @@ export function ajax() {
|
|||
args.headers['Discourse-Visible'] = "true";
|
||||
}
|
||||
|
||||
let handleLogoff = function(xhr) {
|
||||
if (xhr.getResponseHeader('Discourse-Logged-Out') && !_showingLogout) {
|
||||
_showingLogout = true;
|
||||
bootbox.dialog(
|
||||
I18n.t("logout"), {label: I18n.t("refresh"), callback: logout},
|
||||
{
|
||||
onEscape: () => logout(),
|
||||
backdrop: 'static'
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
args.success = (data, textStatus, xhr) => {
|
||||
handleLogoff(xhr);
|
||||
|
||||
if (xhr.getResponseHeader('Discourse-Readonly')) {
|
||||
Ember.run(() => Discourse.Site.currentProp('isReadOnly', true));
|
||||
}
|
||||
|
@ -67,6 +88,8 @@ export function ajax() {
|
|||
};
|
||||
|
||||
args.error = (xhr, textStatus, errorThrown) => {
|
||||
handleLogoff(xhr);
|
||||
|
||||
// note: for bad CSRF we don't loop an extra request right away.
|
||||
// this allows us to eliminate the possibility of having a loop.
|
||||
if (xhr.status === 403 && xhr.responseText === "[\"BAD CSRF\"]") {
|
||||
|
|
|
@ -1,4 +1,10 @@
|
|||
export default function logout(siteSettings, keyValueStore) {
|
||||
if (!siteSettings || !keyValueStore) {
|
||||
const container = Discourse.__container__;
|
||||
siteSettings = siteSettings || container.lookup('site-settings:main');
|
||||
keyValueStore = keyValueStore || container.lookup('key-value-store:main');
|
||||
}
|
||||
|
||||
keyValueStore.abandonLocal();
|
||||
|
||||
const redirect = siteSettings.logout_redirect;
|
||||
|
|
|
@ -22,13 +22,18 @@ def setup_message_bus_env(env)
|
|||
user.groups.pluck('groups.id')
|
||||
end
|
||||
|
||||
extra_headers = {
|
||||
"Access-Control-Allow-Origin" => Discourse.base_url_no_prefix,
|
||||
"Access-Control-Allow-Methods" => "GET, POST",
|
||||
"Access-Control-Allow-Headers" => "X-SILENCE-LOGGER, X-Shared-Session-Key, Dont-Chunk, Discourse-Visible"
|
||||
}
|
||||
|
||||
if env[Auth::DefaultCurrentUserProvider::BAD_TOKEN]
|
||||
extra_headers['Discourse-Logged-Out'] = '1'
|
||||
end
|
||||
|
||||
hash = {
|
||||
extra_headers:
|
||||
{
|
||||
"Access-Control-Allow-Origin" => Discourse.base_url_no_prefix,
|
||||
"Access-Control-Allow-Methods" => "GET, POST",
|
||||
"Access-Control-Allow-Headers" => "X-SILENCE-LOGGER, X-Shared-Session-Key, Dont-Chunk, Discourse-Visible"
|
||||
},
|
||||
extra_headers: extra_headers,
|
||||
user_id: user_id,
|
||||
group_ids: group_ids,
|
||||
is_admin: is_admin,
|
||||
|
|
|
@ -14,6 +14,7 @@ class Auth::DefaultCurrentUserProvider
|
|||
TOKEN_COOKIE ||= "_t"
|
||||
PATH_INFO ||= "PATH_INFO"
|
||||
COOKIE_ATTEMPTS_PER_MIN ||= 10
|
||||
BAD_TOKEN ||= "_DISCOURSE_BAD_TOKEN"
|
||||
|
||||
# do all current user initialization here
|
||||
def initialize(env)
|
||||
|
@ -58,7 +59,8 @@ class Auth::DefaultCurrentUserProvider
|
|||
current_user = @user_token.try(:user)
|
||||
end
|
||||
|
||||
unless current_user
|
||||
if !current_user
|
||||
@env[BAD_TOKEN] = true
|
||||
begin
|
||||
limiter.performed!
|
||||
rescue RateLimiter::LimitExceeded
|
||||
|
@ -69,6 +71,8 @@ class Auth::DefaultCurrentUserProvider
|
|||
)
|
||||
end
|
||||
end
|
||||
elsif @env['HTTP_DISCOURSE_LOGGED_IN']
|
||||
@env[BAD_TOKEN] = true
|
||||
end
|
||||
|
||||
if current_user && should_update_last_seen?
|
||||
|
|
|
@ -78,6 +78,10 @@ module Hijack
|
|||
headers['Content-Length'] = body.bytesize
|
||||
headers['Connection'] = "close"
|
||||
|
||||
if env[Auth::DefaultCurrentUserProvider::BAD_TOKEN]
|
||||
headers['Discourse-Logged-Out'] = '1'
|
||||
end
|
||||
|
||||
status_string = Rack::Utils::HTTP_STATUS_CODES[response.status.to_i] || "Unknown"
|
||||
io.write "#{response.status} #{status_string}\r\n"
|
||||
|
||||
|
|
|
@ -169,6 +169,11 @@ class Middleware::RequestTracker
|
|||
if info && (headers = result[1])
|
||||
headers["X-Runtime"] = "%0.6f" % info[:total_duration]
|
||||
end
|
||||
|
||||
if env[Auth::DefaultCurrentUserProvider::BAD_TOKEN] && (headers = result[1])
|
||||
headers['Discourse-Logged-Out'] = '1'
|
||||
end
|
||||
|
||||
result
|
||||
ensure
|
||||
if (limiters = env['DISCOURSE_RATE_LIMITERS']) && env['DISCOURSE_IS_ASSET_PATH']
|
||||
|
|
|
@ -182,4 +182,17 @@ RSpec.describe SessionController do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'logoff support' do
|
||||
it 'can log off users cleanly' do
|
||||
user = Fabricate(:user)
|
||||
sign_in(user)
|
||||
|
||||
UserAuthToken.destroy_all
|
||||
|
||||
# we need a route that will call current user
|
||||
post '/draft.json', params: {}
|
||||
expect(response.headers['Discourse-Logged-Out']).to eq("1")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue