mirror of
https://gitlab.com/allianceauth/allianceauth.git
synced 2026-02-10 17:16:22 +01:00
Restructure Alliance Auth package (#867)
* Refactor allianceauth into its own package * Add setup * Add missing default_app_config declarations * Fix timerboard namespacing * Remove obsolete future imports * Remove py2 mock support * Remove six * Add experimental 3.7 support and multiple Dj versions * Remove python_2_unicode_compatible * Add navhelper as local package * Update requirements
This commit is contained in:
1
allianceauth/services/modules/mumble/__init__.py
Normal file
1
allianceauth/services/modules/mumble/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
default_app_config = 'allianceauth.services.modules.mumble.apps.MumbleServiceConfig'
|
||||
10
allianceauth/services/modules/mumble/admin.py
Normal file
10
allianceauth/services/modules/mumble/admin.py
Normal file
@@ -0,0 +1,10 @@
|
||||
from django.contrib import admin
|
||||
from .models import MumbleUser
|
||||
|
||||
|
||||
class MumbleUserAdmin(admin.ModelAdmin):
|
||||
fields = ('user', 'username', 'groups') # pwhash is hidden from admin panel
|
||||
list_display = ('user', 'username', 'groups')
|
||||
search_fields = ('user__username', 'username', 'groups')
|
||||
|
||||
admin.site.register(MumbleUser, MumbleUserAdmin)
|
||||
6
allianceauth/services/modules/mumble/apps.py
Normal file
6
allianceauth/services/modules/mumble/apps.py
Normal file
@@ -0,0 +1,6 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class MumbleServiceConfig(AppConfig):
|
||||
name = 'allianceauth.services.modules.mumble'
|
||||
label = 'mumble'
|
||||
67
allianceauth/services/modules/mumble/auth_hooks.py
Normal file
67
allianceauth/services/modules/mumble/auth_hooks.py
Normal file
@@ -0,0 +1,67 @@
|
||||
import logging
|
||||
|
||||
from django.conf import settings
|
||||
from django.template.loader import render_to_string
|
||||
from allianceauth.notifications import notify
|
||||
|
||||
from allianceauth import hooks
|
||||
from allianceauth.services.hooks import ServicesHook
|
||||
from .manager import MumbleManager
|
||||
from .tasks import MumbleTasks
|
||||
from .urls import urlpatterns
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class MumbleService(ServicesHook):
|
||||
def __init__(self):
|
||||
ServicesHook.__init__(self)
|
||||
self.name = 'mumble'
|
||||
self.urlpatterns = urlpatterns
|
||||
self.service_url = settings.MUMBLE_URL
|
||||
self.access_perm = 'mumble.access_mumble'
|
||||
self.service_ctrl_template = 'registered/mumble_service_ctrl.html'
|
||||
|
||||
def delete_user(self, user, notify_user=False):
|
||||
logging.debug("Deleting user %s %s account" % (user, self.name))
|
||||
if MumbleManager.delete_user(user):
|
||||
if notify_user:
|
||||
notify(user, 'Mumble Account Disabled', level='danger')
|
||||
return True
|
||||
return False
|
||||
|
||||
def update_groups(self, user):
|
||||
logger.debug("Updating %s groups for %s" % (self.name, user))
|
||||
if MumbleTasks.has_account(user):
|
||||
MumbleTasks.update_groups.delay(user.pk)
|
||||
|
||||
def validate_user(self, user):
|
||||
if MumbleTasks.has_account(user) and not self.service_active_for_user(user):
|
||||
self.delete_user(user, notify_user=True)
|
||||
|
||||
def update_all_groups(self):
|
||||
logger.debug("Updating all %s groups" % self.name)
|
||||
MumbleTasks.update_all_groups.delay()
|
||||
|
||||
def service_active_for_user(self, user):
|
||||
return user.has_perm(self.access_perm)
|
||||
|
||||
def render_services_ctrl(self, request):
|
||||
urls = self.Urls()
|
||||
urls.auth_activate = 'auth_activate_mumble'
|
||||
urls.auth_deactivate = 'auth_deactivate_mumble'
|
||||
urls.auth_reset_password = 'auth_reset_mumble_password'
|
||||
urls.auth_set_password = 'auth_set_mumble_password'
|
||||
|
||||
return render_to_string(self.service_ctrl_template, {
|
||||
'service_name': self.title,
|
||||
'urls': urls,
|
||||
'service_url': self.service_url,
|
||||
'connect_url': request.user.mumble.username + '@' + self.service_url if MumbleTasks.has_account(request.user) else self.service_url,
|
||||
'username': request.user.mumble.username if MumbleTasks.has_account(request.user) else '',
|
||||
}, request=request)
|
||||
|
||||
|
||||
@hooks.register('services_hook')
|
||||
def register_mumble_service():
|
||||
return MumbleService()
|
||||
107
allianceauth/services/modules/mumble/manager.py
Executable file
107
allianceauth/services/modules/mumble/manager.py
Executable file
@@ -0,0 +1,107 @@
|
||||
import random
|
||||
import string
|
||||
from passlib.hash import bcrypt_sha256
|
||||
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
|
||||
from .models import MumbleUser
|
||||
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class MumbleManager:
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
HASH_FN = 'bcrypt-sha256'
|
||||
|
||||
@staticmethod
|
||||
def __santatize_username(username):
|
||||
sanatized = username.replace(" ", "_")
|
||||
return sanatized
|
||||
|
||||
@staticmethod
|
||||
def __generate_random_pass():
|
||||
return ''.join([random.choice(string.ascii_letters + string.digits) for n in range(16)])
|
||||
|
||||
@staticmethod
|
||||
def __generate_username(username, corp_ticker):
|
||||
return "[" + corp_ticker + "]" + username
|
||||
|
||||
@staticmethod
|
||||
def __generate_username_blue(username, corp_ticker):
|
||||
return "[BLUE][" + corp_ticker + "]" + username
|
||||
|
||||
@classmethod
|
||||
def _gen_pwhash(cls, password):
|
||||
return bcrypt_sha256.encrypt(password.encode('utf-8'))
|
||||
|
||||
@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 = 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, hashfn=cls.HASH_FN)
|
||||
return username_clean, password
|
||||
else:
|
||||
logger.warn("Mumble user %s already exists.")
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def delete_user(user):
|
||||
logger.debug("Deleting user %s from mumble." % user)
|
||||
if MumbleUser.objects.filter(user=user).exists():
|
||||
MumbleUser.objects.filter(user=user).delete()
|
||||
logger.info("Deleted user %s from mumble" % user)
|
||||
return True
|
||||
logger.error("Unable to delete user %s from mumble: MumbleUser model not found" % user)
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def update_user_password(cls, user, password=None):
|
||||
logger.debug("Updating mumble user %s password." % user)
|
||||
if not 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:
|
||||
logger.error("User %s not found on mumble. Unable to update password." % user)
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def update_groups(user, groups):
|
||||
logger.debug("Updating mumble user %s groups %s" % (user, groups))
|
||||
safe_groups = list(set([g.replace(' ', '-') for g in groups]))
|
||||
groups = ''
|
||||
for g in safe_groups:
|
||||
groups = groups + g + ','
|
||||
groups = groups.strip(',')
|
||||
if MumbleUser.objects.filter(user=user).exists():
|
||||
logger.info("Updating mumble user %s groups to %s" % (user, safe_groups))
|
||||
model = MumbleUser.objects.get(user=user)
|
||||
model.groups = groups
|
||||
model.save()
|
||||
return True
|
||||
else:
|
||||
logger.error("User %s not found on mumble. Unable to update groups." % user)
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def user_exists(username):
|
||||
return MumbleUser.objects.filter(username=username).exists()
|
||||
@@ -0,0 +1,28 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.2 on 2016-12-12 00:58
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='MumbleUser',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('username', models.CharField(max_length=254, unique=True)),
|
||||
('pwhash', models.CharField(max_length=40)),
|
||||
('groups', models.TextField(blank=True, null=True)),
|
||||
],
|
||||
options={
|
||||
'db_table': 'services_mumbleuser',
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,19 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.2 on 2016-12-12 01:00
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('mumble', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelTable(
|
||||
name='mumbleuser',
|
||||
table=None,
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,23 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.2 on 2016-12-12 03:31
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('mumble', '0002_auto_20161212_0100'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='mumbleuser',
|
||||
name='user',
|
||||
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='mumble', to=settings.AUTH_USER_MODEL),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,22 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.4 on 2016-12-14 10:24
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('mumble', '0003_mumbleuser_user'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='mumbleuser',
|
||||
name='user',
|
||||
field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='mumble', to=settings.AUTH_USER_MODEL),
|
||||
),
|
||||
]
|
||||
@@ -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),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,61 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.5 on 2017-02-02 05:59
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations
|
||||
from django.conf import settings
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
from django.contrib.auth.management import create_permissions
|
||||
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def migrate_service_enabled(apps, schema_editor):
|
||||
for app_config in apps.get_app_configs():
|
||||
app_config.models_module = True
|
||||
create_permissions(app_config, apps=apps, verbosity=0)
|
||||
app_config.models_module = None
|
||||
|
||||
Group = apps.get_model("auth", "Group")
|
||||
Permission = apps.get_model("auth", "Permission")
|
||||
MumbleUser = apps.get_model("mumble", "MumbleUser")
|
||||
|
||||
perm = Permission.objects.get(codename='access_mumble')
|
||||
|
||||
member_group_name = getattr(settings, str('DEFAULT_AUTH_GROUP'), 'Member')
|
||||
blue_group_name = getattr(settings, str('DEFAULT_BLUE_GROUP'), 'Blue')
|
||||
|
||||
# Migrate members
|
||||
if MumbleUser.objects.filter(user__groups__name=member_group_name).exists() or \
|
||||
getattr(settings, str('ENABLE_AUTH_MUMBLE'), False):
|
||||
try:
|
||||
group = Group.objects.get(name=member_group_name)
|
||||
group.permissions.add(perm)
|
||||
except ObjectDoesNotExist:
|
||||
logger.warning('Failed to migrate ENABLE_AUTH_MUMBLE setting')
|
||||
|
||||
# Migrate blues
|
||||
if MumbleUser.objects.filter(user__groups__name=blue_group_name).exists() or \
|
||||
getattr(settings, str('ENABLE_BLUE_MUMBLE'), False):
|
||||
try:
|
||||
group = Group.objects.get(name=blue_group_name)
|
||||
group.permissions.add(perm)
|
||||
except ObjectDoesNotExist:
|
||||
logger.warning('Failed to migrate ENABLE_BLUE_MUMBLE setting')
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('mumble', '0005_mumbleuser_hashfn'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='mumbleuser',
|
||||
options={'permissions': (('access_mumble', 'Can access the Mumble service'),)},
|
||||
),
|
||||
migrations.RunPython(migrate_service_enabled),
|
||||
]
|
||||
17
allianceauth/services/modules/mumble/models.py
Normal file
17
allianceauth/services/modules/mumble/models.py
Normal file
@@ -0,0 +1,17 @@
|
||||
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=80)
|
||||
hashfn = models.CharField(max_length=20, default='sha1')
|
||||
groups = models.TextField(blank=True, null=True)
|
||||
|
||||
def __str__(self):
|
||||
return self.username
|
||||
|
||||
class Meta:
|
||||
permissions = (
|
||||
("access_mumble", u"Can access the Mumble service"),
|
||||
)
|
||||
56
allianceauth/services/modules/mumble/tasks.py
Normal file
56
allianceauth/services/modules/mumble/tasks.py
Normal file
@@ -0,0 +1,56 @@
|
||||
import logging
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
|
||||
from allianceauth.celeryapp import app
|
||||
from .manager import MumbleManager
|
||||
from .models import MumbleUser
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class MumbleTasks:
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def has_account(user):
|
||||
try:
|
||||
return user.mumble.username != ''
|
||||
except ObjectDoesNotExist:
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def disable_mumble():
|
||||
logger.info("Deleting all MumbleUser models")
|
||||
MumbleUser.objects.all().delete()
|
||||
|
||||
@staticmethod
|
||||
@app.task(bind=True, name="mumble.update_groups")
|
||||
def update_groups(self, pk):
|
||||
user = User.objects.get(pk=pk)
|
||||
logger.debug("Updating mumble groups for user %s" % user)
|
||||
if MumbleTasks.has_account(user):
|
||||
groups = []
|
||||
for group in user.groups.all():
|
||||
groups.append(str(group.name))
|
||||
if len(groups) == 0:
|
||||
groups.append('empty')
|
||||
logger.debug("Updating user %s mumble groups to %s" % (user, groups))
|
||||
try:
|
||||
if not MumbleManager.update_groups(user, groups):
|
||||
raise Exception("Group sync failed")
|
||||
except:
|
||||
logger.exception("Mumble group sync failed for %s, retrying in 10 mins" % user)
|
||||
raise self.retry(countdown=60 * 10)
|
||||
logger.debug("Updated user %s mumble groups." % user)
|
||||
else:
|
||||
logger.debug("User %s does not have a mumble account, skipping" % user)
|
||||
|
||||
@staticmethod
|
||||
@app.task(name="mumble.update_all_groups")
|
||||
def update_all_groups():
|
||||
logger.debug("Updating ALL mumble groups")
|
||||
for mumble_user in MumbleUser.objects.exclude(username__exact=''):
|
||||
MumbleTasks.update_groups.delay(mumble_user.user.pk)
|
||||
@@ -0,0 +1,25 @@
|
||||
<tr>
|
||||
<td class="text-center">{{ service_name }}</td>
|
||||
<td class="text-center">{{ username }}</td>
|
||||
<td class="text-center"><a href="mumble://{{ service_url }}">{{ service_url }}</a></td>
|
||||
<td class="text-center">
|
||||
{% ifequal username "" %}
|
||||
<a href="{% url urls.auth_activate %}" title="Activate" class="btn btn-warning">
|
||||
<span class="glyphicon glyphicon-ok"></span>
|
||||
</a>
|
||||
{% else %}
|
||||
<a href="{% url urls.auth_set_password %}" title="Set Password" class="btn btn-warning">
|
||||
<span class="glyphicon glyphicon-pencil"></span>
|
||||
</a>
|
||||
<a href="{% url urls.auth_reset_password %}" title="Reset Password" class="btn btn-primary">
|
||||
<span class="glyphicon glyphicon-refresh"></span>
|
||||
</a>
|
||||
<a href="{% url urls.auth_deactivate %}" title="Deactivate" class="btn btn-danger">
|
||||
<span class="glyphicon glyphicon-remove"></span>
|
||||
</a>
|
||||
<a href="mumble://{{ connect_url }}" class="btn btn-success" title="Connect">
|
||||
<span class="glyphicon glyphicon-arrow-right"></span>
|
||||
</a>
|
||||
{% endifequal %}
|
||||
</td>
|
||||
</tr>
|
||||
188
allianceauth/services/modules/mumble/tests.py
Normal file
188
allianceauth/services/modules/mumble/tests.py
Normal file
@@ -0,0 +1,188 @@
|
||||
from unittest import mock
|
||||
|
||||
from django.test import TestCase, RequestFactory
|
||||
from django import urls
|
||||
from django.contrib.auth.models import User, Group, Permission
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
|
||||
from allianceauth.tests.auth_utils import AuthUtils
|
||||
|
||||
from .auth_hooks import MumbleService
|
||||
from .models import MumbleUser
|
||||
from .tasks import MumbleTasks
|
||||
|
||||
MODULE_PATH = 'allianceauth.services.modules.mumble'
|
||||
DEFAULT_AUTH_GROUP = 'Member'
|
||||
|
||||
|
||||
def add_permissions():
|
||||
permission = Permission.objects.get(codename='access_mumble')
|
||||
members = Group.objects.get_or_create(name=DEFAULT_AUTH_GROUP)[0]
|
||||
AuthUtils.add_permissions_to_groups([permission], [members])
|
||||
|
||||
|
||||
class MumbleHooksTestCase(TestCase):
|
||||
def setUp(self):
|
||||
self.member = 'member_user'
|
||||
member = AuthUtils.create_member(self.member)
|
||||
MumbleUser.objects.create(user=member, username=self.member, pwhash='password', groups='Member')
|
||||
self.none_user = 'none_user'
|
||||
none_user = AuthUtils.create_user(self.none_user)
|
||||
self.service = MumbleService
|
||||
add_permissions()
|
||||
|
||||
def test_has_account(self):
|
||||
member = User.objects.get(username=self.member)
|
||||
none_user = User.objects.get(username=self.none_user)
|
||||
self.assertTrue(MumbleTasks.has_account(member))
|
||||
self.assertFalse(MumbleTasks.has_account(none_user))
|
||||
|
||||
def test_service_enabled(self):
|
||||
service = self.service()
|
||||
member = User.objects.get(username=self.member)
|
||||
none_user = User.objects.get(username=self.none_user)
|
||||
|
||||
self.assertTrue(service.service_active_for_user(member))
|
||||
self.assertFalse(service.service_active_for_user(none_user))
|
||||
|
||||
@mock.patch(MODULE_PATH + '.tasks.MumbleManager')
|
||||
def test_update_all_groups(self, manager):
|
||||
service = self.service()
|
||||
service.update_all_groups()
|
||||
# Check member and blue user have groups updated
|
||||
self.assertTrue(manager.update_groups.called)
|
||||
self.assertEqual(manager.update_groups.call_count, 1)
|
||||
|
||||
def test_update_groups(self):
|
||||
# Check member has Member group updated
|
||||
service = self.service()
|
||||
member = User.objects.get(username=self.member)
|
||||
member.mumble.groups = '' # Remove the group set in setUp
|
||||
member.mumble.save()
|
||||
|
||||
service.update_groups(member)
|
||||
|
||||
mumble_user = MumbleUser.objects.get(user=member)
|
||||
self.assertIn(DEFAULT_AUTH_GROUP, mumble_user.groups)
|
||||
|
||||
# Check none user does not have groups updated
|
||||
with mock.patch(MODULE_PATH + '.tasks.MumbleManager') as manager:
|
||||
service = self.service()
|
||||
none_user = User.objects.get(username=self.none_user)
|
||||
service.update_groups(none_user)
|
||||
self.assertFalse(manager.update_groups.called)
|
||||
|
||||
def test_validate_user(self):
|
||||
service = self.service()
|
||||
# Test member is not deleted
|
||||
member = User.objects.get(username=self.member)
|
||||
service.validate_user(member)
|
||||
self.assertTrue(member.mumble)
|
||||
|
||||
# Test none user is deleted
|
||||
none_user = User.objects.get(username=self.none_user)
|
||||
MumbleUser.objects.create(user=none_user, username='mr no-name', pwhash='password', groups='Blue,Orange')
|
||||
service.validate_user(none_user)
|
||||
with self.assertRaises(ObjectDoesNotExist):
|
||||
none_mumble = User.objects.get(username=self.none_user).mumble
|
||||
|
||||
def test_delete_user(self):
|
||||
member = User.objects.get(username=self.member)
|
||||
|
||||
service = self.service()
|
||||
result = service.delete_user(member)
|
||||
|
||||
self.assertTrue(result)
|
||||
with self.assertRaises(ObjectDoesNotExist):
|
||||
mumble_user = User.objects.get(username=self.member).mumble
|
||||
|
||||
def test_render_services_ctrl(self):
|
||||
service = self.service()
|
||||
member = User.objects.get(username=self.member)
|
||||
request = RequestFactory().get('/en/services/')
|
||||
request.user = member
|
||||
|
||||
response = service.render_services_ctrl(request)
|
||||
self.assertTemplateUsed(service.service_ctrl_template)
|
||||
self.assertIn(urls.reverse('auth_deactivate_mumble'), response)
|
||||
self.assertIn(urls.reverse('auth_reset_mumble_password'), response)
|
||||
self.assertIn(urls.reverse('auth_set_mumble_password'), response)
|
||||
|
||||
# Test register becomes available
|
||||
member.mumble.delete()
|
||||
member = User.objects.get(username=self.member)
|
||||
request.user = member
|
||||
response = service.render_services_ctrl(request)
|
||||
self.assertIn(urls.reverse('auth_activate_mumble'), response)
|
||||
|
||||
|
||||
class MumbleViewsTestCase(TestCase):
|
||||
def setUp(self):
|
||||
self.member = AuthUtils.create_member('auth_member')
|
||||
self.member.set_password('password')
|
||||
self.member.email = 'auth_member@example.com'
|
||||
self.member.save()
|
||||
AuthUtils.add_main_character(self.member, 'auth_member', '12345', corp_id='111', corp_name='Test Corporation',
|
||||
corp_ticker='TESTR')
|
||||
add_permissions()
|
||||
|
||||
def login(self):
|
||||
self.client.login(username=self.member.username, password='password')
|
||||
|
||||
def test_activate(self):
|
||||
self.login()
|
||||
expected_username = '[TESTR]auth_member'
|
||||
response = self.client.get(urls.reverse('auth_activate_mumble'), follow=False)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertContains(response, expected_username)
|
||||
mumble_user = MumbleUser.objects.get(user=self.member)
|
||||
self.assertEqual(mumble_user.username, expected_username)
|
||||
self.assertTrue(mumble_user.pwhash)
|
||||
self.assertEqual('Member', mumble_user.groups)
|
||||
|
||||
def test_deactivate(self):
|
||||
self.login()
|
||||
MumbleUser.objects.create(user=self.member, username='some member')
|
||||
|
||||
response = self.client.get(urls.reverse('auth_deactivate_mumble'))
|
||||
|
||||
self.assertRedirects(response, expected_url=urls.reverse('auth_services'), target_status_code=200)
|
||||
with self.assertRaises(ObjectDoesNotExist):
|
||||
mumble_user = User.objects.get(pk=self.member.pk).mumble
|
||||
|
||||
def test_set_password(self):
|
||||
self.login()
|
||||
MumbleUser.objects.create(user=self.member, username='some member', pwhash='old')
|
||||
|
||||
response = self.client.post(urls.reverse('auth_set_mumble_password'), data={'password': '1234asdf'})
|
||||
|
||||
self.assertNotEqual(MumbleUser.objects.get(user=self.member).pwhash, 'old')
|
||||
self.assertRedirects(response, expected_url=urls.reverse('auth_services'), target_status_code=200)
|
||||
|
||||
def test_reset_password(self):
|
||||
self.login()
|
||||
MumbleUser.objects.create(user=self.member, username='some member', pwhash='old')
|
||||
|
||||
response = self.client.get(urls.reverse('auth_reset_mumble_password'))
|
||||
|
||||
self.assertNotEqual(MumbleUser.objects.get(user=self.member).pwhash, 'old')
|
||||
self.assertTemplateUsed(response, 'registered/service_credentials.html')
|
||||
self.assertContains(response, 'some member')
|
||||
|
||||
|
||||
class MumbleManagerTestCase(TestCase):
|
||||
def setUp(self):
|
||||
from .manager import MumbleManager
|
||||
self.manager = MumbleManager
|
||||
|
||||
def test_generate_random_password(self):
|
||||
password = self.manager._MumbleManager__generate_random_pass()
|
||||
|
||||
self.assertEqual(len(password), 16)
|
||||
self.assertIsInstance(password, type(''))
|
||||
|
||||
def test_gen_pwhash(self):
|
||||
pwhash = self.manager._gen_pwhash('test')
|
||||
|
||||
self.assertEqual(pwhash[:15], '$bcrypt-sha256$')
|
||||
self.assertEqual(len(pwhash), 75)
|
||||
16
allianceauth/services/modules/mumble/urls.py
Normal file
16
allianceauth/services/modules/mumble/urls.py
Normal file
@@ -0,0 +1,16 @@
|
||||
from django.conf.urls import url, include
|
||||
|
||||
from . import views
|
||||
|
||||
module_urls = [
|
||||
# Mumble service control
|
||||
url(r'^activate/$', views.activate_mumble, name='auth_activate_mumble'),
|
||||
url(r'^deactivate/$', views.deactivate_mumble, name='auth_deactivate_mumble'),
|
||||
url(r'^reset_password/$', views.reset_mumble_password,
|
||||
name='auth_reset_mumble_password'),
|
||||
url(r'^set_password/$', views.set_mumble_password, name='auth_set_mumble_password'),
|
||||
]
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^mumble/', include(module_urls))
|
||||
]
|
||||
104
allianceauth/services/modules/mumble/views.py
Normal file
104
allianceauth/services/modules/mumble/views.py
Normal file
@@ -0,0 +1,104 @@
|
||||
import logging
|
||||
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth.decorators import login_required, permission_required
|
||||
from django.shortcuts import render, redirect
|
||||
from allianceauth.services.forms import ServicePasswordForm
|
||||
|
||||
from .manager import MumbleManager
|
||||
from .tasks import MumbleTasks
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
ACCESS_PERM = 'mumble.access_mumble'
|
||||
|
||||
|
||||
@login_required
|
||||
@permission_required(ACCESS_PERM)
|
||||
def activate_mumble(request):
|
||||
logger.debug("activate_mumble called by user %s" % request.user)
|
||||
character = request.user.profile.main_character
|
||||
ticker = character.corporation_ticker
|
||||
|
||||
logger.debug("Adding mumble user for %s with main character %s" % (request.user, character))
|
||||
result = MumbleManager.create_user(request.user, ticker, character.character_name)
|
||||
|
||||
if result:
|
||||
logger.debug("Updated authserviceinfo for user %s with mumble credentials. Updating groups." % request.user)
|
||||
MumbleTasks.update_groups.apply(args=(request.user.pk,)) # Run synchronously to prevent timing issues
|
||||
logger.info("Successfully activated mumble for user %s" % request.user)
|
||||
messages.success(request, 'Activated Mumble account.')
|
||||
credentials = {
|
||||
'username': result[0],
|
||||
'password': result[1],
|
||||
}
|
||||
return render(request, 'registered/service_credentials.html',
|
||||
context={'credentials': credentials, 'service': 'Mumble'})
|
||||
else:
|
||||
logger.error("Unsuccessful attempt to activate mumble for user %s" % request.user)
|
||||
messages.error(request, 'An error occurred while processing your Mumble account.')
|
||||
return redirect("auth_services")
|
||||
|
||||
|
||||
@login_required
|
||||
@permission_required(ACCESS_PERM)
|
||||
def deactivate_mumble(request):
|
||||
logger.debug("deactivate_mumble called by user %s" % request.user)
|
||||
# if we successfully remove the user or the user is already removed
|
||||
if MumbleManager.delete_user(request.user):
|
||||
logger.info("Successfully deactivated mumble for user %s" % request.user)
|
||||
messages.success(request, 'Deactivated Mumble account.')
|
||||
else:
|
||||
logger.error("Unsuccessful attempt to deactivate mumble for user %s" % request.user)
|
||||
messages.error(request, 'An error occurred while processing your Mumble account.')
|
||||
return redirect("auth_services")
|
||||
|
||||
|
||||
@login_required
|
||||
@permission_required(ACCESS_PERM)
|
||||
def reset_mumble_password(request):
|
||||
logger.debug("reset_mumble_password called by user %s" % request.user)
|
||||
result = MumbleManager.update_user_password(request.user)
|
||||
|
||||
# if blank we failed
|
||||
if result != "":
|
||||
logger.info("Successfully reset mumble password for user %s" % request.user)
|
||||
messages.success(request, 'Reset Mumble password.')
|
||||
credentials = {
|
||||
'username': request.user.mumble.username,
|
||||
'password': result,
|
||||
}
|
||||
return render(request, 'registered/service_credentials.html',
|
||||
context={'credentials': credentials, 'service': 'Mumble'})
|
||||
else:
|
||||
logger.error("Unsuccessful attempt to reset mumble password for user %s" % request.user)
|
||||
messages.error(request, 'An error occurred while processing your Mumble account.')
|
||||
return redirect("auth_services")
|
||||
|
||||
|
||||
@login_required
|
||||
@permission_required(ACCESS_PERM)
|
||||
def set_mumble_password(request):
|
||||
logger.debug("set_mumble_password called by user %s" % request.user)
|
||||
if request.method == 'POST':
|
||||
logger.debug("Received POST request with form.")
|
||||
form = ServicePasswordForm(request.POST)
|
||||
logger.debug("Form is valid: %s" % form.is_valid())
|
||||
if form.is_valid() and MumbleTasks.has_account(request.user):
|
||||
password = form.cleaned_data['password']
|
||||
logger.debug("Form contains password of length %s" % len(password))
|
||||
result = MumbleManager.update_user_password(request.user, password=password)
|
||||
if result != "":
|
||||
logger.info("Successfully reset mumble password for user %s" % request.user)
|
||||
messages.success(request, 'Set Mumble password.')
|
||||
else:
|
||||
logger.error("Failed to install custom mumble password for user %s" % request.user)
|
||||
messages.error(request, 'An error occurred while processing your Mumble account.')
|
||||
return redirect("auth_services")
|
||||
else:
|
||||
logger.debug("Request is not type POST - providing empty form.")
|
||||
form = ServicePasswordForm()
|
||||
|
||||
logger.debug("Rendering form for user %s" % request.user)
|
||||
context = {'form': form, 'service': 'Mumble'}
|
||||
return render(request, 'registered/service_password.html', context=context)
|
||||
Reference in New Issue
Block a user