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)
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))
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)
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)
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)
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))
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()
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)
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))
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))
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))
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()