Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,11 @@ envfile:

isort:
@echo "-> Apply isort changes to ensure proper imports ordering"
@${ACTIVATE} isort --profile black .
@${ACTIVATE} isort --profile black aboutcode/ fedcode/ federatedcode/ tests/

black:
@echo "-> Apply black code formatter"
@${ACTIVATE} black ${BLACK_ARGS} .
@${ACTIVATE} black ${BLACK_ARGS} aboutcode/ fedcode/ federatedcode/ tests/

doc8:
@echo "-> Run doc8 validation"
Expand Down Expand Up @@ -92,7 +92,7 @@ migrate:

test:
@echo "-> Run the test suite"
@${ACTIVATE} pytest -vvs
@${ACTIVATE} pytest -vvs tests/ fedcode/ federatedcode/ aboutcode/

docs:
rm -rf docs/_build/
Expand Down
13 changes: 6 additions & 7 deletions fedcode/management/commands/federate.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

from traceback import format_exc as traceback_format_exc

import requests
from django.core.management.base import BaseCommand

from fedcode.models import FederateRequest
Expand All @@ -17,19 +18,17 @@


def send_fed_req_task():
"""
send_fed_req_task is a task to send the http signed request to the target and save the status of the request
"""
"""Send activity request to the target and save the status."""

for rq in FederateRequest.objects.all().order_by("created_at"):
if not rq.done:
try:
HttpSignature.signed_request(
rq.target, rq.body, FEDERATEDCODE_PRIVATE_KEY, rq.key_id
)
headers = {"Content-Type": "application/json"}
requests.post(rq.target, json=rq.body, headers=headers)
rq.done = True
rq.save()
except Exception as e:
rq.error_message = e
rq.error_message = f"Failed to federate {rq!r} {e!r} \n {traceback_format_exc()}"
finally:
rq.save()

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Generated by Django 5.0.1 on 2024-12-10 10:01

import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("fedcode", "0002_alter_package_options_alter_federaterequest_done_and_more"),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]

operations = [
migrations.RemoveField(
model_name="package",
name="local",
),
migrations.RemoveField(
model_name="person",
name="local",
),
migrations.AlterField(
model_name="person",
name="user",
field=models.OneToOneField(
blank=True,
null=True,
on_delete=django.db.models.deletion.CASCADE,
to=settings.AUTH_USER_MODEL,
),
),
migrations.AddConstraint(
model_name="person",
constraint=models.CheckConstraint(
check=models.Q(
models.Q(("remote_actor__isnull", True), ("user__isnull", False)),
models.Q(("remote_actor__isnull", False), ("user__isnull", True)),
_connector="OR",
),
name="either_local_or_remote",
),
),
]
28 changes: 23 additions & 5 deletions fedcode/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,6 @@ class Actor(models.Model):
blank=False,
)

local = models.BooleanField(
default=True,
)

class Meta:
abstract = True

Expand Down Expand Up @@ -237,6 +233,7 @@ def to_ap(self):
"type": "Note",
"author": self.acct,
"content": self.content,
"update_date": str(self.updated_at),
}


Expand Down Expand Up @@ -365,6 +362,7 @@ class Person(Actor):
user = models.OneToOneField(
User,
null=True,
blank=True,
on_delete=models.CASCADE,
)

Expand All @@ -381,6 +379,21 @@ class Person(Actor):
help_text="Notes created by this user",
)

class Meta:
constraints = [
models.CheckConstraint(
check=(
models.Q(user__isnull=False, remote_actor__isnull=True)
| models.Q(user__isnull=True, remote_actor__isnull=False)
),
name="either_local_or_remote",
),
]

@property
def local(self):
return bool(self.user)

@property
def avatar_absolute_url(self):
return f'{"https://"}{FEDERATEDCODE_DOMAIN}{self.avatar.url}'
Expand Down Expand Up @@ -410,6 +423,8 @@ def absolute_url_ap(self):

@property
def inbox_url(self):
if not self.local:
return self.remote_actor.url
return full_reverse("user-inbox", self.user.username)

@property
Expand Down Expand Up @@ -477,7 +492,10 @@ class Meta:
ordering = ["-updated_at"]

def __str__(self):
return f"{self.person.user.username} - {self.package.purl}"
username = self.person.remote_actor.username
if self.person.local:
username = self.person.user.username
return f"{username} - {self.package.purl}"


class Repository(models.Model):
Expand Down
8 changes: 6 additions & 2 deletions fedcode/templates/pkg_profile.html
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,12 @@
<figure class="image is-64x64 mb-5 mr-5 ml-5 mt-5">
<img class="is-rounded" src="{{ follower.person.avatar.url }}" alt="{{ follower.person.user.username }} profile image">
<p class="is-size-5 has-text-centered">
<a href="{% url 'user-profile' follower.person.user.username %}">@{{ follower.person.user.username }}</a>
</p>
{% if follower.person.local %}
<a href="{% url 'user-profile' follower.person.user.username %}">@{{ follower.person.user.username }}</a>
{% else %}
<a href="{% url 'user-profile' follower.person.remote_actor.username %}">@{{ follower.person.remote_actor.username }}</a>
{% endif %}
</p>
</figure>
</div>
{% endfor %}
Expand Down
22 changes: 21 additions & 1 deletion fedcode/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import json
import logging
import os.path
from urllib.parse import urlparse

import requests
from django.contrib import messages
Expand Down Expand Up @@ -59,6 +60,7 @@
from fedcode.models import Note
from fedcode.models import Package
from fedcode.models import Person
from fedcode.models import RemoteActor
from fedcode.models import Repository
from fedcode.models import Reputation
from fedcode.models import Review
Expand Down Expand Up @@ -696,6 +698,24 @@ def post(self, request, *args, **kwargs):
return HttpResponseBadRequest("Invalid message")


@method_decorator(has_valid_header, name="dispatch")
class RemoteUserSubscribe(View):
def get(self, request, *args, **kwargs):
"""Endpoint to for existing remote user to subscribe to package."""
purl = request.GET.get("purl").rstrip("/")
package = get_object_or_404(Package, purl=purl)
remote_actor = get_object_or_404(RemoteActor, username=kwargs["username"])
host = request.get_host()
if urlparse(remote_actor.url).netloc == host:
_, created = Follow.objects.get_or_create(package=package, person=remote_actor.person)
message = f"Already subscribed package {purl}"
if created:
message = f"Successfully subscribed package {purl}"

return JsonResponse({"status": "success", "message": message})
return HttpResponseBadRequest()


@method_decorator(has_valid_header, name="dispatch")
class PackageInbox(View):
def get(self, request, *args, **kwargs):
Expand Down Expand Up @@ -736,7 +756,7 @@ def get(self, request, *args, **kwargs):
(or at least the ones you're authorized to see).
(client-to-server and/or server-to-server)"""

actor = Package.objects.get(purl=kwargs["purl_string"])
actor = get_object_or_404(Package, purl=kwargs["purl_string"])
return JsonResponse(
{"notes": ap_collection(actor.notes)},
content_type=AP_CONTENT_TYPE,
Expand Down
8 changes: 7 additions & 1 deletion federatedcode/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
# See https://github.com/nexB/federatedcode for support or download.
# See https://aboutcode.org for more information about AboutCode.org OSS projects.
#

from django.conf import settings
from django.conf.urls.static import static
from django.contrib import admin
from django.urls import include
from django.urls import path
Expand All @@ -28,6 +28,7 @@
from fedcode.views import PersonSignUp
from fedcode.views import PersonUpdateView
from fedcode.views import PersonView
from fedcode.views import RemoteUserSubscribe
from fedcode.views import RepositoryListView
from fedcode.views import ReviewListView
from fedcode.views import ReviewView
Expand Down Expand Up @@ -92,6 +93,11 @@
path("api/v0/users/@<str:username>/inbox", UserInbox.as_view(), name="user-inbox"),
path("api/v0/users/@<str:username>/outbox", UserOutbox.as_view(), name="user-outbox"),
path("api/v0/purls/@<path:purl_string>/inbox", PackageInbox.as_view(), name="purl-inbox"),
path(
"api/v0/users/@<str:username>/subscribe",
RemoteUserSubscribe.as_view(),
name="purl-subscribe",
),
path(
"api/v0/purls/@<path:purl_string>/outbox",
PackageOutbox.as_view(),
Expand Down
2 changes: 1 addition & 1 deletion pyproject-aboutcode.federatedcode.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ classifiers = [

dependencies = [
"packageurl_python >= 0.15.6",
"aboutcode.hashid>=0.1.0",
"aboutcode.hashid>=0.2.0",
"python-dotenv>=1.0.1",
"click>=8.1.7",
"requests>=2.32.3",
Expand Down
5 changes: 5 additions & 0 deletions tests/test_ap_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -209,12 +209,14 @@ def test_get_user_inbox(person, vulnerability, review, package):
"type": "Note",
"author": "pkg:maven/org.apache.logging@127.0.0.1:8000",
"content": "yaml data1",
"update_date": str(note1.updated_at),
},
{
"id": f"https://127.0.0.1:8000/notes/{note2.id}",
"type": "Note",
"author": "pkg:maven/org.apache.logging@127.0.0.1:8000",
"content": "yaml data2",
"update_date": str(note2.updated_at),
},
],
},
Expand Down Expand Up @@ -282,6 +284,7 @@ def test_get_user_outbox(person, vulnerability, review, note):
"content": note.content,
"id": f"https://127.0.0.1:8000/notes/{note.id}",
"type": "Note",
"update_date": str(note.updated_at),
}
],
},
Expand Down Expand Up @@ -383,6 +386,7 @@ def test_get_package_inbox(package, service):
"[] fixing_vulnerabilities: []",
"id": f"https://127.0.0.1:8000/notes/{note1.id}",
"type": "Note",
"update_date": str(note1.updated_at),
}
],
"totalItems": 1,
Expand Down Expand Up @@ -413,6 +417,7 @@ def test_get_package_outbox(service, package):
"content": "yaml data1",
"id": f"https://127.0.0.1:8000/notes/{note1.id}",
"type": "Note",
"update_date": str(note1.updated_at),
}
],
"totalItems": 1,
Expand Down
2 changes: 2 additions & 0 deletions tests/test_vocabulary_toap.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ def test_objects_to_ap(repo, review, vulnerability, note, rep, mute_post_save_si
"id": f"https://127.0.0.1:8000/notes/{note.id}",
"author": note.acct,
"content": note.content,
"update_date": str(note.updated_at),
}

assert rep.to_ap == {
Expand All @@ -168,5 +169,6 @@ def test_objects_to_ap(repo, review, vulnerability, note, rep, mute_post_save_si
"id": f"https://127.0.0.1:8000/notes/{note.id}",
"author": note.acct,
"content": note.content,
"update_date": str(note.updated_at),
},
}