Compare commits

...

23 Commits
0.7 ... master

Author SHA1 Message Date
PCoder 6ba71545a1 Fix psycopg2 bug
Internal Server Error: /admin/login/
Traceback (most recent call last):
  File "/home/app/pyvenv/lib/python3.9/site-packages/django/core/handlers/exception.py", line 34, in inner
    response = get_response(request)
  File "/home/app/pyvenv/lib/python3.9/site-packages/django/core/handlers/base.py", line 115, in _get_response
    response = self.process_exception_by_middleware(e, request)
  File "/home/app/pyvenv/lib/python3.9/site-packages/django/core/handlers/base.py", line 113, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/home/app/pyvenv/lib/python3.9/site-packages/django/views/decorators/cache.py", line 44, in _wrapped_view_func
    response = view_func(request, *args, **kwargs)
  File "/home/app/pyvenv/lib/python3.9/site-packages/django/contrib/admin/sites.py", line 399, in login
    return LoginView.as_view(**defaults)(request)
  File "/home/app/pyvenv/lib/python3.9/site-packages/django/views/generic/base.py", line 71, in view
    return self.dispatch(request, *args, **kwargs)
  File "/home/app/pyvenv/lib/python3.9/site-packages/django/utils/decorators.py", line 45, in _wrapper
    return bound_method(*args, **kwargs)
  File "/home/app/pyvenv/lib/python3.9/site-packages/django/views/decorators/debug.py", line 76, in sensitive_post_parameters_wrapper
    return view(request, *args, **kwargs)
  File "/home/app/pyvenv/lib/python3.9/site-packages/django/utils/decorators.py", line 45, in _wrapper
    return bound_method(*args, **kwargs)
  File "/home/app/pyvenv/lib/python3.9/site-packages/django/utils/decorators.py", line 142, in _wrapped_view
    response = view_func(request, *args, **kwargs)
  File "/home/app/pyvenv/lib/python3.9/site-packages/django/utils/decorators.py", line 45, in _wrapper
    return bound_method(*args, **kwargs)
  File "/home/app/pyvenv/lib/python3.9/site-packages/django/views/decorators/cache.py", line 44, in _wrapped_view_func
    response = view_func(request, *args, **kwargs)
  File "/home/app/pyvenv/lib/python3.9/site-packages/django/contrib/auth/views.py", line 61, in dispatch
    return super().dispatch(request, *args, **kwargs)
  File "/home/app/pyvenv/lib/python3.9/site-packages/django/views/generic/base.py", line 97, in dispatch
    return handler(request, *args, **kwargs)
  File "/home/app/pyvenv/lib/python3.9/site-packages/django/views/generic/edit.py", line 141, in post
    if form.is_valid():
  File "/home/app/pyvenv/lib/python3.9/site-packages/django/forms/forms.py", line 185, in is_valid
    return self.is_bound and not self.errors
  File "/home/app/pyvenv/lib/python3.9/site-packages/django/forms/forms.py", line 180, in errors
    self.full_clean()
  File "/home/app/pyvenv/lib/python3.9/site-packages/django/forms/forms.py", line 382, in full_clean
    self._clean_form()
  File "/home/app/pyvenv/lib/python3.9/site-packages/django/forms/forms.py", line 409, in _clean_form
    cleaned_data = self.clean()
  File "/home/app/pyvenv/lib/python3.9/site-packages/django/contrib/auth/forms.py", line 205, in clean
    self.user_cache = authenticate(self.request, username=username, password=password)
  File "/home/app/pyvenv/lib/python3.9/site-packages/django/contrib/auth/__init__.py", line 73, in authenticate
    user = backend.authenticate(request, **credentials)
  File "/home/app/pyvenv/lib/python3.9/site-packages/django/contrib/auth/backends.py", line 20, in authenticate
    user = UserModel._default_manager.get_by_natural_key(username)
  File "/home/app/pyvenv/lib/python3.9/site-packages/django/contrib/auth/base_user.py", line 44, in get_by_natural_key
    return self.get(**{self.model.USERNAME_FIELD: username})
  File "/home/app/pyvenv/lib/python3.9/site-packages/django/db/models/manager.py", line 82, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "/home/app/pyvenv/lib/python3.9/site-packages/django/db/models/query.py", line 402, in get
    num = len(clone)
  File "/home/app/pyvenv/lib/python3.9/site-packages/django/db/models/query.py", line 256, in __len__
    self._fetch_all()
  File "/home/app/pyvenv/lib/python3.9/site-packages/django/db/models/query.py", line 1242, in _fetch_all
    self._result_cache = list(self._iterable_class(self))
  File "/home/app/pyvenv/lib/python3.9/site-packages/django/db/models/query.py", line 55, in __iter__
    results = compiler.execute_sql(chunked_fetch=self.chunked_fetch, chunk_size=self.chunk_size)
  File "/home/app/pyvenv/lib/python3.9/site-packages/django/db/models/sql/compiler.py", line 1175, in execute_sql
    return list(result)
  File "/home/app/pyvenv/lib/python3.9/site-packages/django/db/models/sql/compiler.py", line 1554, in cursor_iter
    for rows in iter((lambda: cursor.fetchmany(itersize)), sentinel):
  File "/home/app/pyvenv/lib/python3.9/site-packages/django/db/models/sql/compiler.py", line 1554, in <lambda>
    for rows in iter((lambda: cursor.fetchmany(itersize)), sentinel):
  File "/home/app/pyvenv/lib/python3.9/site-packages/django/db/utils.py", line 96, in inner
    return func(*args, **kwargs)
  File "/home/app/pyvenv/lib/python3.9/site-packages/django/db/backends/postgresql/utils.py", line 6, in utc_tzinfo_factory
    raise AssertionError("database connection isn't set to UTC")

See: https://stackoverflow.com/a/68025007
2021-11-03 19:48:37 +05:30
PCoder ae3c156df7 Update Django from 2.1.4 to 2.2.16 2020-09-02 20:48:34 +05:30
PCoder 1efe6ee078 Add missing parenthesis 2019-09-26 15:45:54 +05:30
PCoder 9703eb6538 Set name, realm and seed for superusers also 2019-09-26 15:26:10 +05:30
PCoder 273a1acf01 Set ALLOWED_HOSTS from .env 2019-09-26 15:16:07 +05:30
PCoder 3f37fe4826 Set username for non-superusers only 2019-09-26 15:10:16 +05:30
nico14571 79458d54cb Merge branch 'patch-1' into 'master'
Update README.md (Verify using http POST Section)

See merge request ungleich-public/ungleich-otp!3
2019-06-08 12:52:27 +02:00
Ahmed Bilal 0301e1a7e8 Update README.md 2019-06-08 09:36:55 +02:00
Ahmed Bilal 2f2d0c592e Update README.md 2019-06-07 20:06:17 +02:00
wcolmenares 9e3aad1316 updated readme 2019-03-13 19:56:49 -04:00
wcolmenares 7a581e8357 fix verify someone else token 2019-03-11 23:05:13 -04:00
nico14571 84afaaa56d Merge branch 'master' into 'master'
Decouple config and add logging

See merge request ungleich-public/ungleich-otp!1
2019-02-11 11:12:53 +01:00
PCoder d38b5378b0 More logging 2019-02-11 01:06:21 +01:00
PCoder 1b4107306b Add logging 2019-02-10 23:52:52 +01:00
PCoder d598b9584e Add logs directory 2019-02-10 23:37:15 +01:00
PCoder 636b3d3052 Log errors/debug messages 2019-02-10 23:33:28 +01:00
PCoder fd0f0b56bd Load configs from .env and add basic logging config 2019-02-10 23:32:59 +01:00
PCoder e45e5989db Add python-decouple requirement 2019-02-10 23:31:48 +01:00
PCoder 5890d95c59 Update .gitignore 2019-02-10 23:31:26 +01:00
PCoder 27b880ef77 Add values to .env.sample 2019-02-10 23:30:17 +01:00
PCoder 27ba06ce26 Add .env.sample 2019-02-10 23:29:53 +01:00
Nico Schottelius 1a54de525b Cleanup docs, remove debug print 2019-02-08 20:00:28 +01:00
Nico Schottelius 97b612e626 Update doc, run actual authentication on verify 2019-02-08 19:25:07 +01:00
10 changed files with 166 additions and 83 deletions

4
.env.sample Normal file
View File

@ -0,0 +1,4 @@
SECRET_KEY=ldskjflkdsnejnjsdnf
DEBUG=False
ENABLE_DEBUG_LOG=True
ALLOWED_HOSTS=localhost,.ungleich.ch

3
.gitignore vendored
View File

@ -1,3 +1,6 @@
.idea/
venv/ venv/
db.sqlite3 db.sqlite3
aux/ aux/
__pycache__/
static/

112
README.md
View File

@ -18,63 +18,57 @@ Related documentation:
## Overview ## ## Overview ##
This repository the reference implementation of the ungleichotp This repository the reference implementation of the ungleichotp server.
server.
## Using the ungleichotpclient ## ## Using the ungleichotpclient ##
The client can be used to test the ungleich-otp-server.
All client commands need the parameters --auth-name and --auth-realm.
Also either --auth-seed or --auth-token needs to be specified.
``` ```
python manage.py ungleichotpclient create \ python manage.py ungleichotpclient create \
--server-url https://otp.ungleich.ch/ungleichotp/ --server-url https://otp.ungleich.ch/ungleichotp/
--name admin --auth-name admin
--realm ungleich-admin --auth-realm ungleich-admin
--seed AVALIDSEED [--auth-seed THESEEDFORADMIN]
[--auth-token THECURRENTTOKEN]
``` ```
Assuming you want to verify ### Creating new users
(name=ipv6only, realm=ungleich-intern, token=498593) is a
valid triple and you do have credentials to access ungleich-otp
(name=info@ungleich.ch, realm=ungleich-admin, seed=PZKBPTHDGSLZBKIZ),
then the following call will verify the token:
``` ```
UNGLEICHOTPNAME=info@ungleich.ch \ --name USERNAME --realm REALMOFUSER create
UNGLEICHOTPREALM=ungleich-admin \ ```
UNGLEICHOTPSEED=PZKBPTHDGSLZBKIZ \
UNGLEICHOTPSERVER=http://localhost:8000/ungleichotp/verify/ \ The seed is randomly created.
python ungleichotpclient.py -n -r ungleich --token 498593
### Listing users
```
list
```
### Deleting users
```
--name USERNAME --realm REALMOFUSER delete
```
### Verifying a token is correct
Verify using:
```
--name USERNAME --realm REALMOFUSER --token TOKENTOBEVERIFIED verify
``` ```
You can also verify using a seed: You can also verify using a seed:
``` ```
UNGLEICHOTPNAME=info@ungleich.ch \ --name USERNAME --realm REALMOFUSER --seed SEEDOFUSER verify
UNGLEICHOTPREALM=ungleich-admin \
UNGLEICHOTPSEED=PZKBPTHDGSLZBKIZ \
UNGLEICHOTPSERVER=http://localhost:8000/ungleichotp/verify/ \
python ungleichotpclient.py -n -r ungleich --seed CEKXVG3235PO2HDW
```
The client requires pyotp.
## Sample 2018-12-30
create:
(venv) [23:07] line:ungleich-otp% python manage.py ungleichotpclient create --server-url http://localhost:8000/ungleichotp/ --auth-name info@ungleich.ch --auth-realm ungleich-admin --auth-seed PZKBPTHDGSLZBKIZ --name nico$(date +%s) --realm ungleich-admin
verify:
```
(venv) [23:07] line:ungleich-otp% python manage.py ungleichotpclient verify --server-url http://localhost:8000/ungleichotp/ --auth-name info@ungleich.ch --auth-realm ungleich-admin --auth-seed PZKBPTHDGSLZBKIZ --name nico1546206660 --realm ungleich-admin --seed IXTARIU4H2F574M3
```
list:
```
(venv) [23:14] line:ungleich-otp% python manage.py ungleichotpclient list --server-url http://localhost:8000/ungleichotp/ --auth-name info@ungleich.ch --auth-realm ungleich-admin --auth-seed PZKBPTHDGSLZBKIZ
``` ```
@ -106,13 +100,13 @@ All micro services that are trusted to authenticate another micro
service should have an entry in the ungleich-auth realm, which allows service should have an entry in the ungleich-auth realm, which allows
them to verify a token of somebody else. them to verify a token of somebody else.
```
| Name | Capabilities | | Name | Capabilities |
|------------------+--------------------------------------------| |------------------+--------------------------------------------|
| ungleich-admin | authenticate, create, delete, list, update | | ungleich-admin | authenticate, create, delete, list, update |
| ungleich-auth | authenticate | | ungleich-auth | authenticate, verify |
| all other realms | NO ACCESS | | all other realms | authenticate |
```
## Verify using http POST ## ## Verify using http POST ##
@ -124,12 +118,12 @@ Request JSON object:
``` ```
{ {
name: "your-name", auth_name: "auth-name",
realm: "your-realm", auth_realm: "auth-realm",
token: "current time based token", auth_token: "current time based token",
verifyname: "name that wants to be authenticated", name: "name that wants to be authenticated",
verifyrealm: "realm that wants to be authenticated", realm: "realm that wants to be authenticated",
verifytoken: "token that wants to be authenticated", token: "token that wants to be authenticated"
} }
``` ```
@ -166,8 +160,8 @@ your application.
## Limitations ## ## Limitations ##
* Name, Realm and seed are hard coded to 128 bytes length. This can be * Name, Realm and seed are hard coded to 128 bytes length.
changed, if necessary. This can be changed, if necessary.
* Only python3 support for ungleichotp * Only python3 support for ungleichotp
@ -192,8 +186,8 @@ your application.
- [x] (server) Implement creating new "User" by POST / Model based - [x] (server) Implement creating new "User" by POST / Model based
- [n] (server) Remove hard coded JSON in /verify (no - good enough for the moment) - [n] (server) Remove hard coded JSON in /verify (no - good enough for the moment)
- [x] (server) Fully rename server from ungleichotp to ungleichotpserver - [x] (server) Fully rename server from ungleichotp to ungleichotpserver
- [ ] (security) Ensure that only the right realms can verify - [x] (security) Ensure that only the right realms can verify
- [ ] (security) Ensure that only the right realms can manage - [x] (security) Ensure that only the right realms can manage
- [ ] (doc) Add proper documentation - [ ] (doc) Add proper documentation
- [ ] (server) Add tests for verify - [ ] (server) Add tests for verify
- [ ] (server) Add tests for authentication - [ ] (server) Add tests for authentication
@ -206,14 +200,18 @@ your application.
- [ ] (client) Bootstrap Django + DRF (including an object for CRUD) - [ ] (client) Bootstrap Django + DRF (including an object for CRUD)
- [ ] (client) Add custom authentication / remote auth - [ ] (client) Add custom authentication / remote auth
- [ ] (client) Show case: any realm vs. specific realm - [ ] (client) Show case: any realm vs. specific realm
- [ ] (library) Write a "client library" that can use ungleichotp - [x] (library) Write a "client library" that can use ungleichotp
- [ ] (library) extract generic parts from server - [x] (library) extract generic parts from server
- [ ] (library) upload to pypi - [ ] (library) upload to pypi
## Changelog ## Changelog
### 0.8, 2019-02-08
* Verify needed to call super()
### 0.6, 2018-11-18 ### 0.6, 2018-11-18
* Reuse TokenSerializer for VerifySerializer logic * Reuse TokenSerializer for VerifySerializer logic

2
logs/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*
!.gitignore

View File

@ -14,16 +14,16 @@ class Command(BaseCommand):
parser.add_argument('--server-url', required=True) parser.add_argument('--server-url', required=True)
# For creating / verifying # For creating / verifying
parser.add_argument('--name') parser.add_argument('--name', help="Name to create/verify")
parser.add_argument('--realm') parser.add_argument('--realm', help="Realm for create/verify")
parser.add_argument('--token') parser.add_argument('--token', help="Token for create/verify")
parser.add_argument('--seed') parser.add_argument('--seed', help="Seed for create/verify")
# How to authenticate against ungleich-otp # How to authenticate against ungleich-otp
parser.add_argument('--auth-name', required=True) parser.add_argument('--auth-name', required=True, help="Name for auth")
parser.add_argument('--auth-realm', required=True) parser.add_argument('--auth-realm', required=True, help="Realm for auth")
parser.add_argument('--auth-token') parser.add_argument('--auth-token', help="Token for auth")
parser.add_argument('--auth-seed') parser.add_argument('--auth-seed', help="Seed for auth")
parser.add_argument('command', choices=['create', parser.add_argument('command', choices=['create',
'delete', 'delete',

View File

@ -2,6 +2,11 @@ from django.db import models
from django.contrib.auth.models import AbstractUser from django.contrib.auth.models import AbstractUser
from rest_framework import exceptions from rest_framework import exceptions
from rest_framework import authentication from rest_framework import authentication
import json
import logging
import pyotp
logger = logging.getLogger(__name__)
class OTPSeed(AbstractUser): class OTPSeed(AbstractUser):
@ -17,7 +22,13 @@ class OTPSeed(AbstractUser):
""" """
inject username to ensure it stays unique / is setup at all inject username to ensure it stays unique / is setup at all
""" """
self.username = "{}@{}".format(self.name, self.realm) if not self.is_superuser:
self.username = "{}@{}".format(self.name, self.realm)
else:
self.name = self.username
self.realm = "ungleich-admin"
self.seed = pyotp.random_base32()
super().save(*args, **kwargs) super().save(*args, **kwargs)
def __str__(self): def __str__(self):
@ -27,19 +38,20 @@ from otpauth.serializer import TokenSerializer
class OTPAuthentication(authentication.BaseAuthentication): class OTPAuthentication(authentication.BaseAuthentication):
def authenticate(self, request): def authenticate(self, request):
logger.debug("in authenticate {}".format(json.dumps(request.data)))
serializer = TokenSerializer(data=request.data) serializer = TokenSerializer(data=request.data)
if serializer.is_valid(): if serializer.is_valid():
print("trying to save... {}".format(serializer))
instance, token = serializer.save() instance, token = serializer.save()
else: else:
print("Invalide serialize,") logger.error("serializer is invalid")
raise exceptions.AuthenticationFailed() raise exceptions.AuthenticationFailed()
# not dealing with admin realm -> can only be auth [see serializer] # not dealing with admin realm -> can only be auth [see serializer]
if not instance.realm == "ungleich-admin": if not instance.realm == "ungleich-admin":
if not request.path == "/ungleichotp/verify/": if not request.path == "/ungleichotp/verify/":
logger.debug("request.path is not /ungleichotp/verify/")
raise exceptions.AuthenticationFailed() raise exceptions.AuthenticationFailed()
print("AUTH DONE: {} - {}".format(request.path, instance)) logger.debug("AUTH DONE: {} - {}".format(request.path, instance))
return (instance, token) return (instance, token)

View File

@ -1,8 +1,11 @@
import logging
import pyotp import pyotp
import otpauth import otpauth
from rest_framework import serializers, exceptions from rest_framework import serializers, exceptions
from otpauth.models import OTPSeed from otpauth.models import OTPSeed
logger = logging.getLogger(__name__)
# For accessing / modifying the data -- currently unused # For accessing / modifying the data -- currently unused
class OTPSerializer(serializers.ModelSerializer): class OTPSerializer(serializers.ModelSerializer):
class Meta: class Meta:
@ -31,22 +34,34 @@ class TokenSerializer(serializers.Serializer):
auth_realm = self.validated_data.get(self.realm_name) auth_realm = self.validated_data.get(self.realm_name)
# only 2 special realms can login # only 2 special realms can login
if not auth_realm in ["ungleich-admin", "ungleich-auth" ]: # if not auth_realm in ["ungleich-admin", "ungleich-auth" ]:
raise exceptions.AuthenticationFailed() # logger.error("Auth-realm is neither ungleich-admin "
# "nor ungleich-auth".format()
# )
# raise exceptions.AuthenticationFailed()
print("auth: [{}]{}@'{}' {} + {})".format(self.name_name, auth_name, auth_realm, auth_token, self.validated_data)) logger.debug("auth: [{}]{}@'{}' {} + {})".format(
self.name_name, auth_name, auth_realm,
auth_token, self.validated_data
))
# 1. Verify that the connection might authenticate # 1. Verify that the connection might authenticate
try: try:
logger.debug("Checking in db for name:{} & realm:{}".format(
auth_name, auth_realm
))
db_instance = otpauth.models.OTPSeed.objects.get(name=auth_name, realm=auth_realm) db_instance = otpauth.models.OTPSeed.objects.get(name=auth_name, realm=auth_realm)
except (OTPSeed.MultipleObjectsReturned, OTPSeed.DoesNotExist): except (OTPSeed.MultipleObjectsReturned, OTPSeed.DoesNotExist):
print("does not exist") logger.error("OTPSeed name: {}, realm: {} does not exist".format(
auth_name, auth_realm
))
raise exceptions.AuthenticationFailed() raise exceptions.AuthenticationFailed()
logger.debug("Found seed: {}".format(db_instance.seed))
totp = pyotp.TOTP(db_instance.seed) totp = pyotp.TOTP(db_instance.seed)
print("calculated token = {}".format(totp.now())) logger.debug("calculated token = {}".format(totp.now()))
if not totp.verify(auth_token, valid_window=3): if not totp.verify(auth_token, valid_window=3):
logger.error("totp not verified")
raise exceptions.AuthenticationFailed() raise exceptions.AuthenticationFailed()
return (db_instance, auth_token) return (db_instance, auth_token)
@ -65,4 +80,8 @@ class VerifySerializer(TokenSerializer):
auth_realm = self.validated_data.get("auth_realm") auth_realm = self.validated_data.get("auth_realm")
if not auth_realm == "ungleich-auth": if not auth_realm == "ungleich-auth":
logger.error("Auth-realm is not ungleich-auth")
raise exceptions.AuthenticationFailed() raise exceptions.AuthenticationFailed()
# Do the authentication part
super().save()

View File

@ -7,6 +7,11 @@ from rest_framework.response import Response
from django.http import JsonResponse from django.http import JsonResponse
from otpauth.serializer import VerifySerializer, OTPSerializer, TokenSerializer from otpauth.serializer import VerifySerializer, OTPSerializer, TokenSerializer
from otpauth.models import OTPSeed from otpauth.models import OTPSeed
import json
import logging
logger = logging.getLogger(__name__)
class OTPVerifyViewSet(viewsets.ModelViewSet): class OTPVerifyViewSet(viewsets.ModelViewSet):
serializer_class = OTPSerializer serializer_class = OTPSerializer
@ -20,7 +25,7 @@ class OTPVerifyViewSet(viewsets.ModelViewSet):
Now we inspect the payload and return ok, Now we inspect the payload and return ok,
if they also verify if they also verify
""" """
logger.debug("in verify {}".format(json.dumps(request.data)))
serializer = VerifySerializer(data=request.data) serializer = VerifySerializer(data=request.data)
if serializer.is_valid(): if serializer.is_valid():
serializer.save() serializer.save()

View File

@ -1,9 +1,10 @@
pyotp>=2.2.6 pyotp>=2.2.6
django>=2.1.2 django==2.2.16
djangorestframework djangorestframework
python-decouple>=3.1
# DB # DB
psycopg2 psycopg2>=2.8,<2.9
# Recommended # Recommended
markdown markdown

View File

@ -10,6 +10,8 @@ For the full list of settings and their values, see
https://docs.djangoproject.com/en/2.1/ref/settings/ https://docs.djangoproject.com/en/2.1/ref/settings/
""" """
from decouple import config, Csv
import os import os
# Build paths inside the project like this: os.path.join(BASE_DIR, ...) # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
@ -20,7 +22,7 @@ BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# See https://docs.djangoproject.com/en/2.1/howto/deployment/checklist/ # See https://docs.djangoproject.com/en/2.1/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret! # SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'h^*!&u7yaac_6t02kk4de%$aagp6_j#+_wnw3@rqu6os0tlv#r' SECRET_KEY = config('SECRET_KEY')
# SECURITY WARNING: don't run with debug turned on in production! # SECURITY WARNING: don't run with debug turned on in production!
@ -129,10 +131,8 @@ DEBUG_DATABASES = {
} }
} }
DEBUG = False DEBUG = config('DEBUG', False, cast=bool)
ALLOWED_HOSTS = [ ALLOWED_HOSTS = config('ALLOWED_HOSTS', default='localhost', cast=Csv())
".ungleich.ch"
]
DATABASES = { DATABASES = {
'default': { 'default': {
@ -146,6 +146,45 @@ BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
STATIC_ROOT = os.path.join(BASE_DIR, "static") STATIC_ROOT = os.path.join(BASE_DIR, "static")
STATIC_URL = '/static/' STATIC_URL = '/static/'
LOGGING = {
'disable_existing_loggers': False,
'version': 1,
'formatters': {
'standard': {
'format': '%(asctime)s %(levelname)s %(name)s: %(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', cast=bool, default=False):
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': 'DEBUG',
'propagate': True
}
}
loggers_dict.update(logger_item)
LOGGING['loggers'] = loggers_dict
if "DEBUG" in os.environ: if "DEBUG" in os.environ: