Instantiating geopy.geocoders.GoogleV3 with proxies causes exception - python

I am trying to call GoogleV3 geolocator like this:
geolocator = GoogleV3(proxies={"http": "http://user:password#ip:port"})
address, (latitude, longitude) = geolocator.geocode('address to geocode')
and it raises:
AttributeError: OpenerDirector instance has no __call__ method
What am I doing wrong? How to fix it?

It is not possible in current implementation of GoogleV3 to pass user and password variables directly to urllib2.opener (GoogleV3 uses urllib2 behind the scenes).
Here is example how urllib2.opener should be called:
proxy_handler = urllib2.ProxyHandler({'http': 'http://www.example.com:3128/'})
proxy_auth_handler = urllib2.ProxyBasicAuthHandler()
proxy_auth_handler.add_password('realm', 'host', 'username', 'password')
opener = urllib2.build_opener(proxy_handler, proxy_auth_handler)
# This time, rather than install the OpenerDirector, we use it directly:
opener.open('http://www.example.com/login.html')
Sadly, but current GoogleV3 implementation does not use urllib2.ProxyBasicAuthHandler .
So, you need to extend it by modifying source: https://github.com/geopy/geopy/blob/master/geopy/geocoders/base.py
On the top add:
from urlparse import urlparse
then find 'if self.proxies is None:' code and replace it with:
if self.proxies is None:
self.urlopen = urllib_urlopen
else:
params = urlparse(proxies[1])
host = params.get('hostname')
username = params.get('username')
password = params.get('password')
if host and username and password:
proxy_auth_handler = urllib2.ProxyBasicAuthHandler()
proxy_auth_handler.add_password(None, host, username, password)
self.urlopen = build_opener(
ProxyHandler(self.proxies, proxy_auth_handler),
)

Related

LDAP python 3 (not working) but in PHP (working) where is the problem?

Python 3 code - not working:
import ldap3 as ldap
SERVER_NAME = 'serverip'
DN = 'dc=mydomain,dc=com,dc=br,ou=people'
USERNAME = 'no-reply'
PASSWORD = 'mypassword'
server = ldap.Server(SERVER_NAME,port=389, get_info='ALL')
connection = ldap.Connection(server, user='{}\\{}'.format(DN, USERNAME), password=PASSWORD)
connection.open()
if connection.bind():
print('Authenticated!')
else:
print('Not Authenticated')
print(connection.result)
This is print result
Not Authenticated
{'result': 34, 'description': 'invalidDNSyntax', 'dn': '', 'message': >'invalid DN', 'referrals': None, 'saslCreds': None, 'type': 'bindResponse'}
PHP code - working:
<?php
$username="no-reply";
$pass="mypassword";
$ldapconfig['host'] = 'serverip';
$ldapconfig['port'] = '389';
$ldapconfig['basedn'] = 'dc=mydomain,dc=com,dc=br';
$ldapconfig['usersdn'] = 'ou=people';
$ds=ldap_connect($ldapconfig['host'], $ldapconfig['port']);
ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3);
$dn="uid=".$username.",".$ldapconfig['usersdn'].",".$ldapconfig['basedn'];
if ($bind=ldap_bind($ds, $dn, $pass)) {
$ldap_result = #ldap_search($ds, $dn, "uid=".$username);
$result = ldap_get_entries($ds, $ldap_result);
print_r($result);
} else {
print_r(new Exception("Error"));
}
?>
I'm writing Python 3 code to use LDAP, but authentication is not working. However, my old code written in PHP works successfully. Where is the problem in my Python code?
The key words in the error message is 'invalid DN'. It's complaining about the format of the username in the call to ldap.Connection. You're doing:
user='{}\\{}'.format(DN, USERNAME)
Which would result in this string:
"dc=mydomain,dc=com,dc=br,ou=people\no-reply"
But that's not a valid format. Because you're using the distinguished name (DN) of the domain, it's assuming the whole string is a DN, but then the \no-reply at the end throws it off.
Your PHP code is doing this:
$dn = "uid=" . $username . "," . $ldapconfig['usersdn'] . "," . $ldapconfig['basedn'];
Which will result in this string:
"uid=no-reply,ou=people,dc=mydomain,dc=com,dc=br"
So modify your Python code to use the same value for the username.
You may even get away with passing just the username ("no-reply"), depending on what your LDAP server is. It may or may not work:
connection = ldap.Connection(server, user=USERNAME, password=PASSWORD)
I managed to solve the problem, it was necessary to rewrite the code simplifying the commands.
from ldap3 import Server, Connection, ALL, SUBTREE
username = 'no-reply'
password = 'mypassword'
serverName = 'ip_address'
DC = 'ou=people,dc=domain,dc=com,dc=contry'
server = Server(serverName, get_info=ALL)
conn = Connection(server, 'uid='+username+','+DC, password, auto_bind=True)
conn.search(search_base = DC,
search_filter = '(uid='+username+')',
search_scope = SUBTREE,
attributes = ['cn', 'givenName', 'company', 'title' ],
paged_size = 5)
print(conn.entries)

requests.session() object not recognized in another class

I am trying to pass my session object from one class to another. But I am not sure whats happening.
class CreateSession:
def __init__(self, user, pwd, url="http://url_to_hit"):
self.url = url
self.user = user
self.pwd = pwd
def get_session(self):
sess = requests.Session()
r = sess.get(self.url + "/", auth=(self.user, self.pwd))
print(r.content)
return sess
class TestGet(CreateSession):
def get_response(self):
s = self.get_session()
print(s)
data = s.get(self.url + '/some-get')
print(data.status_code)
print(data)
if __name__ == "__main__":
TestGet(user='user', pwd='pwd').get_response()
I am getting 401 for get_response(). Not able to understand this.
What's a 401?
The response you're getting means that you're unauthorised to access the resource.
A session is used in order to persist headers and other prerequisites throughout requests, why are you creating the session every time rather than storing it in a variable?
As is, the session should work the only issue is that you're trying to call a resource that you don't have access to. - You're not passing the url parameter either in the initialisation.
Example of how you can effectively use Session:
from requests import Session
from requests.exceptions import HTTPError
class TestGet:
__session = None
__username = None
__password = None
def __init__(self, username, password):
self.__username = username
self.__password = password
#property
def session(self):
if self.__session is None:
self.__session = Session()
self.__session.auth = (self.__user, self.__pwd)
return self.__session
#session.setter
def session(self, value):
raise AttributeError('Setting \'session\' attribute is prohibited.')
def get_response(self, url):
try:
response = self.session.get(url)
# raises if the status code is an error - 4xx, 5xx
response.raise_for_status()
return response
except HTTPError as e:
# you received an http error .. handle it here (e contains the request and response)
pass
test_get = TestGet('my_user', 'my_pass')
first_response = test_get.get_response('http://your-website-with-basic-auth.com')
second_response = test_get.get_response('http://another-url.com')
my_session = test_get.session
my_session.get('http://url.com')

How to set headers properly for suds soap client

I am trying to send a request to an api using suds client.
The request is formed like this:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:v20="https://API">
<soapenv:Header>
<v20:apiHeader>
<v20:username>username</v20:username>
<v20:password>password</v20:password>
<!--Optional:-->
<v20:name>?</v20:name>
</v20:apiHeader>
</soapenv:Header>
<soapenv:Body>
<v20:getLoginDetails/>
</soapenv:Body>
</soapenv:Envelope>
I am sending the request for this like:
client = Client('https://wsdl', faults=False)
client.set_options(headers={'username': username 'authToken': auth_token, 'name': ''})
client.service.getLoginDetails()
the error I am receiving is:
(<HTTPStatus.INTERNAL_SERVER_ERROR: 500>, (Fault){
faultcode = "soap:Server"
faultstring = "apiHeader should not be null"
detail = ""
})
Is this how I should be sending the request? It is definitely something to do with the apiHeader I think; not sure what though, I get the same error using this:
username = Element('username').setText(name)
password= Element('password').setText(pass)
header_list = [username, pass]
self.client.set_options(soapheaders=header_list)
return self.client.service.getLoginDetails()
#Code for your server:
from spyne import Application, rpc, ServiceBase, Unicode
from spyne.model.complex import ComplexModel
from spyne.protocol.soap import Soap11
from spyne.server.wsgi import WsgiApplication
import json
class RequestHeader(ComplexModel):
user_name = Unicode
user_pass = Unicode
class ServiceTest(ServiceBase):
__in_header__ = RequestHeader
#rpc(Unicode, _returns=Unicode)
def getLoginDetails(ctx, request):
values = json.loads(request)
values['user_name'] = ctx.in_header.user_name
values['user_pass'] = ctx.in_header.user_pass
return json.dumps(values)
application = WsgiApplication(Application([ServiceTest],
tns='yourcompany.request',
in_protocol=Soap11(validator='lxml'),
out_protocol=Soap11()
))
if __name__ == '__main__':
from wsgiref.simple_server import make_server
server = make_server('0.0.0.0', 8000, application)
server.serve_forever()
#Code for your client:
import json
from suds.client import Client
#Prepare the request in a JSON format
request = json.dumps({})
#Create a client for the API and make the request
api_client = Client('http://localhost:8000/?wsdl')
header = api_client.factory.create("RequestHeader")
header.user_name = 'my_user_name'
header.user_pass = 'my_user_pass'
api_client.set_options(soapheaders=header)
response = api_client.service.getLoginDetails(request)
print(response)
#it would print:
#{"user_name": "my_user_name", "user_pass": "my_user_pass"}
I've made this work by using something similar to this:
from suds.sax.element import Element
from suds.sax.attribute import Attribute
code = Element('serviceCode').setText('PABB4BEIJING')
pwd = Element('servicePwd').setText('QWERTPABB')
reqsoapheader = Element('ReqSOAPHeader').insert(code)
reqsoap_attribute = Attribute('xmlns', "http://schemas.acme.eu/")
reqsoapheader.append(reqsoap_attribute)
client.set_options(soapheaders=reqsoapheader)
If you wish to add several elements to the SOAP header:
userName = Element('UserName').setText(config['fco.user'])
password = Element('Password').setText(config['fco.pwd'])
fdxns = Attribute('xmlns', "http://fdx.co.il/Authentication")
for field in userName, password:
field.append(fdxns)
soapheaders = [userName, password]
client.set_options(soapheaders=tuple(soapheaders))
This is working for me with Python 2.7, suds 0.4.

urllib3 define server_hostname ( for ssl check ) or ip where request [duplicate]

In my project I'm handling all HTTP requests with python requests library.
Now, I need to query the http server using specific DNS - there are two environments, each using its own DNS, and changes are made independently.
So, when the code is running, it should use DNS specific to the environment, and not the DNS specified in my internet connection.
Has anyone tried this using python-requests? I've only found solution for urllib2:
https://stackoverflow.com/questions/4623090/python-set-custom-dns-server-for-urllib-requests
requests uses urllib3, which ultimately uses httplib.HTTPConnection as well, so the techniques from https://stackoverflow.com/questions/4623090/python-set-custom-dns-server-for-urllib-requests (now deleted, it merely linked to Tell urllib2 to use custom DNS) still apply, to a certain extent.
The urllib3.connection module subclasses httplib.HTTPConnection under the same name, having replaced the .connect() method with one that calls self._new_conn. In turn, this delegates to urllib3.util.connection.create_connection(). It is perhaps easiest to patch that function:
from urllib3.util import connection
_orig_create_connection = connection.create_connection
def patched_create_connection(address, *args, **kwargs):
"""Wrap urllib3's create_connection to resolve the name elsewhere"""
# resolve hostname to an ip address; use your own
# resolver here, as otherwise the system resolver will be used.
host, port = address
hostname = your_dns_resolver(host)
return _orig_create_connection((hostname, port), *args, **kwargs)
connection.create_connection = patched_create_connection
and you'd provide your own code to resolve the host portion of the address into an ip address instead of relying on the connection.create_connection() call (which wraps socket.create_connection()) to resolve the hostname for you.
Like all monkeypatching, be careful that the code hasn't significantly changed in later releases; the patch here was created against urllib3 version 1.21.1. but should work for versions as far back as 1.9.
Note that this answer was re-written to work with newer urllib3 releases, which have added a much more convenient patching location. See the edit history for the old method, applicable to version < 1.9, as a patch to the vendored urllib3 version rather than a stand-alone installation.
You should look into the TransportAdapters, including the source code. The documentation on them isn't great, but they give low-level access to a lot of the functionality described in RFC 2818 and RFC 6125. In particular, those documents encourage (require?) client-side code to support application-specific DNS for the purpose of checking certificates' CommonName and SubjectAltName. The keyword argument you need in those calls is "assert_hostname". Here's how to set it with the requests library:
from requests import Session, HTTPError
from requests.adapters import HTTPAdapter, DEFAULT_POOLSIZE, DEFAULT_RETRIES, DEFAULT_POOLBLOCK
class DNSResolverHTTPSAdapter(HTTPAdapter):
def __init__(self, common_name, host, pool_connections=DEFAULT_POOLSIZE, pool_maxsize=DEFAULT_POOLSIZE,
max_retries=DEFAULT_RETRIES, pool_block=DEFAULT_POOLBLOCK):
self.__common_name = common_name
self.__host = host
super(DNSResolverHTTPSAdapter, self).__init__(pool_connections=pool_connections, pool_maxsize=pool_maxsize,
max_retries=max_retries, pool_block=pool_block)
def get_connection(self, url, proxies=None):
redirected_url = url.replace(self.__common_name, self.__host)
return super(DNSResolverHTTPSAdapter, self).get_connection(redirected_url, proxies=proxies)
def init_poolmanager(self, connections, maxsize, block=DEFAULT_POOLBLOCK, **pool_kwargs):
pool_kwargs['assert_hostname'] = self.__common_name
super(DNSResolverHTTPSAdapter, self).init_poolmanager(connections, maxsize, block=block, **pool_kwargs)
common_name = 'SuperSecretSarahServer'
host = '192.168.33.51'
port = 666
base_url = 'https://{}:{}/api/'.format(common_name, port)
my_session = Session()
my_session.mount(self.base_url.lower(), DNSResolverHTTPSAdapter(common_name, host))
user_name = 'sarah'
url = '{}users/{}'.format(self.base_url, user_name)
default_response_kwargs = {
'auth': (NAME, PASSWORD),
'headers': {'Content-Type': 'application/json'},
'verify': SSL_OPTIONS['ca_certs'],
'cert': (SSL_OPTIONS['certfile'], SSL_OPTIONS['keyfile'])
}
response = my_session.get(url, **default_response_kwargs)
I use common_name for the name expected to be on the certificate and how your code will reference the desired machine. I use host for a name recognized by the external world - FQDN, IP, DNS entry, ... Of course, the SSL_OPTIONS dictionary (in my example) must list appropriate certificate / key filenames on your machine. (Plus, NAME and PASSWORD should resolve to correct strings.)
A customized HTTPAdapter will do the trick.
Don't forget to set server_hostname to enable SNI.
import requests
class HostHeaderSSLAdapter(requests.adapters.HTTPAdapter):
def resolve(self, hostname):
# a dummy DNS resolver
import random
ips = [
'104.16.89.20', # CloudFlare
'151.101.2.109', # Fastly
]
resolutions = {
'cdn.jsdelivr.net': random.choice(ips),
}
return resolutions.get(hostname)
def send(self, request, **kwargs):
from urllib.parse import urlparse
connection_pool_kwargs = self.poolmanager.connection_pool_kw
result = urlparse(request.url)
resolved_ip = self.resolve(result.hostname)
if result.scheme == 'https' and resolved_ip:
request.url = request.url.replace(
'https://' + result.hostname,
'https://' + resolved_ip,
)
connection_pool_kwargs['server_hostname'] = result.hostname # SNI
connection_pool_kwargs['assert_hostname'] = result.hostname
# overwrite the host header
request.headers['Host'] = result.hostname
else:
# theses headers from a previous request may have been left
connection_pool_kwargs.pop('server_hostname', None)
connection_pool_kwargs.pop('assert_hostname', None)
return super(HostHeaderSSLAdapter, self).send(request, **kwargs)
url = 'https://cdn.jsdelivr.net/npm/bootstrap/LICENSE'
session = requests.Session()
session.mount('https://', HostHeaderSSLAdapter())
r = session.get(url)
print(r.headers)
r = session.get(url)
print(r.headers)
I know this is an old thread but here is my python3 compatible solution using tldextract and dnspython. I've left some commented out code to illustrate how to debug and set additional session parameters.
#!/usr/bin/env python3
import sys
from pprint import pprint as pp
import requests
import dns.resolver # NOTE: dnspython package
import tldextract
class CustomAdapter(requests.adapters.HTTPAdapter):
def __init__(self, nameservers):
self.nameservers = nameservers
super().__init__()
def resolve(self, host, nameservers, record_type):
dns_resolver = dns.resolver.Resolver()
dns_resolver.nameservers = nameservers
answers = dns_resolver.query(host, record_type)
for rdata in answers:
return str(rdata)
def get_connection(self, url, proxies=None):
ext = tldextract.extract(url)
fqdn = ".".join([ ext.subdomain, ext.domain, ext.suffix ])
print("FQDN: {}".format(fqdn))
a_record = self.resolve(fqdn, nameservers, 'A')
print("A record: {}".format(a_record))
resolved_url = url.replace(fqdn, a_record) # NOTE: Replace first occurrence only
print("Resolved URL: {}".format(resolved_url))
return super().get_connection(resolved_url, proxies=proxies)
if __name__ == "__main__":
if len(sys.argv) != 2:
print("Usage: {} <url>".format(sys.argv[0]))
sys.exit(0)
url = sys.argv[1]
nameservers = [
'208.67.222.222', # NOTE: OpenDNS
'8.8.8.8' # NOTE: Google
]
session = requests.Session()
session.mount(url, CustomAdapter(nameservers))
parameters = {
# "headers": {'Content-Type': 'application/json'},
# "timeout" : 45,
# "stream" : True
# "proxies" : {
# "http": "http://your_http_proxy:8080/",
# "https": "http://your_https_proxy:8081/"
# },
# "auth": (name, password),
# ...
}
response = session.get(url, **parameters)
pp(response.__dict__)
And here it the console output :
$ ./run.py http://www.test.com
FQDN: www.test.com
A record: 69.172.200.235
Resolved URL: http://69.172.200.235/
{'_content': b'<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3'
b'.org/TR/html4/strict.dtd">\n<html>\n<head>\n<meta http-equiv="C'
b'ontent-Type" content="text/html; charset=iso-8859-1">\n<meta '
b'http-equiv="Content-Script-Type" content="text/javascript">\n'
b'<script type="text/javascript">\nfunction getCookie(c_name) {'
b' // Local function for getting a cookie value\n if (docume'
b'nt.cookie.length > 0) {\n c_start = document.cookie.in'
b'dexOf(c_name + "=");\n if (c_start!=-1) {\n c_st'
b'art=c_start + c_name.length + 1;\n c_end=document.cook'
b'ie.indexOf(";", c_start);\n\n if (c_end==-1) \n '
b' c_end = document.cookie.length;\n\n return unescape('
b'document.cookie.substring(c_start,c_end));\n }\n }\n '
b' return "";\n}\nfunction setCookie(c_name, value, expiredays'
b') { // Local function for setting a value of a cookie\n va'
b'r exdate = new Date();\n exdate.setDate(exdate.getDate()+e'
b'xpiredays);\n document.cookie = c_name + "=" + escape(valu'
b'e) + ((expiredays==null) ? "" : ";expires=" + exdate.toGMTString'
b'()) + ";path=/";\n}\nfunction getHostUri() {\n var loc = doc'
b"ument.location;\n return loc.toString();\n}\nsetCookie('YPF8"
b"827340282Jdskjhfiw_928937459182JAX666', '171.68.244.56', 10)"
b';\ntry { \n location.reload(true); \n} catch (err1) { \n '
b' try { \n location.reload(); \n } catch (err2) { '
b' \n \tlocation.href = getHostUri(); \n } \n}\n</scrip'
b't>\n</head>\n<body>\n<noscript>This site requires JavaScript an'
b'd Cookies to be enabled. Please change your browser settings or '
b'upgrade your browser.</noscript>\n</body>\n</html>\n',
'_content_consumed': True,
'_next': None,
'connection': <requests.adapters.HTTPAdapter object at 0x109130e48>,
'cookies': <RequestsCookieJar[]>,
'elapsed': datetime.timedelta(microseconds=992676),
'encoding': 'ISO-8859-1',
'headers': {'Server': 'nginx/1.14.2', 'Date': 'Wed, 01 May 2019 18:01:58 GMT', 'Content-Type': 'text/html', 'Transfer-Encoding': 'chunked', 'Connection': 'keep-alive', 'Keep-Alive': 'timeout=20', 'X-DIS-Request-ID': '2a5057a7c7b8a93dd700856c48fda74a', 'P3P': 'CP="NON DSP COR ADMa OUR IND UNI COM NAV INT"', 'Cache-Control': 'no-cache', 'Content-Encoding': 'gzip'},
'history': [<Response [302]>],
'raw': <urllib3.response.HTTPResponse object at 0x1095b90b8>,
'reason': 'OK',
'request': <PreparedRequest [GET]>,
'status_code': 200,
'url': 'https://www.test.com/'}
Hope this helps.

modifying user email using jenkins api with python

Is there a way to modify user details (email in particular) using jenkins api?
Posting a modified json file to {root}/user/{username}/api/json does not seem to modify the underlying data:
import urllib2
import json
root = 'your_url'
username = 'your_username'
user_url = root +'/user/{username}/api/json'.format(username=username)
orig_d = json.loads((urllib2.urlopen(user_url).read()))
d = dict(orig_d)
d['fullName'] = 'XXXXX'
json_data = json.dumps(d)
request = urllib2.Request(user_url)
request.add_header('Content-type', 'application/json')
new_d = json.loads(urllib2.urlopen(request, json_data).read())
print new_d, '\n', orig_d
assert orig_d!=new_d, 'They are equal!'
After looking at jenkins source code model/User.java it looks like the only "POST" configuration that is supported on User model is done through doConfigSubmit() member function. So I eneded up faking web POST form at jenkins_url/user/{username}/configSubmit. The following snippet worked for me:
import urllib2, urllib
import json
root = jenkins_url
username = username
user_url = root +'/user/{username}/api/json'.format(username=username)
config_url = root + '/user/{username}/configSubmit'.format(username=username)
d = json.loads((urllib2.urlopen(user_url).read()))
fullname = d['fullName']
description = d['description']
new_email = 'new_user_email'
post_d = {"userProperty1": {"address": new_email}}
request = urllib2.Request(config_url)
values = {'json': json.dumps(post_d),
'fullName': fullname,
'email.address': new_email,
'description': description}
data = urllib.urlencode(values)
response = urllib2.urlopen(request, data).read()

Categories