forked from ungleich-public/ungleich-user
utcnow() is deprecated since Python 3.12. Calling .timestamp() on its naive result also read it as local time, so token ages were off by the host's UTC offset everywhere except on UTC hosts. Unix time from time.time() is what the code meant.
686 lines
28 KiB
Python
686 lines
28 KiB
Python
# Imports from django
|
|
from django.shortcuts import render
|
|
from django.views.generic import View, FormView
|
|
from django.contrib.auth import authenticate, login, logout
|
|
from django.http import HttpResponse
|
|
from django.core.validators import validate_email, ValidationError
|
|
from django.urls import reverse_lazy
|
|
from django.core.mail import EmailMessage
|
|
from django.utils.decorators import method_decorator
|
|
from django.views.decorators.cache import cache_control
|
|
from rest_framework.views import APIView
|
|
from rest_framework.response import Response
|
|
from .models import PendingRegistration, ResetToken
|
|
from .forms import LoginForm
|
|
from .ungleich_ldap import LdapManager
|
|
from decouple import config, Csv
|
|
from pyotp import TOTP
|
|
|
|
import logging
|
|
import re
|
|
import secrets
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
# Imports for the extra stuff not in django
|
|
|
|
from base64 import b64encode, b64decode
|
|
import time
|
|
|
|
import requests
|
|
import json
|
|
|
|
from django.conf import settings
|
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
|
|
|
# Small helper used for registration.
|
|
def is_username_valid(username):
|
|
return re.fullmatch(r"[a-z0-9_-]+", username)
|
|
|
|
admin_seed = config('ADMIN_SEED')
|
|
admin_name = config('ADMIN_NAME')
|
|
admin_realm = config('ADMIN_REALM')
|
|
user_realm = config('USER_REALM')
|
|
otp_url = config('OTPSERVER')
|
|
|
|
# Session key holding the user a password reset link was verified for,
|
|
# and how long (seconds) that verification stays valid.
|
|
RESET_SESSION_KEY = 'password_reset'
|
|
RESET_SESSION_MAX_AGE = 60 * 60
|
|
|
|
|
|
def activate_account_link(base_url, user, pwd, firstname, lastname, email, epochutc):
|
|
"""Store a pending registration and return its activation link.
|
|
|
|
The password is hashed right away; the link only carries a random token.
|
|
"""
|
|
token = secrets.token_urlsafe(32)
|
|
PendingRegistration.objects.create(
|
|
token=token, username=user, email=email, firstname=firstname,
|
|
lastname=lastname, password_hash=LdapManager().hash_password(pwd),
|
|
creation=epochutc,
|
|
)
|
|
return "{base_url}/activate/{token}/".format(base_url=base_url, token=token)
|
|
|
|
|
|
def now_epoch():
|
|
"""Current Unix time in seconds (always UTC)."""
|
|
return int(time.time())
|
|
|
|
|
|
# Same alphabet Django's make_random_password() used (no look-alike chars).
|
|
PASSWORD_CHARS = 'abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789'
|
|
|
|
|
|
def random_password(length=16):
|
|
return ''.join(secrets.choice(PASSWORD_CHARS) for _ in range(length))
|
|
|
|
|
|
def clean_db():
|
|
"""Revoves outdated tokens"""
|
|
# cutoff time is set to 24h hours
|
|
cutoff = now_epoch() - (24*60*60)
|
|
# Get all tokens older than 24 hours
|
|
ResetToken.objects.filter(creation__lt=cutoff).delete()
|
|
PendingRegistration.objects.filter(creation__lt=cutoff).delete()
|
|
return True
|
|
|
|
class Index(FormView):
|
|
template_name = "landing.html"
|
|
form_class = LoginForm
|
|
success_url = 'useroptions.html'
|
|
|
|
def form_valid(self, form):
|
|
username = form.cleaned_data.get('username')
|
|
password = form.cleaned_data.get('password')
|
|
user = authenticate(username=username, password=password)
|
|
if user is not None:
|
|
login(self.request, user)
|
|
return render(self.request, 'useroptions.html', { 'user': user } )
|
|
return render(self.request, 'loginfailed.html')
|
|
|
|
@method_decorator(cache_control(no_cache=True, must_revalidate=True, no_store=True))
|
|
def get(self, request, *args, **kwargs):
|
|
if self.request.user.is_authenticated:
|
|
return render(self.request, 'useroptions.html',
|
|
{ 'user': self.request.user.username } )
|
|
return super(Index, self).get(request, *args, **kwargs)
|
|
|
|
|
|
class Register(View):
|
|
def get(self, request):
|
|
return render(request, 'registeruser.html')
|
|
|
|
# Someone filled out the register page, do some basic checks and throw it at nameko
|
|
def post(self, request):
|
|
service = 'register an user'
|
|
urlname = 'register'
|
|
username = request.POST.get('username')
|
|
|
|
if username == "" or not username:
|
|
return render(request, 'error.html', {
|
|
'urlname': urlname,
|
|
'service': service,
|
|
'error': 'Please supply a username.' } )
|
|
|
|
if not is_username_valid(username):
|
|
return render(request, 'error.html', {
|
|
'urlname': urlname,
|
|
'service': service,
|
|
'error': 'You can only use lowercase letters, numbers, underscores and the dash character in your username.' } )
|
|
|
|
if LdapManager().username_taken(username):
|
|
return render(request, 'error.html', {
|
|
'urlname': urlname,
|
|
'service': service,
|
|
'error': 'This username is already taken.' } )
|
|
|
|
password1 = request.POST.get('password1')
|
|
password2 = request.POST.get('password2')
|
|
if password1 != password2:
|
|
return render(request, 'error.html', { 'urlname': urlname,
|
|
'service': service,
|
|
'error': "Passwords don't match." } )
|
|
|
|
email = request.POST.get('email')
|
|
try:
|
|
validate_email(email)
|
|
except ValidationError:
|
|
return render(request, 'error.html', { 'urlname': urlname,
|
|
'service': service,
|
|
'error': 'The supplied email address is invalid.' } )
|
|
|
|
firstname = request.POST.get('firstname')
|
|
lastname = request.POST.get('lastname')
|
|
if not firstname or not lastname:
|
|
return render(request, 'error.html', { 'urlname': urlname,
|
|
'service': service,
|
|
'error': 'Please enter your firstname and lastname.' } )
|
|
|
|
# so nothing strange happens if there are escapable chars
|
|
pwd = r'%s' % password1
|
|
|
|
try:
|
|
creationtime = now_epoch()
|
|
base_url = "{0}://{1}".format(self.request.scheme,
|
|
self.request.get_host())
|
|
link = activate_account_link(base_url, username, pwd, firstname, lastname, email, creationtime)
|
|
email_from = settings.EMAIL_FROM_ADDRESS
|
|
to = ['%s <%s>' % (username, email)]
|
|
subject = 'Activate your ungleich account'.format(firstname)
|
|
body = 'You can activate your ungleich account account by clicking <a href="{link}">here</a>.' \
|
|
' You can also copy and paste the following link into the address bar of your browser and follow' \
|
|
' the link in order to activate your account.\n\n{link}'.format(link=link)
|
|
# Build the email
|
|
mail = EmailMessage(
|
|
subject=subject,
|
|
body=body,
|
|
from_email=email_from,
|
|
to=to
|
|
)
|
|
mail.send()
|
|
|
|
except Exception as e:
|
|
return render(request, 'error.html', { 'urlname': urlname,
|
|
'service': service,
|
|
'error': e } )
|
|
return render(request, 'confirm_email.html')
|
|
|
|
class ChangeData(LoginRequiredMixin, View):
|
|
login_url = reverse_lazy('login_index')
|
|
# provide the form for the change request
|
|
def get(self, request):
|
|
urlname = 'change_data'
|
|
service = 'get default data for logged in user'
|
|
|
|
user = request.user
|
|
ldap_manager = LdapManager()
|
|
user_exists, entries = ldap_manager.check_user_exists(
|
|
uid=user.username,
|
|
attributes=['uid', 'givenName', 'sn', 'mail'],
|
|
search_base=settings.ENTIRE_SEARCH_BASE
|
|
)
|
|
|
|
if user_exists:
|
|
return render(
|
|
request,
|
|
'changeuserdata.html',
|
|
{ 'user': user.username,
|
|
'firstname': entries[0].givenName
|
|
if entries[0].givenName.value is not None else '',
|
|
'lastname': entries[0].sn
|
|
if entries[0].sn.value is not None else '',
|
|
'email': entries[0].mail
|
|
if entries[0].mail.value is not None else ''}
|
|
)
|
|
else:
|
|
return render(request, 'error.html',
|
|
{'urlname': urlname, 'service': service,
|
|
'error': request.user.username})
|
|
|
|
# get the change request
|
|
def post(self, request):
|
|
# variables for the error page
|
|
service = 'change user data'
|
|
urlname = 'change_data'
|
|
|
|
firstname = request.POST.get('firstname')
|
|
lastname = request.POST.get('lastname')
|
|
email = request.POST.get('email')
|
|
|
|
# Some sanity checks for the supplied data
|
|
if firstname == "":
|
|
return render(request, 'error.html', { 'urlname': urlname, 'service': service, 'error': 'Please enter a firstname.' } )
|
|
elif lastname == "":
|
|
return render(request, 'error.html', { 'urlname': urlname, 'service': service, 'error': 'Please enter a lastname.' } )
|
|
elif email == "":
|
|
return render(request, 'error.html', { 'urlname': urlname, 'service': service, 'error': 'Please enter an email.' } )
|
|
try:
|
|
validate_email(email)
|
|
except ValidationError:
|
|
return render(request, 'error.html', { 'urlname': urlname, 'service': service, 'error': 'The supplied email address is invalid.' } )
|
|
|
|
ldap_manager = LdapManager()
|
|
result, msg = ldap_manager.change_user_details(
|
|
uid=request.user.username,
|
|
details={"givenName": firstname, "sn": lastname, "mail": email}
|
|
)
|
|
|
|
# Data change worked
|
|
if result:
|
|
return render(request, 'changeddata.html', { 'user': request.user.username, 'firstname': firstname, 'lastname': lastname, 'email': email } )
|
|
# Data change did not work, display error
|
|
else:
|
|
return render(request, 'error.html', { 'urlname': urlname, 'service': service, 'error': msg } )
|
|
|
|
|
|
class ResetPassword(View):
|
|
def get(self, request):
|
|
return render(request, 'resetpassword.html')
|
|
|
|
def post(self, request):
|
|
urlname = 'reset_password'
|
|
service = 'send a password reset request'
|
|
user = request.POST.get('user')
|
|
# First, check if the user exists
|
|
ldap_manager = LdapManager()
|
|
user_exists, entries = ldap_manager.check_user_exists(
|
|
uid=user,
|
|
search_base=settings.ENTIRE_SEARCH_BASE,
|
|
attributes=['uid', 'givenName', 'sn', 'mail']
|
|
)
|
|
if user_exists:
|
|
# user exists, so try to get email
|
|
# with get_pool().next() as rpc:
|
|
# (state, tmp1, tmp2, email) = rpc.getuserdata.get_data(user)
|
|
# Either error with the datalookup or no email provided
|
|
email = entries[0].mail.value
|
|
if email is None:
|
|
return render(
|
|
request, 'error.html',
|
|
{'urlname': urlname, 'service': service,
|
|
'error': 'Unable to retrieve email address for user.'}
|
|
)
|
|
|
|
base_url = "{0}://{1}".format(self.request.scheme,
|
|
self.request.get_host())
|
|
# Try to send the email out
|
|
emailsend = self.email(user, email, base_url)
|
|
# Email got sent out
|
|
if emailsend == True:
|
|
return render(
|
|
request, 'send_resetrequest.html', {'user': user}
|
|
)
|
|
# Error while trying to send email
|
|
else:
|
|
return render(
|
|
request, 'error.html',
|
|
{'urlname': urlname, 'service': service,
|
|
'error': emailsend}
|
|
)
|
|
else:
|
|
return render(
|
|
request, 'error.html',
|
|
{ 'urlname': urlname, 'service': service,
|
|
'error': 'The user does not exist.' }
|
|
)
|
|
|
|
# Sends an email to the user with the 24h active link for a password reset
|
|
def email(self, user, email, base_url):
|
|
# getting epoch for the time now in UTC to spare us headache with timezones
|
|
creationtime = now_epoch()
|
|
# Construct the data for the email
|
|
email_from = settings.EMAIL_FROM_ADDRESS
|
|
to = ['%s <%s>' % (user, email)]
|
|
subject = 'Password reset request for %s' % user
|
|
link = self.build_reset_link(user, creationtime, base_url)
|
|
body = 'This is an automated email which was triggered by a reset request for the user %s. Please do not reply to this email.\n' % user
|
|
body += 'If you received this email in error, please disregard it. If you get multiple emails like this, please contact us to look into potential abuse.\n'
|
|
body += 'To reset your password, please follow the link below:\n'
|
|
body += '%s\n\n' % link
|
|
body += 'The link will remain active for 24 hours.\n'
|
|
# Build the email
|
|
mail = EmailMessage(
|
|
subject=subject,
|
|
body=body,
|
|
from_email=email_from,
|
|
to=to
|
|
)
|
|
try:
|
|
mail.send()
|
|
result = True
|
|
except:
|
|
result = "An error occurred while trying to send the mail."
|
|
return result
|
|
|
|
# Builds the reset link for the email and puts the token into the database
|
|
def build_reset_link(self, user, epochutc, base_url):
|
|
# set up the data
|
|
token = secrets.token_urlsafe(32)
|
|
buser = bytes(user, 'utf-8')
|
|
userpart = b64encode(buser)
|
|
# create entry into the database
|
|
newdbentry = ResetToken(user=user, token=token, creation=epochutc)
|
|
newdbentry.save()
|
|
# set up the link
|
|
link = "{base_url}/reset/{user}/{token}/".format(
|
|
base_url=base_url,user=userpart.decode('utf-8'),token=token
|
|
)
|
|
return link
|
|
|
|
|
|
# Catch the resetrequest URL and check it
|
|
class ResetRequest(View):
|
|
|
|
# Gets the URL with user in b64 and the token, and checks it
|
|
# Also cleans the database
|
|
def get(self, request, user=None, token=None):
|
|
# Cleans up outdated tokens
|
|
# If we expect quite a bit of old tokens, maybe somewhere else is better,
|
|
# but for now we don't really expect many unused tokens
|
|
clean_db()
|
|
# If user and token are not supplied by django, it was called from somewhere else, so it's
|
|
# invalid
|
|
if user == None or token == None:
|
|
return HttpResponse('Invalid URL.', status=404)
|
|
# extract user from b64 format
|
|
try:
|
|
user_clean = b64decode(user.encode('utf-8')).decode('utf-8')
|
|
except Exception:
|
|
return HttpResponse('Invalid URL.', status=404)
|
|
# set checks_out = True if token is found in database
|
|
checks_out = False
|
|
dbentries = ResetToken.objects.all().filter(user=user_clean)
|
|
for entry in dbentries:
|
|
if entry.token == token:
|
|
# found the token, now delete it since it's used
|
|
checks_out = True
|
|
entry.delete()
|
|
# No token was found
|
|
if not checks_out:
|
|
return HttpResponse('Invalid URL.', status=404)
|
|
# Token was found: remember in the session which user this browser
|
|
# proved control of, and supply the form. The POST only trusts this.
|
|
request.session[RESET_SESSION_KEY] = {
|
|
'user': user_clean,
|
|
'verified_at': now_epoch(),
|
|
}
|
|
return render(request, 'resetpasswordnew.html', { 'user': user_clean } )
|
|
|
|
|
|
# Gets the post form with the new password and sets it
|
|
def post(self, request):
|
|
service = 'reset the password'
|
|
# get the supplied passwords
|
|
password1 = request.POST.get("password1")
|
|
password2 = request.POST.get("password2")
|
|
# The user comes from the session set by a verified reset link,
|
|
# never from the form.
|
|
verified = request.session.get(RESET_SESSION_KEY) or {}
|
|
user = verified.get('user')
|
|
age = now_epoch() - verified.get('verified_at', 0)
|
|
if not user or age > RESET_SESSION_MAX_AGE:
|
|
request.session.pop(RESET_SESSION_KEY, None)
|
|
return render(request, 'error.html', { 'service': service, 'error': 'Your reset link is invalid or has expired. Please request a new one.' } )
|
|
if password1 == "" or not password1 or password2 == "" or not password2:
|
|
return render(request, 'error.html', { 'service': service, 'error': 'Please supply a password and confirm it.' } )
|
|
if password1 != password2:
|
|
return render(request, 'error.html', {'service': service, 'error': 'The supplied passwords do not match.'})
|
|
if len(password1) < 8:
|
|
return render(request, 'error.html', {'service': service,
|
|
'error': 'The password is too short, please use a longer one. At least 8 characters.'})
|
|
|
|
ldap_manager = LdapManager()
|
|
result = ldap_manager.change_password(
|
|
user,
|
|
password1
|
|
)
|
|
# password change successful
|
|
if result:
|
|
request.session.pop(RESET_SESSION_KEY, None)
|
|
return render(request, 'changedpassword.html', { 'user': user } )
|
|
# Something went wrong while changing the password
|
|
else:
|
|
return render(request, 'error.html', { 'service': service, 'error': result } )
|
|
|
|
|
|
|
|
# The logged in user can change the password here
|
|
|
|
class ChangePassword(LoginRequiredMixin, View):
|
|
login_url = reverse_lazy('login_index')
|
|
# Presents the page for a logged in user
|
|
def get(self, request):
|
|
if not request.user.is_authenticated:
|
|
return render(request, 'mustbeloggedin.html')
|
|
return render(request, 'changepassword.html', { 'user': request.user } )
|
|
|
|
# Does some checks on the supplied data and changes the password
|
|
def post(self, request):
|
|
# Variables for the error page
|
|
urlname = 'change_password'
|
|
service = 'change the password'
|
|
|
|
if not request.user.is_authenticated:
|
|
return render(request, 'mustbeloggedin.html')
|
|
login(request, request.user)
|
|
|
|
user = str(request.user)
|
|
oldpassword = request.POST.get('oldpassword')
|
|
check = authenticate(request, username=user, password=oldpassword)
|
|
# Is the right password for the user supplied?
|
|
if check is None:
|
|
return render(request, 'error.html', { 'urlname': urlname, 'service': service, 'error': 'Wrong password for the user.' } )
|
|
|
|
password1 = request.POST.get('password1')
|
|
password2 = request.POST.get('password2')
|
|
# Are both passwords from the form the same?
|
|
if password1 != password2:
|
|
return render(request, 'error.html', { 'urlname': urlname, 'service': service,
|
|
'error': 'Please check if you typed the same password both times for the new password' } )
|
|
# Check for password length
|
|
if len(password1) < 8:
|
|
return render(request, 'error.html', { 'urlname': urlname, 'service': service,
|
|
'error': 'The password is too short, please use a longer one. At least 8 characters.' } )
|
|
from .ungleich_ldap import LdapManager
|
|
ldap_manager = LdapManager()
|
|
result = ldap_manager.change_password(
|
|
user,
|
|
password1
|
|
)
|
|
# Password was changed
|
|
if result:
|
|
logout(request)
|
|
return render(request, 'changedpassword.html', { 'user': user } )
|
|
# Password not changed, instead got some kind of error
|
|
else:
|
|
return render(request, 'error.html', { 'urlname': urlname, 'service': service, 'error': result } )
|
|
|
|
|
|
# Deletes an account
|
|
class DeleteAccount(LoginRequiredMixin, View):
|
|
login_url = reverse_lazy('login_index')
|
|
# Show the basic form for deleting an account
|
|
def get(self, request):
|
|
return render(request, 'deleteaccount.html')
|
|
|
|
# Reads the filled out form
|
|
def post(self, request):
|
|
# Variables for error page
|
|
urlname = 'account_delete'
|
|
service = 'delete an account'
|
|
|
|
# Does the user exist?
|
|
username = request.POST.get('username')
|
|
ldap_manager = LdapManager()
|
|
user_exists, user_details = ldap_manager.check_user_exists(username)
|
|
if user_exists and request.user.username == username:
|
|
# Do user and password match?
|
|
password = request.POST.get('password')
|
|
pwd = r'%s' % password
|
|
check = authenticate(request, username=username, password=pwd)
|
|
if check is None:
|
|
return render(request, 'error.html',
|
|
{'urlname': urlname, 'service': service,
|
|
'error': 'Wrong password for user.'})
|
|
result = ldap_manager.delete_user(username)
|
|
# User deleted
|
|
if result:
|
|
logout(request)
|
|
return render(request, 'deleteduser.html', {'user': username})
|
|
# User not deleted, got some kind of error
|
|
else:
|
|
return render(request, 'error.html',
|
|
{'urlname': urlname, 'service': service,
|
|
'error': result})
|
|
else:
|
|
return render(request, 'error.html', { 'urlname': urlname, 'service': service, 'error': 'Unknown user.' } )
|
|
|
|
# Log out the session
|
|
class LogOut(View):
|
|
def get(self, request):
|
|
logout(request)
|
|
return HttpResponse(
|
|
"You have been logged out. You will be redirected in 2 seconds."
|
|
"<script>"
|
|
"setTimeout(function (){document.location.href='/';}, 2000);"
|
|
"</script>",
|
|
status=200
|
|
)
|
|
|
|
|
|
|
|
class ActivateAccount(View):
|
|
|
|
def get(self, request, token=None):
|
|
clean_db()
|
|
pending = PendingRegistration.objects.filter(token=token).first()
|
|
if pending is None:
|
|
return HttpResponse('Invalid URL.', status=404)
|
|
# single use: consumed whether or not the creation below succeeds
|
|
pending.delete()
|
|
try:
|
|
ldap_manager = LdapManager()
|
|
ldap_manager.create_user(
|
|
pending.username, pending.password_hash,
|
|
pending.firstname, pending.lastname, pending.email
|
|
)
|
|
req = requests.post(otp_url, data=json.dumps(
|
|
{
|
|
'auth_token': TOTP(admin_seed).now(),
|
|
'auth_name': admin_name,
|
|
'auth_realm': admin_realm,
|
|
'name': pending.username,
|
|
'realm': user_realm
|
|
}), headers={'Content-Type': 'application/json'})
|
|
if req.status_code != 201:
|
|
logger.error("User {} failed to create its otp seed".format(pending.username))
|
|
|
|
#Send welcome email
|
|
except Exception as e:
|
|
return render(request, 'error.html', {'urlname': 'register',
|
|
'service': 'register an user',
|
|
'error': e})
|
|
return render(request, 'usercreated.html', { 'user': pending.username } )
|
|
|
|
|
|
class UserCreateAPI(APIView):
|
|
|
|
def post(self, request):
|
|
username = request.POST.get('username')
|
|
email = request.POST.get('email')
|
|
firstname = request.POST.get('firstname')
|
|
lastname = request.POST.get('lastname')
|
|
|
|
if username == "" or not username:
|
|
return Response('Please supply a username.', 400)
|
|
|
|
if not is_username_valid(username):
|
|
return Response('Username is not valid.', 400)
|
|
|
|
if LdapManager().username_taken(username):
|
|
return Response('Username is already taken.', 400)
|
|
|
|
try:
|
|
validate_email(email)
|
|
except ValidationError:
|
|
return Response('Email is not valid.', 400)
|
|
if not firstname or not lastname:
|
|
return Response('Please provide firstname and lastname', 400)
|
|
|
|
pwd = random_password()
|
|
|
|
base_url = "{0}://{1}".format(self.request.scheme,
|
|
self.request.get_host())
|
|
creationtime = now_epoch()
|
|
link = activate_account_link(base_url, username, pwd, firstname, lastname, email, creationtime)
|
|
|
|
# Construct the data for the email
|
|
email_from = settings.EMAIL_FROM_ADDRESS
|
|
to = ['%s <%s>' % (username, email)]
|
|
subject = 'Ungleich account creation.'
|
|
body = 'A request has been sent to our servers to register you as a ungleich user.\n'
|
|
body += 'In order to complete the registration process you must ' \
|
|
'click <a href="{link}">here</a> or copy & paste the following link into the address bar of ' \
|
|
'your browser.\n{link}\n'.format(link=link)
|
|
body += 'Your credentials are:\n'
|
|
body += 'Username: %s\n\n' % username
|
|
body += 'Password: %s\n\n' % pwd
|
|
body += 'We strongly recommend after the activation to log in and change your password.\n'
|
|
body += 'This link will remain active for 24 hours.\n'
|
|
# Build the email
|
|
mail = EmailMessage(
|
|
subject=subject,
|
|
body=body,
|
|
from_email=email_from,
|
|
to=to
|
|
)
|
|
try:
|
|
mail.send()
|
|
except Exception as e:
|
|
return Response('Failed to send the email, please try again', 400)
|
|
return Response('An email with activation link has been sent in order to complete your registration. Please check your inbox.', 200)
|
|
|
|
|
|
class SeedRetrieveCreate(APIView):
|
|
def post(self, request):
|
|
try:
|
|
username = request.data['username']
|
|
password = request.data[r'password']
|
|
realm = request.data['realm']
|
|
except KeyError:
|
|
return Response('You need to specify username, password, and realm values', 400)
|
|
# authenticate the user against ldap
|
|
|
|
user = authenticate(username=username, password=password)
|
|
if user is not None:
|
|
|
|
req = requests.get(otp_url, data=json.dumps(
|
|
{
|
|
'auth_token': TOTP(admin_seed).now(),
|
|
'auth_name': admin_name,
|
|
'auth_realm': admin_realm}), headers={'Content-Type': 'application/json'})
|
|
|
|
response_data = json.loads(req.text)
|
|
|
|
for elem in response_data:
|
|
if elem['name'] == username and elem['realm'] == realm:
|
|
return Response(elem, 200)
|
|
# If doesn't find a match then check if the realm is allowed and create the user
|
|
allowed_realms = config('ALLOWED_REALMS', cast=Csv())
|
|
if realm not in allowed_realms:
|
|
return Response('Not allowed to perform this action.', 403)
|
|
else:
|
|
req = requests.post(otp_url, data=json.dumps(
|
|
{
|
|
'auth_token': TOTP(admin_seed).now(),
|
|
'auth_name': admin_name,
|
|
'auth_realm': admin_realm,
|
|
'name': username,
|
|
'realm': realm
|
|
}), headers={'Content-Type': 'application/json'})
|
|
if req.status_code == 201:
|
|
msg = json.loads(req.text)
|
|
return Response(msg, 201)
|
|
else:
|
|
return Response(json.loads(req.text), req.status_code)
|
|
|
|
else:
|
|
return Response('Invalid Credentials', 400)
|
|
|
|
|
|
class Seeds(LoginRequiredMixin, View):
|
|
login_url = reverse_lazy('login_index')
|
|
def get(self, request):
|
|
seedlist = []
|
|
response = requests.get(
|
|
otp_url,
|
|
headers={'Content-Type': 'application/json'},
|
|
data=json.dumps(
|
|
{'auth_name': admin_name, 'auth_realm': admin_realm, 'auth_token': TOTP(admin_seed).now()}))
|
|
response_data = json.loads(response.text)
|
|
for i in range(len(response_data)):
|
|
if response_data[i]['name'] == request.user.username:
|
|
value = {'realm': response_data[i]['realm'], 'seed': response_data[i]['seed']}
|
|
seedlist.append(value)
|
|
return render(request, 'seed_list.html', {'seed': seedlist})
|