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'])
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)
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 ]
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))
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'}))
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
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))
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()
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))
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
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