From c8f436cd4e30ee230a11d3c6f52f4b49d8f49850 Mon Sep 17 00:00:00 2001 From: Jason Robinson Date: Mon, 5 Sep 2016 22:08:13 +0300 Subject: [PATCH] Make to_user optional in handle_create_payload Public content does not require a recipient. --- CHANGELOG.md | 6 ++++++ federation/outbound.py | 8 ++++---- federation/protocols/diaspora/protocol.py | 18 ++++++++++++------ .../tests/protocols/diaspora/test_diaspora.py | 2 +- federation/tests/test_outbound.py | 8 +++----- 5 files changed, 26 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8fe3a1f..9229986 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## [unreleased] + +## Breaking changes +- `federation.outbound.handle_create_payload` parameter `to_user` is now optional. Public posts don't need a recipient. This also affects Diaspora protocol `build_send` method where the change is reflected similarly. [#43](https://github.com/jaywink/social-federation/pull/43) + - In practise this means the signature has changed for `handle_create_payload` and `build_send` from **`from_user, to_user, entity`** to **`entity, from_user, to_user=None`**. + ## [0.4.1] - 2016-09-04 ## Fixes diff --git a/federation/outbound.py b/federation/outbound.py index b490a60..617495b 100644 --- a/federation/outbound.py +++ b/federation/outbound.py @@ -3,16 +3,16 @@ from federation.entities.diaspora.mappers import get_outbound_entity from federation.protocols.diaspora.protocol import Protocol -def handle_create_payload(from_user, to_user, entity): +def handle_create_payload(entity, from_user, to_user=None): """Create a payload with the correct protocol. Since we don't know the protocol, we need to first query the recipient. However, for a PoC implementation, supporting only Diaspora, we're going to assume that for now. Args: - from_user (obj) - User sending the object - to_user (obj) - Contact entry to send to entity (obj) - Entity object to send + from_user (obj) - User sending the object + to_user (obj) - Contact entry to send to (required for non-public content) `from_user` must have `private_key` and `handle` attributes. `to_user` must have `key` attribute. @@ -20,5 +20,5 @@ def handle_create_payload(from_user, to_user, entity): # Just use Diaspora protocol for now protocol = Protocol() outbound_entity = get_outbound_entity(entity) - data = protocol.build_send(from_user=from_user, to_user=to_user, entity=outbound_entity) + data = protocol.build_send(entity=outbound_entity, from_user=from_user, to_user=to_user) return data diff --git a/federation/protocols/diaspora/protocol.py b/federation/protocols/diaspora/protocol.py index 7804ea4..acbc14d 100644 --- a/federation/protocols/diaspora/protocol.py +++ b/federation/protocols/diaspora/protocol.py @@ -182,11 +182,11 @@ class Protocol(BaseProtocol): else: return data[0:-data[-1]] - def build_send(self, from_user, to_user, entity, *args, **kwargs): + def build_send(self, entity, from_user, to_user=None, *args, **kwargs): """Build POST data for sending out to remotes.""" xml = entity.to_xml() self.init_message(xml, from_user.handle, from_user.private_key) - xml = self.create_salmon_envelope(to_user.key) + xml = self.create_salmon_envelope(to_user) return {'xml': xml} def init_message(self, message, author_username, private_key): @@ -319,25 +319,31 @@ class Protocol(BaseProtocol): to_encrypt = self.pkcs7_pad(self.create_payload(), AES.block_size) return self.inner_encrypter.encrypt(to_encrypt) - def create_salmon_envelope(self, recipient_public_key): + def create_salmon_envelope(self, recipient): """ Build the whole message, pulling together the encrypted payload and the encrypted header. Selected elements are signed by the author so that tampering can be detected. + + Args: + recipient - Recipient object which must have public key as `key` + + Returns: + XML document as string """ nsmap = { None: PROTOCOL_NS, 'me': 'http://salmon-protocol.org/ns/magic-env' } doc = etree.Element("{%s}diaspora" % nsmap[None], nsmap=nsmap) - if recipient_public_key: - doc.append(self.create_encrypted_header(recipient_public_key)) + if recipient: + doc.append(self.create_encrypted_header(recipient.key)) else: doc.append(self.create_public_header()) env = etree.SubElement(doc, "{%s}env" % nsmap["me"]) etree.SubElement(env, "{%s}encoding" % nsmap["me"]).text = 'base64url' etree.SubElement(env, "{%s}alg" % nsmap["me"]).text = 'RSA-SHA256' - if recipient_public_key: + if recipient: payload = urlsafe_b64encode(b64encode( self.create_encrypted_payload())).decode("ascii") else: diff --git a/federation/tests/protocols/diaspora/test_diaspora.py b/federation/tests/protocols/diaspora/test_diaspora.py index b14baf8..73d65be 100644 --- a/federation/tests/protocols/diaspora/test_diaspora.py +++ b/federation/tests/protocols/diaspora/test_diaspora.py @@ -150,6 +150,6 @@ class TestDiasporaProtocol(DiasporaTestBase): mock_entity_xml = Mock() entity = Mock(to_xml=Mock(return_value=mock_entity_xml)) from_user = Mock(handle="foobar", private_key="barfoo") - data = protocol.build_send(from_user, Mock(), entity) + data = protocol.build_send(entity, from_user) mock_init_message.assert_called_once_with(mock_entity_xml, from_user.handle, from_user.private_key) assert data == {"xml": "xmldata"} diff --git a/federation/tests/test_outbound.py b/federation/tests/test_outbound.py index 5754c70..f9a78ca 100644 --- a/federation/tests/test_outbound.py +++ b/federation/tests/test_outbound.py @@ -13,16 +13,14 @@ class TestHandleCreatePayloadBuildsAPayload(object): mock_protocol = Mock() mock_protocol_class.return_value = mock_protocol from_user = Mock() - to_user = Mock() entity = DiasporaPost() - handle_create_payload(from_user, to_user, entity) - mock_protocol.build_send.assert_called_once_with(from_user=from_user, to_user=to_user, entity=entity) + handle_create_payload(entity, from_user) + mock_protocol.build_send.assert_called_once_with(entity=entity, from_user=from_user, to_user=None) @patch("federation.outbound.get_outbound_entity") def test_handle_create_payload_calls_get_outbound_entity(self, mock_get_outbound_entity): mock_get_outbound_entity.return_value = DiasporaPost() from_user = Mock(private_key=RSA.generate(2048), handle="foobar@domain.tld") - to_user = Mock(key=RSA.generate(2048).publickey()) entity = DiasporaPost() - handle_create_payload(from_user, to_user, entity) + handle_create_payload(entity, from_user) assert mock_get_outbound_entity.called