Assign user to discordauthtoken for security

- prevents exploit of another user entering incorrect password for email yet passing validation using existing token mentioned in #146
 - does not protect against same user entering wrong password (I can live with this.)
Periodic task to remove invalid token every 2 hours
This commit is contained in:
Adarnof 2016-01-05 22:53:52 +00:00
parent b5b13e828a
commit 2c6ca5f273
4 changed files with 47 additions and 24 deletions

View File

@ -9,7 +9,7 @@ from services.managers.mumble_manager import MumbleManager
from services.managers.phpbb3_manager import Phpbb3Manager
from services.managers.ipboard_manager import IPBoardManager
from services.managers.teamspeak3_manager import Teamspeak3Manager
from services.managers.discord_manager import DiscordManager
from services.managers.discord_manager import DiscordManager, DiscordAPIManager
from services.models import AuthTS
from services.models import TSgroup
from authentication.models import AuthServicesInfo
@ -25,6 +25,7 @@ from util.common_task import generate_corp_group_name
from eveonline.models import EveCharacter
from eveonline.models import EveCorporationInfo
from authentication.managers import AuthServicesInfoManager
from services.models import DiscordAuthToken
import logging
@ -277,6 +278,17 @@ def run_databaseUpdate():
add_to_databases(user, groups, syncgroups)
remove_from_databases(user, groups, syncgroups)
# Run every 2 hours
@periodic_task(run_every=crontab(minute="0", hour="*/2"))
def run_discord_token_cleanup():
logger.debug("Running validation of all DiscordAuthTokens")
for auth in DiscordAuthToken.objects.all():
logger.debug("Testing DiscordAuthToken %s" % auth)
if DiscordAPIManager.validate_token(auth.token):
logger.debug("Token passes validation. Retaining %s" % auth)
else:
logger.debug("DiscordAuthToken failed validation. Deleting %s" % auth)
auth.delete()
# Run every 3 hours
@periodic_task(run_every=crontab(minute=0, hour="*/3"))

View File

@ -13,8 +13,8 @@ DISCORD_URL = "https://discordapp.com/api"
class DiscordAPIManager:
def __init__(self, server_id, email, password):
self.token = DiscordAPIManager.get_token_by_user(email, password)
def __init__(self, server_id, email, password, user=None):
self.token = DiscordAPIManager.get_token_by_user(email, password, user)
self.email = email
self.password = password
self.server_id = server_id
@ -131,9 +131,8 @@ class DiscordAPIManager:
r.raise_for_status()
return r.json()
@staticmethod
def accept_invite(invite_id, token):
custom_headers = {'accept': 'application/json', 'authorization': token}
def accept_invite(self, invite_id):
custom_headers = {'accept': 'application/json', 'authorization': self.token}
path = DISCORD_URL + "/invite/" + str(invite_id)
r = requests.post(path, headers=custom_headers)
logger.debug("Received status code %s after accepting invite." % r.status_code)
@ -223,17 +222,20 @@ class DiscordAPIManager:
raise KeyError('Group not found on server: ' + group_name)
@staticmethod
def get_token_by_user(email, password):
def get_token_by_user(email, password, user):
if DiscordAuthToken.objects.filter(email=email).exists():
logger.debug("Discord auth token cached for supplied email starting with %s" % email[0:3])
auth = DiscordAuthToken.objects.get(email=email)
if not auth.user == user:
raise ValueError("User mismatch while validating DiscordAuthToken for email %s - user %s, requesting user %s" % (email, auth.user, user))
logger.debug("Discord auth token cached for supplied email starting with %s" % email[0:3])
auth = DiscordAuthToken.objects.get(email=email, user=user)
if DiscordAPIManager.validate_token(auth.token):
logger.debug("Token still valid. Returning token starting with %s" % auth.token[0:5])
return auth.token
else:
logger.debug("Token has expired. Deleting.")
auth.delete()
logger.debug("Generating auth token for email starting with %s and password of length %s" % (email[0:3], len(password)))
logger.debug("Generating auth token for email starting with %s user %s and password of length %s" % (email[0:3], user, len(password)))
data = {
"email" : email,
"password": password,
@ -244,11 +246,19 @@ class DiscordAPIManager:
logger.debug("Received status code %s after generating auth token for custom user." % r.status_code)
r.raise_for_status()
token = r.json()['token']
auth = DiscordAuthToken(email=email, token=token)
auth = DiscordAuthToken(email=email, token=token, user=user)
auth.save()
logger.debug("Created cached token for email starting with %s" % email[0:3])
return token
def get_profile(self):
custom_headers = {'accept': 'application/json', 'authorization': self.token}
path = DISCORD_URL + "/users/@me"
r = requests.get(path, headers=custom_headers)
logger.debug("Received status code %s after retrieving user profile with email %s" % (r.status_code, self.email[0:3]))
r.raise_for_status()
return r.json()
@staticmethod
def get_user_profile(email, password):
token = DiscordAPIManager.get_token_by_user(email, password)
@ -371,22 +381,21 @@ class DiscordManager:
return current_password
@staticmethod
def add_user(email, password):
def add_user(email, password, user):
try:
logger.debug("Adding new user to discord with email %s and password of length %s" % (email[0:3], len(password)))
api = DiscordAPIManager(settings.DISCORD_SERVER_ID, settings.DISCORD_USER_EMAIL, settings.DISCORD_USER_PASSWORD)
profile = DiscordAPIManager.get_user_profile(email, password)
logger.debug("Adding new user %s to discord with email %s and password of length %s" % (user, email[0:3], len(password)))
server_api = DiscordAPIManager(settings.DISCORD_SERVER_ID, settings.DISCORD_USER_EMAIL, settings.DISCORD_USER_PASSWORD)
user_api = DiscordAPIManager(settings.DISCORD_SERVER_ID, email, password, user=user)
profile = user_api.get_profile()
logger.debug("Got profile for user: %s" % profile)
user_id = profile['id']
logger.debug("Determined user id: %s" % user_id)
if api.check_if_user_banned(user_id):
if server_api.check_if_user_banned(user_id):
logger.debug("User is currently banned. Unbanning %s" % user_id)
api.unban_user(user_id)
invite_code = api.create_invite()['code']
server_api.unban_user(user_id)
invite_code = server_api.create_invite()['code']
logger.debug("Generated invite code beginning with %s" % invite_code[0:5])
token = DiscordAPIManager.get_token_by_user(email, password)
logger.debug("Got auth token for supplied credentials beginning with %s" % token[0:5])
DiscordAPIManager.accept_invite(invite_code, token)
user_api.accept_invite(invite_code)
logger.info("Added user to discord server %s with id %s" % (settings.DISCORD_SERVER_ID, user_id))
return user_id
except:

View File

@ -1,5 +1,5 @@
from django.db import models
from django.contrib.auth.models import Group
from django.contrib.auth.models import Group, User
class TSgroup(models.Model):
ts_group_id = models.IntegerField(primary_key=True)
@ -32,7 +32,9 @@ class UserTSgroup(models.Model):
return self.user.name
class DiscordAuthToken(models.Model):
email = models.CharField(max_length=254, primary_key=True)
email = models.CharField(max_length=254, unique=True)
token = models.CharField(max_length=254)
user = models.ForeignKey(User, on_delete=models.CASCADE, null=True)
def __str__(self):
return self.email
output = "Discord Token for email %s user %s" % (self.email, self.user)
return output.encode('utf-8')

View File

@ -436,7 +436,7 @@ def activate_discord(request):
password = form.cleaned_data['password']
logger.debug("Form contains password of length %s" % len(password))
try:
user_id = DiscordManager.add_user(email, password)
user_id = DiscordManager.add_user(email, password, request.user)
logger.debug("Received discord uid %s" % user_id)
if user_id != "":
AuthServicesInfoManager.update_user_discord_info(user_id, request.user)