def test_user_has_device(self): with self.subTest(user='******'): self.assertFalse(user_has_device(AnonymousUser())) with self.subTest(user='******'): self.assertTrue(user_has_device(self.alice)) with self.subTest(user='******'): self.assertFalse(user_has_device(self.bob))
def get(self, request, *args, **kwargs): if not user_has_device(self.request.user): return redirect(resolve_url("account-admin:edit")) form = self.form_class(user=request.user) return render(request, self.template_name, {"form": form})
def has_permission(self, request): if user_has_device(request.user): # Call django-otp method return super().has_permission(request) else: # Call original django admin method return super(OTPAdminSite, self).has_permission(request)
def test(user: UserProfile) -> bool: """ :if_configured: If ``True``, an authenticated user with no confirmed OTP devices will be allowed. Also, non-authenticated users will be allowed as web_public_visitor users. Default is ``False``. If ``False``, 2FA will not do any authentication. """ if_configured = settings.TWO_FACTOR_AUTHENTICATION_ENABLED if not if_configured: return True # User has completed 2FA verification if user.is_verified(): return True # This request is unauthenticated (logged-out) access; 2FA is # not required or possible. if not user.is_authenticated: # nocoverage return True # If the user doesn't have 2FA set up, we can't enforce 2FA. if not user_has_device(user): return True # User has configured 2FA and is not verified, so the user # fails the test (and we should redirect to the 2FA view). return False
def web_user_lookup(request): template = "hqadmin/web_user_lookup.html" web_user_email = request.GET.get("q") context = { 'current_page': { 'title': "Look up user by email", 'page_name': "Look up user by email", }, 'section': { 'page_name': UserAdministration.section_name, 'url': reverse("default_admin_report"), }, } if not web_user_email: return render(request, template, context) web_user = WebUser.get_by_username(web_user_email) context.update({ 'audit_report_url': reverse('admin_report_dispatcher', args=('user_audit_report',)), }) if web_user is None: messages.error( request, "Sorry, no user found with email {}. Did you enter it correctly?".format(web_user_email) ) else: from django_otp import user_has_device context['web_user'] = web_user django_user = web_user.get_django_user() context['has_two_factor'] = user_has_device(django_user) return render(request, template, context)
def security(request): """View to manage security settings.""" context = {"user_has_device": django_otp.user_has_device(request.user)} if not request.user.tfa_enabled: device = request.user.staticdevice_set.first() if device: tokens = device.token_set.all().values_list("token", flat=True) context.update({"tokens": tokens}) # Set enable flag to True so we can't go here anymore request.user.tfa_enabled = True request.user.save() else: device = request.user.totpdevice_set.first() if device: factory = qrcode.image.svg.SvgPathImage img = qrcode.make(device.config_url, image_factory=factory) buf = io.BytesIO() img.save(buf) context.update({"qrcode": buf.getvalue().decode()}) resp = { "content": render_to_string("core/user_security.html", context, request), "callback": "security" } return render_to_json_response(resp)
def get(self, request): """TOPT generaton""" user = request.user if user_has_device(user, confirmed=True): return Response( dict(errors=["2FA is already activated on this account"]), status=status.HTTP_400_BAD_REQUEST) device = get_user_totp_device(user, False) if not device: device = user.totpdevice_set.create(confirmed=False) user.save() try: img = qrcode.make(device.config_url) bytes_image = io.BytesIO() img.save(bytes_image, format='PNG') bytes_image.seek(0) img = base64.b64encode(bytes_image.read()).decode('utf-8') token = TOTPValidityToken().make_token(user) return Response({ "qrImg": img, "token": token }, status=status.HTTP_201_CREATED) except Exception as e: return Response(status=status.HTTP_400_BAD_REQUEST)
def user_allowed(self, user): if not settings.WAGTAIL_2FA_REQUIRED: return True return user.is_verified() or (self.if_configured and user.is_authenticated and not user_has_device(user))
def is_verified(user): has_device = user_has_device(user) if has_device: return user.otp_device is not None return True
def _verify_user(self, request, user): """ Sets OTP-related fields on an authenticated user. """ user.otp_device = None user.is_verified = functools.partial(is_verified, user) if user.is_authenticated: if not user_has_device(user): return user persistent_id = request.session.get(DEVICE_ID_SESSION_KEY) device = self._device_from_persistent_id( persistent_id) if persistent_id else None if (device is not None) and (device.user_id != user.pk): device = None if (device is None) and (DEVICE_ID_SESSION_KEY in request.session): del request.session[DEVICE_ID_SESSION_KEY] user.otp_device = device return user
def post(self, request, *args, **kwargs): user = request.user tfa_activated = user_has_device(user) reqdata = request.POST.copy() if tfa_activated: form = TwoFactorAuthFormEnabled(prefix='to_enable', data=reqdata) if form.is_valid(): if form.cleaned_data['status']: for device in devices_for_user(user): device.delete() return render( request, 'registration/2fa-disabled-now.html', { 'form': form, 'tfa_activated': tfa_activated, 'status_changed': form.cleaned_data['status'] }, ) else: return render( request, 'registration/2fa.html', { 'form': form, 'tfa_activated': tfa_activated, }, ) else: form = TwoFactorAuthFormDisabled(prefix='to_disable', data=reqdata) if form.is_valid(): if form.cleaned_data['status']: device = TOTPDevice(user=user, name=user.username + '\'s device') device.save() from base64 import b32encode secret_key = b32encode(device.bin_key) return render( request, 'registration/2fa-enabled-now.html', { 'form': form, 'tfa_activated': tfa_activated, 'status_changed': form.cleaned_data['status'], 'device': device, 'secret_key': secret_key, }, ) else: return render( request, 'registration/2fa.html', { 'form': form, 'tfa_activated': tfa_activated, }, )
def authenticate(self, request): response = super().authenticate(request) # response[0] is the user if (response and response[0] and (not user_has_device(response[0]) or response[0].is_verified())): return response else: return None
def dispatch(self, request, *args, **kwargs): # This view is only used to add backup phone numbers, so the user must be authenticated and have 2fa enabled. if not request.user.is_authenticated() or not user_has_device(request.user): return redirect(self.success_url) # Call the super of PhoneSetupView to strip it of the decorators. # PhoneSetupView sets the otp_required decorator, it checks if the user was logged in using 2fa. If not it # tries redirecting to the login page, but using if 2fa is not setup or user logged in using social auth, it # results in a redirect loop. return super(PhoneSetupView, self).dispatch(request, *args, **kwargs)
def has_permission(self, request): return ( super(OTPAdminSite, self).has_permission(request) and request.session[BACKEND_SESSION_KEY] == "agir.people.backend.PersonBackend" and ( request.user.is_verified() or not django_otp.user_has_device(request.user) ) )
def get_has_two_factor(self, obj): if self.context.get('request'): user = self.context.get('request').user if user.is_admin: # Only admins are allowed to see the 2FA status. return user_has_device(obj) else: return None else: return None
def handle_no_permission(self, request): """Redirect unauthenticated users.""" if not request.user.is_authenticated: return redirect_to_login(request.get_full_path(), settings.LOGIN_URL, REDIRECT_FIELD_NAME) if user_has_device(request.user): return redirect_to_login(request.get_full_path(), login_url=reverse("wagtail_2fa_auth")) raise PermissionDenied
def dispatch(self, request, *args, **kwargs): # This view is only used to add backup phone numbers, so the user must be authenticated and have 2fa enabled. if not request.user.is_authenticated() or not user_has_device( request.user): return redirect(self.success_url) # Call the super of PhoneSetupView to strip it of the decorators. # PhoneSetupView sets the otp_required decorator, it checks if the user was logged in using 2fa. If not it # tries redirecting to the login page, but using if 2fa is not setup or user logged in using social auth, it # results in a redirect loop. return super(PhoneSetupView, self).dispatch(request, *args, **kwargs)
def clean(self): self.cleaned_data = super(LoginForm, self).clean() if user_has_device(self.get_user()): self.clean_otp(self.get_user()) if not self.get_user().is_staff: raise forms.ValidationError( self.error_messages['inactive'], code='inactive', ) return self.cleaned_data
def test(user: UserProfile) -> bool: """ :if_configured: If ``True``, an authenticated user with no confirmed OTP devices will be allowed. Default is ``False``. If ``False``, 2FA will not do any authentication. """ if_configured = settings.TWO_FACTOR_AUTHENTICATION_ENABLED if not if_configured: return True return user.is_verified() or (_user_is_authenticated(user) and not user_has_device(user))
def clean_otp_token(self): otp_token = self.cleaned_data["otp_token"] user = self.user if not user_has_device(user): raise ValidationError( "Le système n'a pas trouvé d'appareil (carte OTP ou générateur sur téléphone) pour votre compte. Contactez le support pour en ajouter un." ) for device in devices_for_user(user): if device.verify_is_allowed() and device.verify_token(otp_token): return otp_token raise ValidationError("Ce code n'est pas valide.")
def clean_otp_token(self): otp_token = self.cleaned_data["otp_token"] user = self.user if not user_has_device(user): raise ValidationError( _("The system could not find a device (OTP card or generator on phone) for your account." "Contact support to add one.")) for device in devices_for_user(user): if device.verify_is_allowed() and device.verify_token(otp_token): return otp_token raise ValidationError(_("OTP token is not valid."))
def test_user_allowed_when_no_device_and_if_configured_returns_true(self, rf, user): with override_settings(WAGTAIL_2FA_REQUIRED=True): request = rf.get("/admin/") request.user = user middleware = _OTPMiddleware() user = middleware._verify_user(request, user) assert not user_has_device(user) assert user.is_authenticated mixin = OtpRequiredMixin() mixin.if_configured = True result = mixin.user_allowed(user) assert result is True
def post(self, request): assert 'delete' in request.POST device = device_from_persistent_id(request.user, request.POST['persistent_id']) device.delete() other_devices_same_type = type(device).objects.devices_for_user(request.user, confirmed=None) if other_devices_same_type.filter(name='default').count() == 0 and other_devices_same_type.count() > 0: new_default_device = other_devices_same_type.first() new_default_device.name = 'default' new_default_device.save() if django_otp.user_has_device(request.user, confirmed=None): return HttpResponseRedirect(reverse('two_factor:manage_keys')) return HttpResponseRedirect(reverse('two_factor:profile'))
def get(self, request, uuid, *args, **kwargs): # Superuser only. if not request.user.is_superuser: raise PermissionDenied user = self.get_user(uuid) if not user_has_device(user): return redirect( resolve_url("account-admin:user-detail", pk=user.id)) form = self.form_class(user=request.user) return render(request, self.template_name, {"form": form})
def get(self, request, *args, **kwargs): user = request.user tfa_activated = user_has_device(user) if tfa_activated: form = TwoFactorAuthFormEnabled(prefix='to_enable') else: form = TwoFactorAuthFormDisabled(prefix='to_disable') return render( request, 'registration/2fa.html', { 'form': form, 'tfa_activated': tfa_activated, }, )
def exchange(request): return { 'PROJECT_NAME': settings.PROJECT_NAME, 'USER_HAS_DEVICE': lambda: user_has_device(request.user), 'BRL_CURRENCY_CODE': settings.BRL_CURRENCY_CODE, 'GOOGLE_ANALYTICS_TRACK_ID': settings.GOOGLE_ANALYTICS_TRACK_ID, 'DOMAIN': settings.DOMAIN, 'HOME_VIEW': settings.HOME_VIEW, 'ENABLE_SIGNUP_ADDRESS': settings.ENABLE_SIGNUP_ADDRESS, 'BR_DEPOSIT_MIN': settings.BR_DEPOSIT_MIN, 'BR_DEPOSIT_MAX': settings.BR_DEPOSIT_MAX, 'BR_DEPOSIT_DAILY_LIMIT': settings.BR_DEPOSIT_DAILY_LIMIT, 'DEFAULT_ADDRESS_COUNTRY': settings.DEFAULT_ADDRESS_COUNTRY, 'DEFAULT_USERNAME': settings.DEFAULT_USERNAME, 'DEFAULT_PASSWORD': settings.DEFAULT_PASSWORD, }
def userdetail(request, uid): user = get_object_or_404(User, pk=uid) if settings.LDAP_ENABLED: from django_auth_ldap.backend import LDAPBackend popuser = LDAPBackend().populate_user(user.username) if popuser is None: user.is_active = False user.save() return HttpResponseRedirect(reverse('cred.views.list', args=('changeadvice', user.id))) credlogs = CredAudit.objects.filter(user=user, cred__group__in=request.user.groups.all())[:5] morelink = reverse('staff.views.audit', args=('user', user.id)) return render(request, 'staff_userdetail.html', { 'viewuser': user, 'credlogs': credlogs, 'morelink': morelink, 'hastoken': user_has_device(user)})
def process_request(self, request): super().process_request(request) user = request.user if self._require_verified_user(request): user_has_device = django_otp.user_has_device(user, confirmed=True) if user_has_device and not user.is_verified(): return redirect_to_login(request.get_full_path(), login_url=reverse('wagtail_2fa_auth')) elif not user_has_device and settings.WAGTAIL_2FA_REQUIRED: # only allow the user to visit the admin index page and the # admin setup page return redirect_to_login( request.get_full_path(), login_url=reverse('wagtail_2fa_device_new'))
def get_login_redirect_url(self, request): # This ensures that once a user registers, they immediately # find the 2FA setup page after login. if not user_has_device(request.user): path = reverse('two-factor-setup') # request.user.otp_device will be None if the user is not # verified. I am not sure why at this point in the request # cycle the `is_verified` function (which uses this same # logic) has not been added to the user object. elif request.user.is_authenticated and request.user.otp_device is None: # The session key here seems required to let allauth_2fa # know to bypass normal login and request a token. request.session['allauth_2fa_user_id'] = request.user.id path = reverse('two-factor-authenticate') else: path = reverse('dashboard') return path
def post(self, request): coin = request.POST['coin'] # O POST e imutavel por default, sendo assim, # precisamos alterar essa caracteristica do object para alterar seus valores request.POST._mutable = True # Fazemos isto, pois esse campo precisa passar pela validacao do formulario request.POST['address'] = 'whatever' # Define um valor padrao para code do two factor, caso o usuario nao tenha configurado ele ainda # Fazemos isto, pois esse campo precisa passar pela validacao do formulario if not user_has_device(request.user): request.POST['code'] = '123' account = Accounts.objects.get(user=request.user, currency__code='BTC', currency__type=Currencies.TYPES.investment) withdraw_form = NewWithdrawForm(request.POST, user=request.user, account=account) if not withdraw_form.is_valid(): return {'status': 'error', 'errors': withdraw_form.errors} fee = (withdraw_form.cleaned_data['amount'] * (account.currency.withdraw_fee / 100)) + account.currency.withdraw_fixed_fee checking_account = Accounts.objects.get(user=request.user, currency__code='BTC', currency__type=Currencies.TYPES.checking) with transaction.atomic(): amount = abs(withdraw_form.cleaned_data['amount']) statement = Statement() statement.account = account statement.amount = Decimal('0.00') - amount statement.description = 'Income Withdrawal' statement.type = 'income_withdraw' statement.save() account.takeout(amount) statement = Statement() statement.account = checking_account statement.amount = (amount - abs(fee)) statement.description = 'Income Deposit' statement.type = 'income_deposit' statement.save() checking_account.to_deposit((amount - abs(fee)) ) return {'status': 'success', 'amount': amount}
def web_user_lookup(request): template = "hqadmin/web_user_lookup.html" web_user_email = request.GET.get("q") if not web_user_email: return render(request, template, {}) web_user = WebUser.get_by_username(web_user_email) context = { 'audit_report_url': reverse('admin_report_dispatcher', args=('user_audit_report',)) } if web_user is None: messages.error( request, "Sorry, no user found with email {}. Did you enter it correctly?".format(web_user_email) ) else: from django_otp import user_has_device context['web_user'] = web_user django_user = web_user.get_django_user() context['has_two_factor'] = user_has_device(django_user) return render(request, template, context)
def test(user): return user.is_verified() or (if_configured and user.is_authenticated() and not user_has_device(user))
def get(self, request, *args, **kwargs): if not user_has_device(self.request.user): return redirect(self.redirect_url or resolve_url(settings.LOGIN_REDIRECT_URL)) return super(DisableView, self).get(request, *args, **kwargs)
def otp_required(view=None, redirect_field_name='next', login_url=None, if_configured=False): """ Similar to :func:`~django.contrib.auth.decorators.login_required`, but requires the user to be :term:`verified`. By default, this redirects users to :setting:`OTP_LOGIN_URL`. :param if_configured: If ``True``, an authenticated user with no confirmed OTP devices will be allowed. Default is ``False``. :type if_configured: bool """ if login_url is None: login_url = settings.OTP_LOGIN_URL test = lambda user: user.is_verified() or (if_configured and user.is_authenticated() and not user_has_device(user)) decorator = user_passes_test(test, login_url=login_url, redirect_field_name=redirect_field_name) return decorator if (view is None) else decorator(view)