Skip to content

Commit

Permalink
Moved AuthnzManager to another module than init to avoid cyclic imports.
Browse files Browse the repository at this point in the history
  • Loading branch information
VJalili committed Dec 19, 2017
1 parent 6935adc commit 7552952
Show file tree
Hide file tree
Showing 3 changed files with 145 additions and 145 deletions.
4 changes: 2 additions & 2 deletions lib/galaxy/app.py
Expand Up @@ -174,8 +174,8 @@ def __init__(self, **kwargs):
self.heartbeat.daemon = True
self.application_stack.register_postfork_function(self.heartbeat.start)
if self.config.enable_oidc:
from galaxy import authnz
self.authnz_manager = authnz.AuthnzManager(self, self.config.oidc_config, self.config.oidc_backends_config)
from galaxy.authnz import managers
self.authnz_manager = managers.AuthnzManager(self, self.config.oidc_config, self.config.oidc_backends_config)
self.sentry_client = None
if self.config.sentry_dsn:

Expand Down
143 changes: 0 additions & 143 deletions lib/galaxy/authnz/__init__.py
Expand Up @@ -9,13 +9,6 @@
credentials for cloud-based resource providers (e.g., Amazon AWS, Microsoft Azure).
"""

import importlib
import logging
import xml.etree.ElementTree as ET
from xml.etree.ElementTree import ParseError

log = logging.getLogger(__name__)


class IdentityProvider(object):
"""
Expand Down Expand Up @@ -73,139 +66,3 @@ def callback(self, state_token, authz_code, trans, login_redirect_url):

def disconnect(self, provider, trans, disconnect_redirect_url=None):
raise NotImplementedError()


class AuthnzManager(object):

def __init__(self, app, oidc_config_file, oidc_backends_config_file):
"""
:type app: galaxy.app.UniverseApplication
:param app:
:type config: string
:param config: sets the path for OIDC configuration
file (e.g., oidc_backends_config.xml).
"""
self._parse_oidc_config(oidc_config_file)
self._parse_oidc_backends_config(oidc_backends_config_file)

def _parse_oidc_config(self, config_file):
self.oidc_config = {}
try:
tree = ET.parse(config_file)
root = tree.getroot()
if root.tag != 'OIDC':
raise ParseError("The root element in OIDC_Config xml file is expected to be `OIDC`, "
"found `{}` instead -- unable to continue.".format(root.tag))
for child in root:
if child.tag != 'Setter':
log.error("Expect a node with `Setter` tag, found a node with `{}` tag instead; "
"skipping this node.".format(child.tag))
continue
if 'Property' not in child.attrib or 'Value' not in child.attrib or 'Type' not in child.attrib:
log.error("Could not find the node attributes `Property` and/or `Value` and/or `Type`;"
" found these attributes: `{}`; skipping this node.".format(child.attrib))
continue
try:
func = getattr(importlib.import_module('__builtin__'), child.get('Type'))
except AttributeError:
log.error("The value of attribute `Type`, `{}`, is not a valid built-in type;"
" skipping this node").format(child.get('Type'))
continue
self.oidc_config[child.get('Property')] = func(child.get('Value'))
except ImportError:
raise
except ParseError as e:
raise ParseError("Invalid configuration at `{}`: {} -- unable to continue.".format(config_file, e.message))

def _parse_oidc_backends_config(self, config_file):
self.oidc_backends_config = {}
try:
tree = ET.parse(config_file)
root = tree.getroot()
if root.tag != 'OIDC':
raise ParseError("The root element in OIDC config xml file is expected to be `OIDC`, "
"found `{}` instead -- unable to continue.".format(root.tag))
for child in root:
if child.tag != 'provider':
log.error("Expect a node with `provider` tag, found a node with `{}` tag instead; "
"skipping the node.".format(child.tag))
continue
if 'name' not in child.attrib:
log.error("Could not find a node attribute 'name'; skipping the node '{}'.".format(child.tag))
continue
idp = child.get('name').lower()
if idp == 'google':
self.oidc_backends_config[idp] = self._parse_google_config(child)
if len(self.oidc_backends_config) == 0:
raise ParseError("No valid provider configuration parsed.")
except ImportError:
raise
except ParseError as e:
raise ParseError("Invalid configuration at `{}`: {} -- unable to continue.".format(config_file, e.message))
# except Exception as e:
# raise Exception("Malformed OIDC Configuration XML -- unable to continue. {}".format(e.message))

def _parse_google_config(self, config_xml):
rtv = {
'client_id': config_xml.find('client_id').text,
'client_secret': config_xml.find('client_secret').text,
'redirect_uri': config_xml.find('redirect_uri').text}
if config_xml.find('prompt') is not None:
rtv['prompt'] = config_xml.find('prompt').text
return rtv

def _get_authnz_backend(self, provider):
provider = provider.lower()
if provider in self.oidc_backends_config:
try:
from .psa_authnz import PSAAuthnz
return True, "", PSAAuthnz(provider, self.oidc_config, self.oidc_backends_config[provider])
except Exception as e:
log.exception('An error occurred when loading PSAAuthnz: ', str(e))
return False, str(e), None
else:
msg = 'The requested identity provider `{}` is not a recognized/expected provider'.format(provider)
log.debug(msg)
return False, msg, None

def authenticate(self, provider, trans):
"""
:type provider: string
:param provider: set the name of the identity provider to be
used for authentication flow.
:type trans: GalaxyWebTransaction
:param trans: Galaxy web transaction.
:return: an identity provider specific authentication redirect URI.
"""
try:
success, message, backend = self._get_authnz_backend(provider)
if success is False:
return False, message, None
return True, "Redirecting to the identity provider `{}` for authentication".format(provider), backend.authenticate(trans)
except Exception as e:
msg = 'An error occurred when authenticating a user on `{}` identity provider: {}'.format(provider, str(e))
log.exception(msg)
return False, msg, None

def callback(self, provider, state_token, authz_code, trans, login_redirect_url):
try:
success, message, backend = self._get_authnz_backend(provider)
if success is False:
return False, message, None
return True, message, backend.callback(state_token, authz_code, trans, login_redirect_url)
except Exception as e:
msg = 'An error occurred when handling callback from provider `{}`; {}'.format(provider, str(e))
log.exception(msg)
return False, msg, None

def disconnect(self, provider, trans, disconnect_redirect_url=None):
try:
success, message, backend = self._get_authnz_backend(provider)
if success is False:
return False, message, None
return backend.disconnect(provider, trans, disconnect_redirect_url)
except Exception as e:
msg = 'An error occurred when disconnecting authentication with `{}` for user `{}`; {}'.format(provider, trans.user.username, str(e))
log.exception(msg)
return False, msg, None
143 changes: 143 additions & 0 deletions lib/galaxy/authnz/managers.py
@@ -0,0 +1,143 @@

import importlib
import logging
import xml.etree.ElementTree as ET
from .psa_authnz import PSAAuthnz
from xml.etree.ElementTree import ParseError

log = logging.getLogger(__name__)


class AuthnzManager(object):

def __init__(self, app, oidc_config_file, oidc_backends_config_file):
"""
:type app: galaxy.app.UniverseApplication
:param app:
:type config: string
:param config: sets the path for OIDC configuration
file (e.g., oidc_backends_config.xml).
"""
self._parse_oidc_config(oidc_config_file)
self._parse_oidc_backends_config(oidc_backends_config_file)

def _parse_oidc_config(self, config_file):
self.oidc_config = {}
try:
tree = ET.parse(config_file)
root = tree.getroot()
if root.tag != 'OIDC':
raise ParseError("The root element in OIDC_Config xml file is expected to be `OIDC`, "
"found `{}` instead -- unable to continue.".format(root.tag))
for child in root:
if child.tag != 'Setter':
log.error("Expect a node with `Setter` tag, found a node with `{}` tag instead; "
"skipping this node.".format(child.tag))
continue
if 'Property' not in child.attrib or 'Value' not in child.attrib or 'Type' not in child.attrib:
log.error("Could not find the node attributes `Property` and/or `Value` and/or `Type`;"
" found these attributes: `{}`; skipping this node.".format(child.attrib))
continue
try:
func = getattr(importlib.import_module('__builtin__'), child.get('Type'))
except AttributeError:
log.error("The value of attribute `Type`, `{}`, is not a valid built-in type;"
" skipping this node").format(child.get('Type'))
continue
self.oidc_config[child.get('Property')] = func(child.get('Value'))
except ImportError:
raise
except ParseError as e:
raise ParseError("Invalid configuration at `{}`: {} -- unable to continue.".format(config_file, e.message))

def _parse_oidc_backends_config(self, config_file):
self.oidc_backends_config = {}
try:
tree = ET.parse(config_file)
root = tree.getroot()
if root.tag != 'OIDC':
raise ParseError("The root element in OIDC config xml file is expected to be `OIDC`, "
"found `{}` instead -- unable to continue.".format(root.tag))
for child in root:
if child.tag != 'provider':
log.error("Expect a node with `provider` tag, found a node with `{}` tag instead; "
"skipping the node.".format(child.tag))
continue
if 'name' not in child.attrib:
log.error("Could not find a node attribute 'name'; skipping the node '{}'.".format(child.tag))
continue
idp = child.get('name').lower()
if idp == 'google':
self.oidc_backends_config[idp] = self._parse_google_config(child)
if len(self.oidc_backends_config) == 0:
raise ParseError("No valid provider configuration parsed.")
except ImportError:
raise
except ParseError as e:
raise ParseError("Invalid configuration at `{}`: {} -- unable to continue.".format(config_file, e.message))
# except Exception as e:
# raise Exception("Malformed OIDC Configuration XML -- unable to continue. {}".format(e.message))

def _parse_google_config(self, config_xml):
rtv = {
'client_id': config_xml.find('client_id').text,
'client_secret': config_xml.find('client_secret').text,
'redirect_uri': config_xml.find('redirect_uri').text}
if config_xml.find('prompt') is not None:
rtv['prompt'] = config_xml.find('prompt').text
return rtv

def _get_authnz_backend(self, provider):
provider = provider.lower()
if provider in self.oidc_backends_config:
try:
return True, "", PSAAuthnz(provider, self.oidc_config, self.oidc_backends_config[provider])
except Exception as e:
log.exception('An error occurred when loading PSAAuthnz: ', str(e))
return False, str(e), None
else:
msg = 'The requested identity provider `{}` is not a recognized/expected provider'.format(provider)
log.debug(msg)
return False, msg, None

def authenticate(self, provider, trans):
"""
:type provider: string
:param provider: set the name of the identity provider to be
used for authentication flow.
:type trans: GalaxyWebTransaction
:param trans: Galaxy web transaction.
:return: an identity provider specific authentication redirect URI.
"""
try:
success, message, backend = self._get_authnz_backend(provider)
if success is False:
return False, message, None
return True, "Redirecting to the identity provider `{}` for authentication".format(provider), backend.authenticate(trans)
except Exception as e:
msg = 'An error occurred when authenticating a user on `{}` identity provider: {}'.format(provider, str(e))
log.exception(msg)
return False, msg, None

def callback(self, provider, state_token, authz_code, trans, login_redirect_url):
try:
success, message, backend = self._get_authnz_backend(provider)
if success is False:
return False, message, None
return True, message, backend.callback(state_token, authz_code, trans, login_redirect_url)
except Exception as e:
msg = 'An error occurred when handling callback from provider `{}`; {}'.format(provider, str(e))
log.exception(msg)
return False, msg, None

def disconnect(self, provider, trans, disconnect_redirect_url=None):
try:
success, message, backend = self._get_authnz_backend(provider)
if success is False:
return False, message, None
return backend.disconnect(provider, trans, disconnect_redirect_url)
except Exception as e:
msg = 'An error occurred when disconnecting authentication with `{}` for user `{}`; {}'.format(provider, trans.user.username, str(e))
log.exception(msg)
return False, msg, None

0 comments on commit 7552952

Please sign in to comment.