]>
git.ipfire.org Git - ipfire.org.git/blob - src/web/people.py
10 from .. import countries
14 from . import ui_modules
16 class IndexHandler(auth
.CacheMixin
, base
.BaseHandler
):
17 @tornado.web
.authenticated
21 # Suggest uploading an avatar if this user does not have one
22 if not self
.current_user
.has_avatar():
23 hints
.append("avatar")
25 self
.render("people/index.html", hints
=hints
)
28 class AvatarHandler(base
.BaseHandler
):
30 # Get the desired size of the avatar file
31 size
= self
.get_argument("size", None)
35 except (TypeError, ValueError):
38 logging
.debug("Querying for avatar of %s" % uid
)
41 account
= self
.backend
.accounts
.get_by_uid(uid
)
43 raise tornado
.web
.HTTPError(404, "Could not find account %s" % uid
)
45 # Allow downstream to cache this for 60 minutes
46 self
.set_expires(3600)
49 avatar
= account
.get_avatar(size
)
51 # If there is no avatar, we serve a default image
53 logging
.debug("No avatar uploaded for %s" % account
)
55 return self
.redirect(self
.static_url("img/default-avatar.jpg"))
58 type = imghdr
.what(None, avatar
)
60 # Set headers about content
61 self
.set_header("Content-Disposition", "inline; filename=\"%s.%s\"" % (account
.uid
, type))
62 self
.set_header("Content-Type", "image/%s" % type)
68 class CallsHandler(auth
.CacheMixin
, base
.BaseHandler
):
69 @tornado.web
.authenticated
70 def get(self
, uid
, date
=None):
71 account
= self
.backend
.accounts
.get_by_uid(uid
)
73 raise tornado
.web
.HTTPError(404, "Could not find account %s" % uid
)
75 # Check for permissions
76 if not account
.can_be_managed_by(self
.current_user
):
77 raise tornado
.web
.HTTPError(403, "%s cannot manage %s" % (self
.current_user
, account
))
81 date
= datetime
.datetime
.strptime(date
, "%Y-%m-%d").date()
83 raise tornado
.web
.HTTPError(400, "Invalid date: %s" % date
)
85 date
= datetime
.date
.today()
87 self
.render("people/calls.html", account
=account
, date
=date
)
90 class CallHandler(auth
.CacheMixin
, base
.BaseHandler
):
91 @tornado.web
.authenticated
92 def get(self
, uid
, uuid
):
93 account
= self
.backend
.accounts
.get_by_uid(uid
)
95 raise tornado
.web
.HTTPError(404, "Could not find account %s" % uid
)
97 # Check for permissions
98 if not account
.can_be_managed_by(self
.current_user
):
99 raise tornado
.web
.HTTPError(403, "%s cannot manage %s" % (self
.current_user
, account
))
101 call
= self
.backend
.talk
.freeswitch
.get_call_by_uuid(uuid
)
103 raise tornado
.web
.HTTPError(404, "Could not find call %s" % uuid
)
107 self
.render("people/call.html", account
=account
, call
=call
)
110 class ConferencesHandler(auth
.CacheMixin
, base
.BaseHandler
):
111 @tornado.web
.authenticated
113 self
.render("people/conferences.html", conferences
=self
.backend
.talk
.conferences
)
116 class GroupsHandler(auth
.CacheMixin
, base
.BaseHandler
):
117 @tornado.web
.authenticated
119 # Only staff can see other groups
120 if not self
.current_user
.is_staff():
121 raise tornado
.web
.HTTPError(403)
123 self
.render("people/groups.html")
126 class GroupHandler(auth
.CacheMixin
, base
.BaseHandler
):
127 @tornado.web
.authenticated
129 # Only staff can see other groups
130 if not self
.current_user
.is_staff():
131 raise tornado
.web
.HTTPError(403)
134 group
= self
.backend
.groups
.get_by_gid(gid
)
136 raise tornado
.web
.HTTPError(404, "Could not find group %s" % gid
)
138 self
.render("people/group.html", group
=group
)
141 class SearchHandler(auth
.CacheMixin
, base
.BaseHandler
):
142 @tornado.web
.authenticated
144 q
= self
.get_argument("q")
147 accounts
= self
.backend
.accounts
.search(q
)
149 # Redirect when only one result was found
150 if len(accounts
) == 1:
151 self
.redirect("/users/%s" % accounts
[0].uid
)
154 self
.render("people/search.html", q
=q
, accounts
=accounts
)
157 class SIPHandler(auth
.CacheMixin
, base
.BaseHandler
):
158 @tornado.web
.authenticated
160 account
= self
.backend
.accounts
.get_by_uid(uid
)
162 raise tornado
.web
.HTTPError(404, "Could not find account %s" % uid
)
164 # Check for permissions
165 if not account
.can_be_managed_by(self
.current_user
):
166 raise tornado
.web
.HTTPError(403, "%s cannot manage %s" % (self
.current_user
, account
))
168 self
.render("people/sip.html", account
=account
)
171 class UsersHandler(auth
.CacheMixin
, base
.BaseHandler
):
172 @tornado.web
.authenticated
174 # Only staff can see other users
175 if not self
.current_user
.is_staff():
176 raise tornado
.web
.HTTPError(403)
178 self
.render("people/users.html")
181 class UserHandler(auth
.CacheMixin
, base
.BaseHandler
):
182 @tornado.web
.authenticated
184 account
= self
.backend
.accounts
.get_by_uid(uid
)
186 raise tornado
.web
.HTTPError(404, "Could not find account %s" % uid
)
188 self
.render("people/user.html", account
=account
)
191 class UserEditHandler(auth
.CacheMixin
, base
.BaseHandler
):
192 @tornado.web
.authenticated
194 account
= self
.backend
.accounts
.get_by_uid(uid
)
196 raise tornado
.web
.HTTPError(404, "Could not find account %s" % uid
)
198 # Check for permissions
199 if not account
.can_be_managed_by(self
.current_user
):
200 raise tornado
.web
.HTTPError(403, "%s cannot manage %s" % (self
.current_user
, account
))
202 self
.render("people/user-edit.html", account
=account
, countries
=countries
.get_all())
204 @tornado.web
.authenticated
206 account
= self
.backend
.accounts
.get_by_uid(uid
)
208 raise tornado
.web
.HTTPError(404, "Could not find account %s" % uid
)
210 # Check for permissions
211 if not account
.can_be_managed_by(self
.current_user
):
212 raise tornado
.web
.HTTPError(403, "%s cannot manage %s" % (self
.current_user
, account
))
214 # Unfortunately this cannot be wrapped into a transaction
216 account
.first_name
= self
.get_argument("first_name")
217 account
.last_name
= self
.get_argument("last_name")
218 account
.nickname
= self
.get_argument("nickname", None)
219 account
.street
= self
.get_argument("street", None)
220 account
.city
= self
.get_argument("city", None)
221 account
.postal_code
= self
.get_argument("postal_code", None)
222 account
.country_code
= self
.get_argument("country_code", None)
223 account
.description
= self
.get_argument("description", None)
227 filename
, data
, mimetype
= self
.get_file("avatar")
229 if not mimetype
.startswith("image/"):
230 raise tornado
.web
.HTTPError(400, "Avatar is not an image file: %s" % mimetype
)
232 account
.upload_avatar(data
)
237 account
.mail_routing_address
= self
.get_argument("mail_routing_address", None)
240 account
.phone_numbers
= self
.get_argument("phone_numbers", "").splitlines()
241 account
.sip_routing_address
= self
.get_argument("sip_routing_address", None)
242 except ldap
.STRONG_AUTH_REQUIRED
as e
:
243 raise tornado
.web
.HTTPError(403, "%s" % e
) from e
245 # Redirect back to user page
246 self
.redirect("/users/%s" % account
.uid
)
249 class UserPasswdHandler(auth
.CacheMixin
, base
.BaseHandler
):
250 @tornado.web
.authenticated
252 account
= self
.backend
.accounts
.get_by_uid(uid
)
254 raise tornado
.web
.HTTPError(404, "Could not find account %s" % uid
)
256 # Check for permissions
257 if not account
.can_be_managed_by(self
.current_user
):
258 raise tornado
.web
.HTTPError(403, "%s cannot manage %s" % (self
.current_user
, account
))
260 self
.render("people/passwd.html", account
=account
)
262 @tornado.web
.authenticated
264 account
= self
.backend
.accounts
.get_by_uid(uid
)
266 raise tornado
.web
.HTTPError(404, "Could not find account %s" % uid
)
268 # Check for permissions
269 if not account
.can_be_managed_by(self
.current_user
):
270 raise tornado
.web
.HTTPError(403, "%s cannot manage %s" % (self
.current_user
, account
))
272 # Get current password
273 password
= self
.get_argument("password")
276 password1
= self
.get_argument("password1")
277 password2
= self
.get_argument("password2")
279 # Passwords must match
280 if not password1
== password2
:
281 raise tornado
.web
.HTTPError(400, "Passwords do not match")
283 # XXX Check password complexity
285 # Check if old password matches
286 if not account
.check_password(password
):
287 raise tornado
.web
.HTTPError(403, "Incorrect password for %s" % account
)
290 account
.passwd(password1
)
292 # Redirect back to user's page
293 self
.redirect("/users/%s" % account
.uid
)
296 class SSODiscourse(auth
.CacheMixin
, base
.BaseHandler
):
297 def _get_discourse_params(self
):
298 # Fetch Discourse's parameters
299 sso
= self
.get_argument("sso")
300 sig
= self
.get_argument("sig")
304 return self
.accounts
.decode_discourse_payload(sso
, sig
)
306 # Raise bad request if the signature is invalid
308 raise tornado
.web
.HTTPError(400)
310 def _redirect_user_to_discourse(self
, account
, nonce
, return_sso_url
):
312 Redirects the user back to Discourse passing some
313 attributes of the user account to Discourse
317 "external_id" : account
.uid
,
320 "email" : account
.email
,
321 "require_activation" : "false",
323 # More details about the user
324 "username" : account
.uid
,
325 "name" : "%s" % account
,
326 "bio" : account
.description
or "",
329 "avatar_url" : account
.avatar_url(),
330 "avatar_force_update" : "true",
332 # Send a welcome message
333 "suppress_welcome_message" : "false",
336 "admin" : "true" if account
.is_admin() else "false",
337 "moderator" : "true" if account
.is_moderator() else "false",
340 # Format payload and sign it
341 payload
= self
.accounts
.encode_discourse_payload(**args
)
342 signature
= self
.accounts
.sign_discourse_payload(payload
)
344 qs
= urllib
.parse
.urlencode({
350 self
.redirect("%s?%s" % (return_sso_url
, qs
))
352 @base.ratelimit(minutes
=24*60, requests
=100)
354 params
= self
._get
_discourse
_params
()
356 # Redirect back if user is already logged in
357 if self
.current_user
:
358 return self
._redirect
_user
_to
_discourse
(self
.current_user
, **params
)
360 # Otherwise the user needs to authenticate
361 self
.render("auth/login.html", next
=None)
363 @base.ratelimit(minutes
=24*60, requests
=100)
365 params
= self
._get
_discourse
_params
()
368 username
= self
.get_argument("username")
369 password
= self
.get_argument("password")
372 account
= self
.accounts
.auth(username
, password
)
374 raise tornado
.web
.HTTPError(401, "Unknown user or invalid password: %s" % username
)
376 # If the user has been authenticated, we will redirect to Discourse
377 self
._redirect
_user
_to
_discourse
(account
, **params
)
380 class NewAccountsModule(ui_modules
.UIModule
):
381 def render(self
, days
=14):
382 t
= datetime
.datetime
.utcnow() - datetime
.timedelta(days
=days
)
384 # Fetch all accounts created after t
385 accounts
= self
.backend
.accounts
.get_created_after(t
)
387 accounts
.sort(key
=lambda a
: a
.created_at
, reverse
=True)
389 return self
.render_string("people/modules/accounts-new.html",
390 accounts
=accounts
, t
=t
)
393 class AccountsListModule(ui_modules
.UIModule
):
394 def render(self
, accounts
=None):
396 accounts
= self
.backend
.accounts
398 return self
.render_string("people/modules/accounts-list.html", accounts
=accounts
)
401 class CDRModule(ui_modules
.UIModule
):
402 def render(self
, account
, date
=None, limit
=None):
403 cdr
= account
.get_cdr(date
=date
, limit
=limit
)
405 return self
.render_string("people/modules/cdr.html",
406 account
=account
, cdr
=list(cdr
))
409 class ChannelsModule(ui_modules
.UIModule
):
410 def render(self
, account
):
411 return self
.render_string("people/modules/channels.html",
412 account
=account
, channels
=account
.sip_channels
)
415 class MOSModule(ui_modules
.UIModule
):
416 def render(self
, call
):
417 return self
.render_string("people/modules/mos.html", call
=call
)
420 class PasswordModule(ui_modules
.UIModule
):
421 def render(self
, account
=None):
422 return self
.render_string("people/modules/password.html", account
=account
)
424 def javascript_files(self
):
425 return "js/zxcvbn.js"
427 def embedded_javascript(self
):
428 return self
.render_string("people/modules/password.js")
431 class RegistrationsModule(ui_modules
.UIModule
):
432 def render(self
, account
):
433 return self
.render_string("people/modules/registrations.html", account
=account
)
436 class SIPStatusModule(ui_modules
.UIModule
):
437 def render(self
, account
):
438 return self
.render_string("people/modules/sip-status.html", account
=account
)