Merge branch 'master' into 'master'
Make user service functional See merge request ungleich-public/ungleich-user!1
41
.gitignore
vendored
|
@ -1 +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/
|
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,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,43 +0,0 @@
|
||||||
{% extends "base_short.html" %}
|
|
||||||
{% load staticfiles %}
|
|
||||||
|
|
||||||
|
|
||||||
{% 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 %}
|
|
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):
|
||||||
|
email = forms.CharField(widget=forms.TextInput())
|
||||||
|
password = forms.CharField(widget=forms.PasswordInput())
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
fields = ['email', 'password']
|
||||||
|
|
||||||
|
def clean(self):
|
||||||
|
email = self.cleaned_data.get('email')
|
||||||
|
password = self.cleaned_data.get('password')
|
||||||
|
if self.errors:
|
||||||
|
return self.cleaned_data
|
||||||
|
is_auth = authenticate(username=email, 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_email(self):
|
||||||
|
email = self.cleaned_data.get('email')
|
||||||
|
return email
|
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)
|
|
@ -11,29 +11,50 @@ https://docs.djangoproject.com/en/1.10/ref/settings/
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import dotenv
|
from decouple import config, Csv
|
||||||
import ldap
|
import ldap
|
||||||
from django_auth_ldap.config import LDAPSearch, LDAPSearchUnion
|
from django_auth_ldap.config import LDAPSearch, LDAPSearchUnion
|
||||||
|
|
||||||
# get config
|
|
||||||
dotenv.read_dotenv()
|
|
||||||
|
|
||||||
# LDAP setup
|
# LDAP setup
|
||||||
AUTH_LDAP_SERVER_URI = os.environ['LDAPSERVER']
|
LDAP_SERVER = config('LDAP_SERVER')
|
||||||
AUTH_LDAP_BIND_DN = os.environ['LDAPSEARCHUSER']
|
AUTH_LDAP_SERVER_URI = config('LDAPSERVER')
|
||||||
AUTH_LDAP_BIND_PASSWORD = os.environ['LDAPSEARCHUSERPASSWORD']
|
|
||||||
|
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 union over OUs
|
||||||
search_base = os.environ['LDAPSEARCH'].split()
|
search_base = config('LDAPSEARCH').split()
|
||||||
search_base_ldap = [ LDAPSearch(x, ldap.SCOPE_SUBTREE, "(uid=%(user)s)") for x in search_base ]
|
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_USER_SEARCH = LDAPSearchUnion(*search_base_ldap)
|
||||||
|
|
||||||
if os.environ['LDAP_USE_TLS']:
|
AUTH_LDAP_START_TLS = config('LDAP_USE_TLS', default=False, cast=bool)
|
||||||
AUTH_LDAP_START_TLS=True
|
|
||||||
|
|
||||||
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||||
|
|
||||||
ALLOWED_HOSTS = []
|
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 = [
|
INSTALLED_APPS = [
|
||||||
'django.contrib.admin',
|
'django.contrib.admin',
|
||||||
|
@ -42,6 +63,7 @@ INSTALLED_APPS = [
|
||||||
'django.contrib.sessions',
|
'django.contrib.sessions',
|
||||||
'django.contrib.messages',
|
'django.contrib.messages',
|
||||||
'django.contrib.staticfiles',
|
'django.contrib.staticfiles',
|
||||||
|
'bootstrap3',
|
||||||
'dal',
|
'dal',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -120,7 +142,7 @@ STATIC_URL = '/static/'
|
||||||
|
|
||||||
############################# To be fixed
|
############################# To be fixed
|
||||||
|
|
||||||
STATIC_ROOT = os.path.dirname('/home/downhill/ungleich/vuejsuserservice/dal/dal/static/')
|
STATIC_ROOT= os.path.join(BASE_DIR, 'static/')
|
||||||
|
|
||||||
# Database
|
# Database
|
||||||
# https://docs.djangoproject.com/en/1.10/ref/settings/#databases
|
# https://docs.djangoproject.com/en/1.10/ref/settings/#databases
|
||||||
|
@ -131,7 +153,58 @@ DATABASES = {
|
||||||
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
|
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SECRET_KEY = 'rn=f&ecp#&#escxpk!0e%a$i3sbm$z@5+g4h9q+w7-83*f2f-i'
|
SECRET_KEY = config('SECRET_KEY')
|
||||||
|
|
||||||
# SECURITY WARNING: don't run with debug turned on in production!
|
AUTH_LDAP_USER_ATTR_MAP = {
|
||||||
DEBUG = True
|
"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)
|
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 |
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 276 KiB After Width: | Height: | Size: 276 KiB |
Before Width: | Height: | Size: 8.7 KiB After Width: | Height: | Size: 8.7 KiB |
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 166 KiB After Width: | Height: | Size: 166 KiB |
Before Width: | Height: | Size: 208 KiB After Width: | Height: | Size: 208 KiB |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 778 KiB After Width: | Height: | Size: 778 KiB |
Before Width: | Height: | Size: 327 KiB After Width: | Height: | Size: 327 KiB |
Before Width: | Height: | Size: 1.2 MiB After Width: | Height: | Size: 1.2 MiB |
Before Width: | Height: | Size: 185 KiB After Width: | Height: | Size: 185 KiB |
Before Width: | Height: | Size: 618 KiB After Width: | Height: | Size: 618 KiB |
Before Width: | Height: | Size: 1.1 MiB After Width: | Height: | Size: 1.1 MiB |
Before Width: | Height: | Size: 246 KiB After Width: | Height: | Size: 246 KiB |
Before Width: | Height: | Size: 1,021 KiB After Width: | Height: | Size: 1,021 KiB |
Before Width: | Height: | Size: 333 KiB After Width: | Height: | Size: 333 KiB |