mirror of
https://github.com/retspen/webvirtcloud
synced 2026-11-08 11:16:47 +00:00
- An SSH public key whose comment holds spaces (ssh-keygen -C "John Doe laptop") is accepted; only the type and the key are checked. - The created API token is shown by a GET after a redirect, so reloading the page no longer creates a second token the user never sees. - The OTP login page shows field errors instead of an empty error box. - A user without a local password (an LDAP user) is not offered a password change that could never succeed.
79 lines
2.6 KiB
Python
79 lines
2.6 KiB
Python
import base64
|
|
import binascii
|
|
import struct
|
|
from decimal import Decimal
|
|
|
|
import segno
|
|
from django.core.mail import send_mail
|
|
from django.template.loader import render_to_string
|
|
from django.utils.safestring import mark_safe
|
|
from django.utils.translation import gettext as _
|
|
from django_otp import devices_for_user
|
|
from django_otp.plugins.otp_totp.models import TOTPDevice
|
|
|
|
|
|
def get_existing_totp_device(user):
|
|
"""The user's confirmed TOTP device, or None. Unlike get_user_totp_device
|
|
it never creates one, so an unauthenticated caller cannot turn a
|
|
password-only account into an OTP-required one."""
|
|
for device in devices_for_user(user, confirmed=True):
|
|
if isinstance(device, TOTPDevice):
|
|
return device
|
|
return None
|
|
|
|
|
|
def get_user_totp_device(user):
|
|
devices = devices_for_user(user)
|
|
for device in devices:
|
|
if isinstance(device, TOTPDevice):
|
|
return device
|
|
|
|
device = user.totpdevice_set.create()
|
|
return device
|
|
|
|
|
|
def validate_ssh_key(key):
|
|
# An OpenSSH public key is a type, a key and an optional comment, which may hold spaces
|
|
array = key.strip().encode().split(maxsplit=2)
|
|
if len(array) < 2:
|
|
return False
|
|
typeofkey = array[0]
|
|
string = array[1]
|
|
|
|
# must have only valid rsa-ssh key characters ie binascii characters
|
|
try:
|
|
data = base64.decodebytes(string)
|
|
except binascii.Error:
|
|
return False
|
|
# unpack the contents of data, from data[:4] , property of ssh key .
|
|
try:
|
|
str_len = struct.unpack(">I", data[:4])[0]
|
|
except struct.error:
|
|
return False
|
|
# data[4:str_len] must have string which matches with the typeofkey, another ssh key property.
|
|
if data[4 : 4 + str_len] != typeofkey:
|
|
return False
|
|
return True
|
|
|
|
|
|
def send_email_with_otp(user, device):
|
|
# An inline SVG (1.8 mm modules) and, for mail clients that show no SVG,
|
|
# a PNG (12 px modules); error correction M, 4-module border.
|
|
qr = segno.make(device.config_url, error="m", mode="byte", encoding="utf-8", micro=False)
|
|
style = {"border": 4, "dark": "black", "light": "white"}
|
|
send_mail(
|
|
_("OTP QR Code"),
|
|
_("Please view HTML version of this message."),
|
|
None,
|
|
[user.email],
|
|
html_message=render_to_string(
|
|
"accounts/email/otp.html",
|
|
{
|
|
"totp_url": device.config_url,
|
|
"user": user,
|
|
"qr_svg": mark_safe(qr.svg_inline(scale=Decimal("1.8"), unit="mm", **style)),
|
|
"qr_png": qr.png_data_uri(scale=12, **style),
|
|
},
|
|
),
|
|
fail_silently=False,
|
|
)
|