Office 365 REST API (Python) Mark Email as Read - python

I'm sure I'm doing something simple wrong, but I can't for the life of me figure out how to set the "IsRead" property to true. It's the last step of my process that gets a filtered list of messagesa and stores and processes any attachments.
According to the docs "IsRead" is writable: http://msdn.microsoft.com/office%5Coffice365%5CAPi/complex-types-for-mail-contacts-calendar#ResourcesMessage
http://msdn.microsoft.com/office%5Coffice365%5CAPi/mail-rest-operations#MessageoperationsUpdatemessages
I'm using python 2.7 and the requests module:
# once file acquired mark the email as read
params = {'IsRead':'True'}
base_email_url = u'https://outlook.office365.com/api/v1.0/me/messages/{0}'.format( msgId )
response = requests.patch(base_email_url, params, auth=(email,pwd))
log.debug( response )
The response that comes back is this:
{"error":{"code":"ErrorInvalidRequest","message":"Cannot read the request body."}}
What's the problem with my request?

At first glance it looks OK. I wonder if the Content-Type header isn't being set to "application/json" or something along those lines. Try getting a network trace and verify that the request looks something like:
PATCH https://outlook.office365.com/api/v1.0/Me/Messages('msgid') HTTP/1.1
Accept: application/json;odata.metadata=full
Authorization: Bearer <token>
Content-Type: application/json;odata.metadata=full
Host: outlook.office365.com
Content-Length: 24
Expect: 100-continue
Connection: Keep-Alive
{
"IsRead": "true"
}

Well I have an answer for myself and it is indeed a simple matter.
It was a mistake to not fully read how PATCH is different from GET or POST.
In short it's important to make sure your headers are set for the right content-type.
Here is the working code:
# once file acquired mark the email as read
changes = {u'IsRead':u'True'}
headers = {'Content-Type': 'application/json'}
json_changes = json.dumps(changes)
base_email_url = u'https://outlook.office365.com/api/v1.0/me/messages/{0}'.format( msgId )
response = requests.patch(base_email_url, data=json_changes, auth=__AUTH, headers=headers)
log.debug( response )

Related

Can I use Python requests library to send inconsistent requests

I try to write a python script containing a somewhat unusual HTTP request as part of learning about web attacks and solving the lab at
https://portswigger.net/web-security/request-smuggling/lab-basic-cl-te.
There, I need to issue a request containing both a Content-Length and a Transfer-Encoding header that are in disagreement.
My basic and still unmanipulated request looks like this and works as expected:
with requests.Session() as client:
client.verify = False
client.proxies = proxies
[...]
data = '0\r\n\r\nX'
req = requests.Request('POST', host, data=data)
prep = client.prepare_request(req)
client.send(prep)
[...]
Content-Length: 6\r\n
\r\n
0\r\n
\r\n
X
However, as soon as I add the Transfer-Encoding header, the request itself gets modified.
data = '0\r\n\r\nX'
req = requests.Request('POST', host, data=data)
prep = client.prepare_request(req)
prep.headers['Transfer-Encoding'] = 'chunked'
client.send(prep)
The request that is actually send down the wire is
[...]
Content-Length: 0\r\n
\r\n
whereas the expected request would be
[...]
Content-Length: 6\r\n
Transfer-Encoding: chunked\r\n
\r\n
0\r\n
\r\n
X
The same thing happens if I flip things around, prepare a chunked request and modify the Content-Length header afterwards:
def gen():
yield b'0\r\n'
yield b'\r\n'
yield b'X'
req = requests.Request('POST', host, data=gen())
prep = client.prepare_request(req)
prep.headers['Content-Length'] = '6'
client.send(prep)
Basically, the Transfer-Encoding header gets removed completely, the data is reinterpreted according to the chunking and the Content-Length header gets recalculated to match.
I was under the impression that preparing a request and manipulating its content before sending should send the modified content, but either this is a wrong assumption or I do things horribly wrong.
Is sending such a request possible this way or do I have to go onto a lower level to put arbitrary data on the wire?
requests is a good HTTP client, and as such will prevent you from generating bad HTTP queries. As writing bad HTTP queries will result in 400 errors in a lot of cases.
To generate syntax errors in HTTP queries you need to avoid using high level http clients (like a browser, but also like an http library). Instead you need togo down to the tcp/ip socket management (and maybe ssl also) and start writing the full HTTP protocol with your own code, no library.

Slack post payload in wrong format

How to get payload printed in json format
Getting payload like %7B%22type%22%3A%22interactive_message%22%2C%22actions%22%3A%5B%7B%22
2020-02-17 14:09:22,624 200 POST /slack/message_actions HTTP/1.1 Host: myipteventsbot.tunnel.com X-Real-IP: 3.93.14.114 Content-Length: 5159 User-Agent: Slackbot 1.0 (+https://api.slack.com/robots) Accept-Encoding: gzip,deflate Accept: application/json,*/*
X-Slack-Signature: v0=96ab9dd1e89047f6cea117313a17989eb25849f7a86ad1f1eff8a0be692238cf X-Slack-Request-Timestamp: 1581970162 Content-Type: application/x-www-form-urlencoded
payload=%7B%22type%22%3A%22interactive_message%22%2C%22actions%22%3A%5B%7B%22name%22%3A%22fullfilment_list%22%2C%22type%22%3A%22select%22%2C%22selected_options%22%3A%5B%7B%22value%22%3A%22spu%22%7D%5D%7D%5D%2C%22callback_id%22%3A%22test%22%2C%22team%22%3A%7B%22id%22%3A%22TFHGD33RV%22%2C%22domain%22%3A%22bby-sandbox-general%22%2C%22enterprise_id%22%3A%22EFGBDRLNP%22%2C%22enterprise_name%22%3A%22Best+Buy+Test%22%7D%2C%22channel%22%3A%7B%22id%22%3A%22DTHBLNXD0%22%2C%22name%22%3A%22directmessage%22%7D%2C%22user%22%3A%7B%22id%22%3A%22WLCLZ2LDP%22%2C%22name%22%3A%22a6002043%22%2C%
My code snippet
#app.route("/slack/message_actions", methods=["POST"])
def message_actions():
# Parse the request payload
form_json = json.loads(request.form["payload"])
# Verify that the request came from Slack
verify_slack_token(form_json["token"])
# Check to see what the user's selection was and update the message accordingly
selection = form_json["actions"][0]["selected_options"][0]["value"]
if selection == "spu":
message_text = "spu"
else:
message_text = "sth"
response = slack_client.api_call(
"chat.update",
channel=form_json["channel"]["id"],
ts=form_json["message_ts"],
text="One {} order coming up! :coffee:".format(message_text),
attachments=[] # empty `attachments` to clear the existing massage attachments
)
# Send an HTTP 200 response with empty body so Slack knows we're done here
return make_response("", 200)
figured out i need update my code as below
request.form.get('payload')

Not getting complete API details in Python like Postman

I am just trying to access an API through Postman and its wokring fine.The Postman header response is returning some details like below
Authentication-Token →/DwG7gAxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Connection →keep-alive
Content-Length →16
Content-Type →application/json;charset=UTF-8
Date →Tue, 25 Sep 2018 17:44:01 GMT
Server →Apache-Coyote/1.1
But when I am trying to do same in Python I am just receivig the response status.(200)
How can I get the above Authentication-Token etc details like Postman in python code.
import requests
import json
url = 'https://test-orchestrator.lmig.com/baocdp/rest/login/'
headers = {"Content-Type": "application/json"}
data1 = {"username":"abc", "password":"abc"}
print("Testing authentication for Remedy test environment...")
change_response=requests.post(url,data=json.dumps(data1),headers=headers)
print(change_response)
If you print change_response, it will most likely look like this <status [200]> or something to that effect. If you want to see the contents of the response, you can use the vars response.text, response.content, or response.headers (among others) or since this is a json response, you can use the method response.json() to convert the contents of the response into a dictionary full of native Python data types.
I would reccommend x = response.json(), as the contents of your response seem to contain an auth token that you will most likely need to communicate with this device further. You can then use auth+token = x[token_key] to isolate that token.

Is Python's Requests module adding data to a file that it is posting?

I'm trying to use the requests module to send a csv file to an API that uploads data into a database. Since the data is going into a database, the API is configured to reject files that have an unrecognized column name. The accepted columns are "id", "artist", "video". I have a test.csv file with just 1 row of data:
id,artist,video
1,The Shins,Phantom Limb
When I send the file to the api with the following curl request, it goes through successfully.
curl -i -u myUser:myPassword -X POST -T .\test.csv "http://destination.com/api/endpoint/create-or-update-records"
Here's the curl response message:
HTTP/1.1 100 Continue
HTTP/1.1 200 OK
Date: Sat, 11 Oct 2014 14:47:51 GMT
Content-Type: text/plain; charset=UTF-8
Content-Length: 0
Connection: close
However, when I try to send the file using the requests post method like this:
url = "http://destination.com/api/endpoint/create-or-update-records"
files = {'file': open("test.csv", "rb")}
r = requests.post(url, files=files, auth=("myUser","myPassword"))
The response I get back is this:
Unknown fields: '--5a6f03307ed74747904844625f76a82e'. Valid fields are: 'id', 'artist', 'video'
If I send the file again, I get the same message, but the "--lotsofcharacters" is now a difference set of characters.
I'm guessing I'm missing a setting or something, but I have combed the requests API and can't figure out what it is. What is different between the curl request and the requests request that is causing one to fail, and the other to go through?
You're not posting plain text to your server with requests. The documentation explicitly states that you use the files parameter when you wish to perform a multipart/form-data upload to the server. In this case all you need to do is
with open('test.csv', 'rb') as csv_file:
r = requests.post(url, data=csv_file, headers={'Content-Type': 'text/plain'}, auth=('user', 'password'))

How to extract JSON data from a response containing a header and body?

this is my first question posed to Stack Overflow, because typically I can find the solutions to my problem here, but for this particular situation, I cannot. I am writing a Python plugin for my compiler that outputs REST calls in various languages for interaction with an API. I am authenticating with the socket and ssl modules by sending a username and password in the request body in JSON form. Upon successful authentication, the API returns a response in the following format with important response data in the body:
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Date: Tue, 05 Feb 2013 03:36:18 GMT
Vary: Accept-Charset, Accept-Encoding, Accept-Language, Accept
Accept-Ranges: bytes
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: POST,OPTIONS,GET
Access-Control-Allow-Headers: Content-Type
Server: Restlet-Framework/2.0m5
Content-Type: text/plain;charset=ISO-8859-1
Content-Length: 94
{"authentication-token":"<token>","authentication-secret":"<secret>"}
This is probably a very elementary question for Pythonistas, given its powerful tools for String manipulation. But alas, I am a new programmer who started with Java. I would like to know what would be the best way to parse this entire response to obtain the "<token>" and "<secret>"? Should I use a search for a "{" and dump the substring into a json object? My intuition is telling me to try and use the re module, but I cannot seem to figure out how it would be used in this situation, since the pattern of the token and secret are obviously not predictable. Because I have opted to authenticate with a low-level module set, this response is one big String obtained by constructing the header and appending JSON data to it in the body, then executing the request and obtaining the response with the following code:
#Socket configuration and connection execution
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
conn = ssl.wrap_socket(sock, ca_certs = pem_file)
conn.connect((host, port))
conn.send(req)
response = conn.recv()
print(response)
The print statement outputs the first code sample. Any help or insight would be greatly appreciated!
HTTP headers are split from the rest of the body by a \r\n\r\n sequence. Do something like:
import json
...
(headers, js) = response.split("\r\n\r\n")
data = json.loads(js)
token = data["authentication-token"]
secret = data["authentication-secret"]
You'll probably want to check the response, etc, and various libraries (e.g. requests) can do all of this a whole lot easier for you.

Categories