Compare commits

..

4 Commits

Author SHA1 Message Date
Joel Falknau
d68d75bd05
use new model formats, remove temp templates 2024-12-29 21:16:59 +10:00
Joel Falknau
1385a2ef16
unify user types 2024-12-29 21:15:48 +10:00
Joel Falknau
d09892397b
add TempUser to more authenticator functions 2024-12-29 21:15:03 +10:00
Joel Falknau
168e6cc290
remove temps from more templates 2024-12-29 21:14:40 +10:00
7 changed files with 162 additions and 172 deletions

View File

@ -41,19 +41,19 @@ from threading import Timer
from passlib.hash import bcrypt_sha256
from hashlib import sha1
import django # noqa
import os # noqa
import django
from django.utils.datetime_safe import datetime
import os
import sys
logger = logging.getLogger(__name__)
sys.path.append(os.getcwd())
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "myauth.settings.local") # noqa
os.environ["DJANGO_ALLOW_ASYNC_UNSAFE"] = "true" # noqa
django.setup() # noqa
from django.utils.datetime_safe import datetime
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "myauth.settings.local")
os.environ["DJANGO_ALLOW_ASYNC_UNSAFE"] = "true"
django.setup()
from allianceauth import __version__
from allianceauth.services.modules.mumble.models import MumbleServerServer, MumbleUser
from allianceauth import __version__ # noqa
from allianceauth.services.modules.mumble.models import MumbleServerServer, MumbleUser, TempUser # noqa
def main() -> None:
@ -293,22 +293,48 @@ def main() -> None:
mumble_user.version = user.version
mumble_user.last_connect = datetime.now()
mumble_user.save()
except Exception as e:
logger.error(e)
except MumbleUser.DoesNotExist as a:
try:
mumble_user = TempUser.objects.get(username=user)
mumble_user.release = user.release
mumble_user.version = user.version
mumble_user.last_connect = datetime.now()
mumble_user.save()
except Exception as b:
logger.exception(a)
logger.exception(b)
def userDisconnected(self, user, current=None) -> None:
try:
mumble_user = MumbleUser.objects.get(username=user)
mumble_user.last_disconnect = datetime.now()
mumble_user.save()
except Exception as e:
logger.error(e)
except MumbleUser.DoesNotExist as a:
try:
mumble_user = TempUser.objects.get(username=user)
mumble_user.last_disconnect = datetime.now()
mumble_user.save()
except Exception as b:
logger.exception(a)
logger.exception(b)
def userStateChanged(self, user, current=None) -> None:
pass
def userTextMessage(self, user, text_message=None) -> None:
pass
if text_message.text == "!kicktemps":
if self.server.hasPermission(user.session, 0, 0x10000):
self.server.sendMessage(user.session, "Kicking all templink clients!")
users = self.server.getUsers()
for (userid, auser) in users.items():
if auser.userid > (MumbleServerServer.objects.get(id=1).offset * 2):
self.server.kickUser(auser.session, "Kicking all temp users! :-)")
self.server.sendMessage(user.session, "All templink clients kicked!")
else:
self.server.sendMessage(user.session, "You do not have kick permissions!")
def channelCreated(self, channel, current=None) -> None:
pass
@ -340,13 +366,16 @@ def main() -> None:
try:
mumble_user = MumbleUser.objects.get(username=name)
except MumbleUser.DoesNotExist:
return (-2, None, None)
try:
mumble_user = TempUser.objects.get(username=name)
except TempUser.DoesNotExist:
return (-2, None, None) # No Standard or Temp User
logger.debug("checking password with hash function: %s" % mumble_user.hashfn)
if allianceauth_check_hash(pw, mumble_user.pwhash, mumble_user.hashfn):
logger.info(f'User authenticated: {mumble_user.get_display_name()} {mumble_user.user_id + MumbleServerServer.objects.get(id=1).offset}')
logger.info(f'User authenticated: {mumble_user.display_name} {mumble_user.user_id + MumbleServerServer.objects.get(id=1).offset}')
logger.debug("Group memberships: %s", mumble_user.group_string())
return (mumble_user.user_id + MumbleServerServer.objects.get(id=1).offset, mumble_user.get_display_name(), mumble_user.group_string())
return (mumble_user.user_id + MumbleServerServer.objects.get(id=1).offset, mumble_user.display_name, mumble_user.group_string())
logger.info(
f'Failed authentication attempt for user: {name} {mumble_user.user_id + MumbleServerServer.objects.get(id=1).offset}')
return (AUTH_REFUSED, None, None)
@ -374,7 +403,10 @@ def main() -> None:
try:
return (MumbleUser.objects.get(username=name).pk + MumbleServerServer.objects.get(id=1).offset)
except MumbleUser.DoesNotExist:
return -2 # FALL_THROUGH
try:
return (TempUser.objects.get(username=name).pk + MumbleServerServer.objects.get(id=1).offset * 2)
except TempUser.DoesNotExist:
return -2 # FALL_THROUGH
@fortifyIceFu("")
@checkSecret
@ -387,12 +419,17 @@ def main() -> None:
try:
mumble_user = MumbleUser.objects.get(user_id=id - MumbleServerServer.objects.get(id=1).offset)
mumble_user.username
except MumbleUser.DoesNotExist:
return "" # FALL_THROUGH
try:
mumble_user = TempUser.objects.get(user_id=id - MumbleServerServer.objects.get(id=1).offset * 2)
mumble_user.username
except TempUser.DoesNotExist:
return "" # FALL_THROUGH
# I dont quite rightly know why we have this
# SuperUser shouldnt be in our Authenticator?
# But Maybe it can be
# But Maybe it can be?
if MumbleUser.objects.get(user_id=id - MumbleServerServer.objects.get(id=1).offset).username == "SuperUser":
logger.debug('idToName %d -> "SuperUser" caught')
return "" # FALL_THROUGH

View File

@ -1,6 +1,5 @@
import random
import string
from typing import LiteralString
from allianceauth.eveonline.models import EveCharacter
from allianceauth.services.hooks import NameFormatter
from passlib.hash import bcrypt_sha256
@ -50,6 +49,11 @@ class MumbleUser(AbstractServiceModel):
blank=True, null=True, editable=False,
help_text="Timestamp of the users Last Disconnection from Mumble")
@property
def display_name(self) -> str:
from .auth_hooks import MumbleService
return NameFormatter(MumbleService(), self.user).format_name()
@staticmethod
def get_username(user) -> str:
return user.profile.main_character.character_name # main character as the user.username may be incorrect
@ -97,13 +101,6 @@ class MumbleUser(AbstractServiceModel):
groups_str.append(str(group.name))
return ','.join({g.replace(' ', '-') for g in groups_str})
def get_display_name(self) -> str:
from .auth_hooks import MumbleService
return NameFormatter(MumbleService(), self.user).format_name()
def display_name(self) -> str:
return self.get_display_name()
def create(self, user):
try:
username = self.get_username(user)
@ -146,13 +143,16 @@ class TempLink(models.Model):
class TempUser(models.Model):
class HashFunction(models.TextChoices):
SHA256 = 'bcrypt-sha256', _('SHA256')
SHA1 = 'sha1', _('SHA1')
name = models.CharField(max_length=200) # Display name to show
username = models.CharField(max_length=254, unique=True)
pwhash = models.CharField(max_length=90)
hashfn = models.CharField(
max_length=15,
choices=MumbleUser.HashFunction.choices,
default=MumbleUser.HashFunction.SHA256)
choices=HashFunction.choices,
default=HashFunction.SHA256)
certhash = models.CharField(
verbose_name="Certificate Hash",
max_length=254,blank=True, null=True, editable=False,
@ -178,9 +178,40 @@ class TempUser(models.Model):
templink = models.ForeignKey(TempLink, on_delete=models.CASCADE, null=True, default=None)
character_id = models.IntegerField(default=None, blank=True, null=True)
@property
def display_name(self) -> str:
from .auth_hooks import MumbleService
return NameFormatter(MumbleService(), self.user).format_name()
@staticmethod
def get_username(user) -> str:
return user.profile.main_character.character_name # main character as the user.username may be incorrect
@staticmethod
def sanitise_username(username) -> str:
return username.replace(" ", "_")
@staticmethod
def generate_random_pass() -> str:
return ''.join([random.choice(string.ascii_letters + string.digits) for n in range(16)])
@staticmethod
def gen_pwhash(password) -> str:
return bcrypt_sha256.encrypt(password.encode('utf-8'))
def __str__(self) -> str:
return f"Temp User: {self.username} - {self.name}"
def group_string(self) -> str:
"""Overwritten from MumbleUser, we could add features to this in the future
"""
return str(["Guest"])
class Meta:
verbose_name = _("Temp User")
verbose_name_plural = _("Temp Users")
permissions = ()
class IdlerHandler(models.Model):
name = models.CharField(_("Name"), max_length=50)

View File

@ -11,9 +11,9 @@
{% endblock header_nav_brand %}
{% block content %}
<div class="allianceauth-mumbletemps">
<div class="allianceauth-mumble">
<div class="row">
{% block mumbletemps %}{% endblock %}
{% block mumble %}{% endblock mumble %}
</div>
</div>
{% endblock %}

View File

@ -1,10 +1,9 @@
{% extends "mumbletemps/base.html" %}
{% extends "services/mumble/base.html" %}
{% load i18n %}
{% load humanize %}
{% load timetags %}
{% block mumbletemps %}
{% block mumble %}
<div class="col-md-6 mb-2">
<div class="card h-100">
<div class="card-header bg-warning">
@ -13,20 +12,29 @@
<div class="card-body text-center">
<p>
{% translate 'Temp Links Give Access to mumble with the <b class="text-success">Guest</b> Group for the preset time.' %}
{% blocktranslate trimmed with bold_start="<b>" bold_end="</b>" %}
Temp Links Give Access to mumble with the {{ bold_start }}Guest{{ bold_end }} Group for the preset time
{% endblocktranslate %}
</p>
<p>
{% translate 'Connected users <b class="text-warning">will not</b> be kicked at the end of this period.' %}
{% blocktranslate trimmed with bold_start="<b class='text-warning'>" bold_end="</b>" %}
Connected users {{ bold_start }}will not{{ bold_end }} be kicked at the end of this period.
{% endblocktranslate %}
</p>
<p>
{% translate 'Templink users can be kicked in mumble by typing <span class="badge bg-danger">!kicktemps</span> in mumble chat.' %}
{% blocktranslate trimmed with badge_start="<span class='badge bg-danger'>" badge_end="</span>" %}
Templink users can be kicked in mumble by typing {{ badge_start }}!kicktemps{{ badge_end }} in mumble chat.
{% endblocktranslate %}
</p>
<p>
{% translate 'There are <b class="text-warning">no restrictions</b> on who or how many can use a templink, share wisely.' %}
{% blocktranslate trimmed with bold_start="<b class='text-warning'>" bold_end="</b>" %}
There are {{ bold_start }}no restrictions{{ bold_end }} on who or how many can use a templink, share wisely.
{% endblocktranslate %}
</p>
</div>
</div>
</div>
@ -40,7 +48,7 @@
<div class="card-body text-center">
<p>{% translate "Your link will be displayed on the next page for an easy copy and paste." %}</p>
<form action="{% url 'mumbletemps:index' %}" method="post">
<form action="{% url 'mumble:index' %}" method="post">
{% csrf_token %}
<div class="mb-3">
@ -71,9 +79,9 @@
{% translate "Expires in" %}: <span id="countdown{{ tl.link_ref }}hot"></span>
</p>
<pre>{{ SITE_URL }}{% url 'mumbletemps:join' tl.link_ref %}</pre>
<pre>{{ SITE_URL }}{% url 'mumble:join' tl.link_ref %}</pre>
<button class="btn btn-info" id="clipboard-new" data-clipboard-text="{{ SITE_URL }}{% url 'mumbletemps:join' tl.link_ref %}">
<button class="btn btn-info" id="clipboard-new" data-clipboard-text="{{ SITE_URL }}{% url 'mumble:join' tl.link_ref %}">
{% translate "Copy to Clipboard!" %}
</button>
</div>
@ -116,13 +124,13 @@
<td class="text-center">
<button class="btn btn-info"
id="clipboard-{{ lnk.link_ref }}"
data-clipboard-text="{{ SITE_URL }}{% url 'mumbletemps:join' lnk.link_ref %}">
data-clipboard-text="{{ SITE_URL }}{% url 'mumble:join' lnk.link_ref %}">
{% translate "Copy to Clipboard!" %}
</button>
</td>
<td class="text-center">
<a class="btn btn-danger" href="{% url 'mumbletemps:nuke' lnk.link_ref %}">{% translate "Nuke Link!" %}</a>
<a class="btn btn-danger" href="{% url 'mumble:nuke' lnk.link_ref %}">{% translate "Nuke Link!" %}</a>
</td>
</tr>
{% endfor %}
@ -162,7 +170,7 @@
<td class="text-center">{{ lnk.link_ref }}</td>
<td class="text-center">
<a class="btn btn-danger" href="{% url 'mumbletemps:nuke' lnk.link_ref %}">{% translate "Nuke Link!" %}</a>
<a class="btn btn-danger" href="{% url 'mumble:nuke' lnk.link_ref %}">{% translate "Nuke Link!" %}</a>
</td>
</tr>
{% endfor %}
@ -172,103 +180,18 @@
</div>
</div>
{% endif %}
{% endblock mumbletemps %}
{% endblock mumble %}
{% block extra_javascript %}
{% include 'bundles/clipboard-js.html' %}
{% include "bundles/clipboard-js.html" %}
<script>
/* global ClipboardJS, moment */
/* global ClipboardJS */
const clipboard = new ClipboardJS('#clipboard-new');
{
%
for lnk in tl_list %
}
const lnk {
{
lnk.link_ref
}
} = new ClipboardJS('#clipboard-{{ lnk.link_ref }}'); {
% endfor %
}
{% for lnk in tl_list %}
const lnk{{ lnk.link_ref }} = new ClipboardJS('#clipboard-{{ lnk.link_ref }}');
{% endfor %}
</script>
{% include "bundles/moment-js.html" with locale=True %}
{% include "bundles/timers-js.html" %}
<script>
const locale = '{{ LANGUAGE_CODE }}';
const timers = [{
%
for lnk in tl_list %
} {
'id': "{{ lnk.link_ref }}",
'targetDate': moment("{{ lnk.expires|print_timestamp| date:"
c " }}"),
'expired': false
}, {
% endfor %
}
{
%
if tl %
} {
'id': "{{ tl.link_ref }}hot",
'targetDate': moment('{{ tl.expires|print_timestamp| date:"c" }}'),
'expired': false
}, {
% endif %
}
];
moment.locale(locale);
/**
* Update a timer
*
* @param timer
*/
const updateTimer = (timer) => {
if (timer.targetDate.isAfter(Date.now())) {
const duration = moment.duration(timer.targetDate - moment(), 'milliseconds');
document.getElementById('countdown' + timer.id).innerHTML = getDurationString(duration);
} else {
timer.expired = true;
document.getElementById('countdown' + timer.id).innerHTML = '';
}
};
/**
* Update all timers
*/
const updateAllTimers = () => {
const l = timers.length;
for (let i = 0; i < l; ++i) {
if (timers[i].expired) {
continue;
}
updateTimer(timers[i]);
}
};
/**
* Timed update function
*/
const timedUpdate = () => {
updateAllTimers();
};
// Set initial values
timedUpdate();
// Start timed updates
setInterval(timedUpdate, 1000);
</script>
{% endblock extra_javascript %}

View File

@ -1,19 +1,18 @@
{% extends "mumbletemps/base.html" %}
{% extends "services/mumble/base.html" %}
{% load static %}
{% load i18n %}
{% load timetags %}
{% block mumbletemps %}
{% block mumble %}
<div class="text-center">
<p>
<img class="ra-avatar img-circle" src="{{ link.creator.portrait_url_128 }}" alt="{{ link.creator.character_name }}" />
</p>
<h4>
{% blocktranslate with character=link.creator.character_name %}
{% blocktranslatelate with character=link.creator.character_name %}
{{ character }} has invited you to join Mumble!
{% endblocktranslate %}
{% endblocktranslatelate %}
</h4>
<p>
@ -25,7 +24,7 @@
<p>
<a class="btn btn-primary" href="mumble://{{ connect_url }}">
<img src="{% static 'mumbletemps/images/mumble-icon.png' %}" alt="{% translate 'Mumble' %}" height="64" width="64" style="margin: 15px" />
<img src="{% static 'services/mumble/images/mumble-icon.png' %}" alt="{% translate 'Mumble' %}" height="64" width="64" style="margin: 15px" />
<span style="margin: 15px;">
{% translate "Click to Join Mumble as" %}: <b>{{ temp_user.name }}</b>
@ -67,7 +66,7 @@
const locale = '{{ LANGUAGE_CODE }}';
const timers = [{
'id': '{{ link.link_ref }}',
'targetDate': moment('{{ link.expires|print_timestamp| date:"c" }}'),
'targetDate': moment('{{ link.expires| date:"c" }}'),
'expired': false
}];
@ -118,4 +117,4 @@
// Start timed updates
setInterval(timedUpdate, 1000);
</script>
{% endblock mumbletemps %}
{% endblock mumble %}

View File

@ -1,20 +1,19 @@
{% extends "mumbletemps/base.html" %}
{% extends "services/mumble/base.html" %}
{% load static %}
{% load i18n %}
{% load timetags %}
{% block mumbletemps %}
{% block mumble %}
<div class="col-md-12 text-center">
<div class="mumbletemps-sso-login mb-3">
<div class="mumble-sso-login mb-3">
<p>
<img class="ra-avatar img-circle" src="{{ link.creator.portrait_url_128 }}" alt="{{ link.creator.character_name }}" />
</p>
<h4>
{% blocktranslate with character=link.creator.character_name %}
{% blocktranslatelate with character=link.creator.character_name %}
{{ character }} has invited you to join Mumble!
{% endblocktranslate %}
{% endblocktranslatelate %}
</h4>
<p>
@ -24,7 +23,7 @@
<span class="label label-info">{% translate "Link Ref" %}: {{ link.link_ref }}</span>
</p>
<form action="{% url 'mumbletemps:join' link.link_ref %}" method="post">
<form action="{% url 'mumble:join' link.link_ref %}" method="post">
{% csrf_token %}
<input type="hidden" name="sso" value="True" />
@ -37,7 +36,7 @@
</form>
</div>
<div class="mumbletemps-non-sso-login">
<div class="mumble-non-sso-login">
<div class="row justify-content-center">
<div class="col-md-10 col-lg-8">
<div class="card">
@ -47,7 +46,7 @@
<div class="card-body text-start">
<div class="form-group">
<form action="{% url 'mumbletemps:join' link.link_ref %}" method="post">
<form action="{% url 'mumble:join' link.link_ref %}" method="post">
{% csrf_token %}
<input type="hidden" name="sso" value="False" />
@ -86,7 +85,7 @@
const locale = '{{ LANGUAGE_CODE }}';
const timers = [{
'id': '{{ link.link_ref }}',
'targetDate': moment('{{ link.expires|print_timestamp| date:"c" }}'),
'targetDate': moment('{{ link.expires| date:"c" }}'),
'expired': false
}];
@ -137,4 +136,4 @@
// Start timed updates
setInterval(timedUpdate, 1000);
</script>
{% endblock mumbletemps %}
{% endblock mumble %}

View File

@ -64,15 +64,17 @@ def connection_history(request) -> HttpResponse:
@login_required
@permission_required("mumble.view_connection_history")
def connection_history_data(request) -> JsonResponse:
connection_history_data = MumbleUser.objects.all(
).values(
'user',
'display_name',
'release',
'version',
'last_connect',
'last_disconnect',
)
users = MumbleUser.objects.all()
connection_history_data = []
for user in users:
connection_history_data.append({
'user': str(user),
'display_name': user.display_name,
'release': user.release,
'version': user.version,
'last_connect': user.last_connect,
'last_disconnect': user.last_disconnect,
})
return JsonResponse({"connection_history_data": list(connection_history_data)})
@ -110,7 +112,7 @@ class PseudoUser:
@login_required
@permission_required("mumbletemps.create_new_links")
@permission_required("mumble.create_new_links")
def index(request):
tl = None
@ -118,11 +120,10 @@ def index(request):
duration = request.POST.get("time")
if duration in ["3", "6", "12", "24"]:
expiry = datetime.now().replace(tzinfo=timezone.utc) + timedelta(hours=int(duration))
tl = TempLink.objects.create(
creator=request.user.profile.main_character,
link_ref=get_random_string(15),
expires=expiry.timestamp(),
expires=datetime.now(timezone.utc) + timedelta(hours=int(duration))
)
tl.save()
@ -137,7 +138,7 @@ def index(request):
}
return render(
request=request, template_name="mumbletemps/index.html", context=context
request=request, template_name="services/mumble/index.html", context=context
)
@ -170,7 +171,7 @@ def link(request, link_ref):
context = {"link": templink}
return render(
request=request, template_name="mumbletemps/login.html", context=context
request=request, template_name="services/mumble/login.html", context=context
)
@ -204,7 +205,7 @@ def link_username(request, name, association, link):
}
return render(
request=request, template_name="mumbletemps/link.html", context=context
request=request, template_name="services/mumble/link.html", context=context
)
@ -246,12 +247,12 @@ def link_sso(request, token, link):
}
return render(
request=request, template_name="mumbletemps/link.html", context=context
request=request, template_name="services/mumble/link.html", context=context
)
@login_required
@permission_required("mumbletemps.create_new_links")
@permission_required("mumble.create_new_links")
def nuke(request, link_ref):
try:
TempLink.objects.get(link_ref=link_ref).delete()
@ -262,4 +263,4 @@ def nuke(request, link_ref):
messages.error(request=request, message=f"Deleted Token {link_ref}")
pass # Crappy link
return redirect(to="mumbletemps:index")
return redirect(to="mumble:index")