Django sessions: changing session key when modified - python

I am setting up a payment gateway and am using sessions to store data across page requests. The class below is used for organizing and storing information to the session.
class Gateway:
def __init__(self, session_key=None, session_name="FOO"):
# Store session ID and name
self.session_key = session_key
self.session_name = session_name
# Get the session
session = SessionStore(session_key=self.session_key)
try :
data = session[self.session_name]
except :
data = {user_id:None, checked_in:False }
self.__dict__.update(data)
def save(self) :
session = SessionStore(session_key=self.session_key)
session[self.session_name] = deepcopy(self.__dict__)
try :
del session['session_key']
del session['session_name']
except :
pass
session.save()
This view checks to see if the user is logged in. If he/she is, then he/she is redirected. If not, he/she is asked to either login or check in as a guest.
def check_in(request):
gateway = Gateway(session_key=request.session.session_key)
if request.user.is_authenticated():
gateway.user_id = request.user.id
gateway.checked_in = True
gateway.save()
return redirect('next_step')
else:
login_form = FormLogin()
if request.POST:
data = request.POST.copy()
if 'login' in data:
login_form = FormLogin(data)
if login_form.is_valid():
user = login(request, login_form)
if user:
gateway.user_id = user.id
gateway.checked_in = True
gateway.save()
return redirect('next_step')
elif 'guest' in data:
gateway.checked_in = True
gateway.save()
return redirect('next_step')
return render(
request,
'shop/login.html',
{
'login_form':login_form,
}
)
The next view checks the "checked_in" variable. This is to make sure that users are not skipping over the login/checkin process. (As a side note, the function "login(request, login_form)" is a function that is works perfectly in other contexts and returns the User if it was successful and None otherwise)
def next_step(request):
gateway = Gateway(session_key=request.session.session_key)
if not gateway.checked_in:#edited
messages.info(request, _(u'You must specify login first.'))
return redirect('check_in')
else:
#do the next step
Now for the problem:
Even when the user is authenticated, the "checked_in" variable is still false and causes the views to loop. A new session with a new session id is created each time that that I set the variable and save. The django docs have some explanation about the modification of sessions, but I cannot understand why new session is being created or why the session key is changing.
edit:
I am using the database backend.

I have duplicated this bug/issue:
URL RULE
url(r'^test/', 'shop.views.catalog.test', name="test")
VIEW FUNCTION
def test(request) :
key1 = request.session.session_key
request.session['test'] = 'test'
key2 = request.session.session_key
raise Exception("%s : %s === %s" % (key1, key2, request.session['test']))
Clear cookies for 127.0.0.1
go to 127.0.0.1:8000/test/
Exception at /test/
4793f2453758d7021a43a348a0f40a83 : 8568f729991e740395179c56cd37cf18 === test
refresh the page (w/o clearing cookies)
Exception at /test/
8568f729991e740395179c56cd37cf18 : 8568f729991e740395179c56cd37cf18 === test
so until the first time my session is modified, I have a different session key... unexpected behavior. I'm also curious why.

Django will not persist a session to the database if it has not been accessed or modified, so I believe the session_key you are using to initialise SessionStore is not actually backed by a database entry.
If this is the case: when you save your SessionStore it will be allocated a new session_key automatically [1] (as the existing key does not exist in the DB and we want to avoid session fixation [2]) and saved to the DB but the client will not be allocated this new session_key because your SessionStore is independent of request.session (which remains unmodified).
[1] https://github.com/django/django/blob/master/django/contrib/sessions/backends/db.py#L22
[2] https://groups.google.com/forum/?fromgroups#!topic/django-users/8b_6oTaXv7Q
The simple fix to test this hypothesis out would be to set request.session['kate'] = 'bob' before you initialise your Gateway class as this should force request.session to be persisted. You might like to refactor your Gateway class so that methods that need access to the session take request.session as an argument.

Check your SESSION_COOKIE_SECURE value and make sure you are using HTTPS when True..
https://github.com/lepture/flask-wtf/issues/76

Related

Django-tenant “No tenant for hostname” error

I was able to create a schema (and I confirmed this via database) but for some reason, I am getting a Can't create tenant outside the public schema. Current schema is error when creating schema and then also I am getting this error No tenant for hostname when I try to visit the tenants domain and I am not sure what is causing it. Below is my code:
views.py
def post(self, request):
form = CreatePortalForm(request.POST)
if form.is_valid():
getDomain = form.cleaned_data.get('name')
instance = form.save(commit=False)
user_id = request.user.id
user = User.objects.get(id=user_id)
tenant = Client(schema_name=getDomain, name=getDomain, created_by=user)
tenant.save()
domain = Domain()
domain.domain = getDomain + ".example.com:8000"
domain.tenant = tenant
domain.is_primary = True
domain.save()
with schema_context(tenant.schema_name):
instance.save()
redirect = 'http://' + getDomain + '.example.com:8000'
return HttpResponseRedirect(redirect)
return render(request, "registraton/create_portal.html", {"form": form})
For example, I have created three schemas:
tenant1
tenant2
tenant3
All three tenants have created the tables in the database, but I get the Can't create tenant outside the public schema. Current schema is error when running the above script to create the schema and domain or I get the No tenant for hostname when trying to visit the tenants domain.
Like I said, the schema is creating and migrating successfully but I still cannot get to the domain.example.com as it throws the No tenant for hostname error. Any ideas on what is causing this?
The django-tenants docs says that you shouldn't specify the port for your domain.
See this here

Change user state in database based on session

I have webapp that shows who is currently logged in on it's front page. I did that by having isActive field in user model, if user is logged isActive = True if user is logged out isActive = False. Unfortunately now I needed to implement auto log out on browser close and I've achieved that thanks to
REMEMBER_COOKIE_DURATION = datetime.timedelta(minutes=0)
app.config["SESSION_PERMANENT"] = False
But this obviously doesn't change state in database
My approach was to check active users and check sessions, get the ID's of both and compare them. If user ID is in active_user_list and not in session_id get this user ID and change it's state in database.
def checking_loop():
delay = 180
while True:
active_users = User.query.filter_by(isActive=True).all()
#don't worry about querying database in a loop It's stored locally
#on the PC
active_users_id = []
user_in_session = []
if session.get('_user_id'):
user_in_session.append(session['_user_id'])
for x in active_users:
active_users_id.append(x.id)
users_to_logout = [x for x in active_users_id if x not in user_in_session]
time.sleep(delay)
logout_loop = Thread(target=checking_loop)
logout_loop.start()
So, what's not working? I didn't know that when I call session I only get one ID.
Can I make this work somehow or do you have a better approach for this?
I don't think this will work. Flask session depends on context https://flask.palletsprojects.com/en/2.0.x/reqcontext/#how-the-context-works. I don't know how your code works without view context, but generally global session derives it's value from currently served request (the assumption is only one request can be handled at the time). So in session you'll get current user, not all users.
I'm not sure when session connected to user is deleted or how to iterate over active sessions, but if they are stored in cookies backend won't be able to do that.

How to set user name in session and get the user name in other view methods

I'm new to Python and Django.
I'm using PHP form for login mechanism and once the login is successful, I'm sending the user name to Django application to carry on with other operations but I'm stuck in finding a best way to store the user name in session so that it can be used in other view methods
Below are some of my tries which failed:
1) Storing the user name in a global variable and access throughout the application but when another user login from their machine, user name gets changed in my machine.
2) Storing the user name in Django DB session but another user logs in with different user, it's showing the old user only.
3) I tried storing in the cache but still, it's not working.
DB session handler: It works fine when I access in sessionHandler method but it throws key error when i try to access in login method.
def sessionHandler(request):
userName = request.GET.get('uname')
request.session['user'] = userName
print("current logged in user: " + request.session['user'])
return JsonResponse({'Response': 'success'})
def login(request):
uname = request.session.get('user')
UserName = {"Name": uname}
return render(request, 'login/login.html', UserName)
My use case is that any user who logs in their own machine and with own user name and password should be saved in the session data. Please help me to move further with this.
Thanks in advance !!!

Does this `if` condition ever runs?

I am reading some codes and I am confused here:
class PostListView(ListView):
# compressed
def get_context_data(self, **kwargs):
session_key = 'viewed_topic_{}'.format(self.topic.pk) # <-- here
if not self.request.session.get(session_key, False):
self.topic.views += 1
self.topic.save()
self.request.session[session_key] = True # <-- until here
kwargs['topic'] = self.topic
return super().get_context_data(**kwargs)
so the if condition checks that if there is no session with that key then increment self.topics.view by one.
I am confused here because whenever a user log in to the website their session will be created automatically and there are zero chances for being no session unless a user doesn't log in to the website. (Please notice that this project doesn't allow unauthenticated users to view the home page, sign in is a must.)
Is this if condition will ever be executed?
session_key = 'viewed_topic_{}'.format(self.topic.pk) the line indicates to post/topic specific session key.
if not self.request.session.get(session_key, False): this line checks if the key is available on the session. If not simply adding the key into the session.
Note: This is not the login session key. So, don't be confused with the login session key.
The if condition will be executed(expect there's an exception before). The if block will be executed depending on the condition.
If the condition is considered true depends on if the key for that topic id is not set. This should be true on the 1nd request for that topic ID and false for any later request in the same session. It might be different if other code sets that key or an exception is raised.
BTW You could just use:
if session_key not in self.request.session:
That would also be more readable.

Do I really need SESSION_SAVE_EVERY_REQUEST = True

Context: In my template, I click "Add product" and it takes me to another template where I have a list of all the products, I choose one and it takes me back to my selected product list template, and I keep adding so on.
In my receiver view I have this:
def vw_Sell(request):
if request.POST:
POST_received = request.POST
if 'ses_productList' in request.session:
request.session['ses_productList'].append({
'product': POST_received['idProduct'],
'quant': POST_received['quantity'],
})
else:
request.session['ses_productList'] = []
producList = request.session['ses_productList']
else:
if 'ses_productList' in request.session:
producList = request.session['ses_productList']
else:
producList = {}
context = {
"selected_products": producList
}
return render(request, "shop.html", context)
It only saves the first product to the session dictionary. I researched and found the SESSION_SAVE_EVERY_REQUEST and set it to True, and then it works, but I'm worry if I'm doing the correct way, thanks!
The session will only be saved automatically, if its modified property is True. That property is set every time you call the session object's __setitem__() method (usually via the = operator).
Here is the Django code for it.
However, you are appending to an already existing list, so the session object never knows that anything changed. To save the session, you need to set its modified property manually
request.session.modified = True
to mark the session "dirty" and have the session's Middleware save it.
The session Middleware code.
Final code thanks to #C14L:
if 'ses_productList' in request.session:
request.session['ses_productList'].append({
'product': POST_received['idProduct'],
'quant': POST_received['quantity'],
})
request.session.modified = True

Categories

Resources