1 # Copyright (C) 2014 Ipsilon Project Contributors
3 # See the file named COPYING for the project license
5 # Info plugin for mod_lookup_identity Apache module via SSSD
6 # http://www.adelton.com/apache/mod_lookup_identity/
8 from ipsilon.info.common import InfoProviderBase
9 from ipsilon.info.common import InfoProviderInstaller
10 from ipsilon.info.common import InfoMapping
11 from ipsilon.util.plugin import PluginObject
12 from string import Template
18 SSSD_CONF = '/etc/sssd/sssd.conf'
20 # LDAP attributes to tell SSSD to fetch over the InfoPipe
29 # Map the mod_lookup_identity env variables to Ipsilon. The inverse of
30 # this is in the httpd template.
32 'REMOTE_USER_GECOS': 'fullname',
33 'REMOTE_USER_EMAIL': 'email',
34 'REMOTE_USER_FIRSTNAME': 'givenname',
35 'REMOTE_USER_LASTNAME': 'surname',
36 'REMOTE_USER_STREET': 'street',
37 'REMOTE_USER_STATE': 'state',
38 'REMOTE_USER_POSTALCODE': 'postcode',
39 'REMOTE_USER_TELEPHONENUMBER': 'phone',
43 class InfoProvider(InfoProviderBase):
45 def __init__(self, *pargs):
46 super(InfoProvider, self).__init__(*pargs)
47 self.mapper = InfoMapping()
48 self.mapper.set_mapping(sssd_mapping)
50 self.new_config(self.name)
52 def _get_user_data(self, user):
55 expectgroups = int(cherrypy.request.wsgi_environ.get(
56 'REMOTE_USER_GROUP_N', 0))
57 for key in cherrypy.request.wsgi_environ:
58 if key.startswith('REMOTE_USER_'):
59 if key == 'REMOTE_USER_GROUP_N':
61 if key.startswith('REMOTE_USER_GROUP_'):
62 groups.append(cherrypy.request.wsgi_environ[key])
64 reply[key] = cherrypy.request.wsgi_environ[key]
65 if len(groups) != expectgroups:
66 self.error('Number of groups expected was not found. Expected'
67 ' %d got %d' % (expectgroups, len(groups)))
70 def get_user_attrs(self, user):
73 attrs, groups = self._get_user_data(user)
74 userattrs, extras = self.mapper.map_attrs(attrs)
76 reply['_groups'] = groups
77 reply['_extras'] = {'sssd': extras}
86 LoadModule lookup_identity_module modules/mod_lookup_identity.so
88 <Location /${instance}>
89 LookupUserAttr sn REMOTE_USER_LASTNAME
90 LookupUserAttr locality REMOTE_USER_STATE
91 LookupUserAttr street REMOTE_USER_STREET
92 LookupUserAttr telephoneNumber REMOTE_USER_TELEPHONENUMBER
93 LookupUserAttr givenname REMOTE_USER_FIRSTNAME
94 LookupUserAttr mail REMOTE_USER_EMAIL
95 LookupUserAttr postalCode REMOTE_USER_POSTALCODE
96 LookupUserGroupsIter REMOTE_USER_GROUP
101 class Installer(InfoProviderInstaller):
103 def __init__(self, *pargs):
104 super(Installer, self).__init__()
108 def install_args(self, group):
109 group.add_argument('--info-sssd', choices=['yes', 'no'],
111 help='Use mod_lookup_identity and SSSD to populate'
113 group.add_argument('--info-sssd-domain', action='store',
114 help='SSSD domain to enable mod_lookup_identity'
117 def configure(self, opts):
118 if opts['info_sssd'] != 'yes':
121 if not opts['info_sssd_domain']:
122 print 'info-identity-domain is required'
125 confopts = {'instance': opts['instance']}
127 tmpl = Template(CONF_TEMPLATE)
128 hunk = tmpl.substitute(**confopts) # pylint: disable=star-args
129 with open(opts['httpd_conf'], 'a') as httpd_conf:
130 httpd_conf.write(hunk)
133 sssdconfig = SSSDConfig.SSSDConfig()
134 sssdconfig.import_config()
135 except Exception as e: # pylint: disable=broad-except
136 # Unable to read existing SSSD config so it is probably not
138 print 'Loading SSSD config failed: %s' % e
142 domain = sssdconfig.get_domain(opts['info_sssd_domain'])
143 except SSSDConfig.NoDomainError:
144 print 'No domain %s' % opts['info_sssd_domain']
147 domain.set_option('ldap_user_extra_attrs', ', '.join(SSSD_ATTRS))
150 sssdconfig.new_service('ifp')
151 except SSSDConfig.ServiceAlreadyExists:
154 sssdconfig.activate_service('ifp')
156 ifp = sssdconfig.get_service('ifp')
157 ifp.set_option('allowed_uids', 'apache, root')
158 ifp.set_option('user_attributes', '+' + ', +'.join(SSSD_ATTRS))
160 sssdconfig.save_service(ifp)
161 sssdconfig.save_domain(domain)
162 sssdconfig.write(SSSD_CONF)
165 subprocess.call(['/sbin/service', 'sssd', 'restart'])
166 except Exception: # pylint: disable=broad-except
169 # Give SSSD a chance to restart
172 # Add configuration data to database
173 po = PluginObject(*self.pargs)
176 po.wipe_config_values()
178 # Update global config to add info plugin
180 po.save_enabled_state()