def get_user_extid(service, userdata): """ Retrieves a 'user', 'extid' and 'useremail' from the given service and userdata. """ provider = login_registry[service] extid = getextid(service=service, userid=userdata['userid']) user = None useremail = None if userdata.get('email'): useremail = UserEmail.get(email=userdata['email']) if extid is not None: user = extid.user # It is possible at this time that extid.user and useremail.user are different. # We do not handle it here, but in the parent function login_service_postcallback. elif useremail is not None and useremail.user is not None: user = useremail.user else: # Cross-check with all other instances of the same LoginProvider (if we don't have a user) # This is (for eg) for when we have two Twitter services with different access levels. for other_service, other_provider in login_registry.items(): if other_service != service and other_provider.__class__ == provider.__class__: other_extid = getextid(service=other_service, userid=userdata['userid']) if other_extid is not None: user = other_extid.user break # TODO: Make this work when we have multiple confirmed email addresses available return user, extid, useremail
def validate_email(self, field): field.data = field.data.lower() # Convert to lowercase existing = UserEmail.get(email=field.data) if existing is not None: raise wtforms.ValidationError(Markup( u'This email address is already registered. Do you want to <a href="{loginurl}">login</a> instead?'.format( loginurl=escape(url_for('.login')))))
def verify_email(md5sum): """ If the user has a pending email verification but has lost the email, allow them to send themselves another verification email. This endpoint is only linked to from the account page under the list of email addresses pending verification. """ useremail = UserEmail.get(md5sum=md5sum) if useremail and useremail.user == current_auth.user: # If an email address is already verified (this should not happen unless the # user followed a stale link), tell them it's done -- but only if the email # address belongs to this user, to prevent this endpoint from being used as a # probe for email addresses in the database. flash(_("This email address is already verified"), 'danger') return render_redirect(url_for('.account'), code=303) # Get the existing email claim that we're resending a verification link for emailclaim = UserEmailClaim.get_for(user=current_auth.user, md5sum=md5sum) if not emailclaim: abort(404) verify_form = VerifyEmailForm() if verify_form.validate_on_submit(): send_email_verify_link(emailclaim) flash(_("The verification email has been sent to this address"), 'success') return render_redirect(url_for('.account'), code=303) return render_form( form=verify_form, title=_("Resend the verification email?"), message=_("We will resend the verification email to '{email}'".format( email=emailclaim.email)), formid="email_verify", submit=_("Send"), cancel_url=url_for('.account'), )
def get_user_extid(service, userdata): """ Retrieves a 'user', 'extid' and 'useremail' from the given service and userdata. """ provider = login_registry[service] extid = getextid(service=service, userid=userdata['userid']) user = None useremail = None if userdata.get('email'): useremail = UserEmail.get(email=userdata['email']) if extid is not None: user = extid.user # It is possible at this time that extid.user and useremail.user are different. # We do not handle it here, but in the parent function login_service_postcallback. elif useremail is not None and useremail.user is not None: user = useremail.user else: # Cross-check with all other instances of the same LoginProvider (if we don't have a user) # This is (for eg) for when we have two Twitter services with different access levels. for other_service, other_provider in login_registry.items(): if (other_service != service and other_provider.__class__ == provider.__class__): other_extid = getextid(service=other_service, userid=userdata['userid']) if other_extid is not None: user = other_extid.user break # TODO: Make this work when we have multiple confirmed email addresses available return user, extid, useremail
def validate_email(self, field): field.data = field.data.lower() # Convert to lowercase existing = UserEmail.get(email=field.data) if existing is not None: if existing.user == g.user: raise forms.ValidationError(_("You have already registered this email address")) else: raise forms.ValidationError(_("This email address has already been claimed")) existing = UserEmailClaim.get(email=field.data, user=g.user) if existing is not None: raise forms.ValidationError(_("This email address is pending verification"))
def validate_email(self, field): field.data = field.data.lower() # Convert to lowercase existing = UserEmail.get(email=field.data) if existing is not None: if existing.user == g.user: raise wtforms.ValidationError( "You have already registered this email address.") else: raise wtforms.ValidationError( "This email address has already been claimed.") existing = UserEmailClaim.get(email=field.data, user=g.user) if existing is not None: raise wtforms.ValidationError( "This email address is pending verification.")
def validate_email(self, field): field.data = field.data.lower() # Convert to lowercase existing = UserEmail.get(email=field.data) if existing is not None: if existing.user == current_auth.user: raise forms.ValidationError( _("You have already registered this email address")) else: raise forms.ValidationError( _("This email address has already been claimed")) existing = UserEmailClaim.get_for(user=current_auth.user, email=field.data) if existing is not None: raise forms.ValidationError( _("This email address is pending verification"))
def make_email_primary(): form = EmailPrimaryForm() if form.validate_on_submit(): useremail = UserEmail.get_for(user=current_auth.user, email=form.email.data) if useremail is not None: if useremail.primary: flash(_("This is already your primary email address"), 'info') else: current_auth.user.primary_email = useremail db.session.commit() user_data_changed.send(current_auth.user, changes=['email-update-primary']) flash(_("Your primary email address has been updated"), 'success') else: flash(_("No such email address is linked to this user account"), 'danger') else: flash(_("Please select an email address"), 'danger') return render_redirect(url_for('.account'), code=303)
def remove_email(md5sum): useremail = UserEmail.get_for(user=current_auth.user, md5sum=md5sum) if not useremail: useremail = UserEmailClaim.get_for(user=current_auth.user, md5sum=md5sum) if not useremail: abort(404) if isinstance(useremail, UserEmail) and useremail.primary: flash(_("You cannot remove your primary email address"), 'danger') return render_redirect(url_for('.account'), code=303) if request.method == 'POST': # FIXME: Confirm validation success user_data_changed.send(current_auth.user, changes=['email-delete']) return render_delete_sqla( useremail, db, title=_("Confirm removal"), message=_("Remove email address {email} from your account?").format( email=useremail.email), success=_("You have removed your email address {email}").format( email=useremail.email), next=url_for('.account'), delete_text=_("Remove"), )
def make_fixtures(self): """ Create users, attach them to organizations. Create test client app, add test resource, action and message. """ crusoe = User(username=u"crusoe", fullname=u"Crusoe Celebrity Dachshund") oakley = User(username=u"oakley") piglet = User(username=u"piglet") nameless = User(fullname="Nameless") db.session.add_all([crusoe, oakley, piglet, nameless]) self.crusoe = crusoe self.oakley = oakley self.piglet = piglet self.nameless = nameless crusoe_email = UserEmail(email=u"*****@*****.**", primary=True, user=crusoe) crusoe_phone = UserPhone(phone=u"+8080808080", primary=True, user=crusoe) oakley_email = UserEmail(email=u"*****@*****.**", user=oakley) db.session.add_all([crusoe_email, crusoe_phone, oakley_email]) self.crusoe_email = crusoe_email self.crusoe_phone = crusoe_phone batdog = Organization(name=u'batdog', title=u'Batdog') batdog.owners.users.append(crusoe) batdog.members.users.append(oakley) db.session.add(batdog) self.batdog = batdog specialdachs = Organization(name=u"specialdachs", title=u"Special Dachshunds") specialdachs.owners.users.append(oakley) specialdachs.members.users.append(piglet) db.session.add(specialdachs) self.specialdachs = specialdachs client = Client(title=u"Batdog Adventures", org=batdog, confidential=True, namespace=u'fun.batdogadventures.com', website=u"http://batdogadventures.com") db.session.add(client) self.client = client dachshunds = Team(title=u"Dachshunds", org=batdog) db.session.add(dachshunds) self.dachshunds = dachshunds team_client_permission = TeamClientPermissions( team=dachshunds, client=client, access_permissions=u"admin") self.team_client_permission = team_client_permission db.session.add(team_client_permission) client_team_access = ClientTeamAccess( org=batdog, client=client, access_level=CLIENT_TEAM_ACCESS.ALL) db.session.add(client_team_access) bdfl = Permission(name=u"bdfl", title=u"BDFL", user=crusoe) db.session.add(bdfl) self.bdfl = bdfl user_client_permissions = UserClientPermissions(user=crusoe, client=client) db.session.add(user_client_permissions) self.user_client_permissions = user_client_permissions resource = Resource(name=u"test_resource", title=u"Test Resource", client=client) db.session.add(resource) self.resource = resource resource_action = ResourceAction(name=u'Fun', resource=resource, title=u'fun') db.session.add(resource_action) self.resource_action = resource_action action = ResourceAction(name=u"read", title=u"Read", resource=resource) db.session.add(action) self.action = action message = SMSMessage(phone_number=crusoe_phone.phone, transaction_id=u"Ruff" * 5, message=u"Wuff Wuff") db.session.add(message) db.session.commit() self.message = message
def login_service_postcallback(service, userdata): """ Called from :func:login_service_callback after receiving data from the upstream login service """ # 1. Check whether we have an existing UserExternalId user, extid, useremail = get_user_extid(service, userdata) # If extid is not None, user.extid == user, guaranteed. # If extid is None but useremail is not None, user == useremail.user # However, if both extid and useremail are present, they may be different users if extid is not None: extid.oauth_token = userdata.get('oauth_token') extid.oauth_token_secret = userdata.get('oauth_token_secret') extid.oauth_token_type = userdata.get('oauth_token_type') extid.username = userdata.get('username') # TODO: Save refresh token and expiry date where present extid.oauth_refresh_token = userdata.get('oauth_refresh_token') extid.oauth_expiry_date = userdata.get('oauth_expiry_date') extid.oauth_refresh_expiry = userdata.get( 'oauth_refresh_expiry') # TODO: Check this extid.last_used_at = db.func.utcnow() else: # New external id. Register it. extid = UserExternalId( user=user, # This may be None right now. Will be handled below service=service, userid=userdata['userid'], username=userdata.get('username'), oauth_token=userdata.get('oauth_token'), oauth_token_secret=userdata.get('oauth_token_secret'), oauth_token_type=userdata.get('oauth_token_type'), last_used_at=db.func.utcnow() # TODO: Save refresh token ) if user is None: if current_auth: # Attach this id to currently logged-in user user = current_auth.user extid.user = user else: # Register a new user user = register_internal(None, userdata.get('fullname'), None) extid.user = user if userdata.get('username'): if valid_username(userdata['username']) and user.is_valid_name( userdata['username']): # Set a username for this user if it's available user.username = userdata['username'] else: # We have an existing user account from extid or useremail if current_auth and current_auth.user != user: # Woah! Account merger handler required # Always confirm with user before doing an account merger session['merge_buid'] = user.buid elif useremail and useremail.user != user: # Once again, account merger required since the extid and useremail are linked to different users session['merge_buid'] = useremail.user.buid # Check for new email addresses if userdata.get('email') and not useremail: user.add_email(userdata['email']) # If there are multiple email addresses, add any that are not already claimed. # If they are already claimed by another user, this calls for an account merge # request, but we can only merge two users at a time. Ask for a merge if there # isn't already one pending if userdata.get('emails'): for email in userdata['emails']: existing = UserEmail.get(email) if existing: if existing.user != user and 'merge_buid' not in session: session['merge_buid'] = existing.user.buid else: user.add_email(email) if userdata.get('emailclaim'): emailclaim = UserEmailClaim(user=user, email=userdata['emailclaim']) db.session.add(emailclaim) send_email_verify_link(emailclaim) # Is the user's fullname missing? Populate it. if not user.fullname and userdata.get('fullname'): user.fullname = userdata['fullname'] if not current_auth: # If a user isn't already logged in, login now. login_internal(user) flash( _("You have logged in via {service}").format( service=login_registry[service].title), 'success', ) next_url = get_next_url(session=True) db.session.add(extid) # If we made a new extid, add it to the session now db.session.commit() # Finally: set a login method cookie and send user on their way if not current_auth.user.is_profile_complete(): login_next = url_for('.account_new', next=next_url) else: login_next = next_url if 'merge_buid' in session: return set_loginmethod_cookie( redirect(url_for('.account_merge', next=login_next), code=303), service) else: return set_loginmethod_cookie(redirect(login_next, code=303), service)
def make_fixtures(self): """ Create users, attach them to organizations. Create test client app, add test resource, action and message. """ crusoe = User(username="******", fullname="Crusoe Celebrity Dachshund") oakley = User(username="******") piglet = User(username="******") nameless = User(fullname="Nameless") db.session.add_all([crusoe, oakley, piglet, nameless]) self.crusoe = crusoe self.oakley = oakley self.piglet = piglet self.nameless = nameless crusoe_email = UserEmail( email="*****@*****.**", user=crusoe, primary=True ) crusoe_phone = UserPhone(phone="+8080808080", user=crusoe, primary=True) oakley_email = UserEmail(email="*****@*****.**", user=oakley) db.session.add_all([crusoe_email, crusoe_phone, oakley_email]) self.crusoe_email = crusoe_email self.crusoe_phone = crusoe_phone batdog = Organization(name='batdog', title='Batdog') batdog.owners.users.append(crusoe) db.session.add(batdog) self.batdog = batdog specialdachs = Organization(name="specialdachs", title="Special Dachshunds") specialdachs.owners.users.append(oakley) db.session.add(specialdachs) self.specialdachs = specialdachs auth_client = AuthClient( title="Batdog Adventures", organization=batdog, confidential=True, namespace='fun.batdogadventures.com', website="http://batdogadventures.com", ) db.session.add(auth_client) self.auth_client = auth_client dachshunds = Team(title="Dachshunds", organization=batdog) db.session.add(dachshunds) self.dachshunds = dachshunds auth_client_team_permissions = AuthClientTeamPermissions( team=dachshunds, auth_client=auth_client, access_permissions="admin" ) self.auth_client_team_permissions = auth_client_team_permissions db.session.add(auth_client_team_permissions) auth_client_user_permissions = AuthClientUserPermissions( user=crusoe, auth_client=auth_client ) db.session.add(auth_client_user_permissions) self.auth_client_user_permissions = auth_client_user_permissions message = SMSMessage( phone_number=crusoe_phone.phone, transactionid="Ruff" * 5, message="Wuff Wuff", ) db.session.add(message) db.session.commit() self.message = message
def validate_email(self, field): field.data = field.data.lower() # Convert to lowercase existing = UserEmail.get(email=field.data) if existing is not None and existing.user != self.edit_obj: raise wtforms.ValidationError("This email address has been claimed by another user")
def login_service_postcallback(service, userdata): user, extid, useremail = get_user_extid(service, userdata) if extid is not None: extid.oauth_token = userdata.get("oauth_token") extid.oauth_token_secret = userdata.get("oauth_token_secret") extid.oauth_token_type = userdata.get("oauth_token_type") extid.username = userdata.get("username") # TODO: Save refresh token and expiry date where present extid.oauth_refresh_token = userdata.get("oauth_refresh_token") extid.oauth_expiry_date = userdata.get("oauth_expiry_date") extid.oauth_refresh_expiry = userdata.get("oauth_refresh_expiry") # TODO: Check this else: # New external id. Register it. extid = UserExternalId( user=user, # This may be None right now. Will be handled below service=service, userid=userdata["userid"], username=userdata.get("username"), oauth_token=userdata.get("oauth_token"), oauth_token_secret=userdata.get("oauth_token_secret"), oauth_token_type=userdata.get("oauth_token_type") # TODO: Save refresh token ) db.session.add(extid) if user is None: if g.user: # Attach this id to currently logged-in user user = g.user extid.user = user else: # Register a new user user = register_internal(None, userdata.get("fullname"), None) extid.user = user if userdata.get("username"): if valid_username(userdata["username"]) and user.is_valid_username(userdata["username"]): # Set a username for this user if it's available user.username = userdata["username"] else: # This id is attached to a user if g.user and g.user != user: # Woah! Account merger handler required # Always confirm with user before doing an account merger session["merge_userid"] = user.userid # Check for new email addresses if userdata.get("email") and not useremail: user.add_email(userdata["email"]) # If there are multiple email addresses, add any that are not already claimed. # If they are already claimed by another user, this calls for an account merge # request, but we can only merge two users at a time. Ask for a merge if there # isn't already one pending if userdata.get("emails"): for email in userdata["emails"]: existing = UserEmail.get(email) if existing: if existing.user != user and "merge_userid" not in session: session["merge_userid"] = existing.user.userid else: user.add_email(email) if userdata.get("emailclaim"): emailclaim = UserEmailClaim(user=user, email=userdata["emailclaim"]) db.session.add(emailclaim) send_email_verify_link(emailclaim) # Is the user's fullname missing? Populate it. if not user.fullname and userdata.get("fullname"): user.fullname = userdata["fullname"] if not g.user: # If a user isn't already logged in, login now. login_internal(user) flash(_(u"You have logged in via {service}").format(service=login_registry[service].title), "success") next_url = get_next_url(session=True) db.session.commit() # Finally: set a login method cookie and send user on their way if not user.is_profile_complete(): login_next = url_for(".profile_new", next=next_url) else: login_next = next_url if "merge_userid" in session: return set_loginmethod_cookie(redirect(url_for(".profile_merge", next=login_next), code=303), service) else: return set_loginmethod_cookie(redirect(login_next, code=303), service)
def validate_email(self, field): existing = UserEmail.get(email=field.data) if existing is not None and existing.user != self.edit_obj: raise forms.ValidationError(_("This email address has been claimed by another user"))
def validate_email(self, field): field.data = field.data.lower() # Convert to lowercase existing = UserEmail.get(email=field.data) if existing is not None and existing.user != self.edit_obj: raise wtforms.ValidationError( "This email address has been claimed by another user")
def login_service_postcallback(service, userdata): """ Called from :func:login_service_callback after receiving data from the upstream login service """ # 1. Check whether we have an existing UserExternalId user, extid, useremail = get_user_extid(service, userdata) # If extid is not None, user.extid == user, guaranteed. # If extid is None but useremail is not None, user == useremail.user # However, if both extid and useremail are present, they may be different users if extid is not None: extid.oauth_token = userdata.get('oauth_token') extid.oauth_token_secret = userdata.get('oauth_token_secret') extid.oauth_token_type = userdata.get('oauth_token_type') extid.username = userdata.get('username') # TODO: Save refresh token and expiry date where present extid.oauth_refresh_token = userdata.get('oauth_refresh_token') extid.oauth_expiry_date = userdata.get('oauth_expiry_date') extid.oauth_refresh_expiry = userdata.get('oauth_refresh_expiry') # TODO: Check this extid.last_used_at = db.func.utcnow() else: # New external id. Register it. extid = UserExternalId( user=user, # This may be None right now. Will be handled below service=service, userid=userdata['userid'], username=userdata.get('username'), oauth_token=userdata.get('oauth_token'), oauth_token_secret=userdata.get('oauth_token_secret'), oauth_token_type=userdata.get('oauth_token_type'), last_used_at=db.func.utcnow() # TODO: Save refresh token ) if user is None: if current_auth: # Attach this id to currently logged-in user user = current_auth.user extid.user = user else: # Register a new user user = register_internal(None, userdata.get('fullname'), None) extid.user = user if userdata.get('username'): if valid_username(userdata['username']) and user.is_valid_name(userdata['username']): # Set a username for this user if it's available user.username = userdata['username'] else: # We have an existing user account from extid or useremail if current_auth and current_auth.user != user: # Woah! Account merger handler required # Always confirm with user before doing an account merger session['merge_buid'] = user.buid elif useremail and useremail.user != user: # Once again, account merger required since the extid and useremail are linked to different users session['merge_buid'] = useremail.user.buid # Check for new email addresses if userdata.get('email') and not useremail: user.add_email(userdata['email']) # If there are multiple email addresses, add any that are not already claimed. # If they are already claimed by another user, this calls for an account merge # request, but we can only merge two users at a time. Ask for a merge if there # isn't already one pending if userdata.get('emails'): for email in userdata['emails']: existing = UserEmail.get(email) if existing: if existing.user != user and 'merge_buid' not in session: session['merge_buid'] = existing.user.buid else: user.add_email(email) if userdata.get('emailclaim'): emailclaim = UserEmailClaim(user=user, email=userdata['emailclaim']) db.session.add(emailclaim) send_email_verify_link(emailclaim) # Is the user's fullname missing? Populate it. if not user.fullname and userdata.get('fullname'): user.fullname = userdata['fullname'] if not current_auth: # If a user isn't already logged in, login now. login_internal(user) flash(_(u"You have logged in via {service}").format(service=login_registry[service].title), 'success') next_url = get_next_url(session=True) db.session.add(extid) # If we made a new extid, add it to the session now db.session.commit() # Finally: set a login method cookie and send user on their way if not current_auth.user.is_profile_complete(): login_next = url_for('.account_new', next=next_url) else: login_next = next_url if 'merge_buid' in session: return set_loginmethod_cookie(redirect(url_for('.account_merge', next=login_next), code=303), service) else: return set_loginmethod_cookie(redirect(login_next, code=303), service)
def confirm_email(md5sum, secret): emailclaim = UserEmailClaim.get_by(md5sum=md5sum, verification_code=secret) if emailclaim is not None: if 'verify' in emailclaim.permissions(current_auth.user): existing = UserEmail.get(email=emailclaim.email) if existing is not None: claimed_email = emailclaim.email claimed_user = emailclaim.user db.session.delete(emailclaim) db.session.commit() if claimed_user != current_auth.user: return render_message( title=_("Email address already claimed"), message=Markup( _( "The email address <code>{email}</code> has already been verified by another user" ).format(email=escape(claimed_email)) ), ) else: return render_message( title=_("Email address already verified"), message=Markup( _( "Hello <strong>{fullname}</strong>! " "Your email address <code>{email}</code> has already been verified" ).format( fullname=escape(claimed_user.fullname), email=escape(claimed_email), ) ), ) useremail = emailclaim.user.add_email( emailclaim.email, primary=emailclaim.user.email is None, type=emailclaim.type, private=emailclaim.private, ) db.session.delete(emailclaim) UserEmailClaim.all(useremail.email).delete(synchronize_session=False) db.session.commit() user_data_changed.send(current_auth.user, changes=['email']) return render_message( title=_("Email address verified"), message=Markup( _( "Hello <strong>{fullname}</strong>! " "Your email address <code>{email}</code> has now been verified" ).format( fullname=escape(emailclaim.user.fullname), email=escape(useremail.email), ) ), ) else: return render_message( title=_("This was not for you"), message=_( "You’ve opened an email verification link that was meant for another user. " "If you are managing multiple accounts, please login with the correct account " "and open the link again" ), code=403, ) else: return render_message( title=_("Expired confirmation link"), message=_( "The confirmation link you clicked on is either invalid or has expired" ), code=404, )
def validate_email(self, field): existing = UserEmail.get(email=field.data) if existing is not None and existing.user != self.edit_obj: raise forms.ValidationError( _("This email address has been claimed by another user"))