Example #1
0
def account_edit(newprofile=False):
    form = ProfileForm(obj=current_auth.user)
    form.edit_user = current_auth.user
    form.fullname.description = current_app.config.get('FULLNAME_REASON')
    form.email.description = current_app.config.get('EMAIL_REASON')
    form.username.description = current_app.config.get('USERNAME_REASON')
    form.timezone.description = current_app.config.get('TIMEZONE_REASON')
    if current_auth.user.email or newprofile is False:
        del form.email

    if form.validate_on_submit():
        # Can't auto-populate here because user.email is read-only
        current_auth.user.fullname = form.fullname.data
        current_auth.user.username = form.username.data
        current_auth.user.timezone = form.timezone.data

        if newprofile and not current_auth.user.email:
            useremail = UserEmailClaim.get(user=current_auth.user,
                                           email=form.email.data)
            if useremail is None:
                useremail = UserEmailClaim(user=current_auth.user,
                                           email=form.email.data)
                db.session.add(useremail)
            send_email_verify_link(useremail)
            db.session.commit()
            user_data_changed.send(current_auth.user,
                                   changes=['profile', 'email-claim'])
            flash(_(
                "Your profile has been updated. We sent you an email to confirm your address"
            ),
                  category='success')
        else:
            db.session.commit()
            user_data_changed.send(current_auth.user, changes=['profile'])
            flash(_("Your profile has been updated"), category='success')

        if newprofile:
            return render_redirect(get_next_url(), code=303)
        else:
            return render_redirect(url_for('account'), code=303)
    if newprofile:
        return render_form(
            form,
            title=_("Update profile"),
            formid='account_new',
            submit=_("Continue"),
            message=Markup(
                _(u"Hello, <strong>{fullname}</strong>. Please spare a minute to fill out your profile"
                  ).format(fullname=escape(current_auth.user.fullname))),
            ajax=True)
    else:
        return render_form(form,
                           title=_("Edit profile"),
                           formid='account_edit',
                           submit=_("Save changes"),
                           ajax=True)
Example #2
0
def register():
    form = RegisterForm()
    # Make Recaptcha optional
    if not (current_app.config.get('RECAPTCHA_PUBLIC_KEY')
            and current_app.config.get('RECAPTCHA_PRIVATE_KEY')):
        del form.recaptcha
    form.fullname.description = current_app.config.get('FULLNAME_REASON')
    form.email.description = current_app.config.get('EMAIL_REASON')
    form.username.description = current_app.config.get('USERNAME_REASON')
    if form.validate_on_submit():
        user = register_internal(None, form.fullname.data, form.password.data)
        user.username = form.username.data or None
        useremail = UserEmailClaim(user=user, email=form.email.data)
        db.session.add(useremail)
        send_email_verify_link(useremail)
        login_internal(user)
        db.session.commit()
        flash("You are now one of us. Welcome aboard!", category='success')
        if 'next' in request.args:
            return redirect(request.args['next'], code=303)
        else:
            return redirect(url_for('index'), code=303)
    return render_form(form=form,
                       title='Create an account',
                       formid='register',
                       submit='Register')
Example #3
0
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'),
    )
Example #4
0
def register():
    if current_auth.is_authenticated:
        return redirect(url_for('index'))
    form = RegisterForm()
    # Make Recaptcha optional
    if not (current_app.config.get('RECAPTCHA_PUBLIC_KEY')
            and current_app.config.get('RECAPTCHA_PRIVATE_KEY')):
        del form.recaptcha
    form.fullname.description = current_app.config.get('FULLNAME_REASON')
    form.email.description = current_app.config.get('EMAIL_REASON')
    form.username.description = current_app.config.get('USERNAME_REASON')
    if form.validate_on_submit():
        user = register_internal(form.username.data, form.fullname.data,
                                 form.password.data)
        useremail = UserEmailClaim(user=user, email=form.email.data)
        db.session.add(useremail)
        send_email_verify_link(useremail)
        login_internal(user)
        db.session.commit()
        flash(_("You are now one of us. Welcome aboard!"), category='success')
        return redirect(get_next_url(session=True), code=303)
    return render_form(
        form=form,
        title=_("Create an account"),
        formid='register',
        submit=_("Register"),
        message=current_app.config.get('CREATE_ACCOUNT_MESSAGE'))
Example #5
0
def add_email():
    form = NewEmailAddressForm()
    if form.validate_on_submit():
        useremail = UserEmailClaim.get(user=g.user, email=form.email.data)
        if useremail is None:
            useremail = UserEmailClaim(user=g.user, email=form.email.data)
            db.session.add(useremail)
            db.session.commit()
        send_email_verify_link(useremail)
        flash("We sent you an email to confirm your address.", 'success')
        user_data_changed.send(g.user, changes=['email-claim'])
        return render_redirect(url_for('.profile'), code=303)
    return render_form(form=form,
                       title="Add an email address",
                       formid="email_add",
                       submit="Add email",
                       ajax=True)
Example #6
0
 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"))
Example #7
0
def add_email():
    form = NewEmailAddressForm()
    if form.validate_on_submit():
        useremail = UserEmailClaim.get(user=current_auth.user,
                                       email=form.email.data)
        if useremail is None:
            useremail = UserEmailClaim(user=current_auth.user,
                                       email=form.email.data,
                                       type=form.type.data)
            db.session.add(useremail)
            db.session.commit()
        send_email_verify_link(useremail)
        flash(_("We sent you an email to confirm your address"), 'success')
        user_data_changed.send(current_auth.user, changes=['email-claim'])
        return render_redirect(url_for('.account'), code=303)
    return render_form(form=form,
                       title=_("Add an email address"),
                       formid='email_add',
                       submit=_("Add email"),
                       ajax=True)
Example #8
0
def add_email():
    form = NewEmailAddressForm()
    if form.validate_on_submit():
        useremail = UserEmailClaim.get(user=g.user, email=form.email.data)
        if useremail is None:
            useremail = UserEmailClaim(user=g.user, email=form.email.data)
            db.session.add(useremail)
            db.session.commit()
        send_email_verify_link(useremail)
        flash("We sent you an email to confirm your address.", 'success')
        user_data_changed.send(g.user, changes=['email-claim'])
        return render_redirect(url_for('.profile'), code=303)
    return render_form(form=form, title="Add an email address", formid="email_add", submit="Add email", ajax=True)
Example #9
0
def profile_edit(newprofile=False):
    form = ProfileForm(obj=g.user)
    form.fullname.description = current_app.config.get('FULLNAME_REASON')
    form.email.description = current_app.config.get('EMAIL_REASON')
    form.username.description = current_app.config.get('USERNAME_REASON')
    form.description.description = current_app.config.get('BIO_REASON')
    form.timezone.description = current_app.config.get('TIMEZONE_REASON')
    if g.user.email or newprofile is False:
        del form.email

    if form.validate_on_submit():
        # Can't auto-populate here because user.email is read-only
        g.user.fullname = form.fullname.data
        g.user.username = form.username.data
        g.user.description = form.description.data
        g.user.timezone = form.timezone.data

        if newprofile and not g.user.email:
            useremail = UserEmailClaim(user=g.user, email=form.email.data)
            db.session.add(useremail)
            send_email_verify_link(useremail)
            db.session.commit()
            user_data_changed.send(g.user, changes=['profile', 'email-claim'])
            flash(
                "Your profile has been updated. We sent you an email to confirm your address",
                category='success')
        else:
            db.session.commit()
            user_data_changed.send(g.user, changes=['profile'])
            flash("Your profile has been updated.", category='success')

        if newprofile:
            return render_redirect(get_next_url(), code=303)
        else:
            return render_redirect(url_for('profile'), code=303)
    if newprofile:
        return render_form(
            form,
            title="Update profile",
            formid="profile_new",
            submit="Continue",
            message=
            u"Hello, %s. Please spare a minute to fill out your profile." %
            g.user.fullname,
            ajax=True)
    else:
        return render_form(form,
                           title="Edit profile",
                           formid="profile_edit",
                           submit="Save changes",
                           ajax=True)
Example #10
0
 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.")
Example #11
0
def add_email():
    form = NewEmailAddressForm()
    if form.validate_on_submit():
        useremail = UserEmailClaim.get(user=current_auth.user, email=form.email.data)
        if useremail is None:
            useremail = UserEmailClaim(user=current_auth.user, email=form.email.data, type=form.type.data)
            db.session.add(useremail)
            db.session.commit()
        send_email_verify_link(useremail)
        flash(_("We sent you an email to confirm your address"), 'success')
        user_data_changed.send(current_auth.user, changes=['email-claim'])
        return render_redirect(url_for('.account'), code=303)
    return render_form(form=form, title=_("Add an email address"), formid='email_add',
        submit=_("Add email"), ajax=True)
Example #12
0
 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"))
Example #13
0
def profile_edit(newprofile=False):
    form = ProfileForm(obj=g.user)
    form.edit_user = g.user
    form.fullname.description = current_app.config.get('FULLNAME_REASON')
    form.email.description = current_app.config.get('EMAIL_REASON')
    form.username.description = current_app.config.get('USERNAME_REASON')
    form.description.description = current_app.config.get('BIO_REASON')
    form.timezone.description = current_app.config.get('TIMEZONE_REASON')
    if g.user.email or newprofile is False:
        del form.email

    if newprofile is True:
        del form.description

    if form.validate_on_submit():
        # Can't auto-populate here because user.email is read-only
        g.user.fullname = form.fullname.data
        g.user.username = form.username.data
        if not newprofile:
            g.user.description = form.description.data
        g.user.timezone = form.timezone.data

        if newprofile and not g.user.email:
            useremail = UserEmailClaim.get(user=g.user, email=form.email.data)
            if useremail is None:
                useremail = UserEmailClaim(user=g.user, email=form.email.data)
                db.session.add(useremail)
            send_email_verify_link(useremail)
            db.session.commit()
            user_data_changed.send(g.user, changes=['profile', 'email-claim'])
            flash("Your profile has been updated. We sent you an email to confirm your address", category='success')
        else:
            db.session.commit()
            user_data_changed.send(g.user, changes=['profile'])
            flash("Your profile has been updated.", category='success')

        if newprofile:
            return render_redirect(get_next_url(), code=303)
        else:
            return render_redirect(url_for('profile'), code=303)
    if newprofile:
        return render_form(form, title="Update profile", formid="profile_new", submit="Continue",
            message=Markup(u"Hello, <strong>{fullname}</strong>. Please spare a minute to fill out your profile.".format(
                fullname=escape(g.user.fullname))),
            ajax=True)
    else:
        return render_form(form, title="Edit profile", formid="profile_edit", submit="Save changes", ajax=True)
Example #14
0
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"),
    )
Example #15
0
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,
        )
Example #16
0
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)
Example #17
0
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 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)