def authenticate_users(email, password): """ Yup, this is a 'util' method instead of a proper authentication backend. The reason for this is that as our membersystem allows duplicate email fields, a user can potentially authenticate herself for multiple accounts, and the Django auth backend system doesn't account for that (it returns exactly one user, or None). """ email = email.strip() # Support this special case explicitly because it will hit a lot of Actors # and check for a matching User for each of them, which takes a long time if email == '': return [] # Add matching local users that aren't members matches = [ u for u in User.get_users().filter( memberid__isnull=True, email__iexact=email ) if u.check_password(password) ] # Add matching members in Actor for actor in Actor.get_personal_members().filter(email__iexact=email): try: # Ok, look for any matching active user user = User.get_users( include_pending=True, include_expired=True ).get( memberid=actor.memberid, is_inactive=False # ignore inactive users ) # Reset state if this user was previously pending but is now a # proper member if user.is_pending: user.is_pending = False user.save() # Reset state if this user was previously marked as expired for # some reason if user.is_expired: user.is_expired = False user.save() # Now perform the password check for authentication if user.check_password(password): matches.append(user) except User.DoesNotExist: pass # Add matching pending members for enrollment in Enrollment.filter_on_email(email): try: # Ok, look for any matching active AND pending user user = User.get_users( include_pending=True, include_expired=True ).get( memberid=enrollment.memberid, is_pending=True, is_inactive=False # ignore inactive users ) # Reset state if this user was previously marked as expired for # some reason if user.is_expired: user.is_expired = False user.save() # Now perform the password check for authentication # Check that the user isn't already matched as an Actor since this # theoretically could be a duplicate if user.check_password(password) and user not in matches: matches.append(user) except User.DoesNotExist: pass # And just return these matches return matches
def send_restore_password_email(request): if 'email' not in request.POST: raise PermissionDenied email = request.POST['email'].strip() if not validator.email(email): return HttpResponse(json.dumps({'status': 'invalid_email'})) # The address might match one non-member, check it: local_matches = list(User.objects.filter(memberid__isnull=True, email=email)) # The address might match several members, registered or not focus_unregistered_matches = False # Search through matching Actors for actor in Actor.get_personal_members().filter(email=email): try: # Ok, look for any matching active user user = User.get_users( include_pending=True, include_expired=True ).get( memberid=actor.memberid, is_inactive=False # ignore inactive users; these need to register first ) # Reset state if this user was previously pending but is now a proper member if user.is_pending: user.is_pending = False user.save() # Reset state if this user was previously marked as expired for some reason if user.is_expired: user.is_expired = False user.save() local_matches.append(user) except User.DoesNotExist: # There is an actor but no corresponding user - inform the user that they need to register focus_unregistered_matches = True # Now search through matching active enrollments for enrollment in Enrollment.filter_on_email(email): try: # Ok, look for any matching active AND pending user user = User.get_users( include_pending=True, include_expired=True ).get( memberid=enrollment.memberid, is_pending=True, is_inactive=False # ignore inactive users; these need to register first ) # Reset state if this user was previously marked as expired for some reason if user.is_expired: user.is_expired = False user.save() # Check that the user isn't already matched as an Actor since this theoretically could be a duplicate if user not in local_matches: local_matches.append(user) except User.DoesNotExist: pass if len(local_matches) == 0: # No email-address matches. if focus_unregistered_matches: # Oh, the email address exists in Focus, but the user(s) aren't in our user-base. Let them know. return HttpResponse(json.dumps({'status': 'unregistered_email'})) else: return HttpResponse(json.dumps({'status': 'unknown_email'})) else: for user in local_matches: key = crypto.get_random_string(length=settings.RESTORE_PASSWORD_KEY_LENGTH) while User.objects.filter(password_restore_key=key).exists(): # Ensure that the key isn't already in use. With the current key length of 40, we'll have # ~238 bits of entropy which means that this will never ever happen, ever. # You will win the lottery before this happens. And I want to know if it does, so log it. logger.warning( "Noen fikk en random-generert password-restore-key som allerede finnes!", extra={ 'request': request, 'should_you_play_the_lottery': True, 'key': key } ) key = crypto.get_random_string(length=settings.RESTORE_PASSWORD_KEY_LENGTH) user.password_restore_key = key user.password_restore_date = datetime.now() user.save() if len(local_matches) == 1: context = { 'found_user': user, 'validity_period': settings.RESTORE_PASSWORD_VALIDITY, } message = render_to_string('common/user/login/restore-password-email.txt', context, request=request) else: context = { 'users': local_matches, 'validity_period': settings.RESTORE_PASSWORD_VALIDITY, } message = render_to_string( 'common/user/login/restore-password-email-multiple.txt', context, request=request, ) send_mail("Nytt passord på Min side", message, settings.DEFAULT_FROM_EMAIL, [email]) return HttpResponse(json.dumps({'status': 'success'}))