Quinton Robbins
Quinton Robbins

Reputation: 31

Django sessions: changing session key when modified

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.

Upvotes: 3

Views: 5892

Answers (3)

CeDeROM
CeDeROM

Reputation: 600

Check your SESSION_COOKIE_SECURE value and make sure you are using HTTPS when True..

https://github.com/lepture/flask-wtf/issues/76

Upvotes: -1

Steven
Steven

Reputation: 2688

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.

Upvotes: 1

Francis Yaconiello
Francis Yaconiello

Reputation: 10919

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']))
  1. Clear cookies for 127.0.0.1
  2. go to 127.0.0.1:8000/test/
    • Exception at /test/ 4793f2453758d7021a43a348a0f40a83 : 8568f729991e740395179c56cd37cf18 === test
  3. 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.

Upvotes: 1

Related Questions