Example #1
0
    def post(self, request, *args, **kwargs):
        r = request.POST.get("webauthn", "")
        valid = False

        if 'webauthn_challenge' in self.request.session and r.startswith('{'):
            challenge = self.request.session['webauthn_challenge']

            resp = json.loads(r)
            try:
                devices = [
                    WebAuthnDevice.objects.get(user=self.request.user,
                                               credential_id=resp.get("id"))
                ]
            except WebAuthnDevice.DoesNotExist:
                devices = U2FDevice.objects.filter(user=self.request.user)

            for d in devices:
                try:
                    wu = d.webauthnuser

                    if isinstance(d, U2FDevice):
                        # RP_ID needs to be appId for U2F devices, but we can't
                        # set it that way in U2FDevice.webauthnuser, since that
                        # breaks the frontend part.
                        wu.rp_id = settings.SITE_URL

                    webauthn_assertion_response = webauthn.WebAuthnAssertionResponse(
                        wu,
                        resp,
                        challenge,
                        settings.SITE_URL,
                        uv_required=False  # User Verification
                    )
                    sign_count = webauthn_assertion_response.verify()
                except Exception:
                    logger.exception('U2F login failed')
                else:
                    if isinstance(d, WebAuthnDevice):
                        d.sign_count = sign_count
                        d.save()
                    valid = True
                    break

        valid = valid or self.form.is_valid()

        if valid:
            t = int(time.time())
            request.session['pretix_auth_login_time'] = t
            request.session['pretix_auth_last_used'] = t
            next_url = get_auth_backends()[
                request.user.auth_backend].get_next_url(request)
            if next_url and url_has_allowed_host_and_scheme(
                    next_url, allowed_hosts=None):
                return redirect(next_url)
            return redirect(reverse('control:index'))
        else:
            messages.error(
                request,
                _('The password you entered was invalid, please try again.'))
            return self.get(request, *args, **kwargs)
Example #2
0
def register(request):
    """
    Render and process a basic registration form.
    """
    if not settings.PRETIX_REGISTRATION or 'native' not in get_auth_backends():
        raise PermissionDenied('Registration is disabled')
    ctx = {}
    if request.user.is_authenticated:
        return redirect(request.GET.get("next", 'control:index'))
    if request.method == 'POST':
        form = RegistrationForm(data=request.POST)
        if form.is_valid():
            user = User.objects.create_user(
                form.cleaned_data['email'],
                form.cleaned_data['password'],
                locale=request.LANGUAGE_CODE,
                timezone=request.timezone
                if hasattr(request, 'timezone') else settings.TIME_ZONE)
            user = authenticate(request=request,
                                email=user.email,
                                password=form.cleaned_data['password'])
            user.log_action('pretix.control.auth.user.created', user=user)
            auth_login(request, user)
            request.session['pretix_auth_login_time'] = int(time.time())
            request.session['pretix_auth_long_session'] = (
                settings.PRETIX_LONG_SESSIONS
                and form.cleaned_data.get('keep_logged_in', False))
            return redirect('control:index')
    else:
        form = RegistrationForm()
    ctx['form'] = form
    return render(request, 'pretixcontrol/auth/register.html', ctx)
Example #3
0
def process_login(request, user, keep_logged_in):
    """
    This method allows you to return a response to a successful log-in. This will set all session values correctly
    and redirect to either the URL specified in the ``next`` parameter, or the 2FA login screen, or the dashboard.

    :return: This method returns a ``HttpResponse``.
    """
    request.session[
        'pretix_auth_long_session'] = settings.PRETIX_LONG_SESSIONS and keep_logged_in
    next_url = get_auth_backends()[user.auth_backend].get_next_url(request)
    if user.require_2fa:
        request.session['pretix_auth_2fa_user'] = user.pk
        request.session['pretix_auth_2fa_time'] = str(int(time.time()))
        twofa_url = reverse('control:auth.login.2fa')
        if next_url and url_has_allowed_host_and_scheme(next_url,
                                                        allowed_hosts=None):
            twofa_url += '?next=' + quote(next_url)
        return redirect(twofa_url)
    else:
        auth_login(request, user)
        request.session['pretix_auth_login_time'] = int(time.time())
        if next_url and url_has_allowed_host_and_scheme(next_url,
                                                        allowed_hosts=None):
            return redirect(next_url)
        return redirect(reverse('control:index'))
Example #4
0
 def get_context_data(self, **kwargs):
     ctx = super().get_context_data(**kwargs)
     ctx['teams'] = self.object.teams.select_related('organizer')
     b = get_auth_backends()
     ctx['backend'] = (b[self.object.auth_backend].verbose_name
                       if self.object.auth_backend in b else
                       self.object.auth_backend)
     return ctx
Example #5
0
 def form(self):
     return ReauthForm(
         user=self.request.user,
         backend=get_auth_backends()[self.request.user.auth_backend],
         request=self.request,
         data=self.request.POST if self.request.method == "POST" else None,
         initial={
             'email': self.request.user.email,
         })
Example #6
0
 def get(self, request, *args, **kwargs):
     u = get_auth_backends()[
         request.user.auth_backend].request_authenticate(request)
     if u and u == request.user:
         if "next" in request.GET and is_safe_url(request.GET.get("next"),
                                                  allowed_hosts=None):
             return redirect(request.GET.get("next"))
         return redirect(reverse('control:index'))
     return super().get(request, *args, **kwargs)
Example #7
0
 def get(self, request, *args, **kwargs):
     backend = get_auth_backends()[request.user.auth_backend]
     u = backend.request_authenticate(request)
     if u and u == request.user:
         next_url = backend.get_next_url(request)
         if next_url and url_has_allowed_host_and_scheme(
                 next_url, allowed_hosts=None):
             return redirect(next_url)
         return redirect(reverse('control:index'))
     return super().get(request, *args, **kwargs)
Example #8
0
 def get(self, request, *args, **kwargs):
     backend = get_auth_backends()[request.user.auth_backend]
     u = backend.request_authenticate(request)
     if u and u == request.user:
         next_url = backend.get_next_url(request)
         t = int(time.time())
         request.session['pretix_auth_login_time'] = t
         request.session['pretix_auth_last_used'] = t
         if next_url and url_has_allowed_host_and_scheme(next_url, allowed_hosts=None):
             return redirect(next_url)
         return redirect(reverse('control:index'))
     return super().get(request, *args, **kwargs)
Example #9
0
def login(request):
    """
    Render and process a most basic login form. Takes an URL as GET
    parameter "next" for redirection after successful login
    """
    ctx = {}
    backenddict = get_auth_backends()
    backends = sorted(backenddict.values(),
                      key=lambda b: (b.identifier != "native", b.verbose_name))
    for b in backends:
        u = b.request_authenticate(request)
        if u and u.auth_backend == b.identifier:
            return process_login(request, u, False)
        b.url = b.authentication_url(request)

    backend = backenddict.get(request.GET.get('backend', 'native'),
                              backends[0])
    if not backend.visible:
        backend = [b for b in backends if b.visible][0]
    if request.user.is_authenticated:
        next_url = backend.get_next_url(request) or 'control:index'
        if next_url and url_has_allowed_host_and_scheme(next_url,
                                                        allowed_hosts=None):
            return redirect(next_url)
        return redirect(reverse('control:index'))
    if request.method == 'POST':
        form = LoginForm(backend=backend, data=request.POST, request=request)
        if form.is_valid(
        ) and form.user_cache and form.user_cache.auth_backend == backend.identifier:
            return process_login(
                request, form.user_cache,
                form.cleaned_data.get('keep_logged_in', False))
    else:
        form = LoginForm(backend=backend, request=request)
    ctx['form'] = form
    ctx['can_register'] = settings.PRETIX_REGISTRATION
    ctx['can_reset'] = settings.PRETIX_PASSWORD_RESET
    ctx['backends'] = backends
    ctx['backend'] = backend
    return render(request, 'pretixcontrol/auth/login.html', ctx)
Example #10
0
    def create(self, validated_data):
        if 'email' in validated_data:
            try:
                user = User.objects.get(email__iexact=validated_data['email'])
            except User.DoesNotExist:
                if self.context['team'].invites.filter(
                        email__iexact=validated_data['email']).exists():
                    raise ValidationError(
                        _('This user already has been invited for this team.'))
                if 'native' not in get_auth_backends():
                    raise ValidationError(
                        'Users need to have a pretix account before they can be invited.'
                    )

                invite = self.context['team'].invites.create(
                    email=validated_data['email'])
                self._send_invite(invite)
                invite.team.log_action('pretix.team.invite.created',
                                       data={'email': validated_data['email']},
                                       **self.context['log_kwargs'])
                return invite
            else:
                if self.context['team'].members.filter(pk=user.pk).exists():
                    raise ValidationError(
                        _('This user already has permissions for this team.'))

                self.context['team'].members.add(user)
                self.context['team'].log_action('pretix.team.member.added',
                                                data={
                                                    'email': user.email,
                                                    'user': user.pk,
                                                },
                                                **self.context['log_kwargs'])
                return TeamInvite(email=user.email)
        else:
            raise ValidationError('No email address given.')
Example #11
0
 def dispatch(self, request, *args, **kwargs):
     if not settings.PRETIX_PASSWORD_RESET or 'native' not in get_auth_backends(
     ):
         raise PermissionDenied('Registration is disabled')
     return super().dispatch(request, *args, **kwargs)
Example #12
0
def invite(request, token):
    """
    Registration form in case of an invite
    """
    ctx = {}

    if 'native' not in get_auth_backends():
        raise PermissionDenied('Invites are disabled')

    try:
        inv = TeamInvite.objects.get(token=token)
    except TeamInvite.DoesNotExist:
        messages.error(
            request,
            _('You used an invalid link. Please copy the link from your email to the address bar '
              'and make sure it is correct and that the link has not been used before.'
              ))
        return redirect('control:auth.login')

    if request.user.is_authenticated:
        if inv.team.members.filter(pk=request.user.pk).exists():
            messages.error(
                request,
                _('You cannot accept the invitation for "{}" as you already are part of '
                  'this team.').format(inv.team.name))
            return redirect('control:index')
        else:
            with transaction.atomic():
                inv.team.members.add(request.user)
                inv.team.log_action('pretix.team.member.joined',
                                    data={
                                        'email': request.user.email,
                                        'invite_email': inv.email,
                                        'user': request.user.pk
                                    })
                inv.delete()
            messages.success(
                request,
                _('You are now part of the team "{}".').format(inv.team.name))
            return redirect('control:index')

    if request.method == 'POST':
        form = RegistrationForm(data=request.POST)
        with transaction.atomic():
            valid = form.is_valid()
            if valid:
                user = User.objects.create_user(
                    form.cleaned_data['email'],
                    form.cleaned_data['password'],
                    locale=request.LANGUAGE_CODE,
                    timezone=request.timezone
                    if hasattr(request, 'timezone') else settings.TIME_ZONE)
                user = authenticate(request=request,
                                    email=user.email,
                                    password=form.cleaned_data['password'])
                user.log_action('pretix.control.auth.user.created', user=user)
                auth_login(request, user)
                request.session['pretix_auth_login_time'] = int(time.time())
                request.session['pretix_auth_long_session'] = (
                    settings.PRETIX_LONG_SESSIONS
                    and form.cleaned_data.get('keep_logged_in', False))

                inv.team.members.add(request.user)
                inv.team.log_action('pretix.team.member.joined',
                                    data={
                                        'email': user.email,
                                        'invite_email': inv.email,
                                        'user': user.pk
                                    })
                inv.delete()
                messages.success(
                    request,
                    _('Welcome to pretix! You are now part of the team "{}".').
                    format(inv.team.name))
                return redirect('control:index')
    else:
        form = RegistrationForm(initial={'email': inv.email})
    ctx['form'] = form
    return render(request, 'pretixcontrol/auth/invite.html', ctx)
Example #13
0
    def post(self, request, *args, **kwargs):
        self.object = self.get_object()

        if 'remove-member' in request.POST:
            try:
                user = User.objects.get(pk=request.POST.get('remove-member'))
            except (User.DoesNotExist, ValueError):
                pass
            else:
                other_admin_teams = self.request.organizer.teams.exclude(
                    pk=self.object.pk).filter(can_change_teams=True,
                                              members__isnull=False).exists()
                if not other_admin_teams and self.object.can_change_teams and self.object.members.count(
                ) == 1:
                    messages.error(
                        self.request,
                        _('You cannot remove the last member from this team as no one would '
                          'be left with the permission to change teams.'))
                    return redirect(self.get_success_url())
                else:
                    self.object.members.remove(user)
                    self.object.log_action('pretix.team.member.removed',
                                           user=self.request.user,
                                           data={
                                               'email': user.email,
                                               'user': user.pk
                                           })
                    messages.success(
                        self.request,
                        _('The member has been removed from the team.'))
                    return redirect(self.get_success_url())

        elif 'remove-invite' in request.POST:
            try:
                invite = self.object.invites.get(
                    pk=request.POST.get('remove-invite'))
            except (TeamInvite.DoesNotExist, ValueError):
                messages.error(self.request, _('Invalid invite selected.'))
                return redirect(self.get_success_url())
            else:
                invite.delete()
                self.object.log_action('pretix.team.invite.deleted',
                                       user=self.request.user,
                                       data={'email': invite.email})
                messages.success(self.request,
                                 _('The invite has been revoked.'))
                return redirect(self.get_success_url())

        elif 'resend-invite' in request.POST:
            try:
                invite = self.object.invites.get(
                    pk=request.POST.get('resend-invite'))
            except (TeamInvite.DoesNotExist, ValueError):
                messages.error(self.request, _('Invalid invite selected.'))
                return redirect(self.get_success_url())
            else:
                self._send_invite(invite)
                self.object.log_action('pretix.team.invite.resent',
                                       user=self.request.user,
                                       data={'email': invite.email})
                messages.success(self.request,
                                 _('The invite has been resent.'))
                return redirect(self.get_success_url())

        elif 'remove-token' in request.POST:
            try:
                token = self.object.tokens.get(
                    pk=request.POST.get('remove-token'))
            except (TeamAPIToken.DoesNotExist, ValueError):
                messages.error(self.request, _('Invalid token selected.'))
                return redirect(self.get_success_url())
            else:
                token.active = False
                token.save()
                self.object.log_action('pretix.team.token.deleted',
                                       user=self.request.user,
                                       data={'name': token.name})
                messages.success(self.request,
                                 _('The token has been revoked.'))
                return redirect(self.get_success_url())

        elif "user" in self.request.POST and self.add_form.is_valid(
        ) and self.add_form.has_changed():

            try:
                user = User.objects.get(
                    email__iexact=self.add_form.cleaned_data['user'])
            except User.DoesNotExist:
                if self.object.invites.filter(email__iexact=self.add_form.
                                              cleaned_data['user']).exists():
                    messages.error(
                        self.request,
                        _('This user already has been invited for this team.'))
                    return self.get(request, *args, **kwargs)
                if 'native' not in get_auth_backends():
                    messages.error(
                        self.request,
                        _('Users need to have a pretix account before they can be invited.'
                          ))
                    return self.get(request, *args, **kwargs)

                invite = self.object.invites.create(
                    email=self.add_form.cleaned_data['user'])
                self._send_invite(invite)
                self.object.log_action(
                    'pretix.team.invite.created',
                    user=self.request.user,
                    data={'email': self.add_form.cleaned_data['user']})
                messages.success(
                    self.request,
                    _('The new member has been invited to the team.'))
                return redirect(self.get_success_url())
            else:
                if self.object.members.filter(pk=user.pk).exists():
                    messages.error(
                        self.request,
                        _('This user already has permissions for this team.'))
                    return self.get(request, *args, **kwargs)

                self.object.members.add(user)
                self.object.log_action('pretix.team.member.added',
                                       user=self.request.user,
                                       data={
                                           'email': user.email,
                                           'user': user.pk,
                                       })
                messages.success(
                    self.request,
                    _('The new member has been added to the team.'))
                return redirect(self.get_success_url())

        elif "name" in self.request.POST and self.add_token_form.is_valid(
        ) and self.add_token_form.has_changed():
            token = self.object.tokens.create(
                name=self.add_token_form.cleaned_data['name'])
            self.object.log_action(
                'pretix.team.token.created',
                user=self.request.user,
                data={
                    'name': self.add_token_form.cleaned_data['name'],
                    'id': token.pk
                })
            messages.success(
                self.request,
                _('A new API token has been created with the following secret: {}\n'
                  'Please copy this secret to a safe place. You will not be able to '
                  'view it again here.').format(token.token))
            return redirect(self.get_success_url())
        else:
            messages.error(self.request, _('Your changes could not be saved.'))
            return self.get(request, *args, **kwargs)