Suds throwing 403 Forbidden on SharePoint - python

I'm having an issue with getting suds to authenticate using the python-ntlm package against a SharePoint 2010 site. I have tried the solutions linked in this thread with no luck.
I'm running Python 2.7.10, suds 0.4, and python-ntlm 1.1.0.
Here's my code:
from suds.client import *
from suds.transport.https import WindowsHttpAuthenticated
import logging
logging.basicConfig(level=logging.INFO)
logging.getLogger('suds.client').setLevel(logging.DEBUG)
logging.getLogger('suds.transport').setLevel(logging.DEBUG)
logging.getLogger('ntlm').setLevel(logging.DEBUG)
url = "https://web.site.com/sites/Collection/_vti_bin/Lists.asmx?WSDL"
ntlm = WindowsHttpAuthenticated(username='DOMAIN\UserName',
password='Password')
client = Client(url, transport=ntlm)
client.service.GetListCollection()
Here's the debug output:
DEBUG:suds.transport.http:opening (https://web.site.com/sites/Collection/_vti_bin/Lists.asmx?WSDL)
DEBUG:suds.transport.http:opening (http://www.w3.org/2001/XMLSchema.xsd)
DEBUG:suds.transport.http:opening (http://www.w3.org/2001/xml.xsd)
DEBUG:suds.client:sending to (https://web.site.com/sites/Collection/_vti_bin/Lists.asmx)
message:
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:ns0="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://schemas.microsoft.com/sharepoint/soap/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header/>
<ns0:Body>
<ns1:GetListCollection/>
</ns0:Body>
</SOAP-ENV:Envelope>
DEBUG:suds.client:headers = {'SOAPAction': u'"http://schemas.microsoft.com/sharepoint/soap/GetListCollection"', 'Content-Type': 'text/xml; charset=utf-8'}
DEBUG:suds.transport.http:sending:
URL:https://web.site.com/sites/Collection/_vti_bin/Lists.asmx
HEADERS: {'SOAPAction': u'"http://schemas.microsoft.com/sharepoint/soap/GetListCollection"', 'Content-Type': 'text/xml; charset=utf-8', 'Content-type': 'text/xml; charset=utf-8', 'Soapaction': u'"http://schemas.microsoft.com/sharepoint/soap/GetListCollection"'}
MESSAGE:
<?xml version="1.0" encoding="UTF-8"?><SOAP-ENV:Envelope xmlns:ns0="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://schemas.microsoft.com/sharepoint/soap/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"><SOAP-ENV:Header/><ns0:Body><ns1:GetListCollection/></ns0:Body></SOAP-ENV:Envelope>
ERROR:suds.client:<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:ns0="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://schemas.microsoft.com/sharepoint/soap/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header/>
<ns0:Body>
<ns1:GetListCollection/>
</ns0:Body>
</SOAP-ENV:Envelope>
DEBUG:suds.client:http failed:
403 FORBIDDEN
Traceback (most recent call last):
File "C:/Users/username/PycharmProjects/Suds/soap.py", line 14, in <module>
client.service.GetListCollection()
File "C:\Python27\lib\site-packages\suds\client.py", line 542, in __call__
return client.invoke(args, kwargs)
File "C:\Python27\lib\site-packages\suds\client.py", line 602, in invoke
result = self.send(soapenv)
File "C:\Python27\lib\site-packages\suds\client.py", line 649, in send
result = self.failed(binding, e)
File "C:\Python27\lib\site-packages\suds\client.py", line 708, in failed
raise Exception((status, reason))
Exception: (403, u'Forbidden')
However, the equivalent works perfectly fine in cURL (formatted for readability)
curl -u 'Username':'Password' --ntlm -X POST \
-H 'Content-Type: text/xml'\
-H 'SOAPAction: "http://schemas.microsoft.com/sharepoint/soap/GetListCollection"' \
"https://web.site.com/sites/Collection/_vti_bin/Lists.asmx" \
--data-binary #soapenvelope.xml
I also have not been able to find a way to force suds to run through a Fiddler proxy while also passing NTLM authentication. Outlined here, it seems to ignore proxy settings anyway, and I can't find a way to mix and match the local proxy through fiddler and have it attempt NTLM authentication against the Lists web service.
Environment Information (to make this easily searchable):
SharePoint 2010
Forms Based Authentication / FedAuth
External Oracle SSO using SAML v1.x

After a good amount of reading for the NTLM protocol, I figured out that cURL was sending the NTLM Type 1 message on the first request. However, suds (and PowerShells' New-WebServiceProxy) was not doing that. It was getting the 403 Forbidden, but did not carry on the handshake process since that was unexpected. Using the create_NTLM_NEGOTIATE_MESSAGE() in python-ntlm3, I was able to generate that Type 1 message.
By adding the 'Authorization' header with the Type 1 message, that forced the 401 Unauthorized, and caused the WindowsHttpAuthenticated to complete the handshake as expected.
from ntlm3 import ntlm
from suds.client import Client
from suds.transport.https import WindowsHttpAuthenticated
url = "https://web.site.com/sites/Collection/_vti_bin/Lists.asmx"
wsdl = (url + "?WSDL")
domain = 'DOM'
username = 'USER'
password = 'PASS'
transport = WindowsHttpAuthenticated(username=username,
password=password)
client = Client(url=wsdl,
location=url,
transport=transport)
negotiate = "%s\\%s" % (domain, username)
ntlmauth = 'NTLM %s' % ntlm.create_NTLM_NEGOTIATE_MESSAGE(negotiate).decode('ascii')
header = {'Authorization': ntlmauth}
client.set_options(headers=header)
client.service.GetList('Test')
The magical reason all this is happening is because we use Forms Based Authentication (FBA). Normal authentication requests get routed to an Oracle SSO, which is why (I believe) it is not responding normally and causing the 403 Unauthorized.
Hopefully this helps keep someone from banging their head against the wall.

Related

Python soap requests (VIES)

I'm trying to send soap request to check if clients NIP is valid via Python
import requests
url="http://ec.europa.eu/taxation_customs/vies/checkVatTestService.wsdl"
headers = {"content-type" : "application/soap+xml"}
body = """
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:urn="urn:ec.europa.eu:taxud:vies:services:checkVat:types">
<soapenv:Header/>
<soapenv:Body>
<urn:checkVat>
<urn:countryCode>SK</urn:countryCode>
<urn:vatNumber>2020016449</urn:vatNumber>
</urn:checkVat>
</soapenv:Body>
</soapenv:Envelope>
"""
response = requests.post(url, data = body, headers = headers)
print(response)
I always get code 200 which means it's invalid, but their webpage says it is valid.
I've seen other methods, but I really need to use this one.
Your code will produce the following result:
<Response [200]>
which is correct, but it's not what you expect, because I believe there are a few misunderstandings at play here.
First of all, <Response [200]> is the response of the HTTP request, not the response of the web service. Don't confuse the two. You are making a SOAP call over HTTP. That HTTP response will have a HTTP response code, which in this case is 200, and a SOAP payload with the response of the web service inside, and that's what's you are mostly interested in.
Second, you are not calling the web service endpoint. You are making a request to the WSDL of the web service. The two are different. You could have noticed that by replacing the last line of code with print(response.text) and you would have seen the WSDL content there.
The endpoint of the service is found inside the WSDL and it is http://ec.europa.eu/taxation_customs/vies/services/checkVatTestService. This is where you have to POST the request.
Third, based on the documentation in the WSDL, this seems a test service that gives you back all the possible responses of the service depending on which VAT number you use:
<xsd:documentation>
Specific disclaimer for this service -----------------------------------------
Here is the list of VAT Number to use to receive each kind of answer :
100 = Valid request with Valid VAT Number
200 = Valid request with an Invalid VAT Number
201 = Error : INVALID_INPUT
202 = Error : INVALID_REQUESTER_INFO
300 = Error : SERVICE_UNAVAILABLE
301 = Error : MS_UNAVAILABLE
302 = Error : TIMEOUT
400 = Error : VAT_BLOCKED
401 = Error : IP_BLOCKED
500 = Error : GLOBAL_MAX_CONCURRENT_REQ
501 = Error : GLOBAL_MAX_CONCURRENT_REQ_TIME
600 = Error : MS_MAX_CONCURRENT_REQ
601 = Error : MS_MAX_CONCURRENT_REQ_TIME
For all the other cases, The web service will responds with a "SERVICE_UNAVAILABLE" error.
</xsd:documentation>
Use this code instead:
import requests
url="http://ec.europa.eu/taxation_customs/vies/services/checkVatTestService"
headers = {"content-type" : "application/soap+xml"}
body = """
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:urn="urn:ec.europa.eu:taxud:vies:services:checkVat:types">
<soapenv:Header/>
<soapenv:Body>
<urn:checkVat>
<urn:countryCode>SK</urn:countryCode>
<urn:vatNumber>2020016449</urn:vatNumber>
</urn:checkVat>
</soapenv:Body>
</soapenv:Envelope>
"""
response = requests.post(url, data = body, headers = headers)
print(response.text)
When you call it you will get (formatting not included):
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<soap:Fault>
<faultcode>soap:Server</faultcode>
<faultstring>SERVICE_UNAVAILABLE</faultstring>
</soap:Fault>
</soap:Body>
</soap:Envelope>
which is exactly what I expect because the documentation says it: For all the other cases, The web service will responds with a "SERVICE_UNAVAILABLE" error.
If I want a a valid response I only need to change the VAT number to this within the SOAP request message: <urn:vatNumber>100</urn:vatNumber> and now the service responds with (formatting not included):
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<checkVatResponse xmlns="urn:ec.europa.eu:taxud:vies:services:checkVat:types">
<countryCode>SK</countryCode>
<vatNumber>100</vatNumber>
<requestDate>2022-03-27+02:00</requestDate>
<valid>true</valid>
<name>John Doe</name>
<address>123 Main St, Anytown, UK</address>
</checkVatResponse>
</soap:Body>
</soap:Envelope>

Getting '401 unauthorized' when trying to send a GET request to systeminfo.xml

I want to get the configuration of devices on my XProtect Corporate account, I am following this post
http://doc.developer.milestonesys.com/html/reference/protocols/imageserver_getdevices.html
but I am getting 401, unauthorized every time.
Until now I have managed to authenticate myself and got a token, now as i have the corporate account, I will need to send the token for the authorization, I am using requests lib in python to send a GET request like this
head = {'Authorization': 'Token {}'.format(myToken)}
response = requests.get('http://server-ip/rcserver/sysmteminfo.xml', headers=head)
print response
I am not sure about how to send my token in this get request
Any help will be highly appreciated
So I finally able to get the configuration of all the devices on the server. Problem was, the above approach works for XProtect Enterprise and XProtect Professional only, if you have an XProtect Corporate, you need a NTLM authentication and you have to call GetConfiguration function on the Server SOAP protocol.
r2= '<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><GetConfiguration xmlns="http://videoos.net/2/XProtectCSServerCommand"><token>' + str(myToken) + '</token></GetConfiguration></soap:Body></soap:Envelope>'
data_len = str(len(r2))
s.sendall("POST /ManagementServer/ServerCommandService.svc HTTP/1.1\r\nHost: server-ip\r\nContent-Type: text/xml; charset=utf-8\r\nAuthorization: Basic (base64 encoded '*[BASIC]\username:password*')\r\nContent-Length: "+data_len+"\r\nSOAPAction: http://videoos.net/2/XProtectCSServerCommand/IServerCommandService/GetConfiguration\r\n\r\n"+ r2)

How to login to website using soap zeep in python. check the xml of post

My wsdl Login method xml:
Host: 127.0.0.1
Content-Type: text/xml; charset=utf-8
Content-Length: length
SOAPAction: "http://tempuri.org/Login"
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Header>
<EhelplineWebAPISecurityContext xmlns="http://tempuri.org/">
<SessionId>string</SessionId>
<EHLWebServiceAPIKey>string</EHLWebServiceAPIKey>
</EhelplineWebAPISecurityContext>
</soap:Header>
<soap:Body>
<Login xmlns="http://tempuri.org/">
<LoginId>string</LoginId>
<password>string</password>
</Login>
</soap:Body>
</soap:Envelope>
Login method is there in wsdl.
from zeep import Client
URL="wsdl url"
from zeep import Client
from zeep.transports import Transport
from requests import Session
client= Client(URL)
client = client.service.Login(LoginId="id",password="password")
but i am getting this error.
File "C:/Python27/zeeplogin.py", line 7, in <module>
client = client.service.Login(LoginId="id",password="password")
File "C:\Python27\lib\site-packages\zeep\client.py", line 41, in __call__
self._op_name, args, kwargs)
File "C:\Python27\lib\site-packages\zeep\wsdl\bindings\soap.py", line 121, in send
return self.process_reply(client, operation_obj, response)
File "C:\Python27\lib\site-packages\zeep\wsdl\bindings\soap.py", line 178, in process_reply
return self.process_error(doc, operation)
File "C:\Python27\lib\site-packages\zeep\wsdl\bindings\soap.py", line 280, in process_error
detail=fault_node.find('detail'))
Fault: Object reference not set to an instance of an object.
My LoginId and password is right.
Have you tried this?
from zeep import Client
URL="wsdl url"
from zeep import Client
from zeep.transports import Transport
from requests import Session
client= Client(URL)
client = client.service.Login("id","password")

Travelport Galileo python SoapClient

I need to develop python soapclient for Travelport Galileo uAPI.
This is 30-day trial credentials for Travelport Universal API
Universal API User ID: Universal API/uAPI2514620686-0edbb8e4
Universal API Password: D54HWfck9nRZNPbXmpzCGwc95
Branch Code for Galileo (1G): P7004130
URLs: https://emea.universal-api.pp.travelport.com/B2BGateway/connect/uAPI/
This is quote from documentation galileo
HTTP Header
The HTTP header includes:
SOAP endpoints, which vary by:
Geographical region.
Requested service. In the preceding example, the HotelService is used for the endpoint; however, the service name is modified based on the request transaction.
gzip compression, which is optional, but strongly recommended. To accept gzip compression in the response, specify “Accept-Encoding: gzip,deflate” in the header.
Authorization, which follows the standard basic authorization pattern.
The text that follows “Authorization: Basic” can be encoded using Base 64. This functionality is supported by most programming languages.
The syntax of the authorization credentials must include the prefix "Universal API/" before the User Name and Password assigned by Travelport.
POST https://americas.universal-api.pp.travelport.com/
B2BGateway/connect/uAPI/HotelService HTTP/2.0
Accept-Encoding: gzip,deflate
Content-Type: text/xml;charset=UTF-8
SOAPAction: ""
Authorization: Basic UniversalAPI/UserName:Password
Content-Length: length
This is i my python code
import urllib2
import base64
import suds
class HTTPSudsPreprocessor(urllib2.BaseHandler):
def http_request(self, req):
message = \
"""
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:air="http://www.travelport.com/schema/air_v16_0" xmlns:com="http://www.travelport.com/schema/common_v13_0" -->
<soapenv:header>
<soapenv:body>
<air:availabilitysearchreq xmlns:air="http://www.travelport.com/schema/air_v16_0" xmlns:com="http://www.travelport.com/schema/common_v13_0" authorizedby="Test" targetbranch="P7004130">
<air:searchairleg>
<air:searchorigin>
<com:airport code="LHR">
</com:airport></air:searchorigin>
<air:searchdestination>
<com:airport code="JFK">
</com:airport></air:searchdestination>
<air:searchdeptime preferredtime="2011-11-08">
</air:searchdeptime></air:searchairleg>
</air:availabilitysearchreq>
</soapenv:body>
"""
auth = base64.b64encode('Universal API/uAPI2514620686-0edbb8e4:D54HWfck9nRZNPbXmpzCGwc95')
req.add_header('Content-Type', 'text/xml; charset=utf-8')
req.add_header('Accept', 'gzip,deflate')
req.add_header('Cache-Control','no-cache')
req.add_header('Pragma', 'no-cache')
req.add_header('SOAPAction', '')
req.add_header('Authorization', 'Basic %s'%(auth))
return req
https_request = http_request
URL = "https://emea.universal-api.pp.travelport.com/B2BGateway/connect/uAPI/"
https = suds.transport.https.HttpTransport()
opener = urllib2.build_opener(HTTPSudsPreprocessor)
https.urlopener = opener
suds.client.Client(URL, transport = https)
But it is not working.
Traceback (most recent call last):
File "soap.py", line 42, in <module>
suds.client.Client(URL, transport = https)
File "/usr/local/lib/python2.7/site-packages/suds/client.py", line 112, in __init__
self.wsdl = reader.open(url)
File "/usr/local/lib/python2.7/site-packages/suds/reader.py", line 152, in open
d = self.fn(url, self.options)
File "/usr/local/lib/python2.7/site-packages/suds/wsdl.py", line 136, in __init__
d = reader.open(url)
File "/usr/local/lib/python2.7/site-packages/suds/reader.py", line 79, in open
d = self.download(url)
File "/usr/local/lib/python2.7/site-packages/suds/reader.py", line 95, in download
fp = self.options.transport.open(Request(url))
File "/usr/local/lib/python2.7/site-packages/suds/transport/http.py", line 64, in open
raise TransportError(str(e), e.code, e.fp)
suds.transport.TransportError: HTTP Error 500: Dynamic backend host not specified
I'm trying to solve this problem for the past 2 weeks, so if you can, please advise me solution.
I think you can try to download WSDL files in ZIP archive from this url https://support.travelport.com/webhelp/uAPI/uAPI.htm#Getting_Started/Universal_API_Schemas_and_WSDLs.htm
So you will be able to generate your client classes using those WSDL files, because there is no WSDL endpoint on the https://emea.universal-api.pp.travelport.com/B2BGateway/connect/uAPI/
(like ?wsdl or /.wsdl)

Can I use HTTP Post Requests for SOAP? - SOAP and Django

I am wondering if I could use simply use HTTP POST Requests in order to implement a SOAP API.
If so how should I format and treat the requests?
Yep, I have done this in certain cases where SOAPpy did not work with a given schema. This should get you started.
import httplib
from xml.dom import minidom
http.request("POST", "/path/to/my/webservice", body=xml, headers = {
"Host": "myservername",
"Content-Type": "text/xml; charset=UTF-8",
"Content-Length": len(xml)
})
print minidom.parseString(http.getresponse().read())
For the content string, I would use SoapUI to make the requests manually, and then mimic the XML.
Yes, see http://www.w3schools.com/SOAP/soap_httpbinding.asp
A SOAP method is an HTTP
request/response that complies with
the SOAP encoding rules.
HTTP + XML = SOAP
A SOAP request could be an HTTP POST
or an HTTP GET request.
You'll sometimes find other transport mechanisms used for quality of service reasons, for instance using a messaging framework.
I found a great example of this in the simple-salesforce package. The code is in login.py and pasted below
soap_url = 'https://{domain}.salesforce.com/services/Soap/u/{sf_version}'
domain = 'test' if sandbox else 'login'
soap_url = soap_url.format(domain=domain, sf_version=sf_version)
username = escape(username)
password = escape(password)
# Check if token authentication is used
if 'security_token' in kwargs:
security_token = kwargs['security_token']
# Security Token Soap request body
login_soap_request_body = """<?xml version="1.0" encoding="utf-8" ?>
<env:Envelope
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
<env:Body>
<n1:login xmlns:n1="urn:partner.soap.sforce.com">
<n1:username>{username}</n1:username>
<n1:password>{password}{token}</n1:password>
</n1:login>
</env:Body>
</env:Envelope>""".format(username=username, password=password, token=security_token)
# Check if IP Filtering is used in cojuction with organizationId
elif 'organizationId' in kwargs:
organizationId = kwargs['organizationId']
# IP Filtering Login Soap request body
login_soap_request_body = """<?xml version="1.0" encoding="utf-8" ?>
<soapenv:Envelope
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:urn="urn:partner.soap.sforce.com">
<soapenv:Header>
<urn:CallOptions>
<urn:client>RestForce</urn:client>
<urn:defaultNamespace>sf</urn:defaultNamespace>
</urn:CallOptions>
<urn:LoginScopeHeader>
<urn:organizationId>{organizationId}</urn:organizationId>
</urn:LoginScopeHeader>
</soapenv:Header>
<soapenv:Body>
<urn:login>
<urn:username>{username}</urn:username>
<urn:password>{password}</urn:password>
</urn:login>
</soapenv:Body>
</soapenv:Envelope>""".format(
username=username, password=password, organizationId=organizationId)
else:
except_code = 'INVALID AUTH'
except_msg = 'You must submit either a security token or organizationId for authentication'
raise SalesforceAuthenticationFailed(except_code, except_msg)
login_soap_request_headers = {
'content-type': 'text/xml',
'charset': 'UTF-8',
'SOAPAction': 'login'
}
response = requests.post(soap_url,
login_soap_request_body,
headers=login_soap_request_headers,
proxies=kwargs.get('proxies', None))

Categories