6d710bd6606c8010f4da39060a0ed2bd9e4288e0
[cascardo/ipsilon.git] / ipsilon / info / infoldap.py
1 #!/usr/bin/python
2 #
3 # Copyright (C) 2014 Ipsilon Project Contributors
4 #
5 # See the file named COPYING for the project license
6
7 from ipsilon.info.common import InfoProviderBase
8 from ipsilon.info.common import InfoProviderInstaller
9 from ipsilon.util.plugin import PluginObject
10 from ipsilon.util.log import Log
11 import ldap
12
13
14 class InfoProvider(InfoProviderBase, Log):
15
16     def __init__(self):
17         super(InfoProvider, self).__init__()
18         self.name = 'ldap'
19         self.description = """
20 Info plugin that uses LDAP to retrieve user data. """
21         self._options = {
22             'server url': [
23                 """ The LDAP server url """,
24                 'string',
25                 'ldap://example.com'
26             ],
27             'tls': [
28                 " What TLS level show be required " +
29                 "(Demand, Allow, Try, Never, NoTLS) ",
30                 'string',
31                 'Demand'
32             ],
33             'bind dn': [
34                 """ User DN to bind as, if empty uses anonymous bind. """,
35                 'string',
36                 'uid=ipsilon,ou=People,dc=example,dc=com'
37             ],
38             'bind password': [
39                 """ Password to use for bind operation """,
40                 'string',
41                 'Password'
42             ],
43             'user dn template': [
44                 """ Template to turn username into DN. """,
45                 'string',
46                 'uid=%(username)s,ou=People,dc=example,dc=com'
47             ],
48         }
49
50     @property
51     def server_url(self):
52         return self.get_config_value('server url')
53
54     @property
55     def tls(self):
56         return self.get_config_value('tls')
57
58     @property
59     def bind_dn(self):
60         return self.get_config_value('bind dn')
61
62     @property
63     def bind_password(self):
64         return self.get_config_value('bind password')
65
66     @property
67     def user_dn_tmpl(self):
68         return self.get_config_value('user dn template')
69
70     def _ldap_bind(self):
71
72         tls = self.tls.lower()
73         tls_req_opt = None
74         if tls == "never":
75             tls_req_opt = ldap.OPT_X_TLS_NEVER
76         elif tls == "demand":
77             tls_req_opt = ldap.OPT_X_TLS_DEMAND
78         elif tls == "allow":
79             tls_req_opt = ldap.OPT_X_TLS_ALLOW
80         elif tls == "try":
81             tls_req_opt = ldap.OPT_X_TLS_TRY
82         if tls_req_opt is not None:
83             ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, tls_req_opt)
84
85         conn = ldap.initialize(self.server_url)
86
87         if tls != "notls":
88             if not self.server_url.startswith("ldaps"):
89                 conn.start_tls_s()
90
91         conn.simple_bind_s(self.bind_dn, self.bind_password)
92
93         return conn
94
95     def get_user_data_from_conn(self, conn, dn):
96         result = conn.search_s(dn, ldap.SCOPE_BASE)
97         if result is None or result == []:
98             raise Exception('User object could not be found!')
99         elif len(result) > 1:
100             raise Exception('No unique user object could be found!')
101         return result[0][1]
102
103     def get_user_attrs(self, user):
104         userattrs = None
105         try:
106             conn = self._ldap_bind()
107             dn = self.user_dn_tmpl % {'username': user}
108             userattrs = self.get_user_data_from_conn(conn, dn)
109         except Exception, e:  # pylint: disable=broad-except
110             self.error(e)
111
112         return userattrs
113
114
115 class Installer(InfoProviderInstaller):
116
117     def __init__(self):
118         super(Installer, self).__init__()
119         self.name = 'nss'
120
121     def install_args(self, group):
122         group.add_argument('--info-ldap', choices=['yes', 'no'], default='no',
123                            help='Use LDAP to populate user attrs')
124         group.add_argument('--info-ldap-server-url', action='store',
125                            help='LDAP Server Url')
126         group.add_argument('--info-ldap-bind-dn', action='store',
127                            help='LDAP Bind DN')
128         group.add_argument('--info-ldap-bind-pwd', action='store',
129                            help='LDAP Bind Password')
130         group.add_argument('--info-ldap-user-dn-template', action='store',
131                            help='LDAP User DN Template')
132
133     def configure(self, opts):
134         if opts['info_ldap'] != 'yes':
135             return
136
137         # Add configuration data to database
138         po = PluginObject()
139         po.name = 'ldap'
140         po.wipe_data()
141         po.wipe_config_values(self.facility)
142         config = dict()
143         if 'info_ldap_server_url' in opts:
144             config['server url'] = opts['info_ldap_server_url']
145         elif 'ldap_server_url' in opts:
146             config['server url'] = opts['ldap_server_url']
147         config = {'bind dn': opts['info_ldap_bind_dn']}
148         config = {'bind password': opts['info_ldap_bind_pwd']}
149         config = {'user dn template': opts['info_ldap_user_dn_template']}
150         if 'info_ldap_bind_dn' in opts:
151             config['bind dn'] = opts['info_ldap_bind_dn']
152         if 'info_ldap_bind_pwd' in opts:
153             config['bind password'] = opts['info_ldap_bind_pwd']
154         if 'info_ldap_user_dn_template' in opts:
155             config['user dn template'] = opts['info_ldap_user_dn_template']
156         elif 'ldap_bind_dn_template' in opts:
157             config['user dn template'] = opts['ldap_bind_dn_template']
158         config['tls'] = 'Demand'
159         po.set_config(config)
160         po.save_plugin_config(self.facility)
161
162         # Replace global config, only one plugin info can be used
163         po.name = 'global'
164         globalconf = po.get_plugin_config(self.facility)
165         if 'order' in globalconf:
166             order = globalconf['order'].split(',')
167         else:
168             order = []
169         order.append('ldap')
170         globalconf['order'] = ','.join(order)
171         po.set_config(globalconf)
172         po.save_plugin_config(self.facility)