def enable_otp_session(self, user=None): user.totpdevice_set.create(name="default") assert self.client.login(username=user.username, password="******") if default_device(user): session = self.client.session session[DEVICE_ID_SESSION_KEY] = default_device(user).persistent_id session.save()
def django_2fa_mixin_hack(request): raise_anonymous = False if not request.user or not request.user.is_authenticated or \ (not request.user.is_verified() and default_device(request.user)): # If the user has not authenticated raise or redirect to the login # page. Also if the user just enabled two-factor authentication and # has not yet logged in since should also have the same result. If # the user receives a 'you need to enable TFA' by now, he gets # confuses as TFA has just been enabled. So we either raise or # redirect to the login page. if raise_anonymous: raise PermissionDenied() else: return redirect_to_login(request.get_full_path(), reverse('two_factor:login')) device = default_device(request.user) if not request.user.is_verified(): if device: return redirect_to_login(request.get_full_path(), reverse('two_factor:login')) else: return render( request, 'two_factor/core/otp_required.html', status=403, )
def login_user(self, user=None): username = user.get_username() assert self.client.login(username=username, password=self.password) if default_device(user): session = self.client.session session[DEVICE_ID_SESSION_KEY] = default_device(user).persistent_id session.save()
def login_user(self): user = list(self._passwords.keys())[0] username = user.get_username() assert self.client.login(username=username, password=self._passwords[user]) if default_device(user): session = self.client.session session[DEVICE_ID_SESSION_KEY] = default_device(user).persistent_id session.save()
def test_default_device(self): user = User.objects.create_user('bouke') self.assertEqual(default_device(user), None) user.phonedevice_set.create(name='backup') self.assertEqual(default_device(user), None) default = user.phonedevice_set.create(name='default') self.assertEqual(default_device(user).pk, default.pk)
def test_default_device(self): user = self.create_user() self.assertEqual(default_device(user), None) PhoneDevice.objects.create(user=user, name='backup') self.assertEqual(default_device(user), None) default = PhoneDevice.objects.create(user=user, name='default') self.assertEqual(default_device(user).pk, default.pk)
def test_default_device(self): user = self.create_user() self.assertEqual(default_device(user), None) user.phonedevice_set.create(name="backup", number="+1") self.assertEqual(default_device(user), None) default = user.phonedevice_set.create(name="default", number="+1") self.assertEqual(default_device(user).pk, default.pk)
def login_user(self, user=None): if not user: user = list(self._passwords.keys())[0] username = user.get_username() assert self.client.login(username=username, password=self._passwords[user]) if default_device(user): session = self.client.session session[DEVICE_ID_SESSION_KEY] = default_device(user).persistent_id session.save()
def test_default_device(self): user = User.objects.create_user("bouke") self.assertEqual(default_device(user), None) user.phonedevice_set.create(name="backup") self.assertEqual(default_device(user), None) default = user.phonedevice_set.create(name="default") self.assertEqual(default_device(user).pk, default.pk)
def test_default_device(self): user = self.create_user() self.assertEqual(default_device(user), None) user.phonedevice_set.create(name='backup', number='+1') self.assertEqual(default_device(user), None) default = user.phonedevice_set.create(name='default', number='+1') self.assertEqual(default_device(user).pk, default.pk)
def get_context_data(self, **kwargs): try: backup_device = self.request.user.staticdevice_set.get(name='backup') except StaticDevice.DoesNotExist: backup_device = None return { 'default_device': default_device(self.request.user), 'default_device_type': default_device(self.request.user).__class__.__name__, 'backup_device': backup_device, }
def login_user(self, user=None): if not user: user = list(self._passwords.keys())[0] try: username = user.get_username() except AttributeError: username = user.username assert self.client.login(username=username, password=self._passwords[user]) if default_device(user): session = self.client.session session[DEVICE_ID_SESSION_KEY] = default_device(user).persistent_id session.save()
def get_context_data(self, **kwargs): try: backup_tokens = self.request.user.staticdevice_set.all( )[0].token_set.count() except Exception: backup_tokens = 0 form = UserForm() return { 'default_device': default_device(self.request.user), 'default_device_type': default_device(self.request.user).__class__.__name__, 'form': form, }
def account(request): try: backup_tokens = request.user.staticdevice_set.all()[0].token_set.count() except Exception: backup_tokens = 0 status = { 'default_device': default_device(request.user), 'default_device_type': default_device(request.user).__class__.__name__, 'backup_phones': backup_phones(request.user), 'backup_tokens': backup_tokens, 'site_user': request.user } return render_to_response('account.html', status, context_instance=RequestContext(request))
def get_context_data(self, **kwargs): try: backup_tokens = self.request.user.staticdevice_set.all( )[0].token_set.count() except Exception: backup_tokens = 0 return { 'default_device': default_device(self.request.user), 'default_device_type': default_device(self.request.user).__class__.__name__, 'backup_phones': backup_phones(self.request.user), 'backup_tokens': backup_tokens, 'available_phone_methods': get_available_phone_methods() }
def profile(request): # Get a list of the users API Keys keys = ApiKey.objects.filter(user=request.user) try: backup_tokens = request.user.staticdevice_set.all()[0].token_set.count() except IndexError: backup_tokens = 0 # Get a list of the users current sessions sessions = request.user.session_set.filter(expire_date__gt=now()) # Get the current session key session_key = request.session.session_key # Process the form if we have data coming in if request.method == 'POST': form = UserProfileForm(request.POST, instance=request.user.profile) if form.is_valid(): form.save() else: form = UserProfileForm(instance=request.user.profile) # Show the template return render(request, 'account_profile.html', { 'keys': keys, 'sessions': sessions, 'session_key': session_key, 'form': form, 'user': request.user, 'default_device': default_device(request.user), 'backup_tokens': backup_tokens, })
def teacher_edit_account(request): teacher = request.user.userprofile.teacher backup_tokens = 0 # For teachers using 2FA, find out how many backup tokens they have if default_device(request.user): try: backup_tokens = request.user.staticdevice_set.all()[0].token_set.count() except Exception: backup_tokens = 0 if request.method == "POST": form = TeacherEditAccountForm(request.user, request.POST) if form.is_valid(): data = form.cleaned_data changing_email = False # check not default value for CharField if data["password"] != "": teacher.user.user.set_password(data["password"]) teacher.user.user.save() update_session_auth_hash(request, form.user) teacher.title = data["title"] teacher.user.user.first_name = data["first_name"] teacher.user.user.last_name = data["last_name"] new_email = data["email"] if new_email != "" and new_email != teacher.user.user.email: # new email to set and verify changing_email = True send_verification_email(request, teacher.user, new_email) teacher.save() teacher.user.user.save() if changing_email: logout(request) messages.success( request, "Your account details have been successfully changed. Your email will be changed once you have verified it, until then you can still log in with your old email.", ) return render( request, "portal/email_verification_needed.html", {"userprofile": teacher.user, "email": new_email} ) messages.success(request, "Your account details have been successfully changed.") return HttpResponseRedirect(reverse_lazy("teacher_home")) else: form = TeacherEditAccountForm( request.user, initial={ "title": teacher.title, "first_name": teacher.user.user.first_name, "last_name": teacher.user.user.last_name, "school": teacher.school, }, ) return render(request, "portal/teach/teacher_edit_account.html", {"form": form, "backup_tokens": backup_tokens})
def get_context_data(self, **kwargs): context = super(EditProfileView, self).get_context_data(**kwargs) # Check if user has 2fa enabled if default_device(self.request.user): context["2fa_enabled"] = True return context
def dispatch(self, request, *args, **kwargs): if not request.user.is_authenticated() or \ (not request.user.is_verified() and default_device(request.user)): # If the user has not authenticated raise or redirect to the login # page. Also if the user just enabled two-factor authentication and # has not yet logged in since should also have the same result. If # the user receives a 'you need to enable TFA' by now, he gets # confuses as TFA has just been enabled. So we either raise or # redirect to the login page. if self.raise_anonymous: raise PermissionDenied() else: return redirect( '%s?%s' % (self.get_login_url(), urlencode( {self.redirect_field_name: request.get_full_path()}))) if not request.user.is_verified(): if self.raise_unverified: raise PermissionDenied() elif self.get_verification_url(): return redirect( '%s?%s' % (self.verification_url, urlencode( {self.redirect_field_name: request.get_full_path()}))) else: return TemplateResponse( request=request, template='two_factor/core/otp_required.html', status=403, ) return super(OTPRequiredMixin, self).dispatch(request, *args, **kwargs)
def two_form_authentication_warnings(request, teacher): # For teachers using 2FA, warn if they don't have any backup tokens set, and warn solo-admins to set up another admin if default_device(request.user): # check backup tokens try: backup_tokens = request.user.staticdevice_set.all()[0].token_set.count() except Exception: backup_tokens = 0 if not backup_tokens > 0: link = reverse('two_factor:profile') messages.warning(request, 'You do not have any backup tokens set up for two factor authentication, so could lose ' 'access to your account if you have problems with your smartphone or tablet. ' '<a href="{link}">Set up backup tokens now</a>.'.format(link = link), extra_tags='safe') # check admin if teacher.is_admin: admins = Teacher.objects.filter(school=teacher.school, is_admin=True) manageSchoolLink = reverse('organisation_manage') if len(admins) == 1: messages.warning(request, 'You are the only administrator in your school and are using Two Factor Authentication ' '(2FA). We recommend you <a href="{manageSchoolLink}">set up another ' 'administrator</a> who will be able to disable your 2FA should you have problems with ' 'your smartphone or tablet.'.format(manageSchoolLink = manageSchoolLink), extra_tags='safe')
def wrapped(request, *args, **kwargs): u = request.user if (not hasattr(u, 'userprofile') or not hasattr(u.userprofile, 'teacher') or (not u.is_verified() and default_device(u))): return HttpResponseRedirect(reverse_lazy('teach')) return view_func(request, *args, **kwargs)
def dispatch(self, request, *args, **kwargs): if not request.user.is_authenticated() or \ (not request.user.is_verified() and default_device(request.user)): # If the user has not authenticated raise or redirect to the login # page. Also if the user just enabled two-factor authentication and # has not yet logged in since should also have the same result. If # the user receives a 'you need to enable TFA' by now, he gets # confuses as TFA has just been enabled. So we either raise or # redirect to the login page. if self.raise_anonymous: raise PermissionDenied() else: return redirect('%s?%s' % ( self.get_login_url(), urlencode({self.redirect_field_name: request.get_full_path()}) )) if not request.user.is_verified(): if self.raise_unverified: raise PermissionDenied() elif self.get_verification_url(): return redirect('%s?%s' % ( self.verification_url, urlencode({self.redirect_field_name: request.get_full_path()}) )) else: return TemplateResponse( request=request, template='two_factor/core/otp_required.html', status=403, ) return super(OTPRequiredMixin, self).dispatch(request, *args, **kwargs)
def profile(request): # Get a list of the users API Keys keys = ApiKey.objects.filter(user=request.user) try: backup_tokens = request.user.staticdevice_set.all()[0].token_set.count( ) except IndexError: backup_tokens = 0 # Get a list of the users current sessions sessions = request.user.session_set.filter(expire_date__gt=now()) # Get the current session key session_key = request.session.session_key # Process the form if we have data coming in if request.method == 'POST': form = UserProfileForm(request.POST, instance=request.user.profile) if form.is_valid(): form.save() else: form = UserProfileForm(instance=request.user.profile) # Show the template return render( request, 'account_profile.html', { 'keys': keys, 'sessions': sessions, 'session_key': session_key, 'form': form, 'user': request.user, 'default_device': default_device(request.user), 'backup_tokens': backup_tokens, })
def account(request): try: backup_tokens = request.user.staticdevice_set.all()[0].token_set.count( ) except Exception: backup_tokens = 0 status = { 'default_device': default_device(request.user), 'default_device_type': default_device(request.user).__class__.__name__, 'backup_phones': backup_phones(request.user), 'backup_tokens': backup_tokens, 'site_user': request.user } return render_to_response('account.html', status, context_instance=RequestContext(request))
def login(self, request, redirect_url=None): # prevent admin users hijacking this login page to circumvent # two factor authentication if site_settings.ADMIN_ENABLED: if default_device(self.user): raise Http404 if request.limited: return render(request, 'account/ratelimit_triggered.html', {}) return super(RatelimitedLoginForm, self).login(request, redirect_url)
def process_request(self, request): if settings.ADMIN_ENABLED: allowed_urls = [reverse('admin:logout')] requested_url = request.get_full_path() two_factor_base = reverse('two_factor:profile') if two_factor_base not in requested_url \ and requested_url not in allowed_urls \ and request.user and any([request.user.is_superuser, request.user.is_staff]) \ and not default_device(request.user): return HttpResponseRedirect(reverse('two_factor:setup'))
def user_login_callback(sender, request=None, user=None, **kwargs): if is_user_privileged( user ) and not user.require_2_fact_auth and default_device(user) is None: link = '<a href="%(url)s" class="alert-link">' + _('click here') + \ '</a>' % {'url': reverse('two_factor:setup')} msg = mark_safe( _('We strongly recommend that you protect your account with Two-Factor authentication. ' 'Please %(link)s to set it up.') % {'link': link}) if msg not in [m.message for m in messages.get_messages(request)]: messages.info(request, msg)
def get_context_data(self, **kwargs): data = super().get_context_data(**kwargs) try: backup_tokens = self.request.user.staticdevice_set.all( )[0].token_set.count() except Exception: backup_tokens = 0 data["backup_tokens"] = backup_tokens data["default_device"] = default_device(self.request.user) return data
def teacher_edit_account(request): teacher = request.user.userprofile.teacher backup_tokens = 0 # For teachers using 2FA, find out how many backup tokens they have if default_device(request.user): try: backup_tokens = request.user.staticdevice_set.all()[0].token_set.count() except Exception: backup_tokens = 0 if request.method == 'POST': form = TeacherEditAccountForm(request.user, request.POST) if form.is_valid(): data = form.cleaned_data changing_email=False # check not default value for CharField if (data['password'] != ''): teacher.user.user.set_password(data['password']) teacher.user.user.save() update_session_auth_hash(request, form.user) teacher.title = data['title'] teacher.user.user.first_name = data['first_name'] teacher.user.user.last_name = data['last_name'] new_email = data['email'] if new_email != '' and new_email != teacher.user.user.email: # new email to set and verify changing_email=True send_verification_email(request, teacher.user, new_email) teacher.save() teacher.user.user.save() if changing_email: logout(request) messages.success(request, 'Your account details have been successfully changed. Your email will be changed once you have verified it, until then you can still log in with your old email.') return render(request, 'portal/email_verification_needed.html', { 'userprofile': teacher.user, 'email': new_email }) messages.success(request, 'Your account details have been successfully changed.') return HttpResponseRedirect(reverse_lazy('teacher_home')) else: form = TeacherEditAccountForm(request.user, initial={ 'title' : teacher.title, 'first_name': teacher.user.user.first_name, 'last_name': teacher.user.user.last_name, 'school': teacher.school, }) return render(request, 'portal/teach/teacher_edit_account.html', { 'form': form, 'backup_tokens': backup_tokens })
def otp_setup(self, obj): if default_device(obj): # we can't just user method.boolean = True, # because we want a setup OTP link return format_html( "<img src='/assets/admin/img/icon-yes.svg' alt='True'>") else: if obj.is_staff or obj.is_superuser: url = reverse("two-factor-setup") else: url = reverse("two-factor-setup-redirect", kwargs={"username": obj.username}) return format_html("<a href='{url}'>Set up OTP</a>", url=url)
def get_context_data(self, **kwargs): if self.request.user.is_superuser and settings.HIJACK_ENABLE: swappable_form = BecomeUserForm() else: swappable_form = None show_change_password = password_management_enabled( ) and self.request.user.has_usable_password(), return super().get_context_data( swappable_form=swappable_form, default_device=default_device(self.request.user), show_change_password=show_change_password, **kwargs, )
def perform_two_factor(backend, user=None, user_id=None, two_factor_complete=False, **kwargs): if not user and user_id: User = get_user_model() user = User.objects.get(id=uuid.UUID(user_id)) if user and default_device(user) and not two_factor_complete: if not settings.TWO_FACTOR_ENABLED: raise TwoFactorDisabled(backend) return HttpResponseRedirect(reverse('login') + '?from-social') return {'user': user}
def list(self, request): try: token_set = request.user.staticdevice_set.first().token_set.all() except AttributeError: token_set = [] return Response({ 'default': get_info_text_for_device(default_device(request.user)), 'backup_phone_numbers': [ { 'id': phone.pk, 'number': mask_phone_number(format_phone_number(phone.number)), } for phone in backup_phones(request.user) ], 'backup_tokens': [token.token for token in token_set], })
def get_context_data(self, **kwargs): return { **super().get_context_data(**kwargs), 'associated': [ BackendMeta.wrap(user_social_auth) for user_social_auth in self.request.user.social_auth.all() ], 'two_factor_default_device': default_device(self.request.user), 'social_backends': list( sorted([ bm for bm in backend_meta.BackendMeta.registry.values() if bm.show ], key=lambda sb: sb.name)), }
def manage_user_view(request, user): user_object = get_object_or_404(get_user_model(), username=user) patients = list(Patient.objects.filter(user=user_object)) context = { "username": user_object.username, "user_id": user_object.id, "reset_password_form": ResetPasswordForm(), "attributes": [ { "key": "First Name", "value": user_object.first_name }, { "key": "Last Name", "value": user_object.last_name }, { "key": "Last Login", "value": user_object.last_login }, { "key": "Signup Time", "value": user_object.date_joined }, { "key": "Administrator", "value": user_object.is_superuser }, { "key": "Account Enabled", "value": user_object.is_active }, ], "patients": patients, "twofa": default_device(user_object), "disable_2fa_form": Disable2FAForm(), "addpatient_form": AddPatientForm(), } return render(request, "administration/manage_user.html", context)
def profile(request): # Delete our expired keys ApiKey.delete_expired(user=request.user) # Get a list of the users API Keys keys = ApiKey.objects.filter(user=request.user) try: backup_tokens = request.user.staticdevice_set.all()[0].token_set.count( ) except IndexError: backup_tokens = 0 # Get list of current user sessions sessions = UserSession.objects.filter(user_id=request.user.id) # Get the current session key session_key = request.session.session_key # Process the form if we have data coming in if request.method == "POST": form = UserProfileForm(request.POST, instance=request.user.profile) if form.is_valid(): saved_form = form.save() if request.FILES.getlist("icon"): f = request.FILES.getlist("icon")[0] saved_form.avatar = b64encode(f.file.read()) saved_form.save() else: form = UserProfileForm(instance=request.user.profile) # Show the template return render( request, "account_profile.html", { "keys": keys, "sessions": sessions, "session_key": session_key, "form": form, "user": request.user, "default_device": default_device(request.user), "backup_tokens": backup_tokens, }, )
def clean_otp(self, user): if user is None: return device = default_device(user) token = self.cleaned_data.get('otp_token') user.otp_device = None try: if self.cleaned_data.get('otp_challenge'): self._handle_challenge(device) elif token: user.otp_device = self._verify_token(user, token, device) else: raise forms.ValidationError(_('Please enter your OTP token.'), code='required') finally: if user.otp_device is None: self._update_form(user)
def view(request, state_slug=''): try: state = StateEnum[state_slug.upper()] except KeyError: return redirect( 'admin:view', StateEnum.WAITING_FOR_APPROVAL.name.lower(), ) jobs = Job.objects.filter(state=state) page = AutoPaginator(request, jobs, 20).current_page() by_state = Job.objects.by_state() return render( request, 'admin/view.html', { 'page': page, 'state': state, 'by_state': by_state, 'default_device': default_device(request.user), })
def post_login(self, request, *args, **kwargs): next = request.POST.get('next') if not is_safe_url(next): next = settings.LOGIN_REDIRECT_URL form = PleioAuthenticationForm(request=request, data=request.POST) if form.is_valid(): username = form.cleaned_data.get('username') user = User.objects.get(email=username) request.session['username'] = username device = default_device(user) if not device: auth_login(request, user, backend='django.contrib.auth.backends.ModelBackend') return redirect(next) else: request.session['login_step'] = 'token' form = PleioAuthenticationTokenForm(user, request) else: for key, value in form.errors.items(): if value[0] == 'inactive': try: username = request.POST.get('username') user = User.objects.get(email=username) user.send_activation_token() return redirect('register_complete') except: pass EventLog.add_event(request, 'invalid login') return render( request, 'login.html', { 'form': form, 'login_step': request.session.get('login_step'), 'reCAPTCHA': EventLog.reCAPTCHA_needed(request), 'next': next })
def do_two_factor_login(request: HttpRequest, user_profile: UserProfile) -> None: device = default_device(user_profile) if device: django_otp.login(request, device)
def get(self, request, *args, **kwargs): default_device(request.user).delete() return super(NewPhoneView, self).get(request, *args, **kwargs)
def build_page_params_for_home_page_load( request: HttpRequest, user_profile: Optional[UserProfile], realm: Realm, insecure_desktop_app: bool, narrow: List[List[str]], narrow_stream: Optional[Stream], narrow_topic: Optional[str], first_in_realm: bool, prompt_for_invites: bool, needs_tutorial: bool, ) -> Tuple[int, Dict[str, Any]]: """ This function computes page_params for when we load the home page. The page_params data structure gets sent to the client. """ client_capabilities = { "notification_settings_null": True, "bulk_message_deletion": True, "user_avatar_url_field_optional": True, "stream_typing_notifications": False, # Set this to True when frontend support is implemented. "user_settings_object": True, } if user_profile is not None: client = RequestNotes.get_notes(request).client assert client is not None register_ret = do_events_register( user_profile, client, apply_markdown=True, client_gravatar=True, slim_presence=True, client_capabilities=client_capabilities, narrow=narrow, include_streams=False, ) else: # Since events for spectator is not implemented, we only fetch the data # at the time of request and don't register for any events. # TODO: Implement events for spectator. from zerver.lib.events import fetch_initial_state_data, post_process_state register_ret = fetch_initial_state_data( user_profile, realm=realm, event_types=None, queue_id=None, client_gravatar=False, user_avatar_url_field_optional=client_capabilities["user_avatar_url_field_optional"], user_settings_object=client_capabilities["user_settings_object"], slim_presence=False, include_subscribers=False, include_streams=False, ) post_process_state(user_profile, register_ret, False) furthest_read_time = get_furthest_read_time(user_profile) request_language = get_and_set_request_language( request, register_ret["user_settings"]["default_language"], translation.get_language_from_path(request.path_info), ) two_fa_enabled = settings.TWO_FACTOR_AUTHENTICATION_ENABLED and user_profile is not None billing_info = get_billing_info(user_profile) user_permission_info = get_user_permission_info(user_profile) # Pass parameters to the client-side JavaScript code. # These end up in a JavaScript Object named 'page_params'. page_params = dict( ## Server settings. test_suite=settings.TEST_SUITE, insecure_desktop_app=insecure_desktop_app, login_page=settings.HOME_NOT_LOGGED_IN, warn_no_email=settings.WARN_NO_EMAIL, search_pills_enabled=settings.SEARCH_PILLS_ENABLED, # Only show marketing email settings if on Zulip Cloud corporate_enabled=settings.CORPORATE_ENABLED, ## Misc. extra data. language_list=get_language_list(), needs_tutorial=needs_tutorial, first_in_realm=first_in_realm, prompt_for_invites=prompt_for_invites, furthest_read_time=furthest_read_time, bot_types=get_bot_types(user_profile), two_fa_enabled=two_fa_enabled, apps_page_url=get_apps_page_url(), show_billing=billing_info.show_billing, promote_sponsoring_zulip=promote_sponsoring_zulip_in_realm(realm), show_plans=billing_info.show_plans, show_webathena=user_permission_info.show_webathena, # Adding two_fa_enabled as condition saves us 3 queries when # 2FA is not enabled. two_fa_enabled_user=two_fa_enabled and bool(default_device(user_profile)), is_spectator=user_profile is None, # There is no event queue for spectators since # events support for spectators is not implemented yet. no_event_queue=user_profile is None, ) for field_name in register_ret.keys(): page_params[field_name] = register_ret[field_name] if narrow_stream is not None: # In narrow_stream context, initial pointer is just latest message recipient = narrow_stream.recipient try: max_message_id = ( Message.objects.filter(recipient=recipient).order_by("id").reverse()[0].id ) except IndexError: max_message_id = -1 page_params["narrow_stream"] = narrow_stream.name if narrow_topic is not None: page_params["narrow_topic"] = narrow_topic page_params["narrow"] = [dict(operator=term[0], operand=term[1]) for term in narrow] page_params["max_message_id"] = max_message_id assert isinstance(page_params["user_settings"], dict) page_params["user_settings"]["enable_desktop_notifications"] = False page_params["translation_data"] = get_language_translation_data(request_language) return register_ret["queue_id"], page_params
def teach(request): invalid_form = False limits = getattr(request, 'limits', {'ip': [0], 'email': [0]}) captcha_limit = 5 using_captcha = (limits['ip'][0] > captcha_limit or limits['email'][0] > captcha_limit) should_use_captcha = (limits['ip'][0] >= captcha_limit or limits['email'][0] >= captcha_limit) LoginFormWithCaptcha = partial( create_form_subclass_with_recaptcha(TeacherLoginForm, recaptcha_client), request) InputLoginForm = LoginFormWithCaptcha if using_captcha else TeacherLoginForm OutputLoginForm = LoginFormWithCaptcha if should_use_captcha else TeacherLoginForm login_form = OutputLoginForm(prefix='login') signup_form = TeacherSignupForm(prefix='signup') if request.method == 'POST': if 'login' in request.POST: login_form = InputLoginForm(request.POST, prefix='login') if login_form.is_valid(): userProfile = login_form.user.userprofile if userProfile.awaiting_email_verification: send_verification_email(request, userProfile) return render(request, 'portal/email_verification_needed.html', {'userprofile': userProfile}) login(request, login_form.user) if default_device(request.user): return render(request, 'portal/2FA_redirect.html', { 'form': AuthenticationForm(), 'username': request.user.username, 'password': login_form.cleaned_data['password'], }) else: link = reverse('two_factor:profile') messages.info( request, ("You are not currently set up with two-factor authentication. " + "Use your phone or tablet to enhance your account's security. " + "Click <a href='" + link + "'>here</a> to find out more and " + "set it up or go to your account page at any time."), extra_tags='safe') next_url = request.GET.get('next', None) if next_url: return HttpResponseRedirect(next_url) return HttpResponseRedirect(reverse_lazy('teacher_home')) else: login_form = OutputLoginForm(request.POST, prefix='login') invalid_form = True if 'signup' in request.POST: signup_form = TeacherSignupForm(request.POST, prefix='signup') if signup_form.is_valid(): data = signup_form.cleaned_data teacher = Teacher.objects.factory( title=data['title'], first_name=data['first_name'], last_name=data['last_name'], email=data['email'], password=data['password']) send_verification_email(request, teacher.user) return render(request, 'portal/email_verification_needed.html', {'userprofile': teacher.user}) logged_in_as_teacher = hasattr(request.user, 'userprofile') and \ hasattr(request.user.userprofile, 'teacher') and \ (request.user.is_verified() or not default_device(request.user)) res = render(request, 'portal/teach.html', { 'login_form': login_form, 'signup_form': signup_form, 'logged_in_as_teacher': logged_in_as_teacher, }) res.count = invalid_form return res
def has_2FA(u): return default_device(u)
def aggregated_data(request): tables = [] table_head = ["Data description", "Value", "More info"] table_data = [] """ Overall statistics """ table_data.append( [ "Number of users", Teacher.objects.count() + Student.objects.count(), "Number of teachers + Number of students", ] ) tables.append( { "title": "Overall Statistics", "description": "CFL site overall statistics", "header": table_head, "data": table_data, } ) """ School statistics """ table_data = [] table_data.append(["Number of schools signed up", School.objects.count(), ""]) num_of_teachers_per_school = School.objects.annotate(num_teachers=Count("teacher_school")) stats_teachers_per_school = num_of_teachers_per_school.aggregate(Avg("num_teachers")) table_data.append(["Average number of teachers per school", stats_teachers_per_school["num_teachers__avg"], ""]) tables.append({"title": "Schools or Clubs", "description": "", "header": table_head, "data": table_data}) """ Teacher statistics """ table_data = [] table_data.append(["Number of teachers signed up", Teacher.objects.count(), ""]) table_data.append(["Number of teachers not in a school", Teacher.objects.filter(school=None).count(), ""]) table_data.append( [ "Number of teachers with request pending to join a school", Teacher.objects.exclude(pending_join_request=None).count(), "", ] ) table_data.append( [ "Number of teachers with unverified email address", Teacher.objects.filter(user__awaiting_email_verification=True).count(), "", ] ) teachers = Teacher.objects.all() two_factor_teachers = 0 for teacher in teachers: if default_device(teacher.user.user): two_factor_teachers += 1 table_data.append(["Number of teachers setup with 2FA", two_factor_teachers, ""]) num_of_classes_per_teacher = Teacher.objects.annotate(num_classes=Count("class_teacher")) stats_classes_per_teacher = num_of_classes_per_teacher.aggregate(Avg("num_classes")) num_of_classes_per_active_teacher = num_of_classes_per_teacher.exclude(school=None) stats_classes_per_active_teacher = num_of_classes_per_active_teacher.aggregate(Avg("num_classes")) table_data.append(["Average number of classes per teacher", stats_classes_per_teacher["num_classes__avg"], ""]) table_data.append( [ "Average number of classes per active teacher", stats_classes_per_active_teacher["num_classes__avg"], "Excludes teachers without a school", ] ) table_data.append( ["Number of of teachers with no classes", num_of_classes_per_teacher.filter(num_classes=0).count(), ""] ) table_data.append( [ "Number of of active teachers with no classes", num_of_classes_per_active_teacher.filter(num_classes=0).count(), "Excludes teachers without a school", ] ) tables.append({"title": "Teachers", "description": "", "header": table_head, "data": table_data}) """ Class statistics """ table_data = [] table_data.append(["Number of classes", Class.objects.count(), ""]) num_students_per_class = Class.objects.annotate(num_students=Count("students")) stats_students_per_class = num_students_per_class.aggregate(Avg("num_students")) stats_students_per_active_class = num_students_per_class.exclude(num_students=0).aggregate(Avg("num_students")) table_data.append(["Average number of students per class", stats_students_per_class["num_students__avg"], ""]) table_data.append( [ "Average number of students per active class", stats_students_per_active_class["num_students__avg"], "Excludes classes which are empty", ] ) tables.append({"title": "Classes", "description": "", "header": table_head, "data": table_data}) """ Student statistics """ table_data = [] table_data.append(["Number of students", Student.objects.count(), ""]) independent_students = Student.objects.filter(class_field=None) table_data.append(["Number of independent students", independent_students.count(), ""]) table_data.append( [ "Number of independent students with unverified email address", independent_students.filter(user__awaiting_email_verification=True).count(), "", ] ) table_data.append(["Number of school students", Student.objects.exclude(class_field=None).count(), ""]) tables.append({"title": "Students", "description": "", "header": table_head, "data": table_data}) """ Rapid Router Student Progress statistics """ table_data = [] students_with_attempts = Student.objects.annotate(num_attempts=Count("attempts")).exclude(num_attempts=0) table_data.append(["Number of students who have started RR", students_with_attempts.count(), ""]) school_students_with_attempts = students_with_attempts.exclude(class_field=None) table_data.append(["Number of school students who have started RR", school_students_with_attempts.count(), ""]) independent_students_with_attempts = students_with_attempts.filter(class_field=None) table_data.append( ["Number of independent students who have started RR", independent_students_with_attempts.count(), ""] ) # TODO revisit this once episodes have been restructured, as this doesn't work because of episode being a PROPERTY of level... # Need to filter out so we're only looking at attempts on levels that could be relevant, and don't look at null scores # default_level_attempts = Attempt.objects.filter(level__default=True).exclude(level__episode=None).exclude(level__episode__in_development=True).exclude(score=None) # table_data.append(["Average score recorded on default RR levels", default_level_attempts.aggregate(Avg('score'))['score__avg'], ""]) # perfect_default_level_attempts = default_level_attempts.filter(score=20) # perfect_attempts = perfect_default_level_attempts.count() # all_attempts = default_level_attempts.count() # percentage = None # if all_attempts != 0: # percentage = (float(perfect_attempts)/float(all_attempts))*100 # table_data.append(["Percentage of perfect scores on default RR levels", percentage, ""]) # school_default_level_attempts = default_level_attempts.exclude(student__class_field=None) # table_data.append(["Average score recorded amongst school students on default RR levels", school_default_level_attempts.aggregate(Avg('score'))['score__avg'], ""]) # school_perfect_default_level_attempts = school_default_level_attempts.filter(score=20) # school_perfect_attempts = school_perfect_default_level_attempts.count() # school_all_attempts = school_default_level_attempts.count() # percentage = None # if school_all_attempts != 0: # percentage = (float(school_perfect_attempts)/float(school_all_attempts))*100 # table_data.append(["Percentage of perfect scores amongst school students on default RR levels", percentage, ""]) # independent_default_level_attempts = default_level_attempts.filter(student__class_field=None) # table_data.append(["Average score recorded amongst independent students on default RR levels", independent_default_level_attempts.aggregate(Avg('score'))['score__avg'], ""]) # independent_perfect_default_level_attempts = independent_default_level_attempts.filter(score=20) # independent_perfect_attempts = independent_perfect_default_level_attempts.count() # independent_all_attempts = independent_default_level_attempts.count() # percentage = None # if independent_all_attempts != 0: # percentage = (float(independent_perfect_attempts)/float(independent_all_attempts))*100 # table_data.append(["Percentage of perfect scores amongst independent students on default RR levels", percentage, ""]) # student_attempts_on_default_levels = default_level_attempts.values('student').annotate(num_completed=Count('level')) # school_student_attempts_on_default_levels = school_default_level_attempts.values('student').annotate(num_completed=Count('level')) # independent_student_attempts_on_default_levels = independent_default_level_attempts.values('student').annotate(num_completed=Count('level')) # avg_levels_completed = student_attempts_on_default_levels.aggregate(Avg('num_completed'))['num_completed__avg'] # table_data.append(["Average number of levels completed by students", avg_levels_completed, ""]) # avg_levels_completed = school_student_attempts_on_default_levels.aggregate(Avg('num_completed'))['num_completed__avg'] # table_data.append(["Average number of levels completed by school students", avg_levels_completed, ""]) # avg_levels_completed = independent_student_attempts_on_default_levels.aggregate(Avg('num_completed'))['num_completed__avg'] # table_data.append(["Average number of levels completed by independent students", avg_levels_completed, ""]) # default_levels = Level.objects.filter(default=True) # available_levels = [level for level in default_levels if level.episode and not level.episode.in_development] # print len(available_levels) tables.append( {"title": "Rapid Router Student Progress", "description": "", "header": table_head, "data": table_data} ) """ Rapid Router Levels statistics """ table_data = [] num_user_levels = UserProfile.objects.annotate(num_custom_levels=Count("levels")).exclude(num_custom_levels=0) stats_user_levels = num_user_levels.aggregate(Avg("num_custom_levels")) table_data.append(["Number of users with custom levels", num_user_levels.count(), ""]) table_data.append( [ "Of users with custom levels, average number of custom levels", stats_user_levels["num_custom_levels__avg"], "", ] ) num_teacher_levels = num_user_levels.exclude(teacher=None) stats_teacher_levels = num_teacher_levels.aggregate(Avg("num_custom_levels")) table_data.append(["Number of teachers with custom levels", num_teacher_levels.count(), ""]) table_data.append( [ "Of teachers with custom levels, average number of custom levels", stats_teacher_levels["num_custom_levels__avg"], "", ] ) num_student_levels = num_user_levels.exclude(student=None) stats_student_levels = num_student_levels.aggregate(Avg("num_custom_levels")) table_data.append(["Number of students with custom levels", num_student_levels.count(), ""]) table_data.append( [ "Of students with custom levels, average number of custom levels", stats_student_levels["num_custom_levels__avg"], "", ] ) num_school_student_levels = num_student_levels.exclude(student__class_field=None) stats_school_student_levels = num_school_student_levels.aggregate(Avg("num_custom_levels")) table_data.append(["Number of school students with custom levels", num_school_student_levels.count(), ""]) table_data.append( [ "Of school students with custom levels, average number of custom levels", stats_school_student_levels["num_custom_levels__avg"], "", ] ) num_independent_student_levels = num_student_levels.filter(student__class_field=None) stats_independent_student_levels = num_independent_student_levels.aggregate(Avg("num_custom_levels")) table_data.append(["Number of independent students with custom levels", num_independent_student_levels.count(), ""]) table_data.append( [ "Of independent students with custom levels, average number of custom levels", stats_independent_student_levels["num_custom_levels__avg"], "", ] ) tables.append({"title": "Rapid Router Levels", "description": "", "header": table_head, "data": table_data}) return render(request, "portal/admin/aggregated_data.html", {"tables": tables})
def get(self, request, *args, **kwargs): default_device(request.user).delete() return super(TwoFactorResetView, self).get(request, *args, **kwargs)
def _using_two_factor(user): '''Returns whether the user is using 2fa or not.''' return default_device(user)
def logged_in_as_teacher(u): if not hasattr(u, 'userprofile') or not hasattr(u.userprofile, 'teacher'): return False return u.is_verified() or not default_device(u)
def is_logged_in(u): return u.is_authenticated() and (not default_device(u) or (hasattr(u, 'is_verified') and u.is_verified()))
def home_real(request: HttpRequest) -> HttpResponse: # We need to modify the session object every two weeks or it will expire. # This line makes reloading the page a sufficient action to keep the # session alive. request.session.modified = True user_profile = request.user # If a user hasn't signed the current Terms of Service, send them there if settings.TERMS_OF_SERVICE is not None and settings.TOS_VERSION is not None and \ int(settings.TOS_VERSION.split('.')[0]) > user_profile.major_tos_version(): return accounts_accept_terms(request) narrow = [] # type: List[List[str]] narrow_stream = None narrow_topic = request.GET.get("topic") if request.GET.get("stream"): try: narrow_stream_name = request.GET.get("stream") (narrow_stream, ignored_rec, ignored_sub) = access_stream_by_name( user_profile, narrow_stream_name) narrow = [["stream", narrow_stream.name]] except Exception: logging.exception("Narrow parsing exception", extra=dict(request=request)) if narrow_stream is not None and narrow_topic is not None: narrow.append(["topic", narrow_topic]) register_ret = do_events_register(user_profile, request.client, apply_markdown=True, client_gravatar=True, narrow=narrow) user_has_messages = (register_ret['max_message_id'] != -1) # Reset our don't-spam-users-with-email counter since the # user has since logged in if user_profile.last_reminder is not None: # nocoverage # TODO: Look into the history of last_reminder; we may have # eliminated that as a useful concept for non-bot users. user_profile.last_reminder = None user_profile.save(update_fields=["last_reminder"]) # Brand new users get narrowed to PM with welcome-bot needs_tutorial = user_profile.tutorial_status == UserProfile.TUTORIAL_WAITING first_in_realm = realm_user_count(user_profile.realm) == 1 # If you are the only person in the realm and you didn't invite # anyone, we'll continue to encourage you to do so on the frontend. prompt_for_invites = first_in_realm and \ not PreregistrationUser.objects.filter(referred_by=user_profile).count() if user_profile.pointer == -1 and user_has_messages: # Put the new user's pointer at the bottom # # This improves performance, because we limit backfilling of messages # before the pointer. It's also likely that someone joining an # organization is interested in recent messages more than the very # first messages on the system. register_ret['pointer'] = register_ret['max_message_id'] user_profile.last_pointer_updater = request.session.session_key if user_profile.pointer == -1: latest_read = None else: latest_read = get_usermessage_by_message_id(user_profile, user_profile.pointer) if latest_read is None: # Don't completely fail if your saved pointer ID is invalid logging.warning("%s has invalid pointer %s" % (user_profile.email, user_profile.pointer)) # We pick a language for the user as follows: # * First priority is the language in the URL, for debugging. # * If not in the URL, we use the language from the user's settings. request_language = translation.get_language_from_path(request.path_info) if request_language is None: request_language = register_ret['default_language'] translation.activate(request_language) # We also save the language to the user's session, so that # something reasonable will happen in logged-in portico pages. request.session[translation.LANGUAGE_SESSION_KEY] = translation.get_language() two_fa_enabled = settings.TWO_FACTOR_AUTHENTICATION_ENABLED # Pass parameters to the client-side JavaScript code. # These end up in a global JavaScript Object named 'page_params'. page_params = dict( # Server settings. development_environment = settings.DEVELOPMENT, debug_mode = settings.DEBUG, test_suite = settings.TEST_SUITE, poll_timeout = settings.POLL_TIMEOUT, login_page = settings.HOME_NOT_LOGGED_IN, root_domain_uri = settings.ROOT_DOMAIN_URI, maxfilesize = settings.MAX_FILE_UPLOAD_SIZE, max_avatar_file_size = settings.MAX_AVATAR_FILE_SIZE, server_generation = settings.SERVER_GENERATION, use_websockets = settings.USE_WEBSOCKETS, save_stacktraces = settings.SAVE_FRONTEND_STACKTRACES, warn_no_email = settings.WARN_NO_EMAIL, server_inline_image_preview = settings.INLINE_IMAGE_PREVIEW, server_inline_url_embed_preview = settings.INLINE_URL_EMBED_PREVIEW, password_min_length = settings.PASSWORD_MIN_LENGTH, password_min_guesses = settings.PASSWORD_MIN_GUESSES, jitsi_server_url = settings.JITSI_SERVER_URL, search_pills_enabled = settings.SEARCH_PILLS_ENABLED, # Misc. extra data. have_initial_messages = user_has_messages, initial_servertime = time.time(), # Used for calculating relative presence age default_language_name = get_language_name(register_ret['default_language']), language_list_dbl_col = get_language_list_for_templates(register_ret['default_language']), language_list = get_language_list(), needs_tutorial = needs_tutorial, first_in_realm = first_in_realm, prompt_for_invites = prompt_for_invites, furthest_read_time = sent_time_in_epoch_seconds(latest_read), has_mobile_devices = num_push_devices_for_user(user_profile) > 0, bot_types = get_bot_types(user_profile), two_fa_enabled = two_fa_enabled, # Adding two_fa_enabled as condition saves us 3 queries when # 2FA is not enabled. two_fa_enabled_user = two_fa_enabled and bool(default_device(user_profile)), ) undesired_register_ret_fields = [ 'streams', ] for field_name in set(register_ret.keys()) - set(undesired_register_ret_fields): page_params[field_name] = register_ret[field_name] if narrow_stream is not None: # In narrow_stream context, initial pointer is just latest message recipient = get_stream_recipient(narrow_stream.id) try: initial_pointer = Message.objects.filter(recipient=recipient).order_by('id').reverse()[0].id except IndexError: initial_pointer = -1 page_params["narrow_stream"] = narrow_stream.name if narrow_topic is not None: page_params["narrow_topic"] = narrow_topic page_params["narrow"] = [dict(operator=term[0], operand=term[1]) for term in narrow] page_params["max_message_id"] = initial_pointer page_params["pointer"] = initial_pointer page_params["have_initial_messages"] = (initial_pointer != -1) page_params["enable_desktop_notifications"] = False statsd.incr('views.home') show_invites = True # Some realms only allow admins to invite users if user_profile.realm.invite_by_admins_only and not user_profile.is_realm_admin: show_invites = False if user_profile.is_guest: show_invites = False show_billing = False show_plans = False if settings.CORPORATE_ENABLED: from corporate.models import Customer if user_profile.is_billing_admin or user_profile.is_realm_admin: customer = Customer.objects.filter(realm=user_profile.realm).first() if customer is not None and customer.has_billing_relationship: show_billing = True if user_profile.realm.plan_type == Realm.LIMITED: show_plans = True request._log_data['extra'] = "[%s]" % (register_ret["queue_id"],) page_params['translation_data'] = {} if request_language != 'en': page_params['translation_data'] = get_language_translation_data(request_language) csp_nonce = generate_random_token(48) emojiset = user_profile.emojiset if emojiset == UserProfile.TEXT_EMOJISET: # If current emojiset is `TEXT_EMOJISET`, then fallback to # GOOGLE_EMOJISET for picking which spritesheet's CSS to # include (and thus how to display emojis in the emoji picker # and composebox typeahead). emojiset = UserProfile.GOOGLE_BLOB_EMOJISET response = render(request, 'zerver/app/index.html', context={'user_profile': user_profile, 'emojiset': emojiset, 'page_params': JSONEncoderForHTML().encode(page_params), 'csp_nonce': csp_nonce, 'avatar_url': avatar_url(user_profile), 'show_debug': settings.DEBUG and ('show_debug' in request.GET), 'pipeline': settings.PIPELINE_ENABLED, 'search_pills_enabled': settings.SEARCH_PILLS_ENABLED, 'show_invites': show_invites, 'show_billing': show_billing, 'show_plans': show_plans, 'is_admin': user_profile.is_realm_admin, 'is_guest': user_profile.is_guest, 'show_webathena': user_profile.realm.webathena_enabled, 'enable_feedback': settings.ENABLE_FEEDBACK, 'embedded': narrow_stream is not None, 'invite_as': PreregistrationUser.INVITE_AS, },) patch_cache_control(response, no_cache=True, no_store=True, must_revalidate=True) return response