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