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