summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xipsilon/admin/common.py74
-rwxr-xr-xipsilon/info/common.py11
-rwxr-xr-xipsilon/info/infoldap.py55
-rwxr-xr-xipsilon/info/nss.py1
-rwxr-xr-xipsilon/login/authfas.py63
-rwxr-xr-xipsilon/login/authform.py34
-rwxr-xr-xipsilon/login/authkrb.py1
-rwxr-xr-xipsilon/login/authldap.py74
-rwxr-xr-xipsilon/login/authpam.py44
-rwxr-xr-xipsilon/login/authtest.py36
-rwxr-xr-xipsilon/login/common.py11
-rwxr-xr-xipsilon/providers/common.py17
-rwxr-xr-xipsilon/providers/openid/auth.py4
-rwxr-xr-xipsilon/providers/openid/extensions/common.py10
-rwxr-xr-xipsilon/providers/openid/meta.py4
-rwxr-xr-xipsilon/providers/openidp.py69
-rwxr-xr-xipsilon/providers/saml2idp.py84
-rwxr-xr-xipsilon/util/config.py51
-rwxr-xr-xipsilon/util/plugin.py107
-rw-r--r--templates/admin/plugin_config.html61
-rw-r--r--templates/admin/plugins.html4
-rw-r--r--templates/admin/providers.html4
22 files changed, 401 insertions, 418 deletions
diff --git a/ipsilon/admin/common.py b/ipsilon/admin/common.py
index b9dfbf4..3d5313a 100755
--- a/ipsilon/admin/common.py
+++ b/ipsilon/admin/common.py
@@ -21,6 +21,7 @@ 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
class AdminPage(Page):
@@ -46,33 +47,13 @@ class AdminPluginConfig(AdminPage):
self.menu = [parent]
self.back = parent.url
- # Get the defaults
- options = po.get_config_desc()
- if options is None:
- options = dict()
-
- self.options_order = []
- if hasattr(po, 'conf_opt_order'):
- self.options_order = po.conf_opt_order
-
- # append any undefined options
- add = []
- for k in options.keys():
- if k not in self.options_order:
- add.append(k)
- if len(add):
- add.sort()
- for k in add:
- self.options_order.append(k)
-
def root_with_msg(self, message=None, message_type=None):
return self._template('admin/plugin_config.html', title=self.title,
menu=self.menu, action=self.url, back=self.back,
message=message, message_type=message_type,
name='admin_%s_%s_form' % (self.facility,
self._po.name),
- options_order=self.options_order,
- plugin=self._po)
+ config=self._po.get_config_obj())
@admin_protect
def GET(self, *args, **kwargs):
@@ -83,31 +64,46 @@ class AdminPluginConfig(AdminPage):
message = "Nothing was modified."
message_type = "info"
- new_values = dict()
-
- # Get the defaults
- options = self._po.get_config_desc()
- if options is None:
- options = dict()
-
- for key, value in kwargs.iteritems():
- if key in options:
- if value != self._po.get_config_value(key):
- cherrypy.log.error("Storing [%s]: %s = %s" %
- (self._po.name, key, value))
- new_values[key] = value
-
- if len(new_values) != 0:
+ new_db_values = dict()
+
+ conf = self._po.get_config_obj()
+
+ for name, option in conf.iteritems():
+ if name in kwargs:
+ value = kwargs[name]
+ if isinstance(option, pconfig.List):
+ value = [x.strip() for x in value.split('\n')]
+ elif isinstance(option, pconfig.Condition):
+ value = True
+ else:
+ if isinstance(option, pconfig.Condition):
+ value = False
+ elif isinstance(option, pconfig.Choice):
+ value = list()
+ for a in option.get_allowed():
+ aname = '%s_%s' % (name, a)
+ if aname in kwargs:
+ value.append(a)
+ else:
+ continue
+
+ if value != option.get_value():
+ cherrypy.log.error("Storing [%s]: %s = %s" %
+ (self._po.name, name, value))
+ option.set_value(value)
+ new_db_values[name] = option.export_value()
+
+ if len(new_db_values) != 0:
# First we try to save in the database
try:
- self._po.save_plugin_config(self.facility, new_values)
+ self._po.save_plugin_config(self.facility, new_db_values)
message = "New configuration saved."
message_type = "success"
except Exception: # pylint: disable=broad-except
message = "Failed to save data!"
message_type = "error"
- # And only if it succeeds we change the live object
+ # Then refresh the actual objects
self._po.refresh_plugin_config(self.facility)
return self.root_with_msg(message=message,
@@ -210,7 +206,7 @@ class AdminPlugins(AdminPage):
po.name = "global"
globalconf = dict()
globalconf['order'] = ','.join(names)
- po.set_config(globalconf)
+ po.import_config(globalconf)
po.save_plugin_config(self.facility)
def reorder_plugins(self, names):
diff --git a/ipsilon/info/common.py b/ipsilon/info/common.py
index 03de66a..586b9e5 100755
--- a/ipsilon/info/common.py
+++ b/ipsilon/info/common.py
@@ -5,14 +5,15 @@
# See the file named COPYING for the project license
from ipsilon.util.log import Log
-from ipsilon.util.plugin import PluginLoader, PluginObject
-from ipsilon.util.plugin import PluginInstaller
+from ipsilon.util.plugin import PluginInstaller, PluginLoader
+from ipsilon.util.plugin import PluginObject, PluginConfig
-class InfoProviderBase(PluginObject, Log):
+class InfoProviderBase(PluginConfig, PluginObject):
def __init__(self):
- super(InfoProviderBase, self).__init__()
+ PluginConfig.__init__(self)
+ PluginObject.__init__(self)
self._site = None
self.is_enabled = False
@@ -29,7 +30,7 @@ class InfoProviderBase(PluginObject, Log):
# configure self
if self.name in plugins['config']:
- self.set_config(plugins['config'][self.name])
+ self.import_config(plugins['config'][self.name])
plugins['enabled'].append(self)
self.is_enabled = True
diff --git a/ipsilon/info/infoldap.py b/ipsilon/info/infoldap.py
index 70d36d5..369d3f1 100755
--- a/ipsilon/info/infoldap.py
+++ b/ipsilon/info/infoldap.py
@@ -8,7 +8,7 @@ from ipsilon.info.common import InfoProviderBase
from ipsilon.info.common import InfoProviderInstaller
from ipsilon.info.common import InfoMapping
from ipsilon.util.plugin import PluginObject
-from ipsilon.util.log import Log
+from ipsilon.util import config as pconfig
import ldap
@@ -27,7 +27,7 @@ ldap_mapping = {
}
-class InfoProvider(InfoProviderBase, Log):
+class InfoProvider(InfoProviderBase):
def __init__(self):
super(InfoProvider, self).__init__()
@@ -36,34 +36,29 @@ class InfoProvider(InfoProviderBase, Log):
self.name = 'ldap'
self.description = """
Info plugin that uses LDAP to retrieve user data. """
- self._options = {
- 'server url': [
- """ The LDAP server url """,
- 'string',
- 'ldap://example.com'
- ],
- 'tls': [
- " What TLS level show be required " +
- "(Demand, Allow, Try, Never, NoTLS) ",
- 'string',
- 'Demand'
- ],
- 'bind dn': [
- """ User DN to bind as, if empty uses anonymous bind. """,
- 'string',
- 'uid=ipsilon,ou=People,dc=example,dc=com'
- ],
- 'bind password': [
- """ Password to use for bind operation """,
- 'string',
- 'Password'
- ],
- 'user dn template': [
- """ Template to turn username into DN. """,
- 'string',
- 'uid=%(username)s,ou=People,dc=example,dc=com'
- ],
- }
+ self.new_config(
+ self.name,
+ pconfig.String(
+ 'server url',
+ 'The LDAP server url.',
+ 'ldap://example.com'),
+ pconfig.Template(
+ 'user dn template',
+ 'Template to turn username into DN.',
+ 'uid=%(username)s,ou=People,dc=example,dc=com'),
+ pconfig.Pick(
+ 'tls',
+ 'What TLS level show be required',
+ ['Demand', 'Allow', 'Try', 'Never', 'NoTLS'],
+ 'Demand'),
+ pconfig.String(
+ 'bind dn',
+ 'DN to bind as, if empty uses anonymous bind.',
+ 'uid=ipsilon,ou=People,dc=example,dc=com'),
+ pconfig.String(
+ 'bind password',
+ 'Password to use for bind operation'),
+ )
@property
def server_url(self):
diff --git a/ipsilon/info/nss.py b/ipsilon/info/nss.py
index 24f3522..3dfd885 100755
--- a/ipsilon/info/nss.py
+++ b/ipsilon/info/nss.py
@@ -25,6 +25,7 @@ class InfoProvider(InfoProviderBase):
self.mapper = InfoMapping()
self.mapper.set_mapping(posix_map)
self.name = 'nss'
+ self.new_config(self.name)
def _get_posix_user(self, user):
p = pwd.getpwnam(user)
diff --git a/ipsilon/login/authfas.py b/ipsilon/login/authfas.py
index c2d8fff..71db372 100755
--- a/ipsilon/login/authfas.py
+++ b/ipsilon/login/authfas.py
@@ -7,6 +7,7 @@ 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
from fedora.client.fasproxy import FasProxyClient
@@ -112,41 +113,33 @@ class LoginManager(LoginManagerBase):
self.description = """
Form based login Manager that uses the Fedora Authentication Server
"""
- self._options = {
- 'help text': [
- """ The text shown to guide the user at login time. """,
- 'string',
- 'Login wth your FAS credentials'
- ],
- 'username text': [
- """ The text shown to ask for the username in the form. """,
- 'string',
- 'FAS Username'
- ],
- 'password text': [
- """ The text shown to ask for the password in the form. """,
- 'string',
- 'Password'
- ],
- 'FAS url': [
- """ The FAS Url. """,
- 'string',
- 'https://admin.fedoraproject.org/accounts/'
- ],
- 'FAS Proxy client user Agent': [
- """ The User Agent presented to the FAS Server. """,
- 'string',
- 'Ipsilon v1.0'
- ],
- 'FAS Insecure Auth': [
- """ If 'YES' skips FAS server cert verification. """,
- 'string',
- ''
- ],
- }
- self.conf_opt_order = ['FAS url', 'FAS Proxy client user Agent',
- 'FAS Insecure Auth', 'username text',
- 'password text', 'help text']
+ self.new_config(
+ self.name,
+ pconfig.String(
+ 'FAS url',
+ 'The FAS Url.',
+ 'https://admin.fedoraproject.org/accounts/'),
+ pconfig.String(
+ 'FAS Proxy client user Agent',
+ 'The User Agent presented to the FAS Server.',
+ 'Ipsilon v1.0'),
+ pconfig.Condition(
+ 'FAS Insecure Auth',
+ 'If checked skips FAS server cert verification.',
+ False),
+ pconfig.String(
+ 'username text',
+ 'Text used to ask for the username at login time.',
+ 'FAS Username'),
+ pconfig.String(
+ 'password text',
+ 'Text used to ask for the password at login time.',
+ 'Password'),
+ pconfig.String(
+ 'help text',
+ 'Text used to guide the user at login time.',
+ 'Login with your FAS credentials')
+ )
@property
def help_text(self):
diff --git a/ipsilon/login/authform.py b/ipsilon/login/authform.py
index 418a5e5..4e9f5c1 100755
--- a/ipsilon/login/authform.py
+++ b/ipsilon/login/authform.py
@@ -21,6 +21,7 @@ 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
from string import Template
import cherrypy
import subprocess
@@ -54,24 +55,21 @@ class LoginManager(LoginManagerBase):
self.description = """
Form based login Manager. Relies on mod_intercept_form_submit plugin for
actual authentication. """
- self._options = {
- 'help text': [
- """ The text shown to guide the user at login time. """,
- 'string',
- 'Insert your Username and Password and then submit.'
- ],
- 'username text': [
- """ The text shown to ask for the username in the form. """,
- 'string',
- 'Username'
- ],
- 'password text': [
- """ The text shown to ask for the password in the form. """,
- 'string',
- 'Password'
- ],
- }
- self.conf_opt_order = ['username text', 'password text', 'help text']
+ self.new_config(
+ self.name,
+ pconfig.String(
+ 'username text',
+ 'Text used to ask for the username at login time.',
+ 'Username'),
+ pconfig.String(
+ 'password text',
+ 'Text used to ask for the password at login time.',
+ 'Password'),
+ pconfig.String(
+ 'help text',
+ 'Text used to guide the user at login time.',
+ 'Insert your Username and Password and then submit.')
+ )
@property
def help_text(self):
diff --git a/ipsilon/login/authkrb.py b/ipsilon/login/authkrb.py
index 6c561ac..f2af0a0 100755
--- a/ipsilon/login/authkrb.py
+++ b/ipsilon/login/authkrb.py
@@ -84,6 +84,7 @@ class LoginManager(LoginManagerBase):
self.description = """
Kereros Negotiate authentication plugin. Relies on the mod_auth_kerb apache
plugin for actual authentication. """
+ self.new_config(self.name)
def get_tree(self, site):
self.page = Krb(site, self)
diff --git a/ipsilon/login/authldap.py b/ipsilon/login/authldap.py
index ed75e91..f51f375 100755
--- a/ipsilon/login/authldap.py
+++ b/ipsilon/login/authldap.py
@@ -6,6 +6,7 @@ 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
from ipsilon.info.infoldap import InfoProvider as LDAPInfo
import ldap
@@ -107,47 +108,38 @@ class LoginManager(LoginManagerBase):
self.description = """
Form based login Manager that uses a simple bind LDAP operation to perform
authentication. """
- self._options = {
- 'help text': [
- """ The text shown to guide the user at login time. """,
- 'string',
- 'Insert your Username and Password and then submit.'
- ],
- 'username text': [
- """ The text shown to ask for the username in the form. """,
- 'string',
- 'Username'
- ],
- 'password text': [
- """ The text shown to ask for the password in the form. """,
- 'string',
- 'Password'
- ],
- 'server url': [
- """ The LDAP server url """,
- 'string',
- 'ldap://example.com'
- ],
- 'tls': [
- " What TLS level show be required " +
- "(Demand, Allow, Try, Never, NoTLS) ",
- 'string',
- 'Demand'
- ],
- 'bind dn template': [
- """ Template to turn username into DN. """,
- 'string',
- 'uid=%(username)s,ou=People,dc=example,dc=com'
- ],
- 'get user info': [
- """ Get user info via ldap directly after auth (Yes/No) """,
- 'string',
- 'Yes'
- ],
- }
- self.conf_opt_order = ['server url', 'bind dn template',
- 'get user info', 'tls', 'username text',
- 'password text', 'help text']
+ self.new_config(
+ self.name,
+ pconfig.String(
+ 'server url',
+ 'The LDAP server url.',
+ 'ldap://example.com'),
+ pconfig.Template(
+ 'bind dn template',
+ 'Template to turn username into DN.',
+ 'uid=%(username)s,ou=People,dc=example,dc=com'),
+ pconfig.Condition(
+ 'get user info',
+ 'Get user info via ldap using user credentials',
+ True),
+ pconfig.Pick(
+ 'tls',
+ 'What TLS level show be required',
+ ['Demand', 'Allow', 'Try', 'Never', 'NoTLS'],
+ 'Demand'),
+ pconfig.String(
+ 'username text',
+ 'Text used to ask for the username at login time.',
+ 'Username'),
+ pconfig.String(
+ 'password text',
+ 'Text used to ask for the password at login time.',
+ 'Password'),
+ pconfig.String(
+ 'help text',
+ 'Text used to guide the user at login time.',
+ 'Provide your Username and Password')
+ )
@property
def help_text(self):
diff --git a/ipsilon/login/authpam.py b/ipsilon/login/authpam.py
index 10b550e..c7cb9a0 100755
--- a/ipsilon/login/authpam.py
+++ b/ipsilon/login/authpam.py
@@ -20,6 +20,7 @@
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
import subprocess
@@ -76,30 +77,25 @@ class LoginManager(LoginManagerBase):
self.description = """
Form based login Manager that uses the system's PAM infrastructure
for authentication. """
- self._options = {
- 'service name': [
- """ The name of the PAM service used to authenticate. """,
- 'string',
- 'remote'
- ],
- 'help text': [
- """ The text shown to guide the user at login time. """,
- 'string',
- 'Insert your Username and Password and then submit.'
- ],
- 'username text': [
- """ The text shown to ask for the username in the form. """,
- 'string',
- 'Username'
- ],
- 'password text': [
- """ The text shown to ask for the password in the form. """,
- 'string',
- 'Password'
- ],
- }
- self.conf_opt_order = ['service name', 'username text',
- 'password text', 'help text']
+ self.new_config(
+ self.name,
+ pconfig.String(
+ 'service name',
+ 'The name of the PAM service used to authenticate.',
+ 'remote'),
+ pconfig.String(
+ 'username text',
+ 'Text used to ask for the username at login time.',
+ 'Username'),
+ pconfig.String(
+ 'password text',
+ 'Text used to ask for the password at login time.',
+ 'Password'),
+ pconfig.String(
+ 'help text',
+ 'Text used to guide the user at login time.',
+ 'Provide your Username and Password')
+ )
@property
def service_name(self):
diff --git a/ipsilon/login/authtest.py b/ipsilon/login/authtest.py
index 6288826..e3f8eff 100755
--- a/ipsilon/login/authtest.py
+++ b/ipsilon/login/authtest.py
@@ -20,6 +20,7 @@
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
@@ -63,23 +64,24 @@ class LoginManager(LoginManagerBase):
self.page = None
self.description = """
Form based TEST login Manager, DO NOT EVER ACTIVATE IN PRODUCTION """
- self._options = {
- 'help text': [
- """ The text shown to guide the user at login time. """,
- 'string',
- 'Insert your Username and Password and then submit.'
- ],
- 'username text': [
- """ The text shown to ask for the username in the form. """,
- 'string',
- 'Username'
- ],
- 'password text': [
- """ The text shown to ask for the password in the form. """,
- 'string',
- 'Password'
- ],
- }
+ self.new_config(
+ self.name,
+ pconfig.String(
+ 'username text',
+ 'Text used to ask for the username at login time.',
+ 'Username'),
+ pconfig.String(
+ 'password text',
+ 'Text used to ask for the password at login time.',
+ 'Password'),
+ pconfig.String(
+ 'help text',
+ 'Text used to guide the user at login time.',
+ 'DISABLE IN PRODUCTION, USE ONLY FOR TEST ' +
+ 'Use any username they are all valid, "admin" gives ' +
+ 'administrative powers. ' +
+ 'Use the fixed password "ipsilon" for any user')
+ )
@property
def help_text(self):
diff --git a/ipsilon/login/common.py b/ipsilon/login/common.py
index 028b754..ad09ce1 100755
--- a/ipsilon/login/common.py
+++ b/ipsilon/login/common.py
@@ -19,8 +19,8 @@
from ipsilon.util.page import Page
from ipsilon.util.user import UserSession
-from ipsilon.util.plugin import PluginLoader, PluginObject
-from ipsilon.util.plugin import PluginInstaller
+from ipsilon.util.plugin import PluginInstaller, PluginLoader
+from ipsilon.util.plugin import PluginObject, PluginConfig
from ipsilon.info.common import Info
from ipsilon.util.cookies import SecureCookie
import cherrypy
@@ -29,10 +29,11 @@ import cherrypy
USERNAME_COOKIE = 'ipsilon_default_username'
-class LoginManagerBase(PluginObject):
+class LoginManagerBase(PluginConfig, PluginObject):
def __init__(self):
- super(LoginManagerBase, self).__init__()
+ PluginConfig.__init__(self)
+ PluginObject.__init__(self)
self._site = None
self.path = '/'
self.next_login = None
@@ -126,7 +127,7 @@ class LoginManagerBase(PluginObject):
# configure self
if self.name in plugins['config']:
- self.set_config(plugins['config'][self.name])
+ self.import_config(plugins['config'][self.name])
# and add self to the root
root = plugins['root']
diff --git a/ipsilon/providers/common.py b/ipsilon/providers/common.py
index d882b40..ead50e2 100755
--- a/ipsilon/providers/common.py
+++ b/ipsilon/providers/common.py
@@ -18,8 +18,8 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from ipsilon.util.log import Log
-from ipsilon.util.plugin import PluginLoader, PluginObject
-from ipsilon.util.plugin import PluginInstaller
+from ipsilon.util.plugin import PluginInstaller, PluginLoader
+from ipsilon.util.plugin import PluginObject, PluginConfig
from ipsilon.util.page import Page
import cherrypy
@@ -49,10 +49,11 @@ class InvalidRequest(ProviderException):
self._debug(message)
-class ProviderBase(PluginObject):
+class ProviderBase(PluginConfig, PluginObject):
def __init__(self, name, path):
- super(ProviderBase, self).__init__()
+ PluginConfig.__init__(self)
+ PluginObject.__init__(self)
self.name = name
self.path = path
self.tree = None
@@ -74,14 +75,14 @@ class ProviderBase(PluginObject):
# configure self
plugins = site[FACILITY]
if self.name in plugins['config']:
- self.set_config(plugins['config'][self.name])
+ self.import_config(plugins['config'][self.name])
# 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') == '1':
+ if self.get_config_value('enabled') is True:
# and enable self
self._enable(site)
@@ -97,7 +98,7 @@ class ProviderBase(PluginObject):
return
self._enable(site)
- self.set_config_value('enabled', '1')
+ self.set_config_value('enabled', True)
self.save_plugin_config(FACILITY)
def disable(self, site):
@@ -109,7 +110,7 @@ class ProviderBase(PluginObject):
root.del_subtree(self.name)
self.is_enabled = False
- self.set_config_value('enabled', '0')
+ self.set_config_value('enabled', False)
self.save_plugin_config(FACILITY)
self._debug('IdP Provider disabled: %s' % self.name)
diff --git a/ipsilon/providers/openid/auth.py b/ipsilon/providers/openid/auth.py
index da110f7..fba8d10 100755
--- a/ipsilon/providers/openid/auth.py
+++ b/ipsilon/providers/openid/auth.py
@@ -168,7 +168,7 @@ class AuthenticateRequest(ProviderPageBase):
"Trust Root": request.trust_root,
}
userattrs = us.get_user_attrs()
- for n, e in self.cfg.extensions.items():
+ for n, e in self.cfg.extensions.available().items():
data = e.get_display_data(request, userattrs)
self.debug('%s returned %s' % (n, repr(data)))
for key, value in data.items():
@@ -194,7 +194,7 @@ class AuthenticateRequest(ProviderPageBase):
claimed_id=identity_url
)
userattrs = session.get_user_attrs()
- for _, e in self.cfg.extensions.items():
+ for _, e in self.cfg.extensions.available().items():
resp = e.get_response(request, userattrs)
if resp is not None:
response.addExtension(resp)
diff --git a/ipsilon/providers/openid/extensions/common.py b/ipsilon/providers/openid/extensions/common.py
index b75d394..804f695 100755
--- a/ipsilon/providers/openid/extensions/common.py
+++ b/ipsilon/providers/openid/extensions/common.py
@@ -49,13 +49,14 @@ FACILITY = 'openid_extensions'
class LoadExtensions(Log):
- def __init__(self, enabled):
+ def __init__(self):
loader = PluginLoader(LoadExtensions, FACILITY, 'OpenidExtension')
self.plugins = loader.get_plugin_data()
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']:
self.debug('<%s> not available' % item)
@@ -63,5 +64,8 @@ class LoadExtensions(Log):
self.debug('Enable OpenId extension: %s' % item)
self.plugins['available'][item].enable()
- def get_extensions(self):
- return self.plugins['available']
+ def available(self):
+ available = self.plugins['available']
+ if available is None:
+ available = dict()
+ return available
diff --git a/ipsilon/providers/openid/meta.py b/ipsilon/providers/openid/meta.py
index a04a78c..ea79439 100755
--- a/ipsilon/providers/openid/meta.py
+++ b/ipsilon/providers/openid/meta.py
@@ -42,7 +42,7 @@ class XRDSHandler(MetaHandler):
'http://specs.openid.net/auth/2.0/server',
'http://openid.net/server/1.0',
]
- for _, e in self.cfg.extensions.items():
+ for _, e in self.cfg.extensions.available().items():
types.extend(e.get_type_uris())
return self.reply(types=types,
@@ -65,7 +65,7 @@ class UserXRDSHandler(XRDSHandler):
'http://specs.openid.net/auth/2.0/signon',
'http://openid.net/signon/1.0',
]
- for _, e in self.cfg.extensions.items():
+ for _, e in self.cfg.extensions.available().items():
types.extend(e.get_type_uris())
return self.reply(types=types,
diff --git a/ipsilon/providers/openidp.py b/ipsilon/providers/openidp.py
index 5abdcad..197b1cf 100755
--- a/ipsilon/providers/openidp.py
+++ b/ipsilon/providers/openidp.py
@@ -9,6 +9,7 @@ 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
+from ipsilon.util import config as pconfig
from ipsilon.info.common import InfoMapping
from openid.server.server import Server
@@ -24,42 +25,41 @@ class IdpProvider(ProviderBase):
self.page = None
self.server = None
self.basepath = None
- self.extensions = None
+ self.extensions = LoadExtensions()
+ print self.extensions.available()
+ print self.extensions.available().keys()
self.description = """
Provides OpenID 2.0 authentication infrastructure. """
- self._options = {
- 'default email domain': [
- """Default email domain, for users missing email property.""",
- 'string',
- 'example.com'
- ],
- 'endpoint url': [
- """The Absolute URL of the OpenID provider""",
- 'string',
- 'http://localhost:8080/idp/openid/'
- ],
- 'identity url template': [
- """The templated URL where identities are exposed.""",
- 'string',
- 'http://localhost:8080/idp/openid/id/%(username)s'
- ],
- 'trusted roots': [
- """List of trusted relying parties.""",
- 'list',
- []
- ],
- 'untrusted roots': [
- """List of untrusted relying parties.""",
- 'list',
- []
- ],
- 'enabled extensions': [
- """List of enabled extensions""",
- 'list',
- []
- ],
- }
+ self.new_config(
+ self.name,
+ pconfig.String(
+ 'default email domain',
+ 'Used for users missing the email property.',
+ 'example.com'),
+ pconfig.String(
+ 'endpoint url',
+ 'The Absolute URL of the OpenID provider',
+ 'http://localhost:8080/idp/openid/'),
+ pconfig.Template(
+ 'identity url template',
+ 'The templated URL where identities are exposed.',
+ 'http://localhost:8080/idp/openid/id/%(username)s'),
+ pconfig.List(
+ 'trusted roots',
+ 'List of trusted relying parties.'),
+ pconfig.List(
+ 'untrusted roots',
+ 'List of untrusted relying parties.'),
+ pconfig.Choice(
+ 'enabled extensions',
+ 'Choose the extensions to enable',
+ self.extensions.available().keys()),
+ pconfig.Condition(
+ 'enabled',
+ 'Whether the OpenID IDP is enabled',
+ False)
+ )
@property
def endpoint_url(self):
@@ -112,11 +112,10 @@ Provides OpenID 2.0 authentication infrastructure. """
def init_idp(self):
self.server = Server(MemoryStore(), op_endpoint=self.endpoint_url)
- loader = LoadExtensions(self.enabled_extensions)
- self.extensions = loader.get_extensions()
def on_enable(self):
self.init_idp()
+ self.extensions.enable(self._config['enabled extensions'].get_value())
class Installer(object):
diff --git a/ipsilon/providers/saml2idp.py b/ipsilon/providers/saml2idp.py
index cb2c4a2..8896e16 100755
--- a/ipsilon/providers/saml2idp.py
+++ b/ipsilon/providers/saml2idp.py
@@ -27,6 +27,7 @@ from ipsilon.tools import saml2metadata as metadata
from ipsilon.tools import files
from ipsilon.util.user import UserSession
from ipsilon.util.plugin import PluginObject
+from ipsilon.util import config as pconfig
import cherrypy
import lasso
import os
@@ -126,48 +127,47 @@ class IdpProvider(ProviderBase):
self.description = """
Provides SAML 2.0 authentication infrastructure. """
- self._options = {
- 'idp storage path': [
- """ Path to data storage accessible by the IdP """,
- 'string',
- '/var/lib/ipsilon/saml2'
- ],
- 'idp metadata file': [
- """ The IdP Metadata file genearated at install time. """,
- 'string',
- 'metadata.xml'
- ],
- 'idp certificate file': [
- """ The IdP PEM Certificate genearated at install time. """,
- 'string',
- 'certificate.pem'
- ],
- 'idp key file': [
- """ The IdP Certificate Key genearated at install time. """,
- 'string',
- 'certificate.key'
- ],
- 'allow self registration': [
- """ Allow authenticated users to register applications. """,
- 'boolean',
- True
- ],
- 'default allowed nameids': [
- """Default Allowed NameIDs for Service Providers. """,
- 'list',
- ['persistent', 'transient', 'email', 'kerberos', 'x509']
- ],
- 'default nameid': [
- """Default NameID used by Service Providers. """,
- 'string',
- 'persistent'
- ],
- 'default email domain': [
- """Default email domain, for users missing email property.""",
- 'string',
- 'example.com'
- ]
- }
+ self.new_config(
+ self.name,
+ pconfig.String(
+ 'idp storage path',
+ 'Path to data storage accessible by the IdP.',
+ '/var/lib/ipsilon/saml2'),
+ pconfig.String(
+ 'idp metadata file',
+ 'The IdP Metadata file genearated at install time.',
+ 'metadata.xml'),
+ pconfig.String(
+ 'idp certificate file',
+ 'The IdP PEM Certificate genearated at install time.',
+ 'certificate.pem'),
+ pconfig.String(
+ 'idp key file',
+ 'The IdP Certificate Key genearated at install time.',
+ 'certificate.key'),
+ pconfig.Condition(
+ 'allow self registration',
+ 'Allow authenticated users to register applications.',
+ True),
+ pconfig.Choice(
+ 'default allowed nameids',
+ 'Default Allowed NameIDs for Service Providers.',
+ metadata.SAML2_NAMEID_MAP.keys(),
+ ['persistent', 'transient', 'email', 'kerberos', 'x509']),
+ pconfig.Pick(
+ 'default nameid',
+ 'Default NameID used by Service Providers.',
+ metadata.SAML2_NAMEID_MAP.keys(),
+ 'persistent'),
+ pconfig.String(
+ '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
import sys
diff --git a/ipsilon/util/config.py b/ipsilon/util/config.py
index 304c630..94443e3 100755
--- a/ipsilon/util/config.py
+++ b/ipsilon/util/config.py
@@ -165,7 +165,7 @@ class List(Option):
def export_value(self):
if self._assigned_value:
- return ', '.join(self._assigned_value)
+ return ','.join(self._assigned_value)
return None
def import_value(self, value):
@@ -205,19 +205,16 @@ class Choice(Option):
def set_value(self, value):
if type(value) is not list:
value = [value]
+ self._assigned_value = list()
for val in value:
- if type(val) is not tuple:
- val = (val, True)
- if val[0] not in self._allowed_values:
+ if val not in self._allowed_values:
raise ValueError(
- 'Value "%s" not allowed [%s]' % (val[0],
+ 'Value "%s" not allowed [%s]' % (val,
self._allowed_values))
- if val[1] is True:
- if val[0] not in self._assigned_value:
- self._assigned_value.append(val[0])
- else:
- if val[0] in self._assigned_value:
- self._assigned_value.remove(val[0])
+ self._assigned_value.append(val)
+
+ if not self._assigned_value:
+ self._assigned_value = None
def unset_value(self, value):
if type(value) is str:
@@ -236,11 +233,14 @@ class Choice(Option):
def import_value(self, value):
enabled = [x.strip() for x in value.split(',')]
+ if enabled:
+ if self._assigned_value is None:
+ self._assigned_value = list()
for val in enabled:
if val not in self._allowed_values:
# We silently ignore invalid options on import for now
continue
- self._assigned_value[val] = True
+ self._assigned_value.append(val)
class Pick(Option):
@@ -268,30 +268,11 @@ class Pick(Option):
self._str_import_value(value)
-class Condition(Option):
+class Condition(Pick):
def __init__(self, name, description, default_value=False):
- super(Condition, self).__init__(name, description)
- self._default_value = default_value
-
- def set_value(self, value):
- if value is True:
- self._assigned_value = True
- elif value is False:
- self._assigned_value = False
- else:
- raise ValueError('Value must be True or False, got %s' % value)
-
- def export_value(self):
- if self._assigned_value is True:
- return '1'
- elif self._assigned_value is False:
- return '0'
- else:
- return None
+ super(Condition, self).__init__(name, description,
+ [True, False], default_value)
def import_value(self, value):
- if value in ['1', 'YES']:
- self._assigned_value = True
- else:
- self._assigned_value = False
+ self._assigned_value = value == 'True'
diff --git a/ipsilon/util/plugin.py b/ipsilon/util/plugin.py
index 919a28d..f303078 100755
--- a/ipsilon/util/plugin.py
+++ b/ipsilon/util/plugin.py
@@ -21,6 +21,7 @@ import os
import imp
import cherrypy
import inspect
+from ipsilon.util.config import Config
from ipsilon.util.data import AdminStore
from ipsilon.util.log import Log
@@ -112,82 +113,24 @@ class PluginObject(Log):
def __init__(self):
self.name = None
self._config = None
- self._options = None
self._data = AdminStore()
- def get_config_desc(self, name=None):
- """ The configuration description is a dictionary that provides
- A description of the supported configuration options, as well
- as the default configuration option values.
- The key is the option name, the value is an array of 3 elements:
- - description
- - option type
- - default value
- """
- if name is None:
- return self._options
-
- opt = self._options.get(name, None)
- if opt is None:
- return ''
- return opt[0]
-
- def _value_to_list(self, name):
- if name not in self._config:
- return
- value = self._config[name]
- if type(value) is list:
- return
- vlist = [x.strip() for x in value.split(',')]
- self._config[name] = vlist
-
- def set_config(self, config):
+ def import_config(self, config):
self._config = config
- if self._config is None:
- return
- if self._options:
- for name, opt in self._options.iteritems():
- if opt[1] == 'list':
- self._value_to_list(name)
-
- def get_config_value(self, name):
- value = None
- if self._config:
- value = self._config.get(name, None)
- if value is None:
- if self._options:
- opt = self._options.get(name, None)
- if opt:
- value = opt[2]
- if cherrypy.config.get('debug', False):
- cherrypy.log('[%s] %s: %s' % (self.name, name, value))
-
- return value
-
- def set_config_value(self, option, value):
- if not self._config:
- self._config = dict()
- self._config[option] = value
- if self._options and option in self._options:
- if self._options[option][1] == 'list':
- self._value_to_list(option)
+ def export_config(self):
+ return self._config
def get_plugin_config(self, facility):
return self._data.load_options(facility, self.name)
def refresh_plugin_config(self, facility):
config = self.get_plugin_config(facility)
- self.set_config(config)
+ self.import_config(config)
def save_plugin_config(self, facility, config=None):
if config is None:
- config = self._config
- config = config.copy()
-
- for key, value in config.items():
- if type(value) is list:
- config[key] = ','.join(value)
+ config = self.export_config()
self._data.save_options(facility, self.name, config)
@@ -209,3 +152,41 @@ class PluginObject(Log):
def wipe_data(self):
self._data.wipe_data(self.name)
+
+
+class PluginConfig(Log):
+
+ def __init__(self):
+ self._config = None
+
+ def new_config(self, name, *config_args):
+ self._config = Config(name, *config_args)
+
+ def get_config_obj(self):
+ if self._config is None:
+ raise AttributeError('Config not initialized')
+ return self._config
+
+ def import_config(self, config):
+ if not self._config:
+ raise AttributeError('Config not initialized, cannot import')
+
+ for key, value in config.iteritems():
+ if key in self._config:
+ self._config[key].import_value(str(value))
+
+ def export_config(self):
+ config = dict()
+ for name, option in self._config.iteritems():
+ config[name] = option.export_value()
+ return config
+
+ def get_config_value(self, name):
+ if not self._config:
+ raise AttributeError('Config not initialized')
+ return self._config[name].get_value()
+
+ def set_config_value(self, name, value):
+ if not self._config:
+ raise AttributeError('Config not initialized')
+ return self._config[name].set_value(value)
diff --git a/templates/admin/plugin_config.html b/templates/admin/plugin_config.html
index 1372f55..7071c7e 100644
--- a/templates/admin/plugin_config.html
+++ b/templates/admin/plugin_config.html
@@ -19,18 +19,59 @@
<div id="options">
<form class="form-horizontal" role="form" id="{{ name }}" action="{{ action }}" method="post" enctype="application/x-www-form-urlencoded">
-
- {% for o in options_order %}
+ {% for k, v in config.iteritems() %}
<div class="form-group">
- <label class="col-sm-2" for="{{ o }}">{{ o }}:</label>
+ <label class="col-sm-2" for="{{ v.name }}">{{ v.name }}:</label>
<div class="col-sm-10">
- {% set val = plugin.get_config_value(o) %}
- {% if val is string %}
- <input type="text" class="form-control" name="{{ o }}" value="{{ val }}">
- {% else %}
- <input type="text" class="form-control" name="{{ o }}" value="{{ val|join(', ') }}">
- {% endif %}
- <span class="help-block">{{ plugin.get_config_desc(o) }}</span>
+ {%- set value = v.get_value() -%}
+ {% if v.__class__.__name__ in ['String', 'Template'] -%}
+ <input type="text" class="form-control" name="{{ v.name }}"
+ {%- if value %}
+ value="{{ value }}"
+ {%- endif -%}
+ >
+ {% elif v.__class__.__name__ == 'List' -%}
+ <textarea class="form-control" name="{{ v.name }}">
+ {%- if value %}
+ {{- value|join('\n') -}}
+ {%- endif -%}
+ </textarea>
+ {% elif v.__class__.__name__ == 'Choice' -%}
+ {% set entries = v.get_allowed() -%}
+ <div class="row">
+ {% for e in entries -%}
+ <div class="col-md-4">
+ <input type="checkbox" name="{{ v.name }}_{{ e }}"
+ {%- if value and e in value %}
+ checked="true"
+ {%- endif -%}
+ >&nbsp;{{ e }}
+ </div>
+ {% endfor %}
+ </div>
+ {% elif v.__class__.__name__ == 'Pick' -%}
+ {% set entries = v.get_allowed() -%}
+ <div class="row">
+ {% for e in entries -%}
+ <div class="col-md-4">
+ <input type="radio" name="{{ v.name }}" value="{{ e }}"
+ {%- if e == value %}
+ checked="true"
+ {%- endif -%}
+ >&nbsp;{{ e }}
+ </div>
+ {% endfor %}
+ </div>
+ {% elif v.__class__.__name__ == 'Condition' -%}
+ <input type="checkbox" name="{{ v.name }}"
+ {%- if value %}
+ checked="true"
+ {% endif -%}
+ >
+ {% else -%}
+ {{ v.__class__.__name__ }}
+ {% endif -%}
+ <span class="help-block">{{ v.description }}</span>
</div>
</div>
<hr>
diff --git a/templates/admin/plugins.html b/templates/admin/plugins.html
index 7bbe544..7ef50a0 100644
--- a/templates/admin/plugins.html
+++ b/templates/admin/plugins.html
@@ -28,7 +28,7 @@
<a class="text-info" href="{{ baseurl }}/disable/{{ p }}">Disable</a>
</div>
<div class="col-md-6 col-sm-6 col-xs-12">
- {%- if available[p].get_config_desc() %}
+ {%- if available[p].get_config_obj() %}
<a class="text-primary" href="{{ baseurl }}/{{ p }}">Configure</a>
{% endif %}
</div>
@@ -88,7 +88,7 @@
<a class="text-info" href="{{ baseurl }}/enable/{{ p }}">Enable</a>
</div>
<div class="col-md-6 col-sm-6 col-xs-12">
- {%- if available[p].get_config_desc() %}
+ {%- if available[p].get_config_obj() %}
<span class="text-muted">Configure</span>
{% endif %}
</div>
diff --git a/templates/admin/providers.html b/templates/admin/providers.html
index c0147d8..333b10d 100644
--- a/templates/admin/providers.html
+++ b/templates/admin/providers.html
@@ -28,7 +28,7 @@
<p class="text-info"><a href="{{ baseurl }}/disable/{{ p }}">Disable</a></p>
</div>
<div class="col-md-4 col-sm-4 col-xs-12">
- {%- if available[p].get_config_desc() %}
+ {%- if available[p].get_config_obj() %}
<p class="text-primary"><a href="{{ baseurl }}/{{ p }}">Configure</a></p>
{% endif %}
</div>
@@ -54,7 +54,7 @@
<p class="text-info"><a href="{{ baseurl }}/enable/{{ p }}">Enable</a></p>
</div>
<div class="col-md-4 col-sm-4 col-xs-12">
- {%- if available[p].get_config_desc() %}
+ {%- if available[p].get_config_obj() %}
<p class="text-muted">Configure</p>
{% endif %}
</div>