How to enable CORS in python - python-2.7

Let me start this with, I do not know python, I've had maybe 1 day going through the python tutorials. The situation is this. I have an angular app that has a python app hosted with Apache on a vm in an iframe. I didn't write the python app, but another developer wrote me an endpoint where I am supposed to be able to post from my angular app.
The developer who made the python endpoint is saying that there is something wrong with my request but I am fairly certain there isn't anything wrong. I am almost 100% certain that the problem is that there are no CORS headers in the response and/or the response is not set up to respond to the OPTIONS method. Below is the entirety of the python endpoint:
import os, site, inspect
site.addsitedir(os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))+"/../")
import json
from datetime import datetime
import pymongo
from Config import Config
def application(environ, start_response):
response = environ['wsgi.input'].read(int(environ['CONTENT_LENGTH']))
if response:
json_response = json.loads(response)
document = {
'payment_id': json_response['payment_id'],
'log': json_response['log'],
'login_id': json_response['login_id'],
'browser': environ.get('HTTP_USER_AGENT', None),
'ip_address': environ.get('REMOTE_ADDR', None),
'created_at': datetime.utcnow(),
}
client = pymongo.MongoClient(Config.getValue('MongoServer'))
db = client.updatepromise
db.PaymentLogs.insert(document)
start_response('200 OK', [('Content-Type', 'application/json')
return '{"success": true}'
start_response('400 Bad Request', [('Content-Type', 'application/json')])
return '{"success": false}'
I have attempted the following to make this work: I added to both start_response functions more headers so the code looks like this now:
start_response('201 OK', [('Content-Type', 'application/json',
('Access-Control-Allow-Headers','authorization'),
('Access-Control-Allow-Methods','HEAD, GET, POST, PUT, PATCH, DELETE'),
('Access-Control-Allow-Origin','*'),
('Access-Control-Max-Age','600'))])
Not: I did this both with the 200 and the 400 response at first, and saw no change at all in the response, then just for the heck of it, I decided to change the 200 to a 201, this also did not come through on the response so I suspect this code isn't even getting run for some reason.
Please help, python newb here.
Addendum, i figured this would help, here is what the Headers look like in the response:
General:
Request URL: http://rpc.local/api/payment_log_api.py
Request Method: OPTIONS
Status Code: 200 OK
Remote Address: 10.1.20.233:80
Referrer Policy: no-referrer-when-downgrade
Response Headers:
Allow: GET,HEAD,POST,OPTIONS
Connection: Keep-Alive
Content-Length: 0
Content-Type: text/x-python
Date: Fri, 27 Apr 2018 15:18:55 GMT
Keep-Alive: timeout=5, max=100
Server: Apache/2.4.18 (Ubuntu)
Request Headers:
Accept: */*
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Access-Control-Request-Headers: authorization,content-type
Access-Control-Request-Method: POST
Connection: keep-alive
Host: rpc.local
Origin: http://10.1.20.61:4200
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36

Here it is. Just add this to the application right at the beginning:
def application(environ, start_response):
if environ['REQUEST_METHOD'] == 'OPTIONS':
start_response(
'200 OK',
[
('Content-Type', 'application/json'),
('Access-Control-Allow-Origin', '*'),
('Access-Control-Allow-Headers', 'Authorization, Content-Type'),
('Access-Control-Allow-Methods', 'POST'),
]
)
return ''

For Python with CGI, I found this to work:
print '''Access-Control-Allow-Origin: *\r\n''',
print '''Content-Type: text/html\r\n'''
Don't forget to enable CORS on the other side as well, e.g., JavaScript jQuery:
$.ajax({ url: URL,
type: "GET",
crossDomain: true,
dataType: "text", etc, etc

Related

Accessing request data object

I am sending the following post Data to django Rest API
Request URL: http://localhost:8000/polls/
Request Method: POST
Status Code: 200 OK
Remote Address: 127.0.0.1:8000
Referrer Policy: no-referrer-when-downgrade
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: http://localhost:8100
Content-Length: 90
Content-Type: text/html; charset=utf-8
Date: Mon, 18 Jun 2018 11:01:54 GMT
Server: WSGIServer/0.2 CPython/3.6.5
Vary: Origin
X-Frame-Options: SAMEORIGIN
Accept: application/json, text/plain, */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Connection: keep-alive
Content-Length: 83
content-type: text/plain
Host: localhost:8000
Origin: http://localhost:8100
Referer: http://localhost:8100/
User-Agent: Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.170 Mobile Safari/537.36
0:
{id: "1", username: "admin", password: "admin", user_group_id: "1", status: "1"}
The sent here will first be authenticated against the one saved in the database. I am trying to access this data but failing to do so .
When I process the data using json parse
case 1.
def dunction(request)
data = json.loads(request.body.decode("utf-8"))
vak=data
return HttpResponse(vak)
Then the following response is received
{'id': '1', 'username': 'admin', 'password': 'admin', 'user_group_id': '1', 'status': '1'}
case 2.
When manipulating the same code
data = json.loads(request.body.decode("utf-8"))
vak=data[0]
return HttpResponse(vak)
received response
idusernamepassworduser_group_idstatus
Case 3.
def dunction(request):
data = json.loads(request.body.decode("utf-8"))
vak=data.username
return HttpResponse(vak)
throws error 'list' object has no attribute 'username'
FYI,
Here I am trying to create a custom authentication function which authenticates the userData and then sends data back to the server.
'DEFAULT_AUTHENTICATION_CLASSES':
'polls.authentication.UserAuthentication',
HttpResponse takes an iterable. You are passing it various things that are in fact iterable: in the first case, a list, so it prints the (only) entry in that list, which is a dictionary; in the second case, you pass it a dict, so it iterates through the dict which gives the keys only. In the third case, for some reason you try and use object notation to access a key of a dict which is inside a list, which won't work at all.
I'm not sure what your actual question is, but if you do want to access the username you would need data[0]['username'].
Note though that the point of DRF is to abstract away a lot of this stuff; you should use the built-in functionality rather than doing any of this.
Use request.data to get the data for processing. return message along with HTTP status code like below
def dunction(request):
username=request.data['username']
password=request.data['password']
group_id=request.data['user_group_id']
status=request.data['status']
return Response({"status":"success"}, status=status.HTTP_200_OK)

Python request.session JIRA "You do not have the permission to see the specified issue"

Firstly, I am aware of the solution presented here: JIRA REST API to get work log - "You do not have the permission to see the specified issue" ... As well the linked blog post (penned by #Nick Josevski), which whilst useful, doesn't address my specifc problem, which could be something trivial...
With following Python 2.7.3 code...
import requests
import getpass
import json
jira_user = raw_input("Username: ")
jira_pass = getpass.getpass()
session = requests.Session()
session.verify = jira_ca_certs # Our internal certs
auth_info = {"username": jira_user, "password": jira_pass}
login_url = 'http://JIRA_SERVER.com/login.jsp'
session.post(login_url, data=auth_info)
I generate the cookies post basic authenticatation to jira (note: I am using "http" without specifying the PORT to authenticate with the login page).. As session automatically holds the returned cookies, I can use session.cookies to set the header:
cookies = requests.utils.dict_from_cookiejar(session.cookies)
headers = {'Content-type': 'application/json', 'cookie': cookies}
Following which, I test the captured cookies with a basic get to the secure JIRA url, using https + PORT:
base = session.get('https://JIRA_SERVER.com:1234', headers=headers)
print 'base: ', base
The above, as expected, returns (though this might not be a valid test?) ...
base: <Response [200]>
Now to test the code for it's intended purpose. I extend the url for a specific JIRA issue, using the same approach:
jira = session.get('https://JIRA_SERVER.com:1234/rest/api/latest/issue/KRYP-6207', headers=headers)
print 'issue: ', jira
print jira.json()
With JSON output, I get a response stating I do not have the permission:
issue: <Response [401]>
{u'errorMessages': [u'You do not have the permission to see the specified issue.', u'Login Required'], u'errors': {}}
The cookies returned, that I use in the header, are:
headers: {'cookie': 'atlassian.xsrf.token=XXXXXXXXXXXXXXXX|lout; Path=/, JSESSIONID=XXXXXXXXXXXXXXXX; Path=/'}
I don't know why this works for the base url, but not the issue url.. I have used Chrome POSTMAN to check the cookies being returned, and they are the same as those listed above i.e. atlassian.xsrf.token, and JSESSIONID.
Hoping someone here can tell me what I am doing wrong! Thanks in advance ...
Maybe you are not correctly logged in? based on the requests I saw in webbrowser developer tools, I created following requests and it worked with our JIRA:
import requests
import getpass
import json
jira_user = raw_input("Username: ")
jira_pass = getpass.getpass()
session = requests.Session()
headers = {
'Host': 'our.jira.com',
'User-Agent': 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:56.0) Gecko/20100101 Firefox/56.0',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language': 'en-US,en;q=0.5',
'Referer': 'https://our.jira.com/login.jsp',
'Content-Type': 'application/x-www-form-urlencoded',
'Connection': 'keep-alive',
'Upgrade-Insecure-Requests': '1',
}
data = [
('os_username', jira_user),
('os_password', jira_pass),
('os_destination', ''),
('user_role', ''),
('atl_token', ''),
('login', 'Anmelden'),
]
loginPost = session.post('https://our.jira.com/login.jsp', headers=headers, data=data)
xsrf_token = session.cookies.get_dict()['atlassian.xsrf.token']
jsessionid = session.cookies.get_dict()['JSESSIONID']
cookies = {
'atlassian.xsrf.token': xsrf_token,
'JSESSIONID': jsessionid,
}
headers = {
'Host': 'our.jira.com',
'User-Agent': 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:56.0) Gecko/20100101 Firefox/56.0',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language': 'en-US,en;q=0.5',
'Connection': 'keep-alive',
'Upgrade-Insecure-Requests': '1',
}
r = session.get('https://our.jira.com/rest/api/latest/issue/SDN-206', headers=headers, cookies=cookies)
print r.json()

Django+Angular CORS not working with POST

My Angular4 app (running on http://127.0.0.1:4200 development server) is supposed to access a django REST backend on the web. The backend is under my control and is available only via HTTPS (running Apache that tunnels the request to a gunicorn server running on an internal port). Let's say that this is https://example.com/. For historical reasons, logging the user in is done using sessions, because I want the users to be able to also use Django's admin interface after they logged in. The workflow is as follows:
Users opens http://127.0.0.1:4200, I perform a GET request to https://example.com/REST/is_logged_in which returns a 403 when the user isn't logged in via sessions yet, 200 otherwise. In the former case, the user is redirected to https://example.com/login/, rendered by Django's template engine, allowing the user to log in. Once logged in, the user is redirected to http://127.0.0.1:4200
When clicking on some button in my Angular UI, a POST request is performed. This post request fails with 403, even though the preflight OPTIONS request explicitly lists POST as allowed actions.
Here is my CORS configuration in Django:
NG_APP_ABSOLUTE_URL = 'http://127.0.0.1:4200'
# adapt Django's to Angular's presumed XSRF cookie/header names
CSRF_COOKIE_NAME = "XSRF-TOKEN"
CSRF_HEADER_NAME = "HTTP_X_XSRF_TOKEN"
CORS_ORIGIN_WHITELIST = (
urlparse(NG_APP_ABSOLUTE_URL).netloc
)
CSRF_TRUSTED_ORIGINS = (
urlparse(NG_APP_ABSOLUTE_URL).netloc
)
CORS_ALLOW_HEADERS = default_headers + (
'x-xsrf-token',
)
CORS_ALLOW_CREDENTIALS = True
This is what Chrome reports for the (successful, 200) first REST GET request to check whether the user is logged in (after he successfully did) in the response:
Access-Control-Allow-Credentials:true
Access-Control-Allow-Origin:http://127.0.0.1:4200
Allow:GET, HEAD, OPTIONS
Connection:close
Content-Type:application/json
Date:Wed, 26 Apr 2017 15:09:26 GMT
Server:gunicorn/19.6.0
Set-Cookie:XSRF-TOKEN=...; expires=Wed, 25-Apr-2018 15:09:26 GMT; Max-Age=31449600; Path=/
Transfer-Encoding:chunked
Vary:Accept,Cookie,Origin
X-Frame-Options:SAMEORIGIN
The corresponding request had this:
Cookie:sessionid=...; XSRF-TOKEN=...
Host:example.com
Origin:http://127.0.0.1:4200
Referer:http://127.0.0.1:4200/
Now, to the actual problem:
Preflight request:
Request URL:https://example.com/REST/change_user_data/
Request Method:OPTIONS
Status Code:200 OK
Access-Control-Request-Headers:content-type
Access-Control-Request-Method:POST
Connection:keep-alive
Host:example.com
Origin:http://127.0.0.1:4200
Referer:http://127.0.0.1:4200/dashboard/account
Preflight response:
Access-Control-Allow-Credentials:true
Access-Control-Allow-Headers:accept, accept-encoding, authorization, content-type, dnt, origin, user-agent, x-csrftoken, x-requested-with, x-xsrf-token
Access-Control-Allow-Methods:DELETE, GET, OPTIONS, PATCH, POST, PUT
Access-Control-Allow-Origin:http://127.0.0.1:4200
Access-Control-Max-Age:86400
Connection:close
Content-Length:0
Content-Type:text/html; charset=utf-8
Date:Wed, 26 Apr 2017 15:36:56 GMT
Server:gunicorn/19.6.0
Vary:Origin
X-Frame-Options:SAMEORIGIN
Now my failing (403) POST request:
Accept:application/json
Accept-Encoding:gzip, deflate, br
Accept-Language:de-DE,de;q=0.8,en-US;q=0.6,en;q=0.4
Connection:keep-alive
Content-Length:60
Content-Type:application/json
Cookie:sessionid=...; XSRF-TOKEN=...
Host:example.com
Origin:http://127.0.0.1:4200
Referer:http://127.0.0.1:4200/dashboard/account
The response headers:
HTTP/1.1 403 Forbidden
Date: Wed, 26 Apr 2017 15:36:56 GMT
Server: gunicorn/19.6.0
Vary: Accept,Cookie,Origin
X-Frame-Options: SAMEORIGIN
Content-Type: application/json
Access-Control-Allow-Credentials: true
Allow: POST, OPTIONS
Access-Control-Allow-Origin: http://127.0.0.1:4200
Set-Cookie: XSRF-TOKEN=...; expires=Wed, 25-Apr-2018 15:36:56 GMT; Max-Age=31449600; Path=/
Connection: close
Transfer-Encoding: chunked
Why wouldn't this request work? It makes little sense to me!
Best regards!
I had the same problem, trying to send a POST request to Django (port 8000) from my Angular CLI (port 4200). I thought it was a problem of Django so I installed cors package however the "problem" is with the browser (actually is not a problem, it is a security issue, see here). Anyway, I solved the problem adding a proxy rule for my Angular CLI, as follows:
First, instead of sending my requests to http://localhost:8000/api/... is send them to /api/ (i.e. to my ng server running at port 4200).
Then I added a file in my Angular project called "proxy.conf.json" with the following content:
{
"/api": {
"target": "http://localhost:8000",
"secure": false
}
}
Finally, run your ng server with the flag "--proxy-config":
ng serve --watch --proxy-config proxy.conf.json
All API requests will be sent to the port 4200 and Angular will internally redirect them to Django, avoiding the CORS problem.
Note that this is only valid for development and won't be used when you build your app code and add it as the static code of your Django server.
Finally, with this solution I didn't need anymore the python module for cors so you could remove it.

Empty Body in Vapor Client GET Response

I'm trying to connect to
Bamboo HR's API, and I've managed to make this work with curl and Swift Foundation's URLRequest/URLSession (as well as an older Express node.js app).
However, when trying to utilize Vapor's client with the .get() method, I'm getting a successful 200 response from BambooHR - but the response's .body is empty.
Here's the code snippet:
key and {myDomain} are placeholders in the example
let encodedKey = "\(key):x".utf8.base64String
let directoryRootUrl = "https://api.bamboohr.com/api/gateway.php/{myDomain}/v1/employees/directory"
let response = try drop.client.get(
directoryRootURL,
headers: [
"Accept": "application/json",
"Authorization": "Basic \(encodedKey)",
"Host": "api.bamboohr.com"
])
When I do print(response), this is what's displayed:
Response
- HTTP/1.0 200 OK
- Headers:
Connection: close
Vary: User-Agent
Server: Apache
Content-Security-Policy: {...}
Date: Mon, 16 Jan 2017 00:26:31 GMT
Content-Type: application/json
X-Content-Type-Options: nosniff
- Body:
I'm wondering if I'm doing anything wrong with Vapor, or if it's a bug.
Like tobygriffin suggested, setting:
drop.client = FoundationClient.self
after creating the Droplet worked.

how to get a 304 for images stored on amazon s3 when using django-storages on django app?

I just started storing user uploaded images on Amazon's S3. It's pretty nice because it took care of my storage problem. However, I am struggling when it comes to having the browser cache the images.
I am using django-storages. In their docs they specify that you can put things on the request header for an image by setting the AWS_HEADER var in your settings. I am doing that and getting no results.
Basically when the app requests the image(s), I get a 200 EVERY TIME. ARG... when I take the browser straight to the image (copy and paste the link into a new window) I get a 200 then a 304 every time after that.
It's very frustrating because it re downloads the image every time. Some pages have up to 25 small thumbnails on them and it's redownloading everything every time the page is reloaded.
I am serving my static files using djangos staticfiles and they are working properly. I get a 200, then 304 after the file is cached.
here are my AWS settings in settings.py
DEFAULT_FILE_STORAGE = 'storages.backends.s3boto.S3BotoStorage'
AWS_ACCESS_KEY_ID = '***'
AWS_SECRET_ACCESS_KEY = '***'
AWS_STORAGE_BUCKET_NAME = 'foobar_uploads'
AWS_HEADERS = {
'Expires': 'Thu, 15 Apr 2020 20:00:00 GMT',
'Cache-Control': 'max-age=86400',
}
AWS_CALLING_FORMAT = CallingFormat.SUBDOMAIN
here are the request and response headers for when the app requests the image: (i've replaced what i feel might be sensitive information with '*')
##request##
GET /user_uploads/*****/2012/3/17/14/46/thumb_a_28_DSC_0472.jpg?Signature=FVR6T%2BXFwHMmdQ9K3n7Ppp7QxoY%3D&Expires=1332023525&AWSAccessKeyId=***** HTTP/1.1
Host: *****_user_uploads_sandbox.s3.amazonaws.com
Connection: keep-alive
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_3) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.79 Safari/535.11
Accept: */*
Referer: http://localhost:8000/m/my-photos/
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3
##response##
HTTP/1.1 200 OK
x-amz-id-2: Hn3S+3gmeLHIjKCpz+2ocE6aPsLCVHh56jJYTsPHwxU98y89x+9X1Ml202evBUHT
x-amz-request-id: 528CEB880CA89AD3
Date: Sat, 17 Mar 2012 21:32:06 GMT
Cache-Control: max-age=86400
Expires: Thu, 15 Apr 2020 20:00:00 GMT
Last-Modified: Sat, 17 Mar 2012 20:46:29 GMT
ETag: "a3bc70e0c3fc0deb974edf95668e9030"
Accept-Ranges: bytes
Content-Type: image/jpeg
Content-Length: 8608
Server: AmazonS3
here are the request/response headers for when i manually request the image by copy and pasting link to the image:
##request##
GET /user_uploads/*****/2012/3/17/14/46/thumb_a_28_DSC_0472.jpg?Signature=FVR6T%2BXFwHMmdQ9K3n7Ppp7QxoY%3D&Expires=1332023525&AWSAccessKeyId=***** HTTP/1.1
Host: porlio_user_uploads_sandbox.s3.amazonaws.com
Connection: keep-alive
Cache-Control: max-age=0
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_3) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.79 Safari/535.11
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3
If-None-Match: "a3bc70e0c3fc0deb974edf95668e9030"
If-Modified-Since: Sat, 17 Mar 2012 20:46:29 GMT
##response##
HTTP/1.1 304 Not Modified
x-amz-id-2: FZH0imrbNxziMznhl5zAoo38CaM7Z+TFnd8R6HtTYB3eTmVpCih+1IniKaliRo18
x-amz-request-id: 3CACF77FBB39D088
Date: Sat, 17 Mar 2012 21:33:22 GMT
Last-Modified: Sat, 17 Mar 2012 20:46:29 GMT
ETag: "a3bc70e0c3fc0deb974edf95668e9030"
Server: AmazonS3
I see there are a few differences such as the "If-None-Match:" or the "If-Modified-Since:" . I think that if I were to set those, then it should work like I'd like.
Is there an easy way to do this?
Thanks for any help!
EDIT 1: I read this article and couldn't translate it very well.. http://coder.cl/2012/01/django-and-amazon-s3/comment-page-1/
If you don't want django-storages to add the auth query to your static media, add the following to your settings.py:
AWS_QUERYSTRING_AUTH = False
This is caused by the S3 Boto backend, which will automatically append the query string unless instructed not to, via django-storages. If you look at the source code for the application, you'll notice that it looks for some extra undocumented settings in your settings.py file (around line 34).
I responded to this similar issue raised on the Github for django-compressor here.
It doesn't look like you're sending a "If-None-Match" or "If-Modified-Since" in your app's request, so S3 has no way to send you back a 304 since it has no idea what you have. As you can see your browser has the file cached, so it is sending both of those headers and getting a proper 304 response.
If you're keeping local copies of files, you'll need to store the Last-Modified and/or ETag and send them along with your request for the image.
As a related aside, I'd consider using just the max-age header and skipping Expires. According the to the spec, Expires should not be more then a year in the future. Using both Expires and max-age is redundant, and max-age can be set to a relative amount (as you have done).
I highly recommend this article on caching and his related REDbot tool for checking your configuration.