Site redirects to sign-in page even though not coded to.

Just setting the browser to:

/diagram/create-new

and the site will redirect to:

/sign-in?next=/diagram/create-new

I can’t find anywhere in the urls.py, views.py or the template that says to do that! Could it be a settings.py thing? Some plugin I installed? I haven’t worked on this project in over 6 months and it looks like I saved it on github in a non-working state.

But here is the code that’s broken:
https://github.com/enjoysmath/QuiverDatabase

1 Like

Yes, that is true. I don’t display the create_new_diagram view unless they’re logged in. Removing that would introduce the bug that anyone can access the page. But maybe I will like that (guests or w/e).

Well I removed it and the same issue is happening exactly - redirects to login page. This is independent of that decorator which just means the user needs to be logged in, which they are already. I forgot to mention that this is occuring after login.

I don’t know if I replied to you correctly with a message. Okay, this one is pointing to you. So please read my undirected message above. This really might be some weird change like in settings.py, but I can’t figure out what:

"""
Django settings for QuiverDatabase project.

Generated by 'django-admin startproject' using Django 3.1.5.

For more information on this file, see
https://docs.djangoproject.com/en/3.1/topics/settings/

For the full list of settings and their values, see
https://docs.djangoproject.com/en/3.1/ref/settings/
"""

from pathlib import Path
import os
import django_heroku
import dj_database_url


LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'console': {
            'class': 'logging.StreamHandler',
            },
        },
    'root': {
        'handlers': ['console'],
        'level': 'DEBUG',
        },
} 

# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent


# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/3.1/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = os.environ.get('SECRET_KEY', 'TODO: use secret key in deployment')

# SECURITY WARNING: don't run with debug turned on in production!
if os.environ.get('ON_HEROKU', '0') == '0':
    DEBUG = True
else:
    DEBUG = False

DEBUG = True    # TODO comment out

ALLOWED_HOSTS = [
    '127.0.0.1',
    'quiver-database.herokuapp.com'
]


# Application definition

INSTALLED_APPS = [
    'password_reset',
    'django.contrib.messages',
    'django_neomodel',    
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.staticfiles',
    'database_service.apps.DatabaseServiceConfig',
    'whitenoise.runserver_nostatic',
    'crispy_forms',
    'rules.apps.RulesConfig',
    'accounts.apps.AccountsConfig',
    'diagram_editor.apps.DiagramEditorConfig',
]

MIDDLEWARE = [
    'django.contrib.sessions.middleware.SessionMiddleware',    
    'django.contrib.messages.middleware.MessageMiddleware',    
    'django.middleware.security.SecurityMiddleware',  
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    'whitenoise.middleware.WhiteNoiseMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

ROOT_URLCONF = 'QuiverDatabase.urls'

TEMPLATES = [
    # Tried Jinja2 template engine, and it didn't work with crispy forms (unfixable errors when doing forms)
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [BASE_DIR.joinpath('QuiverDatabase', 'templates'),
                 BASE_DIR.joinpath('accounts', 'templates')],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages'
            ]
        }
    }
]
    
#TEMPLATES = [
    #{
        #'BACKEND': 'django.template.backends.django.DjangoTemplates',
        #'DIRS': [str(BASE_DIR.joinpath('templates'))],
        #'APP_DIRS': True,
        #'OPTIONS': {
            #'debug': True,
            #'context_processors': [
                #'django.template.context_processors.debug',
                #'django.template.context_processors.request',
                #'django.contrib.auth.context_processors.auth',
                #'django.contrib.messages.context_processors.messages',
            #],
        #},
    #},
    #{
        #"BACKEND": "django.template.backends.jinja2.Jinja2",
        #"DIRS": [os.path.join(BASE_DIR, "templates")],
        #"APP_DIRS": True,
        #'OPTIONS': {
            #"environment": "QuiverDatabase.jinja2.environment",
        #},
    #},    
#]

WSGI_APPLICATION = 'QuiverDatabase.wsgi.application'

# Database
# https://docs.djangoproject.com/en/3.1/ref/settings/#databases

if os.environ.get('ON_HEROKU', '0') == '0':
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.sqlite3',
            'NAME': BASE_DIR / 'db.sqlite3',
        }
    }
else:
    DATABASES = {
        # TODO: what does this max_age setting do?
        'default' : dj_database_url.config(conn_max_age=600)
    }

# Password validation
# https://docs.djangoproject.com/en/3.1/ref/settings/#auth-password-validators

AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
        },
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
        },
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
        },
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
        },
]


# Internationalization
# https://docs.djangoproject.com/en/3.1/topics/i18n/

LANGUAGE_CODE = 'en-us'

TIME_ZONE = 'UTC'

USE_I18N = True

USE_L10N = True

USE_TZ = True


# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/3.1/howto/static-files/

STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'static')
STATICFILES_DIRS = [
    os.path.join(BASE_DIR, 'diagrams/static'),
    os.path.join(BASE_DIR, 'static'),    
    os.path.join(BASE_DIR,'QuiverDatabase/static'),
    # ^^^ BUGFIX: this fixes a lot of issues such as KaTeX load error
]
STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'


def neo4j_url():
    url = os.environ.get('NEO4J_SCHEMA', 'bolt') + "://"
    url += os.environ.get('NEO4J_USERNAME', 'neo4j') + ":"
    url += os.environ.get('NEO4J_PASSWORD', 'fusion123') + "@"
    url += os.environ.get('NEO4J_HOST', 'localhost') + ":"
    url += os.environ.get('NEO4J_PORT', '7687')
    return url

NEOMODEL_NEO4J_BOLT_URL = neo4j_url()

NEOMODEL_SIGNALS = True
NEOMODEL_FORCE_TIMEZONE = False
NEOMODEL_ENCRYPTED_CONNECTION = False  # TODO: how do we switch this on without error?

from neomodel import config   # BUGFIX: had to do it this way
config.MAX_POOL_SIZE = 50  # TODO: what does this affect?

#LOGIN_REDIRECT_URL = 'home'
#LOGOUT_REDIRECT_URL = 'home'

# Activate Django-Heroku.
django_heroku.settings(locals())

MEDIA_ROOT = os.path.join(BASE_DIR,'media')
MEDIA_URL = '/media/'

CRISPY_TEMPLATE_PACK = 'bootstrap3'

MAX_TEXT_LENGTH = 80
MAX_USERNAME_LENGTH = 50
MAX_PASSWORD_LENGTH = 50

MAX_USER_EDIT_DIAGRAMS = 8

#CSRF_USE_SESSIONS = False
#CSRF_COOKIE_HTTPONLY = False
#CSRF_COOKIE_DOMAIN = 'localhost'

EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'

#By default, SESSION_EXPIRE_AT_BROWSER_CLOSE is set to False, which means session cookies 
#will be stored in users' browsers for as long as SESSION_COOKIE_AGE. 
#Use this if you don't want people to have to log in every time they open a browser.
SESSSION_EXPIRE_AT_BROWSER_CLOSE = False

#TODO:
#Clearing the session store¶
#As users create new sessions on your website, session data can accumulate in your session store. If you're using the database backend, the django_session database table will grow. If you're using the file backend, your temporary directory will contain an increasing number of files.

#To understand this problem, consider what happens with the database backend. When a user logs in, Django adds a row to the django_session database table. Django updates this row each time the session data changes. If the user logs out manually, Django deletes the row. But if the user does not log out, the row never gets deleted. A similar process happens with the file backend.

#Django does not provide automatic purging of expired sessions. Therefore, it's your job to purge expired sessions on a regular basis. Django provides a clean-up management command for this purpose: clearsessions. It's recommended to call this command on a regular basis, for example as a daily cron job.

#Note that the cache backend isn't vulnerable to this problem, because caches automatically delete stale data. Neither is the cookie backend, because the session data is stored by the users' browsers.

#from jinja2 import Undefined
#JINJA2_ENVIRONMENT_OPTIONS = { 'undefined' : Undefined }

# TODO: Enable Click-jacking protection
X_FRAME_OPTIONS = 'ALLOW'   # ie set this to "DENY"
# https://docs.djangoproject.com/en/1.11/ref/clickjacking/

#CSRF_COOKIE_SAMESITE = None
#CSRF_COOKIE_SECURE = True

LOGIN_URL = '/sign-in'     # this should coinside with url pattern of login view
LOGOUT_URL = '/sign-out'   # same but for logout view
LOGIN_REDIRECT_URL = '/' # url to main page

I am able to view the call stack of the breakpoint set in login view when create button is clicked. So to see what is calling this erroneous login view. Here’s what my IDE looks like:

However, it’s not straightforward what to look for. Any ideas using the call stack?

I’m able to click on items in the call stack, go to their locations in Django / my code, and hover over variables. I’m seeing that the erroneous sign-in url occurs is here:

I checked everything from the top of the stack down. However, there’s no indication (unless you fully analyze the code) where it comes from. It appears if out of no where.

@KenWhitesell Okay, it’s related to whatever plugin I have that responds to these these highlighted fields in settings.py:

LOGIN_URL, LOGOUT_URL

Because I changed them to ‘sign-in1’ etc.

Ok, couple different things here -

  1. Yes, I see all the messages in the thread. There’s no need to specifically direct a response to me. (It’s ok, it’s just that you don’t need to do that for me to see it.)

  2. The only thing I can see that appears “odd” to me is the order in which you have your middleware specified. While it doesn’t appear to me to violate the guidelines for Middleware ordering, I do know that the sequence is important and I tend to follow the order given.

  3. You’re using the password_reset app which hasn’t been updated since 2018 and is only validated through Django 2.1 - it may be obsolete for your current environment.

  4. Sorry, I typically can’t read screen captures. They provide no useful information to me.

Ideas for further research:

  • Re-order your middleware, remove the duplicate middleware entry
  • Remove the password_reset app and all references to it.
  • After logging in, check your database to verify that an entry was made in the sessions table. You could delete all the entries in the table before trying it to make it easier to spot.
1 Like

Well, every plugin I have is listed in settings.py above, I can’t comment themout without causing an error that they are not listed. I must have done something involving a login plugin, I thought maybe password reset.

Do you think I should start from scratch and just paste in piece by piece until the site breaks if it even does (again)?

I will try each of those, I’ve marked your answer as the solution for now, because I’m pretty sure that it will work once I try a few of those things. But if it doesn’t, I will mark another answer later today (hopefully).

Right now I’ve started from scratch (fresh repository) but I’m going about the design differently. I’m starting with a django-bootstrap4 example code, and modifying it. Because I suck so much at web dev that I can’t even get a bootstrap4 form to render properly. I think this is the right approach, go through each Django recipe one by one until the site is built. I already coded the critical part (subgraph search). It’s probably not optimal but once we’re off the ground we can spend more time on the critical code.

I am an 8 year PyQt5 / Python expert. How long will it take me to master Django & webdev? It seems way harder imho.

I’m not sure I would describe it as “harder”, but it is more “complex”.

There are more different parts to a complete web app - Security, Python, JavaScript, HTML, CSS, networking and deployment issues, just to name a few.

In a desktop app, you’ve got complete control of the UI, you “know” who’s running the app, and it’s all local. In a web app, you may be accessed by a number of different browsers, by different people, all at the same time, from anywhere around the world.

Yes, it does raise the complexity to a whole new level.

1 Like

@KenWhitesell thank you for your guidance. I decided to rewrite from scratch for various reasons. Hopefully I don’t do the stupid thing I did before that broke the site - causing it to always redirect to sign-in.