diff --git a/.env.sample b/.env.sample index bea17eb..8996b35 100644 --- a/.env.sample +++ b/.env.sample @@ -1,2 +1,6 @@ DEBUG=True ALLOWED_HOSTS=.localhost, .ipv6.work +AUTH_LDAP_SERVER_URI=ldap:// +AUTH_LDAP_BIND_DN=cn=admin,dc=example,dc=com +AUTH_LDAP_BIND_PASSWORD=admin +AUTH_LDAP_USER_DN_TEMPLATE=uid=%(user)s,ou=users,dc=example,dc=com \ No newline at end of file diff --git a/README.md b/README.md index 13e097f..e3c4d00 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,13 @@ +## Notes + +django-auth-ldap requires `openldap-devel` + + +## Deployment + docker build -t ipv6dotwork . sudo docker rm -f ipv6dotwork -sudo docker run -d -p 127.0.0.1:8001:8000 --env-file .env --name ipv6dotwork ipv6dotwork \ No newline at end of file +sudo docker run -d -p 127.0.0.1:8001:8000 --env-file .env --name ipv6dotwork ipv6dotwork + diff --git a/ipv6work/settings.py b/ipv6work/settings.py index f5e16ca..a007bb8 100644 --- a/ipv6work/settings.py +++ b/ipv6work/settings.py @@ -56,6 +56,7 @@ INSTALLED_APPS += [ # Our apps INSTALLED_APPS += [ 'jobs', + 'users', ] MIDDLEWARE = [ @@ -120,7 +121,7 @@ AUTH_PASSWORD_VALIDATORS = [ AUTHENTICATION_BACKENDS = ( 'rules.permissions.ObjectPermissionBackend', - 'django.contrib.auth.backends.ModelBackend', + 'django_auth_ldap.backend.LDAPBackend', ) LOGIN_REDIRECT_URL = '/' @@ -153,3 +154,30 @@ MEDIA_ROOT = os.path.join(BASE_DIR, 'mediafiles/') MEDIA_URL = '/media/' CRISPY_TEMPLATE_PACK = 'bootstrap4' + + +AUTH_LDAP_SERVER_URI = config('AUTH_LDAP_SERVER_URI') +AUTH_LDAP_BIND_DN = config('AUTH_LDAP_BIND_DN') +AUTH_LDAP_BIND_PASSWORD = config('AUTH_LDAP_BIND_PASSWORD') +AUTH_LDAP_USER_DN_TEMPLATE = config('AUTH_LDAP_USER_DN_TEMPLATE') +AUTH_LDAP_USER_ATTR_MAP = { + 'first_name': 'givenName', + 'last_name': 'sn', + 'email': 'mail', +} +AUTH_LDAP_ALWAYS_UPDATE_USER = True +LOGGING = { + 'disable_existing_loggers': False, + 'version': 1, + 'handlers': { + 'console': { + 'class': 'logging.StreamHandler', + }, + }, + 'loggers': { + 'django_auth_ldap': { + 'level': 'DEBUG', + 'handlers': ['console'], + }, + }, +} diff --git a/ipv6work/urls.py b/ipv6work/urls.py index 159604e..2b75576 100644 --- a/ipv6work/urls.py +++ b/ipv6work/urls.py @@ -18,6 +18,8 @@ from django.contrib import admin from django.urls import path, include, re_path from django.contrib.auth import views as auth_views +from users.views import signup + urlpatterns = [ re_path( 'login/', @@ -27,6 +29,7 @@ urlpatterns = [ 'logout/', auth_views.LogoutView.as_view(), name='logout'), + path('signup/', signup), path('admin/', admin.site.urls), path('', include('jobs.urls')) ] diff --git a/requirements.txt b/requirements.txt index f130312..c4aa70f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,3 +3,4 @@ django-crispy-forms==1.7.2 git+https://github.com/yourlabs/django-autocomplete-light.git#egg=django-autocomplete-light rules==2.0 python-decouple==3.1 +ldap3==2.5.1 diff --git a/users/__init__.py b/users/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/users/admin.py b/users/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/users/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/users/apps.py b/users/apps.py new file mode 100644 index 0000000..4ce1fab --- /dev/null +++ b/users/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class UsersConfig(AppConfig): + name = 'users' diff --git a/users/forms.py b/users/forms.py new file mode 100644 index 0000000..7a4b374 --- /dev/null +++ b/users/forms.py @@ -0,0 +1,19 @@ +from django import forms +from django.contrib.auth.forms import UserCreationForm +from django.contrib.auth import get_user_model + +User = get_user_model() + + +class SignUpForm(UserCreationForm): + first_name = forms.CharField( + max_length=30, required=False, help_text='Optional.') + last_name = forms.CharField( + max_length=30, required=False, help_text='Optional.') + email = forms.EmailField( + max_length=254, help_text='Required. Inform a valid email address.') + + class Meta: + model = User + fields = ('username', 'first_name', 'last_name', + 'email', 'password1', 'password2', ) diff --git a/users/ldap_funcs.py b/users/ldap_funcs.py new file mode 100644 index 0000000..7a9ea52 --- /dev/null +++ b/users/ldap_funcs.py @@ -0,0 +1,28 @@ +from django.conf import settings +from ldap3 import Server, ServerPool, Connection, ObjectDef, AttrDef, Reader, Writer + +server = Server(settings.AUTH_LDAP_SERVER_URI) + + + +def create_user(user, password, firstname, lastname, email): + conn = Connection(server, settings.AUTH_LDAP_BIND_DN, + settings.AUTH_LDAP_BIND_PASSWORD) + if not conn.bind(): + raise Exception('Could not connect to LDAP Server') + obj_new_user = ObjectDef( + ['inetOrgPerson'], conn) + w = Writer(conn, obj_new_user) + dn = 'uid=%s,ou=users,dc=example,dc=com' % user + w.new(dn) + w[0].givenName = firstname + w[0].sn = lastname + w[0].cn = firstname + " " + lastname + w[0].mail = email + w[0].userPassword = password + + if not w.commit(): + conn.unbind() + raise Exception("Couldn't write user") + conn.unbind() + return True \ No newline at end of file diff --git a/users/migrations/__init__.py b/users/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/users/models.py b/users/models.py new file mode 100644 index 0000000..71a8362 --- /dev/null +++ b/users/models.py @@ -0,0 +1,3 @@ +from django.db import models + +# Create your models here. diff --git a/users/templates/users/signup.html b/users/templates/users/signup.html new file mode 100644 index 0000000..0663021 --- /dev/null +++ b/users/templates/users/signup.html @@ -0,0 +1,21 @@ +{% extends 'base.html' %} + +{% block body_content %} +

Sign up

+
+ {% csrf_token %} + {% for field in form %} +

+ {{ field.label_tag }}
+ {{ field }} + {% if field.help_text %} + {{ field.help_text }} + {% endif %} + {% for error in field.errors %} +

{{ error }}

+ {% endfor %} +

+ {% endfor %} + +
+{% endblock %} \ No newline at end of file diff --git a/users/tests.py b/users/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/users/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/users/views.py b/users/views.py new file mode 100644 index 0000000..9671505 --- /dev/null +++ b/users/views.py @@ -0,0 +1,26 @@ +from django.conf import settings +from django.contrib.auth import login, authenticate +from django.http import HttpResponseRedirect +from django.shortcuts import render + +from .forms import SignUpForm +from .ldap_funcs import create_user + + +def signup(request): + if request.method == 'POST': + form = SignUpForm(request.POST) + if form.is_valid(): + username = form.cleaned_data.get('username') + raw_password = form.cleaned_data.get('password1') + first_name = form.cleaned_data.get('first_name') + last_name = form.cleaned_data.get('last_name') + email = form.cleaned_data.get('email') + create_user(username, raw_password, first_name, last_name, email) + form.save() + user = authenticate(username=username, password=raw_password) + login(request, user, backend='django_auth_ldap.backend.LDAPBackend') + return HttpResponseRedirect(settings.LOGIN_REDIRECT_URL) + else: + form = SignUpForm() + return render(request, 'users/signup.html', {'form': form})