diff options
Diffstat (limited to 'ipsilon')
24 files changed, 317 insertions, 445 deletions
diff --git a/ipsilon/admin/common.py b/ipsilon/admin/common.py index 530cb22..9c82142 100755 --- a/ipsilon/admin/common.py +++ b/ipsilon/admin/common.py @@ -20,7 +20,6 @@ import cherrypy from ipsilon.util.page import Page from ipsilon.util.page import admin_protect -from ipsilon.util.plugin import PluginObject from ipsilon.util import config as pconfig @@ -96,7 +95,7 @@ class AdminPluginConfig(AdminPage): if len(new_db_values) != 0: # First we try to save in the database try: - self._po.save_plugin_config(self.facility, new_db_values) + self._po.save_plugin_config(new_db_values) message = "New configuration saved." message_type = "success" except Exception: # pylint: disable=broad-except @@ -104,7 +103,7 @@ class AdminPluginConfig(AdminPage): message_type = "error" # Then refresh the actual objects - self._po.refresh_plugin_config(self.facility) + self._po.refresh_plugin_config() return self.root_with_msg(message=message, message_type=message_type) @@ -123,45 +122,40 @@ class AdminPluginsOrder(AdminPage): def GET(self, *args, **kwargs): return self.parent.root_with_msg() - def _get_enabled_by_name(self): - by_name = dict() - for p in self._site[self.facility]['available'].values(): + def _get_enabled_list(self): + cur = list() + for p in self._site[self.facility].available.values(): if p.is_enabled: - by_name[p.name] = p - return by_name + cur.append(p.name) + return cur @admin_protect def POST(self, *args, **kwargs): message = "Nothing was modified." message_type = "info" - by_name = self._get_enabled_by_name() + cur_enabled = self._get_enabled_list() if 'order' in kwargs: order = kwargs['order'].split(',') if len(order) != 0: - new_names = [] - new_plugins = [] + new_order = [] try: for v in order: val = v.strip() - if val not in by_name: + if val not in cur_enabled: error = "Invalid plugin name: %s" % val raise ValueError(error) - new_names.append(val) - new_plugins.append(by_name[val]) - if len(new_names) < len(by_name): - for val in by_name: - if val not in new_names: - new_names.append(val) - new_plugins.append(by_name[val]) + new_order.append(val) + if len(new_order) < len(cur_enabled): + for val in cur_enabled: + if val not in new_order: + new_order.append(val) - self.parent.save_enabled_plugins(new_names) - self.parent.reorder_plugins(new_names) + self.parent.save_enabled_plugins(new_order) # When all is saved update also live config. The - # live config is a list of the actual plugin - # objects. - self._site[self.facility]['enabled'] = new_plugins + # live config is the ordered list of plugin names. + self._site[self.facility].refresh_enabled() message = "New configuration saved." message_type = "success" @@ -190,9 +184,9 @@ class AdminPlugins(AdminPage): self.order = None parent.add_subtree(name, self) - for plugin in self._site[facility]['available']: + for plugin in self._site[facility].available: cherrypy.log.error('Admin info plugin: %s' % plugin) - obj = self._site[facility]['available'][plugin] + obj = self._site[facility].available[plugin] page = AdminPluginConfig(obj, self._site, self) if hasattr(obj, 'admin'): obj.admin.mount(page) @@ -202,34 +196,17 @@ class AdminPlugins(AdminPage): self.order = AdminPluginsOrder(self._site, self, facility) def save_enabled_plugins(self, names): - po = PluginObject() - po.name = "global" - globalconf = dict() - globalconf['order'] = ','.join(names) - po.import_config(globalconf) - po.save_plugin_config(self.facility) - - def reorder_plugins(self, names): - return + self._site[self.facility].save_enabled(names) def root_with_msg(self, message=None, message_type=None): plugins = self._site[self.facility] - enabled = [] - if self.order: - for plugin in plugins['enabled']: - if plugin.is_enabled: - enabled.append(plugin.name) - else: - for _, plugin in plugins['available'].iteritems(): - if plugin.is_enabled: - enabled.append(plugin.name) targs = {'title': self.title, 'menu': self._master.menu, 'message': message, 'message_type': message_type, - 'available': plugins['available'], - 'enabled': enabled, + 'available': plugins.available, + 'enabled': plugins.enabled, 'baseurl': self.url, 'newurl': self.url} if self.order: @@ -246,15 +223,13 @@ class AdminPlugins(AdminPage): def enable(self, plugin): msg = None plugins = self._site[self.facility] - if plugin not in plugins['available']: + if plugin not in plugins.available: msg = "Unknown plugin %s" % plugin return self.root_with_msg(msg, "error") - obj = plugins['available'][plugin] + obj = plugins.available[plugin] if not obj.is_enabled: - obj.enable(self._site) - if self.order: - enabled = list(x.name for x in plugins['enabled']) - self.save_enabled_plugins(enabled) + obj.enable() + obj.save_enabled_state() msg = "Plugin %s enabled" % obj.name return self.root_with_msg(msg, "success") enable.public_function = True @@ -263,15 +238,13 @@ class AdminPlugins(AdminPage): def disable(self, plugin): msg = None plugins = self._site[self.facility] - if plugin not in plugins['available']: + if plugin not in plugins.available: msg = "Unknown plugin %s" % plugin return self.root_with_msg(msg, "error") - obj = plugins['available'][plugin] + obj = plugins.available[plugin] if obj.is_enabled: - obj.disable(self._site) - if self.order: - enabled = list(x.name for x in plugins['enabled']) - self.save_enabled_plugins(enabled) + obj.disable() + obj.save_enabled_state() msg = "Plugin %s disabled" % obj.name return self.root_with_msg(msg, "success") disable.public_function = True diff --git a/ipsilon/admin/login.py b/ipsilon/admin/login.py index c1a1c73..ae5b15a 100755 --- a/ipsilon/admin/login.py +++ b/ipsilon/admin/login.py @@ -10,15 +10,3 @@ class LoginPlugins(AdminPlugins): def __init__(self, site, parent): super(LoginPlugins, self).__init__('login', site, parent, FACILITY) self.title = 'Login Plugins' - - def reorder_plugins(self, order): - plugins = self._site[FACILITY]['available'] - root = self._site[FACILITY]['root'] - prev_obj = None - for name in order: - if prev_obj is None: - root.first_login = plugins[name] - else: - prev_obj.next_login = plugins[name] - prev_obj = plugins[name] - prev_obj.next_login = None diff --git a/ipsilon/helpers/common.py b/ipsilon/helpers/common.py index 6ec2819..f670bc6 100755 --- a/ipsilon/helpers/common.py +++ b/ipsilon/helpers/common.py @@ -20,8 +20,11 @@ from ipsilon.util.plugin import PluginInstaller +FACILITY = 'environment_helpers' + + class EnvHelpersInstall(object): def __init__(self): - pi = PluginInstaller(EnvHelpersInstall) + pi = PluginInstaller(EnvHelpersInstall, FACILITY) self.plugins = pi.get_plugins() diff --git a/ipsilon/helpers/ipa.py b/ipsilon/helpers/ipa.py index 4a4849a..d01d663 100755 --- a/ipsilon/helpers/ipa.py +++ b/ipsilon/helpers/ipa.py @@ -46,7 +46,7 @@ failure (see logs) and retry. class Installer(object): - def __init__(self): + def __init__(self, *pargs): self.name = 'ipa' self.ptype = 'helper' self.logger = None diff --git a/ipsilon/info/common.py b/ipsilon/info/common.py index 586b9e5..a3a297f 100755 --- a/ipsilon/info/common.py +++ b/ipsilon/info/common.py @@ -11,40 +11,13 @@ from ipsilon.util.plugin import PluginObject, PluginConfig class InfoProviderBase(PluginConfig, PluginObject): - def __init__(self): + def __init__(self, *pargs): PluginConfig.__init__(self) - PluginObject.__init__(self) - self._site = None - self.is_enabled = False + PluginObject.__init__(self, *pargs) def get_user_attrs(self, user): raise NotImplementedError - def enable(self, site): - if self.is_enabled: - return - - if not self._site: - self._site = site - plugins = self._site[FACILITY] - - # configure self - if self.name in plugins['config']: - self.import_config(plugins['config'][self.name]) - - plugins['enabled'].append(self) - self.is_enabled = True - self.debug('Info plugin enabled: %s' % self.name) - - def disable(self, site): - if not self.is_enabled: - return - - plugins = self._site[FACILITY] - plugins['enabled'].remove(self) - self.is_enabled = False - self.debug('Info plugin disabled: %s' % self.name) - class InfoMapping(Log): @@ -96,23 +69,22 @@ class Info(Log): def __init__(self, site): self._site = site - loader = PluginLoader(Info, FACILITY, 'InfoProvider') - self._site[FACILITY] = loader.get_plugin_data() - plugins = self._site[FACILITY] + plugins = PluginLoader(Info, FACILITY, 'InfoProvider') + plugins.get_plugin_data() + self._site[FACILITY] = plugins - available = plugins['available'].keys() + available = plugins.available.keys() self.debug('Available info providers: %s' % str(available)) - plugins['root'] = self - for item in plugins['whitelist']: - self.debug('Login plugin in whitelist: %s' % item) - if item not in plugins['available']: + for item in plugins.enabled: + self.debug('Login plugin in enabled list: %s' % item) + if item not in plugins.available: self.debug('Info Plugin %s not found' % item) continue - plugins['available'][item].enable(self._site) + plugins.available[item].enable() def get_user_attrs(self, user, requested=None): - plugins = self._site[FACILITY]['available'] + plugins = self._site[FACILITY].available result = dict() for _, p in plugins.items(): @@ -146,5 +118,5 @@ class InfoProviderInstaller(object): class InfoProviderInstall(object): def __init__(self): - pi = PluginInstaller(InfoProviderInstall) + pi = PluginInstaller(InfoProviderInstall, FACILITY) self.plugins = pi.get_plugins() diff --git a/ipsilon/info/infoldap.py b/ipsilon/info/infoldap.py index 369d3f1..da9819a 100755 --- a/ipsilon/info/infoldap.py +++ b/ipsilon/info/infoldap.py @@ -29,8 +29,8 @@ ldap_mapping = { class InfoProvider(InfoProviderBase): - def __init__(self): - super(InfoProvider, self).__init__() + def __init__(self, *pargs): + super(InfoProvider, self).__init__(*pargs) self.mapper = InfoMapping() self.mapper.set_mapping(ldap_mapping) self.name = 'ldap' @@ -151,9 +151,10 @@ Info plugin that uses LDAP to retrieve user data. """ class Installer(InfoProviderInstaller): - def __init__(self): + def __init__(self, *pargs): super(Installer, self).__init__() self.name = 'ldap' + self.pargs = pargs def install_args(self, group): group.add_argument('--info-ldap', choices=['yes', 'no'], default='no', @@ -172,10 +173,10 @@ class Installer(InfoProviderInstaller): return # Add configuration data to database - po = PluginObject() + po = PluginObject(*self.pargs) po.name = 'ldap' po.wipe_data() - po.wipe_config_values(self.facility) + po.wipe_config_values() config = dict() if 'info_ldap_server_url' in opts: config['server url'] = opts['info_ldap_server_url'] @@ -193,15 +194,8 @@ class Installer(InfoProviderInstaller): elif 'ldap_bind_dn_template' in opts: config['user dn template'] = opts['ldap_bind_dn_template'] config['tls'] = 'Demand' - po.save_plugin_config(self.facility, config) + po.save_plugin_config(config) - # Replace global config, only one plugin info can be used - po.name = 'global' - globalconf = po.get_plugin_config(self.facility) - if 'order' in globalconf: - order = globalconf['order'].split(',') - else: - order = [] - order.append('ldap') - globalconf['order'] = ','.join(order) - po.save_plugin_config(self.facility, globalconf) + # Update global config to add login plugin + po.is_enabled = True + po.save_enabled_state() diff --git a/ipsilon/info/nss.py b/ipsilon/info/nss.py index 3dfd885..50c84a8 100755 --- a/ipsilon/info/nss.py +++ b/ipsilon/info/nss.py @@ -20,8 +20,8 @@ posix_map = { class InfoProvider(InfoProviderBase): - def __init__(self): - super(InfoProvider, self).__init__() + def __init__(self, *pargs): + super(InfoProvider, self).__init__(*pargs) self.mapper = InfoMapping() self.mapper.set_mapping(posix_map) self.name = 'nss' @@ -75,9 +75,10 @@ class InfoProvider(InfoProviderBase): class Installer(InfoProviderInstaller): - def __init__(self): + def __init__(self, *pargs): super(Installer, self).__init__() self.name = 'nss' + self.pargs = pargs def install_args(self, group): group.add_argument('--info-nss', choices=['yes', 'no'], default='no', @@ -88,18 +89,11 @@ class Installer(InfoProviderInstaller): return # Add configuration data to database - po = PluginObject() + po = PluginObject(*self.pargs) po.name = 'nss' po.wipe_data() - po.wipe_config_values(self.facility) + po.wipe_config_values() - # Replace global config, only one plugin info can be used - po.name = 'global' - globalconf = po.get_plugin_config(self.facility) - if 'order' in globalconf: - order = globalconf['order'].split(',') - else: - order = [] - order.append('nss') - globalconf['order'] = ','.join(order) - po.save_plugin_config(self.facility, globalconf) + # Update global config to add login plugin + po.is_enabled = True + po.save_enabled_state() diff --git a/ipsilon/login/authfas.py b/ipsilon/login/authfas.py index 71db372..cb1c324 100755 --- a/ipsilon/login/authfas.py +++ b/ipsilon/login/authfas.py @@ -5,7 +5,6 @@ from ipsilon.info.common import InfoMapping from ipsilon.login.common import LoginFormBase, LoginManagerBase -from ipsilon.login.common import FACILITY from ipsilon.util.plugin import PluginObject from ipsilon.util import config as pconfig import cherrypy @@ -175,9 +174,10 @@ Form based login Manager that uses the Fedora Authentication Server class Installer(object): - def __init__(self): + def __init__(self, *pargs): self.name = 'fas' self.ptype = 'login' + self.pargs = pargs def install_args(self, group): group.add_argument('--fas', choices=['yes', 'no'], default='no', @@ -188,20 +188,11 @@ class Installer(object): return # Add configuration data to database - po = PluginObject() + po = PluginObject(*self.pargs) po.name = 'fas' po.wipe_data() - - po.wipe_config_values(FACILITY) + po.wipe_config_values() # Update global config to add login plugin - po = PluginObject() - po.name = 'global' - globalconf = po.get_plugin_config(FACILITY) - if 'order' in globalconf: - order = globalconf['order'].split(',') - else: - order = [] - order.append('fas') - globalconf['order'] = ','.join(order) - po.save_plugin_config(FACILITY, globalconf) + po.is_enabled = True + po.save_enabled_state() diff --git a/ipsilon/login/authform.py b/ipsilon/login/authform.py index 4e9f5c1..45c92a5 100755 --- a/ipsilon/login/authform.py +++ b/ipsilon/login/authform.py @@ -18,7 +18,6 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. from ipsilon.login.common import LoginFormBase, LoginManagerBase -from ipsilon.login.common import FACILITY from ipsilon.util.plugin import PluginObject from ipsilon.util.user import UserSession from ipsilon.util import config as pconfig @@ -105,9 +104,10 @@ LoadModule authnz_pam_module modules/mod_authnz_pam.so class Installer(object): - def __init__(self): + def __init__(self, *pargs): self.name = 'form' self.ptype = 'login' + self.pargs = pargs def install_args(self, group): group.add_argument('--form', choices=['yes', 'no'], default='no', @@ -128,21 +128,14 @@ class Installer(object): httpd_conf.write(hunk) # Add configuration data to database - po = PluginObject() + po = PluginObject(*self.pargs) po.name = 'form' po.wipe_data() - po.wipe_config_values(FACILITY) + po.wipe_config_values() - # Update global config, put 'krb' always first - po.name = 'global' - globalconf = po.get_plugin_config(FACILITY) - if 'order' in globalconf: - order = globalconf['order'].split(',') - else: - order = [] - order.append('form') - globalconf['order'] = ','.join(order) - po.save_plugin_config(FACILITY, globalconf) + # Update global config to add login plugin + po.is_enabled = True + po.save_enabled_state() # for selinux enabled platforms, ignore if it fails just report try: diff --git a/ipsilon/login/authkrb.py b/ipsilon/login/authkrb.py index f2af0a0..e426d2c 100755 --- a/ipsilon/login/authkrb.py +++ b/ipsilon/login/authkrb.py @@ -18,7 +18,6 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. from ipsilon.login.common import LoginPageBase, LoginManagerBase -from ipsilon.login.common import FACILITY from ipsilon.util.plugin import PluginObject from ipsilon.util.user import UserSession from string import Template @@ -61,8 +60,9 @@ class KrbError(LoginPageBase): if 'WWW-Authenticate' not in cherrypy.request.headers: cherrypy.response.status = 401 - if self.lm.next_login: - return self.lm.next_login.page.root(*args, **kwargs) + next_login = self.lm.next_login() + if next_login: + return next_login.page.root(*args, **kwargs) conturl = '%s/login' % self.basepath return self._template('login/krb.html', @@ -117,9 +117,10 @@ CONF_TEMPLATE = """ class Installer(object): - def __init__(self): + def __init__(self, *pargs): self.name = 'krb' self.ptype = 'login' + self.pargs = pargs def install_args(self, group): group.add_argument('--krb', choices=['yes', 'no'], default='no', @@ -152,17 +153,15 @@ class Installer(object): httpd_conf.write(hunk) # Add configuration data to database - po = PluginObject() + po = PluginObject(*self.pargs) po.name = 'krb' po.wipe_data() # Update global config, put 'krb' always first - po.name = 'global' - globalconf = po.get_plugin_config(FACILITY) - if 'order' in globalconf: - order = globalconf['order'].split(',') - else: - order = [] - order.insert(0, 'krb') - globalconf['order'] = ','.join(order) - po.save_plugin_config(FACILITY, globalconf) + ph = self.pargs[0] + ph.refresh_enabled() + if 'krb' not in ph.enabled: + enabled = [] + enabled.extend(ph.enabled) + enabled.insert(0, 'krb') + ph.save_enabled(enabled) diff --git a/ipsilon/login/authldap.py b/ipsilon/login/authldap.py index f51f375..06dac09 100755 --- a/ipsilon/login/authldap.py +++ b/ipsilon/login/authldap.py @@ -3,7 +3,6 @@ # Copyright (C) 2014 Ipsilon Contributors, see COPYING for license from ipsilon.login.common import LoginFormBase, LoginManagerBase -from ipsilon.login.common import FACILITY from ipsilon.util.plugin import PluginObject from ipsilon.util.log import Log from ipsilon.util import config as pconfig @@ -176,9 +175,10 @@ authentication. """ class Installer(object): - def __init__(self): + def __init__(self, *pargs): self.name = 'ldap' self.ptype = 'login' + self.pargs = pargs def install_args(self, group): group.add_argument('--ldap', choices=['yes', 'no'], default='no', @@ -193,27 +193,19 @@ class Installer(object): return # Add configuration data to database - po = PluginObject() + po = PluginObject(*self.pargs) po.name = 'ldap' po.wipe_data() + po.wipe_config_values() - po.wipe_config_values(FACILITY) config = dict() if 'ldap_server_url' in opts: config['server url'] = opts['ldap_server_url'] if 'ldap_bind_dn_template' in opts: config['bind dn template'] = opts['ldap_bind_dn_template'] config['tls'] = 'Demand' - po.save_plugin_config(FACILITY, config) + po.save_plugin_config(config) # Update global config to add login plugin - po = PluginObject() - po.name = 'global' - globalconf = po.get_plugin_config(FACILITY) - if 'order' in globalconf: - order = globalconf['order'].split(',') - else: - order = [] - order.append('ldap') - globalconf['order'] = ','.join(order) - po.save_plugin_config(FACILITY, globalconf) + po.is_enabled = True + po.save_enabled_state() diff --git a/ipsilon/login/authpam.py b/ipsilon/login/authpam.py index c7cb9a0..e07bedf 100755 --- a/ipsilon/login/authpam.py +++ b/ipsilon/login/authpam.py @@ -18,7 +18,6 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. from ipsilon.login.common import LoginFormBase, LoginManagerBase -from ipsilon.login.common import FACILITY from ipsilon.util.plugin import PluginObject from ipsilon.util import config as pconfig import pam @@ -120,9 +119,10 @@ for authentication. """ class Installer(object): - def __init__(self): + def __init__(self, *pargs): self.name = 'pam' self.ptype = 'login' + self.pargs = pargs def install_args(self, group): group.add_argument('--pam', choices=['yes', 'no'], default='no', @@ -135,25 +135,16 @@ class Installer(object): return # Add configuration data to database - po = PluginObject() + po = PluginObject(*self.pargs) po.name = 'pam' po.wipe_data() - - po.wipe_config_values(FACILITY) + po.wipe_config_values() config = {'service name': opts['pam_service']} - po.save_plugin_config(FACILITY, config) + po.save_plugin_config(config) # Update global config to add login plugin - po = PluginObject() - po.name = 'global' - globalconf = po.get_plugin_config(FACILITY) - if 'order' in globalconf: - order = globalconf['order'].split(',') - else: - order = [] - order.append('pam') - globalconf['order'] = ','.join(order) - po.save_plugin_config(FACILITY, globalconf) + po.is_enabled = True + po.save_enabled_state() # for selinux enabled platforms, ignore if it fails just report try: diff --git a/ipsilon/login/authtest.py b/ipsilon/login/authtest.py index e3f8eff..8a24500 100755 --- a/ipsilon/login/authtest.py +++ b/ipsilon/login/authtest.py @@ -18,7 +18,6 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. from ipsilon.login.common import LoginFormBase, LoginManagerBase -from ipsilon.login.common import FACILITY from ipsilon.util.plugin import PluginObject from ipsilon.util import config as pconfig import cherrypy @@ -102,9 +101,10 @@ Form based TEST login Manager, DO NOT EVER ACTIVATE IN PRODUCTION """ class Installer(object): - def __init__(self): + def __init__(self, *pargs): self.name = 'testauth' self.ptype = 'login' + self.pargs = pargs def install_args(self, group): group.add_argument('--testauth', choices=['yes', 'no'], default='no', @@ -114,19 +114,12 @@ class Installer(object): if opts['testauth'] != 'yes': return + print self.pargs # Add configuration data to database - po = PluginObject() + po = PluginObject(*self.pargs) po.name = 'testauth' po.wipe_data() # Update global config to add login plugin - po = PluginObject() - po.name = 'global' - globalconf = po.get_plugin_config(FACILITY) - if 'order' in globalconf: - order = globalconf['order'].split(',') - else: - order = [] - order.append('testauth') - globalconf['order'] = ','.join(order) - po.save_plugin_config(FACILITY, globalconf) + po.is_enabled = True + po.save_enabled_state() diff --git a/ipsilon/login/common.py b/ipsilon/login/common.py index ad09ce1..b394fa0 100755 --- a/ipsilon/login/common.py +++ b/ipsilon/login/common.py @@ -31,14 +31,13 @@ USERNAME_COOKIE = 'ipsilon_default_username' class LoginManagerBase(PluginConfig, PluginObject): - def __init__(self): + def __init__(self, *args): PluginConfig.__init__(self) - PluginObject.__init__(self) + PluginObject.__init__(self, *args) + self._root = None self._site = None self.path = '/' - self.next_login = None self.info = None - self.is_enabled = False def redirect_to_path(self, path): base = cherrypy.config.get('base.mount', "") @@ -94,8 +93,9 @@ class LoginManagerBase(PluginConfig, PluginObject): def auth_failed(self, trans): # try with next module - if self.next_login: - return self.redirect_to_path(self.next_login.path) + next_login = self.next_login() + if next_login: + return self.redirect_to_path(next_login.path) # return to the caller if any session = UserSession() @@ -117,62 +117,26 @@ class LoginManagerBase(PluginConfig, PluginObject): def get_tree(self, site): raise NotImplementedError - def enable(self, site): - if self.is_enabled: - return + def register(self, root, site): + self._root = root + self._site = site - if not self._site: - self._site = site + def next_login(self): plugins = self._site[FACILITY] + try: + idx = plugins.enabled.index(self.name) + item = plugins.enabled[idx + 1] + return plugins.available[item] + except (ValueError, IndexError): + return None - # configure self - if self.name in plugins['config']: - self.import_config(plugins['config'][self.name]) + def on_enable(self): # and add self to the root - root = plugins['root'] - root.add_subtree(self.name, self.get_tree(site)) - - # finally add self in login chain - prev_obj = None - for prev_obj in plugins['enabled']: - if prev_obj.next_login: - break - if prev_obj: - while prev_obj.next_login: - prev_obj = prev_obj.next_login - prev_obj.next_login = self - if not root.first_login: - root.first_login = self - - plugins['enabled'].append(self) - self.is_enabled = True - self._debug('Login plugin enabled: %s' % self.name) + self._root.add_subtree(self.name, self.get_tree(self._site)) # Get handle of the info plugin - self.info = root.info - - def disable(self, site): - if not self.is_enabled: - return - - plugins = self._site[FACILITY] - - # remove self from chain - root = plugins['root'] - if root.first_login == self: - root.first_login = self.next_login - elif root.first_login: - prev_obj = root.first_login - while prev_obj.next_login != self: - prev_obj = prev_obj.next_login - if prev_obj: - prev_obj.next_login = self.next_login - self.next_login = None - - plugins['enabled'].remove(self) - self.is_enabled = False - self._debug('Login plugin disabled: %s' % self.name) + self.info = self._root.info class LoginPageBase(Page): @@ -207,8 +171,9 @@ class LoginFormBase(LoginPageBase): def create_tmpl_context(self, **kwargs): next_url = None - if self.lm.next_login is not None: - next_url = '%s?%s' % (self.lm.next_login.path, + next_login = self.lm.next_login() + if next_login: + next_url = '%s?%s' % (next_login.path, self.trans.get_GET_arg()) cookie = SecureCookie(USERNAME_COOKIE) @@ -253,31 +218,42 @@ class Login(Page): def __init__(self, *args, **kwargs): super(Login, self).__init__(*args, **kwargs) self.cancel = Cancel(*args, **kwargs) - self.first_login = None self.info = Info(self._site) - loader = PluginLoader(Login, FACILITY, 'LoginManager') - self._site[FACILITY] = loader.get_plugin_data() - plugins = self._site[FACILITY] + plugins = PluginLoader(Login, FACILITY, 'LoginManager') + plugins.get_plugin_data() + self._site[FACILITY] = plugins - available = plugins['available'].keys() + available = plugins.available.keys() self._debug('Available login managers: %s' % str(available)) - plugins['root'] = self - for item in plugins['whitelist']: - self._debug('Login plugin in whitelist: %s' % item) - if item not in plugins['available']: + for item in plugins.available: + plugin = plugins.available[item] + plugin.register(self, self._site) + + for item in plugins.enabled: + self._debug('Login plugin in enabled list: %s' % item) + if item not in plugins.available: continue - plugins['available'][item].enable(self._site) + plugins.available[item].enable() def add_subtree(self, name, page): self.__dict__[name] = page + def get_first_login(self): + plugin = None + plugins = self._site[FACILITY] + if plugins.enabled: + first = plugins.enabled[0] + plugin = plugins.available[first] + return plugin + def root(self, *args, **kwargs): - if self.first_login: + plugin = self.get_first_login() + if plugin: trans = self.get_valid_transaction('login', **kwargs) redirect = '%s/login/%s?%s' % (self.basepath, - self.first_login.path, + plugin.path, trans.get_GET_arg()) raise cherrypy.HTTPRedirect(redirect) return self._template('login/index.html', title='Login') @@ -312,5 +288,5 @@ class Cancel(Page): class LoginMgrsInstall(object): def __init__(self): - pi = PluginInstaller(LoginMgrsInstall) + pi = PluginInstaller(LoginMgrsInstall, FACILITY) self.plugins = pi.get_plugins() diff --git a/ipsilon/providers/common.py b/ipsilon/providers/common.py index ead50e2..03118ae 100755 --- a/ipsilon/providers/common.py +++ b/ipsilon/providers/common.py @@ -51,68 +51,29 @@ class InvalidRequest(ProviderException): class ProviderBase(PluginConfig, PluginObject): - def __init__(self, name, path): + def __init__(self, name, path, *pargs): PluginConfig.__init__(self) - PluginObject.__init__(self) + PluginObject.__init__(self, *pargs) self.name = name + self._root = None self.path = path self.tree = None - self.is_enabled = False - - def on_enable(self): - # this one does nothing - # derived classes can override with custom behavior - return def get_tree(self, site): raise NotImplementedError - def register(self, site): - if self.tree: - # already registered - return - - # configure self - plugins = site[FACILITY] - if self.name in plugins['config']: - self.import_config(plugins['config'][self.name]) + def register(self, root, site): + self._root = root # init pages and admin interfaces self.tree = self.get_tree(site) - self._debug('IdP Provider registered: %s' % self.name) - if self.get_config_value('enabled') is True: - # and enable self - self._enable(site) - - def _enable(self, site): - root = site[FACILITY]['root'] - root.add_subtree(self.name, self.tree) - self._debug('IdP Provider enabled: %s' % self.name) - self.is_enabled = True - self.on_enable() - - def enable(self, site): - if self.is_enabled: - return - - self._enable(site) - self.set_config_value('enabled', True) - self.save_plugin_config(FACILITY) - - def disable(self, site): - if not self.is_enabled: - return - - # remove self to the root - root = site[FACILITY]['root'] - root.del_subtree(self.name) + def on_enable(self): + self._root.add_subtree(self.name, self.tree) - self.is_enabled = False - self.set_config_value('enabled', False) - self.save_plugin_config(FACILITY) - self._debug('IdP Provider disabled: %s' % self.name) + def on_disable(self): + self._root.del_subtree(self.name) class ProviderPageBase(Page): @@ -155,21 +116,26 @@ FACILITY = 'provider_config' class LoadProviders(Log): def __init__(self, root, site): - loader = PluginLoader(LoadProviders, FACILITY, 'IdpProvider') - site[FACILITY] = loader.get_plugin_data() - providers = site[FACILITY] + plugins = PluginLoader(LoadProviders, FACILITY, 'IdpProvider') + plugins.get_plugin_data() + site[FACILITY] = plugins - available = providers['available'].keys() + available = plugins.available.keys() self._debug('Available providers: %s' % str(available)) - providers['root'] = root - for item in providers['available']: - plugin = providers['available'][item] - plugin.register(site) + for item in plugins.available: + plugin = plugins.available[item] + plugin.register(root, site) + + for item in plugins.enabled: + self._debug('Provider plugin in enabled list: %s' % item) + if item not in plugins.available: + continue + plugins.available[item].enable() class ProvidersInstall(object): def __init__(self): - pi = PluginInstaller(ProvidersInstall) + pi = PluginInstaller(ProvidersInstall, FACILITY) self.plugins = pi.get_plugins() diff --git a/ipsilon/providers/openid/extensions/ax.py b/ipsilon/providers/openid/extensions/ax.py index 7daa52a..d00a4fc 100755 --- a/ipsilon/providers/openid/extensions/ax.py +++ b/ipsilon/providers/openid/extensions/ax.py @@ -28,7 +28,7 @@ AP_MAP = { class OpenidExtension(OpenidExtensionBase): - def __init__(self): + def __init__(self, *pargs): super(OpenidExtension, self).__init__('Attribute Exchange') self.type_uris = [ ax.AXMessage.ns_uri, diff --git a/ipsilon/providers/openid/extensions/cla.py b/ipsilon/providers/openid/extensions/cla.py index cc4d11d..481f341 100755 --- a/ipsilon/providers/openid/extensions/cla.py +++ b/ipsilon/providers/openid/extensions/cla.py @@ -10,7 +10,7 @@ from openid_cla import cla class OpenidExtension(OpenidExtensionBase): - def __init__(self): + def __init__(self, *pargs): super(OpenidExtension, self).__init__('CLAs') self.type_uris = [ cla.cla_uri, diff --git a/ipsilon/providers/openid/extensions/common.py b/ipsilon/providers/openid/extensions/common.py index 804f695..02cd1a0 100755 --- a/ipsilon/providers/openid/extensions/common.py +++ b/ipsilon/providers/openid/extensions/common.py @@ -50,22 +50,20 @@ FACILITY = 'openid_extensions' class LoadExtensions(Log): def __init__(self): - loader = PluginLoader(LoadExtensions, FACILITY, 'OpenidExtension') - self.plugins = loader.get_plugin_data() + self.plugins = PluginLoader(LoadExtensions, + FACILITY, 'OpenidExtension') + self.plugins.get_plugin_data() - available = self.plugins['available'].keys() + available = self.plugins.available.keys() self._debug('Available Extensions: %s' % str(available)) def enable(self, enabled): for item in enabled: - if item not in self.plugins['available']: + if item not in self.plugins.available: self.debug('<%s> not available' % item) continue self.debug('Enable OpenId extension: %s' % item) - self.plugins['available'][item].enable() + self.plugins.available[item].enable() def available(self): - available = self.plugins['available'] - if available is None: - available = dict() - return available + return self.plugins.available diff --git a/ipsilon/providers/openid/extensions/fas_teams.py b/ipsilon/providers/openid/extensions/fas_teams.py index fd9dd27..4de2e83 100755 --- a/ipsilon/providers/openid/extensions/fas_teams.py +++ b/ipsilon/providers/openid/extensions/fas_teams.py @@ -10,7 +10,7 @@ from openid_teams import teams class OpenidExtension(Teams): - def __init__(self): + def __init__(self, *pargs): super(OpenidExtension, self).__init__('Fedora Teams') def _resp(self, request, userdata): diff --git a/ipsilon/providers/openid/extensions/sreg.py b/ipsilon/providers/openid/extensions/sreg.py index a2b4db7..e1144fc 100755 --- a/ipsilon/providers/openid/extensions/sreg.py +++ b/ipsilon/providers/openid/extensions/sreg.py @@ -10,7 +10,7 @@ from openid.extensions import sreg class OpenidExtension(OpenidExtensionBase): - def __init__(self): + def __init__(self, *pargs): super(OpenidExtension, self).__init__('Simple Registration') self.type_uris = [ sreg.ns_uri_1_1, diff --git a/ipsilon/providers/openid/extensions/teams.py b/ipsilon/providers/openid/extensions/teams.py index 50c09af..258a437 100755 --- a/ipsilon/providers/openid/extensions/teams.py +++ b/ipsilon/providers/openid/extensions/teams.py @@ -34,5 +34,5 @@ class Teams(OpenidExtensionBase): class OpenidExtension(Teams): - def __init__(self): + def __init__(self, *pargs): super(OpenidExtension, self).__init__('Teams') diff --git a/ipsilon/providers/openidp.py b/ipsilon/providers/openidp.py index 197b1cf..335b41b 100755 --- a/ipsilon/providers/openidp.py +++ b/ipsilon/providers/openidp.py @@ -5,7 +5,6 @@ from __future__ import absolute_import from ipsilon.providers.common import ProviderBase -from ipsilon.providers.common import FACILITY from ipsilon.providers.openid.auth import OpenID from ipsilon.providers.openid.extensions.common import LoadExtensions from ipsilon.util.plugin import PluginObject @@ -19,8 +18,8 @@ from openid.store.memstore import MemoryStore class IdpProvider(ProviderBase): - def __init__(self): - super(IdpProvider, self).__init__('openid', 'openid') + def __init__(self, *pargs): + super(IdpProvider, self).__init__('openid', 'openid', *pargs) self.mapping = InfoMapping() self.page = None self.server = None @@ -55,10 +54,6 @@ Provides OpenID 2.0 authentication infrastructure. """ 'enabled extensions', 'Choose the extensions to enable', self.extensions.available().keys()), - pconfig.Condition( - 'enabled', - 'Whether the OpenID IDP is enabled', - False) ) @property @@ -99,10 +94,10 @@ Provides OpenID 2.0 authentication infrastructure. """ # self.admin = AdminPage(site, self) # Expose OpenID presence in the root - headers = site[FACILITY]['root'].default_headers + headers = self._root.default_headers headers['X-XRDS-Location'] = self.endpoint_url+'XRDS' - html_heads = site[FACILITY]['root'].html_heads + html_heads = self._root.html_heads HEAD_LINK = '<link rel="%s" href="%s">' openid_heads = [HEAD_LINK % ('openid2.provider', self.endpoint_url), HEAD_LINK % ('openid.server', self.endpoint_url)] @@ -114,15 +109,17 @@ Provides OpenID 2.0 authentication infrastructure. """ self.server = Server(MemoryStore(), op_endpoint=self.endpoint_url) def on_enable(self): + super(IdpProvider, self).on_enable() self.init_idp() self.extensions.enable(self._config['enabled extensions'].get_value()) class Installer(object): - def __init__(self): + def __init__(self, *pargs): self.name = 'openid' self.ptype = 'provider' + self.pargs = pargs def install_args(self, group): group.add_argument('--openid', choices=['yes', 'no'], default='yes', @@ -139,12 +136,14 @@ class Installer(object): proto, opts['hostname'], opts['instance']) # Add configuration data to database - po = PluginObject() + po = PluginObject(*self.pargs) po.name = 'openid' po.wipe_data() - - po.wipe_config_values(FACILITY) + po.wipe_config_values() config = {'endpoint url': url, - 'identity_url_template': '%sid/%%(username)s' % url, - 'enabled': '1'} - po.save_plugin_config(FACILITY, config) + 'identity_url_template': '%sid/%%(username)s' % url} + po.save_plugin_config(config) + + # Update global config to add login plugin + po.is_enabled = True + po.save_enabled_state() diff --git a/ipsilon/providers/saml2idp.py b/ipsilon/providers/saml2idp.py index 8896e16..b0f4304 100755 --- a/ipsilon/providers/saml2idp.py +++ b/ipsilon/providers/saml2idp.py @@ -18,7 +18,6 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. from ipsilon.providers.common import ProviderBase, ProviderPageBase -from ipsilon.providers.common import FACILITY from ipsilon.providers.saml2.auth import AuthenticateRequest from ipsilon.providers.saml2.admin import Saml2AdminPage from ipsilon.providers.saml2.provider import IdentityProvider @@ -119,8 +118,8 @@ class SAML2(ProviderPageBase): class IdpProvider(ProviderBase): - def __init__(self): - super(IdpProvider, self).__init__('saml2', 'saml2') + def __init__(self, *pargs): + super(IdpProvider, self).__init__('saml2', 'saml2', *pargs) self.admin = None self.page = None self.idp = None @@ -163,10 +162,6 @@ Provides SAML 2.0 authentication infrastructure. """ 'default email domain', 'Used for users missing the email property.', 'example.com'), - pconfig.Condition( - 'enabled', - 'Whether the SAML IDP is enabled', - False) ) if cherrypy.config.get('debug', False): import logging @@ -242,7 +237,8 @@ Provides SAML 2.0 authentication infrastructure. """ return idp def on_enable(self): - self.init_idp() + super(IdpProvider, self).on_enable() + self.idp = self.init_idp() if hasattr(self, 'admin'): if self.admin: self.admin.add_sps() @@ -250,9 +246,10 @@ Provides SAML 2.0 authentication infrastructure. """ class Installer(object): - def __init__(self): + def __init__(self, *pargs): self.name = 'saml2' self.ptype = 'provider' + self.pargs = pargs def install_args(self, group): group.add_argument('--saml2', choices=['yes', 'no'], default='yes', @@ -297,17 +294,19 @@ class Installer(object): meta.output(os.path.join(path, 'metadata.xml')) # Add configuration data to database - po = PluginObject() + po = PluginObject(*self.pargs) po.name = 'saml2' po.wipe_data() - - po.wipe_config_values(FACILITY) + po.wipe_config_values() config = {'idp storage path': path, 'idp metadata file': 'metadata.xml', 'idp certificate file': cert.cert, - 'idp key file': cert.key, - 'enabled': '1'} - po.save_plugin_config(FACILITY, config) + 'idp key file': cert.key} + po.save_plugin_config(config) + + # Update global config to add login plugin + po.is_enabled = True + po.save_enabled_state() # Fixup permissions so only the ipsilon user can read these files files.fix_user_dirs(path, opts['system_user']) diff --git a/ipsilon/util/plugin.py b/ipsilon/util/plugin.py index f303078..063767c 100755 --- a/ipsilon/util/plugin.py +++ b/ipsilon/util/plugin.py @@ -31,7 +31,7 @@ class Plugins(object): def __init__(self): self._providers_tree = None - def _load_class(self, tree, class_type, file_name): + def _load_class(self, tree, class_type, file_name, *pargs): cherrypy.log.error('Check module %s for class %s' % (file_name, class_type)) name, ext = os.path.splitext(os.path.split(file_name)[-1]) @@ -47,12 +47,12 @@ class Plugins(object): return if hasattr(mod, class_type): - instance = getattr(mod, class_type)() + instance = getattr(mod, class_type)(*pargs) public_name = getattr(instance, 'name', name) tree[public_name] = instance cherrypy.log.error('Added module %s as %s' % (name, public_name)) - def _load_classes(self, tree, path, class_type): + def _load_classes(self, tree, path, class_type, *pargs): files = None try: files = os.listdir(path) @@ -62,58 +62,108 @@ class Plugins(object): for name in files: filename = os.path.join(path, name) - self._load_class(tree, class_type, filename) + self._load_class(tree, class_type, filename, *pargs) - def get_plugins(self, path, class_type): + def get_plugins(self, path, class_type, *pargs): plugins = dict() - self._load_classes(plugins, path, class_type) + self._load_classes(plugins, path, class_type, *pargs) return plugins -class PluginLoader(object): +class PluginLoader(Log): def __init__(self, baseobj, facility, plugin_type): - config = AdminStore().load_options(facility) - cherrypy.log('LOAD: %s\n' % repr(config)) - whitelist = [] - if 'global' in config: - sec = config['global'] - if 'order' in sec: - whitelist = sec['order'].split(',') - if cherrypy.config.get('debug', False): - cherrypy.log('[%s] %s: %s' % (facility, whitelist, config)) - if config is None: - config = dict() + self._pathname, _ = os.path.split(inspect.getfile(baseobj)) + self.facility = facility + self._plugin_type = plugin_type + self.available = dict() + self.enabled = list() + self.__data = None + + # Defer initialization or instantiating the store will fail at load + # time when used with Installer plugins as the cherrypy config context + # is created after all Installer plugins are loaded. + @property + def _data(self): + if not self.__data: + self.__data = AdminStore() + return self.__data + def get_plugins(self): p = Plugins() - (pathname, dummy) = os.path.split(inspect.getfile(baseobj)) - self._plugins = { - 'config': config, - 'available': p.get_plugins(pathname, plugin_type), - 'whitelist': whitelist, - 'enabled': [] - } + return p.get_plugins(self._pathname, self._plugin_type, self) + + def refresh_enabled(self): + config = self._data.load_options(self.facility, name='global') + self.enabled = [] + if config: + if 'enabled' in config: + self.enabled = config['enabled'].split(',') def get_plugin_data(self): - return self._plugins + self.available = self.get_plugins() + self.refresh_enabled() + def save_enabled(self, enabled): + if enabled: + self._data.save_options(self.facility, 'global', + {'enabled': ','.join(enabled)}) + else: + self._data.delete_options(self.facility, 'global', + {'enabled': '*'}) + self.debug('Plugin enabled state saved: %s' % enabled) + self.refresh_enabled() -class PluginInstaller(object): - def __init__(self, baseobj): - (pathname, dummy) = os.path.split(inspect.getfile(baseobj)) - self._pathname = pathname - def get_plugins(self): - p = Plugins() - return p.get_plugins(self._pathname, 'Installer') +class PluginInstaller(PluginLoader): + def __init__(self, baseobj, facility): + super(PluginInstaller, self).__init__(baseobj, facility, 'Installer') class PluginObject(Log): - def __init__(self): + def __init__(self, plugins): self.name = None self._config = None self._data = AdminStore() + self._plugins = plugins + self.is_enabled = False + + def on_enable(self): + return + + def on_disable(self): + return + + def save_enabled_state(self): + enabled = [] + self._plugins.refresh_enabled() + enabled.extend(self._plugins.enabled) + if self.is_enabled: + if self.name not in enabled: + enabled.append(self.name) + else: + if self.name in enabled: + enabled.remove(self.name) + self._plugins.save_enabled(enabled) + + def enable(self): + if self.is_enabled: + return + + self.refresh_plugin_config() + self.on_enable() + self.is_enabled = True + self.debug('Plugin enabled: %s' % self.name) + + def disable(self): + if not self.is_enabled: + return + + self.on_disable() + + self.is_enabled = False + self.debug('Plugin disabled: %s' % self.name) def import_config(self, config): self._config = config @@ -121,18 +171,19 @@ class PluginObject(Log): def export_config(self): return self._config - def get_plugin_config(self, facility): - return self._data.load_options(facility, self.name) + def get_plugin_config(self): + return self._data.load_options(self._plugins.facility, self.name) - def refresh_plugin_config(self, facility): - config = self.get_plugin_config(facility) - self.import_config(config) + def refresh_plugin_config(self): + config = self.get_plugin_config() + if config: + self.import_config(config) - def save_plugin_config(self, facility, config=None): + def save_plugin_config(self, config=None): if config is None: config = self.export_config() - self._data.save_options(facility, self.name, config) + self._data.save_options(self._plugins.facility, self.name, config) def get_data(self, idval=None, name=None, value=None): return self._data.get_data(self.name, idval=idval, name=name, @@ -147,8 +198,8 @@ class PluginObject(Log): def del_datum(self, idval): self._data.del_datum(self.name, idval) - def wipe_config_values(self, facility): - self._data.delete_options(facility, self.name, None) + def wipe_config_values(self): + self._data.delete_options(self._plugins.facility, self.name, None) def wipe_data(self): self._data.wipe_data(self.name) |