Skip to content

Commit d159a57

Browse files
tpazderkablaggacao
authored andcommitted
Sign elements required by WSDL
1 parent 64249f7 commit d159a57

File tree

6 files changed

+75
-29
lines changed

6 files changed

+75
-29
lines changed

src/zeep/wsdl/bindings/soap.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,9 +95,13 @@ def _create(self, operation, args, kwargs, client=None, options=None):
9595
if client.wsse:
9696
if isinstance(client.wsse, list):
9797
for wsse in client.wsse:
98-
envelope, http_headers = wsse.apply(envelope, http_headers)
98+
envelope, http_headers = wsse.apply(
99+
envelope, http_headers, operation_obj.binding.signatures
100+
)
99101
else:
100-
envelope, http_headers = client.wsse.apply(envelope, http_headers)
102+
envelope, http_headers = client.wsse.apply(
103+
envelope, http_headers, operation_obj.binding.signatures
104+
)
101105

102106
# Add extra http headers from the setings object
103107
if client.settings.extra_http_headers:

src/zeep/wsdl/definitions.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -136,9 +136,9 @@ def __init__(self, wsdl, name, port_name):
136136
self.wsdl = wsdl
137137
self._operations = {}
138138
self.signatures = {
139-
'header': [], # Elements of header, that should be signed
140-
'body': False, # If body should be signed
141-
'everything': False, # If every header should be signed
139+
"header": [], # Elements of header, that should be signed
140+
"body": False, # If body should be signed
141+
"everything": False, # If every header should be signed
142142
}
143143

144144
def resolve(self, definitions: Definition) -> None:

src/zeep/wsdl/wsdl.py

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -447,24 +447,28 @@ def parse_binding(
447447
continue
448448

449449
# Begin heuristics for signed parts...
450-
binding_policy = binding.name.localname + '_policy'
451-
signed_parts = doc.xpath('wsp:Policy[@wsu:Id="{}"]//sp:SignedParts'.format(binding_policy),
452-
namespaces=NSMAP)
450+
binding_policy = binding.name.localname + "_policy"
451+
signed_parts = doc.xpath(
452+
'wsp:Policy[@wsu:Id="{}"]//sp:SignedParts'.format(
453+
binding_policy
454+
),
455+
namespaces=NSMAP,
456+
)
453457
for sign in signed_parts:
454458
if len(sign.getchildren()) == 0:
455459
# No children, we should sign everything
456-
binding.signatures['body'] = True
457-
binding.signatures['everything'] = True
460+
binding.signatures["body"] = True
461+
binding.signatures["everything"] = True
458462
break
459463

460464
for child in sign.iterchildren():
461465
if len(child.items()) > 0:
462466
# Header ...
463467
part = {attr: value for attr, value in child.items()}
464-
binding.signatures['header'].append(part)
465-
elif child.tag.split('}')[-1].lower() == 'body':
468+
binding.signatures["header"].append(part)
469+
elif child.tag.split("}")[-1].lower() == "body":
466470
# Body ...
467-
binding.signatures['body'] = True
471+
binding.signatures["body"] = True
468472
logger.debug("Adding binding: %s", binding.name.text)
469473
result[binding.name.text] = binding
470474
break

src/zeep/wsse/signature.py

Lines changed: 38 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from zeep import ns
1515
from zeep.exceptions import SignatureVerificationFailed
1616
from zeep.utils import detect_soap_env
17+
from zeep.wsdl.utils import get_or_create_header
1718
from zeep.wsse.utils import ensure_id, get_security_header
1819

1920
try:
@@ -61,10 +62,10 @@ def __init__(
6162
self.digest_method = digest_method
6263
self.signature_method = signature_method
6364

64-
def apply(self, envelope, headers):
65+
def apply(self, envelope, headers, signatures=None):
6566
key = _make_sign_key(self.key_data, self.cert_data, self.password)
6667
_sign_envelope_with_key(
67-
envelope, key, self.signature_method, self.digest_method
68+
envelope, key, self.signature_method, self.digest_method, signatures
6869
)
6970
return envelope, headers
7071

@@ -99,10 +100,10 @@ class BinarySignature(Signature):
99100
100101
Place the key information into BinarySecurityElement."""
101102

102-
def apply(self, envelope, headers):
103+
def apply(self, envelope, headers, signatures=None):
103104
key = _make_sign_key(self.key_data, self.cert_data, self.password)
104105
_sign_envelope_with_key_binary(
105-
envelope, key, self.signature_method, self.digest_method
106+
envelope, key, self.signature_method, self.digest_method, signatures
106107
)
107108
return envelope, headers
108109

@@ -123,6 +124,7 @@ def sign_envelope(
123124
password=None,
124125
signature_method=None,
125126
digest_method=None,
127+
signatures=None,
126128
):
127129
"""Sign given SOAP envelope with WSSE sig using given key and cert.
128130
@@ -213,10 +215,12 @@ def sign_envelope(
213215
"""
214216
# Load the signing key and certificate.
215217
key = _make_sign_key(_read_file(keyfile), _read_file(certfile), password)
216-
return _sign_envelope_with_key(envelope, key, signature_method, digest_method)
218+
return _sign_envelope_with_key(
219+
envelope, key, signature_method, digest_method, signatures
220+
)
217221

218222

219-
def _signature_prepare(envelope, key, signature_method, digest_method):
223+
def _signature_prepare(envelope, key, signature_method, digest_method, signatures=None):
220224
"""Prepare envelope and sign."""
221225
soap_env = detect_soap_env(envelope)
222226

@@ -241,10 +245,29 @@ def _signature_prepare(envelope, key, signature_method, digest_method):
241245
# Perform the actual signing.
242246
ctx = xmlsec.SignatureContext()
243247
ctx.key = key
244-
_sign_node(ctx, signature, envelope.find(QName(soap_env, "Body")), digest_method)
248+
# Sign default elements if present
245249
timestamp = security.find(QName(ns.WSU, "Timestamp"))
246250
if timestamp != None:
247251
_sign_node(ctx, signature, timestamp, digest_method)
252+
253+
# Sign extra elements defined in WSDL
254+
if signatures is not None:
255+
if signatures["body"] or signatures["everything"]:
256+
_sign_node(
257+
ctx, signature, envelope.find(QName(soap_env, "Body")), digest_method
258+
)
259+
header = get_or_create_header(envelope)
260+
if signatures["everything"]:
261+
for node in header.iterchildren():
262+
_sign_node(ctx, signature, node, digest_method)
263+
else:
264+
for node in signatures["header"]:
265+
_sign_node(
266+
ctx,
267+
signature,
268+
header.find(QName(node["Namespace"], node["Name"])),
269+
digest_method,
270+
)
248271
ctx.sign(signature)
249272

250273
# Place the X509 data inside a WSSE SecurityTokenReference within
@@ -255,16 +278,20 @@ def _signature_prepare(envelope, key, signature_method, digest_method):
255278
return security, sec_token_ref, x509_data
256279

257280

258-
def _sign_envelope_with_key(envelope, key, signature_method, digest_method):
281+
def _sign_envelope_with_key(
282+
envelope, key, signature_method, digest_method, signatures=None
283+
):
259284
_, sec_token_ref, x509_data = _signature_prepare(
260-
envelope, key, signature_method, digest_method
285+
envelope, key, signature_method, digest_method, signatures=signatures
261286
)
262287
sec_token_ref.append(x509_data)
263288

264289

265-
def _sign_envelope_with_key_binary(envelope, key, signature_method, digest_method):
290+
def _sign_envelope_with_key_binary(
291+
envelope, key, signature_method, digest_method, signatures=None
292+
):
266293
security, sec_token_ref, x509_data = _signature_prepare(
267-
envelope, key, signature_method, digest_method
294+
envelope, key, signature_method, digest_method, signatures=signatures
268295
)
269296
ref = etree.SubElement(
270297
sec_token_ref,

src/zeep/wsse/username.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ def __init__(
6565
self.zulu_timestamp = zulu_timestamp
6666
self.hash_password = hash_password
6767

68-
def apply(self, envelope, headers):
68+
def apply(self, envelope, headers, operation_obj=None):
6969
security = utils.get_security_header(envelope)
7070

7171
# The token placeholder might already exists since it is specified in

tests/test_wsse_signature.py

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -118,12 +118,15 @@ def test_sign(
118118
"""
119119
)
120120

121+
# Force body signature
122+
signatures = {"everything": False, "body": True, "header": []}
121123
signature.sign_envelope(
122124
envelope,
123125
KEY_FILE,
124126
KEY_FILE,
125127
signature_method=getattr(xmlsec.Transform, signature_method),
126128
digest_method=getattr(xmlsec.Transform, digest_method),
129+
signatures=signatures,
127130
)
128131
signature.verify_envelope(envelope, KEY_FILE)
129132

@@ -156,7 +159,9 @@ def test_sign_pw():
156159
"""
157160
)
158161

159-
signature.sign_envelope(envelope, KEY_FILE_PW, KEY_FILE_PW, "geheim")
162+
# Force body signature
163+
signatures = {"everything": False, "body": True, "header": []}
164+
signature.sign_envelope(envelope, KEY_FILE_PW, KEY_FILE_PW, "geheim", signatures=signatures)
160165
signature.verify_envelope(envelope, KEY_FILE_PW)
161166

162167

@@ -179,7 +184,9 @@ def test_verify_error():
179184
"""
180185
)
181186

182-
signature.sign_envelope(envelope, KEY_FILE, KEY_FILE)
187+
# Force body signature
188+
signatures = {"everything": False, "body": True, "header": []}
189+
signature.sign_envelope(envelope, KEY_FILE, KEY_FILE, signatures=signatures)
183190
nsmap = {"tns": "http://tests.python-zeep.org/"}
184191

185192
for elm in envelope.xpath("//tns:Argument", namespaces=nsmap):
@@ -208,8 +215,10 @@ def test_signature():
208215
"""
209216
)
210217

218+
# Force body signature
219+
signatures = {"everything": False, "body": True, "header": []}
211220
plugin = wsse.Signature(KEY_FILE_PW, KEY_FILE_PW, "geheim")
212-
envelope, headers = plugin.apply(envelope, {})
221+
envelope, headers = plugin.apply(envelope, {}, signatures=signatures)
213222
plugin.verify(envelope)
214223

215224

@@ -238,14 +247,16 @@ def test_signature_binary(
238247
"""
239248
)
240249

250+
# Force body signature
251+
signatures = {"everything": False, "body": True, "header": []}
241252
plugin = wsse.BinarySignature(
242253
KEY_FILE_PW,
243254
KEY_FILE_PW,
244255
"geheim",
245256
signature_method=getattr(xmlsec.Transform, signature_method),
246257
digest_method=getattr(xmlsec.Transform, digest_method),
247258
)
248-
envelope, headers = plugin.apply(envelope, {})
259+
envelope, headers = plugin.apply(envelope, {}, signatures=signatures)
249260
plugin.verify(envelope)
250261
# Test the reference
251262
bintok = envelope.xpath(

0 commit comments

Comments
 (0)