Skip to content

Commit 194e174

Browse files
committed
Sign elements required by WSDL
1 parent e1d7834 commit 194e174

File tree

7 files changed

+77
-37
lines changed

7 files changed

+77
-37
lines changed

src/zeep/ns.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
21
SOAP_11 = "http://schemas.xmlsoap.org/wsdl/soap/"
32
SOAP_12 = "http://schemas.xmlsoap.org/wsdl/soap12/"
43
SOAP_ENV_11 = "http://schemas.xmlsoap.org/soap/envelope/"

src/zeep/wsdl/bindings/soap.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,9 +90,13 @@ def _create(self, operation, args, kwargs, client=None, options=None):
9090
if client.wsse:
9191
if isinstance(client.wsse, list):
9292
for wsse in client.wsse:
93-
envelope, http_headers = wsse.apply(envelope, http_headers)
93+
envelope, http_headers = wsse.apply(
94+
envelope, http_headers, operation_obj.binding.signatures
95+
)
9496
else:
95-
envelope, http_headers = client.wsse.apply(envelope, http_headers)
97+
envelope, http_headers = client.wsse.apply(
98+
envelope, http_headers, operation_obj.binding.signatures
99+
)
96100

97101
# Add extra http headers from the setings object
98102
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
@@ -127,9 +127,9 @@ def __init__(self, wsdl, name, port_name):
127127
self.wsdl = wsdl
128128
self._operations = {}
129129
self.signatures = {
130-
'header': [], # Elements of header, that should be signed
131-
'body': False, # If body should be signed
132-
'everything': False, # If every header should be signed
130+
"header": [], # Elements of header, that should be signed
131+
"body": False, # If body should be signed
132+
"everything": False, # If every header should be signed
133133
}
134134

135135
def resolve(self, definitions):

src/zeep/wsdl/wsdl.py

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,7 @@
2222
from zeep.wsdl import parse
2323
from zeep.xsd import Schema
2424

25-
NSMAP = {
26-
'wsdl': ns.WSDL,
27-
'wsp': ns.WSP,
28-
'sp': ns.SP,
29-
'wsu': ns.WSU,
30-
}
25+
NSMAP = {"wsdl": ns.WSDL, "wsp": ns.WSP, "sp": ns.SP, "wsu": ns.WSU}
3126

3227
logger = logging.getLogger(__name__)
3328

@@ -430,24 +425,28 @@ def parse_binding(self, doc):
430425
continue
431426

432427
# Begin heuristics for signed parts...
433-
binding_policy = binding.name.localname + '_policy'
434-
signed_parts = doc.xpath('wsp:Policy[@wsu:Id="{}"]//sp:SignedParts'.format(binding_policy),
435-
namespaces=NSMAP)
428+
binding_policy = binding.name.localname + "_policy"
429+
signed_parts = doc.xpath(
430+
'wsp:Policy[@wsu:Id="{}"]//sp:SignedParts'.format(
431+
binding_policy
432+
),
433+
namespaces=NSMAP,
434+
)
436435
for sign in signed_parts:
437436
if len(sign.getchildren()) == 0:
438437
# No children, we should sign everything
439-
binding.signatures['body'] = True
440-
binding.signatures['everything'] = True
438+
binding.signatures["body"] = True
439+
binding.signatures["everything"] = True
441440
break
442441

443442
for child in sign.iterchildren():
444443
if len(child.items()) > 0:
445444
# Header ...
446445
part = {attr: value for attr, value in child.items()}
447-
binding.signatures['header'].append(part)
448-
elif child.tag.split('}')[-1].lower() == 'body':
446+
binding.signatures["header"].append(part)
447+
elif child.tag.split("}")[-1].lower() == "body":
449448
# Body ...
450-
binding.signatures['body'] = True
449+
binding.signatures["body"] = True
451450
logger.debug("Adding binding: %s", binding.name.text)
452451
result[binding.name.text] = binding
453452
break

src/zeep/wsse/signature.py

Lines changed: 39 additions & 12 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:
247-
_sign_node(ctx, signature, timestamp)
251+
_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
@@ -56,7 +56,7 @@ def __init__(
5656
self.use_digest = use_digest
5757
self.timestamp_token = timestamp_token
5858

59-
def apply(self, envelope, headers):
59+
def apply(self, envelope, headers, operation_obj=None):
6060
security = utils.get_security_header(envelope)
6161

6262
# 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
@@ -92,12 +92,15 @@ def test_sign(
9292
"""
9393
)
9494

95+
# Force body signature
96+
signatures = {"everything": False, "body": True, "header": []}
9597
signature.sign_envelope(
9698
envelope,
9799
KEY_FILE,
98100
KEY_FILE,
99101
signature_method=getattr(xmlsec_installed.Transform, signature_method),
100102
digest_method=getattr(xmlsec_installed.Transform, digest_method),
103+
signatures=signatures,
101104
)
102105
signature.verify_envelope(envelope, KEY_FILE)
103106

@@ -130,7 +133,9 @@ def test_sign_pw():
130133
"""
131134
)
132135

133-
signature.sign_envelope(envelope, KEY_FILE_PW, KEY_FILE_PW, "geheim")
136+
# Force body signature
137+
signatures = {"everything": False, "body": True, "header": []}
138+
signature.sign_envelope(envelope, KEY_FILE_PW, KEY_FILE_PW, "geheim", signatures=signatures)
134139
signature.verify_envelope(envelope, KEY_FILE_PW)
135140

136141

@@ -153,7 +158,9 @@ def test_verify_error():
153158
"""
154159
)
155160

156-
signature.sign_envelope(envelope, KEY_FILE, KEY_FILE)
161+
# Force body signature
162+
signatures = {"everything": False, "body": True, "header": []}
163+
signature.sign_envelope(envelope, KEY_FILE, KEY_FILE, signatures=signatures)
157164
nsmap = {"tns": "http://tests.python-zeep.org/"}
158165

159166
for elm in envelope.xpath("//tns:Argument", namespaces=nsmap):
@@ -182,8 +189,10 @@ def test_signature():
182189
"""
183190
)
184191

192+
# Force body signature
193+
signatures = {"everything": False, "body": True, "header": []}
185194
plugin = wsse.Signature(KEY_FILE_PW, KEY_FILE_PW, "geheim")
186-
envelope, headers = plugin.apply(envelope, {})
195+
envelope, headers = plugin.apply(envelope, {}, signatures=signatures)
187196
plugin.verify(envelope)
188197

189198

@@ -212,14 +221,16 @@ def test_signature_binary(
212221
"""
213222
)
214223

224+
# Force body signature
225+
signatures = {"everything": False, "body": True, "header": []}
215226
plugin = wsse.BinarySignature(
216227
KEY_FILE_PW,
217228
KEY_FILE_PW,
218229
"geheim",
219230
signature_method=getattr(xmlsec_installed.Transform, signature_method),
220231
digest_method=getattr(xmlsec_installed.Transform, digest_method),
221232
)
222-
envelope, headers = plugin.apply(envelope, {})
233+
envelope, headers = plugin.apply(envelope, {}, signatures=signatures)
223234
plugin.verify(envelope)
224235
# Test the reference
225236
bintok = envelope.xpath(

0 commit comments

Comments
 (0)