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.

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