In Pyramid, using beaker for sessions, how can I make it so certain responses do not include cookies?
Currently if I curl any url on my app I'll get back something like:
HTTP/1.1 200 OK
Server: nginx/1.2.6
Date: Thu, 07 Nov 2013 02:14:45 GMT
Content-Type: application/json; charset=UTF-8
Content-Length: 776
Connection: keep-alive
Set-Cookie: beaker.session.id=0a6d945c09884ca29d73bc4ff4d09ff0; expires=Thu, 07-Nov-2013 03:14:45 GMT; httponly; Path=/; secure
I don't need that cookie being set with all requests. For example I'd like to remove it from requests that have the subdomain "api". I tried changing:
def main(global_config, **settings):
session_factory = session_factory_from_settings(settings)
config = Configurator(settings=settings, root_factory=get_root)
config.set_session_factory(session_factory)
return config.make_wsgi_app()
to:
def main(global_config, **settings):
session_factory = session_factory_from_settings(settings)
config = Configurator(settings=settings, root_factory=get_root)
#config.set_session_factory(session_factory)
return MethodOverride(config, session_factory)
class MethodOverride(object):
def __init__(self, config, session_factory):
import copy
self.config = copy.deepcopy(config)
config.set_session_factory(session_factory)
self.application = config.make_wsgi_app()
def __call__(self, environ, start_response):
if "api" == environ['HTTP_HOST'].split('.')[0]:
self.application = self.config.make_wsgi_app()
Which I thought would make it so that the session factory wouldn't be set in those instances and therefore no cookies. I don't understand what's going on with the middleware well enough. I'd also be fine with figuring out a way to make it so Response objects that have an "application/json" mimetype don't include that cookie. Any help would be much appreciated.
One way you could do this is by using an NewResponse subscriber that would modify the outgoing response.
For example:
def new_response_subscriber(event):
request = event.request
response = event.response
if "api" == request.environ['HTTP_HOST'].split('.')[0]:
if 'Set-Cookie' in response.headers:
del response.headers['Set-Cookie']
This would be one way to remove all cookies from all responses. Another way to do it would be to create a new session factory that checks to see if the current URL is an API request, and if so, it doesn't create a session at all.
Related
I'm trying to implement an authentication system, part of which involves passing around a session cookie to allow the server to verify the user's identity on a per-request basis; however, when registering under a test-case and attempting to access an authenticated endpoint, despite the cookie being set in the test's local cookiejar, it does not appear in the request headers to the server.
The server creates the cookie here, by only setting the expiration date.
headers={
"set-cookie": [{
"name": "__session",
"value": user_obj.serialize_session(),
"expires": cookie_expiry(user_obj.expires_at()),
}]
}
And the test-case is very simple
session = requests.Session()
def register(username, password):
return session.post(
f"{HOST}/api/auth/register",
...
)
def create_invoice(education, desc, budget, deadline, files):
return session.post(
f"{HOST}/api/invoice/create",
...
)
username, password = ...
account = register(username, password)
req = create_invoice(
...
)
# {"status": "error", "reason": "you must be authenticated to use this endpoint"}
On the client-side, the cookie appears to be valid on the register response:
'set-cookie': '__session="..."; expires=Fri, 22-Apr-2022 02:47:46 GMT'
But, the request from the server-side appears without any Cookie header:
Host: www.example.local:8443
User-Agent: python-requests/2.27.1
Accept-Encoding: gzip, deflate
Accept: */*
Connection: keep-alive
Content-Length: 112
Content-Type: application/json
I've looked everywhere in terms of playing around with the Domain and HttpOnly attributes, but nothing appears to work. Is there a certain reason why the requests session isn't putting the session cookie in the request?
Due to changes arriving in Chrome during July, I need to modify my app to explicitly provide the SameSite=None key value. This is due to the RFC treating the absence of this setting in a more impacting way than if it is present but set to None.
However on the set_cookie method, the samesite parameter is defaulted to None which results in it not being written into the set-cookie. How can I force this into the set-cookie part of the response?
When I try to set the samesite=None with the following code
resp.set_cookie('abcid', 'Hello', domain=request_data.domain, path='/', samesite=None, max_age=63072000)
This does not show any SameSite detail in the returned set-cookie
abcid=Hello; Domain=.localhost; Expires=Tue, 29-Jun-2021 22:34:02 GMT; Max-Age=63072000; Path=/
And if I try and explicitly set the value of Lax (which is one of the accepted values per rfc) as so
resp.set_cookie('abcid', "Hello", domain=request_data.domain, path='/', samesite="Lax", max_age=63072000)
I get back the set-cookie which explicitly has the SameSite=Lax setting
abcid=Hello; Domain=.localhost; Expires=Tue, 29-Jun-2021 23:03:10 GMT; Max-Age=63072000; Path=/; SameSite=Lax
I have tried None, "None", and "" but these either crash the application or omit the SameSite in the resultant response.
Any help would be gratefully received
Once the fix to this issue is
released, you will be able to use
set_cookie()
like this:
from flask import Flask, make_response
app = Flask(__name__)
#app.route('/')
def hello_world():
resp = make_response('Hello, World!');
resp.set_cookie('same-site-cookie', 'foo', samesite='Lax');
resp.set_cookie('cross-site-cookie', 'bar', samesite='Lax', secure=True);
return resp
While you're waiting for the release, you can still
set the header
explicitly:
from flask import Flask, make_response
app = Flask(__name__)
#app.route('/')
def hello_world():
resp = make_response('Hello, World!');
resp.set_cookie('same-site-cookie', 'foo', samesite='Lax');
# Ensure you use "add" to not overwrite existing cookie headers
resp.headers.add('Set-Cookie','cross-site-cookie=bar; SameSite=None; Secure')
return resp
You can also use the following code to set cookies with SameSite=None until fix is released
from werkzeug.http import dump_cookie
# That's a workaround for explicitly setting SameSite to None
# Until the following fix is released:
# https://github.com/pallets/werkzeug/issues/1549
def set_cookie(response, *args, **kwargs):
cookie = dump_cookie(*args, **kwargs)
if 'samesite' in kwargs and kwargs['samesite'] is None:
cookie = "{}; {}".format(cookie, b'SameSite=None'.decode('latin1'))
response.headers.add(
'Set-Cookie',
cookie
)
I am trying to create simple API for my site. I created the route with flask:
#api.route('/api/rate&message_id=<message_id>&performer=<performer_login>', methods=['POST'])
def api_rate_msg(message_id, performer_login):
print("RATE API ", message_id, ' ', performer_id)
return 400
print(...) function don't execute...
I use flask-socketio to communicate between client and server.
I send json from client and process it with:
#socket.on('rate')
def handle_rate(data):
print(data)
payload = {'message_id':data['message_id'], 'performer':data['performer']}
r = requests.post('/api/rate', params=payload)
print (r.status_code)
Note, that data variable is sending from client and is correct(I've checked it).
print(r.status_code) don't exec too...
Where I'm wrong? Please, sorry for my bad english :(
This api function must increase rate of message, which stored in mongodb, if interesting.
Don't put &message_id=<message_id>&performer=<performer_login> in your route string. Instead, get these arguments from request.args.
Try it:
from flask import request
...
#api.route('/api/rate', methods=['POST'])
def api_rate_msg():
print(request.args)
return ''
I've tested it with httpie:
$ http -v POST :5000/api/rate message_id==123 performer_login==foo
POST /api/rate?message_id=123&performer_login=foo HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate
Connection: keep-alive
Content-Length: 0
Host: localhost:5000
User-Agent: HTTPie/0.9.8
HTTP/1.0 200 OK
Content-Length: 0
Content-Type: text/html; charset=utf-8
Date: Sun, 02 Apr 2017 13:54:40 GMT
Server: Werkzeug/0.11.11 Python/2.7.13
And from flask's log:
ImmutableMultiDict([('message_id', u'123'), ('performer_login', u'foo')])
127.0.0.1 - - [02/Apr/2017 22:54:40] "POST /api/rate?message_id=123&performer_login=foo HTTP/1.1" 200 -
Remove the below part from your api route
&message_id=<message_id>&performer=<performer_login
This is not required in POST request. It helps in GET requests. API call in request is not matching the route definition and therefore you have the current problem
I am facing a problem with Tornado. I have an API endpoint for PUT HTTP Method in Tornado. I also have a web application that sends the request to this API with jQuery and AJAX, but always I get a 405 response because the request is going as HTTP Method OPTIONS.
I understand the way it works and I did configured my Tornado Server to allow it. But even so I having this situation.
Can someone help me?
There is my server code:
class BaseHandler(RequestHandler):
def __init__(self, *args, **kwargs):
super(BaseHandler, self).__init__(*args, **kwargs)
self.set_header('Cache-Control', 'no-store, no-cache, must- revalidate, max-age=0')
self.set_header("Access-Control-Allow-Origin", "*")
self.set_header("Access-Control-Allow-Headers", "Content-Type")
self.set_header('Access-Control-Allow-Methods', 'POST, GET, PUT, DELETE, OPTIONS')
Many thanks
You need to add an options handler that just sends the headers with no body:
def options(self):
# no body
self.set_status(204)
self.finish()
See Tornado server: enable CORS requests for a complete code snippet.
Or else just install the tornado-cors package:
pip install tornado-cors
That will add the necessary handlers for you, and ensure the right response headers get sent.
if you don't define put method return 405
class Handler(tornado.web.RequestHandler):
def put(self):
self.set_header('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0')
self.set_header("Access-Control-Allow-Origin", "*")
self.set_header("Access-Control-Allow-Headers", "Content-Type")
self.set_header('Access-Control-Allow-Methods', 'POST, GET, PUT, DELETE, OPTIONS')
[I 170205 04:56:35 web:1971] 200 PUT
My code is show bellow.I am using pythoneve flask angularjs.
def testing():
message = "hai"
yield 'data: %s\n\n' % message
#app.route('/stream')
def stream():
return flask.Response(testing(),mimetype="text/event-stream")
You can do it either on client or server side, here's how:
On the client side (browser), the simplest would be tacking on a query parameter to your request, i.e.
$http.get('/stream?b=123456');
where 123456 is a random string, can be a timestamp in milliseconds. Random parameter would force a browser to resend the request and not use its cache.
OR on the server-side, you would send special cache-control headers:
Cache-Control: no-cache, no-store, must-revalidate
Pragma: no-cache
Expires: 0
Either of these should do the trick, just make sure you implement it correctly in a language you use.