From 2e228f3a0d73769f173d8e29b903ea786e1f940f Mon Sep 17 00:00:00 2001 From: Nico Schottelius Date: Sun, 18 Nov 2018 15:41:47 +0100 Subject: [PATCH] Phase in ungleichotp --- README.md | 19 +++++-- ungleichotp/ungleichotp.py | 70 +++++++++++++++++++++---- ungleichotpserver/otpauth/serializer.py | 2 +- 3 files changed, 74 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index c12726c..31ba2e8 100644 --- a/README.md +++ b/README.md @@ -220,6 +220,13 @@ name on a realm authenticated successfully. The associated permissions your application. +## Limitations ## + +* Name, Realm and seed are hard coded to 128 bytes length. This can be + changed, if necessary. +* Only python3 support for ungleichotp + + ## TODOs - [x] (server) Serialize / input request @@ -240,22 +247,24 @@ your application. - [x] (server) Use Custom authentication - needs to have a user! - [x] (server) Implement creating new "User" by POST / Model based - [n] (server) Remove hard coded JSON in /verify (no - good enough for the moment) +- [x] (server) Fully rename server from ungleichotp to ungleichotpserver - [ ] (security) Ensure that only the right realms can verify - [ ] (security) Ensure that only the right realms can manage +- [ ] (doc) Add proper documentation - [ ] (server) Add tests for verify - [ ] (server) Add tests for authentication -- [ ] (doc) Add proper documentation - [ ] (server) move totp constants into settings - [ ] (server) move field lengths into settings +- [ ] (server) Document how admin vs. rest works - [ ] (server, client) Make settings adjustable by environment - k8s/docker compatible - [ ] (server, client) Read DB from outside (?) (fallback to sqlite) +- [x] (client) Establish auth using urllib +- [ ] (client) Bootstrap Django + DRF (including an object for CRUD) +- [ ] (client) Add custom authentication / remote auth +- [ ] (client) Show case: any realm vs. specific realm - [ ] (library) Write a "client library" that can use ungleichotp - [ ] (library) extract generic parts from server - [ ] (library) upload to pypi -- [ ] (client) Bootstrap Django + DRF (including an object for CRUD) -- [ ] (client) Add custom authentication / remote auth -- [ ] (server) Document how admin vs. rest works -- [ ] (server) Fully rename server from ungleichotp to ungleichotpserver diff --git a/ungleichotp/ungleichotp.py b/ungleichotp/ungleichotp.py index c417176..2e482d3 100644 --- a/ungleichotp/ungleichotp.py +++ b/ungleichotp/ungleichotp.py @@ -1,16 +1,64 @@ from django.contrib.auth.models import User -from rest_framework import authentication -from rest_framework import exceptions +from rest_framework import authentication, exceptions, serializers -class ExampleAuthentication(authentication.BaseAuthentication): +import urllib.request +import pyotp +import json + +# For parsing +class TokenSerializer(serializers.Serializer): + name = serializers.CharField(max_length=128) + token = serializers.CharField(max_length=128) + realm = serializers.CharField(max_length=128) + + token_name = 'token' + name_name = 'name' + realm_name = 'realm' + + def __init__(self, name, realm, seed, serverurl, *args, **kwargs): + self.name = name + self.realm = realm + self.seed = seed + self.serverurl = serverurl + + super(serializers.Serializer, self).__init__(*args, **kwargs) + + def save(self): + to_send = {} + + # Client credentials to be verified + to_send['verifytoken'] = self.validated_data.get(self.token_name) + to_send['verifyname'] = self.validated_data.get(self.name_name) + to_send['verifyrealm'] = self.validated_data.get(self.real_name) + + # Our credentials + to_send['token'] = pyotp.TOTP(self.seed) + to_send['name'] = self.name + to_send['realm'] = self.realm + + 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 not f.status == 200: + raise exceptions.AuthenticationFailed() + + return True + + +class OTPAuthentication(authentication.BaseAuthentication): def authenticate(self, request): - username = request.META.get('X_USERNAME') - if not username: - return None + serializer = TokenSerializer(data=request.data) - try: - user = User.objects.get(username=username) - except User.DoesNotExist: - raise exceptions.AuthenticationFailed('No such user') + if serializer.is_valid(): + print("trying to save... {}".format(serializer)) + user, token = serializer.save() + else: + raise exceptions.AuthenticationFailed() - return (user, None) + return (user, token) diff --git a/ungleichotpserver/otpauth/serializer.py b/ungleichotpserver/otpauth/serializer.py index 05bd0f5..0683a98 100644 --- a/ungleichotpserver/otpauth/serializer.py +++ b/ungleichotpserver/otpauth/serializer.py @@ -29,7 +29,7 @@ class TokenSerializer(serializers.Serializer): name_in = self.validated_data.get(self.name_name) 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)) + print("auth: {} {} {} ({} {} {} -- {})".format(token_in, name_in, realm_in, self.token_name, self.name_name, self.realm_name, self.validated_data)) # 1. Verify that the connection might authenticate try: