mirror of
https://gitlab.com/allianceauth/allianceauth.git
synced 2025-07-13 14:30:17 +02:00
Upgrade Mumble password hashing to bcrypt (#671)
Added transition to bcrypt-sha256 hashing for mumble passwords. All new passwords will be hashed by bcrypt-sha256. The existing SHA-1 hashes will continue to work as a fallback for legacy password hashes.
This commit is contained in:
parent
11d52d476c
commit
2c68f485e2
@ -1,7 +1,7 @@
|
||||
from __future__ import unicode_literals
|
||||
import random
|
||||
import string
|
||||
import hashlib
|
||||
from passlib.hash import bcrypt_sha256
|
||||
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
|
||||
@ -16,6 +16,8 @@ class MumbleManager:
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
HASH_FN = 'bcrypt-sha256'
|
||||
|
||||
@staticmethod
|
||||
def __santatize_username(username):
|
||||
sanatized = username.replace(" ", "_")
|
||||
@ -33,24 +35,24 @@ class MumbleManager:
|
||||
def __generate_username_blue(username, corp_ticker):
|
||||
return "[BLUE][" + corp_ticker + "]" + username
|
||||
|
||||
@staticmethod
|
||||
def _gen_pwhash(password):
|
||||
return hashlib.sha1(password.encode('utf-8')).hexdigest()
|
||||
@classmethod
|
||||
def _gen_pwhash(cls, password):
|
||||
return bcrypt_sha256.encrypt(password.encode('utf-8'))
|
||||
|
||||
@staticmethod
|
||||
def create_user(user, corp_ticker, username, blue=False):
|
||||
@classmethod
|
||||
def create_user(cls, user, corp_ticker, username, blue=False):
|
||||
logger.debug("Creating%s mumble user with username %s and ticker %s" % (' blue' if blue else '',
|
||||
username, corp_ticker))
|
||||
username_clean = MumbleManager.__santatize_username(
|
||||
MumbleManager.__generate_username_blue(username, corp_ticker) if blue else
|
||||
MumbleManager.__generate_username(username, corp_ticker))
|
||||
password = MumbleManager.__generate_random_pass()
|
||||
pwhash = MumbleManager._gen_pwhash(password)
|
||||
username_clean = cls.__santatize_username(
|
||||
cls.__generate_username_blue(username, corp_ticker) if blue else
|
||||
cls.__generate_username(username, corp_ticker))
|
||||
password = cls.__generate_random_pass()
|
||||
pwhash = cls._gen_pwhash(password)
|
||||
logger.debug("Proceeding with mumble user creation: clean username %s, pwhash starts with %s" % (
|
||||
username_clean, pwhash[0:5]))
|
||||
if not MumbleUser.objects.filter(username=username_clean).exists():
|
||||
logger.info("Creating mumble user %s" % username_clean)
|
||||
MumbleUser.objects.create(user=user, username=username_clean, pwhash=pwhash)
|
||||
MumbleUser.objects.create(user=user, username=username_clean, pwhash=pwhash, hashfn=cls.HASH_FN)
|
||||
return username_clean, password
|
||||
else:
|
||||
logger.warn("Mumble user %s already exists.")
|
||||
@ -66,16 +68,17 @@ class MumbleManager:
|
||||
logger.error("Unable to delete user %s from mumble: MumbleUser model not found" % user)
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def update_user_password(user, password=None):
|
||||
@classmethod
|
||||
def update_user_password(cls, user, password=None):
|
||||
logger.debug("Updating mumble user %s password." % user)
|
||||
if not password:
|
||||
password = MumbleManager.__generate_random_pass()
|
||||
pwhash = MumbleManager._gen_pwhash(password)
|
||||
password = cls.__generate_random_pass()
|
||||
pwhash = cls._gen_pwhash(password)
|
||||
logger.debug("Proceeding with mumble user %s password update - pwhash starts with %s" % (user, pwhash[0:5]))
|
||||
try:
|
||||
model = MumbleUser.objects.get(user=user)
|
||||
model.pwhash = pwhash
|
||||
model.hashfn = cls.HASH_FN
|
||||
model.save()
|
||||
return password
|
||||
except ObjectDoesNotExist:
|
||||
|
25
services/modules/mumble/migrations/0005_mumbleuser_hashfn.py
Normal file
25
services/modules/mumble/migrations/0005_mumbleuser_hashfn.py
Normal file
@ -0,0 +1,25 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.4 on 2017-01-23 10:28
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('mumble', '0004_auto_20161214_1024'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='mumbleuser',
|
||||
name='hashfn',
|
||||
field=models.CharField(default='sha1', max_length=20),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='mumbleuser',
|
||||
name='pwhash',
|
||||
field=models.CharField(max_length=80),
|
||||
),
|
||||
]
|
@ -7,7 +7,8 @@ from django.db import models
|
||||
class MumbleUser(models.Model):
|
||||
user = models.OneToOneField('auth.User', related_name='mumble', null=True)
|
||||
username = models.CharField(max_length=254, unique=True)
|
||||
pwhash = models.CharField(max_length=40)
|
||||
pwhash = models.CharField(max_length=80)
|
||||
hashfn = models.CharField(max_length=20, default='sha1')
|
||||
groups = models.TextField(blank=True, null=True)
|
||||
|
||||
def __str__(self):
|
||||
|
@ -19,15 +19,9 @@ from .auth_hooks import MumbleService
|
||||
from .models import MumbleUser
|
||||
from .tasks import MumbleTasks
|
||||
|
||||
import hashlib
|
||||
|
||||
MODULE_PATH = 'services.modules.mumble'
|
||||
|
||||
|
||||
def gen_pwhash(password):
|
||||
return hashlib.sha1(password).hexdigest()
|
||||
|
||||
|
||||
class MumbleHooksTestCase(TestCase):
|
||||
def setUp(self):
|
||||
self.member = 'member_user'
|
||||
@ -198,4 +192,5 @@ class MumbleManagerTestCase(TestCase):
|
||||
def test_gen_pwhash(self):
|
||||
pwhash = self.manager._gen_pwhash('test')
|
||||
|
||||
self.assertEqual(pwhash, 'a94a8fe5ccb19ba61c4c0873d391e987982fbbd3')
|
||||
self.assertEqual(pwhash[:15], '$bcrypt-sha256$')
|
||||
self.assertEqual(len(pwhash), 75)
|
||||
|
31
thirdparty/Mumble/authenticator.py
vendored
31
thirdparty/Mumble/authenticator.py
vendored
@ -70,6 +70,8 @@ try:
|
||||
except ImportError: # python 2.4 compat
|
||||
from sha import sha as sha1
|
||||
|
||||
from passlib.hash import bcrypt_sha256
|
||||
|
||||
|
||||
def eprint(*args, **kwargs):
|
||||
print(*args, file=sys.stderr, **kwargs)
|
||||
@ -521,7 +523,10 @@ def do_main_program():
|
||||
return (FALL_THROUGH, None, None)
|
||||
|
||||
try:
|
||||
sql = 'SELECT id, pwhash, groups FROM %smumble_mumbleuser WHERE username = %%s' % cfg.database.prefix
|
||||
sql = 'SELECT id, pwhash, groups, hashfn ' \
|
||||
'FROM %smumble_mumbleuser ' \
|
||||
'WHERE username = %%s' % cfg.database.prefix
|
||||
|
||||
cur = threadDB.execute(sql, [name])
|
||||
except threadDbException:
|
||||
return (FALL_THROUGH, None, None)
|
||||
@ -532,14 +537,16 @@ def do_main_program():
|
||||
info('Fall through for unknown user "%s"', name)
|
||||
return (FALL_THROUGH, None, None)
|
||||
|
||||
uid, upwhash, ugroups = res
|
||||
uid, upwhash, ugroups, uhashfn = res
|
||||
|
||||
if ugroups:
|
||||
groups = ugroups.split(',')
|
||||
else:
|
||||
groups = []
|
||||
|
||||
if allianceauth_check_hash(pw, upwhash):
|
||||
debug('checking password with hash function: %s' % uhashfn)
|
||||
|
||||
if allianceauth_check_hash(pw, upwhash, uhashfn):
|
||||
info('User authenticated: "%s" (%d)', name, uid + cfg.user.id_offset)
|
||||
debug('Group memberships: %s', str(groups))
|
||||
return (uid + cfg.user.id_offset, entity_decode(name), groups)
|
||||
@ -745,14 +752,20 @@ def do_main_program():
|
||||
info('Shutdown complete')
|
||||
|
||||
|
||||
#
|
||||
# --- Python implementation of the AllianceAuth MumbleUser hash function
|
||||
#
|
||||
def allianceauth_check_hash(password, hash):
|
||||
def allianceauth_check_hash(password, hash, hash_type):
|
||||
"""
|
||||
Python implementation of the smf check hash function
|
||||
Python implementation of the AllianceAuth MumbleUser hash function
|
||||
:param password: Password to be verified
|
||||
:param hash: Hash for the password to be checked against
|
||||
:param hash_type: Hashing function originally used to generate the hash
|
||||
"""
|
||||
return sha1(password).hexdigest() == hash
|
||||
if hash_type == 'sha1':
|
||||
return sha1(password).hexdigest() == hash
|
||||
elif hash_type == 'bcrypt-sha256':
|
||||
return bcrypt_sha256.verify(password, hash)
|
||||
else:
|
||||
warning("No valid hash function found for %s" % hash_type)
|
||||
return False
|
||||
|
||||
|
||||
#
|
||||
|
Loading…
x
Reference in New Issue
Block a user