From 27ba06ce265cd7147fd43eb2cfe1062deb3889ba Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 10 Feb 2019 23:29:53 +0100 Subject: [PATCH 1/9] Add .env.sample --- .env.sample | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 .env.sample diff --git a/.env.sample b/.env.sample new file mode 100644 index 0000000..e69de29 From 27b880ef77e7ba338d9da676b1ac77e66b9e4983 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 10 Feb 2019 23:30:17 +0100 Subject: [PATCH 2/9] Add values to .env.sample --- .env.sample | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.env.sample b/.env.sample index e69de29..2059120 100644 --- a/.env.sample +++ b/.env.sample @@ -0,0 +1,3 @@ +SECRET_KEY=ldskjflkdsnejnjsdnf +DEBUG=False +ENABLE_DEBUG_LOG=True \ No newline at end of file From 5890d95c59cfc5859f0beccdb571a3451d4ca084 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 10 Feb 2019 23:31:26 +0100 Subject: [PATCH 3/9] Update .gitignore --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index d2265c5..2e99c1e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +.idea/ venv/ db.sqlite3 aux/ +__pycache__/ +static/ From e45e5989dbfb93ca6eec589f9d588905d893058b Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 10 Feb 2019 23:31:48 +0100 Subject: [PATCH 4/9] Add python-decouple requirement --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index 13085cd..b347564 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,7 @@ pyotp>=2.2.6 django>=2.1.2 djangorestframework +python-decouple>=3.1 # DB psycopg2 From fd0f0b56bda08f0fecd565a15712940b2da92e5f Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 10 Feb 2019 23:32:59 +0100 Subject: [PATCH 5/9] Load configs from .env and add basic logging config --- ungleichotpserver/settings.py | 45 +++++++++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/ungleichotpserver/settings.py b/ungleichotpserver/settings.py index 6828a5a..2be3f0f 100644 --- a/ungleichotpserver/settings.py +++ b/ungleichotpserver/settings.py @@ -10,6 +10,8 @@ For the full list of settings and their values, see https://docs.djangoproject.com/en/2.1/ref/settings/ """ +from decouple import config, Csv + import os # 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/ # 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! @@ -129,7 +131,7 @@ DEBUG_DATABASES = { } } -DEBUG = False +DEBUG = config('DEBUG', False, cast=bool) ALLOWED_HOSTS = [ ".ungleich.ch" ] @@ -146,6 +148,45 @@ BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) STATIC_ROOT = os.path.join(BASE_DIR, "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: From 636b3d305263ac1304cd24ddbfbaf548454bd83a Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 10 Feb 2019 23:33:22 +0100 Subject: [PATCH 6/9] Log errors/debug messages --- otpauth/serializer.py | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/otpauth/serializer.py b/otpauth/serializer.py index 21edb8f..2b7e74e 100644 --- a/otpauth/serializer.py +++ b/otpauth/serializer.py @@ -1,8 +1,11 @@ +import logging import pyotp import otpauth from rest_framework import serializers, exceptions from otpauth.models import OTPSeed +logger = logging.getLogger(__name__) + # For accessing / modifying the data -- currently unused class OTPSerializer(serializers.ModelSerializer): class Meta: @@ -32,21 +35,30 @@ class TokenSerializer(serializers.Serializer): # only 2 special realms can login if not auth_realm in ["ungleich-admin", "ungleich-auth" ]: + 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 try: db_instance = otpauth.models.OTPSeed.objects.get(name=auth_name, realm=auth_realm) 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() 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): + logger.error("totp not verified") raise exceptions.AuthenticationFailed() return (db_instance, auth_token) @@ -65,6 +77,7 @@ class VerifySerializer(TokenSerializer): auth_realm = self.validated_data.get("auth_realm") if not auth_realm == "ungleich-auth": + logger.error("Auth-realm is not ungleich-auth") raise exceptions.AuthenticationFailed() # Do the authentication part From d598b9584ec413bdcb99f2d4338bc33340d42fb3 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 10 Feb 2019 23:37:15 +0100 Subject: [PATCH 7/9] Add logs directory --- logs/.gitignore | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 logs/.gitignore diff --git a/logs/.gitignore b/logs/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/logs/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore From 1b4107306b6b6a054646bbc8ec751b8a31c078a1 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 10 Feb 2019 23:52:52 +0100 Subject: [PATCH 8/9] Add logging --- otpauth/models.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/otpauth/models.py b/otpauth/models.py index de2ad3e..6e1898e 100644 --- a/otpauth/models.py +++ b/otpauth/models.py @@ -2,6 +2,10 @@ from django.db import models from django.contrib.auth.models import AbstractUser from rest_framework import exceptions from rest_framework import authentication +import json +import logging + +logger = logging.getLogger(__name__) class OTPSeed(AbstractUser): @@ -27,17 +31,20 @@ from otpauth.serializer import TokenSerializer class OTPAuthentication(authentication.BaseAuthentication): def authenticate(self, request): + logger.debug("in authenticate {}".format(json.dumps(request.data))) serializer = TokenSerializer(data=request.data) if serializer.is_valid(): instance, token = serializer.save() else: + logger.error("serializer is invalid") raise exceptions.AuthenticationFailed() # not dealing with admin realm -> can only be auth [see serializer] if not instance.realm == "ungleich-admin": if not request.path == "/ungleichotp/verify/": + logger.debug("request.path is not /ungleichotp/verify/") raise exceptions.AuthenticationFailed() - # print("AUTH DONE: {} - {}".format(request.path, instance)) + logger.debug("AUTH DONE: {} - {}".format(request.path, instance)) return (instance, token) From d38b5378b0c6ceb7b743bbb476ff6a65084d86ac Mon Sep 17 00:00:00 2001 From: PCoder Date: Mon, 11 Feb 2019 01:06:21 +0100 Subject: [PATCH 9/9] More logging --- otpauth/serializer.py | 5 ++++- otpauth/views.py | 7 ++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/otpauth/serializer.py b/otpauth/serializer.py index 2b7e74e..691e5a5 100644 --- a/otpauth/serializer.py +++ b/otpauth/serializer.py @@ -47,13 +47,16 @@ class TokenSerializer(serializers.Serializer): # 1. Verify that the connection might authenticate 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) except (OTPSeed.MultipleObjectsReturned, OTPSeed.DoesNotExist): logger.error("OTPSeed name: {}, realm: {} does not exist".format( auth_name, auth_realm )) raise exceptions.AuthenticationFailed() - + logger.debug("Found seed: {}".format(db_instance.seed)) totp = pyotp.TOTP(db_instance.seed) logger.debug("calculated token = {}".format(totp.now())) diff --git a/otpauth/views.py b/otpauth/views.py index 10e7351..4134bc3 100644 --- a/otpauth/views.py +++ b/otpauth/views.py @@ -7,6 +7,11 @@ from rest_framework.response import Response from django.http import JsonResponse from otpauth.serializer import VerifySerializer, OTPSerializer, TokenSerializer from otpauth.models import OTPSeed +import json +import logging + +logger = logging.getLogger(__name__) + class OTPVerifyViewSet(viewsets.ModelViewSet): serializer_class = OTPSerializer @@ -20,7 +25,7 @@ class OTPVerifyViewSet(viewsets.ModelViewSet): Now we inspect the payload and return ok, if they also verify """ - + logger.debug("in verify {}".format(json.dumps(request.data))) serializer = VerifySerializer(data=request.data) if serializer.is_valid(): serializer.save()