Compare commits
No commits in common. "master" and "uid-validation" have entirely different histories.
master
...
uid-valida
16
Dockerfile
16
Dockerfile
|
@ -1,16 +0,0 @@
|
||||||
FROM python:3.10.0-alpine3.15
|
|
||||||
|
|
||||||
WORKDIR /usr/src/app
|
|
||||||
|
|
||||||
RUN apk add --update --no-cache\
|
|
||||||
build-base \
|
|
||||||
openldap-dev\
|
|
||||||
python3-dev\
|
|
||||||
&& rm -rf /var/cache/apk/*
|
|
||||||
|
|
||||||
# FIX https://github.com/python-ldap/python-ldap/issues/432
|
|
||||||
RUN echo 'INPUT ( libldap.so )' > /usr/lib/libldap_r.so
|
|
||||||
|
|
||||||
COPY requirements.txt ./
|
|
||||||
RUN pip install --no-cache-dir -r requirements.txt
|
|
||||||
COPY ungleichuser/ .
|
|
85
README.md
85
README.md
|
@ -4,80 +4,17 @@ This service runs on [account.ungleich.ch](https://account.ungleich.ch/) and
|
||||||
allows customers manage their ungleich account (register, edit mail & password,
|
allows customers manage their ungleich account (register, edit mail & password,
|
||||||
...).
|
...).
|
||||||
|
|
||||||
## Status v2
|
Makes use of Django 2.2 LTS (not compatible with 3.0 at the moment) which is
|
||||||
|
supported until early 2022.
|
||||||
* See below ungleichuser/
|
|
||||||
* register view created
|
|
||||||
* Not solved:
|
|
||||||
* email verification *before* user is created
|
|
||||||
* Solved
|
|
||||||
* Finding highest uid, increasing by 1
|
|
||||||
|
|
||||||
## Todos for v2:
|
|
||||||
|
|
||||||
* Rewrite/create new app Django 4.0 based (ungleichuser)
|
|
||||||
* Do not use django-auth-ldap, because it uses python-ldap
|
|
||||||
* Update the template HTML to bootstrap5
|
|
||||||
* Use ldap3
|
|
||||||
* Check/verify the ldap registration
|
|
||||||
* Ensure multiple ldap servers are supported
|
|
||||||
* Cleanup this readme
|
|
||||||
* Use the following "captcha" for all forms:
|
|
||||||
* Extra form field named "random"
|
|
||||||
* Present the user the following text:
|
|
||||||
* Create Dockerfile / ensure it works in kubernetes
|
|
||||||
|
|
||||||
## Next steps
|
|
||||||
|
|
||||||
* Override/use custom form to allow captcha
|
|
||||||
|
|
||||||
## Development Setup
|
## Development Setup
|
||||||
|
|
||||||
```
|
* Clone this repository and enter top-level directory.
|
||||||
python3 -m venv venv
|
* (Optional) Setup a Python virtualenv and install dependencies via pip:
|
||||||
. ./venv/bin/activate
|
- `virtualenv .venv`
|
||||||
pip install -r requirements.txt
|
- `source .venv/bin/activate`
|
||||||
``
|
- `pip install -r requirements.txt`
|
||||||
|
- Note: you might have to install some OS dependencies (i.e. libldap2, libsasl).
|
||||||
## Functionality
|
* Configure the `dal` django app (uses the [decouple](https://pypi.org/project/python-decouple/) library underneath)
|
||||||
|
- Copy `dal/env.sample` to `dal/.env`
|
||||||
x Allow user to register in a specific subtree
|
- Populate `dal/.env`
|
||||||
x Verify that user does not exist in another subtree
|
|
||||||
x Assign an id
|
|
||||||
- Allow password reset [via Mail?]
|
|
||||||
- DB usage: for password reset?
|
|
||||||
|
|
||||||
## Parameters
|
|
||||||
|
|
||||||
Via environment variables:
|
|
||||||
|
|
||||||
* LDAPSERVERS=".." -- White space separated list of LDAP-Servers
|
|
||||||
* ADMIN_DN="" -- we use this DN to connect to LDAP
|
|
||||||
* ADMIN_PASSWORD="" -- we use this password to connect to LDAP
|
|
||||||
* SECRET_KEY
|
|
||||||
* DEBUG
|
|
||||||
* ALLOWED_HOSTS
|
|
||||||
|
|
||||||
|
|
||||||
## Views
|
|
||||||
|
|
||||||
### Register view
|
|
||||||
|
|
||||||
Form: [get]
|
|
||||||
|
|
||||||
* username
|
|
||||||
* password1
|
|
||||||
* password2
|
|
||||||
|
|
||||||
Post receiver: [post]
|
|
||||||
|
|
||||||
* E-Mail verification
|
|
||||||
|
|
||||||
E-Mail verify: [get]
|
|
||||||
|
|
||||||
* Create user
|
|
||||||
|
|
||||||
### Password reset view
|
|
||||||
|
|
||||||
* captcha
|
|
||||||
* username or email
|
|
||||||
|
|
|
@ -65,7 +65,6 @@ INSTALLED_APPS = [
|
||||||
'django.contrib.staticfiles',
|
'django.contrib.staticfiles',
|
||||||
'bootstrap3',
|
'bootstrap3',
|
||||||
'dal',
|
'dal',
|
||||||
'rest_framework'
|
|
||||||
]
|
]
|
||||||
|
|
||||||
MIDDLEWARE = [
|
MIDDLEWARE = [
|
||||||
|
@ -209,10 +208,3 @@ if config('ENABLE_DEBUG_LOG', default=False, cast=bool):
|
||||||
set_library_log_detail_level, OFF, BASIC, NETWORK, EXTENDED
|
set_library_log_detail_level, OFF, BASIC, NETWORK, EXTENDED
|
||||||
)
|
)
|
||||||
set_library_log_detail_level(BASIC)
|
set_library_log_detail_level(BASIC)
|
||||||
|
|
||||||
|
|
||||||
REST_FRAMEWORK = {
|
|
||||||
'DEFAULT_RENDERER_CLASSES': (
|
|
||||||
'rest_framework.renderers.JSONRenderer',
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
|
@ -11,13 +11,14 @@
|
||||||
<meta name="description" content="">
|
<meta name="description" content="">
|
||||||
<meta name="author" content="ungleich glarus ag">
|
<meta name="author" content="ungleich glarus ag">
|
||||||
|
|
||||||
<title> Ungleich User Service </title>
|
<title> Ungleich userservice </title>
|
||||||
|
|
||||||
|
|
||||||
<!-- Bootstrap Core CSS -->
|
<!-- Bootstrap Core CSS -->
|
||||||
<link href="{% static 'datacenterlight/css/bootstrap-3.3.7.min.css' %}" rel="stylesheet">
|
<link href="{% static 'datacenterlight/css/bootstrap-3.3.7.min.css' %}" rel="stylesheet">
|
||||||
|
|
||||||
<!-- Custom CSS -->
|
<!-- Custom CSS -->
|
||||||
|
<!--<link href=" static 'hosting/css/pricing.css' %}" rel="stylesheet">-->
|
||||||
<link href="{% static 'hosting/css/landing-page.css' %}" rel="stylesheet">
|
<link href="{% static 'hosting/css/landing-page.css' %}" rel="stylesheet">
|
||||||
|
|
||||||
<!-- Custom Fonts -->
|
<!-- Custom Fonts -->
|
||||||
|
@ -33,8 +34,19 @@
|
||||||
<script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script>
|
<script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script>
|
||||||
<![endif]-->
|
<![endif]-->
|
||||||
|
|
||||||
|
<!-- with 'hosting/img/'|add:hosting|add:'-intro-bg.png' as image_static
|
||||||
|
<style media="screen" type="text/css">
|
||||||
|
.intro-header {
|
||||||
|
background: url(" static image_static %}") no-repeat center center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
-->
|
||||||
<!-- endwith %}-->
|
<!-- endwith %}-->
|
||||||
|
|
||||||
|
<!-- Google analytics -->
|
||||||
|
<!-- include "google_analytics.html" %} -->
|
||||||
|
<!-- End Google Analytics -->
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
|
|
|
@ -35,8 +35,6 @@
|
||||||
<button type="submit" class="btn choice-btn btn-block">
|
<button type="submit" class="btn choice-btn btn-block">
|
||||||
{% trans "Change Password" %}
|
{% trans "Change Password" %}
|
||||||
</button>
|
</button>
|
||||||
<hr>
|
|
||||||
<a class="btn choice-btn btn-block" href="{% url 'index' %}" role="button">Back to Index</a><br>
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -34,13 +34,7 @@
|
||||||
{% trans "Change User Data" %}
|
{% trans "Change User Data" %}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<hr>
|
|
||||||
<div class="text-center">
|
|
||||||
<a class="btn choice-btn btn-block" href="{% url 'index' %}" role="button">Back to Index</a>
|
|
||||||
</div>
|
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,29 +0,0 @@
|
||||||
{% extends "base_short.html" %}
|
|
||||||
{% load i18n staticfiles bootstrap3 %}
|
|
||||||
|
|
||||||
{% block title %}
|
|
||||||
<title> Verify your email. </title>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<div class="auth-container">
|
|
||||||
<div class="auth-bg"></div>
|
|
||||||
<div class="auth-center">
|
|
||||||
<div class="auth-content">
|
|
||||||
<div class="auth-box">
|
|
||||||
<h1 class="section-heading allcaps">{% trans " Check your email " %}</h1>
|
|
||||||
<p class="text-center">{% trans "In order to complete the sign up process, please check your email and follow the activation instructions." %}</p>
|
|
||||||
<form action="{% url 'index' %}" method="get" class="form" novalidated>
|
|
||||||
<div class="text-center">
|
|
||||||
<button type="submit" class="btn choice-btn btn-block">
|
|
||||||
{% trans "Back to indexpage" %}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
|
@ -27,7 +27,6 @@
|
||||||
</div>
|
</div>
|
||||||
<hr>
|
<hr>
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<a class="btn choice-btn btn-block" href="{% url 'index' %}" role="button">Back to Index</a><br>
|
|
||||||
<button type="submit" class="btn choicered-btn btn-block">
|
<button type="submit" class="btn choicered-btn btn-block">
|
||||||
{% trans "Delete Account" %}
|
{% trans "Delete Account" %}
|
||||||
</button>
|
</button>
|
||||||
|
|
|
@ -1,37 +0,0 @@
|
||||||
{% extends "base_short.html" %}
|
|
||||||
{% load i18n staticfiles bootstrap3 %}
|
|
||||||
|
|
||||||
{% block title %}
|
|
||||||
<title>Options for {{user}}</title>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<div class="auth-container">
|
|
||||||
<div class="auth-bg"></div>
|
|
||||||
<div class="auth-center">
|
|
||||||
<div class="auth-content">
|
|
||||||
<div class="auth-box">
|
|
||||||
<h1 class="section-heading allcaps">{% trans "Seeds of," %} {{user}}</h1><br><br>
|
|
||||||
<table class="table table-hover text-center">
|
|
||||||
<tbody>
|
|
||||||
{% for i in seed %}
|
|
||||||
<tr>
|
|
||||||
<td>{{ i.realm }}</td>
|
|
||||||
<td>{{ i.seed }}</td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
<br>
|
|
||||||
<hr>
|
|
||||||
<form class="form" novalidate>
|
|
||||||
<div class="text-center">
|
|
||||||
<a class="btn choice-btn btn-block" href="{% url 'index' %}" role="button">Back to Index</a><br>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
|
@ -16,7 +16,6 @@
|
||||||
<form class="form">
|
<form class="form">
|
||||||
<a class="btn choice-btn btn-block" href="{% url 'change_data' %}" role="button">{% trans "Change your userdata" %}</a><br>
|
<a class="btn choice-btn btn-block" href="{% url 'change_data' %}" role="button">{% trans "Change your userdata" %}</a><br>
|
||||||
<a class="btn choice-btn btn-block" href="{% url 'change_password' %}" role="button">Change your password</a><br>
|
<a class="btn choice-btn btn-block" href="{% url 'change_password' %}" role="button">Change your password</a><br>
|
||||||
<a class="btn choice-btn btn-block" href="{% url 'user_seeds' %}" role="button">Show seeds</a><br>
|
|
||||||
<a class="btn choicered-btn btn-block" href="{% url 'logout' %}" role="button">{% trans "Logout" %}</a><br><br>
|
<a class="btn choicered-btn btn-block" href="{% url 'logout' %}" role="button">{% trans "Logout" %}</a><br><br>
|
||||||
</form>
|
</form>
|
||||||
<br>
|
<br>
|
||||||
|
|
|
@ -91,7 +91,7 @@ class LdapManager:
|
||||||
logger.debug("{uid} does not exist. Using it".format(uid=uidNumber))
|
logger.debug("{uid} does not exist. Using it".format(uid=uidNumber))
|
||||||
self._set_max_uid(uidNumber)
|
self._set_max_uid(uidNumber)
|
||||||
try:
|
try:
|
||||||
uid = user # user.encode("utf-8")
|
uid = user.encode("utf-8")
|
||||||
conn.add("uid={uid},{customer_dn}".format(
|
conn.add("uid={uid},{customer_dn}".format(
|
||||||
uid=uid, customer_dn=settings.LDAP_CUSTOMER_DN
|
uid=uid, customer_dn=settings.LDAP_CUSTOMER_DN
|
||||||
),
|
),
|
||||||
|
|
|
@ -13,25 +13,19 @@ from .views import (
|
||||||
Index,
|
Index,
|
||||||
LogOut,
|
LogOut,
|
||||||
ResetRequest,
|
ResetRequest,
|
||||||
UserCreateAPI,
|
UserCreateAPI
|
||||||
ActivateAccount,
|
|
||||||
Seeds,
|
|
||||||
SeedRetrieveCreate
|
|
||||||
)
|
)
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('register/', Register.as_view(), name="register"),
|
path('register/', Register.as_view(), name="register"),
|
||||||
path('create/', UserCreateAPI.as_view(), name="create"),
|
path('create/', UserCreateAPI.as_view(), name="create"),
|
||||||
path('changedata/', ChangeData.as_view(), name="change_data"),
|
path('changedata/', ChangeData.as_view(), name="change_data"),
|
||||||
path('seeds/', Seeds.as_view(), name="user_seeds"),
|
|
||||||
path('resetpassword/', ResetPassword.as_view(), name="reset_password"),
|
path('resetpassword/', ResetPassword.as_view(), name="reset_password"),
|
||||||
path('changepassword/', ChangePassword.as_view(), name="change_password"),
|
path('changepassword/', ChangePassword.as_view(), name="change_password"),
|
||||||
path('deleteaccount/', DeleteAccount.as_view(), name="account_delete"),
|
path('deleteaccount/', DeleteAccount.as_view(), name="account_delete"),
|
||||||
path('index/', Index.as_view(), name="index"),
|
path('index/', Index.as_view(), name="index"),
|
||||||
path('logout/', LogOut.as_view(), name="logout"),
|
path('logout/', LogOut.as_view(), name="logout"),
|
||||||
path('reset/<str:user>/<str:token>/', ResetRequest.as_view()),
|
path('reset/<str:user>/<str:token>/', ResetRequest.as_view()),
|
||||||
path('activate/<str:user>/<str:pwd>/<str:firstname>/<str:lastname>/<str:email>/<str:token>/', ActivateAccount.as_view()),
|
|
||||||
path('reset/', ResetRequest.as_view(), name="reset"),
|
path('reset/', ResetRequest.as_view(), name="reset"),
|
||||||
path('otp/', SeedRetrieveCreate.as_view(), name="seed"),
|
|
||||||
path('', Index.as_view(), name="login_index"),
|
path('', Index.as_view(), name="login_index"),
|
||||||
]
|
]
|
260
dal/views.py
260
dal/views.py
|
@ -14,8 +14,6 @@ from rest_framework.response import Response
|
||||||
from .models import ResetToken
|
from .models import ResetToken
|
||||||
from .forms import LoginForm
|
from .forms import LoginForm
|
||||||
from .ungleich_ldap import LdapManager
|
from .ungleich_ldap import LdapManager
|
||||||
from decouple import config, Csv
|
|
||||||
from pyotp import TOTP
|
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import re
|
import re
|
||||||
|
@ -29,8 +27,6 @@ from datetime import datetime
|
||||||
|
|
||||||
from random import choice, randint
|
from random import choice, randint
|
||||||
import string
|
import string
|
||||||
import requests
|
|
||||||
import json
|
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
|
@ -39,54 +35,6 @@ from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
def is_username_valid(username):
|
def is_username_valid(username):
|
||||||
return re.fullmatch(r"^[a-z|0-9|\-|_]+$", username)
|
return re.fullmatch(r"^[a-z|0-9|\-|_]+$", username)
|
||||||
|
|
||||||
admin_seed = config('ADMIN_SEED')
|
|
||||||
admin_name = config('ADMIN_NAME')
|
|
||||||
admin_realm = config('ADMIN_REALM')
|
|
||||||
user_realm = config('USER_REALM')
|
|
||||||
otp_url = config('OTPSERVER')
|
|
||||||
|
|
||||||
|
|
||||||
def activate_account_link(base_url, user, pwd, firstname, lastname, email, epochutc):
|
|
||||||
tokengen = PasswordResetTokenGenerator()
|
|
||||||
pseudouser = PseudoUser()
|
|
||||||
token = tokengen.make_token(pseudouser)
|
|
||||||
buser = bytes(user, 'utf-8')
|
|
||||||
bpwd = bytes(pwd, 'utf-8')
|
|
||||||
bfirstname = bytes(firstname, 'utf-8')
|
|
||||||
blasttname = bytes(lastname, 'utf-8')
|
|
||||||
bemail = bytes(email, 'utf-8')
|
|
||||||
userpart = b64encode(buser)
|
|
||||||
pwdpart = b64encode(bpwd)
|
|
||||||
fnpart = b64encode(bfirstname)
|
|
||||||
lnpart = b64encode(blasttname)
|
|
||||||
mailpart = b64encode(bemail)
|
|
||||||
# create entry into the database
|
|
||||||
newdbentry = ResetToken(user=user, token=token, creation=epochutc)
|
|
||||||
newdbentry.save()
|
|
||||||
# set up the link
|
|
||||||
link = "{base_url}/activate/{user}/{pwd}/{fn}/{ln}/{mail}/{token}/".format(
|
|
||||||
base_url=base_url, user=userpart.decode('utf-8'),
|
|
||||||
pwd=pwdpart.decode('utf-8'),
|
|
||||||
fn=fnpart.decode('utf-8'),
|
|
||||||
ln=lnpart.decode('utf-8'),
|
|
||||||
mail=mailpart.decode('utf-8'),
|
|
||||||
token=token
|
|
||||||
)
|
|
||||||
return link
|
|
||||||
|
|
||||||
|
|
||||||
def clean_db():
|
|
||||||
"""Revoves outdated tokens"""
|
|
||||||
# cutoff time is set to 24h hours
|
|
||||||
# using utcnow() to have no headache with timezones
|
|
||||||
cutoff = int(datetime.utcnow().timestamp()) - (24*60*60)
|
|
||||||
# Get all tokens older than 24 hours
|
|
||||||
oldtokens = ResetToken.objects.all().filter(creation__lt=cutoff)
|
|
||||||
for token in oldtokens:
|
|
||||||
# delete all tokens older than 24 hours
|
|
||||||
token.delete()
|
|
||||||
return True
|
|
||||||
|
|
||||||
class Index(FormView):
|
class Index(FormView):
|
||||||
template_name = "landing.html"
|
template_name = "landing.html"
|
||||||
form_class = LoginForm
|
form_class = LoginForm
|
||||||
|
@ -157,30 +105,34 @@ class Register(View):
|
||||||
pwd = r'%s' % password1
|
pwd = r'%s' % password1
|
||||||
|
|
||||||
try:
|
try:
|
||||||
creationtime = int(datetime.utcnow().timestamp())
|
ldap_manager = LdapManager()
|
||||||
base_url = "{0}://{1}".format(self.request.scheme,
|
ldap_manager.create_user(
|
||||||
self.request.get_host())
|
username, pwd, firstname, lastname, email
|
||||||
link = activate_account_link(base_url, username, pwd, firstname, lastname, email, creationtime)
|
|
||||||
email_from = settings.EMAIL_FROM_ADDRESS
|
|
||||||
to = ['%s <%s>' % (username, email)]
|
|
||||||
subject = 'Activate your ungleich account'.format(firstname)
|
|
||||||
body = 'You can activate your ungleich account account by clicking <a href="{link}">here</a>.' \
|
|
||||||
' You can also copy and paste the following link into the address bar of your browser and follow' \
|
|
||||||
' the link in order to activate your account.\n\n{link}'.format(link=link)
|
|
||||||
# Build the email
|
|
||||||
mail = EmailMessage(
|
|
||||||
subject=subject,
|
|
||||||
body=body,
|
|
||||||
from_email=email_from,
|
|
||||||
to=to
|
|
||||||
)
|
)
|
||||||
mail.send()
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return render(request, 'error.html', { 'urlname': urlname,
|
return render(request, 'error.html', { 'urlname': urlname,
|
||||||
'service': service,
|
'service': service,
|
||||||
'error': e } )
|
'error': e } )
|
||||||
return render(request, 'confirm_email.html')
|
# Finally, we send the send user credentials via email
|
||||||
|
creationtime = int(datetime.utcnow().timestamp())
|
||||||
|
# Construct the data for the email
|
||||||
|
email_from = settings.EMAIL_FROM_ADDRESS
|
||||||
|
to = ['%s <%s>' % (username, email)]
|
||||||
|
subject = '{}, Welcome to datacenterlight'.format(firstname)
|
||||||
|
body = 'The username {} was successfully created.\n'.format(username)
|
||||||
|
# Build the email
|
||||||
|
mail = EmailMessage(
|
||||||
|
subject=subject,
|
||||||
|
body=body,
|
||||||
|
from_email=email_from,
|
||||||
|
to=to
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
mail.send()
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
pass
|
||||||
|
return render(request, 'usercreated.html', { 'user': username } )
|
||||||
|
|
||||||
class ChangeData(LoginRequiredMixin, View):
|
class ChangeData(LoginRequiredMixin, View):
|
||||||
login_url = reverse_lazy('login_index')
|
login_url = reverse_lazy('login_index')
|
||||||
|
@ -358,7 +310,7 @@ class ResetRequest(View):
|
||||||
# Cleans up outdated tokens
|
# Cleans up outdated tokens
|
||||||
# If we expect quite a bit of old tokens, maybe somewhere else is better,
|
# If we expect quite a bit of old tokens, maybe somewhere else is better,
|
||||||
# but for now we don't really expect many unused tokens
|
# but for now we don't really expect many unused tokens
|
||||||
clean_db()
|
self.clean_db()
|
||||||
# If user and token are not supplied by django, it was called from somewhere else, so it's
|
# If user and token are not supplied by django, it was called from somewhere else, so it's
|
||||||
# invalid
|
# invalid
|
||||||
if user == None or token == None:
|
if user == None or token == None:
|
||||||
|
@ -397,10 +349,10 @@ class ResetRequest(View):
|
||||||
if password1 == "" or not password1 or password2 == "" or not password2:
|
if password1 == "" or not password1 or password2 == "" or not password2:
|
||||||
return render(request, 'error.html', { 'service': service, 'error': 'Please supply a password and confirm it.' } )
|
return render(request, 'error.html', { 'service': service, 'error': 'Please supply a password and confirm it.' } )
|
||||||
if password1 != password2:
|
if password1 != password2:
|
||||||
return render(request, 'error.html', {'service': service, 'error': 'The supplied passwords do not match.'})
|
return render(request, 'error.html', { 'service': service, 'error': 'The supplied passwords do not match.' } )
|
||||||
if len(password1) < 8:
|
if len(password1) < 8:
|
||||||
return render(request, 'error.html', {'service': service,
|
return render(request, 'error.html', { 'service': service, 'error': 'The password is too short, please use a longer one. At least 8 characters.' } )
|
||||||
'error': 'The password is too short, please use a longer one. At least 8 characters.'})
|
# everything checks out, now change the password
|
||||||
|
|
||||||
ldap_manager = LdapManager()
|
ldap_manager = LdapManager()
|
||||||
result = ldap_manager.change_password(
|
result = ldap_manager.change_password(
|
||||||
|
@ -414,7 +366,17 @@ class ResetRequest(View):
|
||||||
else:
|
else:
|
||||||
return render(request, 'error.html', { 'service': service, 'error': result } )
|
return render(request, 'error.html', { 'service': service, 'error': result } )
|
||||||
|
|
||||||
|
# Cleans up outdated tokens
|
||||||
|
def clean_db(self):
|
||||||
|
# cutoff time is set to 24h hours
|
||||||
|
# using utcnow() to have no headache with timezones
|
||||||
|
cutoff = int(datetime.utcnow().timestamp()) - (24*60*60)
|
||||||
|
# Get all tokens older than 24 hours
|
||||||
|
oldtokens = ResetToken.objects.all().filter(creation__lt=cutoff)
|
||||||
|
for token in oldtokens:
|
||||||
|
# delete all tokens older than 24 hours
|
||||||
|
token.delete()
|
||||||
|
return True
|
||||||
|
|
||||||
# The logged in user can change the password here
|
# The logged in user can change the password here
|
||||||
|
|
||||||
|
@ -539,58 +501,6 @@ class PseudoUser():
|
||||||
pk = ''.join(choice(string.ascii_letters + string.digits) for _ in range(20))
|
pk = ''.join(choice(string.ascii_letters + string.digits) for _ in range(20))
|
||||||
password = ''.join(choice(string.ascii_letters + string.digits) for _ in range(30))
|
password = ''.join(choice(string.ascii_letters + string.digits) for _ in range(30))
|
||||||
|
|
||||||
|
|
||||||
class ActivateAccount(View):
|
|
||||||
|
|
||||||
def get(self, request, user=None, pwd=None, firstname=None, lastname=None, email=None, token=None):
|
|
||||||
clean_db()
|
|
||||||
if token is None:
|
|
||||||
return HttpResponse('Invalid URL', status=404)
|
|
||||||
elem_list = [user, pwd, firstname, lastname, email]
|
|
||||||
clean_list = []
|
|
||||||
for value in elem_list:
|
|
||||||
try:
|
|
||||||
value_temp = bytes(value, 'utf-8')
|
|
||||||
value_decode = b64decode(value_temp)
|
|
||||||
value_clean = value_decode.decode('utf-8')
|
|
||||||
clean_list.append(value_clean)
|
|
||||||
except Exception as e:
|
|
||||||
return HttpResponse('Invalid URL', status=404)
|
|
||||||
checks_out = False
|
|
||||||
dbentries = ResetToken.objects.all().filter(user=clean_list[0])
|
|
||||||
for entry in dbentries:
|
|
||||||
if entry.token == token:
|
|
||||||
# found the token, now delete it since it's used
|
|
||||||
checks_out = True
|
|
||||||
entry.delete()
|
|
||||||
# No token was found
|
|
||||||
if not checks_out:
|
|
||||||
return HttpResponse('Invalid URL.', status=404)
|
|
||||||
# Token was found, create user
|
|
||||||
try:
|
|
||||||
ldap_manager = LdapManager()
|
|
||||||
ldap_manager.create_user(
|
|
||||||
clean_list[0], clean_list[1], clean_list[2], clean_list[3], clean_list[4]
|
|
||||||
)
|
|
||||||
req = requests.post(otp_url, data=json.dumps(
|
|
||||||
{
|
|
||||||
'auth_token': TOTP(admin_seed).now(),
|
|
||||||
'auth_name': admin_name,
|
|
||||||
'auth_realm': admin_realm,
|
|
||||||
'name': clean_list[0],
|
|
||||||
'realm': user_realm
|
|
||||||
}), headers={'Content-Type': 'application/json'})
|
|
||||||
if req.status_code != 201:
|
|
||||||
logger.error("User {} failed to create its otp seed".format(clean_list[0]))
|
|
||||||
|
|
||||||
#Send welcome email
|
|
||||||
except Exception as e:
|
|
||||||
return render(request, 'error.html', {'urlname': 'register',
|
|
||||||
'service': 'register an user',
|
|
||||||
'error': e})
|
|
||||||
return render(request, 'usercreated.html', { 'user': clean_list[0] } )
|
|
||||||
|
|
||||||
|
|
||||||
class UserCreateAPI(APIView):
|
class UserCreateAPI(APIView):
|
||||||
|
|
||||||
def post(self, request):
|
def post(self, request):
|
||||||
|
@ -614,24 +524,25 @@ class UserCreateAPI(APIView):
|
||||||
|
|
||||||
pwd = r'%s' % User.objects.make_random_password()
|
pwd = r'%s' % User.objects.make_random_password()
|
||||||
|
|
||||||
base_url = "{0}://{1}".format(self.request.scheme,
|
try:
|
||||||
self.request.get_host())
|
ldap_manager = LdapManager()
|
||||||
creationtime = int(datetime.utcnow().timestamp())
|
ldap_manager.create_user(
|
||||||
link = activate_account_link(base_url, username, pwd, firstname, lastname, email, creationtime)
|
username, pwd, firstname, lastname, email
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
return Response('While trying to create the user, an error was encountered: %s' % e, 400)
|
||||||
|
|
||||||
|
# send user credentials via email
|
||||||
|
creationtime = int(datetime.utcnow().timestamp())
|
||||||
# Construct the data for the email
|
# Construct the data for the email
|
||||||
email_from = settings.EMAIL_FROM_ADDRESS
|
email_from = settings.EMAIL_FROM_ADDRESS
|
||||||
to = ['%s <%s>' % (username, email)]
|
to = ['%s <%s>' % (username, email)]
|
||||||
subject = 'Ungleich account creation.'
|
subject = 'Your datacenterlight credentials'
|
||||||
body = 'A request has been sent to our servers to register you as a ungleich user.\n'
|
body = 'Your user was successfully created.\n'
|
||||||
body += 'In order to complete the registration process you must ' \
|
|
||||||
'click <a href="{link}">here</a> or copy & paste the following link into the address bar of ' \
|
|
||||||
'your browser.\n{link}\n'.format(link=link)
|
|
||||||
body += 'Your credentials are:\n'
|
body += 'Your credentials are:\n'
|
||||||
body += 'Username: %s\n\n' % username
|
body += 'Username: %s\n\n' % username
|
||||||
body += 'Password: %s\n\n' % pwd
|
body += 'Password: %s\n\n' % pwd
|
||||||
body += 'We strongly recommend after the activation to log in and change your password.\n'
|
body += 'We strongly recommend you to after log in change your password.\n'
|
||||||
body += 'This link will remain active for 24 hours.\n'
|
|
||||||
# Build the email
|
# Build the email
|
||||||
mail = EmailMessage(
|
mail = EmailMessage(
|
||||||
subject=subject,
|
subject=subject,
|
||||||
|
@ -641,71 +552,6 @@ class UserCreateAPI(APIView):
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
mail.send()
|
mail.send()
|
||||||
except Exception as e:
|
except:
|
||||||
return Response('Failed to send the email, please try again', 400)
|
return Response('User was created, but failed to send the email', 201)
|
||||||
return Response('An email with activation link has been sent in order to complete your registration. Please check your inbox.', 200)
|
return Response('User successfully created', 200)
|
||||||
|
|
||||||
|
|
||||||
class SeedRetrieveCreate(APIView):
|
|
||||||
def post(self, request):
|
|
||||||
try:
|
|
||||||
username = request.data['username']
|
|
||||||
password = request.data[r'password']
|
|
||||||
realm = request.data['realm']
|
|
||||||
print(password)
|
|
||||||
except KeyError:
|
|
||||||
return Response('You need to specify username, password, and realm values', 400)
|
|
||||||
# authenticate the user against ldap
|
|
||||||
|
|
||||||
user = authenticate(username=username, password=password)
|
|
||||||
if user is not None:
|
|
||||||
|
|
||||||
req = requests.get(otp_url, data=json.dumps(
|
|
||||||
{
|
|
||||||
'auth_token': TOTP(admin_seed).now(),
|
|
||||||
'auth_name': admin_name,
|
|
||||||
'auth_realm': admin_realm}), headers={'Content-Type': 'application/json'})
|
|
||||||
|
|
||||||
response_data = json.loads(req.text)
|
|
||||||
|
|
||||||
for elem in response_data:
|
|
||||||
if elem['name'] == username and elem['realm'] == realm:
|
|
||||||
return Response(elem, 200)
|
|
||||||
# If doesn't find a match then check if the realm is allowed and create the user
|
|
||||||
allowed_realms = config('ALLOWED_REALMS', cast=Csv())
|
|
||||||
if realm not in allowed_realms:
|
|
||||||
return Response('Not allowed to perform this action.', 403)
|
|
||||||
else:
|
|
||||||
req = requests.post(otp_url, data=json.dumps(
|
|
||||||
{
|
|
||||||
'auth_token': TOTP(admin_seed).now(),
|
|
||||||
'auth_name': admin_name,
|
|
||||||
'auth_realm': admin_realm,
|
|
||||||
'name': username,
|
|
||||||
'realm': realm
|
|
||||||
}), headers={'Content-Type': 'application/json'})
|
|
||||||
if req.status_code == 201:
|
|
||||||
msg = json.loads(req.text)
|
|
||||||
return Response(msg, 201)
|
|
||||||
else:
|
|
||||||
return Response(json.loads(req.text), req.status_code)
|
|
||||||
|
|
||||||
else:
|
|
||||||
return Response('Invalid Credentials', 400)
|
|
||||||
|
|
||||||
|
|
||||||
class Seeds(LoginRequiredMixin, View):
|
|
||||||
login_url = reverse_lazy('login_index')
|
|
||||||
def get(self, request):
|
|
||||||
seedlist = []
|
|
||||||
response = requests.get(
|
|
||||||
otp_url,
|
|
||||||
headers={'Content-Type': 'application/json'},
|
|
||||||
data=json.dumps(
|
|
||||||
{'auth_name': admin_name, 'auth_realm': admin_realm, 'auth_token': TOTP(admin_seed).now()}))
|
|
||||||
response_data = json.loads(response.text)
|
|
||||||
for i in range(len(response_data)):
|
|
||||||
if response_data[i]['name'] == request.user.username:
|
|
||||||
value = {'realm': response_data[i]['realm'], 'seed': response_data[i]['seed']}
|
|
||||||
seedlist.append(value)
|
|
||||||
return render(request, 'seed_list.html', {'seed': seedlist})
|
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
# Debian/Devuanp
|
||||||
|
apt install libldap2-dev libsasl2-dev
|
|
@ -0,0 +1,8 @@
|
||||||
|
django < 3
|
||||||
|
django-auth-ldap
|
||||||
|
python-ldap
|
||||||
|
django-bootstrap3
|
||||||
|
django-filter==2.1.0
|
||||||
|
python-decouple
|
||||||
|
ldap3
|
||||||
|
djangorestframework
|
|
@ -1,22 +0,0 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
"""Django's command-line utility for administrative tasks."""
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
"""Run administrative tasks."""
|
|
||||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'ungleichuser.settings')
|
|
||||||
try:
|
|
||||||
from django.core.management import execute_from_command_line
|
|
||||||
except ImportError as exc:
|
|
||||||
raise ImportError(
|
|
||||||
"Couldn't import Django. Are you sure it's installed and "
|
|
||||||
"available on your PYTHONPATH environment variable? Did you "
|
|
||||||
"forget to activate a virtual environment?"
|
|
||||||
) from exc
|
|
||||||
execute_from_command_line(sys.argv)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
|
@ -1,14 +0,0 @@
|
||||||
django==4.0
|
|
||||||
ldap3
|
|
||||||
|
|
||||||
django-crispy-forms
|
|
||||||
bootstrap4
|
|
||||||
|
|
||||||
#django-auth-ldap
|
|
||||||
|
|
||||||
# To check
|
|
||||||
# django-bootstrap3
|
|
||||||
# django-filter==2.1.0
|
|
||||||
# python-decouple
|
|
||||||
# djangorestframework
|
|
||||||
# requests
|
|
|
@ -1,19 +0,0 @@
|
||||||
body {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
background-color: #17a2b8;
|
|
||||||
height: 100vh;
|
|
||||||
}
|
|
||||||
#login .container #login-row #login-column #login-box {
|
|
||||||
margin-top: 120px;
|
|
||||||
max-width: 600px;
|
|
||||||
height: 320px;
|
|
||||||
border: 1px solid #9C9C9C;
|
|
||||||
background-color: #EAEAEA;
|
|
||||||
}
|
|
||||||
#login .container #login-row #login-column #login-box #login-form {
|
|
||||||
padding: 20px;
|
|
||||||
}
|
|
||||||
#login .container #login-row #login-column #login-box #login-form #register-link {
|
|
||||||
margin-top: -85px;
|
|
||||||
}
|
|
|
@ -1,3 +0,0 @@
|
||||||
{% block content %}
|
|
||||||
Your content here
|
|
||||||
{% endblock %}
|
|
|
@ -1,36 +0,0 @@
|
||||||
<link href="//maxcdn.bootstrapcdn.com/bootstrap/4.1.1/css/bootstrap.min.css" rel="stylesheet" id="bootstrap-css">
|
|
||||||
<script src="//maxcdn.bootstrapcdn.com/bootstrap/4.1.1/js/bootstrap.min.js"></script>
|
|
||||||
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
|
|
||||||
<!------ Include the above in your HEAD tag ---------->
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<div id="login">
|
|
||||||
<h3 class="text-center text-white pt-5">Login form</h3>
|
|
||||||
<div class="container">
|
|
||||||
<div id="login-row" class="row justify-content-center align-items-center">
|
|
||||||
<div id="login-column" class="col-md-6">
|
|
||||||
<div id="login-box" class="col-md-12">
|
|
||||||
<form id="login-form" class="form" action="" method="post">
|
|
||||||
<h3 class="text-center text-info">Login</h3>
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="username" class="text-info">Username:</label><br>
|
|
||||||
<input type="text" name="username" id="username" class="form-control">
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="password" class="text-info">Password:</label><br>
|
|
||||||
<input type="text" name="password" id="password" class="form-control">
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="remember-me" class="text-info"><span>Remember me</span> <span><input id="remember-me" name="remember-me" type="checkbox"></span></label><br>
|
|
||||||
<input type="submit" name="submit" class="btn btn-info btn-md" value="submit">
|
|
||||||
</div>
|
|
||||||
<div id="register-link" class="text-right">
|
|
||||||
<a href="#" class="text-info">Register here</a>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
|
@ -1,42 +0,0 @@
|
||||||
{% extends "base.html" %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
|
|
||||||
{% if form.errors %}
|
|
||||||
<p>Your username and password didn't match. Please try again.</p>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if next %}
|
|
||||||
{% if user.is_authenticated %}
|
|
||||||
<p>Your account doesn't have access to this page. To proceed,
|
|
||||||
please login with an account that has access.</p>
|
|
||||||
{% else %}
|
|
||||||
<p>Please login to see this page.</p>
|
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<form method="post" action="{% url 'login' %}">
|
|
||||||
{% csrf_token %}
|
|
||||||
<table>
|
|
||||||
<tr>
|
|
||||||
<td>{{ form.username.label_tag }}</td>
|
|
||||||
<td>{{ form.username }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>{{ form.password.label_tag }}</td>
|
|
||||||
<td>{{ form.password }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>{{ form.random.label_tag }}</td>
|
|
||||||
<td>{{ form.random }}</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<input type="submit" value="login">
|
|
||||||
<input type="hidden" name="next" value="{{ next }}">
|
|
||||||
</form>
|
|
||||||
|
|
||||||
{# Assumes you setup the password_reset view in your URLconf #}
|
|
||||||
<p><a href="{% url 'password_reset' %}">Lost password?</a></p>
|
|
||||||
|
|
||||||
{% endblock %}
|
|
|
@ -1,16 +0,0 @@
|
||||||
"""
|
|
||||||
ASGI config for ungleichuser project.
|
|
||||||
|
|
||||||
It exposes the ASGI callable as a module-level variable named ``application``.
|
|
||||||
|
|
||||||
For more information on this file, see
|
|
||||||
https://docs.djangoproject.com/en/dev/howto/deployment/asgi/
|
|
||||||
"""
|
|
||||||
|
|
||||||
import os
|
|
||||||
|
|
||||||
from django.core.asgi import get_asgi_application
|
|
||||||
|
|
||||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'ungleichuser.settings')
|
|
||||||
|
|
||||||
application = get_asgi_application()
|
|
|
@ -1,22 +0,0 @@
|
||||||
import django.contrib.auth.forms as forms
|
|
||||||
|
|
||||||
from django import forms
|
|
||||||
from django.contrib.auth.forms import UserCreationForm
|
|
||||||
from django.contrib.auth.models import User
|
|
||||||
|
|
||||||
# class UngleichAuthenticationForm(forms.AuthenticationForm):
|
|
||||||
# address = forms.CharField(max_length=45)
|
|
||||||
|
|
||||||
class NewUserForm(UserCreationForm):
|
|
||||||
email = forms.EmailField(required=True)
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = User
|
|
||||||
fields = ("username", "email", "password1", "password2")
|
|
||||||
|
|
||||||
def save(self, commit=True):
|
|
||||||
user = super(NewUserForm, self).save(commit=False)
|
|
||||||
user.email = self.cleaned_data['email']
|
|
||||||
if commit:
|
|
||||||
user.save()
|
|
||||||
return user
|
|
|
@ -1,128 +0,0 @@
|
||||||
"""
|
|
||||||
Django settings for ungleichuser project.
|
|
||||||
|
|
||||||
Generated by 'django-admin startproject' using Django 4.0rc1.
|
|
||||||
|
|
||||||
For more information on this file, see
|
|
||||||
https://docs.djangoproject.com/en/dev/topics/settings/
|
|
||||||
|
|
||||||
For the full list of settings and their values, see
|
|
||||||
https://docs.djangoproject.com/en/dev/ref/settings/
|
|
||||||
"""
|
|
||||||
|
|
||||||
import os
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
|
||||||
BASE_DIR = Path(__file__).resolve().parent.parent
|
|
||||||
|
|
||||||
|
|
||||||
# Quick-start development settings - unsuitable for production
|
|
||||||
# See https://docs.djangoproject.com/en/dev/howto/deployment/checklist/
|
|
||||||
|
|
||||||
# SECURITY WARNING: keep the secret key used in production secret!
|
|
||||||
SECRET_KEY = 'django-insecure-(9@_07zxslciu-%wnc7f$qx%=8esu!fuv(72m!6r4f(&#u8$9s'
|
|
||||||
|
|
||||||
# SECURITY WARNING: don't run with debug turned on in production!
|
|
||||||
DEBUG = True
|
|
||||||
|
|
||||||
ALLOWED_HOSTS = []
|
|
||||||
|
|
||||||
|
|
||||||
# Application definition
|
|
||||||
|
|
||||||
INSTALLED_APPS = [
|
|
||||||
'django.contrib.admin',
|
|
||||||
'django.contrib.auth',
|
|
||||||
'django.contrib.contenttypes',
|
|
||||||
'django.contrib.sessions',
|
|
||||||
'django.contrib.messages',
|
|
||||||
'django.contrib.staticfiles',
|
|
||||||
'crispy_forms',
|
|
||||||
'ungleichuser'
|
|
||||||
]
|
|
||||||
|
|
||||||
MIDDLEWARE = [
|
|
||||||
'django.middleware.security.SecurityMiddleware',
|
|
||||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
|
||||||
'django.middleware.common.CommonMiddleware',
|
|
||||||
'django.middleware.csrf.CsrfViewMiddleware',
|
|
||||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
|
||||||
'django.contrib.messages.middleware.MessageMiddleware',
|
|
||||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
|
||||||
]
|
|
||||||
|
|
||||||
ROOT_URLCONF = 'ungleichuser.urls'
|
|
||||||
|
|
||||||
TEMPLATES = [
|
|
||||||
{
|
|
||||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
|
||||||
'DIRS': [os.path.join(BASE_DIR, 'templates')],
|
|
||||||
'APP_DIRS': True,
|
|
||||||
'OPTIONS': {
|
|
||||||
'context_processors': [
|
|
||||||
'django.template.context_processors.debug',
|
|
||||||
'django.template.context_processors.request',
|
|
||||||
'django.contrib.auth.context_processors.auth',
|
|
||||||
'django.contrib.messages.context_processors.messages',
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
WSGI_APPLICATION = 'ungleichuser.wsgi.application'
|
|
||||||
|
|
||||||
|
|
||||||
# Database
|
|
||||||
# https://docs.djangoproject.com/en/dev/ref/settings/#databases
|
|
||||||
|
|
||||||
DATABASES = {
|
|
||||||
'default': {
|
|
||||||
'ENGINE': 'django.db.backends.sqlite3',
|
|
||||||
'NAME': BASE_DIR / 'db.sqlite3',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
# Password validation
|
|
||||||
# https://docs.djangoproject.com/en/dev/ref/settings/#auth-password-validators
|
|
||||||
|
|
||||||
AUTH_PASSWORD_VALIDATORS = [
|
|
||||||
{
|
|
||||||
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
# Internationalization
|
|
||||||
# https://docs.djangoproject.com/en/dev/topics/i18n/
|
|
||||||
|
|
||||||
LANGUAGE_CODE = 'en-us'
|
|
||||||
|
|
||||||
TIME_ZONE = 'UTC'
|
|
||||||
|
|
||||||
USE_I18N = True
|
|
||||||
|
|
||||||
USE_TZ = True
|
|
||||||
|
|
||||||
|
|
||||||
# Static files (CSS, JavaScript, Images)
|
|
||||||
# https://docs.djangoproject.com/en/dev/howto/static-files/
|
|
||||||
|
|
||||||
STATIC_URL = 'static/'
|
|
||||||
|
|
||||||
# Default primary key field type
|
|
||||||
# https://docs.djangoproject.com/en/dev/ref/settings/#default-auto-field
|
|
||||||
|
|
||||||
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
|
||||||
|
|
||||||
CRISPY_TEMPLATE_PACK = 'bootstrap4'
|
|
|
@ -1,16 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<!-- Required meta tags -->
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>Title</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -1,18 +0,0 @@
|
||||||
{% extends "ungleichuser/header.html" %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
|
|
||||||
{% load crispy_forms_tags %}
|
|
||||||
|
|
||||||
<!--Register-->
|
|
||||||
<div class="container py-5">
|
|
||||||
<h1>Register</h1>
|
|
||||||
<form method="POST">
|
|
||||||
{% csrf_token %}
|
|
||||||
{{ register_form|crispy }}
|
|
||||||
<button class="btn btn-primary" type="submit">Register</button>
|
|
||||||
</form>
|
|
||||||
<p class="text-center">If you already have an account, <a href="/login">login</a> instead.</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{% endblock %}
|
|
|
@ -1,26 +0,0 @@
|
||||||
"""ungleichuser URL Configuration
|
|
||||||
|
|
||||||
The `urlpatterns` list routes URLs to views. For more information please see:
|
|
||||||
https://docs.djangoproject.com/en/dev/topics/http/urls/
|
|
||||||
Examples:
|
|
||||||
Function views
|
|
||||||
1. Add an import: from my_app import views
|
|
||||||
2. Add a URL to urlpatterns: path('', views.home, name='home')
|
|
||||||
Class-based views
|
|
||||||
1. Add an import: from other_app.views import Home
|
|
||||||
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
|
|
||||||
Including another URLconf
|
|
||||||
1. Import the include() function: from django.urls import include, path
|
|
||||||
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
|
|
||||||
"""
|
|
||||||
from django.contrib import admin
|
|
||||||
import django.contrib.auth
|
|
||||||
from django.urls import path, include
|
|
||||||
from . import views
|
|
||||||
|
|
||||||
urlpatterns = [
|
|
||||||
# path('admin/', admin.site.urls),
|
|
||||||
# path("", views.homepage, name="homepage"),
|
|
||||||
path("register", views.register_request, name="register"),
|
|
||||||
path('accounts/', include('django.contrib.auth.urls')),
|
|
||||||
]
|
|
|
@ -1,9 +0,0 @@
|
||||||
import ipaddress
|
|
||||||
import random
|
|
||||||
|
|
||||||
v4_addr = ipaddress.IPv4Address(random.randint(0, 2**32))
|
|
||||||
v6_addr = ipaddress.IPv6Address(random.randint(0, 2**128))
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
addr_type = random.choice(["ipv6", "ipv4"])
|
|
||||||
if addr_type == "ipv6":
|
|
|
@ -1,21 +0,0 @@
|
||||||
from django.shortcuts import render, redirect
|
|
||||||
from .forms import NewUserForm
|
|
||||||
from django.contrib.auth import login
|
|
||||||
from django.contrib import messages
|
|
||||||
|
|
||||||
def register_request(request):
|
|
||||||
if request.method == "POST":
|
|
||||||
form = NewUserForm(request.POST)
|
|
||||||
|
|
||||||
if form.is_valid():
|
|
||||||
user = form.save()
|
|
||||||
login(request, user)
|
|
||||||
messages.success(request, "Registration successful." )
|
|
||||||
return redirect("main:homepage")
|
|
||||||
|
|
||||||
messages.error(request, "Unsuccessful registration. Invalid information.")
|
|
||||||
|
|
||||||
form = NewUserForm()
|
|
||||||
return render (request=request,
|
|
||||||
template_name="ungleichuser/register.html",
|
|
||||||
context={"register_form":form})
|
|
|
@ -1,16 +0,0 @@
|
||||||
"""
|
|
||||||
WSGI config for ungleichuser project.
|
|
||||||
|
|
||||||
It exposes the WSGI callable as a module-level variable named ``application``.
|
|
||||||
|
|
||||||
For more information on this file, see
|
|
||||||
https://docs.djangoproject.com/en/dev/howto/deployment/wsgi/
|
|
||||||
"""
|
|
||||||
|
|
||||||
import os
|
|
||||||
|
|
||||||
from django.core.wsgi import get_wsgi_application
|
|
||||||
|
|
||||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'ungleichuser.settings')
|
|
||||||
|
|
||||||
application = get_wsgi_application()
|
|
Loading…
Reference in New Issue