2019-02-10 22:33:22 +00:00
|
|
|
import logging
|
2018-11-17 10:21:35 +00:00
|
|
|
import pyotp
|
2018-11-17 16:46:16 +00:00
|
|
|
import otpauth
|
2018-12-30 17:20:24 +00:00
|
|
|
from rest_framework import serializers, exceptions
|
|
|
|
from otpauth.models import OTPSeed
|
2018-11-17 10:21:35 +00:00
|
|
|
|
2019-02-10 22:33:22 +00:00
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
2018-12-30 21:30:11 +00:00
|
|
|
# For accessing / modifying the data -- currently unused
|
2018-11-17 20:54:59 +00:00
|
|
|
class OTPSerializer(serializers.ModelSerializer):
|
|
|
|
class Meta:
|
|
|
|
model = OTPSeed
|
2018-11-17 21:15:17 +00:00
|
|
|
fields = ('name', 'realm', 'seed')
|
|
|
|
read_only_fields = ('seed',)
|
|
|
|
|
2018-11-17 21:28:17 +00:00
|
|
|
def create(self, validated_data):
|
2018-11-17 21:53:51 +00:00
|
|
|
validated_data['seed'] = pyotp.random_base32()
|
2018-11-17 21:28:17 +00:00
|
|
|
return OTPSeed.objects.create(**validated_data)
|
2018-11-17 10:21:35 +00:00
|
|
|
|
2018-11-18 11:38:50 +00:00
|
|
|
class TokenSerializer(serializers.Serializer):
|
2018-12-30 23:46:29 +00:00
|
|
|
""" This class is mainly / only used for authentication"""
|
2018-11-18 11:38:50 +00:00
|
|
|
|
2018-12-30 21:30:11 +00:00
|
|
|
auth_name = serializers.CharField(max_length=128)
|
|
|
|
auth_token = serializers.CharField(max_length=128)
|
|
|
|
auth_realm = serializers.CharField(max_length=128)
|
|
|
|
|
2018-12-30 23:46:29 +00:00
|
|
|
token_name = 'auth_token'
|
|
|
|
name_name = 'auth_name'
|
|
|
|
realm_name = 'auth_realm'
|
2018-11-18 12:24:09 +00:00
|
|
|
|
2018-11-18 11:38:50 +00:00
|
|
|
def save(self):
|
2018-12-30 23:46:29 +00:00
|
|
|
auth_token = self.validated_data.get(self.token_name)
|
|
|
|
auth_name = self.validated_data.get(self.name_name)
|
|
|
|
auth_realm = self.validated_data.get(self.realm_name)
|
2018-12-30 21:30:11 +00:00
|
|
|
|
2018-12-30 23:46:29 +00:00
|
|
|
# only 2 special realms can login
|
|
|
|
if not auth_realm in ["ungleich-admin", "ungleich-auth" ]:
|
2019-02-10 22:33:22 +00:00
|
|
|
logger.error("Auth-realm is neither ungleich-admin "
|
|
|
|
"nor ungleich-auth".format()
|
|
|
|
)
|
2018-12-30 23:46:29 +00:00
|
|
|
raise exceptions.AuthenticationFailed()
|
2018-11-18 11:38:50 +00:00
|
|
|
|
2019-02-10 22:33:22 +00:00
|
|
|
logger.debug("auth: [{}]{}@'{}' {} + {})".format(
|
|
|
|
self.name_name, auth_name, auth_realm,
|
|
|
|
auth_token, self.validated_data
|
|
|
|
))
|
2018-11-18 12:29:07 +00:00
|
|
|
|
2018-11-18 11:38:50 +00:00
|
|
|
# 1. Verify that the connection might authenticate
|
|
|
|
try:
|
2019-02-11 00:06:21 +00:00
|
|
|
logger.debug("Checking in db for name:{} & realm:{}".format(
|
|
|
|
auth_name, auth_realm
|
|
|
|
))
|
2018-12-30 21:30:11 +00:00
|
|
|
db_instance = otpauth.models.OTPSeed.objects.get(name=auth_name, realm=auth_realm)
|
2018-11-18 11:38:50 +00:00
|
|
|
except (OTPSeed.MultipleObjectsReturned, OTPSeed.DoesNotExist):
|
2019-02-10 22:33:22 +00:00
|
|
|
logger.error("OTPSeed name: {}, realm: {} does not exist".format(
|
|
|
|
auth_name, auth_realm
|
|
|
|
))
|
2018-11-18 11:38:50 +00:00
|
|
|
raise exceptions.AuthenticationFailed()
|
2019-02-11 00:06:21 +00:00
|
|
|
logger.debug("Found seed: {}".format(db_instance.seed))
|
2018-11-18 11:38:50 +00:00
|
|
|
totp = pyotp.TOTP(db_instance.seed)
|
2019-02-10 22:33:22 +00:00
|
|
|
logger.debug("calculated token = {}".format(totp.now()))
|
2018-11-18 11:38:50 +00:00
|
|
|
|
2018-12-30 21:30:11 +00:00
|
|
|
if not totp.verify(auth_token, valid_window=3):
|
2019-02-10 22:33:22 +00:00
|
|
|
logger.error("totp not verified")
|
2018-11-18 11:38:50 +00:00
|
|
|
raise exceptions.AuthenticationFailed()
|
|
|
|
|
2018-12-30 21:41:17 +00:00
|
|
|
return (db_instance, auth_token)
|
2018-11-18 12:24:09 +00:00
|
|
|
|
2018-12-30 23:46:29 +00:00
|
|
|
# For verifying somebody else's token
|
2018-11-18 12:24:09 +00:00
|
|
|
class VerifySerializer(TokenSerializer):
|
2018-12-30 23:46:29 +00:00
|
|
|
name = serializers.CharField(max_length=128)
|
|
|
|
token = serializers.CharField(max_length=128)
|
|
|
|
realm = serializers.CharField(max_length=128)
|
2018-12-30 21:30:11 +00:00
|
|
|
|
2018-12-30 23:46:29 +00:00
|
|
|
token_name = 'token'
|
|
|
|
name_name = 'name'
|
|
|
|
realm_name = 'realm'
|
2018-12-30 21:30:11 +00:00
|
|
|
|
2018-12-30 23:46:29 +00:00
|
|
|
def save(self):
|
|
|
|
auth_realm = self.validated_data.get("auth_realm")
|
2018-12-30 21:30:11 +00:00
|
|
|
|
2018-12-30 23:46:29 +00:00
|
|
|
if not auth_realm == "ungleich-auth":
|
2019-02-10 22:33:22 +00:00
|
|
|
logger.error("Auth-realm is not ungleich-auth")
|
2018-12-30 23:46:29 +00:00
|
|
|
raise exceptions.AuthenticationFailed()
|
2019-02-08 18:25:07 +00:00
|
|
|
|
|
|
|
# Do the authentication part
|
|
|
|
super().save()
|