From 0e53d1f95b3bb04f82f9ceefde1ac17484986e4d Mon Sep 17 00:00:00 2001 From: Rob Crittenden Date: Thu, 12 Feb 2015 11:49:20 -0500 Subject: Add info plugin that utilizes Apache mod_lookup_identity plugin mod_look_identity looks up identity information from sssd over dbus, making additional identity attributes available. https://fedorahosted.org/ipsilon/ticket/31 Signed-off-by: Rob Crittenden Reviewed-by: Simo Sorce --- contrib/fedora/ipsilon.spec | 20 ++++- ipsilon/info/infosssd.py | 180 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 199 insertions(+), 1 deletion(-) create mode 100644 ipsilon/info/infosssd.py diff --git a/contrib/fedora/ipsilon.spec b/contrib/fedora/ipsilon.spec index 4b17aa7..3e0343b 100644 --- a/contrib/fedora/ipsilon.spec +++ b/contrib/fedora/ipsilon.spec @@ -2,7 +2,7 @@ Name: ipsilon Version: 0.3.0 -Release: 5%{?dist} +Release: 6%{?dist} Summary: An Identity Provider Server Group: System Environment/Base @@ -159,6 +159,19 @@ BuildArch: noarch %description authldap Provides a login plugin to allow authentication and info retrieval via LDAP. +%package infosssd +Summary: SSSD & mod_lookup_identity-based identity plugin +Group: System Environment/Base +License: GPLv3+ +Requires: %{name} = %{version}-%{release} +Requires: mod_lookup_identity +Requires: libsss_simpleifp +Requires; sssd >= 1.12.4 +BuildArch: noarch + +%description infosssd +Provides an info plugin to allow retrieval via mod_lookup_identity and +SSSD. %prep %setup -q @@ -287,8 +300,13 @@ fi %{python2_sitelib}/ipsilon/login/authldap* %{python2_sitelib}/ipsilon/info/infoldap* +%files infosssd +%{python2_sitelib}/ipsilon/info/infosssd.* %changelog +* Thu Feb 12 2015 Rob Crittenden - 0.3.0-6 +- Add mod_identity_lookup info plugin package + * Wed Jan 28 2015 Patrick Uiterwijk - 0.3.0-5 - Split IPA tools diff --git a/ipsilon/info/infosssd.py b/ipsilon/info/infosssd.py new file mode 100644 index 0000000..b187567 --- /dev/null +++ b/ipsilon/info/infosssd.py @@ -0,0 +1,180 @@ +# Copyright (C) 2014 Ipsilon Project Contributors +# +# See the file named COPYING for the project license + +# Info plugin for mod_lookup_identity Apache module via SSSD +# http://www.adelton.com/apache/mod_lookup_identity/ + +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 string import Template +import cherrypy +import time +import subprocess +import SSSDConfig + +SSSD_CONF = '/etc/sssd/sssd.conf' + +# LDAP attributes to tell SSSD to fetch over the InfoPipe +SSSD_ATTRS = ['mail', + 'street', + 'locality', + 'postalCode', + 'telephoneNumber', + 'givenname', + 'sn'] + +# Map the mod_lookup_identity env variables to Ipsilon. The inverse of +# this is in the httpd template. +sssd_mapping = { + 'REMOTE_USER_GECOS': 'fullname', + 'REMOTE_USER_EMAIL': 'email', + 'REMOTE_USER_FIRSTNAME': 'givenname', + 'REMOTE_USER_LASTNAME': 'surname', + 'REMOTE_USER_STREET': 'street', + 'REMOTE_USER_STATE': 'state', + 'REMOTE_USER_POSTALCODE': 'postcode', + 'REMOTE_USER_TELEPHONENUMBER': 'phone', +} + + +class InfoProvider(InfoProviderBase): + + def __init__(self, *pargs): + super(InfoProvider, self).__init__(*pargs) + self.mapper = InfoMapping() + self.mapper.set_mapping(sssd_mapping) + self.name = 'sssd' + self.new_config(self.name) + + def _get_user_data(self, user): + reply = dict() + groups = [] + expectgroups = int(cherrypy.request.wsgi_environ.get( + 'REMOTE_USER_GROUP_N', 0)) + for key in cherrypy.request.wsgi_environ: + if key.startswith('REMOTE_USER_'): + if key == 'REMOTE_USER_GROUP_N': + continue + if key.startswith('REMOTE_USER_GROUP_'): + groups.append(cherrypy.request.wsgi_environ[key]) + else: + reply[key] = cherrypy.request.wsgi_environ[key] + if len(groups) != expectgroups: + self.error('Number of groups expected was not found. Expected' + ' %d got %d' % (expectgroups, len(groups))) + return reply, groups + + def get_user_attrs(self, user): + reply = dict() + try: + attrs, groups = self._get_user_data(user) + userattrs, extras = self.mapper.map_attrs(attrs) + reply['userdata'] = userattrs + reply['groups'] = groups + reply['extras'] = {'sssd': extras} + + except KeyError: + pass + + return reply + + +CONF_TEMPLATE = """ +LoadModule lookup_identity_module modules/mod_lookup_identity.so + + + LookupUserAttr sn REMOTE_USER_LASTNAME + LookupUserAttr locality REMOTE_USER_STATE + LookupUserAttr street REMOTE_USER_STREET + LookupUserAttr telephoneNumber REMOTE_USER_TELEPHONENUMBER + LookupUserAttr givenname REMOTE_USER_FIRSTNAME + LookupUserAttr mail REMOTE_USER_EMAIL + LookupUserAttr postalCode REMOTE_USER_POSTALCODE + LookupUserGroupsIter REMOTE_USER_GROUP + +""" + + +class Installer(InfoProviderInstaller): + + def __init__(self, *pargs): + super(Installer, self).__init__() + self.name = 'sssd' + self.pargs = pargs + + def install_args(self, group): + group.add_argument('--info-sssd', choices=['yes', 'no'], + default='no', + help='Use mod_lookup_identity and SSSD to populate' + ' user attrs') + group.add_argument('--info-sssd-domain', action='store', + help='SSSD domain to enable mod_lookup_identity' + ' for') + + def configure(self, opts): + if opts['info_sssd'] != 'yes': + return + + if not opts['info_sssd_domain']: + print 'info-identity-domain is required' + return False + + confopts = {'instance': opts['instance']} + + tmpl = Template(CONF_TEMPLATE) + hunk = tmpl.substitute(**confopts) # pylint: disable=star-args + with open(opts['httpd_conf'], 'a') as httpd_conf: + httpd_conf.write(hunk) + + try: + sssdconfig = SSSDConfig.SSSDConfig() + sssdconfig.import_config() + except Exception as e: # pylint: disable=broad-except + # Unable to read existing SSSD config so it is probably not + # configured. + print 'Loading SSSD config failed: %s' % e + return False + + try: + domain = sssdconfig.get_domain(opts['info_sssd_domain']) + except SSSDConfig.NoDomainError: + print 'No domain %s' % opts['info_sssd_domain'] + return False + + domain.set_option('ldap_user_extra_attrs', ', '.join(SSSD_ATTRS)) + + try: + sssdconfig.new_service('ifp') + except SSSDConfig.ServiceAlreadyExists: + pass + + sssdconfig.activate_service('ifp') + + ifp = sssdconfig.get_service('ifp') + ifp.set_option('allowed_uids', 'apache, root') + ifp.set_option('user_attributes', '+' + ', +'.join(SSSD_ATTRS)) + + sssdconfig.save_service(ifp) + sssdconfig.save_domain(domain) + sssdconfig.write(SSSD_CONF) + + try: + subprocess.call(['/sbin/service', 'sssd', 'restart']) + except Exception: # pylint: disable=broad-except + pass + + # Give SSSD a chance to restart + time.sleep(5) + + # Add configuration data to database + po = PluginObject(*self.pargs) + po.name = 'sssd' + po.wipe_data() + po.wipe_config_values() + + # Update global config to add info plugin + po.is_enabled = True + po.save_enabled_state() -- cgit