Skip to content

Commit 01b5cf0

Browse files
author
Kristjan
committed
1. OPDM 4.0 support (older versions not supported)
2. Auth from SAML token to wsse username and password 3. Automatic version number update 4. Mutable defaults removed form function calls
1 parent 079a1d8 commit 01b5cf0

File tree

1 file changed

+24
-73
lines changed

1 file changed

+24
-73
lines changed

OPDM/OPDM_SOAP_API.py

Lines changed: 24 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -15,22 +15,17 @@
1515
from zeep.plugins import HistoryPlugin
1616

1717
from lxml import etree
18-
from lxml.builder import ElementMaker
1918

20-
import random
2119
import os
2220
import uuid
2321

24-
import json
2522
import xmltodict
2623

27-
from datetime import datetime, timezone
28-
import aniso8601
29-
3024
import urllib3
3125

32-
import logging
26+
from OPDM import __version__ as package_version
3327

28+
import logging
3429
logger = logging.getLogger(__name__)
3530

3631

@@ -71,10 +66,9 @@ def __init__(self, server, username="", password="", debug=False, verify=False):
7166

7267
self.debug = debug
7368
self.history = HistoryPlugin()
74-
self.API_VERSION = "0.0.11" # TODO - Get the version from versioneer
69+
self.API_VERSION = package_version
7570

7671
service_wsdl = '{}/cxf/OPDMSoapInterface?wsdl'.format(server)
77-
auth_wsdl = '{}/cxf/OPDMSoapInterface/SoapAuthentication?wsdl'.format(server)
7872
ruleset_wsdl = '{}/cxf/OPDMSoapInterface/RuleSetManagementService?wsdl'.format(server)
7973

8074
session = Session()
@@ -85,27 +79,13 @@ def __init__(self, server, username="", password="", debug=False, verify=False):
8579
session.verify = False
8680

8781
# Set up client
82+
self.client = Client(service_wsdl, transport=Transport(session=session), wsse=UsernameToken(username, password=password))
83+
self.ruleset_client = Client(ruleset_wsdl, transport=Transport(session=session), wsse=UsernameToken(username, password=password))
84+
8885
if debug:
89-
self.client = Client(service_wsdl, transport=Transport(session=session), plugins=[self.history])
90-
self.ruleset_client = Client(ruleset_wsdl, transport=Transport(session=session), plugins=[self.history])
86+
self.client.plugins = [self.history]
87+
self.ruleset_client.plugins = [self.history]
9188
logging.basicConfig(format='%(asctime)s | %(name)s | %(levelname)s | %(message)s', level=logging.DEBUG)
92-
else:
93-
self.client = Client(service_wsdl, transport=Transport(session=session))
94-
self.ruleset_client = Client(ruleset_wsdl, transport=Transport(session=session))
95-
96-
# Set up auth
97-
if username == "":
98-
self.auth = False
99-
elif debug:
100-
self.auth = True
101-
self.auth_client = Client(auth_wsdl, transport=Transport(session=session), wsse=UsernameToken(username, password=password), plugins=[self.history])
102-
self.auth_valid_until = datetime.now(timezone.utc)
103-
self.token = None
104-
else:
105-
self.auth = True
106-
self.auth_client = Client(auth_wsdl, transport=Transport(session=session), wsse=UsernameToken(username, password=password))
107-
self.auth_valid_until = datetime.now(timezone.utc)
108-
self.token = None
10989

11090
def _print_last_message_exchange(self):
11191
"""Prints out last sent and received SOAP messages"""
@@ -225,40 +205,9 @@ class Operations:
225205
</sm:GetProfilePublicationReport>
226206
"""
227207

228-
def request_security_token(self, pretty_print=False):
229-
"""Return the token as string and it's validity time"""
230-
token_string = self.auth_client.service.RequestSecurityToken()
231-
token = etree.fromstring(token_string)
232-
valid_unitl = aniso8601.parse_datetime(token.find(".//{*}Conditions").attrib['NotOnOrAfter'])
233-
234-
if pretty_print:
235-
print(json.dumps(xmltodict.parse(token_string), indent=4))
236-
237-
return token, valid_unitl
238-
239-
def check_token(self):
240-
"""Check if token is due to expire and renew if needed"""
241-
utc_now = datetime.now(timezone.utc)
242-
243-
if utc_now > self.auth_valid_until - aniso8601.parse_duration("PT5S"):
244-
self.token, self.auth_valid_until = self.request_security_token()
245-
logger.debug("Requesting new Auth Token")
246-
247-
elif self.debug:
248-
logger.debug(f"Auth Token still valid for {self.auth_valid_until - utc_now}")
249-
250-
def create_saml_header(self):
251-
"""Create SOAP SAML authentication header element for zeep"""
252-
if self.auth:
253-
self.check_token()
254-
WSSE = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
255-
return [ElementMaker(namespace=WSSE).Security(self.token)]
256-
else:
257-
return []
258-
259208
def execute_operation(self, operation_xml):
260209
"""ExecuteOperation(payload: xsd:base64Binary) -> return: ns0:resultDto"""
261-
response = self.client.service.ExecuteOperation(operation_xml, _soapheaders=self.create_saml_header())
210+
response = self.client.service.ExecuteOperation(operation_xml)
262211
return response
263212

264213
def publication_request(self, file_path_or_file_object, content_type="CGMES"):
@@ -278,11 +227,11 @@ def publication_request(self, file_path_or_file_object, content_type="CGMES"):
278227

279228
payload = {"id": file_name, "type": content_type, "content": file_string}
280229

281-
response = self.client.service.PublicationRequest(payload, _soapheaders=self.create_saml_header())
230+
response = self.client.service.PublicationRequest(payload)
282231

283232
return response
284233

285-
def query_object(self, object_type="IGM", metadata_dict="", components=[], dependencies=[]):
234+
def query_object(self, object_type="IGM", metadata_dict=None, components=None, dependencies=None):
286235
"""
287236
object_type ->IGM, CGM, BDS
288237
metadata_dict_example = {'pmd:cgmesProfile': 'SV', 'pmd:scenarioDate': '2018-12-07T00:30:00+01:00', 'pmd:timeHorizon': '1D'}
@@ -293,14 +242,16 @@ def query_object(self, object_type="IGM", metadata_dict="", components=[], depen
293242

294243
_QueryObject = self.Operations.QueryObject.format(query_id=query_id, object_type=object_type).encode()
295244

296-
if metadata_dict != "":
245+
if metadata_dict:
297246
_QueryObject = add_xml_elements(_QueryObject, ".//opdm:OPDMObject", metadata_dict)
298247

299-
for component in components:
300-
_QueryObject = add_xml_elements(_QueryObject, ".//opde:Components", component)
248+
if components:
249+
for component in components:
250+
_QueryObject = add_xml_elements(_QueryObject, ".//opde:Components", component)
301251

302-
for dependency in dependencies:
303-
_QueryObject = add_xml_elements(_QueryObject, ".//opde:Dependencies", dependency)
252+
if dependencies:
253+
for dependency in dependencies:
254+
_QueryObject = add_xml_elements(_QueryObject, ".//opde:Dependencies", dependency)
304255

305256
logger.debug(_QueryObject.decode())
306257

@@ -346,6 +297,7 @@ def get_content(self, content_id, return_payload=False, object_type="file"):
346297

347298
result = xmltodict.parse(etree.tostring(self.execute_operation(get_content_result.encode())), xml_attribs=False)
348299

300+
# TODO - add better error handling, in case error message was returned
349301
if type(result['sm:GetContentResult']['sm:part']) == list:
350302
logger.info("File downloaded")
351303
#logger.debug(result['sm:GetContentResult']['sm:part'][1]['opdm:Profile']['opde:Content'])
@@ -373,8 +325,7 @@ def publication_subscribe(self, object_type="BDS", subscription_id="", publicati
373325
# Get available publications
374326
available_publications = self.publication_list()
375327

376-
377-
object_types = {item['opde:messageType']["@v"].split("-")[-1]:item['opde:publicationID']["@v"] for item in available_publications['sm:PublicationsSubscriptionListResult']['sm:part']['opdm:PublicationsList']['opdm:Publication']}
328+
object_types = {item['opde:messageType']["@v"].split("-")[-1]: item['opde:publicationID']["@v"] for item in available_publications['sm:PublicationsSubscriptionListResult']['sm:part']['opdm:PublicationsList']['opdm:Publication']}
378329

379330
if object_type not in object_types.keys():
380331
logger.warning(f"ObjectType '{object_type}' not supported, supported types are: {object_types}")
@@ -437,19 +388,19 @@ def publication_cancel_subscription(self, subscription_id):
437388

438389
def get_installed_ruleset_version(self):
439390
"""Retrurns a string with the latest ruleset version"""
440-
return self.ruleset_client.service.GetInstalledRuleSetVersion(_soapheaders=self.create_saml_header())
391+
return self.ruleset_client.service.GetInstalledRuleSetVersion()
441392

442393
def list_available_rulesets(self):
443394
"""Returns a list of available rulesets"""
444-
return self.ruleset_client.service.ListAvailableRuleSets(_soapheaders=self.create_saml_header())
395+
return self.ruleset_client.service.ListAvailableRuleSets()
445396

446397
def install_ruleset(self, version=None):
447398
"""Install ruleset library by providing the library version as a string. To get available ruleset libraries use list_available_rulesets()"""
448-
return self.ruleset_client.service.Install(Version=version, _soapheaders=self.create_saml_header())
399+
return self.ruleset_client.service.Install(Version=version)
449400

450401
def reset_ruleset(self):
451402
"""Reset ruleset library"""
452-
return self.ruleset_client.service.Reset(_soapheaders=self.create_saml_header())
403+
return self.ruleset_client.service.Reset()
453404

454405

455406
if __name__ == '__main__':

0 commit comments

Comments
 (0)