]>
git.ipfire.org Git - ipfire.org.git/blob - src/web/people.py
d7a8675d59ae04b76033bff91c5e8a1538185880
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 # Suggest adding a description
26 if not self
.current_user
.description
:
27 hints
.append("description")
29 self
.render("people/index.html", hints
=hints
)
32 class AvatarHandler(base
.BaseHandler
):
34 # Get the desired size of the avatar file
35 size
= self
.get_argument("size", None)
39 except (TypeError, ValueError):
42 logging
.debug("Querying for avatar of %s" % uid
)
45 account
= self
.backend
.accounts
.get_by_uid(uid
)
47 raise tornado
.web
.HTTPError(404, "Could not find account %s" % uid
)
49 # Allow downstream to cache this for 60 minutes
50 self
.set_expires(3600)
53 avatar
= account
.get_avatar(size
)
55 # If there is no avatar, we serve a default image
57 logging
.debug("No avatar uploaded for %s" % account
)
59 return self
.redirect(self
.static_url("img/default-avatar.jpg"))
62 type = imghdr
.what(None, avatar
)
64 # Set headers about content
65 self
.set_header("Content-Disposition", "inline; filename=\"%s.%s\"" % (account
.uid
, type))
66 self
.set_header("Content-Type", "image/%s" % type)
72 class CallsHandler(auth
.CacheMixin
, base
.BaseHandler
):
73 @tornado.web
.authenticated
74 def get(self
, uid
, date
=None):
75 account
= self
.backend
.accounts
.get_by_uid(uid
)
77 raise tornado
.web
.HTTPError(404, "Could not find account %s" % uid
)
79 # Check for permissions
80 if not account
.can_be_managed_by(self
.current_user
):
81 raise tornado
.web
.HTTPError(403, "%s cannot manage %s" % (self
.current_user
, account
))
85 date
= datetime
.datetime
.strptime(date
, "%Y-%m-%d").date()
87 raise tornado
.web
.HTTPError(400, "Invalid date: %s" % date
)
89 date
= datetime
.date
.today()
91 self
.render("people/calls.html", account
=account
, date
=date
)
94 class CallHandler(auth
.CacheMixin
, base
.BaseHandler
):
95 @tornado.web
.authenticated
96 def get(self
, uid
, uuid
):
97 account
= self
.backend
.accounts
.get_by_uid(uid
)
99 raise tornado
.web
.HTTPError(404, "Could not find account %s" % uid
)
101 # Check for permissions
102 if not account
.can_be_managed_by(self
.current_user
):
103 raise tornado
.web
.HTTPError(403, "%s cannot manage %s" % (self
.current_user
, account
))
105 call
= self
.backend
.talk
.freeswitch
.get_call_by_uuid(uuid
)
107 raise tornado
.web
.HTTPError(404, "Could not find call %s" % uuid
)
111 self
.render("people/call.html", account
=account
, call
=call
)
114 class ConferencesHandler(auth
.CacheMixin
, base
.BaseHandler
):
115 @tornado.web
.authenticated
117 self
.render("people/conferences.html", conferences
=self
.backend
.talk
.conferences
)
120 class GroupsHandler(auth
.CacheMixin
, base
.BaseHandler
):
121 @tornado.web
.authenticated
123 # Only staff can see other groups
124 if not self
.current_user
.is_staff():
125 raise tornado
.web
.HTTPError(403)
127 self
.render("people/groups.html")
130 class GroupHandler(auth
.CacheMixin
, base
.BaseHandler
):
131 @tornado.web
.authenticated
133 # Only staff can see other groups
134 if not self
.current_user
.is_staff():
135 raise tornado
.web
.HTTPError(403)
138 group
= self
.backend
.groups
.get_by_gid(gid
)
140 raise tornado
.web
.HTTPError(404, "Could not find group %s" % gid
)
142 self
.render("people/group.html", group
=group
)
145 class SearchHandler(auth
.CacheMixin
, base
.BaseHandler
):
146 @tornado.web
.authenticated
148 q
= self
.get_argument("q")
151 accounts
= self
.backend
.accounts
.search(q
)
153 # Redirect when only one result was found
154 if len(accounts
) == 1:
155 self
.redirect("/users/%s" % accounts
[0].uid
)
158 self
.render("people/search.html", q
=q
, accounts
=accounts
)
161 class SIPHandler(auth
.CacheMixin
, base
.BaseHandler
):
162 @tornado.web
.authenticated
164 account
= self
.backend
.accounts
.get_by_uid(uid
)
166 raise tornado
.web
.HTTPError(404, "Could not find account %s" % uid
)
168 # Check for permissions
169 if not account
.can_be_managed_by(self
.current_user
):
170 raise tornado
.web
.HTTPError(403, "%s cannot manage %s" % (self
.current_user
, account
))
172 self
.render("people/sip.html", account
=account
)
175 class UsersHandler(auth
.CacheMixin
, base
.BaseHandler
):
176 @tornado.web
.authenticated
178 # Only staff can see other users
179 if not self
.current_user
.is_staff():
180 raise tornado
.web
.HTTPError(403)
182 self
.render("people/users.html")
185 class UserHandler(auth
.CacheMixin
, base
.BaseHandler
):
186 @tornado.web
.authenticated
188 account
= self
.backend
.accounts
.get_by_uid(uid
)
190 raise tornado
.web
.HTTPError(404, "Could not find account %s" % uid
)
192 self
.render("people/user.html", account
=account
)
195 class UserEditHandler(auth
.CacheMixin
, base
.BaseHandler
):
196 @tornado.web
.authenticated
198 account
= self
.backend
.accounts
.get_by_uid(uid
)
200 raise tornado
.web
.HTTPError(404, "Could not find account %s" % uid
)
202 # Check for permissions
203 if not account
.can_be_managed_by(self
.current_user
):
204 raise tornado
.web
.HTTPError(403, "%s cannot manage %s" % (self
.current_user
, account
))
206 self
.render("people/user-edit.html", account
=account
, countries
=countries
.get_all())
208 @tornado.web
.authenticated
210 account
= self
.backend
.accounts
.get_by_uid(uid
)
212 raise tornado
.web
.HTTPError(404, "Could not find account %s" % uid
)
214 # Check for permissions
215 if not account
.can_be_managed_by(self
.current_user
):
216 raise tornado
.web
.HTTPError(403, "%s cannot manage %s" % (self
.current_user
, account
))
218 # Unfortunately this cannot be wrapped into a transaction
220 account
.first_name
= self
.get_argument("first_name")
221 account
.last_name
= self
.get_argument("last_name")
222 account
.nickname
= self
.get_argument("nickname", None)
223 account
.street
= self
.get_argument("street", None)
224 account
.city
= self
.get_argument("city", None)
225 account
.postal_code
= self
.get_argument("postal_code", None)
226 account
.country_code
= self
.get_argument("country_code", None)
227 account
.description
= self
.get_argument("description", None)
231 filename
, data
, mimetype
= self
.get_file("avatar")
233 if not mimetype
.startswith("image/"):
234 raise tornado
.web
.HTTPError(400, "Avatar is not an image file: %s" % mimetype
)
236 account
.upload_avatar(data
)
241 account
.mail_routing_address
= self
.get_argument("mail_routing_address", None)
244 account
.phone_numbers
= self
.get_argument("phone_numbers", "").splitlines()
245 account
.sip_routing_address
= self
.get_argument("sip_routing_address", None)
246 except ldap
.STRONG_AUTH_REQUIRED
as e
:
247 raise tornado
.web
.HTTPError(403, "%s" % e
) from e
249 # Redirect back to user page
250 self
.redirect("/users/%s" % account
.uid
)
253 class UserPasswdHandler(auth
.CacheMixin
, base
.BaseHandler
):
254 @tornado.web
.authenticated
256 account
= self
.backend
.accounts
.get_by_uid(uid
)
258 raise tornado
.web
.HTTPError(404, "Could not find account %s" % uid
)
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
))
264 self
.render("people/passwd.html", account
=account
)
266 @tornado.web
.authenticated
268 account
= self
.backend
.accounts
.get_by_uid(uid
)
270 raise tornado
.web
.HTTPError(404, "Could not find account %s" % uid
)
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
))
276 # Get current password
277 password
= self
.get_argument("password")
280 password1
= self
.get_argument("password1")
281 password2
= self
.get_argument("password2")
283 # Passwords must match
284 if not password1
== password2
:
285 raise tornado
.web
.HTTPError(400, "Passwords do not match")
287 # XXX Check password complexity
289 # Check if old password matches
290 if not account
.check_password(password
):
291 raise tornado
.web
.HTTPError(403, "Incorrect password for %s" % account
)
294 account
.passwd(password1
)
296 # Redirect back to user's page
297 self
.redirect("/users/%s" % account
.uid
)
300 class SSODiscourse(auth
.CacheMixin
, base
.BaseHandler
):
301 @base.ratelimit(minutes
=24*60, requests
=100)
302 @tornado.web
.authenticated
304 # Fetch Discourse's parameters
305 sso
= self
.get_argument("sso")
306 sig
= self
.get_argument("sig")
310 params
= self
.accounts
.decode_discourse_payload(sso
, sig
)
312 # Raise bad request if the signature is invalid
314 raise tornado
.web
.HTTPError(400)
316 # Redirect back if user is already logged in
318 "nonce" : params
.get("nonce"),
319 "external_id" : self
.current_user
.uid
,
322 "email" : self
.current_user
.email
,
323 "require_activation" : "false",
325 # More details about the user
326 "username" : self
.current_user
.uid
,
327 "name" : "%s" % self
.current_user
,
328 "bio" : self
.current_user
.description
or "",
331 "avatar_url" : self
.current_user
.avatar_url(),
332 "avatar_force_update" : "true",
334 # Send a welcome message
335 "suppress_welcome_message" : "false",
338 "admin" : "true" if self
.current_user
.is_admin() else "false",
339 "moderator" : "true" if self
.current_user
.is_moderator() else "false",
342 # Format payload and sign it
343 payload
= self
.accounts
.encode_discourse_payload(**args
)
344 signature
= self
.accounts
.sign_discourse_payload(payload
)
346 qs
= urllib
.parse
.urlencode({
352 self
.redirect("%s?%s" % (params
.get("return_sso_url"), qs
))
355 class NewAccountsModule(ui_modules
.UIModule
):
356 def render(self
, days
=14):
357 t
= datetime
.datetime
.utcnow() - datetime
.timedelta(days
=days
)
359 # Fetch all accounts created after t
360 accounts
= self
.backend
.accounts
.get_created_after(t
)
362 accounts
.sort(key
=lambda a
: a
.created_at
, reverse
=True)
364 return self
.render_string("people/modules/accounts-new.html",
365 accounts
=accounts
, t
=t
)
368 class AccountsListModule(ui_modules
.UIModule
):
369 def render(self
, accounts
=None):
371 accounts
= self
.backend
.accounts
373 return self
.render_string("people/modules/accounts-list.html", accounts
=accounts
)
376 class CDRModule(ui_modules
.UIModule
):
377 def render(self
, account
, date
=None, limit
=None):
378 cdr
= account
.get_cdr(date
=date
, limit
=limit
)
380 return self
.render_string("people/modules/cdr.html",
381 account
=account
, cdr
=list(cdr
))
384 class ChannelsModule(ui_modules
.UIModule
):
385 def render(self
, account
):
386 return self
.render_string("people/modules/channels.html",
387 account
=account
, channels
=account
.sip_channels
)
390 class MOSModule(ui_modules
.UIModule
):
391 def render(self
, call
):
392 return self
.render_string("people/modules/mos.html", call
=call
)
395 class PasswordModule(ui_modules
.UIModule
):
396 def render(self
, account
=None):
397 return self
.render_string("people/modules/password.html", account
=account
)
399 def javascript_files(self
):
400 return "js/zxcvbn.js"
402 def embedded_javascript(self
):
403 return self
.render_string("people/modules/password.js")
406 class RegistrationsModule(ui_modules
.UIModule
):
407 def render(self
, account
):
408 return self
.render_string("people/modules/registrations.html", account
=account
)
411 class SIPStatusModule(ui_modules
.UIModule
):
412 def render(self
, account
):
413 return self
.render_string("people/modules/sip-status.html", account
=account
)