From 04bd6dd35dd0e45116aa381f323d739fc84d7bf5 Mon Sep 17 00:00:00 2001 From: Danielle McLean Date: Thu, 10 Aug 2023 18:05:46 +1000 Subject: [PATCH] Add NIP-05 verification compatibility --- users/admin.py | 1 + users/migrations/0018_auto_20230810_1754.py | 22 +++++++++++++++++ users/models.py | 13 ++++++++++ wellknowns/urls.py | 1 + wellknowns/views/__init__.py | 1 + wellknowns/views/nostr.py | 27 +++++++++++++++++++++ 6 files changed, 65 insertions(+) create mode 100644 users/migrations/0018_auto_20230810_1754.py create mode 100644 wellknowns/views/nostr.py diff --git a/users/admin.py b/users/admin.py index 6cd53dd..29a1474 100644 --- a/users/admin.py +++ b/users/admin.py @@ -20,6 +20,7 @@ class ProfileInline(admin.TabularInline): class UserAdmin(BaseUserAdmin): fieldsets = BaseUserAdmin.fieldsets + ( ("Profile", {"fields": ("avatar", "xmpp", "note")}), + ("Nostr", {"fields": ("nostr_key", "nostr_relays")}), ) inlines = ( PgpKeyInline, diff --git a/users/migrations/0018_auto_20230810_1754.py b/users/migrations/0018_auto_20230810_1754.py new file mode 100644 index 0000000..0c0785a --- /dev/null +++ b/users/migrations/0018_auto_20230810_1754.py @@ -0,0 +1,22 @@ +# Generated by Django 3.2.20 on 2023-08-10 07:54 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("users", "0017_rename_key_pgpkey"), + ] + + operations = [ + migrations.AddField( + model_name="user", + name="nostr_key", + field=models.CharField(blank=True, max_length=32, null=True, unique=True), + ), + migrations.AddField( + model_name="user", + name="nostr_relays", + field=models.JSONField(blank=True, default=list), + ), + ] diff --git a/users/models.py b/users/models.py index 9ddac65..8f1f033 100644 --- a/users/models.py +++ b/users/models.py @@ -81,6 +81,19 @@ class User(ModelMeta, AbstractUser): help_text="SHA-256 hash of the user's OpenID URL, used for Libravatar", ) + nostr_key = models.CharField( + max_length=32, + unique=True, + blank=True, + null=True, + help_text="A Nostr public key in 32-byte hex format", + ) + nostr_relays = models.JSONField( + default=list, + blank=True, + help_text="An array of Nostr relay URLs that this public key posts to", + ) + @property def calc_email_md5(self): return md5(self.email.lower().encode("utf-8")).hexdigest() diff --git a/wellknowns/urls.py b/wellknowns/urls.py index 35957c0..562e70b 100644 --- a/wellknowns/urls.py +++ b/wellknowns/urls.py @@ -8,5 +8,6 @@ urlpatterns = [ path("host-meta", views.host_meta_xml, name="host-meta"), path("host-meta.json", views.host_meta_json, name="host-meta.json"), path("manifest.json", views.manifest, name="manifest"), + path("nostr.json", views.nostr_json, name="nostr.json"), path("webfinger", views.webfinger, name="webfinger"), ] diff --git a/wellknowns/views/__init__.py b/wellknowns/views/__init__.py index dd2ff0f..f3d69d3 100644 --- a/wellknowns/views/__init__.py +++ b/wellknowns/views/__init__.py @@ -1,4 +1,5 @@ from .static import keybase from .host_meta import host_meta_xml, host_meta_json from .manifest import manifest +from .nostr import nostr_json from .webfinger import webfinger diff --git a/wellknowns/views/nostr.py b/wellknowns/views/nostr.py new file mode 100644 index 0000000..bf21a3e --- /dev/null +++ b/wellknowns/views/nostr.py @@ -0,0 +1,27 @@ +from django.http import JsonResponse +from users.models import User + +aliases = {"00dani": ("_", "dani")} +unaliases = {alias: name for (name, aliases) in aliases.items() for alias in aliases} + + +def nostr_json(request) -> JsonResponse: + users = User.objects.filter(nostr_key__isnull=False) + + if "name" in request.GET: + name = request.GET["name"] + if name in unaliases: + name = unaliases[name] + users = users.filter(username=name) + + names = {u.username: u.nostr_key for u in users} + for name in list(names.keys()): + for alias in aliases.get(name, []): + names[alias] = names[name] + + relays = {u.nostr_key: u.nostr_relays for u in users if u.nostr_relays} + + response = {"names": names} + if relays: + response["relays"] = relays + return JsonResponse(response)