reset function added, some minor debugging/fixes

This commit is contained in:
downhill 2018-10-16 20:25:50 +02:00
parent 2eea482d80
commit dfd537177e
6 changed files with 57 additions and 36 deletions

View File

@ -10,5 +10,6 @@ class ResetToken(models.Model):
# should be <100, but big usernames make bigger tokens
# if I read that correctly
token = models.CharField(max_length=255)
# Just so we are save for the next few decades ;)
# creation time in epoch (UTC)
# BigInt just so we are save for the next few decades ;)
creation = models.BigIntegerField()

View File

@ -99,6 +99,7 @@ MIDDLEWARE = [
AUTHENTICATION_BACKENDS = (
'django_auth_ldap.backend.LDAPBackend',
# we only use LDAP for this service, so no auth against the standard DB
# 'django.contrib.auth.backends.ModelBackend',
)

View File

@ -2,9 +2,8 @@
<h2> Password reset </h2>
<br><br>
To reset your password, please enter your username below. You will get an email asking you to confirm this and after confirmation an email with your
temporary password. Please remember to change it immediately after logging in.
<br>
To reset your password, please enter your username below. You will get an email with a link to change your password.
<br><br>
<form action={% url 'reset_password' %} method="post">
{% csrf_token %}
Username:<br>

View File

@ -1,27 +1,12 @@
"""dal URL Configuration
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/1.10/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: url(r'^$', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.conf.urls import url, include
2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls'))
"""
# The different URLs this service does use
from django.urls import path
from django.conf.urls import url
from django.contrib import admin
# Import the classes for the views
from .views import Register, ChangeData, ChangePassword, ResetPassword, DeleteAccount, Index, LogOut, ResetRequest
urlpatterns = [
# path('admin/', admin.site.urls),
path('register/', Register.as_view(), name="register"),
path('changedata/', ChangeData.as_view(), name="change_data"),
path('resetpassword/', ResetPassword.as_view(), name="reset_password"),

View File

@ -1,3 +1,4 @@
# Imports from django
from django.shortcuts import render
from django.views.generic import View
from django.contrib.auth import authenticate, login, logout
@ -5,11 +6,17 @@ from django.contrib.auth.models import User
from django.http import HttpResponse, HttpResponseRedirect
from django.core.validators import validate_email, ValidationError
from django.urls import reverse_lazy
from django_nameko import get_pool
from django.contrib.auth.tokens import PasswordResetTokenGenerator
from django.core.mail import EmailMessage
from .models import ResetToken
# Imports for the extra stuff not in django
# django_nameko is an extra module, so gets put in here
from base64 import b64encode, b64decode
from datetime import datetime
from .models import ResetToken
from django_nameko import get_pool
from random import choice, randint
import string
# Check to see if the username is already taken
# Helper function, not to be set up as a view
@ -18,6 +25,24 @@ def check_user_exists(username):
with get_pool().next() as rpc:
return rpc.userlookup.lookup(username)
# To trick the tokengenerator to work with us, because we don't really
# have the expected user Class since we are reading the user from a form
# We store the tokens and don't have to use the check function,
# some one time data works fine.
class LastLogin():
def replace(self, microsecond=0, tzinfo=None):
return randint(1,100000)
class PseudoUser():
# easiest way to handle the check for lastlogin
last_login = LastLogin()
# random alphanumeric strings for primary key and password, just used for token generation
pk = ''.join(choice(string.ascii_letters + string.digits) for _ in range(20))
password = ''.join(choice(string.ascii_letters + string.digits) for _ in range(30))
# The index page
# If there's a session open, it will give the user the options he/she/it can do, if not,
# it will show a landing page explaining what this is and prompt them to login
@ -127,6 +152,7 @@ class ChangeData(View):
service = 'change user data'
urlname = 'change_data'
# Only logged in users may change data
if not request.user.is_authenticated:
return render(request, 'mustbeloggedin.html')
@ -194,26 +220,38 @@ class ResetPassword(View):
def email(self, user, email):
# getting epoch for the time now in UTC to spare us headache with timezones
creationtime = int(datetime.utcnow().timestamp())
#TODO figure out how to send email
email_from = 'Userservice at ungleich <userservice@ungleich.ch>'
to = '%s <%s>' % (user, email)
# Construct the data for the email
email_from = 'Userservice at ungleich <root@localhost>'
to = ['%s <%s>' % (user, email)]
subject = 'Password reset request for %s' % user
noreply = True
link = self.build_reset_link(user, creationtime)
body = 'This is an automated email which was triggered by a reset request for the user %s.\n' % user
body = 'This is an automated email which was triggered by a reset request for the user %s. Please do not reply to this email.\n' % user
body += 'If you received this email in error, please disregard it. If you get multiple emails like this, please contact us to look into potential abuse.\n'
body += 'To reset your password, please follow the link below:\n'
body += '%s\n\n' % link
body += 'The link will remain active for 24 hours.\n'
# For debug
return link
# Build the email
mail = EmailMessage(
subject=subject,
body=body,
from_email=email_from,
to=to
)
try:
mail.send()
result = True
except:
result = "An error occurred while trying to send the mail."
return result
# Builds the reset link for the email and puts the token into the database
def build_reset_link(self, user, epochutc):
# set up the data
host = 'localhost:8000'
tokengen = PasswordResetTokenGenerator()
token = tokengen.make_token(user)
# create some noise for use in the tokengenerator
pseudouser = PseudoUser()
token = tokengen.make_token(pseudouser)
buser = bytes(user, 'utf-8')
userpart = b64encode(buser)
# create entry into the database

View File

@ -46,12 +46,10 @@ def user_or_customer(uid):
conn = Connection(server)
conn.bind()
search_customers = conn.search('ou=customers,dc=ungleich,dc=ch', '(%s)' % uid)
# if conn.search('ou=customers,dc=ungleich,dc=ch', '(%s)' % uid):
if search_customers:
conn.unbind()
return '%s,ou=customers,dc=ungleich,dc=ch' % uid
search_users = conn.search('ou=customers,dc=ungleich,dc=ch', '(%s)' % uid)
# elif conn.search('ou=customers,dc=ungleich,dc=ch', '(%s)' % uid):
if search_users:
conn.unbind()
return '%s,ou=customers,dc=ungleich,dc=ch' % uid
@ -72,11 +70,10 @@ class UserLookUp(object):
conn = Connection(server)
conn.bind()
# Strange result. It keeps complaining LDAP_UID not set if I try to directly
# substitute x and y to the if, see comment above the if x or y:
# substitute x and y to the if
# Searches for user in ou=customers and ou=users
x = conn.search('ou=customers,dc=ungleich,dc=ch', '(%s)' % LDAP_UID)
y = conn.search('ou=users,dc=ungleich,dc=ch', '(%s)' % LDAP_UID)
# Search ou=users and ou=customers
#if conn.search('ou=customers,dc=ungleich,dc=ch', '(%s)' % LDAP_UID) or conn.search('ou=users,dc=ungleich,dc=ch', '(%s)' % LPAD_UID):
if x or y:
# return conn.entries[0] for first search result since we can assume uid is unique
self.dispatch('ldap', '%s [Info: UserLookUp] Searched for %s and found it\n' % (datetime.now(), LDAP_UID) )