X-Git-Url: http://git.cascardo.info/?p=cascardo%2Fipsilon.git;a=blobdiff_plain;f=ipsilon%2Fproviders%2Fsaml2%2Fadmin.py;h=158e590c79ed2f97fa83242ca25a332b380afc0f;hp=7e143128286ac82c0e6185f7c7f90eb624454282;hb=aa5dc3b417db962a075a092d0d3528010c1059f7;hpb=3f7e6358c02d0822c5fe1c2da72a3b32ffe12ec6 diff --git a/ipsilon/providers/saml2/admin.py b/ipsilon/providers/saml2/admin.py old mode 100755 new mode 100644 index 7e14312..158e590 --- a/ipsilon/providers/saml2/admin.py +++ b/ipsilon/providers/saml2/admin.py @@ -1,5 +1,3 @@ -#!/usr/bin/python -# # Copyright (C) 2014 Simo Sorce # # see file 'COPYING' for use and warranty information @@ -18,24 +16,28 @@ # along with this program. If not, see . import cherrypy -from ipsilon.util.page import Page +from ipsilon.util import config as pconfig +from ipsilon.admin.common import AdminPage +from ipsilon.admin.common import ADMIN_STATUS_OK +from ipsilon.admin.common import ADMIN_STATUS_ERROR +from ipsilon.admin.common import ADMIN_STATUS_WARN +from ipsilon.admin.common import get_mapping_list_value +from ipsilon.admin.common import get_complex_list_value from ipsilon.providers.saml2.provider import ServiceProvider from ipsilon.providers.saml2.provider import ServiceProviderCreator from ipsilon.providers.saml2.provider import InvalidProviderId -import re +from copy import deepcopy import requests +import logging -VALID_IN_NAME = r'[^\ a-zA-Z0-9]' - - -class NewSPAdminPage(Page): +class NewSPAdminPage(AdminPage): def __init__(self, site, parent): super(NewSPAdminPage, self).__init__(site, form=True) self.parent = parent self.title = 'New Service Provider' - self.backurl = parent.url + self.back = parent.url self.url = '%s/new' % (parent.url,) def form_new(self, message=None, message_type=None): @@ -44,7 +46,7 @@ class NewSPAdminPage(Page): message=message, message_type=message_type, name='saml2_sp_new_form', - backurl=self.backurl, action=self.url) + back=self.back, action=self.url) def GET(self, *args, **kwargs): return self.form_new() @@ -57,22 +59,16 @@ class NewSPAdminPage(Page): name = None meta = None if 'content-type' not in cherrypy.request.headers: - self._debug("Invalid request, missing content-type") + self.debug("Invalid request, missing content-type") message = "Malformed request" - message_type = "error" + message_type = ADMIN_STATUS_ERROR return self.form_new(message, message_type) ctype = cherrypy.request.headers['content-type'].split(';')[0] if ctype != 'multipart/form-data': - self._debug("Invalid form type (%s), trying to cope" % ( - cherrypy.request.content_type,)) + self.debug("Invalid form type (%s), trying to cope" % ( + cherrypy.request.content_type,)) for key, value in kwargs.iteritems(): if key == 'name': - if re.search(VALID_IN_NAME, value): - message = "Invalid name!" \ - " Use only numbers and letters" - message_type = "error" - return self.form_new(message, message_type) - name = value elif key == 'metatext': if len(value) > 0: @@ -81,7 +77,7 @@ class NewSPAdminPage(Page): if hasattr(value, 'content_type'): meta = value.fullvalue() else: - self._debug("Invalid format for 'meta'") + self.debug("Invalid format for 'meta'") elif key == 'metaurl': if len(value) > 0: try: @@ -89,9 +85,9 @@ class NewSPAdminPage(Page): r.raise_for_status() meta = r.content except Exception, e: # pylint: disable=broad-except - self._debug("Failed to fetch metadata: " + repr(e)) + self.debug("Failed to fetch metadata: " + repr(e)) message = "Failed to fetch metadata: " + repr(e) - message_type = "error" + message_type = ADMIN_STATUS_ERROR return self.form_new(message, message_type) if name and meta: @@ -100,21 +96,21 @@ class NewSPAdminPage(Page): sp = spc.create_from_buffer(name, meta) sp_page = self.parent.add_sp(name, sp) message = "SP Successfully added" - message_type = "success" - return sp_page.form_standard(message, message_type) + message_type = ADMIN_STATUS_OK + return sp_page.root_with_msg(message, message_type) except InvalidProviderId, e: message = str(e) - message_type = "error" + message_type = ADMIN_STATUS_ERROR except Exception, e: # pylint: disable=broad-except - self._debug(repr(e)) + self.debug(repr(e)) message = "Failed to create Service Provider!" - message_type = "error" + message_type = ADMIN_STATUS_ERROR else: message = "A name and a metadata file must be provided" - message_type = "error" + message_type = ADMIN_STATUS_ERROR else: message = "Unauthorized" - message_type = "error" + message_type = ADMIN_STATUS_ERROR return self.form_new(message, message_type) @@ -127,148 +123,149 @@ class UnauthorizedUser(Exception): pass -class SPAdminPage(Page): +class SPAdminPage(AdminPage): def __init__(self, sp, site, parent): super(SPAdminPage, self).__init__(site, form=True) self.parent = parent self.sp = sp self.title = sp.name - self.backurl = parent.url self.url = '%s/sp/%s' % (parent.url, sp.name) + self.menu = [parent] + self.back = parent.url - def form_standard(self, message=None, message_type=None, newurl=None): - return self._template('admin/providers/saml2_sp.html', - message=message, - message_type=message_type, - title=self.title, - name='saml2_sp_%s_form' % self.sp.name, - backurl=self.backurl, action=self.url, - data=self.sp, newurl=newurl) + def root_with_msg(self, message=None, message_type=None): + return self._template('admin/option_config.html', title=self.title, + menu=self.menu, action=self.url, back=self.back, + message=message, message_type=message_type, + name='saml2_sp_%s_form' % (self.sp.name), + config=self.sp.get_config_obj()) def GET(self, *args, **kwargs): - return self.form_standard() - - def change_name(self, key, value): - - if value == self.sp.name: - return False - - if self.user.is_admin or self.user.name == self.sp.owner: - if re.search(VALID_IN_NAME, value): - err = "Invalid name! Use only numbers and letters" - raise InvalidValueFormat(err) - - self._debug("Replacing %s: %s -> %s" % (key, self.sp.name, value)) - return {'name': value, 'rename': [self.sp.name, value]} - else: - raise UnauthorizedUser("Unauthorized to rename Service Provider") - - def change_owner(self, key, value): - if value == self.sp.owner: - return False - - if self.user.is_admin: - self._debug("Replacing %s: %s -> %s" % (key, self.sp.owner, value)) - return {'owner': value} - else: - raise UnauthorizedUser("Unauthorized to set owner value") - - def change_default_nameid(self, key, value): - if value == self.sp.default_nameid: - return False - - if self.user.is_admin: - self._debug("Replacing %s: %s -> %s" % (key, - self.sp.default_nameid, - value)) - if not self.sp.is_valid_nameid(value): - raise InvalidValueFormat('Invalid default nameid value') - return {'default_nameid': value} - else: - raise UnauthorizedUser("Unauthorized to set default nameid value") - - def change_allowed_nameids(self, key, value): - v = set([x.strip() for x in value.split(',')]) - if v == set(self.sp.allowed_nameids): - return False - - if self.user.is_admin: - self._debug("Replacing %s: %s -> %s" % (key, - self.sp.allowed_nameids, - list(v))) - for x in v: - if not self.sp.is_valid_nameid(x): - l = ', '.join(self.sp.valid_nameids()) - err = 'Invalid nameid [%s]. Available [%s].' % (x, l) - raise InvalidValueFormat(err) - return {'allowed_nameids': list(v)} - else: - raise UnauthorizedUser("Unauthorized to set alowed nameids values") + return self.root_with_msg() def POST(self, *args, **kwargs): message = "Nothing was modified." message_type = "info" - results = dict() + new_db_values = dict() - try: - for key, value in kwargs.iteritems(): - if key == 'name': - r = self.change_name(key, value) - if r: - results.update(r) - elif key == 'owner': - r = self.change_owner(key, value) - if r: - results.update(r) - - elif key == 'default_nameid': - r = self.change_default_nameid(key, value) - if r: - results.update(r) - - elif key == 'allowed_nameids': - r = self.change_allowed_nameids(key, value) - if r: - results.update(r) - - except InvalidValueFormat, e: - message = str(e) - message_type = "warning" - return self.form_standard(message, message_type) - except UnauthorizedUser, e: - message = str(e) - message_type = "error" - return self.form_standard(message, message_type) - except Exception, e: # pylint: disable=broad-except - self._debug("Error: %s" % repr(e)) - message = "Internal Error" - message_type = "error" - return self.form_standard(message, message_type) + conf = self.sp.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) + elif type(option) is pconfig.ComplexList: + current = deepcopy(option.get_value()) + value = get_complex_list_value(name, + current, + **kwargs) + # if current value is None do nothing + if value is None: + if option.get_value() is None: + continue + # else pass and let it continue as None + elif type(option) is pconfig.MappingList: + current = deepcopy(option.get_value()) + value = get_mapping_list_value(name, + current, + **kwargs) + # if current value is None do nothing + if value is None: + if option.get_value() is None: + continue + # else pass and let it continue as None + else: + continue + + if value != option.get_value(): + if (type(option) is pconfig.List and + set(value) == set(option.get_value())): + continue + cherrypy.log.error("Storing %s = %s" % + (name, value), severity=logging.DEBUG) + new_db_values[name] = value + + if len(new_db_values) != 0: + try: + # Validate user can make these changes + for (key, value) in new_db_values.iteritems(): + if key == 'Name': + if (not self.user.is_admin and + self.user.name != self.sp.owner): + raise UnauthorizedUser("Unauthorized to set owner") + elif key in ['Owner', 'Default NameID', 'Allowed NameIDs', + 'Attribute Mapping', 'Allowed Attributes']: + if not self.user.is_admin: + raise UnauthorizedUser( + "Unauthorized to set %s" % key + ) + + # Make changes in current config + for name, option in conf.iteritems(): + value = new_db_values.get(name, False) + # A value of None means remove from the data store + if value is False or value == []: + continue + if name == 'Name': + if not self.sp.is_valid_name(value): + raise InvalidValueFormat( + 'Invalid name! Use only numbers and' + ' letters' + ) + self.sp.name = value + self.url = '%s/sp/%s' % (self.parent.url, value) + self.parent.rename_sp(option.get_value(), value) + elif name == 'User Owner': + self.sp.owner = value + elif name == 'Default NameID': + self.sp.default_nameid = value + elif name == 'Allowed NameIDs': + self.sp.allowed_nameids = value + elif name == 'Attribute Mapping': + self.sp.attribute_mappings = value + elif name == 'Allowed Attributes': + self.sp.allowed_attributes = value + except InvalidValueFormat, e: + message = str(e) + message_type = ADMIN_STATUS_WARN + return self.root_with_msg(message, message_type) + except UnauthorizedUser, e: + message = str(e) + message_type = ADMIN_STATUS_ERROR + return self.root_with_msg(message, message_type) + except Exception as e: # pylint: disable=broad-except + self.debug("Error: %s" % repr(e)) + message = "Internal Error" + message_type = ADMIN_STATUS_ERROR + return self.root_with_msg(message, message_type) - if len(results) > 0: try: - if 'name' in results: - self.sp.name = results['name'] - if 'owner' in results: - self.sp.owner = results['owner'] - if 'default_nameid' in results: - self.sp.default_nameid = results['default_nameid'] - if 'allowed_nameids' in results: - self.sp.allowed_nameids = results['allowed_nameids'] self.sp.save_properties() - if 'rename' in results: - rename = results['rename'] - self.url = '%s/sp/%s' % (self.parent.url, rename[1]) - self.parent.rename_sp(rename[0], rename[1]) message = "Properties successfully changed" - message_type = "success" - except Exception: # pylint: disable=broad-except + message_type = ADMIN_STATUS_OK + except Exception as e: # pylint: disable=broad-except + self.error('Failed to save data: %s' % e) message = "Failed to save data!" - message_type = "error" + message_type = ADMIN_STATUS_ERROR + else: + self.sp.refresh_config() - return self.form_standard(message, message_type, self.url) + return self.root_with_msg(message=message, + message_type=message_type) def delete(self): self.parent.del_sp(self.sp.name) @@ -277,15 +274,15 @@ class SPAdminPage(Page): delete.public_function = True -class AdminPage(Page): +class Saml2AdminPage(AdminPage): def __init__(self, site, config): - super(AdminPage, self).__init__(site) + super(Saml2AdminPage, self).__init__(site) self.name = 'admin' self.cfg = config self.providers = [] self.menu = [] self.url = None - self.sp = Page(self._site) + self.sp = AdminPage(self._site) def add_sp(self, name, sp): page = SPAdminPage(sp, self._site, self) @@ -304,7 +301,7 @@ class AdminPage(Page): self.providers.remove(page.sp) self.sp.del_subtree(name) except Exception, e: # pylint: disable=broad-except - self._debug("Failed to remove provider %s: %s" % (name, str(e))) + self.debug("Failed to remove provider %s: %s" % (name, str(e))) def add_sps(self): if self.cfg.idp: @@ -314,7 +311,7 @@ class AdminPage(Page): self.del_sp(sp.name) self.add_sp(sp.name, sp) except Exception, e: # pylint: disable=broad-except - self._debug("Failed to find provider %s: %s" % (p, str(e))) + self.debug("Failed to find provider %s: %s" % (p, str(e))) def mount(self, page): self.menu = page.menu