1 # Copyright (C) 2014 Ipsilon Project Contributors
3 # See the file named COPYING for the project license
5 from ipsilon.info.common import InfoProviderBase
6 from ipsilon.info.common import InfoProviderInstaller
7 from ipsilon.util.plugin import PluginObject
8 from ipsilon.util.policy import Policy
9 from ipsilon.util import config as pconfig
14 # TODO: fetch mapping from configuration
17 ['commonname', 'fullname'],
20 ['destinationindicator', 'country'],
21 ['postalcode', 'postcode'],
23 ['statetorprovincename', 'state'],
24 ['streetaddress', 'street'],
25 ['telephonenumber', 'phone'],
29 class InfoProvider(InfoProviderBase):
31 def __init__(self, *pargs):
32 super(InfoProvider, self).__init__(*pargs)
33 self.mapper = Policy(ldap_mapping)
35 self.description = """
36 Info plugin that uses LDAP to retrieve user data. """
41 'The LDAP server url.',
42 'ldap://example.com'),
45 'Template to turn username into DN.',
46 'uid=%(username)s,ou=People,dc=example,dc=com'),
49 'What TLS level show be required',
50 ['Demand', 'Allow', 'Try', 'Never', 'NoTLS'],
54 'DN to bind as, if empty uses anonymous bind.',
55 'uid=ipsilon,ou=People,dc=example,dc=com'),
58 'Password to use for bind operation'),
61 'The base dn to look for users and groups',
67 return self.get_config_value('server url')
71 return self.get_config_value('tls')
75 return self.get_config_value('bind dn')
78 def bind_password(self):
79 return self.get_config_value('bind password')
82 def user_dn_tmpl(self):
83 return self.get_config_value('user dn template')
87 return self.get_config_value('base dn')
91 tls = self.tls.lower()
94 tls_req_opt = ldap.OPT_X_TLS_NEVER
96 tls_req_opt = ldap.OPT_X_TLS_DEMAND
98 tls_req_opt = ldap.OPT_X_TLS_ALLOW
100 tls_req_opt = ldap.OPT_X_TLS_TRY
101 if tls_req_opt is not None:
102 ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, tls_req_opt)
104 conn = ldap.initialize(self.server_url)
107 if not self.server_url.startswith("ldaps"):
110 conn.simple_bind_s(self.bind_dn, self.bind_password)
114 def _get_user_data(self, conn, dn):
115 result = conn.search_s(dn, ldap.SCOPE_BASE)
116 if result is None or result == []:
117 raise Exception('User object could not be found!')
118 elif len(result) > 1:
119 raise Exception('No unique user object could be found!')
121 for name, value in result[0][1].iteritems():
122 if isinstance(value, list) and len(value) == 1:
127 def _get_user_groups(self, conn, base, username):
128 # TODO: fixme to support RFC2307bis schemas
129 results = conn.search_s(base, ldap.SCOPE_SUBTREE,
130 filterstr='memberuid=%s' % username)
131 if results is None or results == []:
132 self.debug('No groups for %s' % username)
137 groups.append(r[1]['cn'][0])
140 def get_user_data_from_conn(self, conn, dn, base, username):
143 ldapattrs = self._get_user_data(conn, dn)
144 self.debug(ldapattrs)
145 userattrs, extras = self.mapper.map_attributes(ldapattrs)
146 groups = self._get_user_groups(conn, base, username)
148 reply['_groups'] = groups
149 reply['_extras'] = {'ldap': extras}
150 except Exception, e: # pylint: disable=broad-except
155 def get_user_attrs(self, user):
157 conn = self._ldap_bind()
158 dn = self.user_dn_tmpl % {'username': user}
160 return self.get_user_data_from_conn(conn, dn, base, user)
161 except Exception, e: # pylint: disable=broad-except
166 class Installer(InfoProviderInstaller):
168 def __init__(self, *pargs):
169 super(Installer, self).__init__()
173 def install_args(self, group):
174 group.add_argument('--info-ldap', choices=['yes', 'no'], default='no',
175 help='Use LDAP to populate user attrs')
176 group.add_argument('--info-ldap-server-url', action='store',
177 help='LDAP Server Url')
178 group.add_argument('--info-ldap-bind-dn', action='store',
180 group.add_argument('--info-ldap-bind-pwd', action='store',
181 help='LDAP Bind Password')
182 group.add_argument('--info-ldap-user-dn-template', action='store',
183 help='LDAP User DN Template')
184 group.add_argument('--info-ldap-base-dn', action='store',
187 def configure(self, opts):
188 if opts['info_ldap'] != 'yes':
191 # Add configuration data to database
192 po = PluginObject(*self.pargs)
195 po.wipe_config_values()
197 if 'info_ldap_server_url' in opts:
198 config['server url'] = opts['info_ldap_server_url']
199 elif 'ldap_server_url' in opts:
200 config['server url'] = opts['ldap_server_url']
201 if 'info_ldap_bind_dn' in opts:
202 config['bind dn'] = opts['info_ldap_bind_dn']
203 if 'info_ldap_bind_pwd' in opts:
204 config['bind password'] = opts['info_ldap_bind_pwd']
205 if 'info_ldap_user_dn_template' in opts:
206 config['user dn template'] = opts['info_ldap_user_dn_template']
207 elif 'ldap_bind_dn_template' in opts:
208 config['user dn template'] = opts['ldap_bind_dn_template']
209 if 'info_ldap_tls_level' in opts and opts['info_ldap_tls_level']:
210 config['tls'] = opts['info_ldap_tls_level']
211 elif 'ldap_tls_level' in opts and opts['ldap_tls_level']:
212 config['tls'] = opts['ldap_tls_level']
214 config['tls'] = 'Demand'
215 if 'info_ldap_base_dn' in opts and opts['info_ldap_base_dn']:
216 config['base dn'] = opts['info_ldap_base_dn']
217 elif 'ldap_base_dn' in opts and opts['ldap_base_dn']:
218 config['base dn'] = opts['ldap_base_dn']
219 po.save_plugin_config(config)
221 # Update global config to add info plugin
223 po.save_enabled_state()
225 # For selinux enabled platforms permit httpd to connect to ldap,
228 subprocess.call(['/usr/sbin/setsebool', '-P',
229 'httpd_can_connect_ldap=on'])
230 except Exception: # pylint: disable=broad-except