Commit 1aab2716 authored by Jean-Baptiste's avatar Jean-Baptiste

update: sign requests

parent 20ca4bf6
Pipeline #4983 failed with stage
in 43 seconds
from djangoldp_activitypub import models
from djangoldp_activitypub.security import signing, keys
from .objects import *
from .verbs import *
......@@ -5,19 +7,34 @@ from .verbs import *
class AP(object):
@classmethod
def accept(cls, instance):
remote_actor = json.loads(requests.get(instance.remote_id, headers={'Accept': 'application/activity+json'}).content)
remote_actor = json.loads(
requests.get(instance.remote_id, headers={'Accept': 'application/activity+json'}).content)
local_actor = AP.get_actor(instance.local_id)
auth = signing.get_auth(local_actor.private_key, local_actor.private_key_id)
response = requests.post(remote_actor['inbox'],
json={"@context": "https://www.w3.org/ns/activitystreams",
"type": "Accept",
"id": "https://social.lemee.co/@jb#undo/stuff/4",
"actor": instance.local_id,
"object": {
"id": instance.aid,
"type": "Follow",
"actor": instance.remote_id,
"object": instance.local_id,
}},
headers={'Accept': 'application/activity+json', 'content-type': "application/activity+json"})
if response.status_code < 300:
json={"@context": "https://www.w3.org/ns/activitystreams",
"type": "Accept",
"id": "https://social.lemee.co/@jb#undo/stuff/4",
"actor": instance.local_id,
"object": {
"id": instance.aid,
"type": "Follow",
"actor": instance.remote_id,
"object": instance.local_id,
}},
headers={'Accept': 'application/activity+json',
'Content-type': "application/activity+json"},
auth=auth )
if response.ok:
instance.confirmed = True
instance.save()
@classmethod
def get_actor(cls, actor_id):
if not models.Actor.objects.filter(actor_id=actor_id).exists():
private_key, public_key = keys.get_key_pair(2048)
models.Actor.objects.create(actor_id=actor_id, public_key=public_key, private_key=private_key)
return models.Actor.objects.get(actor_id=actor_id)
......@@ -50,3 +50,8 @@ class Actor(Model):
actor_id = URLField(unique=True)
public_key = TextField(max_length=5000, null=True, blank=True)
private_key = TextField(max_length=5000, null=True, blank=True)
@property
def private_key_id(self):
return "{}#main-key".format(self.actor_id)
import datetime
import logging
import pytz
from django import forms
from django.utils import timezone
from django.utils.http import parse_http_date
import requests
import requests_http_signature
logger = logging.getLogger(__name__)
# the request Date should be between now - 30s and now + 30s
DATE_HEADER_VALID_FOR = 30
def verify_date(raw_date):
if not raw_date:
raise forms.ValidationError("Missing date header")
try:
ts = parse_http_date(raw_date)
except ValueError as e:
raise forms.ValidationError(str(e))
dt = datetime.datetime.utcfromtimestamp(ts)
dt = dt.replace(tzinfo=pytz.utc)
delta = datetime.timedelta(seconds=DATE_HEADER_VALID_FOR)
now = timezone.now()
if dt < now - delta or dt > now + delta:
raise forms.ValidationError(
"Request Date is too far in the future or in the past"
)
return dt
def verify(request, public_key):
verify_date(request.headers.get("Date"))
return requests_http_signature.HTTPSignatureAuth.verify(
request, key_resolver=lambda **kwargs: public_key, use_auth_header=False
)
def get_auth(private_key, private_key_id):
return requests_http_signature.HTTPSignatureAuth(
use_auth_header=False,
headers=["(request-target)", "user-agent", "host", "date"],
algorithm="rsa-sha256",
key=private_key.encode("utf-8"),
key_id=private_key_id,
)
......@@ -9,10 +9,11 @@ from rest_framework.viewsets import GenericViewSet
from djangoldp.models import Model
from djangoldp.views import NoCSRFAuthentication
from djangoldp_activitypub import activities, keys
from djangoldp_activitypub.activities import as_activitystream
from djangoldp_activitypub import activities
from djangoldp_activitypub.activities import as_activitystream, AP
from djangoldp_activitypub.activities.errors import ASDecodeError
from djangoldp_activitypub.models import Following, Follower, Activity, Actor
from djangoldp_activitypub.security import keys
def noop(*args, **kwargs):
......@@ -83,12 +84,7 @@ class ActorView(ActivityPubView):
def to_activity_stream(self, container, name, preferredUsername):
actor_id = self.uri(container)
if not Actor.objects.filter(actor_id=actor_id).exists():
private_key, public_key = keys.get_key_pair(2048)
Actor.objects.create(actor_id=actor_id, public_key=public_key, private_key=private_key)
actor = Actor.objects.get(actor_id=actor_id)
actor = AP.get_actor(actor_id)
activity = {
"@context": [
"https://www.w3.org/ns/activitystreams",
......@@ -102,7 +98,7 @@ class ActorView(ActivityPubView):
"outbox": self.uri("{}/{}".format(container, "outbox")),
"inbox": self.uri("{}/{}".format(container, "inbox")),
"publicKey": {
"id": "{}#main-key".format(actor.actor_id),
"id": actor.private_key_id,
"owner": actor.actor_id,
"publicKeyPem": actor.public_key,
},
......
......@@ -12,6 +12,7 @@ packages = find:
install_requires =
djangoldp~=0.0
cryptography>=2,<3
requests-http-signature~=0.0
[semantic_release]
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment