How to set a cookie in a django test case? - python

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()

Related

requests.Session() creating different session every time. How to reuse it?

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.

How do I authorize using Spotipy on Django?

I am having a great deal of trouble trying to authorize Spotipy. It does fine in IDLE, but as soon I try to do it together with Django, hell lets loose. Where I have gotten the farthest, is where I have gotten the code from the callback. But what do I do after that?
This piece of code below takes
def add_song(request, pk, uri):
scope = "user-read-recently-played user-top-read user-read-playback-position user-read-playback-state user-modify-playback-state user-read-currently-playing user-read-private playlist-modify-private playlist-read-private"
token = util.prompt_for_user_token("username", scope, client_id=SECRET,client_secret=SECRET,redirect_uri='http://localhost:8000/rooms/callback/')
sp = spotipy.Spotify(auth=token)
sp.add_to_queue(uri)
auth_manager = spotipy.oauth2.SpotifyOAuth(scope='user-read-currently-playing playlist-modify-private', show_dialog=True)
if request.GET.get("code"):
# Step 3. Being redirected from Spotify auth page
auth_manager.get_access_token(request.GET.get("code"))
return redirect('/')
return render(request, 'rooms/{0}.html'.format(pk))
def callback(request):
return redirect(reverse("index"))
The URL patterns:
urlpatterns = [
path('<int:pk>/add_song/<str:uri>', views.add_song, name='add_song'),
path('callback/<str:token>', views.callback, name="callback")
]
I really need something to continue on. I have been googling so much, but there isn't really anything I know what to do with. Could you provide an example of Django Python authorization or try to help me?
Don't use util.prompt_for_user_token. Assuming you want different users to use your app, how would you pre-know their username?
Use a SpotifyOAuth object instead (you were using it, just not in the way I'm about to show). You can refer to the Client Authorization Code Flow section on the official docs.
Here is the basic of it:
# Seprating the login from add_song()
def login(request):
sp_auth = SpotifyOAuth(
scope=scope,
client_id=client_id,
client_secret=client_secret,
redirect_uri=redirect_uri,
)
# This url will prompt you a Spotify login page, then redirect user to your /callback upon authorization
redirect_url = sp_auth.get_authorize_url() # Note: You should parse this somehow. It may not be in a pretty format.
return redirect(redirect_url)
# You can make add_song() and callback() the same function.
# If you have multiple pages where you need to use the API, I suggest caching the token here as well.
def callback_and_add_song():
sp_auth = [the same as above. You should make a method to modularize it.]
# Get code from url
code = request.GET.get("code", "")
token = sp_auth.get_access_token(code=code)
<You have token now. Do what you need.>
A few more things I noticed in your code that may be not-needed/ wrong:
path('callback/<str:token>': I don't think <str:token> have to be there.
return redirect('/'): In the first if in add_song(), assuming your previous code works, you are still just redirecting somewhere without caching the token/ passing it on.
return redirect(reverse("index")): The same as above. I think for performance and all, you should cache token in callback() then redirect to page(s) where you actually use it.

Can i append a value to my flask request object in the #app.before_request and pass it forward to the endpoint view function?

I am developing some basic REST APIs in python. I am expecting an authorization token in the header of all requests except some unsecured requests like login and register. I am validating the token in #app.before_request and then I want to pass the decoded payload to the corresponding endpoint view function. But, I am not to attach the decoded info to the request object as I get "TypeError: 'Request' object does not support item assignment".
#app.before_request
def before_request():
print(request.endpoint)
if request.endpoint=="register" or request.endpoint=="login":
pass
else:
auth_header = request.headers.get('Authorization')
if auth_header:
auth_token = auth_header.split(" ")[1]
token=decode_auth_token(auth_token)
request["token"]=token
else:
return jsonify({"result":"","error":"No token present in header !"})
I am thinking of this implementation like an auth filter where all requests pass this filter. I can strike off the ill requests at this layer itself and also, I can fetch the user specific info which is needed in the net middleware.
I have a similar usecase (surprisingly similar, actually). I got around it by setting a custom property in the request object, much like your approach, although instead of using direct assignment (i.e. request["token"]=token), I used setattr(request, "token", token).
I got the tip from a bottle plugin which does something very similar:
https://github.com/agile4you/bottle-jwt/blob/master/bottle_jwt/auth.py#L258
You may even wanna try that, or some other plugin to further improve your application.
Flask offers g to propagate data in application context.
from flask import g
#app.before_request
def before_request():
g.token = vaue
#bp.route("/path", methods=["GET"]):
g.token
Reference:
https://flask.palletsprojects.com/en/2.2.x/appcontext/

flask: get session time remaining via AJAX

I am using Flask, with the flask-session plugin for server-side sessions stored in a Redis backend. I have flask set up to use persistent sessions, with a session timeout. How can I make an AJAX request to get the time remaining on the session without resetting the timeout?
The idea is for the client to check with the server before displaying a timeout warning (or logging out the user) in case the user is active in a different tab/window of the same browser.
EDIT: after some digging, I found the config directive SESSION_REFRESH_EACH_REQUEST, which it appears I should be able to use to accomplish what I want: set that to False, and then the session should only be refreshed if something actually changes in the session, so I should be able to make a request to get the timeout without the session timeout changing. It was added in 0.11, and I'm running 0.11.1, so it should be available.
Unfortunately, in practice this doesn't appear to work - at least when checking the ttl of the redis key to get the time remain. I checked, and session.modified is False, so it's not just that I am doing something in the request that modifies the session (unless it just doesn't set that flag)
The following works, though it is rather hacky:
In the application __init__.py, or wherever you call Session(app) or init_app(app):
#set up the session
Session(app)
# Save a reference to the original save_session function so we can call it
original_save_session = app.session_interface.save_session
#----------------------------------------------------------------------
def discretionary_save_session(self, *args, **kwargs):
"""A wrapper for the save_session function of the app session interface to
allow the option of not saving the session when calling specific functions,
for example if the client needs to get information about the session
(say, expiration time) without changing the session."""
# bypass: list of functions on which we do NOT want to update the session when called
# This could be put in the config or the like
#
# Improvement idea: "mark" functions on which we want to bypass saving in
# some way, then check for that mark here, rather than having a hard-coded list.
bypass = ['check_timeout']
#convert function names to URL's
bypass = [flask.url_for(x) for x in bypass]
if not flask.request.path in bypass:
# if the current request path isn't in our bypass list, go ahead and
# save the session normally
return original_save_session(self, *args, **kwargs)
# Override the save_session function to ours
app.session_interface.save_session = discretionary_save_session
Then, in the check_timeout function (which is in the bypass list, above), we can do something like the following to get the remaining time on the session:
#app.route('/auth/check_timeout')
def check_timeout():
""""""
session_id = flask.session.sid
# Or however you want to get a redis instance
redis = app.config.get('REDIS_MASTER')
# If used
session_prefix = app.config.get('SESSION_KEY_PREFIX')
#combine prefix and session id to get the session key stored in redis
redis_key = "{}{}".format(session_prefix, session_id)
# The redis ttl is the time remaining before the session expires
time_remain = redis.ttl(redis_key)
return str(time_remain)
I'm sure the above can be improved upon, however the result is as desired: when calling /auth/check_timeout, the time remaining on the session is returned without modifying the session in any way.

In Pyramid, how to check if view is static in NewRequest event handler?

I have a NewRequest event handler (subscriber) in Pyramid which looks like this:
#subscriber(NewRequest)
def new_request_subscriber(event):
request = event.request
print('Opening DB conn')
// Open the DB
request.db = my_connect_to_db()
request.add_finished_callback(close_db_connection)
However, I have observed that a connection to the DB is opened even if the request goes to a static asset, which is obviously unnecessary. Is there a way, from the NewRequest handler, to check if the request is bound for a static asset? I have tried comparing the view_name to my static view's name, but apparently the view_name attribute is not available at this early stage of processing the request.
If anyone has any interesting ideas about this, please let me know!
The brute force way is to compare the request.path variable to your static view's root, a la request.path.startswith('/static/').
The method I like the best and use in my own apps is to add a property to the request object called db that is lazily evaluated upon access. So while you may add it to the request, it doesn't do anything until it is accessed.
import types
def get_db_connection(request):
if not hasattr(request, '_db'):
request._db = my_connect_to_db()
request.add_finished_callback(close_db_connection)
return request._db
def new_request_subscriber(event):
request = event.request
request.db = types.MethodType(get_db_connection, request)
Later in your code you can access request.db() to get the connection. Unfortunately it's not possible to add a property to an object at runtime (afaik), so you can't set it up so that request.db gives you what you want. You can get this behavior without using a subscriber by the cookbook entry where you subclass Request and add your own lazy property via Pyramid's #reify decorator.
def _connection(request):
print "******Create connection***"
#conn = request.registry.dbsession()
conn = MySQLdb.connect("localhost", "DB_Login_Name", "DB_Password", "data_base_name")
def cleanup(_):
conn.close()
request.add_finished_callback(cleanup)
return conn
#subscriber(NewRequest)
def new_request_subscriber(event):
print "new_request_subscriber"
request = event.request
request.set_property(_connection, "db", reify = True)
try this one, I reference fallow web page
http://pyramid.readthedocs.org/en/1.3-branch/api/request.html
"set_property" section, it works for me.

Categories

Resources