Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
jovuit
funkwhale
Commits
6aa6f1d8
Verified
Commit
6aa6f1d8
authored
Apr 03, 2018
by
Eliot Berriot
Browse files
Test actor can now follow back
parent
2f6d3ae1
Changes
5
Hide whitespace changes
Inline
Side-by-side
api/funkwhale_api/federation/activity.py
View file @
6aa6f1d8
...
...
@@ -83,3 +83,37 @@ def deliver(activity, on_behalf_of, to=[]):
)
response
.
raise_for_status
()
logger
.
debug
(
'Remote answered with %s'
,
response
.
status_code
)
def
get_follow
(
follow_id
,
follower
,
followed
):
return
{
'@context'
:
[
'https://www.w3.org/ns/activitystreams'
,
'https://w3id.org/security/v1'
,
{}
],
'actor'
:
follower
.
url
,
'id'
:
follower
.
url
+
'#follows/{}'
.
format
(
follow_id
),
'object'
:
followed
.
url
,
'type'
:
'Follow'
}
def
get_accept_follow
(
accept_id
,
accept_actor
,
follow
,
follow_actor
):
return
{
"@context"
:
[
"https://www.w3.org/ns/activitystreams"
,
"https://w3id.org/security/v1"
,
{}
],
"id"
:
accept_actor
.
url
+
'#accepts/follows/{}'
.
format
(
accept_id
),
"type"
:
"Accept"
,
"actor"
:
accept_actor
.
url
,
"object"
:
{
"id"
:
follow
[
'id'
],
"type"
:
"Follow"
,
"actor"
:
follow_actor
.
url
,
"object"
:
accept_actor
.
url
},
}
api/funkwhale_api/federation/actors.py
View file @
6aa6f1d8
import
logging
import
requests
import
uuid
import
xml
from
django.conf
import
settings
...
...
@@ -98,7 +99,7 @@ class SystemActor(object):
raise
NotImplementedError
def
post_inbox
(
self
,
data
,
actor
=
None
):
r
aise
NotImplementedErr
or
r
eturn
self
.
handle
(
data
,
actor
=
act
or
)
def
get_outbox
(
self
,
data
,
actor
=
None
):
raise
NotImplementedError
...
...
@@ -106,6 +107,31 @@ class SystemActor(object):
def
post_outbox
(
self
,
data
,
actor
=
None
):
raise
NotImplementedError
def
handle
(
self
,
data
,
actor
=
None
):
"""
Main entrypoint for handling activities posted to the
actor's inbox
"""
logger
.
info
(
'Received activity on %s inbox'
,
self
.
id
)
if
actor
is
None
:
raise
PermissionDenied
(
'Actor not authenticated'
)
serializer
=
serializers
.
ActivitySerializer
(
data
=
data
,
context
=
{
'actor'
:
actor
})
serializer
.
is_valid
(
raise_exception
=
True
)
ac
=
serializer
.
data
try
:
handler
=
getattr
(
self
,
'handle_{}'
.
format
(
ac
[
'type'
].
lower
()))
except
(
KeyError
,
AttributeError
):
logger
.
debug
(
'No handler for activity %s'
,
ac
[
'type'
])
return
return
handler
(
ac
,
actor
)
class
LibraryActor
(
SystemActor
):
id
=
'library'
...
...
@@ -147,23 +173,6 @@ class TestActor(SystemActor):
"orderedItems"
:
[]
}
def
post_inbox
(
self
,
data
,
actor
=
None
):
if
actor
is
None
:
raise
PermissionDenied
(
'Actor not authenticated'
)
serializer
=
serializers
.
ActivitySerializer
(
data
=
data
,
context
=
{
'actor'
:
actor
})
serializer
.
is_valid
(
raise_exception
=
True
)
ac
=
serializer
.
validated_data
logger
.
info
(
'Received activity on %s inbox'
,
self
.
id
)
if
ac
[
'type'
]
==
'Create'
and
ac
[
'object'
][
'type'
]
==
'Note'
:
# we received a toot \o/
command
=
self
.
parse_command
(
ac
[
'object'
][
'content'
])
logger
.
debug
(
'Parsed command: %s'
,
command
)
if
command
==
'ping'
:
self
.
handle_ping
(
ac
,
actor
)
def
parse_command
(
self
,
message
):
"""
Remove any links or fancy markup to extract /command from
...
...
@@ -175,7 +184,16 @@ class TestActor(SystemActor):
except
IndexError
:
return
def
handle_ping
(
self
,
ac
,
sender
):
def
handle_create
(
self
,
ac
,
sender
):
if
ac
[
'object'
][
'type'
]
!=
'Note'
:
return
# we received a toot \o/
command
=
self
.
parse_command
(
ac
[
'object'
][
'content'
])
logger
.
debug
(
'Parsed command: %s'
,
command
)
if
command
!=
'ping'
:
return
now
=
timezone
.
now
()
test_actor
=
self
.
get_actor_instance
()
reply_url
=
'https://{}/activities/note/{}'
.
format
(
...
...
@@ -221,6 +239,31 @@ class TestActor(SystemActor):
to
=
[
ac
[
'actor'
]],
on_behalf_of
=
test_actor
)
def
handle_follow
(
self
,
ac
,
sender
):
# on a follow we:
# 1. send the accept answer
# 2. follow back
test_actor
=
self
.
get_actor_instance
()
accept_uuid
=
uuid
.
uuid4
()
accept
=
activity
.
get_accept_follow
(
accept_id
=
accept_uuid
,
accept_actor
=
test_actor
,
follow
=
ac
,
follow_actor
=
sender
)
activity
.
deliver
(
accept
,
to
=
[
ac
[
'actor'
]],
on_behalf_of
=
test_actor
)
follow_uuid
=
uuid
.
uuid4
()
follow
=
activity
.
get_follow
(
follow_id
=
follow_uuid
,
follower
=
test_actor
,
followed
=
sender
)
activity
.
deliver
(
follow
,
to
=
[
ac
[
'actor'
]],
on_behalf_of
=
test_actor
)
SYSTEM_ACTORS
=
{
'library'
:
LibraryActor
(),
'test'
:
TestActor
(),
...
...
api/funkwhale_api/federation/factories.py
View file @
6aa6f1d8
...
...
@@ -89,3 +89,20 @@ class NoteFactory(factory.Factory):
class
Meta
:
model
=
dict
@
registry
.
register
(
name
=
'federation.Activity'
)
class
ActivityFactory
(
factory
.
Factory
):
type
=
'Create'
id
=
factory
.
Faker
(
'url'
)
published
=
factory
.
LazyFunction
(
lambda
:
timezone
.
now
().
isoformat
()
)
actor
=
factory
.
Faker
(
'url'
)
object
=
factory
.
SubFactory
(
NoteFactory
,
actor
=
factory
.
SelfAttribute
(
'..actor'
),
published
=
factory
.
SelfAttribute
(
'..published'
))
class
Meta
:
model
=
dict
api/funkwhale_api/federation/serializers.py
View file @
6aa6f1d8
...
...
@@ -120,7 +120,9 @@ class ActivitySerializer(serializers.Serializer):
type
=
value
[
'type'
]
except
KeyError
:
raise
serializers
.
ValidationError
(
'Missing object type'
)
except
TypeError
:
# probably a URL
return
value
try
:
object_serializer
=
OBJECT_SERIALIZERS
[
type
]
except
KeyError
:
...
...
api/tests/federation/test_actors.py
View file @
6aa6f1d8
import
pytest
import
uuid
from
django.urls
import
reverse
from
django.utils
import
timezone
...
...
@@ -127,7 +128,7 @@ def test_test_post_outbox_validates_actor(nodb_factories):
assert
msg
in
exc_info
.
value
def
test_test_post_
out
box_handles_create_note
(
def
test_test_post_
in
box_handles_create_note
(
settings
,
mocker
,
factories
):
deliver
=
mocker
.
patch
(
'funkwhale_api.federation.activity.deliver'
)
...
...
@@ -238,3 +239,77 @@ def test_library_actor_manually_approves_based_on_setting(
settings
.
FEDERATION_MUSIC_NEEDS_APPROVAL
=
value
library_conf
=
actors
.
SYSTEM_ACTORS
[
'library'
]
assert
library_conf
.
manually_approves_followers
is
value
def
test_system_actor_handle
(
mocker
,
nodb_factories
):
handler
=
mocker
.
patch
(
'funkwhale_api.federation.actors.TestActor.handle_create'
)
actor
=
nodb_factories
[
'federation.Actor'
]()
activity
=
nodb_factories
[
'federation.Activity'
](
type
=
'Create'
,
actor
=
actor
.
url
)
serializer
=
serializers
.
ActivitySerializer
(
data
=
activity
)
assert
serializer
.
is_valid
()
actors
.
SYSTEM_ACTORS
[
'test'
].
handle
(
activity
,
actor
)
handler
.
assert_called_once_with
(
serializer
.
data
,
actor
)
def
test_test_actor_handles_follow
(
settings
,
mocker
,
factories
):
deliver
=
mocker
.
patch
(
'funkwhale_api.federation.activity.deliver'
)
actor
=
factories
[
'federation.Actor'
]()
now
=
timezone
.
now
()
mocker
.
patch
(
'django.utils.timezone.now'
,
return_value
=
now
)
test_actor
=
actors
.
SYSTEM_ACTORS
[
'test'
].
get_actor_instance
()
data
=
{
'actor'
:
actor
.
url
,
'type'
:
'Follow'
,
'id'
:
'http://test.federation/user#follows/267'
,
'object'
:
test_actor
.
url
,
}
uid
=
uuid
.
uuid4
()
mocker
.
patch
(
'uuid.uuid4'
,
return_value
=
uid
)
expected_accept
=
{
"@context"
:
[
"https://www.w3.org/ns/activitystreams"
,
"https://w3id.org/security/v1"
,
{}
],
"id"
:
test_actor
.
url
+
'#accepts/follows/{}'
.
format
(
uid
),
"type"
:
"Accept"
,
"actor"
:
test_actor
.
url
,
"object"
:
{
"id"
:
data
[
'id'
],
"type"
:
"Follow"
,
"actor"
:
actor
.
url
,
"object"
:
test_actor
.
url
},
}
expected_follow
=
{
'@context'
:
[
'https://www.w3.org/ns/activitystreams'
,
'https://w3id.org/security/v1'
,
{}
],
'actor'
:
test_actor
.
url
,
'id'
:
test_actor
.
url
+
'#follows/{}'
.
format
(
uid
),
'object'
:
actor
.
url
,
'type'
:
'Follow'
}
actors
.
SYSTEM_ACTORS
[
'test'
].
post_inbox
(
data
,
actor
=
actor
)
expected_calls
=
[
mocker
.
call
(
expected_accept
,
to
=
[
actor
.
url
],
on_behalf_of
=
test_actor
,
),
mocker
.
call
(
expected_follow
,
to
=
[
actor
.
url
],
on_behalf_of
=
test_actor
,
)
]
deliver
.
assert_has_calls
(
expected_calls
)
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment