3 # Copyright (C) 2014 Ipsilon Project Contributors
5 # See the file named COPYING for the project license
7 from ipsilon.info.common import InfoProviderBase
8 from ipsilon.info.common import InfoProviderInstaller
9 from ipsilon.info.common import InfoMapping
10 from ipsilon.util.plugin import PluginObject
11 from ipsilon.util.log import Log
15 # TODO: fetch mapping from configuration
18 'commonname': 'fullname',
21 'destinationindicator': 'country',
22 'postalcode': 'postcode',
24 'statetorprovincename': 'state',
25 'streetaddress': 'street',
26 'telephonenumber': 'phone',
30 class InfoProvider(InfoProviderBase, Log):
33 super(InfoProvider, self).__init__()
34 self.mapper = InfoMapping()
35 self.mapper.set_mapping(ldap_mapping)
37 self.description = """
38 Info plugin that uses LDAP to retrieve user data. """
41 """ The LDAP server url """,
46 " What TLS level show be required " +
47 "(Demand, Allow, Try, Never, NoTLS) ",
52 """ User DN to bind as, if empty uses anonymous bind. """,
54 'uid=ipsilon,ou=People,dc=example,dc=com'
57 """ Password to use for bind operation """,
62 """ Template to turn username into DN. """,
64 'uid=%(username)s,ou=People,dc=example,dc=com'
70 return self.get_config_value('server url')
74 return self.get_config_value('tls')
78 return self.get_config_value('bind dn')
81 def bind_password(self):
82 return self.get_config_value('bind password')
85 def user_dn_tmpl(self):
86 return self.get_config_value('user dn template')
90 tls = self.tls.lower()
93 tls_req_opt = ldap.OPT_X_TLS_NEVER
95 tls_req_opt = ldap.OPT_X_TLS_DEMAND
97 tls_req_opt = ldap.OPT_X_TLS_ALLOW
99 tls_req_opt = ldap.OPT_X_TLS_TRY
100 if tls_req_opt is not None:
101 ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, tls_req_opt)
103 conn = ldap.initialize(self.server_url)
106 if not self.server_url.startswith("ldaps"):
109 conn.simple_bind_s(self.bind_dn, self.bind_password)
113 def _get_user_data(self, conn, dn):
114 result = conn.search_s(dn, ldap.SCOPE_BASE)
115 if result is None or result == []:
116 raise Exception('User object could not be found!')
117 elif len(result) > 1:
118 raise Exception('No unique user object could be found!')
120 for name, value in result[0][1].iteritems():
121 if type(value) is list and len(value) == 1:
126 def _get_user_groups(self, conn, dn, ldapattrs):
127 # TODO: fixme to support RFC2307bis schemas
128 if 'memberuid' in ldapattrs:
129 return ldapattrs['memberuid']
133 def get_user_data_from_conn(self, conn, dn):
136 ldapattrs = self._get_user_data(conn, dn)
137 userattrs, extras = self.mapper.map_attrs(ldapattrs)
138 groups = self._get_user_groups(conn, dn, ldapattrs)
139 reply['userdata'] = userattrs
140 reply['groups'] = groups
141 reply['extras'] = {'ldap': extras}
142 except Exception, e: # pylint: disable=broad-except
147 def get_user_attrs(self, user):
149 conn = self._ldap_bind()
150 dn = self.user_dn_tmpl % {'username': user}
151 return self.get_user_data_from_conn(conn, dn)
152 except Exception, e: # pylint: disable=broad-except
157 class Installer(InfoProviderInstaller):
160 super(Installer, self).__init__()
163 def install_args(self, group):
164 group.add_argument('--info-ldap', choices=['yes', 'no'], default='no',
165 help='Use LDAP to populate user attrs')
166 group.add_argument('--info-ldap-server-url', action='store',
167 help='LDAP Server Url')
168 group.add_argument('--info-ldap-bind-dn', action='store',
170 group.add_argument('--info-ldap-bind-pwd', action='store',
171 help='LDAP Bind Password')
172 group.add_argument('--info-ldap-user-dn-template', action='store',
173 help='LDAP User DN Template')
175 def configure(self, opts):
176 if opts['info_ldap'] != 'yes':
179 # Add configuration data to database
183 po.wipe_config_values(self.facility)
185 if 'info_ldap_server_url' in opts:
186 config['server url'] = opts['info_ldap_server_url']
187 elif 'ldap_server_url' in opts:
188 config['server url'] = opts['ldap_server_url']
189 config = {'bind dn': opts['info_ldap_bind_dn']}
190 config = {'bind password': opts['info_ldap_bind_pwd']}
191 config = {'user dn template': opts['info_ldap_user_dn_template']}
192 if 'info_ldap_bind_dn' in opts:
193 config['bind dn'] = opts['info_ldap_bind_dn']
194 if 'info_ldap_bind_pwd' in opts:
195 config['bind password'] = opts['info_ldap_bind_pwd']
196 if 'info_ldap_user_dn_template' in opts:
197 config['user dn template'] = opts['info_ldap_user_dn_template']
198 elif 'ldap_bind_dn_template' in opts:
199 config['user dn template'] = opts['ldap_bind_dn_template']
200 config['tls'] = 'Demand'
201 po.set_config(config)
202 po.save_plugin_config(self.facility)
204 # Replace global config, only one plugin info can be used
206 globalconf = po.get_plugin_config(self.facility)
207 if 'order' in globalconf:
208 order = globalconf['order'].split(',')
212 globalconf['order'] = ','.join(order)
213 po.set_config(globalconf)
214 po.save_plugin_config(self.facility)