]> git.ipfire.org Git - ipfire.org.git/blob - src/web/people.py
people: Allow no account reference when using the password module
[ipfire.org.git] / src / web / people.py
1 #!/usr/bin/python
2
3 import datetime
4 import ldap
5 import logging
6 import imghdr
7 import sshpubkeys
8 import tornado.web
9
10 from .. import countries
11
12 from . import auth
13 from . import base
14 from . import ui_modules
15
16 class IndexHandler(auth.CacheMixin, base.BaseHandler):
17 @tornado.web.authenticated
18 def get(self):
19 self.render("people/index.html")
20
21
22 class AvatarHandler(base.BaseHandler):
23 def get(self, uid):
24 # Get the desired size of the avatar file
25 size = self.get_argument("size", 0)
26
27 try:
28 size = int(size)
29 except (TypeError, ValueError):
30 size = None
31
32 logging.debug("Querying for avatar of %s" % uid)
33
34 # Fetch user account
35 account = self.backend.accounts.get_by_uid(uid)
36 if not account:
37 raise tornado.web.HTTPError(404, "Could not find account %s" % uid)
38
39 # Allow downstream to cache this for 60 minutes
40 self.set_expires(3600)
41
42 # Resize avatar
43 avatar = account.get_avatar(size)
44
45 # If there is no avatar, we serve a default image
46 if not avatar:
47 logging.debug("No avatar uploaded for %s" % account)
48
49 return self.redirect(self.static_url("img/default-avatar.jpg"))
50
51 # Guess content type
52 type = imghdr.what(None, avatar)
53
54 # Set headers about content
55 self.set_header("Content-Disposition", "inline; filename=\"%s.%s\"" % (account.uid, type))
56 self.set_header("Content-Type", "image/%s" % type)
57
58 # Deliver payload
59 self.finish(avatar)
60
61
62 class CallsHandler(auth.CacheMixin, base.BaseHandler):
63 @tornado.web.authenticated
64 def get(self, uid, date=None):
65 account = self.backend.accounts.get_by_uid(uid)
66 if not account:
67 raise tornado.web.HTTPError(404, "Could not find account %s" % uid)
68
69 if date:
70 try:
71 date = datetime.datetime.strptime(date, "%Y-%m-%d").date()
72 except ValueError:
73 raise tornado.web.HTTPError(400, "Invalid date: %s" % date)
74 else:
75 date = datetime.date.today()
76
77 self.render("people/calls.html", account=account, date=date)
78
79
80 class CallHandler(auth.CacheMixin, base.BaseHandler):
81 @tornado.web.authenticated
82 def get(self, uid, uuid):
83 account = self.backend.accounts.get_by_uid(uid)
84 if not account:
85 raise tornado.web.HTTPError(404, "Could not find account %s" % uid)
86
87 call = self.backend.talk.freeswitch.get_call_by_uuid(uuid)
88 if not call:
89 raise tornado.web.HTTPError(404, "Could not find call %s" % uuid)
90
91 # XXX limit
92
93 self.render("people/call.html", account=account, call=call)
94
95
96 class ConferencesHandler(auth.CacheMixin, base.BaseHandler):
97 @tornado.web.authenticated
98 def get(self):
99 self.render("people/conferences.html", conferences=self.backend.talk.conferences)
100
101
102 class SearchHandler(auth.CacheMixin, base.BaseHandler):
103 @tornado.web.authenticated
104 def get(self):
105 q = self.get_argument("q")
106
107 # Perform the search
108 accounts = self.backend.accounts.search(q)
109
110 # Redirect when only one result was found
111 if len(accounts) == 1:
112 self.redirect("/users/%s" % accounts[0].uid)
113 return
114
115 self.render("people/search.html", q=q, accounts=accounts)
116
117
118 class SSHKeysIndexHandler(auth.CacheMixin, base.BaseHandler):
119 @tornado.web.authenticated
120 def get(self, uid):
121 account = self.backend.accounts.get_by_uid(uid)
122 if not account:
123 raise tornado.web.HTTPError(404, "Could not find account %s" % uid)
124
125 self.render("people/ssh-keys/index.html", account=account)
126
127
128 class SSHKeysDownloadHandler(auth.CacheMixin, base.BaseHandler):
129 @tornado.web.authenticated
130 def get(self, uid, hash_sha256):
131 account = self.backend.accounts.get_by_uid(uid)
132 if not account:
133 raise tornado.web.HTTPError(404, "Could not find account %s" % uid)
134
135 # Get SSH key
136 key = account.get_ssh_key_by_hash_sha256(hash_sha256)
137 if not key:
138 raise tornado.web.HTTPError(404, "Could not find key: %s" % hash_sha256)
139
140 # Set HTTP Headers
141 self.add_header("Content-Type", "text/plain")
142
143 self.finish(key.keydata)
144
145
146 class SSHKeysUploadHandler(auth.CacheMixin, base.BaseHandler):
147 @tornado.web.authenticated
148 def get(self, uid):
149 account = self.backend.accounts.get_by_uid(uid)
150 if not account:
151 raise tornado.web.HTTPError(404, "Could not find account %s" % uid)
152
153 # Check for permissions
154 if not account.can_be_managed_by(self.current_user):
155 raise tornado.web.HTTPError(403, "%s cannot manage %s" % (self.current_user, account))
156
157 self.render("people/ssh-keys/upload.html", account=account)
158
159 @tornado.web.authenticated
160 def post(self, uid):
161 account = self.backend.accounts.get_by_uid(uid)
162 if not account:
163 raise tornado.web.HTTPError(404, "Could not find account %s" % uid)
164
165 # Check for permissions
166 if not account.can_be_managed_by(self.current_user):
167 raise tornado.web.HTTPError(403, "%s cannot manage %s" % (self.current_user, account))
168
169 key = self.get_argument("key")
170
171 # Verify password
172 password = self.get_argument("password")
173 if not account.check_password(password):
174 raise tornado.web.HTTPError(403, "Incorrect password for %s" % account)
175
176 # Try to add new SSH key
177 try:
178 account.add_ssh_key(key)
179
180 except sshpubkeys.InvalidKeyException as e:
181 self.render("people/ssh-keys/error-invalid-key.html", account=account, exception=e)
182 return
183
184 self.redirect("/users/%s/ssh-keys" % account.uid)
185
186
187 class SSHKeysDeleteHandler(auth.CacheMixin, base.BaseHandler):
188 @tornado.web.authenticated
189 def get(self, uid, hash_sha256):
190 account = self.backend.accounts.get_by_uid(uid)
191 if not account:
192 raise tornado.web.HTTPError(404, "Could not find account %s" % uid)
193
194 # Get SSH key
195 key = account.get_ssh_key_by_hash_sha256(hash_sha256)
196 if not key:
197 raise tornado.web.HTTPError(404, "Could not find key: %s" % hash_sha256)
198
199 self.render("people/ssh-keys/delete.html", account=account, key=key)
200
201 @tornado.web.authenticated
202 def post(self, uid, hash_sha256):
203 account = self.backend.accounts.get_by_uid(uid)
204 if not account:
205 raise tornado.web.HTTPError(404, "Could not find account %s" % uid)
206
207 # Get SSH key
208 key = account.get_ssh_key_by_hash_sha256(hash_sha256)
209 if not key:
210 raise tornado.web.HTTPError(404, "Could not find key: %s" % hash_sha256)
211
212 # Verify password
213 password = self.get_argument("password")
214 if not account.check_password(password):
215 raise tornado.web.HTTPError(403, "Incorrect password for %s" % account)
216
217 # Delete the key
218 account.delete_ssh_key(key.keydata)
219
220 self.redirect("/users/%s/ssh-keys" % account.uid)
221
222
223 class SIPHandler(auth.CacheMixin, base.BaseHandler):
224 @tornado.web.authenticated
225 def get(self, uid):
226 account = self.backend.accounts.get_by_uid(uid)
227 if not account:
228 raise tornado.web.HTTPError(404, "Could not find account %s" % uid)
229
230 # Check for permissions
231 if not account.can_be_managed_by(self.current_user):
232 raise tornado.web.HTTPError(403, "%s cannot manage %s" % (self.current_user, account))
233
234 self.render("people/sip.html", account=account)
235
236
237 class UsersHandler(auth.CacheMixin, base.BaseHandler):
238 @tornado.web.authenticated
239 def get(self):
240 # Only staff can see other users
241 if not self.current_user.is_staff():
242 raise tornado.web.HTTPError(403)
243
244 self.render("people/users.html")
245
246
247 class UserHandler(auth.CacheMixin, base.BaseHandler):
248 @tornado.web.authenticated
249 def get(self, uid):
250 account = self.backend.accounts.get_by_uid(uid)
251 if not account:
252 raise tornado.web.HTTPError(404, "Could not find account %s" % uid)
253
254 self.render("people/user.html", account=account)
255
256
257 class UserEditHandler(auth.CacheMixin, base.BaseHandler):
258 @tornado.web.authenticated
259 def get(self, uid):
260 account = self.backend.accounts.get_by_uid(uid)
261 if not account:
262 raise tornado.web.HTTPError(404, "Could not find account %s" % uid)
263
264 # Check for permissions
265 if not account.can_be_managed_by(self.current_user):
266 raise tornado.web.HTTPError(403, "%s cannot manage %s" % (self.current_user, account))
267
268 self.render("people/user-edit.html", account=account, countries=countries.get_all())
269
270 @tornado.web.authenticated
271 def post(self, uid):
272 account = self.backend.accounts.get_by_uid(uid)
273 if not account:
274 raise tornado.web.HTTPError(404, "Could not find account %s" % uid)
275
276 # Check for permissions
277 if not account.can_be_managed_by(self.current_user):
278 raise tornado.web.HTTPError(403, "%s cannot manage %s" % (self.current_user, account))
279
280 # Unfortunately this cannot be wrapped into a transaction
281 try:
282 account.first_name = self.get_argument("first_name")
283 account.last_name = self.get_argument("last_name")
284 account.street = self.get_argument("street", None)
285 account.city = self.get_argument("city", None)
286 account.postal_code = self.get_argument("postal_code", None)
287 account.country_code = self.get_argument("country_code", None)
288
289 # Avatar
290 try:
291 filename, data, mimetype = self.get_file("avatar")
292
293 if not mimetype.startswith("image/"):
294 raise tornado.web.HTTPError(400, "Avatar is not an image file: %s" % mimetype)
295
296 account.upload_avatar(data)
297 except TypeError:
298 pass
299
300 # Email
301 account.mail_routing_address = self.get_argument("mail_routing_address", None)
302
303 # Telephone
304 account.phone_numbers = self.get_argument("phone_numbers", "").splitlines()
305 account.sip_routing_address = self.get_argument("sip_routing_address", None)
306 except ldap.STRONG_AUTH_REQUIRED as e:
307 raise tornado.web.HTTPError(403, "%s" % e) from e
308
309 # Redirect back to user page
310 self.redirect("/users/%s" % account.uid)
311
312
313 class UserPasswdHandler(auth.CacheMixin, base.BaseHandler):
314 @tornado.web.authenticated
315 def get(self, uid):
316 account = self.backend.accounts.get_by_uid(uid)
317 if not account:
318 raise tornado.web.HTTPError(404, "Could not find account %s" % uid)
319
320 # Check for permissions
321 if not account.can_be_managed_by(self.current_user):
322 raise tornado.web.HTTPError(403, "%s cannot manage %s" % (self.current_user, account))
323
324 self.render("people/passwd.html", account=account)
325
326 @tornado.web.authenticated
327 def post(self, uid):
328 account = self.backend.accounts.get_by_uid(uid)
329 if not account:
330 raise tornado.web.HTTPError(404, "Could not find account %s" % uid)
331
332 # Check for permissions
333 if not account.can_be_managed_by(self.current_user):
334 raise tornado.web.HTTPError(403, "%s cannot manage %s" % (self.current_user, account))
335
336 # Get current password
337 password = self.get_argument("password")
338
339 # Get new password
340 password1 = self.get_argument("password1")
341 password2 = self.get_argument("password2")
342
343 # Passwords must match
344 if not password1 == password2:
345 raise tornado.web.HTTPError(400, "Passwords do not match")
346
347 # XXX Check password complexity
348
349 # Check if old password matches
350 if not account.check_password(password):
351 raise tornado.web.HTTPError(403, "Incorrect password for %s" % account)
352
353 # Save new password
354 account.passwd(password1)
355
356 # Redirect back to user's page
357 self.redirect("/users/%s" % account.uid)
358
359
360 class AccountsListModule(ui_modules.UIModule):
361 def render(self, accounts=None):
362 if accounts is None:
363 accounts = self.backend.accounts
364
365 return self.render_string("people/modules/accounts-list.html", accounts=accounts)
366
367
368 class CDRModule(ui_modules.UIModule):
369 def render(self, account, date=None, limit=None):
370 cdr = account.get_cdr(date=date, limit=limit)
371
372 return self.render_string("people/modules/cdr.html",
373 account=account, cdr=list(cdr))
374
375
376 class ChannelsModule(ui_modules.UIModule):
377 def render(self, account):
378 return self.render_string("people/modules/channels.html",
379 account=account, channels=account.sip_channels)
380
381
382 class MOSModule(ui_modules.UIModule):
383 def render(self, call):
384 return self.render_string("people/modules/mos.html", call=call)
385
386
387 class PasswordModule(ui_modules.UIModule):
388 def render(self, account=None):
389 return self.render_string("people/modules/password.html", account=account)
390
391 def javascript_files(self):
392 return "js/zxcvbn.js"
393
394 def embedded_javascript(self):
395 return self.render_string("people/modules/password.js")
396
397
398 class RegistrationsModule(ui_modules.UIModule):
399 def render(self, account):
400 return self.render_string("people/modules/registrations.html", account=account)
401
402
403 class SIPStatusModule(ui_modules.UIModule):
404 def render(self, account):
405 return self.render_string("people/modules/sip-status.html", account=account)