def update_from_enrollment(self):
        t1 = time.time()
        if self.print_log:
            print("  update_from_enrollment()")

        self.main_stats['enrollment']['started'] = str(datetime.now())

        actors = Enrollment.get_active().order_by('tempid')
        bulk_count = 10000
        p = Paginator(actors, bulk_count)

        self.main_stats['enrollment']['num_pages'] = p.num_pages
        self.main_stats['enrollment']['pages'] = {}

        if self.task_log:
            self.task_log.data = self.main_stats
            self.task_log.save()

        self.update(p, 'enrollment')

        self.main_stats['enrollment']['ended'] = str(datetime.now())
        self.main_stats['enrollment']['duration'] = time.time() - t1

        if self.task_log:
            self.task_log.data = self.main_stats
            self.task_log.save()

        if self.print_log:
            self.print_stats(self.main_stats['enrollment'], 4)
            print("  update_from_enrollment() done in %0.2fs"
                  % self.main_stats['enrollment']['duration'])
Example #2
0
 def get_children(self):
     # Assuming that there aren't members in the Actor table that could be connected to this member.
     children = cache.get('focus.enrollment.children.%s' % self.enrollment.memberid)
     if children is None:
         children = Enrollment.get_active().filter(linked_to=self.enrollment.memberid).exclude(pk=self.enrollment.pk)
         cache.set('focus.enrollment.children.%s' % self.enrollment.memberid, children, settings.FOCUS_MEMBER_CACHE_PERIOD)
     return children
 def __init__(self, memberid):
     try:
         self.enrollment = cache.get('focus.enrollment.%s' % memberid)
         if self.enrollment is None:
             self.enrollment = Enrollment.get_active().get(memberid=memberid)
             cache.set('focus.enrollment.%s' % memberid, self.enrollment, settings.FOCUS_MEMBER_CACHE_PERIOD)
     except Enrollment.DoesNotExist:
         # The referenced memberid doesn't exist among active pending members. This is expected if the pending member
         # was accepted and is now an Actor; let the User object handle that case.
         raise ExpiredMember("Enrollment with memberid '%s' does not exist" % memberid)
Example #4
0
def verify_memberid(ip_address, memberid, country_code, zipcode):
    """
    Returns a User object matching the given memberid, country code, and if
    Norwegian; also zipcode. Includes pending users by default. Raises a range
    of exceptions if the provided data is invalid or does not match any user:
    - MemberidLookupsExceeded: If the client IP address has performed more
      lookups than we allowed
    - CountryDoesNotExist: If the given country code is invalid
    - ActorIsNotPersonalMember: If the data matches an Actor in Focus who
      isn't a personal member
    - NoMatchingMemberid: If the data doesn't match any member
    """
    # Check that the memberid is correct (and retrieve the Actor-entry)
    if memberid_lookups_exceeded(ip_address):
        raise MemberidLookupsExceeded

    if not FocusCountry.objects.filter(code=country_code).exists():
        raise CountryDoesNotExist

    # Not filtering on Actor.get_personal_members() in order to raise explicit
    # exception for non-personal-membership matches; see below
    actor = Actor.objects.filter(
        memberid=memberid,
        address__country_code=country_code
    )

    # Require correct zipcode for domestic members
    if country_code == 'NO':
        actor = actor.filter(address__zipcode=zipcode)

    if actor.exists():
        actor = actor.get()

        # Check that it's a proper member object (note that we didn't filter
        # the query on Actor.get_personal_members())
        if not actor.is_personal_member():
            raise ActorIsNotPersonalMember

        return User.get_or_create_inactive(memberid=actor.memberid)

    # No matching actors, check for pending users
    enrollment = Enrollment.get_active().filter(memberid=memberid)

    # Require correct zipcode for domestic members
    if country_code == 'NO':
        enrollment = enrollment.filter(zipcode=zipcode)

    if enrollment.exists():
        return User.get_or_create_inactive(memberid=memberid)

    # No matches
    raise NoMatchingMemberid
    def get_children(self):
        from user.models import User

        # Only look for children among other pending users. There could theoretically be children that are proper
        # members, but in most such cases the current pending member would also have been accepted, so don't handle it.
        enrollment_children = Enrollment.get_active().filter(
            parent=self.enrollment.memberid,
        ).exclude(
            pk=self.enrollment.pk,
        )
        return [
            User.get_or_create_inactive(memberid=enrollment_child.memberid)
            for enrollment_child in enrollment_children
        ]
Example #6
0
def search(request):
    if not request.is_ajax() or request.method != 'POST':
        return redirect('admin.users.views.index')

    if len(request.POST['q']) < settings.ADMIN_USER_SEARCH_CHAR_LENGTH:
        raise PermissionDenied

    local_users = User.get_users().filter(memberid__isnull=True)
    for word in request.POST['q'].split():
        local_users = local_users.filter(
            Q(first_name__icontains=word) |
            Q(last_name__icontains=word))
    local_users = local_users.order_by('first_name')

    actors = Actor.get_personal_members()
    for word in request.POST['q'].split():
        actors = actors.filter(
            Q(first_name__icontains=word) |
            Q(last_name__icontains=word) |
            Q(memberid__icontains=word))
    actors = actors.order_by('first_name')

    # Match expired users only on memberid
    expired_users = User.objects.all()
    for word in request.POST['q'].split():
        expired_users = expired_users.filter(memberid__icontains=word)
    expired_users = [u for u in expired_users if not Actor.get_personal_members().filter(memberid=u.memberid).exists()]

    # Pending users
    pending_enrollment = Enrollment.get_active()
    for word in request.POST['q'].split():
        pending_enrollment = pending_enrollment.filter(
            Q(first_name__icontains=word) |
            Q(last_name__icontains=word) |
            Q(memberid__icontains=word))
    pending_enrollment = pending_enrollment.order_by('first_name')

    members = User.get_users().filter(memberid__in=[a.memberid for a in actors])
    pending_users = User.get_users(include_pending=True).filter(memberid__in=[e.memberid for e in pending_enrollment])
    actors_without_user = [a for a in actors if a.memberid not in list(members.values_list('memberid', flat=True))]
    users = list(local_users) + list(members) + list(pending_users)

    context = RequestContext(request, {
        'users': users,
        'actors_without_user': actors_without_user,
        'expired_users': expired_users
    })
    return HttpResponse(render_to_string('common/admin/users/user_results.html', context))
Example #7
0
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
Example #8
0
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'}))
Example #9
0
    def get_queryset(self):
        # This method uses Focus and local users to compile a search result
        # containing users.

        # See the comment on the class definition of the lazy_queryset variable
        if self.lazy_queryset:
            return self.lazy_queryset

        SEARCH_MAX_HITS = 100
        search = self.request.query_params.get('search', '').strip()

        if len(search) < settings.ADMIN_USER_SEARCH_CHAR_LENGTH:
            raise PermissionDenied

        local_users = User.get_users().filter(memberid__isnull=True)
        for word in search.split():
            local_users = local_users.filter(
                Q(first_name__icontains=word) |
                Q(last_name__icontains=word) |
                Q(email__icontains=word))
        local_users = local_users.order_by('first_name')[:SEARCH_MAX_HITS]

        actors = Actor.get_personal_members().select_related(
            'address', 'address__zipcode')
        for word in search.split():
            actors = actors.filter(
                Q(first_name__icontains=word) |
                Q(last_name__icontains=word) |
                Q(memberid__icontains=word) |
                Q(email__icontains=word))
        actors = actors.order_by('first_name')[:SEARCH_MAX_HITS]

        # Cache all the actor objects because we will be accessing them through
        # user objects later
        Actor.cache_all(actors)

        # Match expired users only on memberid
        expired_users = User.objects.all()
        for word in search.split():
            expired_users = expired_users.filter(memberid__icontains=word)
        expired_users = [
            u for u in expired_users
            if not Actor.get_personal_members().only('id').filter(
                memberid=u.memberid).exists() and not Enrollment.get_active()
            .only('id').filter(memberid=u.memberid).exists()
        ]

        # Pending users
        pending_enrollment = Enrollment.get_active()
        for word in search.split():
            pending_enrollment = pending_enrollment.filter(
                Q(first_name__icontains=word) |
                Q(last_name__icontains=word) |
                Q(memberid__icontains=word))
        pending_enrollment = pending_enrollment.order_by(
            'first_name')[:SEARCH_MAX_HITS]

        members = User.get_users().filter(
            memberid__in=[a.memberid for a in actors])
        user_member_ids = list(members.values_list('memberid', flat=True))
        pending_users = User.get_users(include_pending=True).filter(
            memberid__in=[e.memberid for e in pending_enrollment])

        # Create inactive users for any actor hit without a corresponding user
        newly_created_members = [
            User.get_or_create_inactive(memberid=a.memberid) for a in actors
            if a.memberid not in user_member_ids
        ]

        users = sorted(
            list(local_users) + list(members) + list(pending_users) +
            list(newly_created_members),
            key=lambda u: u.get_full_name())

        self.lazy_queryset = users
        return users
Example #10
0
def search(request):
    if not request.is_ajax() or request.method != 'POST':
        return redirect('admin:users.index')

    if len(request.POST['q']) < settings.ADMIN_USER_SEARCH_CHAR_LENGTH:
        raise PermissionDenied

    SEARCH_MAX_HITS = 40

    local_users = User.get_users().filter(
        memberid__isnull=True,
    ).prefetch_related(
        'permissions',
    )
    for word in request.POST['q'].split():
        local_users = local_users.filter(
            Q(first_name__icontains=word) |
            Q(last_name__icontains=word) |
            Q(email__icontains=word)
        )
    local_users = local_users.order_by('first_name')[:SEARCH_MAX_HITS]

    actors = Actor.get_personal_members().select_related('address', 'address__zipcode')
    for word in request.POST['q'].split():
        actors = actors.filter(
            Q(first_name__icontains=word) |
            Q(last_name__icontains=word) |
            Q(email__icontains=word) |
            Q(memberid__icontains=word)
        )
    actors = actors.order_by('first_name')[:SEARCH_MAX_HITS]

    # Cache all the actor objects because we will be accessing them through user objects later
    Actor.cache_all(actors)

    # Match expired users only on memberid
    expired_users = User.objects.all()
    for word in request.POST['q'].split():
        expired_users = expired_users.filter(memberid__icontains=word)
    expired_users = [
        u for u in expired_users
        if not Actor.get_personal_members().only('id').filter(memberid=u.memberid).exists()
        and not Enrollment.get_active().only('id').filter(memberid=u.memberid).exists()
    ]

    # Pending users
    pending_enrollment = Enrollment.get_active()
    for word in request.POST['q'].split():
        pending_enrollment = pending_enrollment.filter(
            Q(first_name__icontains=word) |
            Q(last_name__icontains=word) |
            Q(memberid__icontains=word)
        )
    pending_enrollment = pending_enrollment.order_by('first_name')[:SEARCH_MAX_HITS]

    max_hits_exceeded = len(local_users) >= SEARCH_MAX_HITS or \
        len(actors) >= SEARCH_MAX_HITS or \
        len(pending_enrollment) >= SEARCH_MAX_HITS

    members = User.get_users().filter(
        memberid__in=[a.memberid for a in actors],
    ).prefetch_related(
        'permissions',
    )
    user_member_ids = list(members.values_list('memberid', flat=True))
    pending_users = User.get_users(include_pending=True).filter(memberid__in=[e.memberid for e in pending_enrollment])
    actors_without_user = [a for a in actors if a.memberid not in user_member_ids]
    users = list(local_users) + list(members) + list(pending_users)

    context = {
        'users': users,
        'actors_without_user': actors_without_user,
        'expired_users': expired_users,
        'max_hits_exceeded': max_hits_exceeded,
    }
    return HttpResponse(render_to_string('central/admin/users/user_results.html', context, request=request))
Example #11
0
    def save_to_focus(self, parent=None):
        first_name, last_name = self.name.rsplit(maxsplit=1)
        total_price = self.price()
        parent = '' if parent is None else str(parent)

        # Yearbook
        receive_yearbook = self.receive_yearbook()
        if self.enrollment.country != 'NO' and receive_yearbook:
            total_price += FOREIGN_SHIPMENT_PRICE

        # Address
        adr1 = self.enrollment.address1
        if self.enrollment.country == 'NO':
            adr2 = ''
            adr3 = ''
            zipcode = self.enrollment.zipcode
            area = self.enrollment.area
        elif self.enrollment.country == 'DK' or self.enrollment.country == 'SE':
            adr2 = ''
            adr3 = "%s-%s %s" % (self.enrollment.country, self.enrollment.zipcode, self.enrollment.area)
            zipcode = '0000'
            area = ''
        else:
            adr2 = self.enrollment.address2
            adr3 = self.enrollment.address3
            zipcode = '0000'
            area = ''

        if self.get_age() >= Enrollment.PARTNEROFFERS_OPTIN_MIN_AGE:
            partneroffers_optin = self.enrollment.partneroffers_optin
        else:
            partneroffers_optin = False

        if partneroffers_optin:
            partneroffers_optin_date = datetime.now()
        else:
            partneroffers_optin_date = None

        # Fetch and increment memberid with stored procedure
        cursor = current_focus_db_connection().cursor()
        cursor.execute("exec sp_custTurist_updateMemberId")
        memberid = cursor.fetchall()[0][0]

        focus_user = FocusEnrollment(
            memberid=memberid,
            last_name=last_name,
            first_name=first_name,
            birth_date=self.dob,
            gender=Actor.convert_gender(self.gender),
            parent=parent,
            adr1=adr1,
            adr2=adr2,
            adr3=adr3,
            country_code=self.enrollment.country,
            phone_home='',
            email=self.email,
            receive_yearbook=receive_yearbook,
            type=self.focus_type(),
            yearbook=ENROLLMENT_YEARBOOK_TYPE if receive_yearbook else '',
            payment_method=PAYMENT_METHOD_CODES[self.enrollment.payment_method],
            phone_mobile=self.phone,
            zipcode_id=zipcode,
            area=area,
            language='nb_no',
            totalprice=total_price,
            partneroffers_optin=partneroffers_optin,
            partneroffers_optin_date=partneroffers_optin_date,
        )
        focus_user.save()

        self.memberid = memberid
        self.save()
Example #12
0
def search(request):
    if not request.is_ajax() or request.method != 'POST':
        return redirect('admin:users.index')

    if len(request.POST['q']) < settings.ADMIN_USER_SEARCH_CHAR_LENGTH:
        raise PermissionDenied

    SEARCH_MAX_HITS = 40

    # Look up non-members in the local user database
    nonmembers = User.get_users().filter(
        memberid__isnull=True,
    ).prefetch_related(
        'user_permission_grants',
        'user_permission_grants__user_permission',
        'user_permission_grants__user_permission__oauth_applications',
    )
    for word in request.POST['q'].split():
        nonmembers = nonmembers.filter(
            Q(first_name__icontains=word) |
            Q(last_name__icontains=word) |
            Q(email__icontains=word)
        )
    nonmembers = nonmembers.order_by('first_name')[:SEARCH_MAX_HITS]

    # Look up members directly in Actors
    actors = Actor.get_personal_members().select_related(
        'address', 'address__zipcode')
    for word in request.POST['q'].split():
        actors = actors.filter(
            Q(first_name__icontains=word) |
            Q(last_name__icontains=word) |
            Q(email__icontains=word) |
            Q(memberid__icontains=word)
        )
    actors = actors.order_by('first_name')[:SEARCH_MAX_HITS]

    # Cache all the actor objects because we will be accessing them through
    # user objects later
    Actor.cache_all(actors)

    # Look up pending members directly in Enrollment
    enrollments = Enrollment.get_active()
    for word in request.POST['q'].split():
        enrollments = enrollments.filter(
            Q(first_name__icontains=word) |
            Q(last_name__icontains=word) |
            Q(memberid__icontains=word)
        )
    enrollments = enrollments.order_by('first_name')[:SEARCH_MAX_HITS]

    # Create a list of all memberids
    focus_memberids = (
        [a.memberid for a in actors]
        + [e.memberid for e in enrollments])

    # Select user objects based on the known memberids
    members = User.get_users(
        include_pending=True,
    ).filter(
        memberid__in=focus_memberids,
    )

    # Create users for any members who don't have a User object
    memberids_existing = [u.memberid for u in members]
    memberids_missing = [
        m for m in focus_memberids
        if m not in memberids_existing]
    created_members = [
        User.get_or_create_inactive(memberid=memberid)
        for memberid in memberids_missing]

    users = list(nonmembers) + list(members) + list(created_members)

    max_hits_exceeded = len(nonmembers) >= SEARCH_MAX_HITS or \
        len(actors) >= SEARCH_MAX_HITS or \
        len(enrollments) >= SEARCH_MAX_HITS

    # Match expired users only on memberid
    expired_users = User.objects.all()
    for word in request.POST['q'].split():
        expired_users = expired_users.filter(memberid__icontains=word)
    expired_users = [
        u for u in expired_users
        if not Actor.get_personal_members().only('id').filter(
            memberid=u.memberid).exists()
        and not Enrollment.get_active().only('id').filter(
            memberid=u.memberid).exists()
    ]

    context = {
        'users': users,
        'expired_users': expired_users,
        'max_hits_exceeded': max_hits_exceeded,
    }
    return HttpResponse(render_to_string('central/admin/users/user_results.html', context, request=request))
Example #13
0
 def __init__(self, memberid):
     enrollment = cache.get('focus.enrollment.%s' % memberid)
     if enrollment is None:
         enrollment = Enrollment.get_active().get(memberid=memberid)
         cache.set('focus.enrollment.%s' % memberid, enrollment, settings.FOCUS_MEMBER_CACHE_PERIOD)
     self.enrollment = enrollment
Example #14
0
    def create_pending_user(memberid):
        """Create an inactive pending user. Note that the caller must verify that the memberid does not exist in Actor
        first. Read the implementation for more details."""
        # Note that a known bug will result in an exception here.
        # This occurs during enrollment. When saving an enrollment in Focus, the payment_method field should be set to
        # one of two sentinel values for card or invoice; see focus.util.PAYMENT_METHOD_CODES. However, for some
        # reason, some of these entries are saved with payment_method=0 (which is NOT one of the sentinel values). This
        # will occur in an exception where, because Enrollment.get_active() will filter on valid payment codes, not
        # find the given memberid, and raise an exception which isn't handled in the enrollment view (because it
        # shouldn't need to be).
        Enrollment.get_active().get(memberid=memberid) # Ensure that the enrollment exists
        try:
            # Check if the user object already exists first.
            existing_user = User.objects.get(memberid=memberid)

            # Note that we don't check if this user is inactive or not.
            # If they are, maybe someone double-clicked some link or something.
            # It doesn't matter, let this user pass as the created one.

            if not existing_user.verify_still_pending():
                # Should never happen - this method should only be called:
                # - When creating new members, in which case the database should return a memberid which is
                #   *guaranteed* to be new, unique and NOT used in Actor
                # - If the caller has verified that the memberid isn't in use in Actor, and needs to create a new
                #   inactive pending user.
                # We could ignore this and just return the user, but since the caller expects this to be a pending
                # user, there's no telling what craziness might happen, so it is (probably) better to raise an
                # exception here. I don't expect this to happen unless there's been made a code logic mistake
                # somewhere.
                raise Exception("Tried to create a pending user with a memberid which exists in the Actor table.")

            if existing_user.is_expired:
                # Oh, what happened here? Well, they're not expired anymore since the actor exists, so fix that and
                # let them pass.
                existing_user.is_expired = False
                existing_user.save()

            if not existing_user.is_pending:
                # Oh, what happened here? Since our context is that a corresponding actor doesn't exist (see method
                # comment), this user should be pending, so fix it and let them pass
                existing_user.is_pending = True
                existing_user.save()

            return existing_user
        except User.DoesNotExist:
            user = User(
                identifier='%s' % memberid,
                memberid=memberid,
                is_inactive=True,
                is_pending=True
            )
            user.set_unusable_password()
            key = crypto.get_random_string(length=settings.RESTORE_PASSWORD_KEY_LENGTH)
            while User.objects.filter(pending_registration_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 pending-registration-key som allerede finnes!",
                    extra={
                        'should_you_play_the_lottery': True,
                        'key': key
                    }
                )
                key = crypto.get_random_string(length=settings.RESTORE_PASSWORD_KEY_LENGTH)
            user.pending_registration_key = key
            user.save()
            return user