From 65456c23b83c53ccbdc91b48fc28ae9cfdf89f70 Mon Sep 17 00:00:00 2001 From: Wassilios Lytras Date: Mon, 10 Jan 2022 08:17:33 +0100 Subject: [PATCH 1/2] Add callback function for partnerships, that takes precedence over individual org and partner lookup. --- pyas2lib/as2.py | 26 ++++++++++++++++++++++---- pyas2lib/tests/test_basic.py | 29 +++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 4 deletions(-) diff --git a/pyas2lib/as2.py b/pyas2lib/as2.py index 8b454f3..71b099d 100644 --- a/pyas2lib/as2.py +++ b/pyas2lib/as2.py @@ -513,7 +513,14 @@ def _decompress_data(self, payload): return False, payload - def parse(self, raw_content, find_org_cb, find_partner_cb, find_message_cb=None): + def parse( + self, + raw_content, + find_org_cb, + find_partner_cb, + find_message_cb=None, + find_partnership_cb=None, + ): """Function parses the RAW AS2 message; decrypts, verifies and decompresses it and extracts the payload. @@ -533,6 +540,12 @@ def parse(self, raw_content, find_org_cb, find_partner_cb, find_message_cb=None) order to check for duplicates. The message id and partner id is passed as arguments to it. + :param find_partnership_cb: + An optional callback that return Organization object and + Partner object if exist. The as2-to and as2-from header value + are passed as an argument to it. Takes precedence over find_org_cb + and find_partner_cb. + :return: A three element tuple containing (status, (exception, traceback) , mdn). The status is a string indicating the status of the @@ -554,12 +567,17 @@ def parse(self, raw_content, find_org_cb, find_partner_cb, find_message_cb=None) try: # Get the organization and partner for this transmission org_id = unquote_as2name(as2_headers["as2-to"]) - self.receiver = find_org_cb(org_id) + partner_id = unquote_as2name(as2_headers["as2-from"]) + + if find_partnership_cb: + self.receiver, self.sender = find_partnership_cb(org_id, partner_id) + else: + self.receiver = find_org_cb(org_id) + self.sender = find_partner_cb(partner_id) + if not self.receiver: raise PartnerNotFound(f"Unknown AS2 organization with id {org_id}") - partner_id = unquote_as2name(as2_headers["as2-from"]) - self.sender = find_partner_cb(partner_id) if not self.sender: raise PartnerNotFound(f"Unknown AS2 partner with id {partner_id}") diff --git a/pyas2lib/tests/test_basic.py b/pyas2lib/tests/test_basic.py index 3c2c182..dc857d4 100644 --- a/pyas2lib/tests/test_basic.py +++ b/pyas2lib/tests/test_basic.py @@ -183,8 +183,37 @@ def test_encrypted_signed_compressed_message(self): self.assertEqual(out_message.mic, in_message.mic) self.assertEqual(self.test_data.splitlines(), in_message.content.splitlines()) + def test_encrypted_signed_message_partnership(self): + """Test Encrypted Signed Uncompressed Message with Partnership""" + + # Build an As2 message to be transmitted to partner + self.partner.sign = True + self.partner.encrypt = True + out_message = as2.Message(self.org, self.partner) + out_message.build(self.test_data) + raw_out_message = out_message.headers_str + b"\r\n" + out_message.content + + # Parse the generated AS2 message as the partner + in_message = as2.Message() + status, _, _ = in_message.parse( + raw_out_message, + find_org_cb=lambda x: None, + find_partner_cb=lambda x: None, + find_partnership_cb=self.find_partnership, + ) + + # Compare the mic contents of the input and output messages + self.assertEqual(status, "processed") + self.assertTrue(in_message.signed) + self.assertTrue(in_message.encrypted) + self.assertEqual(out_message.mic, in_message.mic) + self.assertEqual(self.test_data.splitlines(), in_message.content.splitlines()) + def find_org(self, as2_id): return self.org def find_partner(self, as2_id): return self.partner + + def find_partnership(self, as2_org, as2_partner): + return self.org, self.partner From 1e563b21b7241162d23c495c5d3715dc716fea0f Mon Sep 17 00:00:00 2001 From: Wassilios Lytras Date: Wed, 2 Feb 2022 10:55:33 +0100 Subject: [PATCH 2/2] The find_org_cb and find_partner_cb will be made optional with either these 2 or find_org_partner_cb needing to be set --- pyas2lib/as2.py | 40 ++++++++++++++++++++++++------------ pyas2lib/tests/test_basic.py | 6 ++---- 2 files changed, 29 insertions(+), 17 deletions(-) diff --git a/pyas2lib/as2.py b/pyas2lib/as2.py index 71b099d..3bb48bc 100644 --- a/pyas2lib/as2.py +++ b/pyas2lib/as2.py @@ -516,10 +516,10 @@ def _decompress_data(self, payload): def parse( self, raw_content, - find_org_cb, - find_partner_cb, + find_org_cb=None, + find_partner_cb=None, find_message_cb=None, - find_partnership_cb=None, + find_org_partner_cb=None, ): """Function parses the RAW AS2 message; decrypts, verifies and decompresses it and extracts the payload. @@ -528,23 +528,25 @@ def parse( A byte string of the received HTTP headers followed by the body. :param find_org_cb: - A callback the returns an Organization object if exists. The - as2-to header value is passed as an argument to it. + A conditional callback the returns an Organization object if exists. The + as2-to header value is passed as an argument to it. Must be provided + when find_partner_cb is provided and find_org_partner_cb is None :param find_partner_cb: - A callback the returns an Partner object if exists. The - as2-from header value is passed as an argument to it. + An conditional callback the returns an Partner object if exists. The + as2-from header value is passed as an argument to it. Must be provided + when find_org_cb is provided and find_org_partner_cb is None. :param find_message_cb: An optional callback the returns an Message object if exists in order to check for duplicates. The message id and partner id is passed as arguments to it. - :param find_partnership_cb: - An optional callback that return Organization object and + :param find_org_partner_cb: + A conditional callback that return Organization object and Partner object if exist. The as2-to and as2-from header value - are passed as an argument to it. Takes precedence over find_org_cb - and find_partner_cb. + are passed as an argument to it. Must be provided + when find_org_cb and find_org_partner_cb is None. :return: A three element tuple containing (status, (exception, traceback) @@ -554,6 +556,18 @@ def parse( the partner did not request it. """ + # Validate passed arguments + if not any( + [ + find_org_cb and find_partner_cb and not find_org_partner_cb, + find_org_partner_cb and not find_partner_cb and not find_org_cb, + ] + ): + raise TypeError( + "Incorrect arguments passed: either find_org_cb and find_partner_cb " + "or only find_org_partner_cb must be passed." + ) + # Parse the raw MIME message and extract its content and headers status, detailed_status, exception, mdn = "processed", None, (None, None), None self.payload = parse_mime(raw_content) @@ -569,8 +583,8 @@ def parse( org_id = unquote_as2name(as2_headers["as2-to"]) partner_id = unquote_as2name(as2_headers["as2-from"]) - if find_partnership_cb: - self.receiver, self.sender = find_partnership_cb(org_id, partner_id) + if find_org_partner_cb: + self.receiver, self.sender = find_org_partner_cb(org_id, partner_id) else: self.receiver = find_org_cb(org_id) self.sender = find_partner_cb(partner_id) diff --git a/pyas2lib/tests/test_basic.py b/pyas2lib/tests/test_basic.py index dc857d4..04d97dd 100644 --- a/pyas2lib/tests/test_basic.py +++ b/pyas2lib/tests/test_basic.py @@ -197,9 +197,7 @@ def test_encrypted_signed_message_partnership(self): in_message = as2.Message() status, _, _ = in_message.parse( raw_out_message, - find_org_cb=lambda x: None, - find_partner_cb=lambda x: None, - find_partnership_cb=self.find_partnership, + find_org_partner_cb=self.find_org_partner, ) # Compare the mic contents of the input and output messages @@ -215,5 +213,5 @@ def find_org(self, as2_id): def find_partner(self, as2_id): return self.partner - def find_partnership(self, as2_org, as2_partner): + def find_org_partner(self, as2_org, as2_partner): return self.org, self.partner