Merge branch 'william' into 'master'
William See merge request downhill/vuejs-userservice!2
42
.gitignore
vendored
Normal file
|
@ -0,0 +1,42 @@
|
|||
*.log
|
||||
db.sqlite3
|
||||
*.pyc
|
||||
*.DS_Store
|
||||
build/
|
||||
*.egg_info
|
||||
#editors && utilites.
|
||||
*.swp
|
||||
*~
|
||||
__pycache__/
|
||||
.ropeproject/
|
||||
#django
|
||||
local_settings.py
|
||||
|
||||
!.keep
|
||||
media/
|
||||
!media/keep
|
||||
/CACHE/
|
||||
/static/
|
||||
|
||||
\#*#
|
||||
.\#*
|
||||
*~
|
||||
|
||||
secret-key
|
||||
|
||||
node_modules/
|
||||
*.db
|
||||
ungleich.db
|
||||
*~*
|
||||
|
||||
secret-key
|
||||
|
||||
*.psd
|
||||
|
||||
.idea/
|
||||
|
||||
.env
|
||||
*.mo
|
||||
|
||||
venv/
|
||||
dal/ldap_max_uid_file
|
|
@ -1,15 +0,0 @@
|
|||
from django.db import models
|
||||
|
||||
# Basic DB to correlate tokens, users and creation time
|
||||
|
||||
class ResetToken(models.Model):
|
||||
|
||||
# users wouldn't use usernames >100 chars
|
||||
user = models.CharField(max_length=100)
|
||||
# Not so sure about tokens, better make it big
|
||||
# should be <100, but big usernames make bigger tokens
|
||||
# if I read that correctly
|
||||
token = models.CharField(max_length=255)
|
||||
# creation time in epoch (UTC)
|
||||
# BigInt just so we are save for the next few decades ;)
|
||||
creation = models.BigIntegerField()
|
|
@ -1,254 +0,0 @@
|
|||
"""
|
||||
Django settings for dal project.
|
||||
|
||||
Generated by 'django-admin startproject' using Django 1.10.7.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/1.10/topics/settings/
|
||||
|
||||
For the full list of settings and their values, see
|
||||
https://docs.djangoproject.com/en/1.10/ref/settings/
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
import ldap
|
||||
|
||||
from django_auth_ldap.config import LDAPSearch, LDAPSearchUnion
|
||||
|
||||
from configparser import ConfigParser
|
||||
|
||||
config = ConfigParser()
|
||||
config.read('userservice.conf')
|
||||
|
||||
# LDAP config
|
||||
|
||||
AUTH_LDAP_SERVER_URI = config['LDAP']['LDAPSERVER']
|
||||
# The search user
|
||||
AUTH_LDAP_BIND_DN = config['LDAP']['SEARCHUSER']
|
||||
# The password for the search user
|
||||
AUTH_LDAP_BIND_PASSWORD = config.get('LDAP','SEARCHUSERPASSWORD', raw=True)
|
||||
# Search union over two ou
|
||||
AUTH_LDAP_USER_SEARCH = LDAPSearchUnion(
|
||||
LDAPSearch("ou=users,dc=ungleich,dc=ch", ldap.SCOPE_SUBTREE, "(uid=%(user)s)"),
|
||||
LDAPSearch("ou=customers,dc=ungleich,dc=ch", ldap.SCOPE_SUBTREE, "(uid=%(user)s)"),
|
||||
)
|
||||
|
||||
# Basic User
|
||||
#AUTH_LDAP_USER_DN_TEMPLATE = "uid=%(user)s,ou=users,dc=ungleich,dc=ch"
|
||||
|
||||
# Search over just one ou
|
||||
#AUTH_LDAP_USER_SEARCH = LDAPSearch( LDAPSearch("ou=users,dc=ungleich,dc=ch",
|
||||
# ldap.SCOPE_SUBTREE, "(uid=%(user)s)")
|
||||
# )
|
||||
|
||||
|
||||
# Maps some user keys since ldap has extensive infos
|
||||
#AUTH_LDAP_USER_ATTR_MAP = {"first_name": "givenName", "last_name": "sn"}
|
||||
|
||||
# Maps some profile keys since ldap has extensive infos
|
||||
#AUTH_LDAP_PROFILE_ATTR_MAP = {"home_directory": "homeDirectory"}
|
||||
|
||||
# LDAP config end
|
||||
|
||||
# Django nameko config
|
||||
|
||||
# Where's the Rabbitmq at
|
||||
NAMEKO_CONFIG = {
|
||||
'AMQP_URI': 'amqp://%s' % config['System']['RABBITMQ']
|
||||
}
|
||||
|
||||
# Standard pool size
|
||||
NAMEKO_POOL_SIZE = 4
|
||||
|
||||
# Django nameko config end
|
||||
|
||||
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
|
||||
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
STATIC_ROOT = os.path.dirname('/home/downhill/ungleich/vuejsuserservice/dal/dal/static/')
|
||||
|
||||
# Quick-start development settings - unsuitable for production
|
||||
# See https://docs.djangoproject.com/en/1.10/howto/deployment/checklist/
|
||||
|
||||
# SECURITY WARNING: keep the secret key used in production secret!
|
||||
SECRET_KEY = 'rn=f&ecp#&#escxpk!0e%a$i3sbm$z@5+g4h9q+w7-83*f2f-i'
|
||||
|
||||
# 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',
|
||||
'bootstrap3',
|
||||
'sekizai',
|
||||
'dal',
|
||||
]
|
||||
|
||||
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',
|
||||
]
|
||||
|
||||
# Backend for auth
|
||||
|
||||
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',
|
||||
)
|
||||
|
||||
|
||||
ROOT_URLCONF = 'dal.urls'
|
||||
|
||||
TEMPLATES = [
|
||||
{
|
||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||
'DIRS': [],
|
||||
'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',
|
||||
'sekizai.context_processors.sekizai',
|
||||
],
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
WSGI_APPLICATION = 'dal.wsgi.application'
|
||||
|
||||
# Django Bootstrap - Settings
|
||||
# Added Configuration for bootstrap static files to load over https.
|
||||
BOOTSTRAP3 = {
|
||||
|
||||
# The URL to the jQuery JavaScript file
|
||||
'jquery_url': '//code.jquery.com/jquery.min.js',
|
||||
|
||||
# The Bootstrap base URL
|
||||
'base_url': '//maxcdn.bootstrapcdn.com/bootstrap/3.3.4/',
|
||||
|
||||
# The complete URL to the Bootstrap CSS file
|
||||
# (None means derive it from base_url)
|
||||
'css_url': None,
|
||||
|
||||
# The complete URL to the Bootstrap CSS file (None means no theme)
|
||||
'theme_url': None,
|
||||
|
||||
# The complete URL to the Bootstrap JavaScript file
|
||||
# (None means derive it from base_url)
|
||||
'javascript_url': None,
|
||||
|
||||
# Put JavaScript in the HEAD section of the HTML document
|
||||
# (only relevant if you use bootstrap3.html)
|
||||
'javascript_in_head': False,
|
||||
|
||||
# Include jQuery with Bootstrap JavaScript
|
||||
# (affects django-bootstrap3 template tags)
|
||||
'include_jquery': False,
|
||||
|
||||
# Label class to use in horizontal forms
|
||||
'horizontal_label_class': 'col-md-3',
|
||||
|
||||
# Field class to use in horizontal forms
|
||||
'horizontal_field_class': 'col-md-9',
|
||||
|
||||
# Set HTML required attribute on required fields
|
||||
'set_required': True,
|
||||
|
||||
# Set HTML disabled attribute on disabled fields
|
||||
'set_disabled': False,
|
||||
|
||||
# Set placeholder attributes to label if no placeholder is provided
|
||||
'set_placeholder': True,
|
||||
|
||||
# Class to indicate required (better to set this in your Django form)
|
||||
'required_css_class': '',
|
||||
|
||||
# Class to indicate error (better to set this in your Django form)
|
||||
'error_css_class': 'has-error',
|
||||
|
||||
# Class to indicate success, meaning the field has valid input
|
||||
# (better to set this in your Django form)
|
||||
'success_css_class': 'has-success',
|
||||
|
||||
# Renderers (only set these if you have studied the source and understand
|
||||
# the inner workings)
|
||||
'formset_renderers': {
|
||||
'default': 'bootstrap3.renderers.FormsetRenderer',
|
||||
},
|
||||
'form_renderers': {
|
||||
'default': 'bootstrap3.renderers.FormRenderer',
|
||||
},
|
||||
'field_renderers': {
|
||||
'default': 'bootstrap3.renderers.FieldRenderer',
|
||||
'inline': 'bootstrap3.renderers.InlineFieldRenderer',
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
|
||||
# Database
|
||||
# https://docs.djangoproject.com/en/1.10/ref/settings/#databases
|
||||
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.sqlite3',
|
||||
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# Password validation
|
||||
# https://docs.djangoproject.com/en/1.10/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/1.10/topics/i18n/
|
||||
|
||||
LANGUAGE_CODE = 'en-us'
|
||||
|
||||
TIME_ZONE = 'UTC'
|
||||
|
||||
USE_I18N = True
|
||||
|
||||
USE_L10N = True
|
||||
|
||||
USE_TZ = True
|
||||
|
||||
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
# https://docs.djangoproject.com/en/1.10/howto/static-files/
|
||||
|
||||
STATIC_URL = '/static/'
|
|
@ -1,14 +0,0 @@
|
|||
<title> Userdata changed. </title>
|
||||
|
||||
<h2> The data for {{user}} has been changed. </h2>
|
||||
<br><br>
|
||||
<ul>
|
||||
<li> Username: {{user}} </li>
|
||||
<li> Firstname: {{firstname}} </li>
|
||||
<li> Lastname: {{lastname}} </li>
|
||||
<li> Email: {{email}} </li>
|
||||
</ul>
|
||||
<br><br>
|
||||
<form action={% url 'index' %} method="get">
|
||||
<input type="submit" value="Back to indexpage">
|
||||
</form>
|
|
@ -1,7 +0,0 @@
|
|||
<title> Password for {{user}} changed. </title>
|
||||
|
||||
<h2> The password for {{user}} has been changed. </h2>
|
||||
<br><br>
|
||||
<form action={% url 'index' %} method="get">
|
||||
<input type="submit" value="Back to indexpage">
|
||||
</form>
|
|
@ -1,20 +0,0 @@
|
|||
<title> Changing the password for {{user}} </title>
|
||||
|
||||
<h2> Changing the password for {{user}} </h2>
|
||||
<br><br>
|
||||
<form action={% url 'index' %} method="get">
|
||||
<input type="submit" value="Back to indexpage">
|
||||
</form>
|
||||
<br><br>
|
||||
To change the password for {{user}}, please supply
|
||||
<form action={% url 'change_password' %} method="post">
|
||||
{% csrf_token %}
|
||||
<br>The old password:<br>
|
||||
<input type="password" name="oldpassword" id="oldpassword">
|
||||
<br><br>The new password (at least 8 characters):<br>
|
||||
<input type="password" name="password1" id="password1">
|
||||
<br>Please repeat the new Password:<br>
|
||||
<input type="password" name="password2" id="password2">
|
||||
<br><br>
|
||||
<input type="submit" value="Submit">
|
||||
</form>
|
|
@ -1,19 +0,0 @@
|
|||
<title> Changing user data for {{user}} </title>
|
||||
|
||||
<h2> Changing user data for {{user}} </h2>
|
||||
<br><br>
|
||||
<form action={% url 'index' %} method="get">
|
||||
<input type="submit" value="Back to indexpage">
|
||||
</form>
|
||||
<br><br>
|
||||
<form action={% url 'change_data' %} method="post">
|
||||
{% csrf_token %}
|
||||
<br>Firstname:<br>
|
||||
<input type="text" name="firstname" id="firstname" value="{{firstname}}">
|
||||
<br><br>Lastname:<br>
|
||||
<input type="text" name="lastname" id="lastname" value="{{lastname}}">
|
||||
<br><br>Email:<br>
|
||||
<input type="text" name="email" id="email" value="{{email}}">
|
||||
<br><br>
|
||||
<input type="submit" value="Submit">
|
||||
</form>
|
|
@ -1,18 +0,0 @@
|
|||
<title> Deleting an Account </title>
|
||||
|
||||
<h2> Deleting an Account </h2>
|
||||
<br><br>
|
||||
<form action={% url 'index' %} method="get">
|
||||
<input type="submit" value="Back to indexpage">
|
||||
</form>
|
||||
<br><br>
|
||||
To delete an account, please type the username and password below:
|
||||
<form action={% url 'account_delete' %} method="post">
|
||||
{% csrf_token %}
|
||||
<br><br>Username:<br>
|
||||
<input type="text" name="username" id="username">
|
||||
<br><br>Password:<br>
|
||||
<input type="password" name="password" id="password">
|
||||
<br><br>
|
||||
<input type="submit" value="Submit">
|
||||
</form>
|
|
@ -1,7 +0,0 @@
|
|||
<title> Deleted user {{user}} </title>
|
||||
|
||||
<h2> The user {{user}} was deleted from our system. </h2>
|
||||
<br>
|
||||
<form action={% url 'index' %} method="get">
|
||||
<input type="submit" value="Back to indexpage">
|
||||
</form>
|
|
@ -1,17 +0,0 @@
|
|||
<title> An error has occurred! </title>
|
||||
|
||||
<h2> We are sorry, an error has occured while handling your request. </h2>
|
||||
|
||||
While trying to {{service}}, an error was encountered: {{error}}
|
||||
<br><br>
|
||||
You can try to:
|
||||
<br>
|
||||
{% if urlname %}
|
||||
<form action={% url urlname %} method="get">
|
||||
<input type="submit" value="Go back and try again">
|
||||
</form>
|
||||
<br>or<br>
|
||||
{% endif %}
|
||||
<form action={% url 'index' %} method="get">
|
||||
<input type="submit" value="Go to the indexpage">
|
||||
</form>
|
|
@ -1,44 +0,0 @@
|
|||
{% load i18n %}
|
||||
|
||||
<style>
|
||||
.col-lg-12 {
|
||||
background-color: grey;
|
||||
color: white;
|
||||
}
|
||||
.list-inline {
|
||||
background-color: grey;
|
||||
color: white;
|
||||
}
|
||||
</style>
|
||||
|
||||
<footer>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<ul class="list-inline">
|
||||
<li class="col-lg-12">
|
||||
<a href="#">Home</a>
|
||||
</li>
|
||||
<li class="footer-menu-divider">⋅</li>
|
||||
<li>
|
||||
<a href="#about">How it works</a></li>
|
||||
<li class="footer-menu-divider">⋅</li>
|
||||
<li>
|
||||
<a href="#about">Your infrastructure</a></li>
|
||||
<li>⋅</li>
|
||||
<li>
|
||||
<a href="#about">Our infrastructure</a></li>
|
||||
<li class="footer-menu-divider">⋅</li>
|
||||
<li>
|
||||
<a href="#services">Pricing</a>
|
||||
</li>
|
||||
<li class="footer-menu-divider">⋅</li>
|
||||
<li>
|
||||
<a href="#contact">Contact</a>
|
||||
</li>
|
||||
</ul>
|
||||
<p class="copyright text-muted small">Copyright © ungleich glarus ag {% now "Y" %}. {% trans "All Rights Reserved" %}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
|
@ -1,45 +0,0 @@
|
|||
{% extends "base_short.html" %}
|
||||
{% load staticfiles bootstrap3 %}
|
||||
|
||||
|
||||
{% block content %}
|
||||
|
||||
<style>
|
||||
.auth-footer {
|
||||
color: black;
|
||||
}
|
||||
a:link { color: #000000 }
|
||||
</style>
|
||||
<div class="auth_container">
|
||||
<div class="auth_bg"></div>
|
||||
<div class="auth_center">
|
||||
<div class="auth_content">
|
||||
<div class="auth-box">
|
||||
<h2 class="section-heading allcaps"> Login </h2>
|
||||
{% include 'includes/_messages.html' %}
|
||||
<form action={% url 'index' %} method="post" class="form" nonvalidated>
|
||||
{% csrf_token %}
|
||||
<div class="text-center">
|
||||
<div class="form-group"><label class="sr-only control-label" for="username">Username</label><input class="form-control" type="text" name="username" id="username" placeholder="Username"></div>
|
||||
<div class="form-group"><label class="sr-only control-label" for="password">Password</label><input class="form-control" type="password" name="password" id="password" placeholder="Password"></div>
|
||||
<br><br>
|
||||
<button type="submit" class="btn choice-btn"> Log in </button>
|
||||
</div>
|
||||
</form>
|
||||
<div class="auth-footer">
|
||||
<div>
|
||||
Don't have an account yet?
|
||||
<a href={% url 'register' %}> Sign up </a>
|
||||
</div>
|
||||
<div>
|
||||
or <a href={% url 'reset_password' %}> Reset your password </a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
<title> Login failed! </title>
|
||||
|
||||
<h2> Sorry, but your login has failed </h2>
|
||||
<br><br>This service runs for our LDAP users, so maybe you don't already have an LDAP account with us? If so, please register one.
|
||||
<form action={% url 'register' %} method="get">
|
||||
<input type="submit" value="Register an user">
|
||||
</form>
|
||||
<br><br>
|
||||
<form action={% url 'index' %} method="get">
|
||||
<input type="submit" value="Back to indexpage">
|
||||
</form>
|
|
@ -1,7 +0,0 @@
|
|||
<title> You must be logged in to access this page </title>
|
||||
|
||||
<h2> You must be logged in to access this page </h2>
|
||||
<br><br>
|
||||
<form action={% url 'index' %} method="get">
|
||||
<input type="submit" value="Back to indexpage">
|
||||
</form>
|
|
@ -1,27 +0,0 @@
|
|||
<title> Register an user at ungleich </title>
|
||||
|
||||
<h2> Register an user at ungleich </h2>
|
||||
<br><br>
|
||||
<form action={% url 'index' %} method="get">
|
||||
<input type="submit" value="Back to indexpage">
|
||||
</form>
|
||||
<br><br>
|
||||
To register yourself an user, please fill out the fields below:
|
||||
<br>
|
||||
<form action={% url 'register' %} method="post">
|
||||
{% csrf_token %}
|
||||
<br>Username (alphanumeric):<br>
|
||||
<input type="text" name="username" id="username">
|
||||
<br>Password (at least 8 characters):<br>
|
||||
<input type="password" name="password1" id="password1">
|
||||
<br>Please confirm your Password:<br>
|
||||
<input type="password" name="password2" id="password2">
|
||||
<br>Firstname:<br>
|
||||
<input type="text" name="firstname" id="firstname">
|
||||
<br>Lastname:<br>
|
||||
<input type="text" name="lastname" id="lastname">
|
||||
<br>Emailaddress:<br>
|
||||
<input type="text" name="email" id="email">
|
||||
<br>
|
||||
<input type="submit" value="Submit">
|
||||
</form>
|
|
@ -1,13 +0,0 @@
|
|||
<title> Password reset </title>
|
||||
|
||||
<h2> Password reset </h2>
|
||||
<br><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>
|
||||
<input type="text" name="user" id="user">
|
||||
<br>
|
||||
<input type="submit" value="Submit">
|
||||
</form>
|
|
@ -1,14 +0,0 @@
|
|||
<title> Set new password for {{user}} </title>
|
||||
|
||||
<h2> Please set new password for {{user}} </h2>
|
||||
<br><br>
|
||||
<form action={% url 'reset' %} method="post">
|
||||
{% csrf_token %}
|
||||
New Password:<br>
|
||||
<input type="password" name="password1" id="password1">
|
||||
<br>Please confirm new password:<br>
|
||||
<input type="password" name="password2" id="password2">
|
||||
<br>
|
||||
<input type="hidden" name="user" id="user" value="{{user}}">
|
||||
<input type="submit" value="Submit">
|
||||
</form>
|
|
@ -1,10 +0,0 @@
|
|||
<title> Reset request processed and confirmation email sent </title>
|
||||
|
||||
<h2> Reset request processed and confirmation email sent </h2>
|
||||
<br><br>
|
||||
You will shortly get the confirmation email to confirm that you wish to reset the password for {{user}}.<br>
|
||||
Please follow the link in the email to reset your password.
|
||||
<br><br>
|
||||
<form action={% url 'index' %} method="get">
|
||||
<input type="submit" value="Back to indexpage">
|
||||
</form>
|
|
@ -1,7 +0,0 @@
|
|||
<title> User {{ user }} created. </title>
|
||||
|
||||
<h2> User {{ user }} was successfully created. </h2>
|
||||
<br><br>
|
||||
<form action={% url 'index' %} method="get">
|
||||
<input type="submit" value="Back to Indexpage">
|
||||
</form>
|
|
@ -1,24 +0,0 @@
|
|||
<title> Options for {{user}} </title>
|
||||
|
||||
<h2> Welcome, {{user}} </h2>
|
||||
<br><br>
|
||||
You have the following options:
|
||||
<br>
|
||||
<form action={% url 'change_data' %} method="get">
|
||||
<input type="submit" value="Change your userdata">
|
||||
</form>
|
||||
<br>
|
||||
<form action={% url 'change_password' %} method="get">
|
||||
<input type="submit" value="Change your password">
|
||||
</form>
|
||||
<br>
|
||||
<form action={% url 'reset_password' %} method="get">
|
||||
<input type="submit" value="Reset your password">
|
||||
</form>
|
||||
<br>
|
||||
<form action={% url 'account_delete' %} method="get">
|
||||
<input type="submit" value="Delete your account">
|
||||
</form>
|
||||
<form action={% url 'logout' %} method="get">
|
||||
<input type="submit" value="Logout">
|
||||
</form>
|
437
dal/dal/views.py
|
@ -1,437 +0,0 @@
|
|||
# Imports from django
|
||||
from django.shortcuts import render
|
||||
from django.views.generic import View
|
||||
from django.contrib.auth import authenticate, login, logout
|
||||
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.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 django_nameko import get_pool
|
||||
from random import choice, randint
|
||||
import string
|
||||
from configparser import ConfigParser
|
||||
|
||||
config = ConfigParser()
|
||||
config.read('userservice.conf')
|
||||
|
||||
# Check to see if the username is already taken
|
||||
# Helper function, not to be set up as a view
|
||||
# Check the LDAP if the user exists
|
||||
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
|
||||
|
||||
class Index(View):
|
||||
|
||||
# Basic binary choice, if it is an authenticated user, go straight to the options page,
|
||||
# if not, then show the landing page
|
||||
def get(self, request):
|
||||
if request.user.is_authenticated:
|
||||
return render(request, 'useroptions.html', { 'user': request.user } )
|
||||
return render(request, 'landing.html')
|
||||
|
||||
# Basically does the same as the GET request, just with trying to login the user beforehand
|
||||
# Shows an errorpage if authentication fails, since just looping to the landing page
|
||||
# would be frustrating
|
||||
def post(self, request):
|
||||
username = request.POST.get('username')
|
||||
password = request.POST.get('password')
|
||||
pwd = r'%s' % password
|
||||
user = authenticate(request, username=username, password=pwd)
|
||||
if user is not None:
|
||||
login(request, user)
|
||||
return render(request, 'useroptions.html', { 'user': user } )
|
||||
return render(request, 'loginfailed.html')
|
||||
|
||||
|
||||
# Registering a user
|
||||
|
||||
class Register(View):
|
||||
|
||||
# Someone wants to register, throw up the page for that
|
||||
def get(self, request):
|
||||
return render(request, 'registeruser.html')
|
||||
|
||||
# Someone filled out the register page, do some basic checks and throw it at nameko
|
||||
def post(self, request):
|
||||
# message for the error template
|
||||
service = 'register an user'
|
||||
# urlname for 'go back' on the errorpage
|
||||
urlname = 'register'
|
||||
username = request.POST.get('username')
|
||||
if username == "" or not username:
|
||||
return render(request, 'error.html', { 'urlname': urlname, 'service': service, 'error': 'Please supply a username.' } )
|
||||
# Check to see if username is already taken
|
||||
# isalnum() may be a bit harsh, but is the most logical choice to make sure it's a username we
|
||||
# can use
|
||||
if not username.isalnum():
|
||||
return render(request, 'error.html', { 'urlname': urlname, 'service': service, 'error': 'Username has to be alphanumeric.' } )
|
||||
elif check_user_exists(username):
|
||||
return render(request, 'error.html', { 'urlname': urlname, 'service': service, 'error': 'User already exists.' } )
|
||||
password1 = request.POST.get('password1')
|
||||
password2 = request.POST.get('password2')
|
||||
# check if the supplied passwords match
|
||||
if password1 != password2:
|
||||
return render(request, 'error.html', { 'urlname': urlname, 'service': service,
|
||||
'error': 'Your passwords did not match. Please supply the same password twice.' } )
|
||||
# check for at least a bit of length on the password
|
||||
if len(password1) < 8:
|
||||
return render(request, 'error.html', { 'urlname': urlname, 'service': service,
|
||||
'error': 'Your password is too short, please use a longer one. At least 8 characters.' } )
|
||||
|
||||
email = request.POST.get('email')
|
||||
# Is the emailaddress valid?
|
||||
try:
|
||||
validate_email(email)
|
||||
except ValidationError:
|
||||
return render(request, 'error.html', { 'urlname': urlname, 'service': service, 'error': 'The supplied email address is invalid.' } )
|
||||
|
||||
firstname = request.POST.get('firstname')
|
||||
lastname = request.POST.get('lastname')
|
||||
if firstname == "" or not firstname or lastname == "" or not lastname:
|
||||
return render(request, 'error.html', { 'urlname': urlname, 'service': service, 'error': 'Please enter your firstname and lastname.' } )
|
||||
# throw it to nameko to create the user
|
||||
with get_pool().next() as rpc:
|
||||
# so nothing strange happens if there are escapable chars
|
||||
pwd = r'%s' % password1
|
||||
result = rpc.createuser.create_user(username, pwd, firstname, lastname, email)
|
||||
if result == True:
|
||||
return render(request, 'usercreated.html', { 'user': username } )
|
||||
else:
|
||||
return render(request, 'error.html', { 'urlname': urlname, 'service': service, 'error': result } )
|
||||
|
||||
|
||||
|
||||
# Change user data for logged in users
|
||||
|
||||
class ChangeData(View):
|
||||
|
||||
|
||||
# provide the form for the change request
|
||||
def get(self, request):
|
||||
urlname = 'change_data'
|
||||
service = 'get default data for logged in user'
|
||||
if not request.user.is_authenticated:
|
||||
return render(request, 'mustbeloggedin.html')
|
||||
user = request.user
|
||||
login(request, user)
|
||||
# get basic data (firstname, lastname, email)
|
||||
with get_pool().next() as rpc:
|
||||
(state, firstname, lastname, email) = rpc.getuserdata.get_data(str(request.user))
|
||||
# If it throws an error, the errormessage gets put into firstname.. not great naming, but works best this way
|
||||
if state == "error":
|
||||
return render(request, 'error.html', { 'urlname': urlname, 'service': service, 'error': firstname } )
|
||||
# The template puts the old data as standard in the fields
|
||||
else:
|
||||
return render(request, 'changeuserdata.html', { 'user': str(request.user), 'firstname': firstname, 'lastname': lastname, 'email': email } )
|
||||
|
||||
# get the change request
|
||||
def post(self, request):
|
||||
# variables for the error page
|
||||
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')
|
||||
|
||||
user = str(request.user)
|
||||
firstname = request.POST.get('firstname')
|
||||
lastname = request.POST.get('lastname')
|
||||
email = request.POST.get('email')
|
||||
|
||||
# Some sanity checks for the supplied data
|
||||
if firstname == "":
|
||||
return render(request, 'error.html', { 'urlname': urlname, 'service': service, 'error': 'Please enter a firstname.' } )
|
||||
elif lastname == "":
|
||||
return render(request, 'error.html', { 'urlname': urlname, 'service': service, 'error': 'Please enter a lastname.' } )
|
||||
elif email == "":
|
||||
return render(request, 'error.html', { 'urlname': urlname, 'service': service, 'error': 'Please enter an email.' } )
|
||||
try:
|
||||
validate_email(email)
|
||||
except ValidationError:
|
||||
return render(request, 'error.html', { 'urlname': urlname, 'service': service, 'error': 'The supplied email address is invalid.' } )
|
||||
# Trying to change the data
|
||||
with get_pool().next() as rpc:
|
||||
result = rpc.changeuserdata.change_data(user, firstname, lastname, email)
|
||||
# Data change worked
|
||||
if result == True:
|
||||
return render(request, 'changeddata.html', { 'user': user, 'firstname': firstname, 'lastname': lastname, 'email': email } )
|
||||
# Data change did not work, display error
|
||||
else:
|
||||
return render(request, 'error.html', { 'urlname': urlname, 'service': service, 'error': result } )
|
||||
|
||||
|
||||
# Resets the password for a user
|
||||
# Sends email to the user with a link to reset the password
|
||||
|
||||
class ResetPassword(View):
|
||||
|
||||
# Presents the form with some information
|
||||
def get(self, request):
|
||||
return render(request, 'resetpassword.html')
|
||||
|
||||
# gets the data from confirming the reset request and checks if it was not a misclick
|
||||
# (by having the user type in his username)
|
||||
def post(self, request):
|
||||
urlname = 'reset_password'
|
||||
service = 'send a password reset request'
|
||||
user = request.POST.get('user')
|
||||
# First, check if the user exists
|
||||
if not check_user_exists(user):
|
||||
return render(request, 'error.html', { 'urlname': urlname, 'service': service, 'error': 'The user does not exist.' } )
|
||||
# user exists, so try to get email
|
||||
with get_pool().next() as rpc:
|
||||
(state, tmp1, tmp2, email) = rpc.getuserdata.get_data(user)
|
||||
# Either error with the datalookup or no email provided
|
||||
if state == "error" or email == 'No email given' or not email:
|
||||
return render(request, 'error.html', { 'urlname': urlname, 'service': service, 'error': 'Unable to retrieve email address for user.' } )
|
||||
# Try to send the email out
|
||||
emailsend = self.email(user, email)
|
||||
# Email got sent out
|
||||
if emailsend == True:
|
||||
return render(request, 'send_resetrequest.html', { 'user': user } )
|
||||
# Error while trying to send email
|
||||
else:
|
||||
return render(request, 'error.html', { 'urlname': urlname, 'service': service, 'error': emailsend } )
|
||||
|
||||
# Sends an email to the user with the 24h active link for a password reset
|
||||
def email(self, user, email):
|
||||
# getting epoch for the time now in UTC to spare us headache with timezones
|
||||
creationtime = int(datetime.utcnow().timestamp())
|
||||
# Construct the data for the email
|
||||
email_from = 'Userservice at ungleich <%s>' % config['EMAIL']['EMAILFROM']
|
||||
to = ['%s <%s>' % (user, email)]
|
||||
subject = 'Password reset request for %s' % user
|
||||
link = self.build_reset_link(user, creationtime)
|
||||
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'
|
||||
# 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 = 'account-staging.ungleich.ch'
|
||||
tokengen = PasswordResetTokenGenerator()
|
||||
# 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
|
||||
newdbentry = ResetToken(user=user, token=token, creation=epochutc)
|
||||
newdbentry.save()
|
||||
# set up the link
|
||||
link = 'https://%s/reset/%s/%s/' % (host, userpart.decode('utf-8'), token)
|
||||
return link
|
||||
|
||||
|
||||
# Catch the resetrequest URL and check it
|
||||
class ResetRequest(View):
|
||||
|
||||
# Gets the URL with user in b64 and the token, and checks it
|
||||
# Also cleans the database
|
||||
def get(self, request, user=None, token=None):
|
||||
# Cleans up outdated tokens
|
||||
# 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
|
||||
self.clean_db()
|
||||
# If user and token are not supplied by django, it was called from somewhere else, so it's
|
||||
# invalid
|
||||
if user == None or token == None:
|
||||
return HttpResponse('Invalid URL.', status=404)
|
||||
# extract user from b64 format
|
||||
tmp_user = bytes(user, 'utf-8')
|
||||
user = b64decode(tmp_user)
|
||||
user_clean = user.decode('utf-8')
|
||||
# set checks_out = True if token is found in database
|
||||
dbentries = ResetToken.objects.all().filter(user=user_clean)
|
||||
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, supply the form
|
||||
else:
|
||||
return render(request, 'resetpasswordnew.html', { 'user': user_clean } )
|
||||
|
||||
|
||||
# Gets the post form with the new password and sets it
|
||||
def post(self, request):
|
||||
service = 'reset the password'
|
||||
# get the supplied passwords
|
||||
password1 = request.POST.get("password1")
|
||||
password2 = request.POST.get("password2")
|
||||
# get the hidden value of user
|
||||
user = request.POST.get("user")
|
||||
# some checks over the supplied data
|
||||
if user == "" or not user:
|
||||
return render(request, 'error.html', { 'service': service, 'error': 'Something went wrong. Did you use the supplied form?' } )
|
||||
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.' } )
|
||||
if password1 != password2:
|
||||
return render(request, 'error.html', { 'service': service, 'error': 'The supplied passwords do not match.' } )
|
||||
if len(password1) < 8:
|
||||
return render(request, 'error.html', { 'service': service, 'error': 'The password is too short, please use a longer one. At least 8 characters.' } )
|
||||
# everything checks out, now change the password
|
||||
with get_pool().next() as rpc:
|
||||
pwd = r'%s' % password1
|
||||
result = rpc.changepassword.change_password(user, pwd)
|
||||
# password change successfull
|
||||
if result == True:
|
||||
return render(request, 'changedpassword.html', { 'user': user } )
|
||||
# Something went wrong while changing the password
|
||||
else:
|
||||
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
|
||||
|
||||
class ChangePassword(View):
|
||||
|
||||
# Presents the page for a logged in user
|
||||
def get(self, request):
|
||||
if not request.user.is_authenticated:
|
||||
return render(request, 'mustbeloggedin.html')
|
||||
return render(request, 'changepassword.html', { 'user': request.user } )
|
||||
|
||||
# Does some checks on the supplied data and changes the password
|
||||
def post(self, request):
|
||||
# Variables for the error page
|
||||
urlname = 'change_password'
|
||||
service = 'change the password'
|
||||
|
||||
if not request.user.is_authenticated:
|
||||
return render(request, 'mustbeloggedin.html')
|
||||
login(request, request.user)
|
||||
|
||||
user = str(request.user)
|
||||
oldpassword = request.POST.get('oldpassword')
|
||||
check = authenticate(request, username=user, password=oldpassword)
|
||||
# Is the right password for the user supplied?
|
||||
if check is None:
|
||||
return render(request, 'error.html', { 'urlname': urlname, 'service': service, 'error': 'Wrong password for the user.' } )
|
||||
|
||||
password1 = request.POST.get('password1')
|
||||
password2 = request.POST.get('password2')
|
||||
# Are both passwords from the form the same?
|
||||
if password1 != password2:
|
||||
return render(request, 'error.html', { 'urlname': urlname, 'service': service,
|
||||
'error': 'Please check if you typed the same password both times for the new password' } )
|
||||
# Check for password length
|
||||
if len(password1) < 8:
|
||||
return render(request, 'error.html', { 'urlname': urlname, 'service': service,
|
||||
'error': 'The password is too short, please use a longer one. At least 8 characters.' } )
|
||||
with get_pool().next() as rpc:
|
||||
# Trying to change the password
|
||||
pwd = r'%s' % password1
|
||||
result = rpc.changepassword.change_password(user, pwd)
|
||||
# Password was changed
|
||||
if result == True:
|
||||
return render(request, 'changedpassword.html', { 'user': user } )
|
||||
# Password not changed, instead got some kind of error
|
||||
else:
|
||||
return render(request, 'error.html', { 'urlname': urlname, 'service': service, 'error': result } )
|
||||
|
||||
|
||||
# Deletes an account
|
||||
class DeleteAccount(View):
|
||||
|
||||
# Show the basic form for deleting an account
|
||||
def get(self, request):
|
||||
return render(request, 'deleteaccount.html')
|
||||
|
||||
# Reads the filled out form
|
||||
def post(self, request):
|
||||
# Variables for error page
|
||||
urlname = 'account_delete'
|
||||
service = 'delete an account'
|
||||
|
||||
# Does the user exist?
|
||||
username = request.POST.get('username')
|
||||
if not check_user_exists(username):
|
||||
return render(request, 'error.html', { 'urlname': urlname, 'service': service, 'error': 'Unknown user.' } )
|
||||
|
||||
# Do user and password match?
|
||||
password = request.POST.get('password')
|
||||
pwd = r'%s' % password
|
||||
check = authenticate(request, username=username, password=pwd)
|
||||
if check is None:
|
||||
return render(request, 'error.html', { 'urlname': urlname, 'service': service, 'error': 'Wrong password for user.' } )
|
||||
|
||||
# Try to delete the user
|
||||
with get_pool().next() as rpc:
|
||||
result = rpc.deleteuser.delete_user(username)
|
||||
# User deleted
|
||||
if result == True:
|
||||
logout(request)
|
||||
return render(request, 'deleteduser.html', { 'user': username } )
|
||||
# User not deleted, got some kind of error
|
||||
else:
|
||||
return render(request, 'error.html', { 'urlname': urlname, 'service': service, 'error': result } )
|
||||
|
||||
# Log out the session
|
||||
class LogOut(View):
|
||||
|
||||
def get(self, request):
|
||||
logout(request)
|
||||
return HttpResponse("You have been logged out.", status=200)
|
10
dal/env.sample
Normal file
|
@ -0,0 +1,10 @@
|
|||
# Create .env to be loaded automatically
|
||||
|
||||
LDAPSERVER="ldap://ldap1.ungleich.ch ldap://ldap2.ungleich.ch"
|
||||
LDAPSEARCHUSER="user here"
|
||||
LDAPSEARCHUSERPASSWORD="password here"
|
||||
|
||||
|
||||
# Space separated list of search bases for users
|
||||
LDAPSEARCH="ou=users,dc=ungleich,dc=ch ou=customers,dc=ungleich,dc=ch"
|
||||
LDAPCREATE="ou=customers,dc=ungleich,dc=ch"
|
31
dal/forms.py
Normal file
|
@ -0,0 +1,31 @@
|
|||
from django import forms
|
||||
from django.contrib.auth import authenticate
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
|
||||
class LoginForm(forms.Form):
|
||||
username = forms.CharField(widget=forms.TextInput())
|
||||
password = forms.CharField(widget=forms.PasswordInput())
|
||||
|
||||
class Meta:
|
||||
fields = ['username', 'password']
|
||||
|
||||
def clean(self):
|
||||
username = self.cleaned_data.get('username')
|
||||
password = self.cleaned_data.get('password')
|
||||
if self.errors:
|
||||
return self.cleaned_data
|
||||
is_auth = authenticate(username=username, password=password)
|
||||
if not is_auth:
|
||||
raise forms.ValidationError(
|
||||
_("Your username and/or password were incorrect.")
|
||||
)
|
||||
# elif is_auth.validated == 0:
|
||||
# raise forms.ValidationError(
|
||||
# _("Your account is not activated yet.")
|
||||
# )
|
||||
return self.cleaned_data
|
||||
|
||||
def clean_username(self):
|
||||
username = self.cleaned_data.get('username')
|
||||
return username
|
37
dal/migrations/0001_initial.py
Normal file
|
@ -0,0 +1,37 @@
|
|||
# Generated by Django 2.1.7 on 2019-02-24 17:35
|
||||
|
||||
import dal.models
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='ResetToken',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('user', models.CharField(max_length=100)),
|
||||
('token', models.CharField(max_length=255)),
|
||||
('creation', models.BigIntegerField()),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='UserAccountValidationDetail',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('validated', models.IntegerField(choices=[(0, 'Not validated'), (1, 'Validated')], default=0)),
|
||||
('validation_slug', models.CharField(db_index=True, default=dal.models.get_validation_slug, max_length=50, unique=True)),
|
||||
('date_validation_started', models.DateTimeField(auto_now_add=True)),
|
||||
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
),
|
||||
]
|
0
dal/migrations/__init__.py
Normal file
32
dal/models.py
Normal file
|
@ -0,0 +1,32 @@
|
|||
from django.db import models
|
||||
from django.contrib.auth.hashers import make_password
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
# Basic DB to correlate tokens, users and creation time
|
||||
|
||||
class ResetToken(models.Model):
|
||||
|
||||
# users wouldn't use usernames >100 chars
|
||||
user = models.CharField(max_length=100)
|
||||
# Not so sure about tokens, better make it big
|
||||
# should be <100, but big usernames make bigger tokens
|
||||
# if I read that correctly
|
||||
token = models.CharField(max_length=255)
|
||||
# creation time in epoch (UTC)
|
||||
# BigInt just so we are save for the next few decades ;)
|
||||
creation = models.BigIntegerField()
|
||||
|
||||
|
||||
def get_validation_slug():
|
||||
return make_password(None)
|
||||
|
||||
|
||||
class UserAccountValidationDetail(models.Model):
|
||||
user = models.OneToOneField(User, on_delete=models.CASCADE)
|
||||
VALIDATED_CHOICES = ((0, 'Not validated'), (1, 'Validated'))
|
||||
validated = models.IntegerField(choices=VALIDATED_CHOICES, default=0)
|
||||
validation_slug = models.CharField(
|
||||
db_index=True, unique=True, max_length=50,
|
||||
default=get_validation_slug
|
||||
)
|
||||
date_validation_started = models.DateTimeField(auto_now_add=True)
|
218
dal/settings.py
Normal file
|
@ -0,0 +1,218 @@
|
|||
"""
|
||||
Django settings for dal project.
|
||||
|
||||
Generated by 'django-admin startproject' using Django 1.10.7.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/1.10/topics/settings/
|
||||
|
||||
For the full list of settings and their values, see
|
||||
https://docs.djangoproject.com/en/1.10/ref/settings/
|
||||
"""
|
||||
|
||||
import os
|
||||
from decouple import config, Csv
|
||||
import ldap
|
||||
from django_auth_ldap.config import LDAPSearch, LDAPSearchUnion
|
||||
|
||||
# LDAP setup
|
||||
LDAP_SERVER = config('LDAP_SERVER')
|
||||
AUTH_LDAP_SERVER_URI = config('LDAPSERVER')
|
||||
|
||||
LDAP_ADMIN_DN = config('LDAP_ADMIN_DN')
|
||||
LDAP_ADMIN_PASSWORD = config('LDAP_ADMIN_PASSWORD')
|
||||
AUTH_LDAP_BIND_DN = LDAP_ADMIN_DN
|
||||
AUTH_LDAP_BIND_PASSWORD = LDAP_ADMIN_PASSWORD
|
||||
AUTH_LDAP_SERVER = AUTH_LDAP_SERVER_URI
|
||||
|
||||
LDAP_CUSTOMER_DN = config('LDAP_CUSTOMER_DN')
|
||||
LDAP_USERS_DN = config('LDAP_USERS_DN')
|
||||
LDAP_CUSTOMER_GROUP_ID = config('LDAP_CUSTOMER_GROUP_ID', cast=int)
|
||||
|
||||
LDAP_MAX_UID_FILE_PATH = config(
|
||||
'LDAP_MAX_UID_FILE_PATH',
|
||||
default=os.path.join(os.path.abspath(
|
||||
os.path.dirname(__file__)), 'ldap_max_uid_file'
|
||||
)
|
||||
)
|
||||
LDAP_DEFAULT_START_UID = config('LDAP_DEFAULT_START_UID', cast=int)
|
||||
|
||||
# Search union over OUs
|
||||
search_base = config('LDAPSEARCH').split()
|
||||
search_base_ldap = [ LDAPSearch(x, ldap.SCOPE_SUBTREE, "(uid=%(user)s)") for x in search_base ]
|
||||
AUTH_LDAP_USER_SEARCH = LDAPSearchUnion(*search_base_ldap)
|
||||
|
||||
AUTH_LDAP_START_TLS = config('LDAP_USE_TLS', default=False, cast=bool)
|
||||
|
||||
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
DEBUG = config('DEBUG', default=False, cast=bool)
|
||||
|
||||
EMAIL_FROM_ADDRESS = config('EMAIL_FROM_ADDRESS')
|
||||
|
||||
EMAIL_HOST = config("EMAIL_HOST", default="localhost")
|
||||
EMAIL_PORT = config("EMAIL_PORT", default=25, cast=int)
|
||||
EMAIL_USE_TLS = config("EMAIL_USE_TLS", default=True, cast=bool)
|
||||
|
||||
ALLOWED_HOSTS = config('ALLOWED_HOSTS', cast=Csv())
|
||||
|
||||
INSTALLED_APPS = [
|
||||
'django.contrib.admin',
|
||||
'django.contrib.auth',
|
||||
'django.contrib.contenttypes',
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
'bootstrap3',
|
||||
'dal',
|
||||
'rest_framework'
|
||||
]
|
||||
|
||||
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',
|
||||
]
|
||||
|
||||
AUTHENTICATION_BACKENDS = (
|
||||
'django_auth_ldap.backend.LDAPBackend',
|
||||
)
|
||||
|
||||
|
||||
ROOT_URLCONF = 'dal.urls'
|
||||
|
||||
TEMPLATES = [
|
||||
{
|
||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||
'DIRS': [],
|
||||
'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 = 'dal.wsgi.application'
|
||||
|
||||
# Password validation
|
||||
# https://docs.djangoproject.com/en/1.10/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/1.10/topics/i18n/
|
||||
|
||||
LANGUAGE_CODE = 'en-us'
|
||||
|
||||
TIME_ZONE = 'UTC'
|
||||
|
||||
USE_I18N = True
|
||||
|
||||
USE_L10N = True
|
||||
|
||||
USE_TZ = True
|
||||
|
||||
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
# https://docs.djangoproject.com/en/1.10/howto/static-files/
|
||||
|
||||
STATIC_URL = '/static/'
|
||||
|
||||
############################# To be fixed
|
||||
|
||||
STATIC_ROOT= os.path.join(BASE_DIR, 'static/')
|
||||
|
||||
# Database
|
||||
# https://docs.djangoproject.com/en/1.10/ref/settings/#databases
|
||||
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.sqlite3',
|
||||
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
|
||||
}
|
||||
}
|
||||
SECRET_KEY = config('SECRET_KEY')
|
||||
|
||||
AUTH_LDAP_USER_ATTR_MAP = {
|
||||
"first_name": "givenName",
|
||||
"last_name": "sn",
|
||||
"email": "mail"
|
||||
}
|
||||
|
||||
ENTIRE_SEARCH_BASE = config("ENTIRE_SEARCH_BASE")
|
||||
|
||||
LOGGING = {
|
||||
'disable_existing_loggers': False,
|
||||
'version': 1,
|
||||
'formatters': {
|
||||
'standard': {
|
||||
'format': '%(asctime)s %(levelname)s %(name)s %(funcName)s %(lineno)d: %(message)s'
|
||||
}
|
||||
},
|
||||
'handlers': {
|
||||
'default': {
|
||||
'level': 'DEBUG',
|
||||
'class': 'logging.handlers.RotatingFileHandler',
|
||||
'filename': 'logs/debug.log',
|
||||
'maxBytes': 1024*1024*5,
|
||||
'backupCount': 10,
|
||||
'formatter': 'standard',
|
||||
},
|
||||
'console': {
|
||||
'class': 'logging.StreamHandler',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if config('ENABLE_DEBUG_LOG', default=False, cast=bool):
|
||||
loggers_dict = {}
|
||||
modules_to_log_list = config(
|
||||
'MODULES_TO_LOG', default='django', cast=Csv()
|
||||
)
|
||||
for custom_module in modules_to_log_list:
|
||||
logger_item = {
|
||||
custom_module: {
|
||||
'handlers': ['default'],
|
||||
'level': 'INFO',
|
||||
'propagate': True
|
||||
}
|
||||
}
|
||||
loggers_dict.update(logger_item)
|
||||
|
||||
LOGGING['loggers'] = loggers_dict
|
||||
|
||||
if 'ldap3' in modules_to_log_list:
|
||||
from ldap3.utils.log import (
|
||||
set_library_log_detail_level, OFF, BASIC, NETWORK, EXTENDED
|
||||
)
|
||||
set_library_log_detail_level(BASIC)
|
||||
|
||||
|
||||
REST_FRAMEWORK = {
|
||||
'DEFAULT_RENDERER_CLASSES': (
|
||||
'rest_framework.renderers.JSONRenderer',
|
||||
)
|
||||
}
|
4
dal/static/datacenterlight/font-awesome/css/font-awesome.min.css
vendored
Normal file
Before Width: | Height: | Size: 434 KiB After Width: | Height: | Size: 434 KiB |
Before Width: | Height: | Size: 106 KiB After Width: | Height: | Size: 106 KiB |
Before Width: | Height: | Size: 9.7 KiB After Width: | Height: | Size: 9.7 KiB |
Before Width: | Height: | Size: 394 KiB After Width: | Height: | Size: 394 KiB |
Before Width: | Height: | Size: 298 KiB After Width: | Height: | Size: 298 KiB |
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 50 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 8.9 KiB After Width: | Height: | Size: 8.9 KiB |
Before Width: | Height: | Size: 685 KiB After Width: | Height: | Size: 685 KiB |
Before Width: | Height: | Size: 5.8 KiB After Width: | Height: | Size: 5.8 KiB |