kopia lustrzana https://github.com/snarfed/bridgy-fed
incoming AP: when we can't load the keyId, store a deleted Object
prevents re-fetching the same failing keyId actor over and over when we get the same Delete actor activity sent to all of our inboxespull/470/head
rodzic
f7d1a3fd96
commit
e6be9cabb8
|
@ -152,7 +152,7 @@ class ActivityPub(Protocol):
|
||||||
logger.info(f'Headers: {json_dumps(dict(request.headers), indent=2)}')
|
logger.info(f'Headers: {json_dumps(dict(request.headers), indent=2)}')
|
||||||
|
|
||||||
# parse_signature_header lower-cases all keys
|
# parse_signature_header lower-cases all keys
|
||||||
keyId = parse_signature_header(sig).get('keyid')
|
keyId = fragmentless(parse_signature_header(sig).get('keyid'))
|
||||||
if not keyId:
|
if not keyId:
|
||||||
error('HTTP Signature missing keyId', status=401)
|
error('HTTP Signature missing keyId', status=401)
|
||||||
|
|
||||||
|
@ -165,16 +165,21 @@ class ActivityPub(Protocol):
|
||||||
error('Invalid Digest header, required for HTTP Signature', status=401)
|
error('Invalid Digest header, required for HTTP Signature', status=401)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
key_actor = cls.get_object(keyId).as2
|
key_actor = cls.get_object(keyId)
|
||||||
except BadGateway:
|
except BadGateway:
|
||||||
obj_id = as1.get_object(activity).get('id')
|
obj_id = as1.get_object(activity).get('id')
|
||||||
if (activity.get('type') == 'Delete' and obj_id and
|
if (activity.get('type') == 'Delete' and obj_id and
|
||||||
fragmentless(keyId) == fragmentless(obj_id)):
|
keyId == fragmentless(obj_id)):
|
||||||
logging.info("Object/actor being deleted is also keyId; ignoring")
|
logging.info('Object/actor being deleted is also keyId')
|
||||||
abort(202, 'OK')
|
key_actor = Object(id=keyId, source_protocol='activitypub', deleted=True)
|
||||||
raise
|
key_actor.put()
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
|
||||||
key = key_actor.get("publicKey", {}).get('publicKeyPem')
|
if key_actor.deleted:
|
||||||
|
abort(202, f'Ignoring, signer {keyId} is already deleted')
|
||||||
|
|
||||||
|
key = key_actor.as2.get("publicKey", {}).get('publicKeyPem')
|
||||||
logger.info(f'Verifying signature for {request.path} with key {key}')
|
logger.info(f'Verifying signature for {request.path} with key {key}')
|
||||||
try:
|
try:
|
||||||
verified = HeaderVerifier(request.headers, key,
|
verified = HeaderVerifier(request.headers, key,
|
||||||
|
|
|
@ -386,7 +386,7 @@ class Protocol:
|
||||||
id = util.fragmentless(id)
|
id = util.fragmentless(id)
|
||||||
logger.info(f'Loading Object {id}')
|
logger.info(f'Loading Object {id}')
|
||||||
obj = models.Object.get_by_id(id)
|
obj = models.Object.get_by_id(id)
|
||||||
if obj and obj.as1:
|
if obj and (obj.as1 or obj.deleted):
|
||||||
logger.info(' got from datastore')
|
logger.info(' got from datastore')
|
||||||
return obj
|
return obj
|
||||||
|
|
||||||
|
|
|
@ -234,7 +234,8 @@ class ActivityPubTest(testutil.TestCase):
|
||||||
'owner': 'http://own/er',
|
'owner': 'http://own/er',
|
||||||
'publicKeyPem': self.user.public_pem().decode(),
|
'publicKeyPem': self.user.public_pem().decode(),
|
||||||
},
|
},
|
||||||
}).put()
|
})
|
||||||
|
self.key_id_obj.put()
|
||||||
|
|
||||||
def assert_object(self, id, **props):
|
def assert_object(self, id, **props):
|
||||||
return super().assert_object(id, delivered_protocol='webmention', **props)
|
return super().assert_object(id, delivered_protocol='webmention', **props)
|
||||||
|
@ -829,7 +830,7 @@ class ActivityPubTest(testutil.TestCase):
|
||||||
def test_inbox_verify_http_signature(self, mock_common_log, mock_activitypub_log,
|
def test_inbox_verify_http_signature(self, mock_common_log, mock_activitypub_log,
|
||||||
_, mock_get, ___):
|
_, mock_get, ___):
|
||||||
# actor with a public key
|
# actor with a public key
|
||||||
self.key_id_obj.delete()
|
self.key_id_obj.key.delete()
|
||||||
Protocol.get_object.cache.clear()
|
Protocol.get_object.cache.clear()
|
||||||
mock_get.return_value = self.as2_resp({
|
mock_get.return_value = self.as2_resp({
|
||||||
**ACTOR,
|
**ACTOR,
|
||||||
|
@ -907,13 +908,23 @@ class ActivityPubTest(testutil.TestCase):
|
||||||
self.assertEqual('active', other.key.get().status)
|
self.assertEqual('active', other.key.get().status)
|
||||||
|
|
||||||
def test_delete_actor_not_fetchable(self, _, mock_get, ___):
|
def test_delete_actor_not_fetchable(self, _, mock_get, ___):
|
||||||
self.key_id_obj.delete()
|
self.key_id_obj.key.delete()
|
||||||
Protocol.get_object.cache.clear()
|
Protocol.get_object.cache.clear()
|
||||||
|
|
||||||
mock_get.return_value = requests_response(status=410)
|
mock_get.return_value = requests_response(status=410)
|
||||||
got = self.post('/inbox', json={**DELETE, 'object': 'http://my/key/id'})
|
got = self.post('/inbox', json={**DELETE, 'object': 'http://my/key/id'})
|
||||||
self.assertEqual(202, got.status_code)
|
self.assertEqual(202, got.status_code)
|
||||||
|
|
||||||
|
def test_delete_actor_empty_deleted_object(self, _, mock_get, ___):
|
||||||
|
self.key_id_obj.as2 = None
|
||||||
|
self.key_id_obj.deleted = True
|
||||||
|
self.key_id_obj.put()
|
||||||
|
Protocol.get_object.cache.clear()
|
||||||
|
|
||||||
|
got = self.post('/inbox', json={**DELETE, 'object': 'http://my/key/id'})
|
||||||
|
self.assertEqual(202, got.status_code)
|
||||||
|
mock_get.assert_not_called()
|
||||||
|
|
||||||
def test_delete_note(self, _, mock_get, ___):
|
def test_delete_note(self, _, mock_get, ___):
|
||||||
obj = Object(id='http://an/obj', as2={})
|
obj = Object(id='http://an/obj', as2={})
|
||||||
obj.put()
|
obj.put()
|
||||||
|
|
|
@ -71,3 +71,11 @@ class ProtocolTest(testutil.TestCase):
|
||||||
stored.put()
|
stored.put()
|
||||||
self.assert_entities_equal(stored, FakeProtocol.get_object('foo'))
|
self.assert_entities_equal(stored, FakeProtocol.get_object('foo'))
|
||||||
self.assertEqual([], FakeProtocol.fetched)
|
self.assertEqual([], FakeProtocol.fetched)
|
||||||
|
|
||||||
|
@patch('requests.get')
|
||||||
|
def test_get_object_empty_deleted(self, mock_get):
|
||||||
|
stored = Object(id='foo', deleted=True)
|
||||||
|
stored.put()
|
||||||
|
|
||||||
|
self.assert_entities_equal(stored, FakeProtocol.get_object('foo'))
|
||||||
|
mock_get.assert_not_called()
|
||||||
|
|
Ładowanie…
Reference in New Issue