def openid_end(return_url, request): """Step two of logging in; the OpenID provider redirects back here.""" cons = Consumer(session=request.session, store=openid_store) params = request.params if 'return_key' in params and not key_from_request(request): # We've followed a return_key that has terminated at the OpenID request # i.e. this is a stashed OpenID request (or a bogus return_key); the # OpenID request will therefore NOT have the return_key in its # return_to URL, so strip it log.debug("OpenID check stripping stale or bogus return_key(s) '{0}'" .format(params.getall('return_key'))) # Janrain OpenID treats params as a normal dict, so it's safe to lose # the MultiDict here (AFAICT). params = dict((k, v) for k, v in params.iteritems() if k != 'return_key') res = cons.complete(params, return_url) if res.status == SUCCESS: pass elif res.status == FAILURE: # The errors are, very helpfully, plain strings. Nevermind that # there's a little hierarchy of exception classes inside the openid # library; they all get squashed into homogenous goo in the return # value. F*****g awesome. Check for a few common things here and # assume the rest are wacky internal errors log.error('openid failure: ' + res.message) if res.message == 'Nonce already used or out of range': # You tend to get this if you hit refresh on login_finish raise OpenIDError("Sorry! Your login attempt expired; please start over.") else: raise OpenIDError("Something has gone hilariously wrong.") elif res.status == CANCEL: raise OpenIDError("Looks like you canceled the login.") else: log.error("Unexpected OpenID return status '{0}' with message '{1}'" .format(res.status, res.message)) raise OpenIDError("Something has gone hilariously wrong.") identity_url = unicode(res.identity_url) identity_webfinger = request.session.pop('pending_identity_webfinger', None) sreg_res = SRegResponse.fromSuccessResponse(res) or dict() pape_res = PAPEResponse.fromSuccessResponse(res) auth_time = pape_res.auth_time return identity_url, identity_webfinger, auth_time, sreg_res
def account_login(context, request): # XXX worth mentioning on this page how to log in with SSL, and offer a # crypto link if they already hit cancel and want to try again # Auto-fill OpenID on re-auth return_key = key_from_request(request) openid = request.auth.openid_url if return_key else None # just_landed is imprecise, but should serve to reduce false positives just_landed = request.referrer is None just_landed = just_landed or (request.host not in request.referrer) if not request.cookies and not just_landed: request.session.flash( 'It looks like you might not have cookies enabled in your ' 'browser. Alas, cookies are required to log in. If you ' 'think this message is in error, try refreshing the page.', icon='cookie--exclamation', level='warning') form = LoginForm(openid_identifier=openid, return_key=return_key) return {'form': form}
def account_login(context, request): # XXX worth mentioning on this page how to log in with SSL, and offer a # crypto link if they already hit cancel and want to try again # Auto-fill OpenID on re-auth return_key = key_from_request(request) openid = request.auth.openid_url if return_key else None # just_landed is imprecise, but should serve to reduce false positives just_landed = (request.referrer is None or request.host not in request.referrer) if not request.cookies and not just_landed: request.session.flash( 'It looks like you might not have cookies enabled in your ' 'browser. Alas, cookies are required to log in. If you ' 'think this message is in error, try refreshing the page.', icon='cookie--exclamation', level='warning') form = LoginForm(openid_identifier=openid, return_key=return_key) return {'form': form}
def account_login_browserid(context, request): return_key = key_from_request(request) def fail(msg=None): if msg: request.session.flash(msg, level=u'error', icon='key--exclamation') # XXX setting the status to 403 triggers Pyramid's exception view next_url = request.route_url('account.login') if return_key is not None: next_url = update_params(next_url, return_key=return_key) return {'next_url': next_url} ## Verify the identity assertion assertion = request.POST.get('assertion') try: data = verify_browserid(assertion, request) except BrowserIDError as e: flash_browserid_error(e, request) return fail() ## Attempt to resolve the identity to a local user email = data.get('email') if data.get('status') != 'okay' or not email: return fail("BrowserID authentication failed.") identity_email = model.session.query(IdentityEmail) \ .filter_by(email=email) \ .limit(1).first() if not identity_email: # New user or new ID request.session['pending_identity_email'] = email return {'next_url': request.route_url('account.register'), 'post_id': 'postform'} ## Attempt to log in try: auth_headers = security.remember( request, identity_email.user, browserid_email=email) request.session.changed() except BrowserIDNotFoundError: return fail("The email address '{0}' is registered against the account " "'{1}'. To log in as '{1}', log out then back in." .format(email, identity_email.user.name)) except BrowserIDAuthDisabledError: return fail("Your BrowserID is no longer accepted as your account has " "disabled BrowserID authentication.") # An existing user has logged in successfully. Bravo! request.response.headerlist.extend(auth_headers) log.debug("User {0} logged in via BrowserID: {1}" .format(identity_email.user.name, identity_email)) ## Handle redirection if identity_email.user == request.user: # Someone is just freshening up their cookie request.session.flash(u'Re-authentication successful', icon='user') if return_key is not None: old_url = fetch_stash(request, key=return_key)['url'] if old_url: next_url = update_params(old_url, return_key=return_key) log.debug('Following Return Key \'{0}\' to URL: {1}' .format(return_key, next_url)) return {'next_url': next_url} return {'next_url': request.route_url('root')} # Existing user; new login request.session.flash( 'Logged in with BrowserID', level=u'success', icon='user') return {'next_url': request.route_url('root')}
def account_login_xhr(context, request): return_key = key_from_request(request) next_url = request.route_url('account.login') if return_key is not None: next_url = update_params(next_url, return_key=return_key) return {'next_url': next_url}
def login_finish(context, request): """Step two of logging in; the OpenID provider redirects back here.""" def retry(): """Redirect to the login page, preserving the return key (if any).""" location = request.route_url('account.login') if return_key: location = update_params(location, return_key=return_key) return HTTPSeeOther(location=location) return_url = request.route_url('account.login_finish') return_key = key_from_request(request) if return_key is not None: return_url = update_params(return_url, return_key=return_key) try: identity_url, identity_webfinger, auth_time, sreg_res = openid_end( return_url=return_url, request=request) except OpenIDError as exc: request.session.flash(exc.message, level='error', icon='key--exclamation') return retry() # Find who owns this URL, if anyone identity_owner = model.session.query(User) \ .filter(User.identity_urls.any(url=identity_url)) \ .limit(1).first() if not identity_owner: if return_key: request.session.flash('Unknown OpenID URL.', level='error', icon='key--exclamation') return retry() # Someone is either registering a new account, or adding a new OpenID # to an existing account request.session['pending_identity_url'] = identity_url request.session.changed() # Try to pull a name and email address out of the SReg response username = re.sub(u'[^_a-z0-9]', u'', sreg_res.get('nickname', u'').lower()) form = RegistrationForm( username=username, email=sreg_res.get('email', u''), timezone=sreg_res.get('timezone', u'UTC'), ) return render_to_response( 'account/register.mako', dict( form=form, identity_url=identity_url, identity_webfinger=identity_webfinger, identity_email=None), request=request) elif identity_owner == request.user: # Someone is just freshening up their cookie auth_headers = safe_openid_login(request, identity_owner, identity_url) if auth_headers is None: return retry() request.session.flash(u'Re-authentication successful', icon='user') if return_key: # Fetch a stashed request old_url = fetch_stash(request, key=return_key)['url'] if old_url: location = update_params(old_url, return_key=return_key) log.debug('Following Return Key \'{0}\' to URL: {1}' .format(return_key, location)) return HTTPSeeOther(location, headers=auth_headers) return HTTPSeeOther(request.route_url('root'), headers=auth_headers) else: # Existing user; new login # Log the successful OpenID authentication, mindful of users that may # have OpenID logins disabled, for instance. # XXX should we deny a logged-in user to authenticate as another user? auth_headers = security.forget(request) headers = safe_openid_login(request, identity_owner, identity_url) if headers is None: return retry() auth_headers += headers # An existing user has logged in successfully. Bravo! log.debug("User {0!r} logged in via OpenID: {1!r}" .format(identity_owner.name, identity_url)) request.session.flash( u"Welcome back, {0}!" .format(identity_owner.display_name or identity_owner.name), level=u'success', icon='user') # XXX this should ALSO probably do the return_key redirect, good grief return HTTPSeeOther( location=request.route_url('root'), headers=auth_headers)
def account_login_persona(context, request): return_key = key_from_request(request) def fail(msg=None, exc=None): if msg: request.session.flash(msg, level=u'error', icon='key--exclamation') # XXX setting the status to 403 triggers Pyramid's exception view next_url = request.route_url('account.login') if return_key is not None: next_url = update_params(next_url, return_key=return_key) return { 'status': 'redirect', 'redirect-to': next_url, } ## Verify the identity assertion assertion = request.POST.get('assertion') try: data = verify_persona(assertion, request) except PersonaError as e: flash_persona_error(e, request) return fail() ## Attempt to resolve the identity to a local user email = data.get('email') if data.get('status') != 'okay' or not email: return fail("Persona authentication failed.") identity_email = model.session.query(IdentityEmail) \ .filter_by(email=email) \ .limit(1).first() if not identity_email: # New user or new ID request.session['pending_identity_email'] = email return { 'status': 'redirect', 'redirect-to': request.route_url('account.register'), } ## Attempt to log in try: auth_headers = security.remember( request, identity_email.user, persona_addr=email) request.session.changed() except PersonaNotFoundError: return fail("The email address '{0}' is registered against the account " "'{1}'. To log in as '{1}', log out then back in." .format(email, identity_email.user.name)) except PersonaAuthDisabledError: return fail("Your Persona is no longer accepted as your account has " "disabled Persona authentication.") # An existing user has logged in successfully. Bravo! request.response.headerlist.extend(auth_headers) log.debug("User {0} logged in via Persona: {1}" .format(identity_email.user.name, identity_email)) ## Handle redirection if identity_email.user == request.user: # Someone is just freshening up their cookie request.session.flash(u'Re-authentication successful', icon='user') if return_key is not None: old_url = fetch_stash(request, key=return_key)['url'] if old_url: next_url = update_params(old_url, return_key=return_key) log.debug('Following Return Key \'{0}\' to URL: {1}' .format(return_key, next_url)) return { 'status': 'redirect', 'redirect-to': next_url, } else: # Existing user; new login request.session.flash( 'Logged in with Persona', level=u'success', icon='user') # XXX this seems a mite fragile redirect = request.route_url('root') pathq = request.POST.get('pathq') if pathq and not pathq.startswith(request.route_path('account.login')): scheme, netloc = urlparse(redirect)[:2] redirect = urlunparse((scheme, netloc, pathq, '', '', '')) return { 'status': 'redirect', 'redirect-to': redirect, }