Skip to content

Commit 40e321b

Browse files
authored
Add an option to send a partnership callback function.
* Fix github action build fail due to: https://stackoverflow.com/questions/71673404/importerror-cannot-import-name-unicodefun-from-click * Added partner setting to force canonicalize binary. * Formatted with black * Option to send a partnership callback option. * Raise missing partner/org errors also for partnership * Adding tests for invalid callback function combination
1 parent e4164db commit 40e321b

File tree

2 files changed

+104
-7
lines changed

2 files changed

+104
-7
lines changed

pyas2lib/as2.py

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -564,26 +564,39 @@ def _decompress_data(self, payload):
564564

565565
return False, payload
566566

567-
def parse(self, raw_content, find_org_cb, find_partner_cb, find_message_cb=None):
567+
def parse(
568+
self,
569+
raw_content,
570+
find_org_cb=None,
571+
find_partner_cb=None,
572+
find_message_cb=None,
573+
find_org_partner_cb=None,
574+
):
568575
"""Function parses the RAW AS2 message; decrypts, verifies and
569576
decompresses it and extracts the payload.
570577
571578
:param raw_content:
572579
A byte string of the received HTTP headers followed by the body.
573580
574581
:param find_org_cb:
575-
A callback the returns an Organization object if exists. The
582+
A conditional callback the returns an Organization object if exists. The
576583
as2-to header value is passed as an argument to it.
577584
578585
:param find_partner_cb:
579-
A callback the returns an Partner object if exists. The
586+
A conditional callback the returns a Partner object if exists. The
580587
as2-from header value is passed as an argument to it.
581588
582589
:param find_message_cb:
583-
An optional callback the returns an Message object if exists in
590+
An optional callback the returns a Message object if exists in
584591
order to check for duplicates. The message id and partner id is
585592
passed as arguments to it.
586593
594+
:param find_org_partner_cb:
595+
A conditional callback that return Organization object and
596+
Partner object if exist. The as2-to and as2-from header value
597+
are passed as an argument to it. Must be provided
598+
when find_org_cb and find_org_partner_cb is None.
599+
587600
:return:
588601
A three element tuple containing (status, (exception, traceback)
589602
, mdn). The status is a string indicating the status of the
@@ -592,6 +605,18 @@ def parse(self, raw_content, find_org_cb, find_partner_cb, find_message_cb=None)
592605
the partner did not request it.
593606
"""
594607

608+
# Validate passed arguments
609+
if not any(
610+
[
611+
find_org_cb and find_partner_cb and not find_org_partner_cb,
612+
find_org_partner_cb and not find_partner_cb and not find_org_cb,
613+
]
614+
):
615+
raise TypeError(
616+
"Incorrect arguments passed: either find_org_cb and find_partner_cb "
617+
"or only find_org_partner_cb must be passed."
618+
)
619+
595620
# Parse the raw MIME message and extract its content and headers
596621
status, detailed_status, exception, mdn = "processed", None, (None, None), None
597622
self.payload = parse_mime(raw_content)
@@ -605,12 +630,16 @@ def parse(self, raw_content, find_org_cb, find_partner_cb, find_message_cb=None)
605630
try:
606631
# Get the organization and partner for this transmission
607632
org_id = unquote_as2name(as2_headers["as2-to"])
608-
self.receiver = find_org_cb(org_id)
633+
partner_id = unquote_as2name(as2_headers["as2-from"])
634+
if find_org_partner_cb:
635+
self.receiver, self.sender = find_org_partner_cb(org_id, partner_id)
636+
elif find_org_cb and find_partner_cb:
637+
self.receiver = find_org_cb(org_id)
638+
self.sender = find_partner_cb(partner_id)
639+
609640
if not self.receiver:
610641
raise PartnerNotFound(f"Unknown AS2 organization with id {org_id}")
611642

612-
partner_id = unquote_as2name(as2_headers["as2-from"])
613-
self.sender = find_partner_cb(partner_id)
614643
if not self.sender:
615644
raise PartnerNotFound(f"Unknown AS2 partner with id {partner_id}")
616645

pyas2lib/tests/test_basic.py

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,30 @@ def test_encrypted_signed_compressed_message(self):
184184
self.assertEqual(out_message.mic, in_message.mic)
185185
self.assertEqual(self.test_data.splitlines(), in_message.content.splitlines())
186186

187+
def test_encrypted_signed_message_partnership(self):
188+
"""Test Encrypted Signed Uncompressed Message with Partnership"""
189+
190+
# Build an As2 message to be transmitted to partner
191+
self.partner.sign = True
192+
self.partner.encrypt = True
193+
out_message = as2.Message(self.org, self.partner)
194+
out_message.build(self.test_data)
195+
raw_out_message = out_message.headers_str + b"\r\n" + out_message.content
196+
197+
# Parse the generated AS2 message as the partner
198+
in_message = as2.Message()
199+
status, _, _ = in_message.parse(
200+
raw_out_message,
201+
find_org_partner_cb=self.find_org_partner,
202+
)
203+
204+
# Compare the mic contents of the input and output messages
205+
self.assertEqual(status, "processed")
206+
self.assertTrue(in_message.signed)
207+
self.assertTrue(in_message.encrypted)
208+
self.assertEqual(out_message.mic, in_message.mic)
209+
self.assertEqual(self.test_data.splitlines(), in_message.content.splitlines())
210+
187211
def test_plain_message_with_domain(self):
188212
"""Test Message building with an org domain"""
189213

@@ -224,8 +248,52 @@ def test_invalid_message_id_length_raises_error(self):
224248
in str(excinfo.value)
225249
)
226250

251+
def test_invalid_cb_function_passed(self):
252+
"""Checking allowed combination of CB functions"""
253+
254+
# Create AS2 message and parse with wrong combination of callback functions
255+
256+
as2_message = as2.Message()
257+
with pytest.raises(
258+
TypeError,
259+
match="Incorrect arguments passed: either find_org_cb and find_partner_cb "
260+
"or only find_org_partner_cb must be passed.",
261+
):
262+
263+
_, _, _ = as2_message.parse(
264+
"abc",
265+
find_org_partner_cb=self.find_org_partner,
266+
find_partner_cb=self.find_partner,
267+
)
268+
269+
with pytest.raises(
270+
TypeError,
271+
match="Incorrect arguments passed: either find_org_cb and find_partner_cb "
272+
"or only find_org_partner_cb must be passed.",
273+
):
274+
_, _, _ = as2_message.parse(
275+
"abc",
276+
find_org_partner_cb=self.find_org_partner,
277+
find_org_cb=self.find_org,
278+
)
279+
280+
with pytest.raises(
281+
TypeError,
282+
match="Incorrect arguments passed: either find_org_cb and find_partner_cb "
283+
"or only find_org_partner_cb must be passed.",
284+
):
285+
_, _, _ = as2_message.parse(
286+
"abc",
287+
find_org_partner_cb=self.find_org_partner,
288+
find_org_cb=self.find_org,
289+
find_partner_cb=self.find_partner,
290+
)
291+
227292
def find_org(self, as2_id):
228293
return self.org
229294

230295
def find_partner(self, as2_id):
231296
return self.partner
297+
298+
def find_org_partner(self, as2_org, as2_partner):
299+
return self.org, self.partner

0 commit comments

Comments
 (0)