2022-11-20 18:13:44 +00:00
|
|
|
import pytest
|
2022-12-31 18:13:51 +00:00
|
|
|
from pytest_httpx import HTTPXMock
|
2022-11-20 18:13:44 +00:00
|
|
|
|
|
|
|
from core.models import Config
|
|
|
|
from users.models import Domain, Identity, User
|
|
|
|
from users.views.identity import CreateIdentity
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.django_db
|
2022-11-22 03:10:01 +00:00
|
|
|
def test_create_identity_form(config_system, client):
|
2022-11-20 18:13:44 +00:00
|
|
|
""" """
|
|
|
|
# Make a user
|
|
|
|
user = User.objects.create(email="test@example.com")
|
|
|
|
admin = User.objects.create(email="admin@example.com", admin=True)
|
|
|
|
# Make a domain
|
|
|
|
domain = Domain.objects.create(domain="example.com", local=True)
|
|
|
|
domain.users.add(user)
|
|
|
|
domain.users.add(admin)
|
|
|
|
|
|
|
|
# Test identity_min_length
|
|
|
|
data = {
|
|
|
|
"username": "a",
|
|
|
|
"domain": domain.domain,
|
|
|
|
"name": "The User",
|
|
|
|
}
|
|
|
|
|
|
|
|
form = CreateIdentity.form_class(user=user, data=data)
|
|
|
|
assert not form.is_valid()
|
|
|
|
assert "username" in form.errors
|
|
|
|
assert "value has at least" in form.errors["username"][0]
|
|
|
|
|
|
|
|
form = CreateIdentity.form_class(user=admin, data=data)
|
|
|
|
assert form.errors == {}
|
|
|
|
|
|
|
|
# Test restricted_usernames
|
|
|
|
data = {
|
|
|
|
"username": "@root",
|
|
|
|
"domain": domain.domain,
|
|
|
|
"name": "The User",
|
|
|
|
}
|
|
|
|
|
|
|
|
form = CreateIdentity.form_class(user=user, data=data)
|
|
|
|
assert not form.is_valid()
|
|
|
|
assert "username" in form.errors
|
|
|
|
assert "restricted to administrators" in form.errors["username"][0]
|
|
|
|
|
|
|
|
form = CreateIdentity.form_class(user=admin, data=data)
|
|
|
|
assert form.errors == {}
|
|
|
|
|
|
|
|
# Test valid chars
|
|
|
|
data = {
|
|
|
|
"username": "@someval!!!!",
|
|
|
|
"domain": domain.domain,
|
|
|
|
"name": "The User",
|
|
|
|
}
|
|
|
|
|
|
|
|
for u in (user, admin):
|
|
|
|
form = CreateIdentity.form_class(user=u, data=data)
|
|
|
|
assert not form.is_valid()
|
|
|
|
assert "username" in form.errors
|
|
|
|
assert form.errors["username"][0].startswith("Only the letters")
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.django_db
|
2022-11-22 03:10:01 +00:00
|
|
|
def test_identity_max_per_user(config_system, client):
|
2022-11-20 18:13:44 +00:00
|
|
|
"""
|
2022-11-20 18:37:26 +00:00
|
|
|
Ensures that the identity limit is functioning
|
2022-11-20 18:13:44 +00:00
|
|
|
"""
|
|
|
|
# Make a user
|
|
|
|
user = User.objects.create(email="test@example.com")
|
|
|
|
# Make a domain
|
|
|
|
domain = Domain.objects.create(domain="example.com", local=True)
|
|
|
|
domain.users.add(user)
|
|
|
|
# Make an identity for them
|
|
|
|
for i in range(Config.system.identity_max_per_user):
|
|
|
|
identity = Identity.objects.create(
|
|
|
|
actor_uri=f"https://example.com/@test{i}@example.com/actor/",
|
|
|
|
username=f"test{i}",
|
|
|
|
domain=domain,
|
|
|
|
name=f"Test User{i}",
|
|
|
|
local=True,
|
|
|
|
)
|
|
|
|
identity.users.add(user)
|
|
|
|
|
|
|
|
data = {
|
|
|
|
"username": "toomany",
|
|
|
|
"domain": domain.domain,
|
|
|
|
"name": "Too Many",
|
|
|
|
}
|
|
|
|
form = CreateIdentity.form_class(user=user, data=data)
|
|
|
|
assert form.errors["__all__"][0].startswith("You are not allowed more than")
|
|
|
|
|
|
|
|
user.admin = True
|
|
|
|
form = CreateIdentity.form_class(user=user, data=data)
|
|
|
|
assert form.is_valid()
|
2022-11-20 18:37:26 +00:00
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.django_db
|
2022-11-21 01:29:19 +00:00
|
|
|
def test_fetch_actor(httpx_mock, config_system):
|
2022-11-20 18:37:26 +00:00
|
|
|
"""
|
|
|
|
Ensures that making identities via actor fetching works
|
|
|
|
"""
|
|
|
|
# Make a shell remote identity
|
|
|
|
identity = Identity.objects.create(
|
|
|
|
actor_uri="https://example.com/test-actor/",
|
|
|
|
local=False,
|
|
|
|
)
|
|
|
|
|
|
|
|
# Trigger actor fetch
|
|
|
|
httpx_mock.add_response(
|
|
|
|
url="https://example.com/.well-known/webfinger?resource=acct:test@example.com",
|
|
|
|
json={
|
|
|
|
"subject": "acct:test@example.com",
|
|
|
|
"aliases": [
|
|
|
|
"https://example.com/test-actor/",
|
|
|
|
],
|
|
|
|
"links": [
|
|
|
|
{
|
|
|
|
"rel": "http://webfinger.net/rel/profile-page",
|
|
|
|
"type": "text/html",
|
|
|
|
"href": "https://example.com/test-actor/",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"rel": "self",
|
|
|
|
"type": "application/activity+json",
|
|
|
|
"href": "https://example.com/test-actor/",
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
)
|
|
|
|
httpx_mock.add_response(
|
|
|
|
url="https://example.com/test-actor/",
|
|
|
|
json={
|
|
|
|
"@context": [
|
|
|
|
"https://www.w3.org/ns/activitystreams",
|
|
|
|
"https://w3id.org/security/v1",
|
2023-05-13 16:01:27 +00:00
|
|
|
{
|
|
|
|
"toot": "http://joinmastodon.org/ns#",
|
|
|
|
"featured": {"@id": "toot:featured", "@type": "@id"},
|
|
|
|
},
|
2022-11-20 18:37:26 +00:00
|
|
|
],
|
|
|
|
"id": "https://example.com/test-actor/",
|
|
|
|
"type": "Person",
|
|
|
|
"inbox": "https://example.com/test-actor/inbox/",
|
|
|
|
"publicKey": {
|
|
|
|
"id": "https://example.com/test-actor/#main-key",
|
|
|
|
"owner": "https://example.com/test-actor/",
|
|
|
|
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\nits-a-faaaake\n-----END PUBLIC KEY-----\n",
|
|
|
|
},
|
|
|
|
"followers": "https://example.com/test-actor/followers/",
|
|
|
|
"following": "https://example.com/test-actor/following/",
|
2023-05-13 16:01:27 +00:00
|
|
|
"featured": "https://example.com/test-actor/collections/featured/",
|
2022-11-20 18:37:26 +00:00
|
|
|
"icon": {
|
|
|
|
"type": "Image",
|
|
|
|
"mediaType": "image/jpeg",
|
|
|
|
"url": "https://example.com/icon.jpg",
|
|
|
|
},
|
|
|
|
"image": {
|
|
|
|
"type": "Image",
|
|
|
|
"mediaType": "image/jpeg",
|
|
|
|
"url": "https://example.com/image.jpg",
|
|
|
|
},
|
2022-12-16 23:38:52 +00:00
|
|
|
"manuallyApprovesFollowers": False,
|
2022-11-20 18:37:26 +00:00
|
|
|
"name": "Test User",
|
|
|
|
"preferredUsername": "test",
|
|
|
|
"published": "2022-11-02T00:00:00Z",
|
|
|
|
"summary": "<p>A test user</p>",
|
|
|
|
"url": "https://example.com/test-actor/view/",
|
|
|
|
},
|
|
|
|
)
|
2023-10-01 15:27:23 +00:00
|
|
|
httpx_mock.add_response(
|
|
|
|
url="https://example.com/test-actor/collections/featured/",
|
|
|
|
json={
|
|
|
|
"type": "Collection",
|
|
|
|
"totalItems": 1,
|
|
|
|
"orderedItems": [
|
|
|
|
{
|
|
|
|
"id": "https://example.com/test-actor/posts/123456789",
|
|
|
|
"type": "Note",
|
|
|
|
"attributedTo": "https://example.com/test-actor/",
|
|
|
|
"content": "<p>Test post</p>",
|
|
|
|
"published": "2022-11-02T00:00:00Z",
|
|
|
|
"to": "as:Public",
|
|
|
|
"url": "https://example.com/test-actor/posts/123456789",
|
|
|
|
}
|
|
|
|
],
|
|
|
|
},
|
|
|
|
)
|
2023-07-17 06:37:47 +00:00
|
|
|
identity.fetch_actor()
|
2022-11-20 18:37:26 +00:00
|
|
|
|
|
|
|
# Verify the data arrived
|
|
|
|
identity = Identity.objects.get(pk=identity.pk)
|
|
|
|
assert identity.name == "Test User"
|
|
|
|
assert identity.username == "test"
|
|
|
|
assert identity.domain_id == "example.com"
|
|
|
|
assert identity.profile_uri == "https://example.com/test-actor/view/"
|
|
|
|
assert identity.inbox_uri == "https://example.com/test-actor/inbox/"
|
2023-05-13 16:01:27 +00:00
|
|
|
assert (
|
|
|
|
identity.featured_collection_uri
|
|
|
|
== "https://example.com/test-actor/collections/featured/"
|
|
|
|
)
|
2022-11-20 18:37:26 +00:00
|
|
|
assert identity.icon_uri == "https://example.com/icon.jpg"
|
|
|
|
assert identity.image_uri == "https://example.com/image.jpg"
|
|
|
|
assert identity.summary == "<p>A test user</p>"
|
|
|
|
assert "ts-a-faaaake" in identity.public_key
|
2022-12-31 18:13:51 +00:00
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.django_db
|
2023-07-17 06:37:47 +00:00
|
|
|
def test_fetch_webfinger_url(httpx_mock: HTTPXMock, config_system):
|
2022-12-31 18:13:51 +00:00
|
|
|
"""
|
|
|
|
Ensures that we can deal with various kinds of webfinger URLs
|
|
|
|
"""
|
|
|
|
|
|
|
|
# With no host-meta, it should be the default
|
|
|
|
assert (
|
2023-07-17 06:37:47 +00:00
|
|
|
Identity.fetch_webfinger_url("example.com")
|
2022-12-31 18:13:51 +00:00
|
|
|
== "https://example.com/.well-known/webfinger?resource={uri}"
|
|
|
|
)
|
|
|
|
|
|
|
|
# Inject a host-meta directing it to a subdomain
|
|
|
|
httpx_mock.add_response(
|
|
|
|
url="https://example.com/.well-known/host-meta",
|
|
|
|
text="""<?xml version="1.0" encoding="UTF-8"?>
|
|
|
|
<XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0">
|
|
|
|
<Link rel="lrdd" template="https://fedi.example.com/.well-known/webfinger?resource={uri}"/>
|
|
|
|
</XRD>""",
|
|
|
|
)
|
|
|
|
assert (
|
2023-07-17 06:37:47 +00:00
|
|
|
Identity.fetch_webfinger_url("example.com")
|
2022-12-31 18:13:51 +00:00
|
|
|
== "https://fedi.example.com/.well-known/webfinger?resource={uri}"
|
|
|
|
)
|
|
|
|
|
|
|
|
# Inject a host-meta directing it to a different URL format
|
|
|
|
httpx_mock.add_response(
|
|
|
|
url="https://example.com/.well-known/host-meta",
|
|
|
|
text="""<?xml version="1.0" encoding="UTF-8"?>
|
|
|
|
<XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0">
|
|
|
|
<Link rel="lrdd" template="https://example.com/amazing-webfinger?query={uri}"/>
|
|
|
|
</XRD>""",
|
|
|
|
)
|
|
|
|
assert (
|
2023-07-17 06:37:47 +00:00
|
|
|
Identity.fetch_webfinger_url("example.com")
|
2022-12-31 18:13:51 +00:00
|
|
|
== "https://example.com/amazing-webfinger?query={uri}"
|
|
|
|
)
|
|
|
|
|
|
|
|
# Inject a host-meta directing it to a different url THAT SUPPORTS XML ONLY
|
|
|
|
# (we want to ignore that one)
|
|
|
|
httpx_mock.add_response(
|
|
|
|
url="https://example.com/.well-known/host-meta",
|
|
|
|
text="""<?xml version="1.0" encoding="UTF-8"?>
|
|
|
|
<XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0">
|
|
|
|
<Link rel="lrdd" template="https://xmlfedi.example.com/webfinger?q={uri}" type="application/xrd+xml"/>
|
|
|
|
</XRD>""",
|
|
|
|
)
|
|
|
|
assert (
|
2023-07-17 06:37:47 +00:00
|
|
|
Identity.fetch_webfinger_url("example.com")
|
2022-12-31 18:13:51 +00:00
|
|
|
== "https://example.com/.well-known/webfinger?resource={uri}"
|
|
|
|
)
|
2023-06-22 23:09:19 +00:00
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.django_db
|
|
|
|
def test_attachment_to_ap(identity: Identity, config_system):
|
|
|
|
"""
|
|
|
|
Tests identity attachment conversion to AP format.
|
|
|
|
"""
|
|
|
|
identity.metadata = [
|
|
|
|
{
|
|
|
|
"type": "http://schema.org#PropertyValue",
|
|
|
|
"name": "Website",
|
|
|
|
"value": "http://example.com",
|
|
|
|
}
|
|
|
|
]
|
|
|
|
|
|
|
|
response = identity.to_ap()
|
|
|
|
|
|
|
|
assert response["attachment"]
|
|
|
|
assert len(response["attachment"]) == 1
|
|
|
|
|
|
|
|
attachment = response["attachment"][0]
|
|
|
|
|
|
|
|
assert attachment["type"] == "PropertyValue"
|
|
|
|
assert attachment["name"] == "Website"
|
|
|
|
assert attachment["value"] == (
|
|
|
|
'<a href="http://example.com" rel="nofollow">'
|
|
|
|
'<span class="invisible">http://</span>example.com</a>'
|
|
|
|
)
|