def post(self, request, organization=None, *args, **kwargs): """ Process a login request via username/password. SSO login is handled elsewhere. """ login_form = AuthenticationForm(request, request.data) # Rate limit logins is_limited = ratelimiter.is_limited( "auth:login:username:{}".format( md5_text( login_form.clean_username( request.data.get("username"))).hexdigest()), limit=10, window=60, # 10 per minute should be enough for anyone ) if is_limited: errors = {"__all__": [login_form.error_messages["rate_limited"]]} metrics.incr("login.attempt", instance="rate_limited", skip_internal=True, sample_rate=1.0) return self.respond_with_error(errors) if not login_form.is_valid(): metrics.incr("login.attempt", instance="failure", skip_internal=True, sample_rate=1.0) return self.respond_with_error(login_form.errors) user = login_form.get_user() auth.login(request, user, organization_id=organization.id if organization else None) metrics.incr("login.attempt", instance="success", skip_internal=True, sample_rate=1.0) if not user.is_active: return Response({ "nextUri": "/auth/reactivate/", "user": serialize(user, user, DetailedUserSerializer()), }) active_org = self.get_active_organization(request) redirect_url = auth.get_org_redirect_url(request, active_org) return Response({ "nextUri": auth.get_login_redirect(request, redirect_url), "user": serialize(user, user, DetailedUserSerializer()), })
def handle_basic_auth(self, request): if request.user.is_authenticated(): return self.redirect(get_login_redirect(request)) form = AuthenticationForm( request, request.POST or None, captcha=bool(request.session.get('needs_captcha')), ) if form.is_valid(): login(request, form.get_user()) request.session.pop('needs_captcha', None) return self.redirect(get_login_redirect(request)) elif request.POST and not request.session.get('needs_captcha'): request.session['needs_captcha'] = 1 form = AuthenticationForm(request, request.POST or None, captcha=True) form.errors.pop('captcha', None) request.session.set_test_cookie() context = { 'form': form, 'CAN_REGISTER': features.has('auth:register') or request.session.get('can_register'), } return self.respond('sentry/login.html', context)
def handle(self, request): if request.user.is_authenticated(): return self.redirect(get_login_redirect(request)) form = AuthenticationForm(request, request.POST or None, captcha=bool(request.session.get("needs_captcha"))) if form.is_valid(): login(request, form.get_user()) request.session.pop("needs_captcha", None) return self.redirect(get_login_redirect(request)) elif request.POST and not request.session.get("needs_captcha"): request.session["needs_captcha"] = 1 form = AuthenticationForm(request, request.POST or None, captcha=True) form.errors.pop("captcha", None) request.session.set_test_cookie() context = { "form": form, "next": request.session.get("_next"), "CAN_REGISTER": features.has("auth:register") or request.session.get("can_register"), "AUTH_PROVIDERS": get_auth_providers(), "SOCIAL_AUTH_CREATE_USERS": features.has("social-auth:register"), } return self.respond("sentry/login.html", context)
def login(request): from django.conf import settings if request.user.is_authenticated(): return login_redirect(request) form = AuthenticationForm(request, request.POST or None, captcha=bool(request.session.get('needs_captcha'))) if form.is_valid(): login_user(request, form.get_user()) request.session.pop('needs_captcha', None) return login_redirect(request) elif request.POST and not request.session.get('needs_captcha'): request.session['needs_captcha'] = 1 form = AuthenticationForm(request, request.POST or None, captcha=True) form.errors.pop('captcha', None) request.session.set_test_cookie() context = csrf(request) context.update({ 'form': form, 'next': request.session.get('_next'), 'CAN_REGISTER': settings.SENTRY_ALLOW_REGISTRATION or request.session.get('can_register'), 'AUTH_PROVIDERS': get_auth_providers(), 'SOCIAL_AUTH_CREATE_USERS': settings.SOCIAL_AUTH_CREATE_USERS, }) return render_to_response('sentry/login.html', context, request)
def login(request): from django.conf import settings if request.user.is_authenticated(): return login_redirect(request) form = AuthenticationForm(request, request.POST or None) if form.is_valid(): login_user(request, form.get_user()) return login_redirect(request) request.session.set_test_cookie() context = csrf(request) context.update({ 'form': form, 'next': request.session.get('_next'), 'CAN_REGISTER': settings.SENTRY_ALLOW_REGISTRATION or request.session.get('can_register'), 'AUTH_PROVIDERS': get_auth_providers(), 'SOCIAL_AUTH_CREATE_USERS': settings.SOCIAL_AUTH_CREATE_USERS, }) return render_to_response('sentry/login.html', context, request)
def post(self, request, organization=None, *args, **kwargs): """ Process a login request via username/password. SSO login is handled elsewhere. """ login_form = AuthenticationForm(request, request.DATA) # Rate limit logins is_limited = ratelimiter.is_limited( u'auth:login:username:{}'.format( md5_text(request.DATA.get('username').lower()).hexdigest()), limit=10, window=60, # 10 per minute should be enough for anyone ) if is_limited: errors = {'__all__': [login_form.error_messages['rate_limited']]} metrics.incr('login.attempt', instance='rate_limited', skip_internal=True, sample_rate=1.0) return self.respond_with_error(errors) if not login_form.is_valid(): metrics.incr('login.attempt', instance='failure', skip_internal=True, sample_rate=1.0) return self.respond_with_error(login_form.errors) user = login_form.get_user() auth.login( request, user, organization_id=organization.id if organization else None, ) metrics.incr('login.attempt', instance='success', skip_internal=True, sample_rate=1.0) if not user.is_active: return Response({ 'nextUri': '/auth/reactivate/', 'user': serialize(user, user, DetailedUserSerializer()), }) active_org = self.get_active_organization(request) redirect_url = auth.get_org_redirect_url(request, active_org) return Response({ 'nextUri': auth.get_login_redirect(request, redirect_url), 'user': serialize(user, user, DetailedUserSerializer()), })
def handle_basic_auth(self, request, organization): form = SimplifiedAuthenticationForm( request, request.POST or None, captcha=bool(request.session.get('needs_captcha')), ) if form.is_valid(): login(request, form.get_user()) request.session.pop('needs_captcha', None) return self.redirect(get_login_redirect(request)) elif request.POST and not request.session.get('needs_captcha'): request.session['needs_captcha'] = 1 form = AuthenticationForm(request, request.POST or None, captcha=True) form.errors.pop('captcha', None) context = { 'form': form, 'CAN_REGISTER': features.has('auth:register') or request.session.get('can_register'), 'organization': organization, } return self.respond('sentry/organization-login.html', context)
def get_login_form(self, request): op = request.POST.get('op') return AuthenticationForm( request, request.POST if op == 'login' else None, captcha=bool(request.session.get('needs_captcha')), )
def _get_login_form(self, existing_user=None): request = self.request return AuthenticationForm( request, request.POST if request.POST.get('op') == 'login' else None, initial={ 'username': existing_user.username if existing_user else None, }, )
def handle(self, request): if request.user.is_authenticated(): return self.redirect(get_login_redirect(request)) form = AuthenticationForm(request, request.POST or None, captcha=bool(request.session.get('needs_captcha'))) if form.is_valid(): login(request, form.get_user()) request.session.pop('needs_captcha', None) return self.redirect(get_login_redirect(request)) elif request.POST and not request.session.get('needs_captcha'): request.session['needs_captcha'] = 1 form = AuthenticationForm(request, request.POST or None, captcha=True) form.errors.pop('captcha', None) request.session.set_test_cookie() context = { 'form': form, 'next': request.session.get('_next'), 'CAN_REGISTER': settings.SENTRY_ALLOW_REGISTRATION or request.session.get('can_register'), 'AUTH_PROVIDERS': get_auth_providers(), 'SOCIAL_AUTH_CREATE_USERS': settings.SOCIAL_AUTH_CREATE_USERS, } 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, 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()
def get_login_form(self, request): op = request.POST.get("op") return AuthenticationForm(request, request.POST if op == "login" else None)
def get_login_form(self, request): op = request.POST.get('op') return AuthenticationForm( request, request.POST if op == 'login' else None, )
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 get_login_form(self, request): return AuthenticationForm( request, request.POST or None, captcha=bool(request.session.get('needs_captcha')), )
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))