forked from ungleich-public/ungleich-user
The activation link carried base64-encoded username, password, names
and email, so plaintext passwords ended up in mail archives, browser
history and proxy logs.
Registration now hashes the password to {SSHA} immediately and stores
it in a new PendingRegistration table (migration 0002). The emailed
link is /activate/<token>/ with a random token only, and activation
writes the stored hash into LDAP. Pending entries expire after 24h,
like reset tokens. This also drops the PseudoUser/token-generator
workaround.
Links in emails sent before deployment stop working; those users must
register again.
48 lines
1.7 KiB
Python
48 lines
1.7 KiB
Python
from django.db import models
|
|
from django.contrib.auth.hashers import make_password
|
|
from django.contrib.auth.models import User
|
|
|
|
# Basic DB to correlate tokens, users and creation time
|
|
|
|
class ResetToken(models.Model):
|
|
|
|
# users wouldn't use usernames >100 chars
|
|
user = models.CharField(max_length=100)
|
|
# Not so sure about tokens, better make it big
|
|
# should be <100, but big usernames make bigger tokens
|
|
# if I read that correctly
|
|
token = models.CharField(max_length=255)
|
|
# creation time in epoch (UTC)
|
|
# BigInt just so we are save for the next few decades ;)
|
|
creation = models.BigIntegerField()
|
|
|
|
|
|
class PendingRegistration(models.Model):
|
|
"""A sign-up waiting for its email to be confirmed.
|
|
|
|
Only the {SSHA} hash of the password is kept, and the activation link
|
|
carries nothing but the random token.
|
|
"""
|
|
token = models.CharField(max_length=64, unique=True)
|
|
username = models.CharField(max_length=100)
|
|
email = models.EmailField(max_length=254)
|
|
firstname = models.CharField(max_length=255)
|
|
lastname = models.CharField(max_length=255)
|
|
password_hash = models.CharField(max_length=255)
|
|
# creation time in epoch (UTC), like ResetToken
|
|
creation = models.BigIntegerField()
|
|
|
|
|
|
def get_validation_slug():
|
|
return make_password(None)
|
|
|
|
|
|
class UserAccountValidationDetail(models.Model):
|
|
user = models.OneToOneField(User, on_delete=models.CASCADE)
|
|
VALIDATED_CHOICES = ((0, 'Not validated'), (1, 'Validated'))
|
|
validated = models.IntegerField(choices=VALIDATED_CHOICES, default=0)
|
|
validation_slug = models.CharField(
|
|
db_index=True, unique=True, max_length=50,
|
|
default=get_validation_slug
|
|
)
|
|
date_validation_started = models.DateTimeField(auto_now_add=True)
|