You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

306 lines
9.7 KiB

10 years ago
8 years ago
10 years ago
10 years ago
10 years ago
9 years ago
9 years ago
8 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
8 years ago
9 years ago
9 years ago
9 years ago
9 years ago
8 years ago
8 years ago
8 years ago
  1. import uuid
  2. import re
  3. from django.conf import settings
  4. from django.contrib import messages
  5. from django.contrib.auth import login as auth_login
  6. from django.contrib.auth import logout as auth_logout
  7. from django.contrib.auth import authenticate
  8. from django.contrib.auth.decorators import login_required
  9. from django.contrib.auth.hashers import check_password
  10. from django.contrib.auth.models import User
  11. from django.core import signing
  12. from django.http import HttpResponseForbidden, HttpResponseBadRequest
  13. from django.shortcuts import redirect, render
  14. from hc.accounts.forms import (EmailPasswordForm, InviteTeamMemberForm,
  15. RemoveTeamMemberForm, ReportSettingsForm,
  16. SetPasswordForm, TeamNameForm)
  17. from hc.accounts.models import Profile, Member
  18. from hc.api.models import Channel, Check
  19. from hc.lib.badges import get_badge_url
  20. def _make_user(email):
  21. username = str(uuid.uuid4())[:30]
  22. user = User(username=username, email=email)
  23. user.set_unusable_password()
  24. user.save()
  25. # Ensure a profile gets created
  26. Profile.objects.for_user(user)
  27. channel = Channel()
  28. channel.user = user
  29. channel.kind = "email"
  30. channel.value = email
  31. channel.email_verified = True
  32. channel.save()
  33. return user
  34. def _associate_demo_check(request, user):
  35. if "welcome_code" not in request.session:
  36. return
  37. try:
  38. check = Check.objects.get(code=request.session["welcome_code"])
  39. except Check.DoesNotExist:
  40. return
  41. # Only associate demo check if it doesn't have an owner already.
  42. if check.user:
  43. return
  44. check.user = user
  45. check.save()
  46. check.assign_all_channels()
  47. del request.session["welcome_code"]
  48. def login(request, show_password=False):
  49. bad_credentials = False
  50. if request.method == 'POST':
  51. form = EmailPasswordForm(request.POST)
  52. if form.is_valid():
  53. email = form.cleaned_data["email"]
  54. password = form.cleaned_data["password"]
  55. if len(password):
  56. user = authenticate(username=email, password=password)
  57. if user is not None and user.is_active:
  58. auth_login(request, user)
  59. return redirect("hc-checks")
  60. bad_credentials = True
  61. show_password = True
  62. else:
  63. user = None
  64. try:
  65. user = User.objects.get(email=email)
  66. except User.DoesNotExist:
  67. if settings.REGISTRATION_OPEN:
  68. user = _make_user(email)
  69. _associate_demo_check(request, user)
  70. else:
  71. bad_credentials = True
  72. if user:
  73. profile = Profile.objects.for_user(user)
  74. profile.send_instant_login_link()
  75. return redirect("hc-login-link-sent")
  76. else:
  77. form = EmailPasswordForm()
  78. bad_link = request.session.pop("bad_link", None)
  79. ctx = {
  80. "form": form,
  81. "bad_credentials": bad_credentials,
  82. "bad_link": bad_link,
  83. "show_password": show_password
  84. }
  85. return render(request, "accounts/login.html", ctx)
  86. def logout(request):
  87. auth_logout(request)
  88. return redirect("hc-index")
  89. def login_link_sent(request):
  90. return render(request, "accounts/login_link_sent.html")
  91. def set_password_link_sent(request):
  92. return render(request, "accounts/set_password_link_sent.html")
  93. def check_token(request, username, token):
  94. if request.user.is_authenticated and request.user.username == username:
  95. # User is already logged in
  96. return redirect("hc-checks")
  97. # Some email servers open links in emails to check for malicious content.
  98. # To work around this, we sign user in if the method is POST.
  99. #
  100. # If the method is GET, we instead serve a HTML form and a piece
  101. # of Javascript to automatically submit it.
  102. if request.method == "POST":
  103. user = authenticate(username=username, token=token)
  104. if user is not None and user.is_active:
  105. # This should get rid of "welcome_code" in session
  106. request.session.flush()
  107. user.profile.token = ""
  108. user.profile.save()
  109. auth_login(request, user)
  110. return redirect("hc-checks")
  111. request.session["bad_link"] = True
  112. return redirect("hc-login")
  113. return render(request, "accounts/check_token_submit.html")
  114. @login_required
  115. def profile(request):
  116. profile = request.user.profile
  117. # Switch user back to its default team
  118. if profile.current_team_id != profile.id:
  119. request.team = profile
  120. profile.current_team_id = profile.id
  121. profile.save()
  122. show_api_key = False
  123. if request.method == "POST":
  124. if "set_password" in request.POST:
  125. profile.send_set_password_link()
  126. return redirect("hc-set-password-link-sent")
  127. elif "create_api_key" in request.POST:
  128. profile.set_api_key()
  129. show_api_key = True
  130. messages.success(request, "The API key has been created!")
  131. elif "revoke_api_key" in request.POST:
  132. profile.api_key = ""
  133. profile.save()
  134. messages.info(request, "The API key has been revoked!")
  135. elif "show_api_key" in request.POST:
  136. show_api_key = True
  137. elif "update_reports_allowed" in request.POST:
  138. form = ReportSettingsForm(request.POST)
  139. if form.is_valid():
  140. profile.reports_allowed = form.cleaned_data["reports_allowed"]
  141. profile.save()
  142. messages.success(request, "Your settings have been updated!")
  143. elif "invite_team_member" in request.POST:
  144. if not profile.team_access_allowed:
  145. return HttpResponseForbidden()
  146. form = InviteTeamMemberForm(request.POST)
  147. if form.is_valid():
  148. email = form.cleaned_data["email"]
  149. try:
  150. user = User.objects.get(email=email)
  151. except User.DoesNotExist:
  152. user = _make_user(email)
  153. profile.invite(user)
  154. messages.success(request, "Invitation to %s sent!" % email)
  155. elif "remove_team_member" in request.POST:
  156. form = RemoveTeamMemberForm(request.POST)
  157. if form.is_valid():
  158. email = form.cleaned_data["email"]
  159. farewell_user = User.objects.get(email=email)
  160. farewell_user.profile.current_team = None
  161. farewell_user.profile.save()
  162. Member.objects.filter(team=profile,
  163. user=farewell_user).delete()
  164. messages.info(request, "%s removed from team!" % email)
  165. elif "set_team_name" in request.POST:
  166. if not profile.team_access_allowed:
  167. return HttpResponseForbidden()
  168. form = TeamNameForm(request.POST)
  169. if form.is_valid():
  170. profile.team_name = form.cleaned_data["team_name"]
  171. profile.save()
  172. messages.success(request, "Team Name updated!")
  173. tags = set()
  174. for check in Check.objects.filter(user=request.team.user):
  175. tags.update(check.tags_list())
  176. username = request.team.user.username
  177. badge_urls = []
  178. for tag in sorted(tags, key=lambda s: s.lower()):
  179. if not re.match("^[\w-]+$", tag):
  180. continue
  181. badge_urls.append(get_badge_url(username, tag))
  182. ctx = {
  183. "page": "profile",
  184. "badge_urls": badge_urls,
  185. "profile": profile,
  186. "show_api_key": show_api_key
  187. }
  188. return render(request, "accounts/profile.html", ctx)
  189. @login_required
  190. def set_password(request, token):
  191. profile = request.user.profile
  192. if not check_password(token, profile.token):
  193. return HttpResponseBadRequest()
  194. if request.method == "POST":
  195. form = SetPasswordForm(request.POST)
  196. if form.is_valid():
  197. password = form.cleaned_data["password"]
  198. request.user.set_password(password)
  199. request.user.save()
  200. profile.token = ""
  201. profile.save()
  202. # Setting a password logs the user out, so here we
  203. # log them back in.
  204. u = authenticate(username=request.user.email, password=password)
  205. auth_login(request, u)
  206. messages.success(request, "Your password has been set!")
  207. return redirect("hc-profile")
  208. return render(request, "accounts/set_password.html", {})
  209. def unsubscribe_reports(request, username):
  210. try:
  211. signing.Signer().unsign(request.GET.get("token"))
  212. except signing.BadSignature:
  213. return HttpResponseBadRequest()
  214. user = User.objects.get(username=username)
  215. user.profile.reports_allowed = False
  216. user.profile.save()
  217. return render(request, "accounts/unsubscribed.html")
  218. @login_required
  219. def switch_team(request, target_username):
  220. try:
  221. other_user = User.objects.get(username=target_username)
  222. except User.DoesNotExist:
  223. return HttpResponseForbidden()
  224. # The rules:
  225. # Superuser can switch to any team.
  226. access_ok = request.user.is_superuser
  227. # Users can switch to teams they are members of.
  228. if not access_ok and other_user.id == request.user.id:
  229. access_ok = True
  230. # Users can switch to their own teams.
  231. if not access_ok:
  232. for membership in request.user.member_set.all():
  233. if membership.team.user.id == other_user.id:
  234. access_ok = True
  235. break
  236. if not access_ok:
  237. return HttpResponseForbidden()
  238. request.user.profile.current_team = other_user.profile
  239. request.user.profile.save()
  240. return redirect("hc-checks")