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.
Related
I tried to create a zeep client and use it by two methods.
1) I tried to keep everything in a single module . i created the zeep client object and it was working fine while using a payload.
2) I created a method which returns a zeep client object for a wsdl. I tried to use this a way as method 1) But getting the below error.
zeep.exception.Fault : Incoming message could not be authenticated. No valid credentials found
Can someone please advise what I am missing here which causes this error. My second approach is like this.
\\
def zeepClient(wsdl):
## do all here and return zeep client object.
return client
#Now in another module I do call that above method like this
Client=othermodule.zeepClient(mywsdl)
Payload={my payload}
Client.service.myservice(**Payload)
\\
If I do this , I get above error.But if my above piece of code and my method for zeepClient are all in same place. I am not getting error.
Not sure . What that Returned Client object is missing.
You must provide credentials to the session and then make a request like here
from requests import Session
from requests.auth import HTTPBasicAuth # or HTTPDigestAuth, or OAuth1, etc.
from zeep import Client
from zeep.transports import Transport
session = Session()
session.auth = HTTPBasicAuth(user, password)
client = Client('http://my-endpoint.com/production.svc?wsdl',
transport=Transport(session=session))
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')
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.
I'm trying to get this example to work from https://github.com/ozgur/python-linkedin. I'm using his example. When I run this code. I don't get the RETURN_URL and authorization_code talked about in the example. I'm not sure why, I think it is because I'm not setting up the HTTP API example correctly. I can't find http_api.py, and when I visit http://localhost:8080, I get a "this site can't be reached".
from linkedin import linkedin
API_KEY = 'wFNJekVpDCJtRPFX812pQsJee-gt0zO4X5XmG6wcfSOSlLocxodAXNMbl0_hw3Vl'
API_SECRET = 'daJDa6_8UcnGMw1yuq9TjoO_PMKukXMo8vEMo7Qv5J-G3SPgrAV0FqFCd0TNjQyG'
RETURN_URL = 'http://localhost:8000'
authentication = linkedin.LinkedInAuthentication(API_KEY, API_SECRET, RETURN_URL, linkedin.PERMISSIONS.enums.values())
# Optionally one can send custom "state" value that will be returned from OAuth server
# It can be used to track your user state or something else (it's up to you)
# Be aware that this value is sent to OAuth server AS IS - make sure to encode or hash it
#authorization.state = 'your_encoded_message'
print authentication.authorization_url # open this url on your browser
application = linkedin.LinkedInApplication(authentication)
http_api.py is one of the examples provided in the package. This is an HTTP server that will handle the response from LinkedIn's OAuth end point, so you'll need to boot it up for the example to work.
As stated in the guide, you'll need to execute that example file to get the server working. Note you'll also need to supply the following environment variables: LINKEDIN_API_KEY and LINKEDIN_API_SECRET.
You can run the example file by downloading the repo and calling LINKEDIN_API_KEY=yourkey LINKEDIN_API_SECRET=yoursecret python examples/http_api.py. Note you'll need Python 3.4 for it to work.
I have installed couchDB v 0.10.0, and am attempting to talk to it via python from Couch class downloaded from couchDB wiki. Problem is:
Create database 'mydb': {'error': 'unauthorized', 'reason': 'You are not a server admin.'}
I hand edited the local.ini file to include my standard osx login and password. I now have full access via futon but no joy WRT python. Is this an http header issue?
At a a loss - thanks!
To concour David's reply, (i.e. "This is how I do it using module CouchDB 0.8 in python 2.6 with couchdb 1.0.2")
couch = couchdb.Server(couch_server)
couch.resource.credentials = (USERNAME, PASSWORD)
You can also do:
db = couchdb.Database("http://your.url/yourdb")
db.resource.http.add_credentials(username, password)
after which all your requests should work.
The Couch class in the example does not pass any authentication information to the database, so it is not a miracle that it does not allow privileged operations. So your only options are:
disable authentication completely (as you mentioned)
pass the user name and password as part of the URI
pass the user name and password as an Authorization HTTP request header
If you want to pass a user name and a password, then you will need to change the Couch class. Sending an Authorization HTTP request header is easier, since the Couch class uses the httplib.HTTPConnection class. You can add such a header next to the Accept one this way:
headers = {
"Accept": "application/json",
"Authorization": "Basic " + 'username:password'.encode('base64')[:-1]}
Same for the other HTTP request methods.
The documentation on the basic authentication is here:
http://books.couchdb.org/relax/reference/security
Just pass it as part of the URI...python-couchdb will parse the user/pass out and use them:
http://user:pass#localhost:5984
Above are all nice; but I've found that for oauth validation methods versus basic auth, this works really well:
from couchdb import Server, Session
auth = Session()
auth.name = USERNAME
auth.password = PASSWORD
s = Server('http://localhost:5984/', session=auth)
db = s['dbname']
Note: This will not work with basic authentication; in such a case, fviktor has what I consider to be the best answer. You might also look into the security reference material he linked to if you're interested in persistent auth sessions.
There are several patches for python-couchdb that enable authentication. The code probably will be included in Version 0.7 but until then you can usr teh fork at http://github.com/mdornseif/couchdb-python - it allows you to use http://user:pass#127.0.0.1:5984/ type URLs.
http://blogs.23.nu/c0re/2009/12/running-a-couchdb-cluster-on-amazon-ec2/ (at the bottom) shows how to use CouchDB passwords.