I'm trying to access a SOAP web service using Zeep
There is a publicly available WSDL, and a testing WSDL (has a self-signed cert)
My code to test (from test site) is:
from requests import Session
from zeep import Client
from zeep.transports import Transport
from zeep.wsse.username import UsernameToken
import xml.dom.minidom
WS_USER_NAME = '<username>'
WS_PASSWORD = '<password>'
WS_WSDL = 'https://pre.ipddb.org/WS/Services/IpdDownloadService.svc?wsdl'
session = Session()
session.verify = False
transport = Transport(session=session,
operation_timeout=10)
client = Client(wsdl=WS_WSDL,
wsse=UsernameToken(WS_USER_NAME, WS_PASSWORD),
transport=transport)
with client.options(raw_response=True):
response = client.service.Search(strNames='Rick Astley')
xml = xml.dom.minidom.parseString(response._content)
print xml.toprettyxml()
My response comes back as:
<?xml version="1.0" ?>
<s:Envelope xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:s="http://www.w3.org/2003/05/soap-envelope">
<s:Header>
<a:Action s:mustUnderstand="1">http://www.w3.org/2005/08/addressing/soap/fault</a:Action>
<a:RelatesTo>urn:uuid:5ffbeb15-913b-41ec-a2ef-556c131c07eb</a:RelatesTo>
</s:Header>
<s:Body>
<s:Fault>
<s:Code>
<s:Value>s:Sender</s:Value>
<s:Subcode>
<s:Value xmlns:a="http://schemas.xmlsoap.org/ws/2005/02/sc">a:BadContextToken</s:Value>
</s:Subcode>
</s:Code>
<s:Reason>
<s:Text xml:lang="es-ES">The message could not be processed. This is most likely because the action 'https://www.ipddb.org/ws/IpdDownloadService/Search' is incorrect or because the message contains an invalid or expired security context token or because there is a mismatch between bindings. The security context token would be invalid if the service aborted the channel due to inactivity. To prevent the service from aborting idle sessions prematurely increase the Receive timeout on the service endpoint's binding.</s:Text>
</s:Reason>
</s:Fault>
</s:Body>
</s:Envelope>
I was provided a username and password from the owners of the web service, so I know I'll need to provide that, but there must be something else I'm missing. I believe it has to do with the policy defined in the WSDL, but the web service doesn't provide anything in terms of documentation.
I'm new to SOAP, but is there enough in the WSDL for me to figure what they need to comply with policy?
Will I be able to use Zeep to fulfill all the policies?
Do I need more info from those maintaining the web service?
Not that it's a direct answer to your question, but when I have to make a soap request I like to familiarize myself with the API by using soapui.
Once you provide a WSDL for soapui, it will automatically generate all of the required parameters for a proper request. By doing so, you can validate that the error you're receiving is because of a system policy and not because of zeep.
Related
I have an XML/WCF API I need to implement something against. The API client library is only provided as c# in Windows and our company does not do either c# or Windows. I am now experimenting with Python and zeep. The api is Symmetry access control system API if anyone is interested.
I can connect to the server and read the wsdl structure. This works:
URL='https://localhost/smsXMLWebService/SMSXMLWebService.svc?singleWsdl'
URL2='https://localhost/smsXMLWebService/smsXMLWebService.svc'
session = Session()
session.verify = False
transport = Transport(session=session)
self.client = zeep.Client(URL, transport=transport)
self.service = self.client.create_service('{http://tempuri.org/}WSHttpBinding_ISMSXMLWebService', URL2)
Now everything from that point forward will require login to the platform. In the example c# code this is done as follows:
G4TAPI = new SMSXMLWebServiceClient();
G4TAPI.ClientCredentials.UserName.UserName = txtUserName.Text
G4TAPI.ClientCredentials.UserName.Password = txtPassword.Text.ToLower();
G4TAPI.G4TLogin();
My self.service has now G4TLogin() call and it seems to attempt to connect when I wireshark the traffic. But how do I set the username and password as they are not given as parameters to G4TLogin() method?
This does not work:
self.service.ClientCredentials.UserName.UserName = "api"
This is very much out of my comfort zone and I may be using incorrect terminology here. Any ideas?
The error message is
AttributeError: Service has no operation 'ClientCredentials'
When using Zeep make sure to study the namespaces in WSDL URL, using
python -mzeep "YourWsdlUrlGoesHere"
Get the parameters and make a Python dictionary from them (in my case facing C#, including username and password in the dictionary) note that one might need to make nested dictionary like in my case.
from requests import Session
from requests.auth import HTTPBasicAuth, HTTPDigestAuth
from zeep import Client
from zeep.transports import Transport
request = { "Credential":{"Username": "yourusername",
"Password": "yourpassword"},
"RN": "150147119"
}
session = Session()
client = Client('http://your url and wsdl../Invoice.svc?Wsdl',transport=Transport(session=session))
r = client.service.NameOfYourService(request)
print(r)
Do not pass user and password in Zeep formal format. Passing the dictionary worked for me.
In my case the WSDL suggested user and password be in credential and a string be passed in RN and finally all be passed in a one variable.
I have two separate WSDL files that are provided to me to interact with a service, one WSDL file just provides a method to login and generate an access token. The other WSDL file provides the methods to actually interact with the system.
If I instantiate the zeep SOAP client with the first WSDL file to login do I need to reinstantiate the client for the next WSDL file or can I simply tell it to go look at the next WSDL file?
from zeep import Client
client = Client("https://url.service.com/Session?wsdl")
token = client.service.login(username, password)
client = Client("https://url.service.com/Object?wsdl")
client.service.find(token, 'filter')
I attempted to use create_service but I don't think I'm using it correctly.
Thank you!
You need to reinstantiate the second Client.
I expect that you also need to extend your code to use the same requests Session and Zeeps Transport.
from requests import Session
from zeep import Client
from zeep.transports import Transport
transport = Transport(session=Session())
client = Client("https://url.service.com/Session?wsdl", transport=transport)
token = client.service.login(username, password)
client = Client("https://url.service.com/Object?wsdl", transport=transport)
client.service.find(token, 'filter')
The Problem
Working my way through the documentation on the coinbase developer page and when running the most basic example I get a warning about insecure endpoints. I'd like to solve this warning before moving on.
The Warning
python3.5/site-packages/coinbase/wallet/util.py:45: UserWarning:
WARNING: this client is sending a request to an insecure API endpoint.
Any API request you make may expose your API key and secret to third
parties. Consider using the default endpoint:
The Code
from coinbase.wallet.client import Client
from vars import *
client = Client (
API_KEY,
API_SECRET,
API_VERSION)
print(client.get_accounts)
vars is a file that contains my constants; key, secret, and version.
The Attempts
In all honesty, just a host of google searches that don't seem to be turning up anything useful, in regards to setting the endpoint. Advice?
Removing the versioning parameter appears to have at least silenced the warning.
client = Client (
API_KEY,
API_SECRET)
I am trying to implement Oauth2 for a website using Python oauthlib. I have decided that I would like to use the grant type 'ResourceOwnerPasswordCredentialsGrant', this is because the website and the API are my own and will not be open to third parties.
In 'resource_owner_password_credentials.py' why is 'client_authentication_required' hardcoded to return 'True'?
Do I need to authenticate my client (website)? From my understanding this would be a 'public' and not a confidential client.
Looks like the author of oauthlib interpreted the spec this way. The section on the Resource Owner Password Credentials grant type (https://www.rfc-editor.org/rfc/rfc6749#section-4.3) says:
(B) The client requests an access token from the authorization
server's token endpoint by including the credentials received
from the resource owner. When making the request, the client
authenticates with the authorization server.
Note that it suggests that the client authenticates but it does not use the MUST keyword in that last sentence. Just below that there's slightly more elaborate text (https://www.rfc-editor.org/rfc/rfc6749#section-4.3.2) saying:
If the client type is confidential or the client was issued client
credentials (or assigned other authentication requirements), the
client MUST authenticate with the authorization server as described
in Section 3.2.1.
Note the "If the client is confidential", this time implying that public clients may use this grant type too, which makes sense.
So I believe this is due to a too strict interpretation of the wording in the specs.
I am working with a SOAP service where the project provides an external WSDL file. I am using Python + Suds to connect to the service. I run into issues because the (https) service URL looks like:
/sipxconfig/services/UserService?wsdl
But the WSDL ath that URL does not match the external WSDL file that is provided by the project. The SOAP document returned does match the external WSDL file. So my suds client raises a fault.
I have so far managed to work around this by writing a suds plugin to "correct" the SOAP XML returned so that it matches the dynamically created WSDL (at the URL). However, I was hoping there was a way to feed the subs client the external WSDL file and then switch it to using the URL for the service.
I tried something like this:
wsdl_file = os.path.abspath(args.wsdl_file)
client = Client("file://%s" % wsdl_file, transport=t, username=sip_user, password=sip_pwd, doctor=doctor)
client.set_options(location=url)
#Get the results.
user_search = client.factory.create("UserSearch")
user_search.byUserName = args.find_user
user_search.byFuzzyUserNameOrAlias = args.fuzzy
user_search.byGroup = args.group
result = client.service.findUser(user_search)
#^^^
#Error here!
But it ultimately results in a MethodNotFound exception.
I run netstat in another terminal, and I can see that the client is not making a network connection to the external service.
Has anyone else managed to feed Suds WSDL from a file?
Thanks,
Carl
So I determined I was on the correct track, but my SOAP service had multiple ports. I needed to do the following:
wsdl_file = os.path.abspath(args.wsdl_file)
client = Client("file://%s" % wsdl_file, transport=t, username=sip_user, password=sip_pwd, doctor=doctor)
client.set_options(location=url)
#Get the results.
user_search = client.factory.create("UserSearch")
user_search.byUserName = args.find_user
user_search.byFuzzyUserNameOrAlias = args.fuzzy
user_search.byGroup = args.group
result = client.service['UserService'].findUser(user_search)
# ^^^^^^^^^^^^^^^
# This was the missing bit that threw me off!
Thanks,
Carl