コード例 #1
0
    def handle_basic_auth(self, request, organization):
        can_register = features.has('auth:register') or request.session.get('can_register')

        op = request.POST.get('op')
        login_form = self.get_login_form(request)
        if can_register:
            register_form = self.get_register_form(request)
        else:
            register_form = None

        if can_register and register_form.is_valid():
            user = register_form.save()

            defaults = {
                'role': 'member',
            }

            organization.member_set.create(
                user=user,
                **defaults
            )

            # HACK: grab whatever the first backend is and assume it works
            user.backend = settings.AUTHENTICATION_BACKENDS[0]

            auth.login(request, user)

            # can_register should only allow a single registration
            request.session.pop('can_register', None)

            request.session.pop('needs_captcha', None)

            return self.redirect(auth.get_login_redirect(request))

        elif login_form.is_valid():
            auth.login(request, login_form.get_user())

            request.session.pop('needs_captcha', None)

            return self.redirect(auth.get_login_redirect(request))

        elif request.POST and not request.session.get('needs_captcha'):
            auth.log_auth_failure(request, request.POST.get('username'))
            request.session['needs_captcha'] = 1
            login_form = self.get_login_form(request)
            login_form.errors.pop('captcha', None)
            if can_register:
                register_form = self.get_register_form(request)
                register_form.errors.pop('captcha', None)

        request.session.set_test_cookie()

        context = {
            'op': op or 'login',
            'login_form': login_form,
            'register_form': register_form,
            'organization': organization,
            'CAN_REGISTER': can_register,
        }
        return self.respond('sentry/organization-login.html', context)
コード例 #2
0
ファイル: helper.py プロジェクト: 280185386/sentry
    def _handle_unknown_identity(self, identity):
        """
        Flow is activated upon a user logging in to where an AuthIdentity is
        not present.

        The flow will attempt to answer the following:

        - Is there an existing user with the same email address? Should they be
          merged?

        - Is there an existing user (via authentication) that shoudl be merged?

        - Should I create a new user based on this identity?
        """
        request = self.request
        op = request.POST.get('op')
        if not request.user.is_authenticated():
            try:
                existing_user = auth.find_users(identity['email'])[0]
            except IndexError:
                existing_user = None
            login_form = self._get_login_form(existing_user)

        if op == 'confirm' and request.user.is_authenticated():
            auth_identity = self._handle_attach_identity(identity)
        elif op == 'newuser':
            auth_identity = self._handle_new_user(identity)
        elif op == 'login' and not request.user.is_authenticated():
            # confirm authentication, login
            op = None
            if login_form.is_valid():
                auth.login(request, login_form.get_user())
                request.session.pop('needs_captcha', None)
            else:
                auth.log_auth_failure(request, request.POST.get('username'))
                request.session['needs_captcha'] = 1
        else:
            op = None

        if not op:
            if request.user.is_authenticated():
                return self.respond('sentry/auth-confirm-link.html', {
                    'identity': identity,
                    'existing_user': request.user,
                })

            return self.respond('sentry/auth-confirm-identity.html', {
                'existing_user': existing_user,
                'identity': identity,
                'login_form': login_form,
            })

        user = auth_identity.user
        user.backend = settings.AUTHENTICATION_BACKENDS[0]

        auth.login(self.request, user)

        self.clear_session()

        return HttpResponseRedirect(auth.get_login_redirect(self.request))
コード例 #3
0
    def handle_basic_auth(self, request, organization):
        can_register = features.has('auth:register') or request.session.get('can_register')

        op = request.POST.get('op')
        login_form = self.get_login_form(request)
        if can_register:
            register_form = self.get_register_form(request)
        else:
            register_form = None

        if can_register and register_form.is_valid():
            user = register_form.save()

            defaults = {
                'role': 'member',
            }

            organization.member_set.create(
                user=user,
                **defaults
            )

            # HACK: grab whatever the first backend is and assume it works
            user.backend = settings.AUTHENTICATION_BACKENDS[0]

            auth.login(request, user)

            # can_register should only allow a single registration
            request.session.pop('can_register', None)

            request.session.pop('needs_captcha', None)

            return self.redirect(auth.get_login_redirect(request))

        elif login_form.is_valid():
            auth.login(request, login_form.get_user())

            request.session.pop('needs_captcha', None)

            return self.redirect(auth.get_login_redirect(request))

        elif request.POST and not request.session.get('needs_captcha'):
            auth.log_auth_failure(request, request.POST.get('username'))
            request.session['needs_captcha'] = 1
            login_form = self.get_login_form(request)
            login_form.errors.pop('captcha', None)
            if can_register:
                register_form = self.get_register_form(request)
                register_form.errors.pop('captcha', None)

        request.session.set_test_cookie()

        context = {
            'op': op or 'login',
            'login_form': login_form,
            'register_form': register_form,
            'organization': organization,
            'CAN_REGISTER': can_register,
        }
        return self.respond('sentry/organization-login.html', context)
コード例 #4
0
ファイル: auth_login.py プロジェクト: ovwane/Sentry
    def handle_basic_auth(self, request):
        can_register = features.has('auth:register') or request.session.get(
            'can_register')

        op = request.POST.get('op')

        # Detect that we are on the register page by url /register/ and
        # then activate the register tab by default.
        if not op and '/register' in request.path_info and can_register:
            op = 'register'

        login_form = self.get_login_form(request)
        if can_register:
            register_form = self.get_register_form(request)
        else:
            register_form = None

        if can_register and register_form.is_valid():
            user = register_form.save()

            # HACK: grab whatever the first backend is and assume it works
            user.backend = settings.AUTHENTICATION_BACKENDS[0]

            auth.login(request, user)

            # can_register should only allow a single registration
            request.session.pop('can_register', None)

            request.session.pop('needs_captcha', None)

            return self.redirect(auth.get_login_redirect(request))

        elif login_form.is_valid():
            auth.login(request, login_form.get_user())

            request.session.pop('needs_captcha', None)

            return self.redirect(auth.get_login_redirect(request))

        elif request.POST and not request.session.get('needs_captcha'):
            auth.log_auth_failure(request, request.POST.get('username'))
            request.session['needs_captcha'] = 1
            login_form = self.get_login_form(request)
            login_form.errors.pop('captcha', None)
            if can_register:
                register_form = self.get_register_form(request)
                register_form.errors.pop('captcha', None)

        request.session.set_test_cookie()

        context = {
            'op': op or 'login',
            'login_form': login_form,
            'register_form': register_form,
            'CAN_REGISTER': can_register,
        }

        return self.respond('sentry/login.html', context)
コード例 #5
0
ファイル: auth_login.py プロジェクト: IthacaDream/sentry
    def handle_basic_auth(self, request):
        can_register = features.has('auth:register') or request.session.get('can_register')

        op = request.POST.get('op')

        # Detect that we are on the register page by url /register/ and
        # then activate the register tab by default.
        if not op and '/register' in request.path_info and can_register:
            op = 'register'

        login_form = self.get_login_form(request)
        if can_register:
            register_form = self.get_register_form(request)
        else:
            register_form = None

        if can_register and register_form.is_valid():
            user = register_form.save()

            # HACK: grab whatever the first backend is and assume it works
            user.backend = settings.AUTHENTICATION_BACKENDS[0]

            auth.login(request, user)

            # can_register should only allow a single registration
            request.session.pop('can_register', None)

            request.session.pop('needs_captcha', None)

            return self.redirect(auth.get_login_redirect(request))

        elif login_form.is_valid():
            auth.login(request, login_form.get_user())

            request.session.pop('needs_captcha', None)

            return self.redirect(auth.get_login_redirect(request))

        elif request.POST and not request.session.get('needs_captcha'):
            auth.log_auth_failure(request, request.POST.get('username'))
            request.session['needs_captcha'] = 1
            login_form = self.get_login_form(request)
            login_form.errors.pop('captcha', None)
            if can_register:
                register_form = self.get_register_form(request)
                register_form.errors.pop('captcha', None)

        request.session.set_test_cookie()

        context = {
            'op': op or 'login',
            'login_form': login_form,
            'register_form': register_form,
            'CAN_REGISTER': can_register,
        }
        return self.respond('sentry/login.html', context)
コード例 #6
0
def handle_unknown_identity(request, organization, auth_provider, provider, state, identity):
    """
    Flow is activated upon a user logging in to where an AuthIdentity is
    not present.

    XXX(dcramer): this docstring is out of date

    The flow will attempt to answer the following:

    - Is there an existing user with the same email address? Should they be
      merged?

    - Is there an existing user (via authentication) that should be merged?

    - Should I create a new user based on this identity?
    """
    op = request.POST.get("op")
    if not request.user.is_authenticated():
        # TODO(dcramer): its possible they have multiple accounts and at
        # least one is managed (per the check below)
        try:
            acting_user = User.objects.filter(
                id__in=UserEmail.objects.filter(email__iexact=identity["email"]).values("user"),
                is_active=True,
            ).first()
        except IndexError:
            acting_user = None
        login_form = AuthenticationForm(
            request,
            request.POST if request.POST.get("op") == "login" else None,
            initial={"username": acting_user.username if acting_user else None},
        )
    else:
        acting_user = request.user

    # If they already have an SSO account and the identity provider says
    # the email matches we go ahead and let them merge it. This is the
    # only way to prevent them having duplicate accounts, and because
    # we trust identity providers, its considered safe.
    # Note: we do not trust things like SAML, so the SSO implementation needs
    # to consider if 'email_verified' can be trusted or not
    if acting_user and identity.get("email_verified"):
        # we only allow this flow to happen if the existing user has
        # membership, otherwise we short circuit because it might be
        # an attempt to hijack membership of another organization
        has_membership = OrganizationMember.objects.filter(
            user=acting_user, organization=organization
        ).exists()
        if has_membership:
            if not auth.login(
                request,
                acting_user,
                after_2fa=request.build_absolute_uri(),
                organization_id=organization.id,
            ):
                if acting_user.has_usable_password():
                    return post_login_redirect(request)
                else:
                    acting_user = None
            else:
                # assume they've confirmed they want to attach the identity
                op = "confirm"
        else:
            # force them to create a new account
            acting_user = None
    # without a usable password they cant login, so let's clear the acting_user
    elif acting_user and not acting_user.has_usable_password():
        acting_user = None

    if op == "confirm" and request.user.is_authenticated():
        auth_identity = handle_attach_identity(
            auth_provider, request, organization, provider, identity
        )
    elif op == "newuser":
        auth_identity = handle_new_user(auth_provider, organization, request, identity)
    elif op == "login" and not request.user.is_authenticated():
        # confirm authentication, login
        op = None
        if login_form.is_valid():
            # This flow is special.  If we are going through a 2FA
            # flow here (login returns False) we want to instruct the
            # system to return upon completion of the 2fa flow to the
            # current URL and continue with the dialog.
            #
            # If there is no 2fa we don't need to do this and can just
            # go on.
            if not auth.login(
                request,
                login_form.get_user(),
                after_2fa=request.build_absolute_uri(),
                organization_id=organization.id,
            ):
                return post_login_redirect(request)
        else:
            auth.log_auth_failure(request, request.POST.get("username"))
    else:
        op = None

    if not op:
        if request.user.is_authenticated():
            return respond(
                "sentry/auth-confirm-link.html",
                organization,
                request,
                {
                    "identity": identity,
                    "existing_user": request.user,
                    "identity_display_name": get_display_name(identity),
                    "identity_identifier": get_identifier(identity),
                },
            )

        return respond(
            "sentry/auth-confirm-identity.html",
            organization,
            request,
            {
                "existing_user": acting_user,
                "identity": identity,
                "login_form": login_form,
                "identity_display_name": get_display_name(identity),
                "identity_identifier": get_identifier(identity),
            },
        )

    user = auth_identity.user
    user.backend = settings.AUTHENTICATION_BACKENDS[0]

    # XXX(dcramer): this is repeated from above
    if not auth.login(
        request, user, after_2fa=request.build_absolute_uri(), organization_id=organization.id
    ):
        return post_login_redirect(request)

    state.clear()

    return post_login_redirect(request)
コード例 #7
0
ファイル: helper.py プロジェクト: upwlabs/sentry
    def _handle_unknown_identity(self, identity):
        """
        Flow is activated upon a user logging in to where an AuthIdentity is
        not present.

        The flow will attempt to answer the following:

        - Is there an existing user with the same email address? Should they be
          merged?

        - Is there an existing user (via authentication) that shoudl be merged?

        - Should I create a new user based on this identity?
        """
        request = self.request
        op = request.POST.get('op')

        if not request.user.is_authenticated():
            # TODO(dcramer): its possible they have multiple accounts and at
            # least one is managed (per the check below)
            try:
                existing_user = auth.find_users(identity['email'],
                                                is_active=True)[0]
            except IndexError:
                existing_user = None

            # If they already have an SSO account and the identity provider says
            # the email matches we go ahead and let them merge it. This is the
            # only way to prevent them having duplicate accounts, and because
            # we trust identity providers, its considered safe.
            if existing_user and existing_user.is_managed:
                # we only allow this flow to happen if the existing user has
                # membership, otherwise we short circuit because it might be
                # an attempt to hijack membership of another organization
                has_membership = OrganizationMember.objects.filter(
                    user=existing_user,
                    organization=self.organization,
                ).exists()
                if has_membership:
                    if not auth.login(request,
                                      existing_user,
                                      after_2fa=request.build_absolute_uri(),
                                      organization_id=self.organization.id):
                        return HttpResponseRedirect(
                            auth.get_login_redirect(self.request))
                    # assume they've confirmed they want to attach the identity
                    op = 'confirm'
                else:
                    # force them to create a new account
                    existing_user = None

            login_form = self._get_login_form(existing_user)
        elif request.user.is_managed:
            # per the above, try to auto merge if the user was originally an
            # SSO account but is still logged in
            has_membership = OrganizationMember.objects.filter(
                user=request.user,
                organization=self.organization,
            ).exists()
            if has_membership:
                # assume they've confirmed they want to attach the identity
                op = 'confirm'

        if op == 'confirm' and request.user.is_authenticated():
            auth_identity = self._handle_attach_identity(identity)
        elif op == 'newuser':
            auth_identity = self._handle_new_user(identity)
        elif op == 'login' and not request.user.is_authenticated():
            # confirm authentication, login
            op = None
            if login_form.is_valid():
                # This flow is special.  If we are going through a 2FA
                # flow here (login returns False) we want to instruct the
                # system to return upon completion of the 2fa flow to the
                # current URL and continue with the dialog.
                #
                # If there is no 2fa we don't need to do this and can just
                # go on.
                if not auth.login(request,
                                  login_form.get_user(),
                                  after_2fa=request.build_absolute_uri(),
                                  organization_id=self.organization.id):
                    return HttpResponseRedirect(
                        auth.get_login_redirect(self.request))
            else:
                auth.log_auth_failure(request, request.POST.get('username'))
        else:
            op = None

        if not op:
            if request.user.is_authenticated():
                return self.respond(
                    'sentry/auth-confirm-link.html', {
                        'identity': identity,
                        'existing_user': request.user,
                        'identity_display_name':
                        self._get_display_name(identity),
                        'identity_identifier': self._get_identifier(identity)
                    })

            return self.respond(
                'sentry/auth-confirm-identity.html', {
                    'existing_user': existing_user,
                    'identity': identity,
                    'login_form': login_form,
                    'identity_display_name': self._get_display_name(identity),
                    'identity_identifier': self._get_identifier(identity)
                })

        user = auth_identity.user
        user.backend = settings.AUTHENTICATION_BACKENDS[0]

        # XXX(dcramer): this is repeated from above
        if not auth.login(request,
                          user,
                          after_2fa=request.build_absolute_uri(),
                          organization_id=self.organization.id):
            return HttpResponseRedirect(auth.get_login_redirect(self.request))

        self.clear_session()

        return HttpResponseRedirect(auth.get_login_redirect(self.request))
コード例 #8
0
    def handle_unknown_identity(
        self,
        state: AuthHelperSessionStore,
        identity: Identity,
    ) -> HttpResponseRedirect:
        """
        Flow is activated upon a user logging in to where an AuthIdentity is
        not present.

        XXX(dcramer): this docstring is out of date

        The flow will attempt to answer the following:

        - Is there an existing user with the same email address? Should they be
          merged?

        - Is there an existing user (via authentication) that should be merged?

        - Should I create a new user based on this identity?
        """
        op = self.request.POST.get("op")
        if not self.user.is_authenticated:
            # TODO(dcramer): its possible they have multiple accounts and at
            # least one is managed (per the check below)
            try:
                acting_user = User.objects.filter(
                    id__in=UserEmail.objects.filter(
                        email__iexact=identity["email"]).values("user"),
                    is_active=True,
                ).first()
            except IndexError:
                acting_user = None
            login_form = AuthenticationForm(
                self.request,
                self.request.POST
                if self.request.POST.get("op") == "login" else None,
                initial={
                    "username": acting_user.username if acting_user else None
                },
            )
        else:
            acting_user = self.user

        # If they already have an SSO account and the identity provider says
        # the email matches we go ahead and let them merge it. This is the
        # only way to prevent them having duplicate accounts, and because
        # we trust identity providers, its considered safe.
        # Note: we do not trust things like SAML, so the SSO implementation needs
        # to consider if 'email_verified' can be trusted or not
        if acting_user and identity.get("email_verified"):
            # we only allow this flow to happen if the existing user has
            # membership, otherwise we short circuit because it might be
            # an attempt to hijack membership of another organization
            has_membership = OrganizationMember.objects.filter(
                user=acting_user, organization=self.organization).exists()
            if has_membership:
                try:
                    self._login(acting_user)
                except self._NotCompletedSecurityChecks:
                    if acting_user.has_usable_password():
                        return self._post_login_redirect()
                    else:
                        acting_user = None
                else:
                    # assume they've confirmed they want to attach the identity
                    op = "confirm"
            else:
                # force them to create a new account
                acting_user = None
        # without a usable password they can't login, so let's clear the acting_user
        elif acting_user and not acting_user.has_usable_password():
            acting_user = None

        if op == "confirm" and self.user.is_authenticated:
            auth_identity = self.handle_attach_identity(identity)
        elif op == "newuser":
            auth_identity = self.handle_new_user(identity)
        elif op == "login" and not self.user.is_authenticated:
            # confirm authentication, login
            op = None
            if login_form.is_valid():
                # This flow is special.  If we are going through a 2FA
                # flow here (login returns False) we want to instruct the
                # system to return upon completion of the 2fa flow to the
                # current URL and continue with the dialog.
                #
                # If there is no 2fa we don't need to do this and can just
                # go on.
                try:
                    self._login(login_form.get_user())
                except self._NotCompletedSecurityChecks:
                    return self._post_login_redirect()
            else:
                auth.log_auth_failure(self.request,
                                      self.request.POST.get("username"))
        else:
            op = None

        if not op:
            # A blank character is needed to prevent the HTML span from collapsing
            provider_name = self.auth_provider.get_provider(
            ).name if self.auth_provider else " "

            context = {
                "identity":
                identity,
                "provider":
                provider_name,
                "identity_display_name":
                identity.get("name") or identity.get("email"),
                "identity_identifier":
                identity.get("email") or identity.get("id"),
            }
            if self.user.is_authenticated:
                template = "sentry/auth-confirm-link.html"
                context.update({"existing_user": self.user})
            else:
                self.request.session.set_test_cookie()
                template = "sentry/auth-confirm-identity.html"
                context.update({
                    "existing_user": acting_user,
                    "login_form": login_form
                })
            return self._respond(template, context)

        user = auth_identity.user
        user.backend = settings.AUTHENTICATION_BACKENDS[0]

        # XXX(dcramer): this is repeated from above
        try:
            self._login(user)
        except self._NotCompletedSecurityChecks:
            return self._post_login_redirect()

        state.clear()

        if not is_active_superuser(self.request):
            # set activeorg to ensure correct redirect upon logging in
            self.request.session["activeorg"] = self.organization.slug
        return self._post_login_redirect()
コード例 #9
0
ファイル: auth_login.py プロジェクト: techscientist/sentry
    def handle_basic_auth(self, request):
        can_register = features.has('auth:register') or request.session.get('can_register')

        op = request.POST.get('op')

        # Detect that we are on the register page by url /register/ and
        # then activate the register tab by default.
        if not op and '/register' in request.path_info and can_register:
            op = 'register'

        login_form = self.get_login_form(request)
        if can_register:
            register_form = self.get_register_form(request)
        else:
            register_form = None

        if can_register and register_form.is_valid():
            user = register_form.save()
            user.send_confirm_emails(is_new_user=True)

            # HACK: grab whatever the first backend is and assume it works
            user.backend = settings.AUTHENTICATION_BACKENDS[0]

            auth.login(request, user)

            # can_register should only allow a single registration
            request.session.pop('can_register', None)

            request.session.pop('needs_captcha', None)

            return self.redirect(auth.get_login_redirect(request))

        elif login_form.is_valid():
            user = login_form.get_user()

            auth.login(request, user)

            request.session.pop('needs_captcha', None)

            if not user.is_active:
                return self.redirect(reverse('sentry-reactivate-account'))

            return self.redirect(auth.get_login_redirect(request))

        elif request.POST and not request.session.get('needs_captcha'):
            auth.log_auth_failure(request, request.POST.get('username'))
            request.session['needs_captcha'] = 1
            login_form = self.get_login_form(request)
            login_form.errors.pop('captcha', None)
            if can_register:
                register_form = self.get_register_form(request)
                register_form.errors.pop('captcha', None)

        # When the captcha fails, hide any other errors
        # to prevent brute force attempts.
        if 'captcha' in login_form.errors:
            for k in login_form.errors.keys():
                if k != 'captcha':
                    login_form.errors.pop(k)

        request.session.set_test_cookie()

        context = {
            'op': op or 'login',
            'server_hostname': get_server_hostname(),
            'login_form': login_form,
            'register_form': register_form,
            'CAN_REGISTER': can_register,
        }
        return self.respond('sentry/login.html', context)
コード例 #10
0
ファイル: helper.py プロジェクト: xvdy/sentry
    def _handle_unknown_identity(self, identity):
        """
        Flow is activated upon a user logging in to where an AuthIdentity is
        not present.

        The flow will attempt to answer the following:

        - Is there an existing user with the same email address? Should they be
          merged?

        - Is there an existing user (via authentication) that shoudl be merged?

        - Should I create a new user based on this identity?
        """
        request = self.request
        op = request.POST.get('op')
        if not request.user.is_authenticated():
            try:
                existing_user = auth.find_users(identity['email'])[0]
            except IndexError:
                existing_user = None
            login_form = self._get_login_form(existing_user)

        if op == 'confirm' and request.user.is_authenticated():
            auth_identity = self._handle_attach_identity(identity)
        elif op == 'newuser':
            auth_identity = self._handle_new_user(identity)
        elif op == 'login' and not request.user.is_authenticated():
            # confirm authentication, login
            op = None
            if login_form.is_valid():
                auth.login(request, login_form.get_user())
                request.session.pop('needs_captcha', None)
            else:
                auth.log_auth_failure(request, request.POST.get('username'))
                request.session['needs_captcha'] = 1
        else:
            op = None

        if not op:
            if request.user.is_authenticated():
                return self.respond(
                    'sentry/auth-confirm-link.html', {
                        'identity': identity,
                        'existing_user': request.user,
                        'identity_display_name':
                        self._get_display_name(identity),
                        'identity_identifier': self._get_identifier(identity)
                    })

            return self.respond(
                'sentry/auth-confirm-identity.html', {
                    'existing_user': existing_user,
                    'identity': identity,
                    'login_form': login_form,
                    'identity_display_name': self._get_display_name(identity),
                    'identity_identifier': self._get_identifier(identity)
                })

        user = auth_identity.user
        user.backend = settings.AUTHENTICATION_BACKENDS[0]

        auth.login(self.request, user)

        self.clear_session()

        return HttpResponseRedirect(auth.get_login_redirect(self.request))
コード例 #11
0
ファイル: helper.py プロジェクト: Akashguharoy/sentry
    def _handle_unknown_identity(self, identity):
        """
        Flow is activated upon a user logging in to where an AuthIdentity is
        not present.

        The flow will attempt to answer the following:

        - Is there an existing user with the same email address? Should they be
          merged?

        - Is there an existing user (via authentication) that shoudl be merged?

        - Should I create a new user based on this identity?
        """
        request = self.request
        op = request.POST.get('op')

        if not request.user.is_authenticated():
            # TODO(dcramer): its possible they have multiple accounts and at
            # least one is managed (per the check below)
            try:
                existing_user = auth.find_users(identity['email'], is_active=True)[0]
            except IndexError:
                existing_user = None

            # If they already have an SSO account and the identity provider says
            # the email matches we go ahead and let them merge it. This is the
            # only way to prevent them having duplicate accounts, and because
            # we trust identity providers, its considered safe.
            if existing_user and existing_user.is_managed:
                # we only allow this flow to happen if the existing user has
                # membership, otherwise we short circuit because it might be
                # an attempt to hijack membership of another organization
                has_membership = OrganizationMember.objects.filter(
                    user=existing_user,
                    organization=self.organization,
                ).exists()
                if has_membership:
                    if not auth.login(request, existing_user,
                                      after_2fa=request.build_absolute_uri()):
                        return HttpResponseRedirect(auth.get_login_redirect(
                            self.request))
                    # assume they've confirmed they want to attach the identity
                    op = 'confirm'
                else:
                    # force them to create a new account
                    existing_user = None

            login_form = self._get_login_form(existing_user)
        elif request.user.is_managed:
            # per the above, try to auto merge if the user was originally an
            # SSO account but is still logged in
            has_membership = OrganizationMember.objects.filter(
                user=request.user,
                organization=self.organization,
            ).exists()
            if has_membership:
                # assume they've confirmed they want to attach the identity
                op = 'confirm'

        if op == 'confirm' and request.user.is_authenticated():
            auth_identity = self._handle_attach_identity(identity)
        elif op == 'newuser':
            auth_identity = self._handle_new_user(identity)
        elif op == 'login' and not request.user.is_authenticated():
            # confirm authentication, login
            op = None
            if login_form.is_valid():
                # This flow is special.  If we are going through a 2FA
                # flow here (login returns False) we want to instruct the
                # system to return upon completion of the 2fa flow to the
                # current URL and continue with the dialog.
                #
                # If there is no 2fa we don't need to do this and can just
                # go on.
                if not auth.login(request, login_form.get_user(),
                                  after_2fa=request.build_absolute_uri()):
                    return HttpResponseRedirect(auth.get_login_redirect(
                        self.request))
                request.session.pop('needs_captcha', None)
            else:
                auth.log_auth_failure(request, request.POST.get('username'))
                request.session['needs_captcha'] = 1
        else:
            op = None

        if not op:
            if request.user.is_authenticated():
                return self.respond('sentry/auth-confirm-link.html', {
                    'identity': identity,
                    'existing_user': request.user,
                    'identity_display_name': self._get_display_name(identity),
                    'identity_identifier': self._get_identifier(identity)
                })

            return self.respond('sentry/auth-confirm-identity.html', {
                'existing_user': existing_user,
                'identity': identity,
                'login_form': login_form,
                'identity_display_name': self._get_display_name(identity),
                'identity_identifier': self._get_identifier(identity)
            })

        user = auth_identity.user
        user.backend = settings.AUTHENTICATION_BACKENDS[0]

        auth.login(self.request, user)

        self.clear_session()

        return HttpResponseRedirect(auth.get_login_redirect(self.request))
コード例 #12
0
ファイル: helper.py プロジェクト: binlee1990/sentry
def handle_unknown_identity(request, organization, auth_provider, provider, state, identity):
    """
    Flow is activated upon a user logging in to where an AuthIdentity is
    not present.

    XXX(dcramer): this docstring is out of date

    The flow will attempt to answer the following:

    - Is there an existing user with the same email address? Should they be
      merged?

    - Is there an existing user (via authentication) that should be merged?

    - Should I create a new user based on this identity?
    """
    op = request.POST.get('op')
    if not request.user.is_authenticated():
        # TODO(dcramer): its possible they have multiple accounts and at
        # least one is managed (per the check below)
        try:
            acting_user = User.objects.filter(
                id__in=UserEmail.objects.filter(email__iexact=identity['email']).values('user'),
                is_active=True,
            ).first()
        except IndexError:
            acting_user = None
        login_form = AuthenticationForm(
            request,
            request.POST if request.POST.get('op') == 'login' else None,
            initial={
                'username': acting_user.username if acting_user else None,
            },
        )
    else:
        acting_user = request.user

    # If they already have an SSO account and the identity provider says
    # the email matches we go ahead and let them merge it. This is the
    # only way to prevent them having duplicate accounts, and because
    # we trust identity providers, its considered safe.
    # Note: we do not trust things like SAML, so the SSO implementation needs
    # to consider if 'email_verified' can be trusted or not
    if acting_user and identity.get('email_verified'):
        # we only allow this flow to happen if the existing user has
        # membership, otherwise we short circuit because it might be
        # an attempt to hijack membership of another organization
        has_membership = OrganizationMember.objects.filter(
            user=acting_user,
            organization=organization,
        ).exists()
        if has_membership:
            if not auth.login(
                request,
                acting_user,
                after_2fa=request.build_absolute_uri(),
                organization_id=organization.id
            ):
                if acting_user.has_usable_password():
                    return HttpResponseRedirect(auth.get_login_redirect(request))
                else:
                    acting_user = None
            else:
                # assume they've confirmed they want to attach the identity
                op = 'confirm'
        else:
            # force them to create a new account
            acting_user = None
    # without a usable password they cant login, so let's clear the acting_user
    elif acting_user and not acting_user.has_usable_password():
        acting_user = None

    if op == 'confirm' and request.user.is_authenticated():
        auth_identity = handle_attach_identity(
            auth_provider,
            request,
            organization,
            provider,
            identity,
        )
    elif op == 'newuser':
        auth_identity = handle_new_user(auth_provider, organization, request, identity)
    elif op == 'login' and not request.user.is_authenticated():
        # confirm authentication, login
        op = None
        if login_form.is_valid():
            # This flow is special.  If we are going through a 2FA
            # flow here (login returns False) we want to instruct the
            # system to return upon completion of the 2fa flow to the
            # current URL and continue with the dialog.
            #
            # If there is no 2fa we don't need to do this and can just
            # go on.
            if not auth.login(
                request,
                login_form.get_user(),
                after_2fa=request.build_absolute_uri(),
                organization_id=organization.id
            ):
                return HttpResponseRedirect(auth.get_login_redirect(request))
        else:
            auth.log_auth_failure(request, request.POST.get('username'))
    else:
        op = None

    if not op:
        if request.user.is_authenticated():
            return respond(
                'sentry/auth-confirm-link.html',
                organization,
                request,
                {
                    'identity': identity,
                    'existing_user': request.user,
                    'identity_display_name': get_display_name(identity),
                    'identity_identifier': get_identifier(identity)
                },
            )

        return respond(
            'sentry/auth-confirm-identity.html',
            organization,
            request,
            {
                'existing_user': acting_user,
                'identity': identity,
                'login_form': login_form,
                'identity_display_name': get_display_name(identity),
                'identity_identifier': get_identifier(identity)
            },
        )

    user = auth_identity.user
    user.backend = settings.AUTHENTICATION_BACKENDS[0]

    # XXX(dcramer): this is repeated from above
    if not auth.login(
        request,
        user,
        after_2fa=request.build_absolute_uri(),
        organization_id=organization.id
    ):
        return HttpResponseRedirect(auth.get_login_redirect(request))

    state.clear()

    return HttpResponseRedirect(auth.get_login_redirect(request))
コード例 #13
0
    def handle_unknown_identity(
        self,
        state: AuthHelperSessionStore,
    ) -> HttpResponseRedirect:
        """
        Flow is activated upon a user logging in to where an AuthIdentity is
        not present.

        XXX(dcramer): this docstring is out of date

        The flow will attempt to answer the following:

        - Is there an existing user with the same email address? Should they be
          merged?

        - Is there an existing user (via authentication) that should be merged?

        - Should I create a new user based on this identity?
        """
        op = self.request.POST.get("op")
        login_form = (None if self._logged_in_user else AuthenticationForm(
            self.request,
            self.request.POST
            if self.request.POST.get("op") == "login" else None,
            initial={"username": self._app_user and self._app_user.username},
        ))
        # we don't trust all IDP email verification, so users can also confirm via one time email link
        is_account_verified = False
        if self.request.session.get("confirm_account_verification_key"):
            verification_key = self.request.session.get(
                "confirm_account_verification_key")
            verification_value = get_verification_value_from_key(
                verification_key)
            if verification_value:
                is_account_verified = self.has_verified_account(
                    verification_value)

        is_new_account = not self.user.is_authenticated  # stateful
        if self._app_user and self.identity.get(
                "email_verified") or is_account_verified:
            # we only allow this flow to happen if the existing user has
            # membership, otherwise we short circuit because it might be
            # an attempt to hijack membership of another organization
            has_membership = OrganizationMember.objects.filter(
                user=self._app_user, organization=self.organization).exists()
            if has_membership:
                try:
                    self._login(self.user)
                except self._NotCompletedSecurityChecks:
                    # adding is_account_verified to the check below in order to redirect
                    # to 2fa when the user migrates their idp but has 2fa enabled,
                    # otherwise it would stop them from linking their sso provider
                    if (self._has_usable_password() or is_account_verified
                            or using_okta_migration_workaround(
                                self.organization, self.user,
                                self.auth_provider)):
                        return self._post_login_redirect()
                    else:
                        is_new_account = True
                else:
                    # assume they've confirmed they want to attach the identity
                    op = "confirm"
            elif is_account_verified:
                op = "confirm"
            else:
                # force them to create a new account
                is_new_account = True
        # without a usable password they can't login, so default to a new account
        elif not self._has_usable_password():
            is_new_account = True

        auth_identity = None
        if op == "confirm" and self.user.is_authenticated or is_account_verified:
            auth_identity = self.handle_attach_identity()
        elif op == "newuser":
            auth_identity = self.handle_new_user()
        elif op == "login" and not self._logged_in_user:
            # confirm authentication, login
            op = None
            if login_form.is_valid():
                # This flow is special.  If we are going through a 2FA
                # flow here (login returns False) we want to instruct the
                # system to return upon completion of the 2fa flow to the
                # current URL and continue with the dialog.
                #
                # If there is no 2fa we don't need to do this and can just
                # go on.
                try:
                    self._login(login_form.get_user())
                except self._NotCompletedSecurityChecks:
                    return self._post_login_redirect()
            else:
                auth.log_auth_failure(self.request,
                                      self.request.POST.get("username"))
        else:
            op = None

        if not op:
            existing_user, template = self._dispatch_to_confirmation(
                is_new_account)

            context = {
                "identity":
                self.identity,
                "provider":
                self.provider_name,
                "identity_display_name":
                self.identity.get("name") or self.identity.get("email"),
                "identity_identifier":
                self.identity.get("email") or self.identity.get("id"),
                "existing_user":
                existing_user,
            }
            if login_form:
                context["login_form"] = login_form
            return self._respond(f"sentry/{template}.html", context)

        user = auth_identity.user
        user.backend = settings.AUTHENTICATION_BACKENDS[0]

        # XXX(dcramer): this is repeated from above
        try:
            self._login(user)
        except self._NotCompletedSecurityChecks:
            return self._post_login_redirect()

        state.clear()

        if not is_active_superuser(self.request):
            auth.set_active_org(self.request, self.organization.slug)
        return self._post_login_redirect()