views.py 24.8 KB
Newer Older
Nico Schottelius's avatar
Nico Schottelius committed
1
# Imports from django
Marcus Przyklink's avatar
Marcus Przyklink committed
2
from django.shortcuts import render
PCoder's avatar
PCoder committed
3
from django.views.generic import View, FormView
4
from django.contrib.auth import authenticate, login, logout
William Colmenares's avatar
William Colmenares committed
5
from django.contrib.auth.models import User
PCoder's avatar
PCoder committed
6
from django.http import HttpResponse
7
from django.core.validators import validate_email, ValidationError
Marcus Przyklink's avatar
Marcus Przyklink committed
8
from django.urls import reverse_lazy
9
from django.contrib.auth.tokens import PasswordResetTokenGenerator
10
from django.core.mail import EmailMessage
11
from django.views.decorators.cache import cache_control
William Colmenares's avatar
William Colmenares committed
12 13
from rest_framework.views import APIView
from rest_framework.response import Response
14
from .models import ResetToken
PCoder's avatar
PCoder committed
15
from .forms import LoginForm
PCoder's avatar
PCoder committed
16
from .ungleich_ldap import LdapManager
17

18 19 20 21
import logging

logger = logging.getLogger(__name__)

22
# Imports for the extra stuff not in django
Nico Schottelius's avatar
Nico Schottelius committed
23

24 25
from base64 import b64encode, b64decode
from datetime import datetime
Nico Schottelius's avatar
Nico Schottelius committed
26

27 28
from random import choice, randint
import string
Marcus Przyklink's avatar
Marcus Przyklink committed
29

30
from django.conf import settings
31
from django.contrib.auth.mixins import LoginRequiredMixin
Marcus Przyklink's avatar
Marcus Przyklink committed
32

33

34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
def activate_account_link(base_url, user, pwd, firstname, lastname, email, epochutc):
    tokengen = PasswordResetTokenGenerator()
    pseudouser = PseudoUser()
    token = tokengen.make_token(pseudouser)
    buser = bytes(user, 'utf-8')
    bpwd = bytes(pwd, 'utf-8')
    bfirstname = bytes(firstname, 'utf-8')
    blasttname = bytes(lastname, 'utf-8')
    bemail = bytes(email, 'utf-8')
    userpart = b64encode(buser)
    pwdpart = b64encode(bpwd)
    fnpart = b64encode(bfirstname)
    lnpart = b64encode(blasttname)
    mailpart = b64encode(bemail)
    # create entry into the database
    newdbentry = ResetToken(user=user, token=token, creation=epochutc)
    newdbentry.save()
    # set up the link
52
    link = "{base_url}/activate/{user}/{pwd}/{fn}/{ln}/{mail}/{token}/".format(
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
        base_url=base_url, user=userpart.decode('utf-8'),
        pwd=pwdpart.decode('utf-8'),
        fn=fnpart.decode('utf-8'),
        ln=lnpart.decode('utf-8'),
        mail=mailpart.decode('utf-8'),
        token=token
    )
    return link


def clean_db():
    """Revoves outdated tokens"""
    # cutoff time is set to 24h hours
    # using utcnow() to have no headache with timezones
    cutoff = int(datetime.utcnow().timestamp()) - (24*60*60)
    # Get all tokens older than 24 hours
    oldtokens = ResetToken.objects.all().filter(creation__lt=cutoff)
    for token in oldtokens:
        # delete all tokens older than 24 hours
        token.delete()
    return True

PCoder's avatar
PCoder committed
75 76 77
class Index(FormView):
    template_name = "landing.html"
    form_class = LoginForm
PCoder's avatar
PCoder committed
78 79 80
    success_url = 'useroptions.html'

    def form_valid(self, form):
81
        username = form.cleaned_data.get('username')
PCoder's avatar
PCoder committed
82
        password = form.cleaned_data.get('password')
83
        user = authenticate(username=username, password=password)
PCoder's avatar
PCoder committed
84 85 86 87
        if user is not None:
            login(self.request, user)
            return render(self.request, 'useroptions.html', { 'user': user } )
        return render(self.request, 'loginfailed.html')
Marcus Przyklink's avatar
Marcus Przyklink committed
88

89 90 91 92 93 94 95 96
    @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)


Marcus Przyklink's avatar
Marcus Przyklink committed
97 98 99 100 101 102
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):
103
        service = 'register an user'
Marcus Przyklink's avatar
Marcus Przyklink committed
104 105
        urlname = 'register'
        username = request.POST.get('username')
106

107 108
        if username == "" or not username:
            return render(request, 'error.html', { 'urlname': urlname, 'service': service, 'error': 'Please supply a username.' } )
109

Marcus Przyklink's avatar
Marcus Przyklink committed
110 111 112
        password1 = request.POST.get('password1')
        password2 = request.POST.get('password2')
        if password1 != password2:
113 114
            return render(request, 'error.html', { 'urlname': urlname,
                                                   'service': service,
115
                                                   'error': "Passwords don't match." } )
Marcus Przyklink's avatar
Marcus Przyklink committed
116

Marcus Przyklink's avatar
Marcus Przyklink committed
117
        email = request.POST.get('email')
118 119 120
        try:
            validate_email(email)
        except ValidationError:
121 122 123
            return render(request, 'error.html', { 'urlname': urlname,
                                                   'service': service,
                                                   'error': 'The supplied email address is invalid.' } )
124

Marcus Przyklink's avatar
Marcus Przyklink committed
125 126
        firstname = request.POST.get('firstname')
        lastname = request.POST.get('lastname')
127 128 129 130
        if not firstname or not lastname:
            return render(request, 'error.html', { 'urlname': urlname,
                                                   'service': service,
                                                   'error': 'Please enter your firstname and lastname.' } )
Marcus Przyklink's avatar
Marcus Przyklink committed
131

132 133
        # so nothing strange happens if there are escapable chars
        pwd = r'%s' % password1
Marcus Przyklink's avatar
Marcus Przyklink committed
134

135
        try:
136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151
            creationtime = int(datetime.utcnow().timestamp())
            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
PCoder's avatar
PCoder committed
152
            )
153 154
            mail.send()

155 156 157 158
        except Exception as e:
            return render(request, 'error.html', { 'urlname': urlname,
                                                   'service': service,
                                                   'error': e } )
159
        return render(request, 'confirm_email.html')
160

161
class ChangeData(LoginRequiredMixin, View):
162
    login_url = reverse_lazy('login_index')
Marcus Przyklink's avatar
Marcus Przyklink committed
163 164
    # provide the form for the change request
    def get(self, request):
165 166
        urlname = 'change_data'
        service = 'get default data for logged in user'
167

PCoder's avatar
PCoder committed
168
        user = request.user
169 170 171
        ldap_manager = LdapManager()
        user_exists, entries = ldap_manager.check_user_exists(
            uid=user.username,
PCoder's avatar
PCoder committed
172 173
            attributes=['uid', 'givenName', 'sn', 'mail'],
            search_base=settings.ENTIRE_SEARCH_BASE
174 175 176 177 178 179 180 181 182 183 184
        )

        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 '',
PCoder's avatar
PCoder committed
185 186
                  'email': entries[0].mail
                            if entries[0].mail.value is not None else ''}
187
            )
188
        else:
189 190 191
            return render(request, 'error.html',
                          {'urlname': urlname, 'service': service,
                           'error': request.user.username})
Marcus Przyklink's avatar
Marcus Przyklink committed
192 193 194 195

    # get the change request
    def post(self, request):
        # variables for the error page
196
        service = 'change user data'
Marcus Przyklink's avatar
Marcus Przyklink committed
197 198 199 200 201
        urlname = 'change_data'

        firstname = request.POST.get('firstname')
        lastname = request.POST.get('lastname')
        email = request.POST.get('email')
Nico Schottelius's avatar
Nico Schottelius committed
202

Marcus Przyklink's avatar
Marcus Przyklink committed
203 204 205 206 207 208 209
        # 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.' } )
210 211 212
        try:
            validate_email(email)
        except ValidationError:
Marcus Przyklink's avatar
Marcus Przyklink committed
213
            return render(request, 'error.html', { 'urlname': urlname, 'service': service, 'error': 'The supplied email address is invalid.' } )
PCoder's avatar
PCoder committed
214 215 216 217 218 219 220

        ldap_manager = LdapManager()
        result, msg = ldap_manager.change_user_details(
            uid=request.user.username,
            details={"givenName": firstname, "sn": lastname, "mail": email}
        )

221
        # Data change worked
PCoder's avatar
PCoder committed
222 223
        if result:
            return render(request, 'changeddata.html', { 'user': request.user.username, 'firstname': firstname, 'lastname': lastname, 'email': email } )
224 225
        # Data change did not work, display error
        else:
PCoder's avatar
PCoder committed
226
            return render(request, 'error.html', { 'urlname': urlname, 'service': service, 'error': msg } )
Marcus Przyklink's avatar
Marcus Przyklink committed
227 228


229
class ResetPassword(View):
Marcus Przyklink's avatar
Marcus Przyklink committed
230 231 232 233
    def get(self, request):
        return render(request, 'resetpassword.html')

    def post(self, request):
234 235
        urlname = 'reset_password'
        service = 'send a password reset request'
Marcus Przyklink's avatar
Marcus Przyklink committed
236
        user = request.POST.get('user')
237
        # First, check if the user exists
PCoder's avatar
PCoder committed
238 239 240
        ldap_manager = LdapManager()
        user_exists, entries = ldap_manager.check_user_exists(
            uid=user,
PCoder's avatar
PCoder committed
241
            search_base=settings.ENTIRE_SEARCH_BASE,
PCoder's avatar
PCoder committed
242 243
            attributes=['uid', 'givenName', 'sn', 'mail']
        )
244
        if user_exists:
PCoder's avatar
PCoder committed
245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272
            # 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}
                )
273
        else:
PCoder's avatar
PCoder committed
274 275 276 277 278
            return render(
                request, 'error.html',
                { 'urlname': urlname, 'service': service,
                  'error': 'The user does not exist.' }
            )
279

Marcus Przyklink's avatar
Marcus Przyklink committed
280
    # Sends an email to the user with the 24h active link for a password reset
281
    def email(self, user, email, base_url):
Marcus Przyklink's avatar
Marcus Przyklink committed
282 283
        # getting epoch for the time now in UTC to spare us headache with timezones
        creationtime = int(datetime.utcnow().timestamp())
284
        # Construct the data for the email
PCoder's avatar
PCoder committed
285
        email_from = settings.EMAIL_FROM_ADDRESS
286
        to = ['%s <%s>' % (user, email)]
287
        subject = 'Password reset request for %s' % user
288
        link = self.build_reset_link(user, creationtime, base_url)
289
        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
290 291 292
        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
Marcus Przyklink's avatar
Marcus Przyklink committed
293
        body += 'The link will remain active for 24 hours.\n'
294 295 296 297 298 299 300 301 302 303 304 305 306
        # 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
307

Marcus Przyklink's avatar
Marcus Przyklink committed
308
    # Builds the reset link for the email and puts the token into the database
309
    def build_reset_link(self, user, epochutc, base_url):
Nico Schottelius's avatar
Nico Schottelius committed
310
        # set up the data
Marcus Przyklink's avatar
Marcus Przyklink committed
311
        tokengen = PasswordResetTokenGenerator()
312 313 314
        # create some noise for use in the tokengenerator
        pseudouser = PseudoUser()
        token = tokengen.make_token(pseudouser)
Marcus Przyklink's avatar
Marcus Przyklink committed
315 316
        buser = bytes(user, 'utf-8')
        userpart = b64encode(buser)
Nico Schottelius's avatar
Nico Schottelius committed
317
        # create entry into the database
Marcus Przyklink's avatar
Marcus Przyklink committed
318 319 320
        newdbentry = ResetToken(user=user, token=token, creation=epochutc)
        newdbentry.save()
        # set up the link
321 322 323 324
        link = "{base_url}/reset/{user}/{token}/".format(
            base_url=base_url,user=userpart.decode('utf-8'),token=token
        )
        logger.debug("User reset url is {}".format(link))
325 326 327
        return link


Marcus Przyklink's avatar
Marcus Przyklink committed
328
# Catch the resetrequest URL and check it
329 330 331 332 333 334
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
Marcus Przyklink's avatar
Marcus Przyklink committed
335 336
        # 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
337
        clean_db()
Marcus Przyklink's avatar
Marcus Przyklink committed
338 339
        # If user and token are not supplied by django, it was called from somewhere else, so it's
        # invalid
340 341 342 343 344 345
        if user == None or token == None:
            return HttpResponse('Invalid URL.', status=404)
        # extract user from b64 format
        tmp_user = bytes(user, 'utf-8')
        user = b64decode(tmp_user)
        user_clean = user.decode('utf-8')
Marcus Przyklink's avatar
Marcus Przyklink committed
346
        # set checks_out = True if token is found in database
347
        checks_out = False
Marcus Przyklink's avatar
Marcus Przyklink committed
348 349 350 351 352 353 354
        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
355 356
        if not checks_out:
            return HttpResponse('Invalid URL.', status=404)
Marcus Przyklink's avatar
Marcus Przyklink committed
357
        # Token was found, supply the form
358 359 360 361 362 363 364
        else:
            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'
Marcus Przyklink's avatar
Marcus Przyklink committed
365
        # get the supplied passwords
366 367
        password1 = request.POST.get("password1")
        password2 = request.POST.get("password2")
Marcus Przyklink's avatar
Marcus Przyklink committed
368
        # get the hidden value of user
369
        user = request.POST.get("user")
Marcus Przyklink's avatar
Marcus Przyklink committed
370
        # some checks over the supplied data
William Colmenares's avatar
William Colmenares committed
371
        if user == "" or not user:
Marcus Przyklink's avatar
Marcus Przyklink committed
372
            return render(request, 'error.html', { 'service': service, 'error': 'Something went wrong. Did you use the supplied form?' } )
373 374 375
        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:
376
            return render(request, 'error.html', {'service': service, 'error': 'The supplied passwords do not match.'})
377
        if len(password1) < 8:
378 379
            return render(request, 'error.html', {'service': service,
                                                  'error': 'The password is too short, please use a longer one. At least 8 characters.'})
380 381 382

        ldap_manager = LdapManager()
        result = ldap_manager.change_password(
383
            user,
384 385 386 387
            password1
        )
        # password change successful
        if result:
388
            return render(request, 'changedpassword.html', { 'user': user } )
Marcus Przyklink's avatar
Marcus Przyklink committed
389
        # Something went wrong while changing the password
390 391 392
        else:
            return render(request, 'error.html', { 'service': service, 'error': result } )

393

Marcus Przyklink's avatar
Marcus Przyklink committed
394 395 396

# The logged in user can change the password here

397
class ChangePassword(LoginRequiredMixin, View):
398
    login_url = reverse_lazy('login_index')
Marcus Przyklink's avatar
Marcus Przyklink committed
399 400 401 402 403
    # 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 } )
Nico Schottelius's avatar
Nico Schottelius committed
404

Marcus Przyklink's avatar
Marcus Przyklink committed
405 406 407 408 409 410 411 412
    # 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')
413 414
        login(request, request.user)

415
        user = str(request.user)
Marcus Przyklink's avatar
Marcus Przyklink committed
416 417 418 419 420 421 422 423 424 425
        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:
Nico Schottelius's avatar
Nico Schottelius committed
426
            return render(request, 'error.html', { 'urlname': urlname, 'service': service,
Marcus Przyklink's avatar
Marcus Przyklink committed
427
                'error': 'Please check if you typed the same password both times for the new password' } )
428 429 430 431
        # 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.' } )
PCoder's avatar
PCoder committed
432 433 434
        from .ungleich_ldap import LdapManager
        ldap_manager = LdapManager()
        result = ldap_manager.change_password(
435
            user,
PCoder's avatar
PCoder committed
436 437
            password1
        )
438
        # Password was changed
PCoder's avatar
PCoder committed
439
        if result:
PCoder's avatar
PCoder committed
440
            logout(request)
Marcus Przyklink's avatar
Marcus Przyklink committed
441
            return render(request, 'changedpassword.html', { 'user': user } )
442
        # Password not changed, instead got some kind of error
Marcus Przyklink's avatar
Marcus Przyklink committed
443
        else:
444
            return render(request, 'error.html', { 'urlname': urlname, 'service': service, 'error': result } )
Marcus Przyklink's avatar
Marcus Przyklink committed
445 446


447
# Deletes an account
448
class DeleteAccount(LoginRequiredMixin, View):
449
    login_url = reverse_lazy('login_index')
450
    # Show the basic form for deleting an account
Marcus Przyklink's avatar
Marcus Przyklink committed
451
    def get(self, request):
Marcus Przyklink's avatar
Marcus Przyklink committed
452
        return render(request, 'deleteaccount.html')
Marcus Przyklink's avatar
Marcus Przyklink committed
453

454
    # Reads the filled out form
Marcus Przyklink's avatar
Marcus Przyklink committed
455 456 457 458 459 460
    def post(self, request):
        # Variables for error page
        urlname = 'account_delete'
        service = 'delete an account'

        # Does the user exist?
461
        username = request.POST.get('username')
462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482
        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})
483
        else:
PCoder's avatar
PCoder committed
484
            return render(request, 'error.html', { 'urlname': urlname, 'service': service, 'error': 'Unknown user.' } )
Marcus Przyklink's avatar
Marcus Przyklink committed
485

Marcus Przyklink's avatar
Marcus Przyklink committed
486
# Log out the session
487 488 489
class LogOut(View):
    def get(self, request):
        logout(request)
490 491 492 493 494 495 496
        return HttpResponse(
            "You have been logged out. You will be redirected in 2 seconds."
            "<script>"
            "setTimeout(function (){document.location.href='/';}, 2000);"
            "</script>",
            status=200
        )
497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516



# TO be clarified

# To trick the tokengenerator to work with us, because we don't really
# have the expected user Class since we are reading the user from a form
# We store the tokens and don't have to use the check function,
# some one time data works fine.

class LastLogin():
    def replace(self, microsecond=0, tzinfo=None):
        return randint(1,100000)

class PseudoUser():
    # easiest way to handle the check for lastlogin
    last_login = LastLogin()
    # random alphanumeric strings for primary key and password, just used for token generation
    pk = ''.join(choice(string.ascii_letters + string.digits) for _ in range(20))
    password = ''.join(choice(string.ascii_letters + string.digits) for _ in range(30))
William Colmenares's avatar
William Colmenares committed
517

518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558

class ActivateAccount(View):

    def get(self, request, user=None, pwd=None, firstname=None, lastname=None, email=None, token=None):
        clean_db()
        if token is None:
            return HttpResponse('Invalid URL', status=404)
        elem_list = [user, pwd, firstname, lastname, email]
        clean_list = []
        for value in elem_list:
            try:
                value_temp = bytes(value, 'utf-8')
                value_decode = b64decode(value_temp)
                value_clean = value_decode.decode('utf-8')
                clean_list.append(value_clean)
            except Exception as e:
                return HttpResponse('Invalid URL', status=404)
        checks_out = False
        dbentries = ResetToken.objects.all().filter(user=clean_list[0])
        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, create user
        try:
            ldap_manager = LdapManager()
            ldap_manager.create_user(
                clean_list[0], clean_list[1], clean_list[2], clean_list[3], clean_list[4]
            )
            #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': clean_list[0] } )


William Colmenares's avatar
William Colmenares committed
559 560 561
class UserCreateAPI(APIView):

    def post(self, request):
562

William Colmenares's avatar
William Colmenares committed
563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578
        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)
        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 = r'%s' % User.objects.make_random_password()

579 580
        base_url = "{0}://{1}".format(self.request.scheme,
                                      self.request.get_host())
William Colmenares's avatar
William Colmenares committed
581
        creationtime = int(datetime.utcnow().timestamp())
582 583
        link = activate_account_link(base_url, username, pwd, firstname, lastname, email. creationtime)

William Colmenares's avatar
William Colmenares committed
584 585 586
        # Construct the data for the email
        email_from = settings.EMAIL_FROM_ADDRESS
        to = ['%s <%s>' % (username, email)]
587 588 589 590 591
        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)
William Colmenares's avatar
William Colmenares committed
592 593 594
        body += 'Your credentials are:\n'
        body += 'Username: %s\n\n' % username
        body += 'Password: %s\n\n' % pwd
595 596
        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'
William Colmenares's avatar
William Colmenares committed
597 598 599 600 601 602 603 604 605 606
        # Build the email
        mail = EmailMessage(
            subject=subject,
            body=body,
            from_email=email_from,
            to=to
        )
        try:
            mail.send()
        except:
607 608
            return Response('Failed to send the email', 201)
        return Response('Email with activation link successfully sent', 200)