Merge pull request #13281 from mvdbeek/drop_openid_post_authentication_protocols

This commit is contained in:
Nicola Soranzo
2022-02-01 13:57:32 +00:00
committed by GitHub
14 changed files with 12 additions and 354 deletions
+7 -25
View File
@@ -3701,28 +3701,6 @@
:Type: str
~~~~~~~~~~~~~~~~~
``enable_openid``
~~~~~~~~~~~~~~~~~
:Description:
Enable access to post-authentication options via OpenID.
:Default: ``false``
:Type: bool
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
``openid_consumer_cache_path``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
:Description:
If OpenID is enabled, consumer cache directory to use.
The value of this option will be resolved with respect to
<cache_dir>.
:Default: ``openid_consumer_cache``
:Type: str
~~~~~~~~~~~~~~~~~~~~
``enable_tool_tags``
~~~~~~~~~~~~~~~~~~~~
@@ -4823,13 +4801,17 @@
:Default: ``plugins/welcome_page/new_user/static/topics/``
:Type: str
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~
``vault_config_file``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~
:Description:
Vault configuration
Vault config file.
The value of this option will be resolved with respect to
<config_dir>.
:Default: ``vault_conf.yml``
:Type: str
+2
View File
@@ -313,6 +313,8 @@ OPTION_ACTIONS = {
'communication_server_port': _DeprecatedAndDroppedAction(),
'persistent_communication_rooms': _DeprecatedAndDroppedAction(),
'legacy_eager_objectstore_initialization': _DeprecatedAndDroppedAction(),
'enable_openid': _DeprecatedAndDroppedAction(),
'openid_consumer_cache_path': _DeprecatedAndDroppedAction(),
}
+3 -10
View File
@@ -1811,14 +1811,6 @@ galaxy:
# production server.
#master_api_key: null
# Enable access to post-authentication options via OpenID.
#enable_openid: false
# If OpenID is enabled, consumer cache directory to use.
# The value of this option will be resolved with respect to
# <cache_dir>.
#openid_consumer_cache_path: openid_consumer_cache
# Enable tool tags (associating tools with tags). This has its own
# option since its implementation has a few performance implications
# on startup for large servers.
@@ -2353,7 +2345,8 @@ galaxy:
# is relative to galaxy/static
#welcome_directory: plugins/welcome_page/new_user/static/topics/
# Vault config file
# Vault config file.
# The value of this option will be resolved with respect to
# <config_dir>.
vault_config_file: vault_conf.yml
#vault_config_file: vault_conf.yml
@@ -2701,21 +2701,6 @@ mapping:
a real admin user account via API.
You should probably not set this on a production server.
enable_openid:
type: bool
default: false
required: false
desc: |
Enable access to post-authentication options via OpenID.
openid_consumer_cache_path:
type: str
default: openid_consumer_cache
path_resolves_to: cache_dir
required: false
desc: |
If OpenID is enabled, consumer cache directory to use.
enable_tool_tags:
type: bool
default: false
-1
View File
@@ -147,7 +147,6 @@ class ConfigSerializer(base.ModelSerializer):
'helpsite_url': _use_config,
'lims_doc_url': _defaults_to("https://usegalaxy.org/u/rkchak/p/sts"),
'default_locale': _use_config,
'enable_openid': _use_config,
'enable_tool_recommendations': _use_config,
'enable_account_interface': _use_config,
'tool_recommendation_model_path': _use_config,
-3
View File
@@ -1,3 +0,0 @@
"""
OpenID functionality
"""
-3
View File
@@ -1,3 +0,0 @@
<?xml version="1.0"?>
<openid>
</openid>
-75
View File
@@ -1,75 +0,0 @@
"""
Manage the OpenID consumer and related data stores.
"""
import logging
import os
import pickle
try:
from openid import oidutil
from openid.consumer import consumer
from openid.extensions import sreg
from openid.store import filestore
except ImportError:
oidutil = None
class FakeConsumer:
def __getattr__(x, y):
return None
consumer = FakeConsumer()
OPENID_IMPORT_MESSAGE = ('The Python openid package is required to use this '
'feature, please install it')
log = logging.getLogger(__name__)
def oidlog(message, level=0):
log.debug(message)
if oidutil is not None:
oidutil.log = oidlog
class OpenIDManager:
def __init__(self, cache_path):
assert oidutil is not None, OPENID_IMPORT_MESSAGE
self.session_path = os.path.join(cache_path, 'session')
self.store_path = os.path.join(cache_path, 'store')
for dir in self.session_path, self.store_path:
if not os.path.exists(dir):
os.makedirs(dir)
self.store = filestore.FileOpenIDStore(self.store_path)
def get_session(self, trans):
session_file = os.path.join(self.session_path, str(trans.galaxy_session.id))
if not os.path.exists(session_file):
pickle.dump(dict(), open(session_file, 'wb'))
return pickle.load(open(session_file, 'rb'))
def persist_session(self, trans, oidconsumer):
session_file = os.path.join(self.session_path, str(trans.galaxy_session.id))
pickle.dump(oidconsumer.session, open(session_file, 'wb'))
def get_consumer(self, trans):
return consumer.Consumer(self.get_session(trans), self.store)
def add_sreg(self, trans, request, required=None, optional=None):
if required is None:
required = []
if optional is None:
optional = []
sreg_request = sreg.SRegRequest(required=required, optional=optional)
request.addExtension(sreg_request)
def get_sreg(self, info):
return sreg.SRegResponse.fromSuccessResponse(info)
# so I don't have to expose all of openid.consumer.consumer
FAILURE = consumer.FAILURE
SUCCESS = consumer.SUCCESS
CANCEL = consumer.CANCEL
SETUP_NEEDED = consumer.SETUP_NEEDED
-144
View File
@@ -1,144 +0,0 @@
"""
Contains OpenID provider functionality
"""
import logging
import os
from galaxy.util import parse_xml, string_as_bool
log = logging.getLogger(__name__)
NO_PROVIDER_ID = 'None'
RESERVED_PROVIDER_IDS = [NO_PROVIDER_ID]
class OpenIDProvider:
'''An OpenID Provider object.'''
@classmethod
def from_file(cls, filename):
return cls.from_elem(parse_xml(filename).getroot())
@classmethod
def from_elem(cls, xml_root):
provider_elem = xml_root
provider_id = provider_elem.get('id', None)
provider_name = provider_elem.get('name', provider_id)
op_endpoint_url = provider_elem.find('op_endpoint_url')
if op_endpoint_url is not None:
op_endpoint_url = op_endpoint_url.text
never_associate_with_user = string_as_bool(provider_elem.get('never_associate_with_user', 'False'))
assert (provider_id and provider_name and op_endpoint_url), Exception("OpenID Provider improperly configured")
assert provider_id not in RESERVED_PROVIDER_IDS, Exception(f'Specified OpenID Provider uses a reserved id: {provider_id}')
sreg_required = []
sreg_optional = []
use_for = {}
store_user_preference = {}
use_default_sreg = True
for elem in provider_elem.findall('sreg'):
use_default_sreg = False
for field_elem in elem.findall('field'):
sreg_name = field_elem.get('name')
assert sreg_name, Exception('A name is required for a sreg element')
if string_as_bool(field_elem.get('required')):
sreg_required.append(sreg_name)
else:
sreg_optional.append(sreg_name)
for use_elem in field_elem.findall('use_for'):
use_for[use_elem.get('name')] = sreg_name
for store_user_preference_elem in field_elem.findall('store_user_preference'):
store_user_preference[store_user_preference_elem.get('name')] = sreg_name
if use_default_sreg:
sreg_required = None
sreg_optional = None
use_for = None
return cls(provider_id, provider_name, op_endpoint_url, sreg_required=sreg_required, sreg_optional=sreg_optional, use_for=use_for, store_user_preference=store_user_preference, never_associate_with_user=never_associate_with_user)
def __init__(self, id, name, op_endpoint_url, sreg_required=None, sreg_optional=None, use_for=None, store_user_preference=None, never_associate_with_user=None):
'''When sreg options are not specified, defaults are used.'''
self.id = id
self.name = name
self.op_endpoint_url = op_endpoint_url
if sreg_optional is None:
self.sreg_optional = ['nickname', 'email']
else:
self.sreg_optional = sreg_optional
if sreg_required:
self.sreg_required = sreg_required
else:
self.sreg_required = []
if use_for is not None:
self.use_for = use_for
else:
self.use_for = {}
if 'nickname' in (self.sreg_optional + self.sreg_required):
self.use_for['username'] = 'nickname'
if 'email' in (self.sreg_optional + self.sreg_required):
self.use_for['email'] = 'email'
if store_user_preference:
self.store_user_preference = store_user_preference
else:
self.store_user_preference = {}
if never_associate_with_user:
self.never_associate_with_user = True
else:
self.never_associate_with_user = False
def post_authentication(self, trans, openid_manager, info):
sreg_attributes = openid_manager.get_sreg(info)
for store_pref_name, store_pref_value_name in self.store_user_preference.items():
if store_pref_value_name in (self.sreg_optional + self.sreg_required):
trans.user.preferences[store_pref_name] = sreg_attributes.get(store_pref_value_name)
else:
raise Exception('Only sreg is currently supported.')
trans.sa_session.add(trans.user)
trans.sa_session.flush()
def has_post_authentication_actions(self):
return bool(self.store_user_preference)
class OpenIDProviders:
'''Collection of OpenID Providers'''
NO_PROVIDER_ID = NO_PROVIDER_ID
@classmethod
def from_file(cls, filename):
try:
return cls.from_elem(parse_xml(filename).getroot())
except Exception as e:
log.error(f'Failed to load OpenID Providers: {e}')
return cls()
@classmethod
def from_elem(cls, xml_root):
oid_elem = xml_root
providers = {}
for elem in oid_elem.findall('provider'):
try:
provider = OpenIDProvider.from_file(os.path.join('lib/galaxy/openid', elem.get('file')))
providers[provider.id] = provider
log.debug(f'Loaded OpenID provider: {provider.name} ({provider.id})')
except Exception as e:
log.error(f'Failed to add OpenID provider: {e}')
return cls(providers)
def __init__(self, providers=None):
if providers:
self.providers = providers
else:
self.providers = {}
self._banned_identifiers = [provider.op_endpoint_url for provider in self.providers.values() if provider.never_associate_with_user]
def __iter__(self):
yield from self.providers.values()
def get(self, name, default=None):
if name in self.providers:
return self.providers[name]
else:
return default
def new_provider_from_identifier(self, identifier):
return OpenIDProvider(None, identifier, identifier, never_associate_with_user=identifier in self._banned_identifiers)
@@ -1,74 +0,0 @@
"""
Contains the OpenID interface in the Universe class
"""
import logging
from galaxy import web
from galaxy.openid.openid_manager import OpenIDManager
from galaxy.openid.providers import OpenIDProviders
from galaxy.structured_app import StructuredApp
from galaxy.util import unicodify
from galaxy.web import url_for
from galaxy.webapps.base.controller import BaseUIController
log = logging.getLogger(__name__)
class OpenID(BaseUIController):
def __init__(self, app: StructuredApp):
super().__init__(app)
if app.config.enable_openid:
self.openid_manager = OpenIDManager(app.config.openid_consumer_cache_path)
self.openid_providers = OpenIDProviders.from_file('lib/galaxy/openid/openid_conf.xml')
@web.expose
def openid_auth(self, trans, **kwd):
'''Handles user request to access an OpenID provider'''
if not trans.app.config.enable_openid:
return trans.show_error_message("OpenID authentication is not enabled in this instance of Galaxy.")
consumer = self.openid_manager.get_consumer(trans)
openid_provider = kwd.get('openid_provider')
if openid_provider:
openid_provider_obj = self.openid_providers.get(openid_provider)
else:
return trans.show_error_message("An OpenID provider was not specified.")
if not openid_provider_obj:
return trans.show_error_message("An OpenID provider is invalid.")
process_url = trans.request.base.rstrip('/') + url_for(controller='openid', action='openid_process', openid_provider=openid_provider)
request = None
try:
request = consumer.begin(openid_provider_obj.op_endpoint_url)
if request is None:
return trans.show_error_message(f"No OpenID services are available at {openid_provider_obj.op_endpoint_url}.")
except Exception as e:
return trans.show_error_message(f"Failed to begin OpenID authentication: {unicodify(e)}.")
if request is not None:
self.openid_manager.add_sreg(trans, request, required=openid_provider_obj.sreg_required, optional=openid_provider_obj.sreg_optional)
if request.shouldSendRedirect():
redirect_url = request.redirectURL(
trans.request.base, process_url)
self.openid_manager.persist_session(trans, consumer)
return trans.response.send_redirect(redirect_url)
else:
form = request.htmlMarkup(trans.request.base, process_url, form_tag_attrs={'id': 'openid_message', 'target': '_top'})
self.openid_manager.persist_session(trans, consumer)
return form
return trans.show_error_message("OpenID request failed.")
@web.expose
def openid_process(self, trans, **kwd):
'''Handle's response from OpenID Providers'''
return_link = f"Click <a href='{url_for('/')}'>here</a> to return."
if not trans.app.config.enable_openid:
return trans.show_error_message(f"OpenID authentication is not enabled in this instance of Galaxy. {return_link}")
consumer = self.openid_manager.get_consumer(trans)
info = consumer.complete(kwd, trans.request.url)
openid_provider = kwd.get('openid_provider', None)
if info.status == self.openid_manager.SUCCESS:
openid_provider_obj = self.openid_providers.get(openid_provider)
openid_provider_obj.post_authentication(trans, self.openid_manager, info)
return trans.show_message(f"Processed OpenID authentication. {return_link}")
else:
return trans.show_error_message(f"Authentication via OpenID failed: {info.message}. {return_link}")
-1
View File
@@ -1 +0,0 @@
../../../lib/galaxy/openid
-1
View File
@@ -49,7 +49,6 @@ PACKAGES = [
'galaxy.jobs.runners.util.job_script',
'galaxy.jobs.splitters',
'galaxy.managers',
'galaxy.openid',
'galaxy.schema',
'galaxy.tools',
'galaxy.tools.actions',
-1
View File
@@ -81,7 +81,6 @@ PATH_CONFIG_PROPERTIES = [
'object_store_config_file',
'oidc_backends_config_file',
'oidc_config_file',
'openid_consumer_cache_path',
'sanitize_allowlist_file',
'shed_data_manager_config_file',
'shed_tool_config_file',
-1
View File
@@ -139,7 +139,6 @@ class ExpectedValues:
'object_store_config_file': self._in_config_dir('object_store_conf.xml'),
'oidc_backends_config_file': self._in_config_dir('oidc_backends_config.xml'),
'oidc_config_file': self._in_config_dir('oidc_config.xml'),
'openid_consumer_cache_path': self._in_cache_dir('openid_consumer_cache'),
'sanitize_allowlist_file': self._in_managed_config_dir('sanitize_allowlist.txt'),
'shed_data_manager_config_file': self._in_managed_config_dir('shed_data_manager_conf.xml'),
'shed_tool_config_file': self._in_managed_config_dir('shed_tool_conf.xml'),