Fix some copy-paste errors in help output
[cascardo/ipsilon.git] / ipsilon / login / authldap.py
1 # Copyright (C) 2014  Ipsilon Contributors, see COPYING for license
2
3 from ipsilon.login.common import LoginFormBase, LoginManagerBase
4 from ipsilon.util.plugin import PluginObject
5 from ipsilon.util.log import Log
6 from ipsilon.util import config as pconfig
7 from ipsilon.info.infoldap import InfoProvider as LDAPInfo
8 import ldap
9
10
11 class LDAP(LoginFormBase, Log):
12
13     def __init__(self, site, mgr, page):
14         super(LDAP, self).__init__(site, mgr, page)
15         self.ldap_info = None
16
17     def _ldap_connect(self):
18
19         tls = self.lm.tls.lower()
20         tls_req_opt = None
21         if tls == "never":
22             tls_req_opt = ldap.OPT_X_TLS_NEVER
23         elif tls == "demand":
24             tls_req_opt = ldap.OPT_X_TLS_DEMAND
25         elif tls == "allow":
26             tls_req_opt = ldap.OPT_X_TLS_ALLOW
27         elif tls == "try":
28             tls_req_opt = ldap.OPT_X_TLS_TRY
29         if tls_req_opt is not None:
30             ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, tls_req_opt)
31
32         conn = ldap.initialize(self.lm.server_url)
33
34         if tls != "notls":
35             if not self.lm.server_url.startswith("ldaps"):
36                 conn.start_tls_s()
37         return conn
38
39     def _authenticate(self, username, password):
40
41         conn = self._ldap_connect()
42         dn = self.lm.bind_dn_tmpl % {'username': username}
43         conn.simple_bind_s(dn, password)
44
45         # Bypass info plugins to optimize data retrieval
46         if self.lm.get_user_info:
47             self.lm.info = None
48
49             if not self.ldap_info:
50                 self.ldap_info = LDAPInfo(self._site)
51
52             return self.ldap_info.get_user_data_from_conn(conn, dn)
53
54         return None
55
56     def POST(self, *args, **kwargs):
57         username = kwargs.get("login_name")
58         password = kwargs.get("login_password")
59         userattrs = None
60         authed = False
61         errmsg = None
62
63         if username and password:
64             try:
65                 userdata = self._authenticate(username, password)
66                 if userdata:
67                     userattrs = dict()
68                     for d, v in userdata.get('userdata', {}).items():
69                         userattrs[d] = v
70                     if 'groups' in userdata:
71                         userattrs['groups'] = userdata['groups']
72                     if 'extras' in userdata:
73                         userattrs['extras'] = userdata['extras']
74                 authed = True
75             except Exception, e:  # pylint: disable=broad-except
76                 errmsg = "Authentication failed"
77                 self.error("Exception raised: [%s]" % repr(e))
78         else:
79             errmsg = "Username or password is missing"
80             self.error(errmsg)
81
82         if authed:
83             return self.lm.auth_successful(self.trans, username, 'password',
84                                            userdata=userattrs)
85
86         context = self.create_tmpl_context(
87             username=username,
88             error=errmsg,
89             error_password=not password,
90             error_username=not username
91         )
92         # pylint: disable=star-args
93         return self._template('login/form.html', **context)
94
95
96 class LoginManager(LoginManagerBase):
97
98     def __init__(self, *args, **kwargs):
99         super(LoginManager, self).__init__(*args, **kwargs)
100         self.name = 'ldap'
101         self.path = 'ldap'
102         self.page = None
103         self.ldap_info = None
104         self.service_name = 'ldap'
105         self.description = """
106 Form based login Manager that uses a simple bind LDAP operation to perform
107 authentication. """
108         self.new_config(
109             self.name,
110             pconfig.String(
111                 'server url',
112                 'The LDAP server url.',
113                 'ldap://example.com'),
114             pconfig.Template(
115                 'bind dn template',
116                 'Template to turn username into DN.',
117                 'uid=%(username)s,ou=People,dc=example,dc=com'),
118             pconfig.Condition(
119                 'get user info',
120                 'Get user info via ldap using user credentials',
121                 True),
122             pconfig.Pick(
123                 'tls',
124                 'What TLS level show be required',
125                 ['Demand', 'Allow', 'Try', 'Never', 'NoTLS'],
126                 'Demand'),
127             pconfig.String(
128                 'username text',
129                 'Text used to ask for the username at login time.',
130                 'Username'),
131             pconfig.String(
132                 'password text',
133                 'Text used to ask for the password at login time.',
134                 'Password'),
135             pconfig.String(
136                 'help text',
137                 'Text used to guide the user at login time.',
138                 'Provide your Username and Password')
139         )
140
141     @property
142     def help_text(self):
143         return self.get_config_value('help text')
144
145     @property
146     def username_text(self):
147         return self.get_config_value('username text')
148
149     @property
150     def password_text(self):
151         return self.get_config_value('password text')
152
153     @property
154     def server_url(self):
155         return self.get_config_value('server url')
156
157     @property
158     def tls(self):
159         return self.get_config_value('tls')
160
161     @property
162     def get_user_info(self):
163         return self.get_config_value('get user info')
164
165     @property
166     def bind_dn_tmpl(self):
167         return self.get_config_value('bind dn template')
168
169     def get_tree(self, site):
170         self.page = LDAP(site, self, 'login/ldap')
171         return self.page
172
173
174 class Installer(object):
175
176     def __init__(self, *pargs):
177         self.name = 'ldap'
178         self.ptype = 'login'
179         self.pargs = pargs
180
181     def install_args(self, group):
182         group.add_argument('--ldap', choices=['yes', 'no'], default='no',
183                            help='Configure LDAP authentication')
184         group.add_argument('--ldap-server-url', action='store',
185                            help='LDAP Server Url')
186         group.add_argument('--ldap-bind-dn-template', action='store',
187                            help='LDAP Bind DN Template')
188
189     def configure(self, opts):
190         if opts['ldap'] != 'yes':
191             return
192
193         # Add configuration data to database
194         po = PluginObject(*self.pargs)
195         po.name = 'ldap'
196         po.wipe_data()
197         po.wipe_config_values()
198
199         config = dict()
200         if 'ldap_server_url' in opts:
201             config['server url'] = opts['ldap_server_url']
202         if 'ldap_bind_dn_template' in opts:
203             config['bind dn template'] = opts['ldap_bind_dn_template']
204         config['tls'] = 'Demand'
205         po.save_plugin_config(config)
206
207         # Update global config to add login plugin
208         po.is_enabled = True
209         po.save_enabled_state()