mirror of
https://expo.survex.com/repositories/troggle/.git
synced 2025-12-16 23:07:12 +00:00
151 lines
5.8 KiB
Python
151 lines
5.8 KiB
Python
import base64
|
|
import json
|
|
import os
|
|
from cryptography.fernet import Fernet
|
|
from pathlib import Path
|
|
|
|
from django.conf import settings
|
|
from django.contrib.auth.models import User
|
|
from django.db import models
|
|
|
|
from troggle.core.models.troggle import DataIssue, Person, PersonExpedition
|
|
from troggle.core.utils import current_expo
|
|
|
|
"""This imports the registered troggle users, who are nearly-all, but not quite, Persons.
|
|
exceptions are "expo" and "expoadmin" which are created by the databaseReset.py import program.
|
|
|
|
This can import unencrypted email addresses but never exports them.
|
|
|
|
Passwords are only ever stored as hashes using the standard Django functions.
|
|
"""
|
|
|
|
todo = """
|
|
- Need to check/register with lists.wookware.org for email
|
|
"""
|
|
|
|
SUPER_USERS = ["philip-sargent", "wookey"] # list of userids who get the same rights as "expoadmin" i.e. the Django control panel
|
|
|
|
USERS_FILE = "users.json"
|
|
ENCRYPTED_DIR = "encrypted"
|
|
|
|
def how_many_previous_expos(person):
|
|
return PersonExpedition.objects.filter(person=person).exclude(expedition__year=current_expo()).count()
|
|
|
|
|
|
def register_user(u, email, password=None, pwhash=None, fullname=""):
|
|
"""Create User and we may not have a Person to tie it to if it is a future caver.
|
|
Do not use the lastname field, put the whole free text identification into firstname
|
|
as this saves hassle and works with Wookey too
|
|
"""
|
|
try:
|
|
if existing_user := User.objects.filter(username=u): # WALRUS
|
|
# print(f" - deleting existing user '{existing_user[0]}' before importing")
|
|
existing_user[0].delete()
|
|
user = User.objects.create_user(u, email, first_name=fullname)
|
|
if pwhash:
|
|
user.password = pwhash
|
|
elif password:
|
|
user.set_password(password) # function creates hash and stores hash
|
|
print(f" # hashing provided clear-text password {password} to make pwhash for user {u}")
|
|
else:
|
|
# user.set_password(None) # use Django special setting for invalid password, but then FAILS to send password reset email
|
|
user.set_password("secret") # Why is the Django logic broken. Hmph.
|
|
print(f" # setting INVALID password for user {u}, must be reset by password_reset")
|
|
if u in SUPER_USERS:
|
|
user.is_staff = True
|
|
user.is_superuser = True
|
|
print(f"** {u} is SUPER and can access everything on the Django control panel")
|
|
else:
|
|
user.is_staff = False
|
|
user.is_superuser = False
|
|
user.save()
|
|
except Exception as e:
|
|
print(f"Exception <{e}>")
|
|
print(f"{len(User.objects.all())} users now in db:\n{User.objects.all()}")
|
|
raise
|
|
|
|
expoers = Person.objects.filter(slug=u)
|
|
if len(expoers) == 1:
|
|
person = expoers[0]
|
|
person.user = user
|
|
person.save()
|
|
return user
|
|
|
|
def get_encryptor():
|
|
key = settings.LONGTERM_SECRET_KEY # Django generated
|
|
k = base64.urlsafe_b64encode(key.encode("utf8")[:32]) # make Fernet compatible
|
|
f = Fernet(k)
|
|
return f
|
|
|
|
def load_users():
|
|
"""These are the previously registered users of the troggle system.
|
|
It loads then from JSON permanent storage into the database.
|
|
It does allow unencrypted emails in the import, if labelled as such, but only
|
|
stores encrypted emails.
|
|
It refreshes the JSON every time this functionis called, to allow
|
|
for password rotation in the future.
|
|
"""
|
|
PARSER_USERS = "_users"
|
|
DataIssue.objects.filter(parser=PARSER_USERS).delete()
|
|
|
|
f = get_encryptor()
|
|
|
|
jsonfile = settings.EXPOWEB / ENCRYPTED_DIR / USERS_FILE
|
|
jsonurl = "/" + str(Path(ENCRYPTED_DIR) / USERS_FILE)
|
|
if not (jsonfile.is_file()):
|
|
message = f" ! Users json file does not exist: '{jsonfile}'"
|
|
DataIssue.objects.create(parser=PARSER_USERS, message=message)
|
|
print(message)
|
|
return None
|
|
|
|
with open(jsonfile, 'r', encoding='utf-8') as json_f:
|
|
message = ""
|
|
try:
|
|
registered_users_dict = json.load(json_f)
|
|
except FileNotFoundError:
|
|
message = f"File {jsonfile} not found!"
|
|
except json.JSONDecodeError:
|
|
message = f"Invalid JSON format! - JSONDecodeError for {jsonfile}"
|
|
except Exception as e:
|
|
message = f"! Troggle USERs. Failed to load {jsonfile} JSON file. Exception <{e}>"
|
|
if message:
|
|
print(message)
|
|
DataIssue.objects.update_or_create(parser=PARSER_USERS, message=message, url=jsonurl)
|
|
return None
|
|
users_list = registered_users_dict["registered_users"]
|
|
|
|
print(f" - {len(users_list)} users read from JSON")
|
|
print(f"-- Registering users in database")
|
|
for userdata in users_list:
|
|
if not userdata["username"]:
|
|
message = f"! user: BAD username for {userdata} in {jsonfile}"
|
|
print(message)
|
|
DataIssue.objects.update_or_create(parser=PARSER_USERS, message=message, url=jsonurl)
|
|
continue
|
|
else:
|
|
if userdata["username"] in [ "expo", "expoadmin" ]:
|
|
continue
|
|
if "encrypted" not in userdata:
|
|
userdata["encrypted"] = True
|
|
try:
|
|
u = userdata["username"]
|
|
email = userdata["email"]
|
|
if userdata["encrypted"]:
|
|
email = f.decrypt(email).decode()
|
|
print(f" - user: '{u} <{email}>' (decrypted)")
|
|
except Exception as e:
|
|
print(f"Exception <{e}>\n")
|
|
formatted_json = json.dumps(userdata, indent=4)
|
|
print(formatted_json)
|
|
raise
|
|
return None
|
|
if "pwhash" in userdata:
|
|
pwhash = userdata["pwhash"]
|
|
new_user = register_user(u, email, pwhash=pwhash)
|
|
else:
|
|
new_user = register_user(u, email)
|
|
# save_users() no need on initial parsing
|
|
|
|
|
|
|