forked from expo/troggle
only allows registration once, except for admins
This commit is contained in:
@@ -277,6 +277,15 @@ def is_identified_user(user):
|
|||||||
if user.username in ["expo", "expoadmin"]:
|
if user.username in ["expo", "expoadmin"]:
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def is_admin_user(user):
|
||||||
|
if user.is_anonymous:
|
||||||
|
return False
|
||||||
|
if user.username in ["expoadmin"]:
|
||||||
|
return True
|
||||||
|
if user.is_superuser: # set in parsers/users.py i.e. WOokey, Philip S.
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
def get_git_string(user):
|
def get_git_string(user):
|
||||||
if not is_identified_user(user):
|
if not is_identified_user(user):
|
||||||
|
|||||||
@@ -13,19 +13,24 @@ from troggle.parsers.users import register_user, get_encryptor, ENCRYPTED_DIR, U
|
|||||||
from troggle.parsers.people import troggle_slugify
|
from troggle.parsers.people import troggle_slugify
|
||||||
from troggle.core.utils import (
|
from troggle.core.utils import (
|
||||||
add_commit,
|
add_commit,
|
||||||
is_identified_user
|
is_identified_user,
|
||||||
|
is_admin_user,
|
||||||
)
|
)
|
||||||
from troggle.core.views.auth import expologout
|
from troggle.core.views.auth import expologout
|
||||||
|
|
||||||
"""
|
"""
|
||||||
This is the new individual user login registration, instead of everyone signing
|
This is the new 2025 individual user login registration, instead of everyone signing
|
||||||
in as "expo". This will be useful for the kanban expo organisation tool.. maybe?
|
in as "expo". This may be useful for the kanban expo organisation tool. It also will
|
||||||
|
eventually (?) replace the cookie system for assigning responsibility to git commits.
|
||||||
|
|
||||||
|
This registration system has logic spread between these functions and the Django templates.
|
||||||
|
It seriously needs refactoring as the logic is opaque. Before that, it needs a full set of
|
||||||
|
tests to check all the combinations of users/logged-on/pre-registered/admin etc. etc.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
todo = """
|
todo = """
|
||||||
- Make all this work with New people who have never been on expo before
|
- Make all this work with New people who have never been on expo before - add a queue/email to be approved by an admin
|
||||||
- Stop anyone re-registering an email for an id which already has an email
|
- Stop anyone re-registering an email for an id which already has an email (unless by an admin)
|
||||||
|
|
||||||
- login automatically, and redirect to control panel ?
|
- login automatically, and redirect to control panel ?
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -47,7 +52,7 @@ class ExpoPasswordResetForm(PasswordResetForm):
|
|||||||
return email
|
return email
|
||||||
|
|
||||||
def reset_done(request):
|
def reset_done(request):
|
||||||
"""This page is called when a password reset has successively occured
|
"""This page is called when a password reset has successively occurred.
|
||||||
Unfortunately by this point, we do not know the name of the user who initiated the
|
Unfortunately by this point, we do not know the name of the user who initiated the
|
||||||
password reset, so when we do the git commit of the encrypted users file
|
password reset, so when we do the git commit of the encrypted users file
|
||||||
we do not have a name to put to the responsible person. To do that,
|
we do not have a name to put to the responsible person. To do that,
|
||||||
@@ -70,11 +75,13 @@ def reset_done(request):
|
|||||||
|
|
||||||
def newregister(request, username=None):
|
def newregister(request, username=None):
|
||||||
"""To register a COMPLETELY new user on the troggle system,
|
"""To register a COMPLETELY new user on the troggle system,
|
||||||
WITHOUT any previous expo attendance.
|
WITHOUT any previous expo attendance. Currently allows random anyone to add suff to our system. Yuk.
|
||||||
|
This is DISABLED pending implementing a queue/confirm mechanism
|
||||||
|
except for admin users.
|
||||||
"""
|
"""
|
||||||
current_user = request.user # if not logged in, this is 'AnonymousUser'
|
current_user = request.user # if not logged in, this is 'AnonymousUser'
|
||||||
warning = ""
|
warning = ""
|
||||||
|
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
form = newregister_form(request.POST)
|
form = newregister_form(request.POST)
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
@@ -84,14 +91,16 @@ def newregister(request, username=None):
|
|||||||
nameslug = troggle_slugify(fullname)
|
nameslug = troggle_slugify(fullname)
|
||||||
print(f"NEW user slug {nameslug}")
|
print(f"NEW user slug {nameslug}")
|
||||||
|
|
||||||
expoers = User.objects.filter(username=nameslug)
|
if User.objects.filter(username=nameslug).count() != 0:
|
||||||
if len(expoers) != 0:
|
|
||||||
# Disallow a name which already exists, use the other form.
|
# Disallow a name which already exists, use the other form.
|
||||||
return HttpResponseRedirect(f"/accounts/register/{nameslug}")
|
return HttpResponseRedirect(f"/accounts/register/{nameslug}")
|
||||||
# create User in the system and refresh stored encrypted user list and git commit it:
|
# create User in the system and refresh stored encrypted user list and git commit it:
|
||||||
updated_user = register_user(nameslug, email, password=None, pwhash=None, fullname=fullname)
|
if is_admin_user(request.user):
|
||||||
save_users(request, updated_user, email)
|
updated_user = register_user(nameslug, email, password=None, pwhash=None, fullname=fullname)
|
||||||
return HttpResponseRedirect("/accounts/password_reset/")
|
save_users(request, updated_user, email)
|
||||||
|
return HttpResponseRedirect("/accounts/password_reset/")
|
||||||
|
else:
|
||||||
|
return render(request, "login/register.html", {"form": form, "warning": "Only ADMINs can do this", "newuser": True})
|
||||||
else: # GET
|
else: # GET
|
||||||
form = newregister_form(initial={"visible-passwords": "True"})
|
form = newregister_form(initial={"visible-passwords": "True"})
|
||||||
|
|
||||||
@@ -105,7 +114,7 @@ def re_register_email(request):
|
|||||||
|
|
||||||
and we ignore any username specified in the URL of the request.
|
and we ignore any username specified in the URL of the request.
|
||||||
"""
|
"""
|
||||||
logged_in = (identified_login := is_identified_user(request.user))
|
logged_in = (identified_login := is_identified_user(request.user)) # logged_in used on form
|
||||||
if not logged_in:
|
if not logged_in:
|
||||||
return HttpResponseRedirect("/accounts/login/")
|
return HttpResponseRedirect("/accounts/login/")
|
||||||
|
|
||||||
@@ -130,50 +139,60 @@ def re_register_email(request):
|
|||||||
form = register_email_form(initial=initial_values)
|
form = register_email_form(initial=initial_values)
|
||||||
return render(request, "login/register_email.html", {"form": form})
|
return render(request, "login/register_email.html", {"form": form})
|
||||||
|
|
||||||
|
def reshow_disabled(request, url_username, initial_values, warning, admin_notice):
|
||||||
|
"""Shows the form, but completely disabled, with messages showing what the user did
|
||||||
|
wrong or inappropriately. Could all be replaced by form validation methods ?
|
||||||
|
"""
|
||||||
|
print(warning)
|
||||||
|
print(admin_notice)
|
||||||
|
form = register_form(initial=initial_values)
|
||||||
|
form.fields["username"].widget.attrs["readonly"]="readonly"
|
||||||
|
form.fields["email"].widget.attrs["readonly"]="readonly"
|
||||||
|
form.fields["password1"].widget.attrs["readonly"]="readonly"
|
||||||
|
form.fields["password2"].widget.attrs["readonly"]="readonly"
|
||||||
|
|
||||||
|
return render(request, "login/register.html", {"form": form, "admin_notice": admin_notice, "warning": warning})
|
||||||
|
# not used, loops: return HttpResponse warning, admin_notice): Redirect(f"/accounts/login/?next=/accounts/register/{url_username}")
|
||||||
|
|
||||||
|
|
||||||
def register(request, url_username=None):
|
def register(request, url_username=None):
|
||||||
"""To register an old expoer as a new user on the troggle system,
|
"""To register an old expoer as a new user on the troggle system,
|
||||||
for someone who has previously attended expo,
|
for someone who has previously attended expo.
|
||||||
similar to the "expo" user
|
Authority this gives is the same as the "expo" user
|
||||||
(with cavey:beery password) but specific to an individual.
|
(with cavey:beery password) but specific to an individual.
|
||||||
|
|
||||||
We should only allow this to be done ONCE for each user-id.
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
warning = ""
|
warning = ""
|
||||||
admin_notice = ""
|
admin_notice = ""
|
||||||
initial_values={"visible-passwords": "True"}
|
initial_values={"visible-passwords": "True"}
|
||||||
print(f"{url_username=}")
|
print(f"{url_username=} {request.user.username=}")
|
||||||
|
|
||||||
if request.user.is_anonymous:
|
# Since this form is for old expoers, we can expect that they know how to login as 'expo'. So require this at least.
|
||||||
# Anonymous users are not logged in as anybody. Which is what we expect
|
if request.user.is_anonymous:
|
||||||
pass
|
warning = "ANONYMOUS users not allowed to Register old expoers. Login, e.g. as 'expo', and try again."
|
||||||
else:
|
return reshow_disabled(request, url_username, initial_values, warning, admin_notice)
|
||||||
logged_in = (identified_login := is_identified_user(request.user))
|
|
||||||
if logged_in:
|
if url_username: # if provided in URL, but if POST then this could alternatively be in the POST data field ["username"]
|
||||||
# logged in as a known real person with a User logon
|
# print(url_username, "Person count",Person.objects.filter(slug=url_username).count())
|
||||||
print(f"Already logged in as {identified_login=}, redirecting to re_register_email()")
|
# print(url_username, "User count",User.objects.filter(username=url_username).count())
|
||||||
return re_register_email(request) # discarding url_username
|
|
||||||
else:
|
if (Person.objects.filter(slug=url_username).count() != 1):
|
||||||
print(f"user is logged in as somebody (but not an identified person, so must be 'expo')")
|
# not an old expoer, so redirect to the other form for completely new cavers,
|
||||||
# logout invisibly before we do anything, 'expo' is irrelevant; but 'expoadmin' is significant!
|
# but suppose it was a typo? Better to check this in response to a POST surely?
|
||||||
# , redirecting to expologout()
|
# To do: do this as a form-valiation action.
|
||||||
pass
|
|
||||||
# expologout(request) # returns a response, which we discard
|
|
||||||
|
|
||||||
# At this point we know the request user is not logged in at all.
|
|
||||||
if url_username: # if provided in URL
|
|
||||||
print(url_username, "Person count",Person.objects.filter(slug=url_username).count())
|
|
||||||
if Person.objects.filter(slug=url_username).count() != 1:
|
|
||||||
# not an old expoer, so redirect to the other form
|
|
||||||
return HttpResponseRedirect("/accounts/newregister/")
|
return HttpResponseRedirect("/accounts/newregister/")
|
||||||
# This is where we need to check that this url_username has or has not already been registered.
|
|
||||||
print(url_username, "User count",User.objects.filter(username=url_username).count())
|
already_registered = (User.objects.filter(username=url_username).count() == 1)
|
||||||
if User.objects.filter(username=url_username).count() == 1:
|
if already_registered:
|
||||||
# Do not allow registration unless superuser is logged in, oops, need to refactor/reorder
|
if is_admin_user(request.user):
|
||||||
pass
|
admin_notice = "ADMIN OVERRIDE ! Can re-set everything." # can proceed to reset everything
|
||||||
admin_notice = "ADMIN PRIViedge ?!"
|
else:
|
||||||
|
admin_notice = f"This former expoer '{url_username}' already has a registered email address and individual troggle access password."
|
||||||
|
return reshow_disabled(request, url_username, initial_values, warning, admin_notice)
|
||||||
|
|
||||||
|
logged_in = (identified_login := is_identified_user(request.user)) # used on the form
|
||||||
|
|
||||||
|
if url_username:
|
||||||
initial_values.update({"username": url_username})
|
initial_values.update({"username": url_username})
|
||||||
form = register_form(initial=initial_values)
|
form = register_form(initial=initial_values)
|
||||||
form.fields["username"].widget.attrs["readonly"]="readonly"
|
form.fields["username"].widget.attrs["readonly"]="readonly"
|
||||||
@@ -256,7 +275,7 @@ def write_users(registered_users, encryptedfile, git_string):
|
|||||||
|
|
||||||
class newregister_form(forms.Form): # not a model-form, just a form-form
|
class newregister_form(forms.Form): # not a model-form, just a form-form
|
||||||
"""This is the form for a new user who has not been on expo before and
|
"""This is the form for a new user who has not been on expo before and
|
||||||
does notalready have a username
|
does notalready have a username.
|
||||||
"""
|
"""
|
||||||
fullname = forms.CharField(strip=True, required=True,
|
fullname = forms.CharField(strip=True, required=True,
|
||||||
label="Forename Surname",
|
label="Forename Surname",
|
||||||
|
|||||||
@@ -2,4 +2,5 @@ We have both a templates/login and a templates/registration directory.
|
|||||||
|
|
||||||
The registration stuff is mostly in the /logins directory - oops.
|
The registration stuff is mostly in the /logins directory - oops.
|
||||||
|
|
||||||
This all needs to be cleaned up.
|
This all needs to be cleaned up.. BUT we are limited by Django-standard locations for some of this stuff,
|
||||||
|
so it can't be moved around willy-nilly.
|
||||||
@@ -49,24 +49,28 @@ User Registration - for a personal login to Troggle by a known caver
|
|||||||
ALSO it behaves differently if a username is specified in the URL, when username becomes readonly
|
ALSO it behaves differently if a username is specified in the URL, when username becomes readonly
|
||||||
|
|
||||||
-->
|
-->
|
||||||
<span style="color:red; font-weight: bold;">
|
<span style="color:blue; font-weight: bold;">
|
||||||
{{ admin_notice }}
|
{{ admin_notice }}
|
||||||
</span>
|
</span>
|
||||||
<h3>Register your email address</h3>
|
<h3>Register your email address</h3>
|
||||||
|
|
||||||
{% if newuser %}
|
{% if newuser %}
|
||||||
<p>You need to register on the website before you can fill out the 'signup' form to request to attend Expo.
|
<p>You need to register on the website before you can fill out the 'signup' form to request to attend Expo
|
||||||
|
(not in 2025: this is for future Expos).
|
||||||
{% else %}
|
{% else %}
|
||||||
<p>For previous expoers, your username must be your 'troggle id' as listed on the <a href='/people_ids'>past expoers list</a>
|
<p>For previous expoers, your username must be your 'troggle id' as listed on the <a href='/people_ids'>past expoers list</a>
|
||||||
|
e.g. "<a href="/person/rover-richardson">rover-richardson</a>".
|
||||||
<p>For new people wanting to come to expo for the first time, please use the <a href="/accounts/newregister/">New User</a> registration form
|
<p>For new people wanting to come to expo for the first time, please use the <a href="/accounts/newregister/">New User</a> registration form
|
||||||
{%endif %}
|
{%endif %}
|
||||||
<p>This will also eventually sign you up automatically to the
|
<p>This will also eventually sign you up automatically to the
|
||||||
<a href="https://lists.wookware.org/cgi-bin/mailman/roster/expo">expo email list</a>.
|
<a href="https://lists.wookware.org/cgi-bin/mailman/roster/expo">expo email list</a>.
|
||||||
So type in the same email address that you use there if you have already signed up to that.
|
So type in the same email address that you use there if you have already signed up to that.
|
||||||
<p>
|
<p>
|
||||||
|
<span style="color:red; font-weight: bold;">
|
||||||
|
{{ warning }} </span>
|
||||||
<span style="color:red">
|
<span style="color:red">
|
||||||
{{ warning }} {{ form.errors }}
|
{{ form.errors }} </span>
|
||||||
</span>
|
|
||||||
|
|
||||||
<div style='width: 700px; font-family: monospace; font-weight: bold; font-size: 150%; text-align: right; '>
|
<div style='width: 700px; font-family: monospace; font-weight: bold; font-size: 150%; text-align: right; '>
|
||||||
<form method="post" accept-charset="utf-8">{% csrf_token %}
|
<form method="post" accept-charset="utf-8">{% csrf_token %}
|
||||||
@@ -95,8 +99,8 @@ So type in the same email address that you use there if you have already signed
|
|||||||
</p>
|
</p>
|
||||||
{% if newuser %}
|
{% if newuser %}
|
||||||
{% else %}
|
{% else %}
|
||||||
{% if logged_in %}<!-- one we have initially logged in,
|
{% if logged_in %}<!-- once we have initially logged in,
|
||||||
all later password chnages are done ONLY via email token password re-set-->
|
all later password changes are done ONLY via email token password re-set-->
|
||||||
{% else %}
|
{% else %}
|
||||||
<p>
|
<p>
|
||||||
<label for="id_password1">Troggle password:</label>
|
<label for="id_password1">Troggle password:</label>
|
||||||
@@ -162,6 +166,8 @@ So type in the same email address that you use there if you have already signed
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div style='width: 50em' align="left">
|
<div style='width: 50em' align="left">
|
||||||
|
<p>The "Register" button will take you to a form where you will have to <em>re-type in the email address</em> you enter above.
|
||||||
|
|
||||||
{% include 'login/register_text.html' %}
|
{% include 'login/register_text.html' %}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ The reason for separating this template from the other one (register.html) is th
|
|||||||
gets very confused. So despite the partial duplication, it is easier to debug and maintain by
|
gets very confused. So despite the partial duplication, it is easier to debug and maintain by
|
||||||
having two separate templates.
|
having two separate templates.
|
||||||
-->
|
-->
|
||||||
<h3>Register your email address</h3>
|
<h3>Re-register your email address, or change to a different one</h3>
|
||||||
|
|
||||||
<p>[For new people wanting to come to expo for the first time,
|
<p>[For new people wanting to come to expo for the first time,
|
||||||
please use the <a href="/accounts/newregister/">New User</a> registration form]
|
please use the <a href="/accounts/newregister/">New User</a> registration form]
|
||||||
@@ -76,6 +76,7 @@ So type in the same email address that you use there if you have already signed
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div style='width: 50em' align="left">
|
<div style='width: 50em' align="left">
|
||||||
|
<p>This will take you to a form where you will have to <em>re-type in the email address</em> you enter above.
|
||||||
{% include 'login/register_text.html' %}
|
{% include 'login/register_text.html' %}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ Yes, you can put in any 'de', 'van' or 'von' or whatever 'nobiliary particle' yo
|
|||||||
Nicknames and fun names are done later.
|
Nicknames and fun names are done later.
|
||||||
{% else %}
|
{% else %}
|
||||||
<p>For previous expoers, your username must be your 'troggle id' as listed on the <a href='/people_ids'>past expoers list</a>
|
<p>For previous expoers, your username must be your 'troggle id' as listed on the <a href='/people_ids'>past expoers list</a>
|
||||||
|
e.g. "<a href="/person/rover-richardson">rover-richardson</a>".
|
||||||
{%endif %}
|
{%endif %}
|
||||||
<p>Unfortunately cavers tend to use weird and playful names when signing up for things,
|
<p>Unfortunately cavers tend to use weird and playful names when signing up for things,
|
||||||
so we can't automatically connect the troggle names and ids with the email addresses
|
so we can't automatically connect the troggle names and ids with the email addresses
|
||||||
@@ -37,7 +38,8 @@ has 13,000+ bogus bot registrations which we now have to clean up.
|
|||||||
{%endif %}
|
{%endif %}
|
||||||
|
|
||||||
<h3>Security note</h3>
|
<h3>Security note</h3>
|
||||||
We never store passwords at all, we only store a cryptographic hash.
|
We never store passwords at all, we only store a
|
||||||
|
<a href="https://en.wikipedia.org/wiki/Cryptographic_hash_function#/media/File:Cryptographic_Hash_Function.svg">cryptographic hash</a>.
|
||||||
We do store your email address but only 'in clear' inside the live database online
|
We do store your email address but only 'in clear' inside the live database online
|
||||||
where it is accessible only to the database administrators. There is no troggle report
|
where it is accessible only to the database administrators. There is no troggle report
|
||||||
which publishes your email address.
|
which publishes your email address.
|
||||||
@@ -45,7 +47,8 @@ For permanent storage all email addresses are encrypted. Your real name and trog
|
|||||||
username is public however, and we do not have anonymous people attending expo.
|
username is public however, and we do not have anonymous people attending expo.
|
||||||
<p>
|
<p>
|
||||||
The password we {% if newuser %}will be{% else %}are{%endif %} asking for is solely for logging into troggle.
|
The password we {% if newuser %}will be{% else %}are{%endif %} asking for is solely for logging into troggle.
|
||||||
The troggle login is used to track who is editing the current and past expo data, website content, and historic survey data, as well as for accessing the expo Kanban software. It is not the same as the password you use to access your email
|
The troggle login is used to track who is editing the current and past expo data, website content, and historic survey data, as well as for accessing the expo Kanban software (in future).
|
||||||
|
It is not the same as the password you use to access your email
|
||||||
with your email provider
|
with your email provider
|
||||||
and it is not the same as the password you use to interact with the expo
|
and it is not the same as the password you use to interact with the expo
|
||||||
<a href="https://lists.wookware.org/cgi-bin/mailman/roster/expo">email list</a>.
|
<a href="https://lists.wookware.org/cgi-bin/mailman/roster/expo">email list</a>.
|
||||||
Reference in New Issue
Block a user