Generate authorization digest header with urllib2 or requests - python

I am trying to generate the digest authorization header for use in a python test case. Because of the way the code base works, it is important that I am able to get the header as a string.
This header looks something like this
Authorization: Digest username="the_user", realm="my_realm", nonce="1389832695:d3c620a9e645420228c5c7da7d228f8c", uri="/some/uri", response="b9770bd8f1cf594dade72fe9abbb2f31"
I think my best bets are to use either urllib2 or the requests library.
With urllib2, I have gotten this far:
au=urllib2.HTTPDigestAuthHandler()
au.add_password("my_realm", "http://example.com/", "the_user", "the_password")
but I can't get the header out of that.
With requests, I have gotten this far:
requests.HTTPDigestAuth("the_user", "the_password")
But I when I try to use that, in a request, I am getting errors about setting the realm which I can't figure out how to do

If you're prepared to contort yourself around it, you can get the requests.auth.HTTPDigestAuth class to give you the right answer by doing something like this:
from requests.auth import HTTPDigestAuth
chal = {'realm': 'my_realm', 'nonce': '1389832695:d3c620a9e645420228c5c7da7d228f8c'}
a = HTTPDigestAuth('the_user', password)
a.chal = chal
print a.build_digest_header('GET', '/some/uri')
If I use 'the_password' as the user's password, that gives me this result:
Digest username="the_user", realm="my_realm", nonce="1389832695:d3c620a9e645420228c5c7da7d228f8c", uri="/some/uri", response="0b34daf411f3d9739538c7e7ee845e92"

When I tried #Lukasa answer I got an error:
'_thread._local' object has no attribute 'chal'
So I solved it in a slightly dirty way but it works:
from requests.auth import HTTPDigestAuth
chal = {'realm': 'my_realm',
'nonce': '1389832695:d3c620a9e645420228c5c7da7d228f8c'}
a = HTTPDigestAuth("username", "password")
a.init_per_thread_state()
a._thread_local.chal = chal
print(a.build_digest_header('GET', '/some/uri'))

Related

Postman API request in Python

I am trying to web-scrape data from https://www.mcmaster.com. They have provided me with a .pfx file and a passphrase. When making a GET request on Postman using their .json file, I input my website login/password and upload the .pfx certificate with its passphrase and everything works fine. Now I am trying to do this same thing but in Python, but am a bit unsure.
Here is my current Python code, I am unsure where I would put the website email/password login and how to successfully do a GET request.
import requests_pkcs12
from requests_pkcs12 import get
r = get('https://api.mcmaster.com/v1/login', pkcs12_filename='Schallert.pfx', pkcs12_password='mcmasterAPI#1901')
response = requests_pkcs12.get(r)
print(response.text)
Here is how I have it setup in Postman (Website email/pw login)
.PFX Certificate page
Postman has a built in feature where it will convert requests into code. You can do it like so:
On the far right click the Code Snippet Button (</>)
Once you are on that page, there is two available python options
Then all you need to do is copy the code into a Python file and add all your customizations (Should be already optimized)
One thing I’ll warn you about though is the URL. Postman doesn’t add
http:// or https:// to the URL, which means Python will throw a No
Scheme Supplied error.
Available Packages for Auto Conversion:
Requests
http.client
Meaning you will have to use a different package instead of requests_pkcs12
After a quick web search, it looks like you need to create a temporary certificate as a .pem file, which is then passed to the request.
from contextlib import contextmanager
from pathlib import Path
from tempfile import NamedTemporaryFile
import requests
from cryptography.hazmat.primitives.serialization import Encoding, PrivateFormat, NoEncryption
from cryptography.hazmat.primitives.serialization.pkcs12 import load_key_and_certificates
#contextmanager
def pfx_to_pem(pfx_path, pfx_password):
pfx = Path(pfx_path).read_bytes()
private_key, main_cert, add_certs = load_key_and_certificates(pfx, pfx_password.encode('utf-8'), None)
with NamedTemporaryFile(suffix='.pem') as t_pem:
with open(t_pem.name, 'wb') as pem_file:
pem_file.write(private_key.private_bytes(Encoding.PEM, PrivateFormat.PKCS8, NoEncryption()))
pem_file.write(main_cert.public_bytes(Encoding.PEM))
for ca in add_certs:
pem_file.write(ca.public_bytes(Encoding.PEM))
yield t_pem.name
with pfx_to_pem('your pfx file path', 'your passphrase') as cert:
requests.get(url, cert=cert, data=payload)
The package requests_pkcs12 is a wrapper written above the requests package. So, all the parameters that accept requests will accept by the requests_pkcs12.
Here is the source code proof for that. https://github.com/m-click/requests_pkcs12/blob/master/requests_pkcs12.py#L156
Also, from your screenshots, I understood that you are using POST not GET.
import json
from requests_pkcs12 import post
url = "https://api.mcmaster.com/v1/login"
payload = {'Username': 'yourusername',
'Password': 'yourpassword'}
resp = post(url, pkcs12_filename='Schallert.pfx', pkcs12_password='mcmasterAPI#1901', data=json.dumps(payload))
print(resp)
Footnote: I hope the password mcmasterAPI#1901 it's a fake one. if not please don't share any credentials in the platform.

How to make a request to get a picture from an ipcam?

I have some troubles getting the picture on my ip camera on python. I have an axis camera, I almost do the work on the rtsp link and cv2 video capture but when the hours go by I got an h264 error (here I asked for that problem).
So I decided to use a get request to get the picture, but now I got 401, error. Here is my code:
import requests
from requests.auth import HTTPBasicAuth
r = requests.get("http://xxx.xxx.xxx.xxx/jpg/image.jpg", auth=HTTPBasicAuth('xxx', 'xxx'))
print(r.status_code)
I also tried with out the HTTPBasicAuth but the same, I don't know how to get a good auth here.
Any help?
There is nothing wrong with your code. I have done the same code and works fine on my side. I would suggest you to verify the credentials that you have provided as a 401 response code is received when you provide wrong password or username.
Additionally, don't forget to pass the stream=True parameter inside the requests.get parameter otherwise the process will never successfully return anything even if the credentials actually work.
import requests
from requests.auth import HTTPBasicAuth
r = requests.get("http://xxx.xxx.xxx.xxx/jpg/image.jpg", auth=HTTPBasicAuth('xxx', 'xxx'), stream=True)
for streamDataChunks in r:
process_raw_image_data(streamDataChunks)

Python and socket - connet to specific path

I need to connect/send msg to http://localhost:8001/path/to/my/service, but I am not able to find how to do that. I know how to send if I only have localhost and 8001, but I need this specific path /path/to/my/service. There is where my service is running.
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(<full-url-to-my-service>)
s.sendall(bytes('Message', 'utf-8'))
Update
My service is running on localhost:8001/api/v1/namespaces/my_namespace/services/my_service:http/proxy. How can I connect to it with python?
As #furas told in the comments
socket is primitive object and it doesn't have specialized method for this - and you have to on your own create message with correct data. You have to learn HTTP protocol and use it to send
This is a sample snippet to send a GET request in python using requests library
import requests
URL = 'http://localhost:8001/path/to/my/service'
response_text = requests.get(URL).text
print(response_text)
This assumes the Content-Type that GET URL produces is text. If it is json, then a minor change is required
import requests
URL = 'http://localhost:8001/path/to/my/service'
response_json = requests.get(URL).json()
print(response_json)
There are other ways to achieve the same using other good frameworks like urllib, and so on.
Here is the documentation of requests library for reference
sendall() requires bytes, so String must be encoded.
s.sendall("foobar".encode())

How to connect with an API that requires username and password

I am trying to connect to the api as explained in http://api.instatfootball.com/ , It is supposed to be something like the following get /[lang]/data/[action].[format]?login=[login]&pass=[pass]. I know the [lang], [action] and [format] I need to use and I also have a login and password but don´t know how to access to the information inside the API.
If I write the following code:
import requests
r = requests.get('http://api.instatfootball.com/en/data/stat_params_players.json', auth=('login', 'pass'))
r.text
with the actual login and pass, I get the following output:
{"status":"error"}
This API requires authentication as parameters over an insecure connection, so be aware that this is highly lacking on the API part.
import requests
username = 'login'
password = 'password'
base_url = 'http://api.instatfootball.com/en/data/{endpoint}.json'
r = requests.get(base_url.format(endpoint='stat_params_players'), params={'login': username, 'pass': password})
data = r.json()
print(r.status_code)
print(r.text)
You will need to make a http-request using the URL. This will return the requested data in the response body. Depending on the [format] parameter, you will need to decode the data from xml / json to a native Python object.
As rdas already commented, you can use the request library for python (https://requests.readthedocs.io/en/master/). You will also find some code samples there. It will also do proper decoding of JSON data.
If you want to play around with the API a bit, you can use a tool like Postman for testing and debugging your requests. (https://www.postman.com/)

Why is my HTTP POST request data string (probably) being incorrectly encoded?

I'm having some trouble with twisted.web.client.Agent...
I think the string data in my post request isn't being formatted properly. I'm trying to do something analogous to this synchronous code:
from urllib import urlencode
import urllib2
page = 'http://example.com/'
id_string = 'this:is,my:id:string'
req = urllib2.Request(page, data=urlencode({'id': id_string})) # urlencode call returns 'id=this%3Ais%2Cmy%3Aid%3Astring'
resp = urllib2.urlopen(req)
Here's how I'm building my Agent request as of right now:
from urllib import urlencode
from StringIO import StringIO
page = 'http://example.com/'
id_string = 'my:id_string'
head = {'User-Agent': ['user agent goes here']}
data = urlencode({'id': id_string})
request = agent.request('POST', page, Headers(head), FileBodyProducer(StringIO(data)))
request.addCallback(foo)
Because of the HTTP response I'm getting (null JSON string), I'm beginning to suspect that the id is not being properly encoded in the POST request, but I'm not sure what I can be doing about it. Is using urlencode with the Agent.request call valid? Is there another way I should be encoding these things?
EDIT: Some kind IRC guys have suggested that the problem may stem from the fact that I didn't send the header information that indicates the data is encoded in a url string. I know remarkably little about this kind of stuff... Can anybody point me in the right direction?
As requested, here's my comment in the form of an answer:
HTTP requests with bodies should have the Content-Type header set (to tell the server how to interpret the bytes in the body); in this case, it seems the server is expecting URL-encoded data, like a web browser would send when a form is filled out.
urllib2.Request apparently defaults the content type for you, but the twisted library seems to need it to be set manually. In this case, you want a content type of application/x-www-form-urlencoded.

Categories