diff --git a/README.md b/README.md index cd54a777..f03cf518 100644 --- a/README.md +++ b/README.md @@ -87,6 +87,15 @@ Example: SITE_NAME = "My Monitoring Project" +`REGISTRATION_OPEN` controls whether site visitors can create new accounts. +Set it to `False` if you are setting up a private healthchecks instance, but +it needs to be publicly accessible (so, for example, your cloud services +can send pings). + +If you close new user registration, you can still selectively invite users +to your team account. + + ## Database Configuration Database configuration is stored in `hc/settings.py` and can be overriden diff --git a/hc/accounts/middleware.py b/hc/accounts/middleware.py index b562e98f..23570c05 100644 --- a/hc/accounts/middleware.py +++ b/hc/accounts/middleware.py @@ -11,12 +11,7 @@ class TeamAccessMiddleware(object): teams_q = teams_q.select_related("user") request.teams = list(teams_q) - try: - profile = request.user.profile - except Profile.DoesNotExist: - profile = Profile(user=request.user) - profile.save() - + profile = Profile.objects.for_user(request.user) if profile.current_team: request.team = profile.current_team else: diff --git a/hc/accounts/models.py b/hc/accounts/models.py index 1b311ab0..9789886d 100644 --- a/hc/accounts/models.py +++ b/hc/accounts/models.py @@ -13,6 +13,15 @@ from django.utils import timezone from hc.lib import emails +class ProfileManager(models.Manager): + def for_user(self, user): + profile = self.filter(user=user).first() + if profile is None: + profile = Profile(user=user, team_access_allowed=user.is_superuser) + profile.save() + return profile + + class Profile(models.Model): # Owner: user = models.OneToOneField(User, blank=True, null=True) @@ -25,6 +34,8 @@ class Profile(models.Model): api_key = models.CharField(max_length=128, blank=True) current_team = models.ForeignKey("self", null=True) + objects = ProfileManager() + def __str__(self): return self.team_name or self.user.email diff --git a/hc/accounts/tests/test_login.py b/hc/accounts/tests/test_login.py index df67eccd..779d629a 100644 --- a/hc/accounts/tests/test_login.py +++ b/hc/accounts/tests/test_login.py @@ -1,6 +1,7 @@ from django.contrib.auth.models import User from django.core import mail from django.test import TestCase +from django.test.utils import override_settings from hc.api.models import Check from django.conf import settings @@ -57,3 +58,11 @@ class LoginTestCase(TestCase): self.assertEqual(len(mail.outbox), 1) subject = "Log in to %s" % settings.SITE_NAME self.assertEqual(mail.outbox[0].subject, subject) + + @override_settings(REGISTRATION_OPEN=False) + def test_it_obeys_registration_open(self): + form = {"email": "dan@example.org"} + + r = self.client.post("/accounts/login/", form) + assert r.status_code == 200 + self.assertContains(r, "Incorrect email") diff --git a/hc/accounts/views.py b/hc/accounts/views.py index cb9368c2..d86a9e91 100644 --- a/hc/accounts/views.py +++ b/hc/accounts/views.py @@ -1,6 +1,7 @@ import uuid import re +from django.conf import settings from django.contrib import messages from django.contrib.auth import login as auth_login from django.contrib.auth import logout as auth_logout @@ -25,8 +26,8 @@ def _make_user(email): user.set_unusable_password() user.save() - profile = Profile(user=user) - profile.save() + # Ensure a profile gets created + Profile.objects.for_user(user) channel = Channel() channel.user = user @@ -74,14 +75,20 @@ def login(request, show_password=False): bad_credentials = True show_password = True else: + user = None try: user = User.objects.get(email=email) except User.DoesNotExist: - user = _make_user(email) - _associate_demo_check(request, user) - - user.profile.send_instant_login_link() - return redirect("hc-login-link-sent") + if settings.REGISTRATION_OPEN: + user = _make_user(email) + _associate_demo_check(request, user) + else: + bad_credentials = True + + if user: + profile = Profile.objects.for_user(user) + profile.send_instant_login_link() + return redirect("hc-login-link-sent") else: form = EmailPasswordForm() diff --git a/hc/front/tests/test_basics.py b/hc/front/tests/test_basics.py index 98d08683..7de2014e 100644 --- a/hc/front/tests/test_basics.py +++ b/hc/front/tests/test_basics.py @@ -1,4 +1,5 @@ from django.test import TestCase +from django.test.utils import override_settings from hc.api.models import Check @@ -20,3 +21,9 @@ class BasicsTestCase(TestCase): assert r.status_code == 200 assert code != "x" assert Check.objects.filter(code=code).exists() + + @override_settings(REGISTRATION_OPEN=False) + def test_it_obeys_registration_open(self): + r = self.client.get("/") + + self.assertNotContains(r, "Get Started") diff --git a/hc/front/views.py b/hc/front/views.py index 14fc98c2..8c6e6238 100644 --- a/hc/front/views.py +++ b/hc/front/views.py @@ -93,7 +93,8 @@ def index(request): "ping_url": check.url(), "enable_pushbullet": settings.PUSHBULLET_CLIENT_ID is not None, "enable_pushover": settings.PUSHOVER_API_TOKEN is not None, - "enable_discord": settings.DISCORD_CLIENT_ID is not None + "enable_discord": settings.DISCORD_CLIENT_ID is not None, + "registration_open": settings.REGISTRATION_OPEN } return render(request, "front/welcome.html", ctx) diff --git a/hc/settings.py b/hc/settings.py index e41f24bf..8e8fdc7e 100644 --- a/hc/settings.py +++ b/hc/settings.py @@ -21,6 +21,7 @@ DEBUG = True ALLOWED_HOSTS = [] DEFAULT_FROM_EMAIL = 'healthchecks@example.org' USE_PAYMENTS = False +REGISTRATION_OPEN = True INSTALLED_APPS = ( diff --git a/templates/front/welcome.html b/templates/front/welcome.html index a4207209..cff74639 100644 --- a/templates/front/welcome.html +++ b/templates/front/welcome.html @@ -95,6 +95,7 @@ +{% if registration_open %}
@@ -126,6 +127,7 @@
+{% endif %}
@@ -308,6 +310,7 @@
+ {% if registration_open %} + {% endif %}