0
0
Fork 0
mirror of https://github.com/healthchecks/healthchecks.git synced 2025-04-15 01:18:30 +00:00

Rename "phone" -> "recipient" in Signal add/edit forms

(since this field can now be a phone number *or* a username)
This commit is contained in:
Pēteris Caune 2024-12-28 11:21:34 +02:00
parent b22f359595
commit a05fb5cece
No known key found for this signature in database
GPG key ID: E28D7679E9A9EDE2
7 changed files with 33 additions and 34 deletions

View file

@ -284,15 +284,14 @@ class PhoneUpDownForm(PhoneNumberForm):
class SignalRecipientForm(forms.Form): class SignalRecipientForm(forms.Form):
error_css_class = "has-error" error_css_class = "has-error"
label = forms.CharField(max_length=100, required=False) label = forms.CharField(max_length=100, required=False)
phone = forms.CharField() recipient = forms.CharField()
def clean_phone(self) -> str: def clean_recipient(self) -> str:
v = self.cleaned_data["phone"] v = self.cleaned_data["recipient"]
stripped = v.encode("ascii", "ignore").decode("ascii") stripped = v.encode("ascii", "ignore").decode("ascii")
assert isinstance(stripped, str) assert isinstance(stripped, str)
stripped = stripped.replace(" ", "").replace("-", "") stripped = stripped.replace(" ", "").replace("-", "")
if "." in stripped: if "." in stripped:
# Assume it is a username # Assume it is a username
if not re.match(r"^\w{3,48}\.\d{2,10}$", stripped): if not re.match(r"^\w{3,48}\.\d{2,10}$", stripped):
@ -321,7 +320,7 @@ class SignalForm(SignalRecipientForm):
def get_json(self) -> str: def get_json(self) -> str:
return json.dumps( return json.dumps(
{ {
"value": self.cleaned_data["phone"], "value": self.cleaned_data["recipient"],
"up": self.cleaned_data["up"], "up": self.cleaned_data["up"],
"down": self.cleaned_data["down"], "down": self.cleaned_data["down"],
} }

View file

@ -22,7 +22,7 @@ class AddSignalTestCase(BaseTestCase):
def test_it_creates_channel(self) -> None: def test_it_creates_channel(self) -> None:
form = { form = {
"label": "My Phone", "label": "My Phone",
"phone": "+1234567890", "recipient": "+1234567890",
"down": "true", "down": "true",
"up": "true", "up": "true",
} }
@ -45,7 +45,7 @@ class AddSignalTestCase(BaseTestCase):
def test_it_handles_username(self) -> None: def test_it_handles_username(self) -> None:
form = { form = {
"label": "My Phone", "label": "My Phone",
"phone": "foobar.123", "recipient": "foobar.123",
"down": "true", "down": "true",
"up": "true", "up": "true",
} }
@ -71,7 +71,7 @@ class AddSignalTestCase(BaseTestCase):
self.assertEqual(r.status_code, 403) self.assertEqual(r.status_code, 403)
def test_it_handles_down_false_up_true(self) -> None: def test_it_handles_down_false_up_true(self) -> None:
form = {"phone": "+1234567890", "up": True} form = {"recipient": "+1234567890", "up": True}
self.client.login(username="alice@example.org", password="password") self.client.login(username="alice@example.org", password="password")
self.client.post(self.url, form) self.client.post(self.url, form)
@ -81,7 +81,7 @@ class AddSignalTestCase(BaseTestCase):
self.assertTrue(c.phone.notify_up) self.assertTrue(c.phone.notify_up)
def test_it_rejects_unchecked_up_and_down(self) -> None: def test_it_rejects_unchecked_up_and_down(self) -> None:
form = {"phone": "+1234567890"} form = {"recipient": "+1234567890"}
self.client.login(username="alice@example.org", password="password") self.client.login(username="alice@example.org", password="password")
r = self.client.post(self.url, form) r = self.client.post(self.url, form)
@ -90,17 +90,17 @@ class AddSignalTestCase(BaseTestCase):
def test_it_rejects_bad_phone(self) -> None: def test_it_rejects_bad_phone(self) -> None:
for v in ["not a phone number", False, 15, "+123456789A"]: for v in ["not a phone number", False, 15, "+123456789A"]:
self.client.login(username="alice@example.org", password="password") self.client.login(username="alice@example.org", password="password")
r = self.client.post(self.url, {"phone": v}) r = self.client.post(self.url, {"recipient": v})
self.assertContains(r, "Invalid phone number format.") self.assertContains(r, "Invalid phone number format.")
def test_it_rejects_bad_username(self) -> None: def test_it_rejects_bad_username(self) -> None:
for v in ["a.123", "foobar.0"]: for v in ["a.123", "foobar.0"]:
self.client.login(username="alice@example.org", password="password") self.client.login(username="alice@example.org", password="password")
r = self.client.post(self.url, {"phone": v}) r = self.client.post(self.url, {"recipient": v})
self.assertContains(r, "Invalid username format.") self.assertContains(r, "Invalid username format.")
def test_it_strips_invisible_formatting_characters(self) -> None: def test_it_strips_invisible_formatting_characters(self) -> None:
form = {"phone": "\u202c+1234567890\u202c", "down": True} form = {"recipient": "\u202c+1234567890\u202c", "down": True}
self.client.login(username="alice@example.org", password="password") self.client.login(username="alice@example.org", password="password")
r = self.client.post(self.url, form) r = self.client.post(self.url, form)
@ -110,7 +110,7 @@ class AddSignalTestCase(BaseTestCase):
self.assertEqual(c.phone.value, "+1234567890") self.assertEqual(c.phone.value, "+1234567890")
def test_it_strips_hyphens(self) -> None: def test_it_strips_hyphens(self) -> None:
form = {"phone": "+123-4567890", "down": True} form = {"recipient": "+123-4567890", "down": True}
self.client.login(username="alice@example.org", password="password") self.client.login(username="alice@example.org", password="password")
r = self.client.post(self.url, form) r = self.client.post(self.url, form)
@ -120,7 +120,7 @@ class AddSignalTestCase(BaseTestCase):
self.assertEqual(c.phone.value, "+1234567890") self.assertEqual(c.phone.value, "+1234567890")
def test_it_strips_spaces(self) -> None: def test_it_strips_spaces(self) -> None:
form = {"phone": " +123 45 678 90 ", "down": True} form = {"recipient": " +123 45 678 90 ", "down": True}
self.client.login(username="alice@example.org", password="password") self.client.login(username="alice@example.org", password="password")
r = self.client.post(self.url, form) r = self.client.post(self.url, form)

View file

@ -32,7 +32,7 @@ class EditSignalTestCase(BaseTestCase):
def test_it_updates_channel(self) -> None: def test_it_updates_channel(self) -> None:
form = { form = {
"label": "My Phone", "label": "My Phone",
"phone": "+1234567890", "recipient": "+1234567890",
"down": "true", "down": "true",
"up": "false", "up": "false",
} }
@ -53,7 +53,7 @@ class EditSignalTestCase(BaseTestCase):
def test_it_handles_username(self) -> None: def test_it_handles_username(self) -> None:
form = { form = {
"label": "My Phone", "label": "My Phone",
"phone": "foobar.123", "recipient": "foobar.123",
"down": "true", "down": "true",
"up": "false", "up": "false",
} }

View file

@ -19,7 +19,7 @@ class VerifySignalNumberTestCase(BaseTestCase):
@patch("hc.front.views.Signal") @patch("hc.front.views.Signal")
def test_it_works(self, mock_signal: Mock) -> None: def test_it_works(self, mock_signal: Mock) -> None:
self.client.login(username="alice@example.org", password="password") self.client.login(username="alice@example.org", password="password")
r = self.client.post(self.url, {"phone": "+1234567890"}) r = self.client.post(self.url, {"recipient": "+1234567890"})
self.assertContains(r, "All good, the message was sent") self.assertContains(r, "All good, the message was sent")
@patch("hc.front.views.Signal") @patch("hc.front.views.Signal")
@ -27,7 +27,7 @@ class VerifySignalNumberTestCase(BaseTestCase):
mock_signal.send.side_effect = TransportError("CAPTCHA proof required") mock_signal.send.side_effect = TransportError("CAPTCHA proof required")
self.client.login(username="alice@example.org", password="password") self.client.login(username="alice@example.org", password="password")
r = self.client.post(self.url, {"phone": "+1234567890"}) r = self.client.post(self.url, {"recipient": "+1234567890"})
self.assertContains(r, "We hit a Signal rate-limit") self.assertContains(r, "We hit a Signal rate-limit")
@patch("hc.front.views.Signal") @patch("hc.front.views.Signal")
@ -35,7 +35,7 @@ class VerifySignalNumberTestCase(BaseTestCase):
mock_signal.send.side_effect = TransportError("Recipient not found") mock_signal.send.side_effect = TransportError("Recipient not found")
self.client.login(username="alice@example.org", password="password") self.client.login(username="alice@example.org", password="password")
r = self.client.post(self.url, {"phone": "+1234567890"}) r = self.client.post(self.url, {"recipient": "+1234567890"})
self.assertContains(r, "Recipient not found") self.assertContains(r, "Recipient not found")
@patch("hc.front.views.Signal") @patch("hc.front.views.Signal")
@ -43,13 +43,13 @@ class VerifySignalNumberTestCase(BaseTestCase):
mock_signal.send.side_effect = TransportError("signal-cli call failed (123)") mock_signal.send.side_effect = TransportError("signal-cli call failed (123)")
self.client.login(username="alice@example.org", password="password") self.client.login(username="alice@example.org", password="password")
r = self.client.post(self.url, {"phone": "+1234567890"}) r = self.client.post(self.url, {"recipient": "+1234567890"})
self.assertContains(r, "signal-cli call failed") self.assertContains(r, "signal-cli call failed")
@patch("hc.front.views.Signal") @patch("hc.front.views.Signal")
def test_it_handles_invalid_phone_number(self, mock_signal: Mock) -> None: def test_it_handles_invalid_phone_number(self, mock_signal: Mock) -> None:
self.client.login(username="alice@example.org", password="password") self.client.login(username="alice@example.org", password="password")
r = self.client.post(self.url, {"phone": "+123"}) r = self.client.post(self.url, {"recipient": "+123"})
self.assertContains(r, "Invalid phone number") self.assertContains(r, "Invalid phone number")
def test_it_requires_post(self) -> None: def test_it_requires_post(self) -> None:
@ -58,14 +58,14 @@ class VerifySignalNumberTestCase(BaseTestCase):
self.assertEqual(r.status_code, 405) self.assertEqual(r.status_code, 405)
def test_it_requires_authenticated_user(self) -> None: def test_it_requires_authenticated_user(self) -> None:
r = self.client.post(self.url, {"phone": "+1234567890"}) r = self.client.post(self.url, {"recipient": "+1234567890"})
self.assertRedirects(r, "/accounts/login/?next=" + self.url) self.assertRedirects(r, "/accounts/login/?next=" + self.url)
def test_it_obeys_per_account_rate_limit(self) -> None: def test_it_obeys_per_account_rate_limit(self) -> None:
TokenBucket.objects.create(value=f"signal-verify-{self.alice.id}", tokens=0) TokenBucket.objects.create(value=f"signal-verify-{self.alice.id}", tokens=0)
self.client.login(username="alice@example.org", password="password") self.client.login(username="alice@example.org", password="password")
r = self.client.post(self.url, {"phone": "+1234567890"}) r = self.client.post(self.url, {"recipient": "+1234567890"})
self.assertContains(r, "Verification rate limit exceeded") self.assertContains(r, "Verification rate limit exceeded")
@override_settings(SECRET_KEY="test-secret") @override_settings(SECRET_KEY="test-secret")
@ -76,5 +76,5 @@ class VerifySignalNumberTestCase(BaseTestCase):
obj.save() obj.save()
self.client.login(username="alice@example.org", password="password") self.client.login(username="alice@example.org", password="password")
r = self.client.post(self.url, {"phone": "+123456789"}) r = self.client.post(self.url, {"recipient": "+123456789"})
self.assertContains(r, "Verification rate limit exceeded") self.assertContains(r, "Verification rate limit exceeded")

View file

@ -2285,7 +2285,7 @@ def signal_form(request: HttpRequest, channel: Channel) -> HttpResponse:
form = forms.SignalForm( form = forms.SignalForm(
{ {
"label": channel.name, "label": channel.name,
"phone": channel.phone.value, "recipient": channel.phone.value,
"up": channel.phone.notify_up, "up": channel.phone.notify_up,
"down": channel.phone.notify_down, "down": channel.phone.notify_down,
} }
@ -2697,13 +2697,13 @@ def verify_signal_number(request: AuthenticatedHttpRequest) -> HttpResponse:
if not form.is_valid(): if not form.is_valid():
return render_result("Invalid phone number") return render_result("Invalid phone number")
phone = form.cleaned_data["phone"] recipient = form.cleaned_data["recipient"]
# Enforce per-recipient rate limit (6 messages per minute) # Enforce per-recipient rate limit (6 messages per minute)
if not TokenBucket.authorize_signal(phone): if not TokenBucket.authorize_signal(recipient):
return render_result("Verification rate limit exceeded") return render_result("Verification rate limit exceeded")
try: try:
Signal.send(phone, f"Test message from {settings.SITE_NAME}") Signal.send(recipient, f"Test message from {settings.SITE_NAME}")
except TransportError as e: except TransportError as e:
return render_result(e.message) return render_result(e.message)

View file

@ -15,7 +15,7 @@ $(function () {
url: url, url: url,
type: "post", type: "post",
headers: { "X-CSRFToken": token }, headers: { "X-CSRFToken": token },
data: { phone: $("#id_number").val() }, data: { recipient: $("#id_number").val() },
success: function (data) { success: function (data) {
$("#verify-result").html(data); $("#verify-result").html(data);
$("#submit-btn").attr("disabled", data.indexOf("alert-success") == -1); $("#submit-btn").attr("disabled", data.indexOf("alert-success") == -1);

View file

@ -45,7 +45,7 @@ Signal Settings - {% site_name %}
</div> </div>
</div> </div>
<div class="form-group {{ form.phone.css_classes }}"> <div class="form-group {{ form.recipient.css_classes }}">
<label for="id_number" class="col-sm-2 control-label">Phone or Username</label> <label for="id_number" class="col-sm-2 control-label">Phone or Username</label>
<div class="col-sm-6"> <div class="col-sm-6">
<div class="input-group"> <div class="input-group">
@ -53,9 +53,9 @@ Signal Settings - {% site_name %}
id="id_number" id="id_number"
type="text" type="text"
class="form-control" class="form-control"
name="phone" name="recipient"
placeholder="+1234567890 or xyz.123" placeholder="+1234567890 or xyz.123"
value="{{ form.phone.value|default:"" }}"> value="{{ form.recipient.value|default:"" }}">
<span class="input-group-btn"> <span class="input-group-btn">
<button <button
@ -69,9 +69,9 @@ Signal Settings - {% site_name %}
</span> </span>
</div> </div>
{% if form.phone.errors %} {% if form.recipient.errors %}
<div class="help-block"> <div class="help-block">
{{ form.phone.errors|join:"" }} {{ form.recipient.errors|join:"" }}
</div> </div>
{% else %} {% else %}
<span class="help-block"> <span class="help-block">