diff --git a/core/TESTS/tests.py b/core/TESTS/tests.py index ec7de15..d7eeec3 100644 --- a/core/TESTS/tests.py +++ b/core/TESTS/tests.py @@ -126,18 +126,12 @@ class PageTests(TestCase): def test_expoweb_dir(self): response = self.client.get('/handbook') content = response.content.decode() - self.assertEqual(response.status_code, 200) - ph = r'Introduction to expo' - phmatch = re.search(ph, content) - self.assertIsNotNone(phmatch, "Failed to find expected text: '" + ph +"'") + self.assertEqual(response.status_code, 302) # directory, so redirects to /index.htm def test_expoweb_dirslash(self): response = self.client.get('/handbook/') content = response.content.decode() - self.assertEqual(response.status_code, 200) - ph = r'Introduction to expo' - phmatch = re.search(ph, content) - self.assertIsNotNone(phmatch, "Failed to find expected text: '" + ph +"'") + self.assertEqual(response.status_code, 302) # directory, so redirects to /index.htm def test_expoweb_dir_no_index(self): response = self.client.get('/handbook/troggle') @@ -147,6 +141,31 @@ class PageTests(TestCase): phmatch = re.search(ph, content) self.assertIsNotNone(phmatch, "Failed to find expected text: '" + ph +"'") + def test_expoweb_dir_with_index_htm(self): + response = self.client.get('/years/1999/index.htm') + content = response.content.decode() + self.assertEqual(response.status_code, 200) # directory, so redirects to /index.htm + ph = r'Passage descriptions for 1999' + phmatch = re.search(ph, content) + self.assertIsNotNone(phmatch, "Failed to find expected text: '" + ph +"'") + + def test_expoweb_dir_with_index_html(self): + response = self.client.get('/years/2015/index.html') + content = response.content.decode() + self.assertEqual(response.status_code, 200) # directory, so redirects to /index.htm + ph = r'Things left at top camp 2014' + phmatch = re.search(ph, content) + self.assertIsNotNone(phmatch, "Failed to find expected text: '" + ph +"'") + + def test_expoweb_dir_with_index2(self): + response = self.client.get('/handbook/index.htm') + content = response.content.decode() + self.assertEqual(response.status_code, 200) + ph = r'Introduction to expo' + phmatch = re.search(ph, content) + #print("\n ! - test_expoweb_dir_with_index2\n{}\n{}".format(response.reason_phrase, content)) + self.assertIsNotNone(phmatch, "Failed to find expected text: '" + ph +"'") + def test_expoweb_htm(self): response = self.client.get('/handbook/index.htm') content = response.content.decode() @@ -234,9 +253,9 @@ class PageTests(TestCase): def test_page_folk(self): # This page is separately generated, so it has the full data content - response = self.client.get('/folk/') + response = self.client.get('/folk/index.htm') content = response.content.decode() - self.assertEqual(response.status_code, 200) + self.assertEqual(response.status_code, 200) for ph in [ r'involves some active contribution', r'Naomi Griffiths', r'Gail Smith', diff --git a/core/views/auth.py b/core/views/auth.py new file mode 100644 index 0000000..19b80a4 --- /dev/null +++ b/core/views/auth.py @@ -0,0 +1,58 @@ +from builtins import str +from django.shortcuts import render +from django.http import Http404, HttpResponseRedirect +from django.contrib.auth import authenticate, login, logout +from django.contrib.auth import forms as auth_forms + +# This is copied from CUYC.cuy.website.view.auth +# If we want to do the whole online-email thing, we would also need to copy across the code in these +# imported files and delete what is superfluous. +# Or we could just load the latest version of django-registration app. +#from cuy.club.models import Member, Message +#from ..forms import WebsiteLoginForm, WebsiteRegisterForm +#from ...common import mail_site_error +#from .generic import user_is_active + +'''The login and logout functions. +This is also where we would manage registration: for people wanting to create and validate their individual +logon accounts/forgottenpassword''' + +############################ +# Authentication Functions # +############################ + +def expologout(request): + login_form = auth_forms.AuthenticationForm() + logout(request) + + return render(request, 'login/logout.html', {'form':login_form}) + +def expologin(request): + # GET + if not request.method == 'POST': + if (not request.user.is_authenticated) or (not request.user.is_active): + return render(request, 'login/index.html', {}) + else: + # going to login page when you are already logged in + return render(request, 'tasks.html', {}) + + # POST + username = request.POST['username'] + password = request.POST['password'] + + user = authenticate(username=username, password=password) + if user is None: + return render(request, 'login/index.html', + {'invalid': True, 'username':username}) + if not user.is_active: + return render(request, 'login/enable.html', + {'login_state':'notenabled'}) + + try: + login(request, user) + # Should do the ?next= stuff here.. + return render(request, 'tasks.html', {}) + except: + return render(request, 'errors/generic.html', {}) + + diff --git a/core/views/other.py b/core/views/other.py index 4f53ead..b6e3422 100644 --- a/core/views/other.py +++ b/core/views/other.py @@ -35,8 +35,8 @@ def showrequest(request): return HttpResponse(request.GET) def frontpage(request): - '''never seen in practice''' - # bthe messages system does a popup on this page if there is a recent message, e.g. from the admin site actions. + '''never seen in common practice. Logon should redirect here when this is more useful''' + # the messages system does a popup on this page if there is a recent message, e.g. from the admin site actions. # via django.contrib.messages.middleware.MessageMiddleware # this is set in the templates. if request.user.is_authenticated(): diff --git a/databaseReset.py b/databaseReset.py index 1d66706..9151a43 100644 --- a/databaseReset.py +++ b/databaseReset.py @@ -6,7 +6,6 @@ import json import resource import settings -import credentials """ Command-line utility for loading cave data files into troggle's database. The command line options select which combination of classes of data will be imported, @@ -56,9 +55,13 @@ if os.geteuid() == 0: exit() expouser=settings.EXPOUSER -expouserpass=credentials.EXPOUSERPASS +expouserpass=settings.EXPOUSERPASS expouseremail=settings.EXPOUSER_EMAIL +expoadminuser=settings.EXPOADMINUSER +expoadminuserpass=settings.EXPOADMINUSERPASS +expoadminuseremail=settings.EXPOADMINUSER_EMAIL + def reinit_db(): """Rebuild database from scratch. Deletes the file first if sqlite is used, otherwise it drops the database and creates it. @@ -109,9 +112,28 @@ def reinit_db(): print("users in db already: ",len(User.objects.all())) with transaction.atomic(): try: - print(" - Setting up admin user on: " + django.db.connections.databases['default']['NAME']) + print(" - Setting up expo user on: " + django.db.connections.databases['default']['NAME']) print(" - user: {} ({:.5}...) <{}> ".format(expouser, expouserpass, expouseremail)) user = User.objects.create_user(expouser, expouseremail, expouserpass) + user.is_staff = False + user.is_superuser = False + user.save() + except: + print(" ! INTEGRITY ERROR user on: " + settings.DATABASES['default']['NAME']) + print(django.db.connections.databases['default']['NAME']) + print(" ! You probably have not got a clean db when you thought you had.\n") + print(" ! Also you are probably NOT running an in-memory db now.\n") + print("users in db: ",len(User.objects.all())) + print("tables in db: ",len(connection.introspection.table_names())) + memdumpsql(fn='integrityfail.sql') + django.db.connections.databases['default']['NAME'] = ':memory:' + #raise + + with transaction.atomic(): + try: + print(" - Setting up expoadmin user on: " + django.db.connections.databases['default']['NAME']) + print(" - user: {} ({:.5}...) <{}> ".format(expoadminuser, expoadminuserpass, expoadminuseremail)) + user = User.objects.create_user(expoadminuser, expoadminuseremail, expoadminuserpass) user.is_staff = True user.is_superuser = True user.save() diff --git a/deprecations.txt b/deprecations.txt index e00eb3a..06c148a 100644 --- a/deprecations.txt +++ b/deprecations.txt @@ -4,9 +4,3 @@ from collections import Counter, Iterator, Mapping, OrderedDict /mnt/d/CUCC-Expo/t37/lib/python3.7/site-packages/django/core/paginator.py:126: DeprecationWarning: Using or importing the ABCs from 'collections' instead of from 'collections.abc' is deprecated since Python 3.3,and in 3.9 it will stop working class Page(collections.Sequence): -/mnt/d/CUCC-Expo/t37/lib/python3.7/site-packages/registration/auth_urls_classes.py:20: DeprecationWarning: -include('registration.auth_urls') is deprecated and will be -removed in django-registration 3.0. Use -include('django.contrib.auth.urls') instead. - - DeprecationWarning diff --git a/localsettingsWSL.py b/localsettingsWSL.py index 3a9a793..7262ca0 100644 --- a/localsettingsWSL.py +++ b/localsettingsWSL.py @@ -14,6 +14,8 @@ a system-wide location rather than just a local directory. This file is included at the end of the main troggle/settings.py file so that it overwrites defaults in that file. + +Read https://adamj.eu/tech/2020/03/16/use-pathlib-in-your-django-project/ """ print(" * importing troggle/localsettings.py") @@ -24,11 +26,11 @@ print(" * importing troggle/localsettings.py") # - we don't want to have to change the expo system password ! #----------------------------------------------------------------- # default values, real secrets imported from credentials.py -# EXPOUSERPASS = "nnn:gggggg" -# EMAIL_HOST_PASSWORD = "insert-real-email-password-here" -from credentials import EXPOUSERPASS -from credentials import EMAIL_HOST_PASSWORD +SECRET_KEY = "z514d%crn*fpd*ewt_27m_r^a#vaeozn0---^fj!355qki*vj2" +EXPOUSERPASS = "161:gosser" +EXPOADMINUSERPASS = "gosser:161" +EMAIL_HOST_PASSWORD = "smtp-django-test" EXPOFILESREMOTE = False # if True, then re-routes urls in expofiles to remote sever. Tests are then less accurate. #SECURE_SSL_REDIRECT = True # breaks 7 tests in test suite 301 not 200 (or 302) and runserver fails completely @@ -127,11 +129,13 @@ TEMPLATES = [ ] # Passwords are loaded from credentials.py by settings.py -EXPOUSERPASS = "nnn:gggggg" # overwritten by loading from credentials.py -EMAIL_HOST_PASSWORD = "insert-real-email-password-here" # overwritten by loading from credentials.py +#EXPOUSERPASS = "nnn:gggggg" # overwritten by loading from credentials.py +#EMAIL_HOST_PASSWORD = "insert-real-email-password-here" # overwritten by loading from credentials.py EXPOUSER = 'expo' EXPOUSER_EMAIL = 'philip.sargent@gmail.com' +EXPOADMINUSER = 'expoadmin' +EXPOADMINUSER_EMAIL = 'philip.sargent@gmail.com' EMAIL_HOST = "smtp-auth.mythic-beasts.com" EMAIL_HOST_USER = "django-test@klebos.net" # Philip Sargent really diff --git a/pre-push.sh b/pre-push.sh index 92cf6d2..eb4e617 100644 --- a/pre-push.sh +++ b/pre-push.sh @@ -1,6 +1,6 @@ #! /bin/sh # create and sanitise files for pushing to repo -# Philip Sargent 2020/06/20 +# Philip Sargent 2021/04/06 echo deprecations. python -Wall manage.py check -v 3 2>deprecations.txt >/dev/null echo diffsettings. @@ -13,14 +13,17 @@ python manage.py inspectdb > troggle-inspectdb.py #egrep -in "unable|error" troggle-inspectdb.py echo remove passwords. cp localsettings.py localsettingsWSL.py -sed -i '/EXPOUSERPASS/ s/^.*$/EXPOUSERPASS = "nnn:gggggg - real-expo-password--is-imported-from-credentials.py"/' diffsettings.txt -echo " reset: EXPOUSERPASS = \"nnn:gggggg\" - real-expo-password--is-imported-from-credentials.py" +sed -i '/EXPOUSERPASS/ s/^.*$/EXPOUSERPASS = "nnn:gggggg - real-expo-password---imported-from-localsettings.py"/' diffsettings.txt +echo " reset: EXPOUSERPASS = \"nnn:gggggg\" - real-expo-password---imported-from-localsettings.py" -sed -i '/EMAIL_HOST_PASSWORD/ s/^.*$/EMAIL_HOST_PASSWORD = "real-email-password--is-imported-from-credentials.py"/' diffsettings.txt -echo " reset: EMAIL_HOST_PASSWORD = \"real-email-password-is-imported-from-credentials.py\"" +sed -i '/EXPOADMINUSERPASS/ s/^.*$/EXPOADMINUSERPASS = "nnn:gggggg - real-expo-password---imported-from-localsettings.py"/' diffsettings.txt +echo " reset: EXPOUSERPASS = \"gggggg:nnn\" - real-expo-password---imported-from-localsettings.py" -sed -i '/SECRET_KEY/ s/^.*$/SECRET_KEY = "real-SECRET_KEY-is-imported-from-credentials.py"/' diffsettings.txt -echo " reset: SECRET_KEY = \"real-SECRET_KEY-is-imported-from-credentials.py\"" +sed -i '/EMAIL_HOST_PASSWORD/ s/^.*$/EMAIL_HOST_PASSWORD = "real-email-password---imported-from-localsettings.py"/' diffsettings.txt +echo " reset: EMAIL_HOST_PASSWORD = \"real-email-password--imported-from-localsettings.py\"" + +sed -i '/SECRET_KEY/ s/^.*$/SECRET_KEY = "real-SECRET_KEY--imported-from-localsettings.py"/' diffsettings.txt +echo " reset: SECRET_KEY = \"real-SECRET_KEY--imported-from-localsettings.py\"" # diff --git a/requirements.txt b/requirements.txt index 92e6601..311dcee 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ confusable-homoglyphs==3.2.0 Django==1.11.29 -django-registration==2.5.2 docutils==0.16 +gunicorn==20.1.0 Pillow==7.2.0 pytz==2020.1 Unidecode==1.1.1 diff --git a/settings.py b/settings.py index 8c27be8..15f139f 100644 --- a/settings.py +++ b/settings.py @@ -23,7 +23,6 @@ print("* importing troggle/settings.py") # default value, then gets overwritten by real secrets SECRET_KEY = "not-the-real-secret-key-a#vaeozn0---^fj!355qki*vj2" -from credentials import SECRET_KEY # Note that this builds upon the django system installed # global settings in @@ -123,7 +122,7 @@ INSTALLED_APPS = ( 'django.contrib.messages', 'django.contrib.admindocs', # 'django.contrib.staticfiles', # Using workarounds with expopages - 'registration', # only for expo user. REPLACE using django.contrib.auth + #'registration', # only for expo user. REPLACE using django.contrib.auth 'troggle.core', ) diff --git a/templates/base.html b/templates/base.html index 971152d..1fe8029 100644 --- a/templates/base.html +++ b/templates/base.html @@ -20,7 +20,7 @@ You are logged in as {{ user.username }} {% if user.person %}({{ user.person }}) {% endif %}. - | Log out {% else %} Sign up | Log in {% endif %} + | Log out {% else %} Register | Log in {% endif %} {% endblock%} {% block editLink %} diff --git a/templates/errors/generic.html b/templates/errors/generic.html new file mode 100644 index 0000000..a626a5a --- /dev/null +++ b/templates/errors/generic.html @@ -0,0 +1,25 @@ +{% extends 'base.html' %} + +{% block title %}Website Error - {% endblock %} +{% block content %} + +
+

Website Error

+
+
+
+
+
+

There has been an error.

+

We are terribly sorry but an unknown fault has occurred.

+ + +
+
+
+ + + + + +{% endblock %} \ No newline at end of file diff --git a/templates/frontpage.html b/templates/frontpage.html index eca67cb..4405d10 100644 --- a/templates/frontpage.html +++ b/templates/frontpage.html @@ -33,25 +33,25 @@ {% block content %} - -

Welcome

-This is Troggle, the online system for Cambridge University Caving Club's Expeditions to Austria. +This is Troggle, the online system for Cambridge University Caving Club's Expeditions to Austria.

-Here you will find information about the {{expedition.objects.count}} expeditions the club has undertaken since 1976. Browse survey information, photos, and description wikis for {{Cave.objects.count}} caves, {{subcave.objects.count}} areas within those caves, and {{extantqms.count}} going leads yet to be explored. We have {{Photo.objects.count}} photos and {{Logbookentry.objects.count}} logbook entries. +Here you will find information about the {{expedition.objects.count}} expeditions the club has undertaken since 1976. Browse survey information, photos, and description pages for {{Cave.objects.count}} caves and {{extantqms.count}} going leads yet to be explored. We have {{Photo.objects.count}} photos and {{Logbookentry.objects.count}} logbook entries.

-You are not logged-in, so not all the pages will be visible and you will not be able to edit anything. + +You are not logged-in, so a few of the unpublished cave survey pages will not be visible to you. And of course you will not be able to edit anything.

- + + +

Nial in Eishohle in 2007. {% endblock content %} {% block margins %} - - + {% endblock margins %} \ No newline at end of file diff --git a/templates/login/index.html b/templates/login/index.html new file mode 100644 index 0000000..6db786e --- /dev/null +++ b/templates/login/index.html @@ -0,0 +1,53 @@ +{% extends 'base.html' %} + +{% block content %} + +

+

Troggle user login

+
+ +{% if message %} +
+ {% if title %} +
+

{{title}}

+
+ {% endif %} +

{{message }}

+
+{% endif %} +

Troggle ordinary user login - no access to Django control panel

+

(This is using template login/index.html) +