From b889f0fa262248a9a463d1658ac7d596fa1f6f7d Mon Sep 17 00:00:00 2001
From: Christian Dresen <c.dresen@fh-muenster.de>
Date: Wed, 6 Jan 2016 17:48:19 +0100
Subject: [PATCH] Warpauth

Finished Password-Reset Function
---
 web/warpauth/admin.py                         |  9 +++
 web/warpauth/ldap_connector.py                | 34 ++++++++++
 web/warpauth/models.py                        |  5 +-
 web/warpauth/templates/warpauth/about.html    |  9 +--
 web/warpauth/templates/warpauth/login.html    |  2 +-
 web/warpauth/templates/warpauth/profile.html  |  4 +-
 .../warpauth/profile/change_password.html     |  3 +
 .../reset_password/change_password.html       |  6 +-
 .../warpauth/reset_password/token_gen.html    |  1 +
 web/warpauth/util.py                          | 25 +------
 web/warpauth/views/admin/__init__.py          |  0
 web/warpauth/views/login.py                   |  2 +-
 web/warpauth/views/profile.py                 | 24 +++++--
 web/warpauth/views/reset_password.py          | 67 ++++++++++++-------
 14 files changed, 124 insertions(+), 67 deletions(-)
 create mode 100644 web/warpauth/ldap_connector.py
 create mode 100644 web/warpauth/views/admin/__init__.py

diff --git a/web/warpauth/admin.py b/web/warpauth/admin.py
index ec50db8..52146bc 100644
--- a/web/warpauth/admin.py
+++ b/web/warpauth/admin.py
@@ -1,4 +1,13 @@
 from django.contrib import admin
 
 # Register your models here.
+from warpauth.models import PasswordResetToken, LdapUser
 
+
+@admin.register(PasswordResetToken)
+class PasswordResetTokenAdmin(admin.ModelAdmin):
+        pass
+
+@admin.register(LdapUser)
+class LdapUserAdmin(admin.ModelAdmin):
+        pass
\ No newline at end of file
diff --git a/web/warpauth/ldap_connector.py b/web/warpauth/ldap_connector.py
new file mode 100644
index 0000000..8f3c9bb
--- /dev/null
+++ b/web/warpauth/ldap_connector.py
@@ -0,0 +1,34 @@
+import ldap
+from warpzone import settings
+
+#
+# LDAP Connector Class
+# All direct LDAP Operations must use this class
+# For LDAP User search please use LDAPDB instead
+#
+# ToDo: Escape input parameter for prevent LDAP Injection
+# ToDo: Implement Singleton Design Pattern
+#
+
+
+class LDAPConnector:
+
+    def __init__(self):
+        self.__ldap_object = ldap.initialize(settings.AUTH_LDAP_SERVER_URI)
+        self.__ldap_object.bind_s(settings.AUTH_LDAP_BIND_DN, settings.AUTH_LDAP_BIND_PASSWORD)
+
+    def get_ldap_object(self):
+        return self.__ldap_object
+
+    def change_user_password(self, user, old_pw, new_pw, reset_pw=False):
+        try:
+            if old_pw is not None or reset_pw:
+                self.__ldap_object.passwd_s(user, old_pw, new_pw)
+            return 1
+        except ldap.UNWILLING_TO_PERFORM as e:
+            error = str(e)
+            if 'unwilling to verify old password' in error or 'old password value is empty' in error:
+                return -1
+            else:
+                print (e)
+                return -2
diff --git a/web/warpauth/models.py b/web/warpauth/models.py
index ae0b9bf..5d7b8d0 100644
--- a/web/warpauth/models.py
+++ b/web/warpauth/models.py
@@ -1,9 +1,7 @@
 from django.db import models
 from ldapdb.models.fields import CharField, IntegerField, ListField
 import ldapdb.models
-from django.forms import ModelForm, HiddenInput
-from django import forms
-from django.utils import timezone
+
 
 class PasswordResetToken(models.Model):
     user = models.CharField(max_length=100)
@@ -20,7 +18,6 @@ class LdapUser(ldapdb.models.Model):
 
     uid = CharField(db_column='uid', unique=True, primary_key=True)
     email = CharField(db_column='description', max_length=200)
-    password = CharField(db_column='Password')
 
     def __str__(self):
         return self.uid
diff --git a/web/warpauth/templates/warpauth/about.html b/web/warpauth/templates/warpauth/about.html
index 23230b5..1bca999 100644
--- a/web/warpauth/templates/warpauth/about.html
+++ b/web/warpauth/templates/warpauth/about.html
@@ -3,8 +3,9 @@
 {% load i18n %}
 {% block content %}
 
-<h2>{{ body_text }}</h2>
-<p class="lead">{% trans "Welcome to Warpzone internal" %}</p><p />
-
-
+    <h2>{{ body_text }}</h2>
+    <p class="lead">{% trans "Welcome to Warpzone internal" %}</p>
+    <p>
+        Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.
+    </p>
 {% endblock %}
\ No newline at end of file
diff --git a/web/warpauth/templates/warpauth/login.html b/web/warpauth/templates/warpauth/login.html
index 4bed3cc..2c0fc07 100644
--- a/web/warpauth/templates/warpauth/login.html
+++ b/web/warpauth/templates/warpauth/login.html
@@ -7,7 +7,7 @@
         <h2 class="form-signin-heading">{% trans "Please sign in" %}</h2>
         <label for="inputUser">{% trans "Username" %}</label>
         <input name="username" type="user" id="inputUser" class="form-control" placeholder="{% trans "Username" %}" required autofocus>
-        Hallo
+
         <label for="inputPassword" style="padding-top:10px;">{% trans "Password" %}</label>
         <input name="password" type="password" id="inputPassword" class="form-control" placeholder="{% trans "Password" %}" required>
         {% if fail == True %}
diff --git a/web/warpauth/templates/warpauth/profile.html b/web/warpauth/templates/warpauth/profile.html
index ee7ed5c..b6b3988 100644
--- a/web/warpauth/templates/warpauth/profile.html
+++ b/web/warpauth/templates/warpauth/profile.html
@@ -3,8 +3,8 @@
 {% load i18n %}
 {% block content %}
 
-<h2>{{ body_text }}</h2>
-<p class="lead">{% trans "Profile Settings" %}</p><p />
+
+<p class="lead">{% trans "Profile Settings" %}</p><p></p>
 
 
 <div>
diff --git a/web/warpauth/templates/warpauth/profile/change_password.html b/web/warpauth/templates/warpauth/profile/change_password.html
index d2e48ad..6ede004 100644
--- a/web/warpauth/templates/warpauth/profile/change_password.html
+++ b/web/warpauth/templates/warpauth/profile/change_password.html
@@ -2,6 +2,9 @@
 
 <div>
     <br />
+    {% if error %}
+        <div class="alert alert-danger">{{ error }}</div><br>
+    {% endif %}
     <form class="form-horizontal" method="POST" action="/profile/change_password/" role="form">
         {% csrf_token %}
         <div class="form-group">
diff --git a/web/warpauth/templates/warpauth/reset_password/change_password.html b/web/warpauth/templates/warpauth/reset_password/change_password.html
index e1e7e1d..0ae3d12 100644
--- a/web/warpauth/templates/warpauth/reset_password/change_password.html
+++ b/web/warpauth/templates/warpauth/reset_password/change_password.html
@@ -5,7 +5,11 @@
     {% if token_error %}
         <h2 class="form-signin-heading">{% trans "The Token is invalid" %}</h2>
     {% elif request.POST and not form_error%}
-        {{ request.POST }}
+        <div class="alert alert-success">
+            {% trans "Your password was sucessfully changed. You will be redirected within 5 seconds. If not please click" %}
+            <a href="/login">{% trans "here" %}</a>
+            <meta refresh>
+        </div>
     {% else %}
         <form  class="form" style="max-width: 330px; margin: 0 auto; padding: 40px">
             {% csrf_token %}
diff --git a/web/warpauth/templates/warpauth/reset_password/token_gen.html b/web/warpauth/templates/warpauth/reset_password/token_gen.html
index 69f2b6e..98033d1 100644
--- a/web/warpauth/templates/warpauth/reset_password/token_gen.html
+++ b/web/warpauth/templates/warpauth/reset_password/token_gen.html
@@ -5,6 +5,7 @@
     {% if request.POST %}
         <h2 class="form-signin-heading">{% trans "If your information were right, you've got an Email" %}</h2>
 
+        Token is: <a href="{{ debug }}">{{ debug }}</a>
 
     {% else %}
         <form  class="form" style="max-width: 330px; margin: 0 auto; padding: 40px">
diff --git a/web/warpauth/util.py b/web/warpauth/util.py
index 24ec821..628d52a 100644
--- a/web/warpauth/util.py
+++ b/web/warpauth/util.py
@@ -1,25 +1,4 @@
-import ldap
-
-from warpzone import settings
 pages = {'pages': [
-    {"link":"pizza", "name": "PizzaSheet"},
-    {"link":"about", "name": "About"},
+    {"link": "pizza", "name": "PizzaSheet"},
+    {"link": "about", "name": "About"},
 ]}
-
-
-def __init_ldap():
-    ldapObject = ldap.initialize(settings.AUTH_LDAP_SERVER_URI)
-    ldapObject.bind_s(settings.AUTH_LDAP_BIND_DN, settings.AUTH_LDAP_BIND_PASSWORD)
-    return ldapObject
-
-
-def ldap_change_password(user,old_pw, new_pw):
-    ldapObject = __init_ldap()
-    try:
-        ldapObject.passwd_s(user,old_pw,new_pw)
-        return 1
-    except ldap.UNWILLING_TO_PERFORM as e:
-        if 'unwilling to verify old password' in e:
-            return -1
-        return 0
-
diff --git a/web/warpauth/views/admin/__init__.py b/web/warpauth/views/admin/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/web/warpauth/views/login.py b/web/warpauth/views/login.py
index 215c3ff..cffb99b 100644
--- a/web/warpauth/views/login.py
+++ b/web/warpauth/views/login.py
@@ -7,7 +7,7 @@ from django.contrib.auth.decorators import login_required
 from warpauth.util import *
 
 def login_view(request):
-    pages['body'] = "login"
+    pages['page_title'] = "Login"
     pages['body_text'] = "Login"
     pages['fail'] = False
 
diff --git a/web/warpauth/views/profile.py b/web/warpauth/views/profile.py
index 2db2d4e..f40bfef 100644
--- a/web/warpauth/views/profile.py
+++ b/web/warpauth/views/profile.py
@@ -2,26 +2,40 @@ from django.shortcuts import render
 from django.http import HttpResponse, HttpResponseBadRequest, HttpResponseNotAllowed, HttpResponseNotFound
 from django.shortcuts import redirect
 from django.contrib.auth.decorators import login_required
+from warpauth.ldap_connector import LDAPConnector
 from warpauth.models import LdapUser
 from warpauth.util import *
 
+
 ##
 # http://www.python-ldap.org/doc/html/ldap.html#ldap.LDAPObject
 ##
 
 @login_required(login_url='/login/', redirect_field_name=None)
 def index(request):
-    print(request.user.ldap_user.group_names)
+    pages["error"] = ""
+    pages["success"] = False
     pages['ldap_groups'] = request.user.ldap_user.group_names
-    #ldap_change_password(request.user.ldap_user.dn,"123456","12345")
-
     return HttpResponse(render(request, 'warpauth/profile.html', pages))
 
 
 @login_required(login_url='/login/', redirect_field_name=None)
 def change_password(request):
+    pages["error"] = ""
+    pages["success"] = False
+
     if request.method != 'POST':
         redirect("/")
-    print(request.POST)
 
-    return HttpResponse(render(request, 'warpauth/profile.html', pages))
\ No newline at end of file
+    if "old_pw" not in request.POST or "new_pw" not in request.POST or "new_pw_confirm" not in request.POST:
+        pages["error"] = "Please fill in all fields"
+    elif request.POST["new_pw"] != request.POST["new_pw_confirm"]:
+        pages["error"] = "Password confirmation did not match"
+    else:
+        ldap_connector = LDAPConnector()
+        ret = ldap_connector.change_password(request.user.ldap_user.dn, request.POST["old_pw"], request.POST["new_pw"])
+        if ret == -1:
+            pages["error"] = "Old password did not match"
+        else:
+            pages["success"] = True
+    return HttpResponse(render(request, 'warpauth/profile.html', pages))
diff --git a/web/warpauth/views/reset_password.py b/web/warpauth/views/reset_password.py
index 67fc27c..9e90388 100644
--- a/web/warpauth/views/reset_password.py
+++ b/web/warpauth/views/reset_password.py
@@ -1,17 +1,23 @@
-from django.shortcuts import render
-from django.http import HttpResponse, HttpResponseBadRequest, HttpResponseNotAllowed, HttpResponseNotFound
-from django.contrib.auth import authenticate, login, logout
-from django.shortcuts import redirect
-from django.contrib.auth.decorators import login_required
-from django.views.decorators.cache import cache_page
-
-from warpauth.util import *
-from warpauth.models import PasswordResetToken, LdapUser
+import os
 import hashlib
 import logging
-import os
 import datetime
+
 from django.core.exceptions import ObjectDoesNotExist, ValidationError
+from django.http import HttpResponse
+from django.shortcuts import render
+from warpauth.ldap_connector import LDAPConnector
+
+from warpauth.util import *
+from warpauth.models import PasswordResetToken, LdapUser
+
+from warpzone.settings import PW_RESET_TOKEN_LIFETIME
+
+#
+# Function to generate a password reset Token
+# ToDo: Implement Email with Token
+# ToDo: Remove Debug outputs
+#
 
 def gen_token(request):
     logger = logging.getLogger("reset_password")
@@ -20,44 +26,53 @@ def gen_token(request):
             usr = LdapUser.objects.get(uid=request.POST["username"])
             if usr.email == request.POST["email"]:
                 p = PasswordResetToken()
-                p.user=usr.uid
-                p.email=usr.email
+                p.user = usr.uid
+                p.email = usr.email
                 p.hash = hashlib.sha1(os.urandom(128)).hexdigest()
                 p.save()
+                pages["debug"] = "http://localhost/reset_password/%s" % p.hash
                 logger.info("Success for %s", usr.uid)
         except Exception as e:
             print(e)
-            logger.error("Failed for %s with %s", request.POST["username"],e)
+            logger.error("Failed for %s with %s", request.POST["username"], e)
     else:
         pass
     return HttpResponse(render(request, 'warpauth/reset_password/token_gen.html', pages))
 
+#
+# Function to change user password with a reset Token
+# ToDo: Implement Email after password change
+# ToDo: Implement automatic redirection if succeeded in template
+#
 
 def change_password(request, reset_hash=None):
-    logger = logging.getLogger("reset_password")
-# Debug
+    # logger = logging.getLogger("reset_password")
+    # Debug
     for pw in PasswordResetToken.objects.all():
-        print (pw.hash)
-#
+        print(pw.hash)
+    #
     try:
-        passwordHash = PasswordResetToken.objects.get(hash=reset_hash)
-        timediff = datetime.datetime.now() - passwordHash.created
-        if timediff.seconds/60 > 5:
-        #    passwordHash.delete()
+        pw_reset_token = PasswordResetToken.objects.get(hash=reset_hash)
+        time_difference = datetime.datetime.now() - pw_reset_token.created
+        if time_difference.seconds/60 > PW_RESET_TOKEN_LIFETIME:
+            pw_reset_token.delete()
             raise ValidationError("Token not valid")
 
         if request.POST:
-            if request.POST["password"] != request.POST["password2"]:
+            if request.POST["password"] != request.POST["password2"] or request.POST["password"] == "":
                 pages["form_error"] = True
             else:
-                pass
+                user = LdapUser.objects.get(uid=pw_reset_token.user)
+                ldap_connector = LDAPConnector()
+                ldap_connector.change_user_password(user.build_dn(), None, request.POST["password"], True)
+                pw_reset_token.delete()
         else:
-            pages["username"] = passwordHash.user
+            pages["username"] = pw_reset_token.user
+
     except (ObjectDoesNotExist, ValidationError) as e:
+        print(e)
         pages["token_error"] = True
     except Exception as e:
         print(e)
 
-
-
     return HttpResponse(render(request, 'warpauth/reset_password/change_password.html', pages))
-- 
GitLab