def create_local_accounts(factories, count, dependencies): password = factories["users.User"].build().password users = factories["users.User"].build_batch(size=count) for user in users: # we set the hashed password by hand, because computing one for each user # is CPU intensive user.password = password users = users_models.User.objects.bulk_create(users, batch_size=BATCH_SIZE) actors = [] domain = federation_models.Domain.objects.get_or_create( name=settings.FEDERATION_HOSTNAME)[0] users = [u for u in users if u.pk] private, public = keys.get_key_pair() for user in users: if not user.pk: continue actor = federation_models.Actor(private_key=private.decode("utf-8"), public_key=public.decode("utf-8"), **users_models.get_actor_data( user.username, domain=domain)) actors.append(actor) actors = federation_models.Actor.objects.bulk_create(actors, batch_size=BATCH_SIZE) for user, actor in zip(users, actors): user.actor = actor users_models.User.objects.bulk_update(users, ["actor"]) return actors
def create_actor(user): args = get_actor_data(user) private, public = keys.get_key_pair() args["private_key"] = private.decode("utf-8") args["public_key"] = public.decode("utf-8") return federation_models.Actor.objects.create(user=user, **args)
def generate_actor(username, **kwargs): actor_data = user_models.get_actor_data(username, **kwargs) private, public = keys.get_key_pair() actor_data["private_key"] = private.decode("utf-8") actor_data["public_key"] = public.decode("utf-8") return federation_models.Actor.objects.create(**actor_data)
def test_can_verify_django_request(factories, fake_request): private_key, public_key = keys.get_key_pair() signed_request = factories["federation.SignedRequest"]( auth__key=private_key, auth__headers=["date"]) prepared = signed_request.prepare() django_request = fake_request.get( "/", **{ "HTTP_DATE": prepared.headers["date"], "HTTP_SIGNATURE": prepared.headers["signature"], }) assert signing.verify_django(django_request, public_key) is None
def test_can_verify_django_request_failure(factories, fake_request, now): private_key, public_key = keys.get_key_pair() signed_request = factories["federation.SignedRequest"]( auth__key=private_key, auth__headers=["date"]) prepared = signed_request.prepare() django_request = fake_request.get( "/", **{ "HTTP_DATE": http_date((now + datetime.timedelta(seconds=31)).timestamp()), "HTTP_SIGNATURE": prepared.headers["signature"], }) with pytest.raises(forms.ValidationError): signing.verify_django(django_request, public_key)
def test_can_verify_django_request_digest_failure(factories, fake_request): private_key, public_key = keys.get_key_pair() signed_request = factories["federation.SignedRequest"]( auth__key=private_key, method="post", data=b"hello=world", auth__headers=["date", "digest"], ) prepared = signed_request.prepare() django_request = fake_request.post( "/", **{ "HTTP_DATE": prepared.headers["date"], "HTTP_DIGEST": prepared.headers["digest"] + "noop", "HTTP_SIGNATURE": prepared.headers["signature"], }) with pytest.raises(cryptography.exceptions.InvalidSignature): signing.verify_django(django_request, public_key)
def test_authenticate(factories, mocker, api_request): private, public = keys.get_key_pair() factories["federation.Domain"](name="test.federation", nodeinfo_fetch_date=None) actor_url = "https://test.federation/actor" mocker.patch( "funkwhale_api.federation.actors.get_actor_data", return_value={ "@context": jsonld.get_default_context(), "id": actor_url, "type": "Person", "outbox": "https://test.com", "inbox": "https://test.com", "followers": "https://test.com", "preferredUsername": "******", "publicKey": { "publicKeyPem": public.decode("utf-8"), "owner": actor_url, "id": actor_url + "#main-key", }, }, ) update_domain_nodeinfo = mocker.patch( "funkwhale_api.federation.tasks.update_domain_nodeinfo") signed_request = factories["federation.SignedRequest"]( auth__key=private, auth__key_id=actor_url + "#main-key", auth__headers=["date"]) prepared = signed_request.prepare() django_request = api_request.get( "/", **{ "HTTP_DATE": prepared.headers["date"], "HTTP_SIGNATURE": prepared.headers["signature"], }) authenticator = authentication.SignatureAuthentication() user, _ = authenticator.authenticate(django_request) actor = django_request.actor assert user.is_anonymous is True assert actor.public_key == public.decode("utf-8") assert actor.fid == actor_url update_domain_nodeinfo.assert_called_once_with( domain_name="test.federation")
def test_authenticate_skips_blocked_actor(factories, api_request): policy = factories["moderation.InstancePolicy"](block_all=True, for_actor=True) private, public = keys.get_key_pair() actor_url = policy.target_actor.fid signed_request = factories["federation.SignedRequest"]( auth__key=private, auth__key_id=actor_url + "#main-key", auth__headers=["date"]) prepared = signed_request.prepare() django_request = api_request.get( "/", **{ "HTTP_DATE": prepared.headers["date"], "HTTP_SIGNATURE": prepared.headers["signature"], }) authenticator = authentication.SignatureAuthentication() with pytest.raises(exceptions.BlockedActorOrDomain): authenticator.authenticate(django_request)
def test_autenthicate_supports_blind_key_rotation(factories, mocker, api_request): actor = factories["federation.Actor"]() actor_url = actor.fid # request is signed with a pair of new keys new_private, new_public = keys.get_key_pair() mocker.patch( "funkwhale_api.federation.actors.get_actor_data", return_value={ "@context": jsonld.get_default_context(), "id": actor_url, "type": "Person", "outbox": "https://test.com", "inbox": "https://test.com", "followers": "https://test.com", "preferredUsername": "******", "publicKey": { "publicKeyPem": new_public.decode("utf-8"), "owner": actor_url, "id": actor_url + "#main-key", }, }, ) signed_request = factories["federation.SignedRequest"]( auth__key=new_private, auth__key_id=actor_url + "#main-key", auth__headers=["date"], ) prepared = signed_request.prepare() django_request = api_request.get( "/", **{ "HTTP_DATE": prepared.headers["date"], "HTTP_SIGNATURE": prepared.headers["signature"], }) authenticator = authentication.SignatureAuthentication() user, _ = authenticator.authenticate(django_request) actor = django_request.actor assert user.is_anonymous is True assert actor.public_key == new_public.decode("utf-8") assert actor.fid == actor_url
def test_authenticate_checks_signature_with_allow_list(preferences, factories, api_request): preferences["moderation__allow_list_enabled"] = True domain = factories["federation.Domain"](allowed=False) private, public = keys.get_key_pair() actor_url = "https://{}/actor".format(domain.name) signed_request = factories["federation.SignedRequest"]( auth__key=private, auth__key_id=actor_url + "#main-key", auth__headers=["date"]) prepared = signed_request.prepare() django_request = api_request.get( "/", **{ "HTTP_DATE": prepared.headers["date"], "HTTP_SIGNATURE": prepared.headers["signature"], }) authenticator = authentication.SignatureAuthentication() with pytest.raises(exceptions.BlockedActorOrDomain): authenticator.authenticate(django_request)
def test_authenticate_ignore_inactive_policy(factories, api_request, mocker): policy = factories["moderation.InstancePolicy"](block_all=True, for_domain=True, is_active=False) private, public = keys.get_key_pair() actor_url = "https://{}/actor".format(policy.target_domain.name) signed_request = factories["federation.SignedRequest"]( auth__key=private, auth__key_id=actor_url + "#main-key", auth__headers=["date"]) mocker.patch( "funkwhale_api.federation.actors.get_actor_data", return_value={ "@context": jsonld.get_default_context(), "id": actor_url, "type": "Person", "outbox": "https://test.com", "inbox": "https://test.com", "followers": "https://test.com", "preferredUsername": "******", "publicKey": { "publicKeyPem": public.decode("utf-8"), "owner": actor_url, "id": actor_url + "#main-key", }, }, ) prepared = signed_request.prepare() django_request = api_request.get( "/", **{ "HTTP_DATE": prepared.headers["date"], "HTTP_SIGNATURE": prepared.headers["signature"], }) authenticator = authentication.SignatureAuthentication() authenticator.authenticate(django_request) actor = django_request.actor assert actor.public_key == public.decode("utf-8") assert actor.fid == actor_url