In between hacking commit

Trying to rip out the auth part
This commit is contained in:
Nico Schottelius 2018-12-30 22:30:11 +01:00
parent 2de270859a
commit 1b85b28935
8 changed files with 183 additions and 21 deletions

View file

@ -18,13 +18,21 @@ Related documentation:
## Overview ## ## Overview ##
This repository contains... This repository the reference implementation of the ungleichotp
server.
* ungleichotpserver: the reference implementation of the ungleichotp server
* ungleichotpclient.py: a sample implementation of an ungleichotp client
## Verifying a token using ungleichotpclient.py ##
## Using the ungleichotpclient ##
```
python manage.py ungleichotpclient create \
--server-url https://otp.ungleich.ch/ungleichotp/
--name admin
--realm ungleich-admin
--seed AVALIDSEED
```
Assuming you want to verify Assuming you want to verify
(name=ipv6only, realm=ungleich-intern, token=498593) is a (name=ipv6only, realm=ungleich-intern, token=498593) is a
@ -40,7 +48,7 @@ UNGLEICHOTPSERVER=http://localhost:8000/ungleichotp/verify/ \
python ungleichotpclient.py -n -r ungleich --token 498593 python ungleichotpclient.py -n -r ungleich --token 498593
``` ```
You can also veriy using a seed: You can also verify using a seed:
``` ```
UNGLEICHOTPNAME=info@ungleich.ch \ UNGLEICHOTPNAME=info@ungleich.ch \

0
otpauth/management/__init__.py Executable file
View file

View file

View file

@ -0,0 +1,91 @@
from django.conf import settings
from django.core.management.base import BaseCommand
import pyotp
import json
import urllib.request
import urllib.error
import sys
class Command(BaseCommand):
help = 'Access ungleichotp'
def add_arguments(self, parser):
parser.add_argument('--server-url', required=True)
# For creating / verifying
parser.add_argument('--name')
parser.add_argument('--realm')
parser.add_argument('--token')
# How to authenticate against ungleich-otp
parser.add_argument('--auth-name', required=True)
parser.add_argument('--auth-realm', required=True)
parser.add_argument('--auth-token')
parser.add_argument('--auth-seed')
parser.add_argument('command', choices=['create',
'delete',
'list',
'verify'], help='Action to take')
def handle(self, *args, **options):
command_to_verb = { 'create': 'POST',
'delete': 'DELETE',
'list': 'GET' }
if not options['auth_token']:
if not options['auth_seed']:
print("Either token or seed are required")
sys.exit(1)
else:
options['auth_token'] = pyotp.TOTP(options['auth_seed']).now()
to_send = {}
# Our credentials
to_send['auth_token'] = options['auth_token']
to_send['auth_name'] = options['auth_name']
to_send['auth_realm'] = options['auth_realm']
if options['command'] in ["create", "verify"]:
if not options['name'] or not options['realm']:
print("Need to specify --name and --realm")
sys.exit(1)
if options['command'] == "verify" and not options['token']:
print("Need to specify --token for verify")
sys.exit(1)
# Client credentials to be verified
to_send['name'] = options['name']
to_send['realm'] = options['realm']
to_send['token'] = options['token']
if options['command'] == "verify":
options['server_url'] = "{}/verify".format(options['server_url'])
print("{} {} {}".format(args, options, to_send))
self.rest_send(options['server_url'], to_send)
# Logically: how can we create if we already send realm/name/token ?
# Need to use auth* (?)
@staticmethod
def rest_send(serverurl, to_send):
data = json.dumps(to_send).encode("utf-8")
req = urllib.request.Request(url=serverurl,
data=data,
headers={'Content-Type': 'application/json'},
method='POST')
f = urllib.request.urlopen(req)
if f.status == 200:
return True
return False

View file

@ -16,6 +16,14 @@ class OTPSeed(AbstractUser):
def __str__(self): def __str__(self):
return "'{}'@{}".format(self.name, self.realm) return "'{}'@{}".format(self.name, self.realm)
@property
def auth_name(self):
print("authname: {}".format(self))
@auth_name.setter
def auth_name(self):
print("authname: {}".format(self))
from otpauth.serializer import TokenSerializer from otpauth.serializer import TokenSerializer
class OTPAuthentication(authentication.BaseAuthentication): class OTPAuthentication(authentication.BaseAuthentication):
@ -26,6 +34,7 @@ class OTPAuthentication(authentication.BaseAuthentication):
print("trying to save... {}".format(serializer)) print("trying to save... {}".format(serializer))
user, token = serializer.save() user, token = serializer.save()
else: else:
print("Invalide serialize,")
raise exceptions.AuthenticationFailed() raise exceptions.AuthenticationFailed()
return (user, token) return (user, token)

View file

@ -3,7 +3,7 @@ import otpauth
from rest_framework import serializers, exceptions from rest_framework import serializers, exceptions
from otpauth.models import OTPSeed from otpauth.models import OTPSeed
# For accessing / modifying the data # For accessing / modifying the data -- currently unused
class OTPSerializer(serializers.ModelSerializer): class OTPSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = OTPSeed model = OTPSeed
@ -14,32 +14,46 @@ class OTPSerializer(serializers.ModelSerializer):
validated_data['seed'] = pyotp.random_base32() validated_data['seed'] = pyotp.random_base32()
return OTPSeed.objects.create(**validated_data) return OTPSeed.objects.create(**validated_data)
# For parsing authentication
class TokenSerializer(serializers.Serializer): class TokenSerializer(serializers.Serializer):
name = serializers.CharField(max_length=128) name = serializers.CharField(max_length=128)
token = serializers.CharField(max_length=128)
realm = serializers.CharField(max_length=128) realm = serializers.CharField(max_length=128)
token_name = 'token' auth_name = serializers.CharField(max_length=128)
name_name = 'name' auth_token = serializers.CharField(max_length=128)
realm_name = 'realm' auth_realm = serializers.CharField(max_length=128)
def create(self, validated_data):
validated_data['seed'] = pyotp.random_base32()
return OTPSeed.objects.create(**validated_data)
def update(self, instance, validated_data):
instance.name = validated_data.get('name', instance.name)
instance.realm = validated_data.get('realm', instance.realm)
instance.save()
return instance
def save(self): def save(self):
token_in = self.validated_data.get(self.token_name) name_in = self.validated_data.get("name")
name_in = self.validated_data.get(self.name_name) realm_in = self.validated_data.get("realm")
realm_in = self.validated_data.get(self.realm_name)
print("auth: {} {} {} ({} {} {} -- {})".format(token_in, name_in, realm_in, self.token_name, self.name_name, self.realm_name, self.validated_data)) auth_token = self.validated_data.get("auth_token")
auth_name = self.validated_data.get("auth_name")
auth_realm = self.validated_data.get("auth_realm")
print("auth: {}@'{}' {} + {})".format(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:
db_instance = otpauth.models.OTPSeed.objects.get(name=name_in, realm=realm_in) 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")
raise exceptions.AuthenticationFailed() raise exceptions.AuthenticationFailed()
totp = pyotp.TOTP(db_instance.seed) totp = pyotp.TOTP(db_instance.seed)
print("calculated token = {}".format(totp.now()))
if not totp.verify(token_in, valid_window=3): if not totp.verify(auth_token, valid_window=3):
raise exceptions.AuthenticationFailed() raise exceptions.AuthenticationFailed()
return (db_instance, token_in) return (db_instance, token_in)
@ -53,3 +67,12 @@ class VerifySerializer(TokenSerializer):
token_name = 'verifytoken' token_name = 'verifytoken'
name_name = 'verifyname' name_name = 'verifyname'
realm_name = 'verifyrealm' realm_name = 'verifyrealm'
# token_name = 'token'
# name_name = 'name'
# realm_name = 'realm'
# auth_token_name = 'auth_token'
# auth_name_name = 'auth_name'
# auth_realm_name = 'auth_realm'

View file

@ -5,14 +5,46 @@ from rest_framework.decorators import action
from rest_framework.response import Response from rest_framework.response import Response
from django.http import JsonResponse from django.http import JsonResponse
from otpauth.serializer import VerifySerializer, OTPSerializer from otpauth.serializer import VerifySerializer, OTPSerializer, TokenSerializer
from otpauth.models import OTPSeed from otpauth.models import OTPSeed
# class OTPVerifyViewSet(viewsets.ModelViewSet):
# serializer_class = OTPSerializer
# queryset = OTPSeed.objects.all()
# @action(detail=False, methods=['post'])
# def verify(self, request):
# """the standard serializer above already verified that
# (name, realm, token) is valid.
# Now we inspect the verify-prefixed names and return ok,
# if they also verify
# """
# serializer = VerifySerializer(data=request.data)
# if serializer.is_valid():
# serializer.save()
# return Response({'status': 'OK'})
# return JsonResponse(serializer.errors, status=400)
class OTPVerifyViewSet(viewsets.ModelViewSet): class OTPVerifyViewSet(viewsets.ModelViewSet):
serializer_class = OTPSerializer serializer_class = TokenSerializer
queryset = OTPSeed.objects.all() queryset = OTPSeed.objects.all()
def list(self, request):
print("Liiiiiiiisting")
data = serializers.serialize('json', self.get_queryset())
return HttpResponse(data, content_type="application/json")
obj = [o.name for o in OTPSeed.objects.all()]
obj = OTPSeed.objects.all()
return Response(obj)
# return Response({'LISTstatus': 'OK'})
@action(detail=False, methods=['post']) @action(detail=False, methods=['post'])
def verify(self, request): def verify(self, request):
"""the standard serializer above already verified that """the standard serializer above already verified that

View file

@ -143,7 +143,6 @@ DATABASES = {
# Static files # Static files
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
print(BASE_DIR)
STATIC_ROOT = os.path.join(BASE_DIR, "static") STATIC_ROOT = os.path.join(BASE_DIR, "static")
STATIC_URL = '/static/' STATIC_URL = '/static/'