In web.py, you can get access to the request's cookies with web.webapi.cookies(), and you can set the value of a cookie with web.webapi.setcookie(...). The documentation isn't clear on how one deletes a cookie, however -- do you just setcookie with a value of None?
You're right, it's certainly not obvious from setcookie()'s docstring, or from the online docs, but it is there somewhere:
The third (and optional) argument to web.setcookie(), "expires", allows you to set when you want your cookie to expire. Any negative number will expire the cookie immediately.
For example, here's part of what we do in our sign-out code (delete the user's session cookie):
web.setcookie('session', '', expires=-1, domain=session_cookie_domain)
Note that you must delete the cookie with the same domain and secure flag as you set it with, otherwise it won't delete. Also, with web.py, you normally use web.setcookie() as a shortcut to web.webapi.setcookie().
web.py doesn't seem to have a way to delete a cookie. If you go through the documentation in the cookbook, it's sparse and doesn't even talk about things like path (so you can set a cookie to a specific location within a domain). So we must turn to the sourcecode. In this, try as I may, I cannot find any reference to a delete,remove, or revoke cookie method.
Having said that, after testing, it is safe to use None to expire a cookie. Here's a quick web app that'll display it.
import web
web.config.debug = False
urls = (
'/', 'index'
)
class index:
def GET(self):
c = web.cookies().get('test1')
if c:
if c=="test":
print 'this is ' + c
web.setcookie('test1', 'test2', domain = 'example.com')
else:
print c
web.setcookie('test1', 'test', domain = 'example.com' expires = None)
return c
else:
print "didnt find cookie"
web.setcookie('test1', 'test', domain = 'example.com' expires='')
return 'I set fire to the rain'
app = web.application(urls, globals())
if __name__ == "__main__":
app.run()
In this the web app first checks if the cookie exists. If it doesn't it sets the cookie 'test1' with the value 'test'. On the next refresh, it will change the value to 'test2'. On the next refresh it sets the value again to 'test' but also expires the cookie. This should result in the next refresh showing 'I set fire to the rain'.
Related
I am trying to initialize a requests.Session, to keep a connection with a webpage. However, I read that each time the session class is called, a new session is created.
How is it possible to keep the connection alive? Because with my current code, it's giving me the webpage content after I call the login method (that's OK, it shows that I logged into the page and gives me the content I want), but when I call the update method, it gives me the content from the login page again, not from the page I actually want after login.
import requests
class LoginLogout:
# creating session
def __init__(self):
self.s = requests.Session()
# login method
def login(self, user, password, server):
payload_data = {'user': user, 'pass': password, 'server': server}
print(self.s.post(LOGIN_LINK, payload_data))
# update method
def update(self, updt_link):
print(self.s.get(updt_link))
def logout(self):
response = self.s.get('...some webpage/user/logout...')
self.s.close()
print(response)
Here I am calling the objects:
if switch_parameter == "login":
login_var = LoginLogout()
login_var.login(USER, PASSWORD, SERVER)
print('IS IT OK ?', login_var.s.get('.../login...')) # <-OK it shows 200 result (but should I use there "s" too ?)
elif switch_parameter == "start":
start()
elif switch_parameter == "stop":
stop()
elif switch_parameter == "update":
update_prem = LoginLogout()
update_prem.update('...different/page...')
# (am I missing here "s" ?, or I shouldnt be using it here anyway)
elif switch_parameter == "logout":
logout()
else:
pass
What am I doing wrong here? I just want to use login to log into the website and keep the session active, while calling update every time I need to get another page. Am I even on the right track or completely wrong?
The whole point of requests.Session is to persist ephemeral constants (like cookies) between requests. In your code you initialize a new session object, when you initialize a LoginLogout object.
You do that here:
if switch_parameter == "login":
login_var = LoginLogout()
...
And you do that here:
elif switch_parameter == "update":
update_prem = LoginLogout()
...
Now login_var and update_prem are obviously different objects and both have the s attribute, each holding a different requests.Session object. How do you expect the attributes of one session to be magically available to the other?
If you want to use an existing session, use it. Don't create a new one.
I don't know about your actual use case of course, but from what you have presented here, it seems you need to do something like this:
scraper_obj = LoginLogout()
scraper_obj.login(USER, PASSWORD, SERVER)
...
scraper_obj.update('...')
...
scraper_obj.logout()
Since your created a wrapper around the actual requests.Session instance with LoginLogout, you should not ever need to deal with its s attribute directly, assuming you have methods on LoginLogout for every kind of request you want to make. You initialize it once and then use its methods to perform requests via its internal session object.
PS
You casually mentioned in a follow-up comment that you set this up as a script to be called repeatedly from the outside and depending on the parameter passed to the script, you want to either log into the site or scrape a specific page.
This shows that you either don't understand how "logging in" even works or that you don't understand how processes work. Typically some session attribute (e.g. cookie) is created on the client so that it can present it to the server to show that it is already authenticated. When using requests as an HTTP client library, this data is stored inside a requests.Session object.
When you call a Python script, you create a new process. Just because you start the same script twice in a row does not mean that one of those processes has any connection to the other. Calling the script to login once has absolutely no effect on what happens the next time you call that script to do something else. None of those earlier session attributes will be present in the second process. I hope this is clear now.
There is my code:
class WebHandler(RequestHandler):
def get(self):
self.set_cookie('name1', 'value.1')
self.set_cookie('name2', 'value.2')
self.write('OK')
When I run this code, chrome browser doesn't set cookie 'name1' but still set cookie 'name2'. When I remove dot character in value of cookies, it's oke. How do I set multiple cookies with dot character in value?
From my point of view, it is possible that the question is a bit incomplete, but I will still try to answer it in the hope that it will help you and others.
Tornado’s set_secure_cookie() and get_secure_cookie() functions send and retrieve browser cookies that are protected against malicious modifications in the browser. To use these functions, you must specify the cookie_secret parameter in the application constructor. Let’s look at a simple example.
The application will render a page that counts how many times it has been reloaded in the browser. If no cookie has been set (or if the cookie has been tampered with), the application will set a new cookie with the value 1. Otherwise, the application will increment the value read from the cookie.
import tornado.httpserver
import tornado.ioloop
import tornado.web
import tornado.options
from tornado.options import define, options
define("port", default=8000, help="run on the given port", type=int)
class MainHandler(tornado.web.RequestHandler):
def get(self):
cookie = self.get_secure_cookie("count")
count = int(cookie) + 1 if cookie else 1
countString = "1 time" if count == 1 else "{} times".format(count)
self.set_secure_cookie("count", str(count))
self.write(
"""
<html><head><title>Cookie Counter</title></head>
<body><h1>You’ve viewed this page {} times.</h1>
</body></html>
""".format(
countString
)
)
if __name__ == "__main__":
tornado.options.parse_command_line()
settings = {
"cookie_secret": "u5SXVuerTfyQTT7uTbu7HjqiqHnh8UsBm37J4Y5lwto="
}
application = tornado.web.Application([(r"/", MainHandler)], **settings)
http_server = tornado.httpserver.HTTPServer(application)
http_server.listen(options.port)
try:
tornado.ioloop.IOLoop.instance().start()
except KeyboardInterrupt:
print("Server has shut down.")
If you inspect the value of the cookie in the browser, you will notice that the value stored for count is "count=\"2|1:0|10:1612910394|5:count|4:MQ==|e8c35def2daaec8da8ca5f3f1db63168f97027024a824d17b5e405f4f97c26ce\"". Tornado encodes the cookie value as a Base-64 string and appends a timestamp and an HMAC signature to the cookie contents. If the cookie’s timestamp is too old (or from the future), or if the signature doesn’t match the expected value, the get_secure_cookie() function assumes the cookie has been tampered with and will return None, as if the cookie had not been set.
The cookie_secret value passed to the Application constructor should be a unique, random string. Executing the following code snippet in a Python shell will generate one for you:
>>> import base64, uuid
>>> base64.b64encode(uuid.uuid4().bytes + uuid.uuid4().bytes)
b'KeQWrXgiTjWQIEzcRbx0vV/IM/mYAEqvs+EtZ/5dvfs='
Tornado’s secure cookies are still susceptible to snooping, however. Attackers may be able to intercept cookies via scripts or plug ins in the browser, or simply by eavesdropping unencrypted network data. Remember that cookie values are signed rather than encrypted. Malicious programs are able to read stored cookies and either transmit their data to arbitrary servers or forge requests by sending them unmodified to the application. Therefore, it’s important to avoid storing sensitive user data in a browser cookie.
We also need to be aware of the possibility that a user could modify his own cookies, which could lead to a privilege escalation attack. If, for example, we store the number of remaining articles a user has paid to view in a cookie, we would want to prevent the user from updating that number himself in an attempt to get free content. The httponly and secure cookie properties can help prevent these sorts of attacks.
Setting the secure attribute on a cookie instructs the browser to transfer the cookie only over SSL connections. (It’s a little confusing, but this is not the same as Tornado’s secure cookies, which are more accurately described as signed cookies.) Since Python version 2.6, the Cookie object also supports the httponly attribute. Including this attribute instructs the browser to make the cookie inaccessible to JavaScript, which can prevent cross-site scripting attacks from reading the cookie’s value.
To enable these features, you can pass keyword arguments to the set_cookie and
set_secure_cookie methods. For example, a secure, HTTP-only cookie (that’s not
signed by Tornado) could be sent with the call:
self.set_cookie('foo', 'bar', httponly=True, secure=True)
Since the Tornado comes with built-in XSRF protection. For more details on this, see the official documentation, you can also see one of my answers in which it is set "xsrf_cookies": True.
I am trying to set cookies to never expire (even after browser has been closed) in Google App Engine using the documentation for webapp2 sessions and building a response. I've tried:
Code snippet 1:
from webapp2
from webapp2_extras import sessions
class MainHandler(webapp2.RequestHandler):
def never_expire_session(self):
sessions.get_session(name='my_session', max_age=2147483647)
#login_required
def get(self):
....
html_template = JINJA_ENVIRONMENT.get_template('index.html').render(myapp)
self.response.out.write(html_template)
OR
Code snippet 2:
class MainHandler(webapp2.RequestHandler):
def never_expire_session(self):
return self.response.set_cookie('my_session', max_age=2147483647)
#login_required
def get(self):
....
html_template = JINJA_ENVIRONMENT.get_template('index.html').render(myapp)
self.never_expire_session()
self.response.out.write(html_template)
Code Snippet 2 results in a response of NoneNone. (Not sure the output of the first code snippet.)
I have also tried the answer here, that recommends configuring the parameters:
config = {}
config['webapp2_extras.sessions'] = {'session_max_age': 2147483647}
....
....
app = webapp2.WSGIApplication([
....
], debug=True, config=config)
I've cleared my cookies, restarted my browser, and checked the console and the cookie value shows as a randomly generated sequence of numbers and letters and the expiration of the cookie is set to one year from datetime.now. Nothing I've tried works.
How can I correctly set the session cookie so it never expires, even on browser exit?
I was experiencing a caching issue which prevented my session cookie from showing in the developer console and being applied. After erasing the browser history and restarting my computer, it cleared the cache. Eventually I got it to work with the following:
from webapp2
class MainHandler(webapp2.RequestHandler):
def get(self):
the_value = ''.join(SystemRandom().choice(string.digits _ string.ascii_lowercase) for_in range (50))
max_age = 2147483647
# max age for Internet Explorer
cookie_expires = datetime.datetime.now() + datetime.timedelta (200)
return self.response.set_cookie('my_cookie', the_value, max_age=max_age, httponly=True, secure=True)
NOTE: if you want to apply this change to a specific cookie, rename "my_cookie" to that cookie (i.e. "SACSID" a cookie on many Google applications)
We can even do this using webapp2_extras.sessions, no need for response.set_cookie(...). There's a default_config dictionary in webapp2_extras.sessions, just set the max_age to 2147483647 or whatever time (in seconds) works for you, it should work. Worked in my case. Look over the official documentation for more info about default_config.
sessions.default_config['cookie_args']['max_age'] = 2147483647
I'm working with the imgur api. I'm using Flask without a db.
I'm trying to access a single dedicated account, so there won't be more than one user account in this app (thus i just have to manually authorize it once and do the rest with the tokens the service gives to me, no need for a database).
Once authorized the app on the account, Imgur gives me two tokens: an access_token that expires after an hour and a refresh_token that doesn't expire and can be used to request a new access_token.
I can't think any method different that this to send correct requests to the server using this two tokens and refreshing the access one only when needed:
from flask import Flask
from foobar import foo, bar
import config
app = Flask(__name__)
app.config.from_object(config)
#I initialize the access_token with the value hardcoded in the config file
access_token = config.access_token
#app.route('/',methods=['POST'])
def home():
#I access the value i initialized when starting the server
global access_token
if request.method == 'POST':
#I send the access_token to a method that contacts Imgur servers and checks if it's still valid. If not it will request and return a new one.
access_token = foo(access_token)
#I send the token, now sure it's always a valid one, to the rest of the logic that uses it to do actual requests
bar(access_token)
This works. As long i don't restart the server it will require a new access_token only if needed, but it doesn't really look a good way of doing things. The idea of hardcoding a token that expires in an hour sounds really silly and i don't really know if using that global variable there could be a good practice nor if it would be reinitialized from the config file in some scenario i'm currently ignoring. How can i avoid this? I should write the new refreshed access_token on a file and load it from there in the config? Does that create security concerns?
Also, the refresh_token doesn't expire so i think there is no problem on storing it in the cofing file, but sill every time i request an access_token it also gives me a new refresh_token, should i update that too? It seems unnecessary, the old one still works, but maybe i'm missing something.
According to imgur's API doc, access_token expires after one month not after one hour. I think it's acceptable method to store it in a file. You are already storing the refresh_token in the config file, so if an attacker can access the app's directory, it does not matter whether access_token is also there. (Of course you should run the Flask app as a separate user, and all files should be accessible only by that user.)
You can also store the expire date along with the access_token, so you can check whether you have the renew it before a request.
I'm struggling to figure this one out, sessions work when i run my application normally but i can't figure out how to set data in the session in my test case.
The docs say in a test case you have to save the session to apply the changes before making the request. https://docs.djangoproject.com/en/1.2/topics/testing/#persistent-state
e.g.
from django.test import TestCase
class TestLogin(TestCase):
def test_processuser(self):
redirect = '/processuser/'
session = self.client.session
session["id"] = '1234'
session.save()
response = self.client.get(redirect)
However the session object returned from self.client.session is just a normal python dict?
Diging into the code the Client.session call is this:
def _session(self):
"""
Obtains the current session variables.
"""
if 'django.contrib.sessions' in settings.INSTALLED_APPS:
engine = import_module(settings.SESSION_ENGINE)
cookie = self.cookies.get(settings.SESSION_COOKIE_NAME, None)
if cookie:
return engine.SessionStore(cookie.value)
return {}
session = property(_session)
cookie = self.cookies.get(settings.SESSION_COOKIE_NAME, None) returns None so it just returns a dict in stead of a session store.
It looks like i have to do some more preparation in the test client before i save a session? Not really got much experience in this any help would be appreciated.
Django 1.2.5
Python 2.6.5
Cheers,
Asim.
Edit: this answer is now outdated; as of at least Django 1.7, you can just set the cookie directly on the test client.
See e.g. this answer to this question or the comments on this answer to another, similar, question.
Old outdated answer follows...
Adding this for people who really do need to set a cookie, e.g. because they need to do something which isn't covered by the Django auth mechanism...
You can't set cookies directly on TestClient objects but if you use the RequestFactory class you can do it. So instead of (say):
response = Client().post('/foo')
you do:
request = RequestFactory().post('/foo')
request.COOKIES['blah'] = 'hello'
response = foo_view(request)
where foo_view is the view corresponding to the '/foo' path, i.e. the view you're looking to test.
HTH somebody.
The simplest thing would be to login as someone, so the test client would set the cookie for you.
self.client.login(username,password)
should do. Refer the documentation for more.
Contrary to the most upvoted answer, you CAN set cookies directly on the test client.
Remember everything is an object, you just have to know where/what to patch
so it goes like this:
client.cookies[key] = data
client.cookies is an instance of http.cookies.SimpleCookie from the standard library and it behaves like a dict. so you can use .update for bulk updates to a cookies value. This can be useful if you want to alter other cookie values like max-age, path domain etc.
Finally, if you want to set a signed_cookie, You can reuse the helpers from django like this:
from django.core.signing import get_cookie_signer
signed_cookie_value = get_cookie_signer(salt=key).sign(data)
client.cookies[key] = signed_cookie_value
Pay attention to the salt. It has to match on both ends (Signing and retrieval). A Different salt value for signing would generate a different cookie that cannot be retrieved when you call response.get_signed_cookie(key)
For other people who are running into this problem please be aware that the Client.logout() function will throw away your cookies. For example:
response = self.client.post(self.url, self.data)
print response.client.cookies.items() # Displays the cookie you just set
self.client.logout()
response = self.client.post(reverse('loginpage'), {'username': 'username', 'password': 'password'}, follow=True)
print response.client.cookies.items() # Does not display the cookie you set before since it got destroyed by logout()
To make sure your cookies stay alive during testing make a call to your logout page in stead of using the Client.logout() function, like so:
response = self.client.post(self.url, self.data)
print response.client.cookies.items() # Displays the cookie you just set
self.client.get(reverse('logoutpage'))
response = self.client.post(reverse('loginpage'), {'username': 'username', 'password': 'password'}, follow=True)
print response.client.cookies.items() # Does display the cookie you set before since it did not get destroyed by client.logout()