diff --git a/web/warpauth/admin.py b/web/warpauth/admin.py index ec50db8d804c505b73770de751daa99262235fbd..52146bcf70acf8edccd27d683178ec3531ca0762 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 0000000000000000000000000000000000000000..8f3c9bb53144e9121f32dccf0a8bde9a7b5a24db --- /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 ae0b9bffb20196c6602f77ee97fe1ea9bb1c5954..5d7b8d0e5004c9c436c9b09dbf1e40706fc56bd2 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 23230b583cc6cb9c1f249386e74141fbfebd4eb3..1bca9990050f399c7d712be68f8e10ca7c6bea57 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 4bed3ccf348b4e36b93ef42bfa301b908eb9e425..2c0fc07e2d6922bc5bc50d09a37eaab3b3fc3926 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 ee7ed5ca7cc11b510e4f3388898310c08ff02384..b6b39885cb59c454e722814db82f70c29eec9dd2 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 d2e48ad24bc925a7346687f2505c4a6e4f8fb6e6..6ede004e1177ac09c8148e5b47ab8307acf7a09b 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 e1e7e1d8cb0d3f79887f48a67a9ddbeeb07af8e0..0ae3d12dc1da628a52079237541e01902df79513 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 69f2b6ebdcf6382889d5a5beca4d700220ba4bdd..98033d13993f78fd26c0aaedb5ebc86c2b8fc940 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 24ec821b0a5a4f31e30ec4680b78ccc7b9e54b62..628d52a40f6a0f5f37bb87080deb0e440507c79c 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 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/web/warpauth/views/login.py b/web/warpauth/views/login.py index 215c3ff68c0c596c99fc8713b941a2491051a2e6..cffb99b3c9c169f3189ea40c5c2296b98fb6d7b6 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 2db2d4ea44a9cbda8c6105e660e00574ba4a0f50..f40bfef2502538d126beb42b463274dd6d8bdc03 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 67fc27c1dc1fa028f1f07757734e712bc1ad26ff..9e903884435921677adfac3171c6c78f2edfa7d3 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))