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

Add config option for post logout redirect url #14570

Merged
merged 2 commits into from Sep 7, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
11 changes: 7 additions & 4 deletions client/src/components/Masthead/MastheadItem.vue
Expand Up @@ -5,7 +5,7 @@
v-b-tooltip.hover.bottom
v-b-popover.manual.bottom="{ id: tab.id, content: popoverNote, html: true }"
:class="classes"
:href="tab.url"
:href="getPath(tab.url)"
:target="tab.target || '_parent'"
:link-classes="linkClasses"
:title="tab.tooltip"
Expand Down Expand Up @@ -36,7 +36,7 @@
<b-dropdown-item
v-else-if="item.hidden !== true"
:key="`item-${idx}`"
:href="item.url"
:href="getPath(item.url)"
:target="item.target || '_parent'"
role="menuitem"
:active="item.disabled"
Expand All @@ -52,7 +52,7 @@
import Vue from "vue";
import { VBPopoverPlugin, VBTooltipPlugin } from "bootstrap-vue";
import { BNavItem, BNavItemDropdown, BDropdownItem } from "bootstrap-vue";
import { getAppRoot } from "onload/loadConfig";
import { safePath } from "utils/redirect";

Vue.use(VBPopoverPlugin);
Vue.use(VBTooltipPlugin);
Expand Down Expand Up @@ -83,7 +83,7 @@ export default {
return this.tab.menu;
},
popoverNote() {
return `Please <a href="${getAppRoot()}login">log in or register</a> to use this feature.`;
return `Please <a href="${safePath("/login")}">log in or register</a> to use this feature.`;
},
classes() {
const isActiveTab = this.tab.id == this.activeTab;
Expand Down Expand Up @@ -117,6 +117,9 @@ export default {
this.$refs.dropdown.hide();
}
},
getPath(url) {
return safePath(url);
},
open(tab, event) {
if (tab.onclick) {
event.preventDefault();
Expand Down
31 changes: 16 additions & 15 deletions client/src/utils/logout.js
@@ -1,30 +1,30 @@
import axios from "axios";
import { getGalaxyInstance } from "app";

const POST_LOGOUT_URL = "root/login?is_logout_redirect=true";
import { safePath } from "utils/redirect";

/**
* Handles user logout. Invalidates the current session, checks to see if we
* need to log out of OIDC too, and goes to our POST_LOGOUT_URL (or some other
* configured redirect). */
export function userLogout(logoutAll = false) {
const galaxy = getGalaxyInstance();
const session_csrf_token = galaxy.session_csrf_token;
const url = `${galaxy.root}user/logout?session_csrf_token=${session_csrf_token}&logout_all=${logoutAll}`;
const Galaxy = getGalaxyInstance();
const post_user_logout_href = Galaxy.config.post_user_logout_href;
const session_csrf_token = Galaxy.session_csrf_token;
const url = `/user/logout?session_csrf_token=${session_csrf_token}&logout_all=${logoutAll}`;
axios
.get(url)
.get(safePath(url))
.then((response) => {
if (galaxy.user) {
galaxy.user.clearSessionStorage();
if (Galaxy.user) {
Galaxy.user.clearSessionStorage();
}
// Check if we need to logout of OIDC IDP
if (galaxy.config.enable_oidc) {
if (Galaxy.config.enable_oidc) {
const provider = localStorage.getItem("galaxy-provider");
if (provider) {
localStorage.removeItem("galaxy-provider");
return axios.get(`${galaxy.root}authnz/logout?provider=${provider}`);
return axios.get(safePath(`/authnz/logout?provider=${provider}`));
}
return axios.get(`${galaxy.root}authnz/logout`);
return axios.get(safePath("/authnz/logout"));
} else {
// Otherwise pass through the initial logout response
return response;
Expand All @@ -34,7 +34,7 @@ export function userLogout(logoutAll = false) {
if (response.data?.redirect_uri) {
window.top.location.href = response.data.redirect_uri;
} else {
window.top.location.href = `${galaxy.root}${POST_LOGOUT_URL}`;
window.top.location.href = safePath(post_user_logout_href);
}
});
}
Expand All @@ -49,7 +49,8 @@ export function userLogoutAll() {
* serverside. Currently only used when marking an account deleted -- any
* subsequent navigation after the deletion API request would fail otherwise */
export function userLogoutClient() {
const galaxy = getGalaxyInstance();
galaxy.user?.clearSessionStorage();
window.top.location.href = `${galaxy.root}${POST_LOGOUT_URL}`;
const Galaxy = getGalaxyInstance();
Galaxy.user?.clearSessionStorage();
const post_user_logout_href = Galaxy.config.post_user_logout_href;
window.top.location.href = safePath(post_user_logout_href);
}
11 changes: 11 additions & 0 deletions doc/source/admin/galaxy_options.rst
Expand Up @@ -3337,6 +3337,17 @@
:Type: str


~~~~~~~~~~~~~~~~~~~~~~~~~
``post_user_logout_href``
~~~~~~~~~~~~~~~~~~~~~~~~~

:Description:
This is the default url to which users are redirected after they
log out.
:Default: ``/root/login?is_logout_redirect=true``
:Type: str


~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
``normalize_remote_user_email``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Expand Down
6 changes: 3 additions & 3 deletions lib/galaxy/authnz/__init__.py
Expand Up @@ -72,15 +72,15 @@ def callback(self, state_token: str, authz_code: str, trans, login_redirect_url)
def disconnect(self, provider, trans, disconnect_redirect_url=None):
raise NotImplementedError()

def logout(self, trans, post_logout_redirect_url=None):
def logout(self, trans, post_user_logout_href=None):
"""
Return a URL that will log the user out of the IDP. In OIDC this is
called the 'end_session_endpoint'.

:type trans: GalaxyWebTransaction
:param trans: Galaxy web transaction.

:type post_logout_redirect_url: string
:param post_logout_redirect_url: Optional URL to redirect to after logging out of IDP.
:type post_user_logout_href: string
:param post_user_logout_href: Optional URL to redirect to after logging out of IDP.
"""
raise NotImplementedError()
6 changes: 3 additions & 3 deletions lib/galaxy/authnz/custos_authnz.py
Expand Up @@ -243,11 +243,11 @@ def disconnect(self, provider, trans, email=None, disconnect_redirect_url=None):
except Exception as e:
return False, f"Failed to disconnect provider {provider}: {util.unicodify(e)}", None

def logout(self, trans, post_logout_redirect_url=None):
def logout(self, trans, post_user_logout_href=None):
try:
redirect_url = self.config["end_session_endpoint"]
if post_logout_redirect_url is not None:
redirect_url += f"?redirect_uri={quote(post_logout_redirect_url)}"
if post_user_logout_href is not None:
redirect_url += f"?redirect_uri={quote(post_user_logout_href)}"
return redirect_url
except Exception as e:
log.error("Failed to generate logout redirect_url", exc_info=e)
Expand Down
8 changes: 4 additions & 4 deletions lib/galaxy/authnz/managers.py
Expand Up @@ -369,16 +369,16 @@ def create_user(self, provider, token, trans, login_redirect_url):
log.exception(msg)
return False, msg, (None, None)

def logout(self, provider, trans, post_logout_redirect_url=None):
def logout(self, provider, trans, post_user_logout_href=None):
"""
Log the user out of the identity provider.

:type provider: string
:param provider: set the name of the identity provider.
:type trans: GalaxyWebTransaction
:param trans: Galaxy web transaction.
:type post_logout_redirect_url: string
:param post_logout_redirect_url: (Optional) URL for identity provider
:type post_user_logout_href: string
:param post_user_logout_href: (Optional) URL for identity provider
to redirect to after logging user out.
:return: a tuple (success boolean, message, redirect URI)
"""
Expand All @@ -391,7 +391,7 @@ def logout(self, provider, trans, post_logout_redirect_url=None):
success, message, backend = self._get_authnz_backend(provider)
if success is False:
return False, message, None
return True, message, backend.logout(trans, post_logout_redirect_url)
return True, message, backend.logout(trans, post_user_logout_href)
except Exception:
msg = f"An error occurred when logging out from `{provider}` identity provider. Please contact an administrator for assistance."
log.exception(msg)
Expand Down
16 changes: 16 additions & 0 deletions lib/galaxy/config/sample/galaxy.yml.sample
Expand Up @@ -160,6 +160,18 @@ gravity:
# Must match ``tus_upload_store`` setting in ``galaxy:`` section.
# upload_dir:

# Comma-separated string of enabled tusd hooks.
#
# Leave at the default value to require authorization at upload creation time.
# This means Galaxy's web process does not need to be running after creating the initial
# upload request.
#
# Set to empty string to disable all authorization. This means data can be uploaded (but not processed)
# without the Galaxy web process being available.
#
# You can find a list of available hooks at https://github.com/tus/tusd/blob/master/docs/hooks.md#list-of-available-hooks.
# hooks_enabled_events: pre-create

# Extra arguments to pass to tusd command line.
# extra_args:

Expand Down Expand Up @@ -1694,6 +1706,10 @@ galaxy:
# log your users out.
#remote_user_logout_href: null

# This is the default url to which users are redirected after they log
# out.
#post_user_logout_href: /root/login?is_logout_redirect=true

# If your proxy and/or authentication source does not normalize e-mail
# addresses or user names being passed to Galaxy - set this option to
# true to force these to lower case.
Expand Down
7 changes: 7 additions & 0 deletions lib/galaxy/config/schemas/config_schema.yml
Expand Up @@ -2427,6 +2427,13 @@ mapping:
If use_remote_user is enabled, you can set this to a URL that will log your
users out.

post_user_logout_href:
type: str
default: /root/login?is_logout_redirect=true
required: false
desc: |
This is the default url to which users are redirected after they log out.

normalize_remote_user_email:
type: bool
default: false
Expand Down
1 change: 1 addition & 0 deletions lib/galaxy/managers/configuration.py
Expand Up @@ -145,6 +145,7 @@ def _config_is_truthy(item, key, **context):
"prefer_custos_login": _use_config,
"enable_quotas": _use_config,
"remote_user_logout_href": _use_config,
"post_user_logout_href": _use_config,
"datatypes_disable_auto": _use_config,
"allow_user_dataset_purge": _defaults_to(False), # schema default is True
"ga_code": _use_config,
Expand Down
6 changes: 4 additions & 2 deletions lib/galaxy/webapps/galaxy/controllers/authnz.py
Expand Up @@ -180,9 +180,11 @@ def disconnect(self, trans, provider, email=None, **kwargs):
@web.json
@web.expose
def logout(self, trans, provider, **kwargs):
post_logout_redirect_url = f"{trans.request.base + url_for('/')}root/login?is_logout_redirect=true"
post_user_logout_href = trans.app.config.post_user_logout_href
if post_user_logout_href is not None:
post_user_logout_href = trans.request.base + url_for(post_user_logout_href)
success, message, redirect_uri = trans.app.authnz_manager.logout(
provider, trans, post_logout_redirect_url=post_logout_redirect_url
provider, trans, post_user_logout_href=post_user_logout_href
)
if success:
return {"redirect_uri": redirect_uri}
Expand Down