def test_rate_limiting_on_form_posts(client, msend): # confirm a form client.post( "/[email protected]", headers={"referer": "http://somewhere.com"}, data={"name": "john"}, ) form = Form.get_with(host="somewhere.com", email="*****@*****.**") form.confirmed = True DB.session.add(form) DB.session.commit() # submit form many times replies = [] for _ in range(1000): r = client.post( "/[email protected]", headers={"referer": "http://somewhere.com"}, data={"name": "attacker"}, ) replies.append(r.status_code) limit = int(settings.RATE_LIMIT.split(" ")[0]) # the number of submissions should not be more than the rate limit form = Form.get_with(host="somewhere.com", email="*****@*****.**") assert form.counter < limit # should have gotten some 302 and then many 429 responses assert replies.count(302) <= limit assert replies.count(429) >= 900 - limit
def test_a_confusing_case(client, msend): """ Final boss. """ prepare() try: _, uf = create_user_and_form(client) DB.session.add( Form(email=uf.email, confirmed=False, host="example.com")) DB.session.add( Form(email=uf.email, confirmed=True, host="example.com/contact")) DB.session.add( Form(email=uf.email, confirmed=True, host="www.example.com/")) DB.session.commit() assert Form.query.count() == 4 form = Form.get_with(email=uf.email, host="example.com/") assert form assert form.confirmed assert form.host == "www.example.com/" form2 = Form.get_with(email=uf.email, host="www.example.com") assert form2 assert form2.confirmed assert form.host == "www.example.com/" contact = form.get_with(email=uf.email, host="www.example.com/contact/") assert contact assert contact.host == "example.com/contact" assert form.id != contact.id assert form.id == form2.id r = client.post( "/" + uf.email, headers={"Referer": "http://example.com/"}, data={"name": "example"}, ) assert r.status_code == 302 assert "next" in r.location r = client.post( "/" + uf.email, headers={"Referer": "www.example.com"}, data={"name": "example"}, ) assert r.status_code == 302 assert "next" in r.location assert msend.call_count == 2 assert Form.query.count() == 4 form3 = Form.get_with(email=uf.email, host="example.com") assert 2 == form.submissions.count() assert form3.id == form2.id == form.id finally: revert()
def test_a_confusing_case(client, msend): """ Final boss. """ prepare() try: _, uf = create_user_and_form(client) DB.session.add(Form(email=uf.email, confirmed=False, host="example.com")) DB.session.add(Form(email=uf.email, confirmed=True, host="example.com/contact")) DB.session.add(Form(email=uf.email, confirmed=True, host="www.example.com/")) DB.session.commit() assert Form.query.count() == 4 form = Form.get_with(email=uf.email, host="example.com/") assert form assert form.confirmed assert form.host == "www.example.com/" form2 = Form.get_with(email=uf.email, host="www.example.com") assert form2 assert form2.confirmed assert form.host == "www.example.com/" contact = form.get_with(email=uf.email, host="www.example.com/contact/") assert contact assert contact.host == "example.com/contact" assert form.id != contact.id assert form.id == form2.id r = client.post( "/" + uf.email, headers={"Referer": "http://example.com/"}, data={"name": "example"}, ) assert r.status_code == 302 assert "next" in r.location r = client.post( "/" + uf.email, headers={"Referer": "www.example.com"}, data={"name": "example"}, ) assert r.status_code == 302 assert "next" in r.location assert msend.call_count == 2 assert Form.query.count() == 4 form3 = Form.get_with(email=uf.email, host="example.com") assert 2 == form.submissions.count() assert form3.id == form2.id == form.id finally: revert()
def test_backwards_none_confirmed(client, msend): """ This time no form is confirmed. """ prepare() try: for host in hosts: f = Form(email=email, confirmed=False, host=host) DB.session.add(f) DB.session.commit() for referer in hosts: r = client.post( "/" + email, headers={"Referer": "http://" + referer}, data={"name": "example"}, ) assert r.status_code == 200 assert b"confirm" in r.get_data() assert Form.get_with(email=email, host=referer).host == hosts[0] assert Form.query.count() == len(hosts) assert Form.query.filter_by(confirmed=True).count() == 0 assert Submission.query.count() == 0 finally: revert()
def google_call(hashid): form = Form.get_with(hashid=hashid) session["ggl:form"] = form.hashid hint = form.email if not form.email.endswith("@gmail.com") and current_user.email.endswith( "@gmail.com" ): hint = current_user.email flow = google_auth_oauthlib.flow.Flow.from_client_config( settings.GOOGLE_CLIENT_CONFIG, scopes=["https://www.googleapis.com/auth/drive.file"], ) flow.redirect_uri = url_for( "google_callback", _external=True, _scheme="https", # can't figure out how to get this to honor PREFERRED_URL_SCHEME ) authorization_url, _ = flow.authorization_url( access_type="offline", login_hint=hint, prompt="consent", state=hashid, include_granted_scopes="true", ) return redirect(authorization_url)
def google_callback(): hashid = session["ggl:form"] del session["ggl:form"] if hashid != request.args.get("state"): return script_error("oauth-failed") form = Form.get_with(hashid=hashid) flow = google_auth_oauthlib.flow.Flow.from_client_config( settings.GOOGLE_CLIENT_CONFIG, state=hashid, scopes=["https://www.googleapis.com/auth/drive.file"], ) flow.redirect_uri = url_for("google_callback", _external=True, _scheme="https") flow.fetch_token(authorization_response=request.url.replace("http://", "https://")) plugin = Plugin(form_id=form.id, kind=PluginKind.google_sheets) plugin.access_token = json.dumps( { "token": flow.credentials.token, "refresh_token": flow.credentials.refresh_token, "token_uri": flow.credentials.token_uri, "id_token": flow.credentials.id_token, "client_id": flow.credentials.client_id, "client_secret": flow.credentials.client_secret, } ) spreadsheet_title = ( form.name if form.name else f"{settings.SERVICE_NAME} submissions for {form.hashid}" )[:128] google_creds = json.loads(plugin.access_token) credentials = google.oauth2.credentials.Credentials(**google_creds) spreadsheet_id, worksheet_id = create_google_sheet( form, spreadsheet_title, credentials ) plugin.plugin_data = { "spreadsheet_id": spreadsheet_id, "worksheet_id": worksheet_id, } DB.session.add(plugin) DB.session.commit() return script_data({"spreadsheet_id": spreadsheet_id})
def validate_user_form(hashid): """ Gets a form from a hashid, created on the dashboard. Checks to make sure the submission can be accepted by this form. """ form = Form.get_with(hashid=hashid) if not form: raise SubmitFormError(errors.bad_hashid_error(hashid)) if form.disabled: raise SubmitFormError(errors.disabled_error()) return form
def mailchimp_call(hashid): form = Form.get_with(hashid=hashid) session["mcp:form"] = form.hashid return redirect( "https://login.mailchimp.com/oauth2/authorize?" + urlencode( { "response_type": "code", "client_id": settings.MAILCHIMP_CLIENT_ID, "redirect_uri": url_for( "mailchimp_callback", _external=True, _scheme="https" ), "state": form.hashid, } ) )
def mailchimp_callback(): error = request.args.get("error") if error: return script_error(error if error == "access-denied" else "oauth-failed") hashid = session["mcp:form"] del session["mcp:form"] if hashid != request.args.get("state"): return script_error("oauth-failed") code = request.args.get("code") r = requests.post( "https://login.mailchimp.com/oauth2/token", data={ "grant_type": "authorization_code", "client_id": settings.MAILCHIMP_CLIENT_ID, "client_secret": settings.MAILCHIMP_CLIENT_SECRET, "code": code, "redirect_uri": url_for( "mailchimp_callback", _external=True, _scheme="https" ), }, ) if not r.ok: return script_error("oauth-failed") data = r.json() form = Form.get_with(hashid=hashid) plugin = Plugin(form_id=form.id, kind=PluginKind.mailchimp) r = requests.get( "https://login.mailchimp.com/oauth2/metadata", headers={"Authorization": "OAuth " + data["access_token"]}, ) if not r.ok: return script_error("oauth-failed") meta = r.json() plugin.access_token = data["access_token"] plugin.plugin_data = {"api_endpoint": meta["api_endpoint"], "dc": meta["dc"]} plugin.enabled = False DB.session.add(plugin) DB.session.commit() return script_data({"ok": True})
def slack_call(hashid): form = Form.get_with(hashid=hashid) session["slk:form"] = form.hashid return redirect( "https://slack.com/oauth/authorize?" + urlencode( { "client_id": settings.SLACK_CLIENT_ID, "scope": "incoming-webhook", "redirect_uri": url_for( "slack_callback", _external=True, _scheme="https" ), "state": form.hashid, } ) )
def export_submissions(hashid, format=None): if not current_user.has_feature("dashboard"): return jsonify({"error": "Please upgrade your account."}), 402 form = Form.get_with(hashid=hashid) if not form.controlled_by(current_user): return abort(401) submissions, fields = form.submissions_with_fields(with_ids=False) if format == "json": return Response( json.dumps( { "host": form.host, "email": form.email, "fields": fields, "submissions": submissions, }, sort_keys=True, indent=2, ), mimetype="application/json", headers={ "Content-Disposition": "attachment; filename=form-%s-submissions-%s.json" % (hashid, datetime.datetime.now().isoformat().split(".")[0]) }, ) elif format == "csv": out = io.BytesIO() w = csv.DictWriter(out, fieldnames=fields, encoding="utf-8") w.writeheader() for sub in submissions: w.writerow(sub) return Response( out.getvalue(), mimetype="text/csv", headers={ "Content-Disposition": "attachment; filename=form-%s-submissions-%s.csv" % (hashid, datetime.datetime.now().isoformat().split(".")[0]) }, )
def get_or_create_form(email, host): """ Gets the form if it already exits, otherwise checks to ensure that this is a valid new form submission. If so, creates a new form. """ form = Form.get_with(email=email, host=host) if not form: if request_wants_json(): # Can't create a new ajax form unless from the dashboard ajax_error_str = ( "To prevent spam, only " + settings.UPGRADED_PLAN_NAME + " accounts may create AJAX forms." ) raise SubmitFormError((jsonify({"error": ajax_error_str}), 400)) if ( url_domain(settings.SERVICE_URL) in host and host.rstrip("/") != settings.TEST_URL ): # Bad user is trying to submit a form spoofing formspree.io g.log.info( "User attempting to create new form spoofing SERVICE_URL. Ignoring." ) raise SubmitFormError( ( render_template( "error.html", title="Unable to submit form", text="Sorry." ), 400, ) ) # all good, create form form = Form(email, host=host, confirmed=False, normalize=True) if form.disabled: raise SubmitFormError(errors.disabled_error()) return form
def trello_call(hashid): form = Form.get_with(hashid=hashid) session["trl:form"] = form.hashid return redirect( "https://trello.com/1/authorize?{}".format( urlencode( { "return_url": url_for( "fragment_postmessage", _external=True, _scheme="https" ), "expiration": "never", "scope": "read,write", "name": settings.SERVICE_NAME, "key": settings.TRELLO_API_KEY, "callback_method": "fragment", "response_type": "fragment", } ) ) )
def slack_callback(): error = request.args.get("error") if error: return script_error(error if error == "access-denied" else "oauth-failed") hashid = session["slk:form"] del session["slk:form"] if hashid != request.args.get("state"): return script_error("oauth-failed") code = request.args.get("code") r = requests.get( "https://slack.com/api/oauth.access", params={ "client_id": settings.SLACK_CLIENT_ID, "client_secret": settings.SLACK_CLIENT_SECRET, "code": code, "redirect_uri": url_for("slack_callback", _external=True, _scheme="https"), }, ) if not r.ok: return script_error("oauth-failed") data = r.json() form = Form.get_with(hashid=hashid) plugin = Plugin(form_id=form.id, kind=PluginKind.slack) plugin.access_token = data["access_token"] plugin.plugin_data = { "team_name": data["team_name"], "team_id": data["team_id"], "incoming_webhook": data["incoming_webhook"], } plugin.enabled = True DB.session.add(plugin) DB.session.commit() return script_data( {"channel": data["incoming_webhook"]["channel"], "team": data["team_name"]} )
def test_user_gets_previous_forms_assigned_to_him(client, msend): # verify a form for márkö@example.com client.post( u"/márkö@example.com", headers={"Referer": "tomatoes.com"}, data={"name": "alice"}, ) f = Form.get_with(host="tomatoes.com", email="márkö@example.com") f.confirm_sent = True f.confirmed = True DB.session.add(f) DB.session.commit() # register márkö@example.com r = client.post("/register", data={ "email": "márkö@example.com", "password": "******" }) # confirm that the user account doesn't have access to the form r = client.get( "/api-int/forms", headers={ "Accept": "application/json", "Referer": settings.SERVICE_URL }, ) forms = json.loads(r.data.decode("utf-8"))["forms"] assert 0 == len(forms) # verify user email link, qs = parse_confirmation_link_sent(msend.call_args[1]["text"]) client.get(link, query_string=qs) # confirm that the user has no access to the form since he is not gold r = client.get( "/api-int/forms", headers={ "Accept": "application/json", "Referer": settings.SERVICE_URL }, ) forms = json.loads(r.data.decode("utf-8"))["forms"] assert 0 == len(forms) # upgrade user user = User.query.filter_by(email="márkö@example.com").first() user.plan = Plan.gold DB.session.add(user) DB.session.commit() # confirm that the user account has access to the form r = client.get( "/api-int/forms", headers={ "Accept": "application/json", "Referer": settings.SERVICE_URL }, ) forms = json.loads(r.data.decode("utf-8"))["forms"] assert 1 == len(forms) assert forms[0]["email"] == "márkö@example.com" assert forms[0]["host"] == "tomatoes.com" # verify a form for another address r = client.post("/[email protected]", headers={"Referer": "mark.com"}, data={"name": "luke"}) f = Form.get_with(host="mark.com", email="*****@*****.**") f.confirm_sent = True f.confirmed = True DB.session.add(f) DB.session.commit() # confirm that the user account doesn't have access to the form r = client.get( "/api-int/forms", headers={ "Accept": "application/json", "Referer": settings.SERVICE_URL }, ) forms = json.loads(r.data.decode("utf-8"))["forms"] assert 1 == len(forms) # add this other email address to user account client.post( "/api-int/account/emails", headers={ "Content-Type": "application/json", "Referer": settings.SERVICE_URL }, data=json.dumps({"address": "*****@*****.**"}), ) link, qs = parse_confirmation_link_sent(msend.call_args[1]["text"]) client.get(link, query_string=qs) # confirm that the user account now has access to the form r = client.get( "/api-int/forms", headers={ "Accept": "application/json", "Referer": settings.SERVICE_URL }, ) forms = json.loads(r.data.decode("utf-8"))["forms"] assert 2 == len(forms) assert (forms[0]["email"] == "*****@*****.**" ) # forms are sorted by -id, so the newer comes first assert forms[0]["host"] == "mark.com" # create a new form spontaneously with an email already verified r = client.post( u"/márkö@example.com", headers={"Referer": "elsewhere.com"}, data={"name": "luke"}, ) f = Form.get_with(host="elsewhere.com", email="márkö@example.com") f.confirm_sent = True f.confirmed = True DB.session.add(f) DB.session.commit() # confirm that the user has already accessto that form r = client.get( "/api-int/forms", headers={ "Accept": "application/json", "Referer": settings.SERVICE_URL }, ) forms = json.loads(r.data.decode("utf-8"))["forms"] assert 3 == len(forms) assert forms[0]["email"] == "márkö@example.com" assert forms[0]["host"] == "elsewhere.com"
def test_form_creation(client, msend): # register user r = client.post( "/register", data={"email": "*****@*****.**", "password": "******"} ) assert r.status_code == 302 assert 1 == User.query.count() email = Email.query.first() email.verified = True DB.session.add(email) DB.session.commit() # fail to create form r = client.post( "/api-int/forms", headers={"Content-type": "application/json", "Referer": settings.SERVICE_URL}, data={"email": "*****@*****.**"}, ) assert r.status_code == 402 assert "error" in json.loads(r.data.decode("utf-8")) assert 0 == Form.query.count() # upgrade user manually user = User.query.first() user.plan = Plan.gold DB.session.add(user) DB.session.commit() # successfully create form r = client.post( "/api-int/forms", headers={"Content-type": "application/json", "Referer": settings.SERVICE_URL}, data=json.dumps({"email": "*****@*****.**"}), ) resp = json.loads(r.data.decode("utf-8")) assert r.status_code == 201 assert "submission_url" in resp assert "hashid" in resp form_endpoint = resp["hashid"] assert resp["hashid"] in resp["submission_url"] assert 1 == Form.query.count() assert Form.query.first().id == Form.get_with(hashid=resp["hashid"]).id # post to form, already confirmed because it was created from the dashboard r = client.post( "/" + form_endpoint, headers={ "Referer": "http://formspree.io", "Content-Type": "application/x-www-form-urlencoded", }, data=urllib.parse.urlencode({"name": "bruce"}), follow_redirects=True, ) assert "Thanks!" in r.data.decode("utf-8") assert ( "Someone just submitted your form on {}".format("formspree.io") in msend.call_args[1]["text"] ) assert 1 == Form.query.count() # send 5 forms (monthly limits should not apply to the gold user) assert settings.MONTHLY_SUBMISSIONS_LIMIT == 2 for i in range(5): r = client.post( "/" + form_endpoint, headers={"Referer": "formspree.io"}, data={"name": "ana", "submission": "__%s__" % i}, ) form = Form.query.first() assert form.counter == 6 assert form.get_monthly_counter() == 6 assert "ana" in msend.call_args[1]["text"] assert "__4__" in msend.call_args[1]["text"] assert "past the limit" not in msend.call_args[1]["text"] # submit from a different host r = client.post( "/" + form_endpoint, headers={"Referer": "http://different.com"}, data={"name": "ignored"}, follow_redirects=True, ) assert r.status_code == 200 assert "Thanks!" in r.data.decode("utf-8") assert ( "Someone just submitted your form on {}".format("different.com") in msend.call_args[1]["text"] ) assert 1 == Form.query.count() # test submissions endpoint with the user downgraded user.plan = Plan.free DB.session.add(user) DB.session.commit() r = client.get( "/api-int/forms/" + form_endpoint, headers={"Referer": settings.SERVICE_URL} ) assert r.status_code == 402 # it should fail # test submissions endpoint without a logged user client.get("/logout") r = client.get( "/api-int/forms/" + form_endpoint, headers={"Referer": settings.SERVICE_URL} ) assert r.status_code == 401 # should return a json error (via flask login) assert "error" in r.json
def resend_confirmation(email): g.log = g.log.bind(email=email, host=request.form.get("host")) g.log.info("Resending confirmation.") if verify_captcha(request.form, request.remote_addr): # check if this email is listed on SendGrid's bounces r = requests.get( "https://api.sendgrid.com/api/bounces.get.json", params={ "email": email, "api_user": settings.SENDGRID_USERNAME, "api_key": settings.SENDGRID_PASSWORD, }, ) if r.ok and len(r.json()) and "reason" in r.json()[0]: # tell the user to verify his mailbox reason = r.json()[0]["reason"] g.log.info("Email is blocked on SendGrid. Telling the user.") if request_wants_json(): resp = jsonify({ "error": "Verify your mailbox, we can't reach it.", "reason": reason, }) else: resp = make_response( render_template( "info.html", title="Verify the availability of your mailbox", text= "We encountered an error when trying to deliver the confirmation message to <b>" + email + "</b> at the first time we tried. For spam reasons, we will not try again until we are sure the problem is fixed. Here's the reason:</p><p><center><i>" + reason + "</i></center></p><p>Please make sure this problem is not happening still, then go to <a href='/unblock/" + email + "'>this page</a> to unblock your address.</p><p>After you have unblocked the address, please try to resend the confirmation again.</p>", )) return resp # ~~~ # if there's no bounce, we proceed to resend the confirmation. # BUG: What if this is an owned form with hashid?? form = Form.get_with(email=email, host=request.form["host"]) if not form: if request_wants_json(): return jsonify({"error": "This form does not exist."}), 400 else: return ( render_template( "error.html", title="Check email address", text="This form does not exist.", ), 400, ) form.confirm_sent = False status = form.send_confirmation() if status["code"] == Form.STATUS_CONFIRMATION_SENT: if request_wants_json(): return jsonify({"success": "confirmation email sent"}) else: return render_template( "forms/confirmation_sent.html", email=email, host=request.form["host"], resend=status["code"] == Form.STATUS_CONFIRMATION_DUPLICATED, ) # fallback response -- should happen only when the recaptcha is failed. g.log.warning("Failed to resend confirmation.") return ( render_template( "error.html", title="Unable to send email", text= "Please make sure you pass the <i>reCaptcha</i> test before submitting.", ), 500, )
def test_automatically_created_forms(client, host="somewhere.com", email="*****@*****.**"): form = create_and_activate_form(client, host="somewhere.com", email="*****@*****.**") # add four filler submissions DB.session.add(Submission(form.id)) DB.session.add(Submission(form.id)) DB.session.add(Submission(form.id)) DB.session.commit() # submit again client.post( "/[email protected]", headers={"referer": "http://somewhere.com"}, data={ "_replyto": "*****@*****.**", "_next": "http://google.com", "name": "johannes", "message": "yahoo!", }, ) form = Form.query.first() assert form.submissions.count() == 4 # check archived values submissions = form.submissions.all() assert 4 == len(submissions) assert "message" in submissions[0].data assert "_next" not in submissions[0].data # check if submissions over the limit are correctly deleted assert settings.ARCHIVED_SUBMISSIONS_LIMIT == 4 client.post( "/[email protected]", headers={"referer": "http://somewhere.com"}, data={"which-submission-is-this": "the fifth!"}, ) form = Form.query.first() assert 4 == form.submissions.count() newest = form.submissions.first() # first should be the newest assert newest.data["which-submission-is-this"] == "the fifth!" client.post( "/[email protected]", headers={"referer": "http://somewhere.com"}, data={"which-submission-is-this": "the sixth!"}, ) client.post( "/[email protected]", headers={"referer": "http://somewhere.com"}, data={"which-submission-is-this": "the seventh!"}, ) form = Form.query.first() assert 4 == form.submissions.count() submissions = form.submissions.all() assert submissions[0].data["which-submission-is-this"] == "the seventh!" assert submissions[3].data["message"] == "yahoo!" # # try another form (to ensure that a form is not deleting wrong submissions) client.post( "/[email protected]", headers={"referer": "http://here.com"}, data={"name": "send me the confirmation!"}, ) secondform = Form.get_with(host="here.com", email="*****@*****.**") assert secondform is not None # this form wasn't confirmed, so it still has no submissions assert secondform.submissions.count() == 0 # confirm secondform.confirmed = True DB.session.add(form) DB.session.commit() # submit more times and test client.post( "/[email protected]", headers={"referer": "http://here.com"}, data={"name": "leibniz"}, ) secondform = Form.query.filter_by(host="here.com", email="*****@*****.**").first() assert 1 == secondform.submissions.count() assert secondform.submissions.first().data["name"] == "leibniz" client.post( "/[email protected]", headers={"referer": "http://here.com"}, data={"name": "schelling"}, ) secondform = Form.query.filter_by(host="here.com", email="*****@*****.**").first() assert 2 == secondform.submissions.count() newest, last = secondform.submissions.all() assert newest.data["name"] == "schelling" assert last.data["name"] == "leibniz" client.post( "/[email protected]", headers={"referer": "http://here.com"}, data={"name": "husserl"}, ) client.post( "/[email protected]", headers={"referer": "http://here.com"}, data={"name": "barban"}, ) client.post( "/[email protected]", headers={"referer": "http://here.com"}, data={"name": "gliffet"}, ) secondform = Form.query.filter_by(host="here.com", email="*****@*****.**").first() assert 4 == secondform.submissions.count() last = secondform.submissions.order_by(Submission.id.desc()).first() assert last.data["name"] == "gliffet" # now check the previous form again form = Form.query.first() submissions = form.submissions.all() assert submissions[0].data["which-submission-is-this"] == "the seventh!" assert submissions[3].data["message"] == "yahoo!"
def resend_confirmation(email): g.log = g.log.bind(email=email, host=request.form.get("host")) g.log.info("Resending confirmation.") if verify_captcha(request.form, request.remote_addr): # check if this email is listed on SendGrid's bounces r = requests.get( "https://api.sendgrid.com/api/bounces.get.json", params={ "email": email, "api_user": settings.SENDGRID_USERNAME, "api_key": settings.SENDGRID_PASSWORD, }, ) if r.ok and len(r.json()) and "reason" in r.json()[0]: # tell the user to verify his mailbox reason = r.json()[0]["reason"] g.log.info("Email is blocked on SendGrid. Telling the user.") if request_wants_json(): resp = jsonify( { "error": "Verify your mailbox, we can't reach it.", "reason": reason, } ) else: resp = make_response( render_template( "info.html", title="Verify the availability of your mailbox", text="We encountered an error when trying to deliver the confirmation message to <b>" + email + "</b> at the first time we tried. For spam reasons, we will not try again until we are sure the problem is fixed. Here's the reason:</p><p><center><i>" + reason + "</i></center></p><p>Please make sure this problem is not happening still, then go to <a href='/unblock/" + email + "'>this page</a> to unblock your address.</p><p>After you have unblocked the address, please try to resend the confirmation again.</p>", ) ) return resp # ~~~ # if there's no bounce, we proceed to resend the confirmation. # BUG: What if this is an owned form with hashid?? form = Form.get_with(email=email, host=request.form["host"]) if not form: if request_wants_json(): return jsonify({"error": "This form does not exist."}), 400 else: return ( render_template( "error.html", title="Check email address", text="This form does not exist.", ), 400, ) form.confirm_sent = False status = form.send_confirmation() if status["code"] == Form.STATUS_CONFIRMATION_SENT: if request_wants_json(): return jsonify({"success": "confirmation email sent"}) else: return render_template( "forms/confirmation_sent.html", email=email, host=request.form["host"], resend=status["code"] == Form.STATUS_CONFIRMATION_DUPLICATED, ) # fallback response -- should happen only when the recaptcha is failed. g.log.warning("Failed to resend confirmation.") return ( render_template( "error.html", title="Unable to send email", text="Please make sure you pass the <i>reCaptcha</i> test before submitting.", ), 500, )
def test_form_creation(client, msend): # register user r = client.post("/register", data={ "email": "*****@*****.**", "password": "******" }) assert r.status_code == 302 assert 1 == User.query.count() # fail to create form r = client.post( "/api-int/forms", headers={ "Content-type": "application/json", "Referer": settings.SERVICE_URL }, data=json.dumps({"email": "*****@*****.**"}), ) assert r.status_code == 402 assert "error" in json.loads(r.data.decode("utf-8")) assert 0 == Form.query.count() # upgrade user manually user = User.query.filter_by(email="*****@*****.**").first() user.plan = Plan.gold DB.session.add(user) DB.session.commit() # verify email email = Email.query.filter_by(address="*****@*****.**", owner_id=user.id).first() email.verified = True DB.session.add(email) DB.session.commit() # successfully create form msend.reset_mock() r = client.post( "/api-int/forms", headers={ "Accept": "application/json", "Content-type": "application/json", "Referer": settings.SERVICE_URL, }, data=json.dumps({"email": "*****@*****.**"}), ) # no email should have been sent assert not msend.called # should return success payload resp = json.loads(r.data.decode("utf-8")) assert r.status_code == 201 assert "submission_url" in resp assert "hashid" in resp form_endpoint = resp["hashid"] assert resp["hashid"] in resp["submission_url"] # a form should now exist in the db assert 1 == Form.query.count() assert Form.query.first().id == Form.get_with(hashid=resp["hashid"]).id assert Form.query.first().confirmed # post to the form r = client.post( "/" + form_endpoint, headers={"Referer": "http://testsite.com"}, data={"name": "bruce"}, follow_redirects=True, ) # Should get thank-you page assert "Thanks!" in r.data.decode("utf-8") assert "You're only one step away" not in msend.call_args[1]["text"] # send 5 forms (monthly limits should not apply to the gold user) assert settings.MONTHLY_SUBMISSIONS_LIMIT == 2 for i in range(5): r = client.post( "/" + form_endpoint, headers={"Referer": "http://testsite.com"}, data={ "name": "ana", "submission": "__%s__" % i }, ) # form should now have 6 submissions form = Form.query.first() assert form.counter == 6 # check last submission email assert "ana" in msend.call_args[1]["text"] assert "__4__" in msend.call_args[1]["text"] assert "past the limit" not in msend.call_args[1]["text"] # submit form from a different host -- this is OK now r = client.post( "/" + form_endpoint, headers={"Referer": "http://different.com"}, data={"name": "permitted"}, follow_redirects=True, ) assert r.status_code == 200 assert "permitted" in msend.call_args[1]["text"] # submit form without a Referer -- this is OK now r = client.post("/" + form_endpoint, data={"name": "permitted"}, follow_redirects=True) assert r.status_code == 200 assert "permitted" in msend.call_args[1]["text"] assert "unknown webpage" in msend.call_args[1]["text"]
def test_automatically_created_forms( client, host="somewhere.com", email="*****@*****.**" ): form = create_and_activate_form( client, host="somewhere.com", email="*****@*****.**" ) # add four filler submissions DB.session.add(Submission(form.id)) DB.session.add(Submission(form.id)) DB.session.add(Submission(form.id)) DB.session.commit() # submit again client.post( "/[email protected]", headers={"referer": "http://somewhere.com"}, data={ "_replyto": "*****@*****.**", "_next": "http://google.com", "name": "johannes", "message": "yahoo!", }, ) form = Form.query.first() assert form.submissions.count() == 4 # check archived values submissions = form.submissions.all() assert 4 == len(submissions) assert "message" in submissions[0].data assert "_next" not in submissions[0].data # check if submissions over the limit are correctly deleted assert settings.ARCHIVED_SUBMISSIONS_LIMIT == 4 client.post( "/[email protected]", headers={"referer": "http://somewhere.com"}, data={"which-submission-is-this": "the fifth!"}, ) form = Form.query.first() assert 4 == form.submissions.count() newest = form.submissions.first() # first should be the newest assert newest.data["which-submission-is-this"] == "the fifth!" client.post( "/[email protected]", headers={"referer": "http://somewhere.com"}, data={"which-submission-is-this": "the sixth!"}, ) client.post( "/[email protected]", headers={"referer": "http://somewhere.com"}, data={"which-submission-is-this": "the seventh!"}, ) form = Form.query.first() assert 4 == form.submissions.count() submissions = form.submissions.all() assert submissions[0].data["which-submission-is-this"] == "the seventh!" assert submissions[3].data["message"] == "yahoo!" # # try another form (to ensure that a form is not deleting wrong submissions) client.post( "/[email protected]", headers={"referer": "http://here.com"}, data={"name": "send me the confirmation!"}, ) secondform = Form.get_with(host="here.com", email="*****@*****.**") assert secondform is not None # this form wasn't confirmed, so it still has no submissions assert secondform.submissions.count() == 0 # confirm secondform.confirmed = True DB.session.add(form) DB.session.commit() # submit more times and test client.post( "/[email protected]", headers={"referer": "http://here.com"}, data={"name": "leibniz"}, ) secondform = Form.query.filter_by( host="here.com", email="*****@*****.**" ).first() assert 1 == secondform.submissions.count() assert secondform.submissions.first().data["name"] == "leibniz" client.post( "/[email protected]", headers={"referer": "http://here.com"}, data={"name": "schelling"}, ) secondform = Form.query.filter_by( host="here.com", email="*****@*****.**" ).first() assert 2 == secondform.submissions.count() newest, last = secondform.submissions.all() assert newest.data["name"] == "schelling" assert last.data["name"] == "leibniz" client.post( "/[email protected]", headers={"referer": "http://here.com"}, data={"name": "husserl"}, ) client.post( "/[email protected]", headers={"referer": "http://here.com"}, data={"name": "barban"}, ) client.post( "/[email protected]", headers={"referer": "http://here.com"}, data={"name": "gliffet"}, ) secondform = Form.query.filter_by( host="here.com", email="*****@*****.**" ).first() assert 4 == secondform.submissions.count() last = secondform.submissions.order_by(Submission.id.desc()).first() assert last.data["name"] == "gliffet" # now check the previous form again form = Form.query.first() submissions = form.submissions.all() assert submissions[0].data["which-submission-is-this"] == "the seventh!" assert submissions[3].data["message"] == "yahoo!"
def test_grandfather_limit_and_decrease(client, msend): settings.GRANDFATHER_MONTHLY_LIMIT = 2 settings.MONTHLY_SUBMISSIONS_LIMIT = 1 settings.FORM_LIMIT_DECREASE_ACTIVATION_SEQUENCE = 1 # the form limit should be 2 for the first form and 1 for the second # submit the forms client.post( "/[email protected]", headers={"referer": "http://somewhere.com"}, data={"name": "john"}, ) form_grandfathered = Form.get_with( host="somewhere.com", email="*****@*****.**" ) client.post( "/[email protected]", headers={"referer": "http://somewhere.com"}, data={"name": "john"}, ) form_new = Form.get_with(host="somewhere.com", email="*****@*****.**") # confirm formS form_grandfathered.confirmed = True DB.session.add(form_grandfathered) form_new.confirmed = True DB.session.add(form_new) DB.session.commit() # submit each form 3 times msend.reset_mock() for i in range(3): client.post( "/[email protected]", headers={"referer": "http://somewhere.com"}, data={"_replyto": "*****@*****.**", "name": "johann", "value": "v%s" % i}, ) assert len(msend.call_args_list) == 4 assert "*****@*****.**" == msend.call_args_list[-4][1]["to"] assert "90%" in msend.call_args_list[-4][1]["text"] assert "*****@*****.**" == msend.call_args_list[-3][1]["to"] assert "v0" in msend.call_args_list[-3][1]["text"] assert "*****@*****.**" == msend.call_args_list[-2][1]["to"] assert "v1" in msend.call_args_list[-2][1]["text"] assert "*****@*****.**" == msend.call_args_list[-1][1]["to"] assert "limit" in msend.call_args_list[-1][1]["text"] msend.reset_mock() for i in range(3): client.post( "/[email protected]", headers={"referer": "http://somewhere.com"}, data={"_replyto": "*****@*****.**", "name": "johann", "value": "v%s" % i}, ) assert len(msend.call_args_list) == 3 assert "*****@*****.**" == msend.call_args_list[-3][1]["to"] assert "v0" in msend.call_args_list[-3][1]["text"] assert "*****@*****.**" == msend.call_args_list[-2][1]["to"] assert "limit" in msend.call_args_list[-2][1]["text"] assert "*****@*****.**" == msend.call_args_list[-1][1]["to"] assert "limit" in msend.call_args_list[-1][1]["text"]
def test_user_gets_previous_forms_assigned_to_him(client, msend): # verify a form for márkö@example.com client.post( u"/márkö@example.com", headers={"Referer": "tomatoes.com"}, data={"name": "alice"}, ) f = Form.get_with(host="tomatoes.com", email="márkö@example.com") f.confirm_sent = True f.confirmed = True DB.session.add(f) DB.session.commit() # register márkö@example.com r = client.post( "/register", data={"email": "márkö@example.com", "password": "******"} ) # confirm that the user account doesn't have access to the form r = client.get( "/api-int/forms", headers={"Accept": "application/json", "Referer": settings.SERVICE_URL}, ) forms = json.loads(r.data.decode("utf-8"))["forms"] assert 0 == len(forms) # verify user email link, qs = parse_confirmation_link_sent(msend.call_args[1]["text"]) client.get(link, query_string=qs) # confirm that the user has no access to the form since he is not gold r = client.get( "/api-int/forms", headers={"Accept": "application/json", "Referer": settings.SERVICE_URL}, ) forms = json.loads(r.data.decode("utf-8"))["forms"] assert 0 == len(forms) # upgrade user user = User.query.filter_by(email="márkö@example.com").first() user.plan = Plan.gold DB.session.add(user) DB.session.commit() # confirm that the user account has access to the form r = client.get( "/api-int/forms", headers={"Accept": "application/json", "Referer": settings.SERVICE_URL}, ) forms = json.loads(r.data.decode("utf-8"))["forms"] assert 1 == len(forms) assert forms[0]["email"] == "márkö@example.com" assert forms[0]["host"] == "tomatoes.com" # verify a form for another address r = client.post( "/[email protected]", headers={"Referer": "mark.com"}, data={"name": "luke"} ) f = Form.get_with(host="mark.com", email="*****@*****.**") f.confirm_sent = True f.confirmed = True DB.session.add(f) DB.session.commit() # confirm that the user account doesn't have access to the form r = client.get( "/api-int/forms", headers={"Accept": "application/json", "Referer": settings.SERVICE_URL}, ) forms = json.loads(r.data.decode("utf-8"))["forms"] assert 1 == len(forms) # add this other email address to user account client.post( "/api-int/account/emails", headers={"Content-Type": "application/json", "Referer": settings.SERVICE_URL}, data=json.dumps({"address": "*****@*****.**"}), ) link, qs = parse_confirmation_link_sent(msend.call_args[1]["text"]) client.get(link, query_string=qs) # confirm that the user account now has access to the form r = client.get( "/api-int/forms", headers={"Accept": "application/json", "Referer": settings.SERVICE_URL}, ) forms = json.loads(r.data.decode("utf-8"))["forms"] assert 2 == len(forms) assert ( forms[0]["email"] == "*****@*****.**" ) # forms are sorted by -id, so the newer comes first assert forms[0]["host"] == "mark.com" # create a new form spontaneously with an email already verified r = client.post( u"/márkö@example.com", headers={"Referer": "elsewhere.com"}, data={"name": "luke"}, ) f = Form.get_with(host="elsewhere.com", email="márkö@example.com") f.confirm_sent = True f.confirmed = True DB.session.add(f) DB.session.commit() # confirm that the user has already accessto that form r = client.get( "/api-int/forms", headers={"Accept": "application/json", "Referer": settings.SERVICE_URL}, ) forms = json.loads(r.data.decode("utf-8"))["forms"] assert 3 == len(forms) assert forms[0]["email"] == "márkö@example.com" assert forms[0]["host"] == "elsewhere.com"
def test_form_creation(client, msend): # register user r = client.post( "/register", data={"email": "*****@*****.**", "password": "******"} ) assert r.status_code == 302 assert 1 == User.query.count() # fail to create form r = client.post( "/api-int/forms", headers={"Content-type": "application/json", "Referer": settings.SERVICE_URL}, data=json.dumps({"email": "*****@*****.**"}), ) assert r.status_code == 402 assert "error" in json.loads(r.data.decode("utf-8")) assert 0 == Form.query.count() # upgrade user manually user = User.query.filter_by(email="*****@*****.**").first() user.plan = Plan.gold DB.session.add(user) DB.session.commit() # verify email email = Email.query.filter_by( address="*****@*****.**", owner_id=user.id ).first() email.verified = True DB.session.add(email) DB.session.commit() # successfully create form msend.reset_mock() r = client.post( "/api-int/forms", headers={ "Accept": "application/json", "Content-type": "application/json", "Referer": settings.SERVICE_URL, }, data=json.dumps({"email": "*****@*****.**"}), ) # no email should have been sent assert not msend.called # should return success payload resp = json.loads(r.data.decode("utf-8")) assert r.status_code == 201 assert "submission_url" in resp assert "hashid" in resp form_endpoint = resp["hashid"] assert resp["hashid"] in resp["submission_url"] # a form should now exist in the db assert 1 == Form.query.count() assert Form.query.first().id == Form.get_with(hashid=resp["hashid"]).id assert Form.query.first().confirmed # post to the form r = client.post( "/" + form_endpoint, headers={"Referer": "http://testsite.com"}, data={"name": "bruce"}, follow_redirects=True, ) # Should get thank-you page assert "Thanks!" in r.data.decode("utf-8") assert "You're only one step away" not in msend.call_args[1]["text"] # send 5 forms (monthly limits should not apply to the gold user) assert settings.MONTHLY_SUBMISSIONS_LIMIT == 2 for i in range(5): r = client.post( "/" + form_endpoint, headers={"Referer": "http://testsite.com"}, data={"name": "ana", "submission": "__%s__" % i}, ) # form should now have 6 submissions form = Form.query.first() assert form.counter == 6 # check last submission email assert "ana" in msend.call_args[1]["text"] assert "__4__" in msend.call_args[1]["text"] assert "past the limit" not in msend.call_args[1]["text"] # submit form from a different host -- this is OK now r = client.post( "/" + form_endpoint, headers={"Referer": "http://different.com"}, data={"name": "permitted"}, follow_redirects=True, ) assert r.status_code == 200 assert "permitted" in msend.call_args[1]["text"] # submit form without a Referer -- this is OK now r = client.post( "/" + form_endpoint, data={"name": "permitted"}, follow_redirects=True ) assert r.status_code == 200 assert "permitted" in msend.call_args[1]["text"] assert "unknown webpage" in msend.call_args[1]["text"]
def test_grandfather_limit_and_decrease(client, msend): settings.GRANDFATHER_MONTHLY_LIMIT = 2 settings.MONTHLY_SUBMISSIONS_LIMIT = 1 settings.FORM_LIMIT_DECREASE_ACTIVATION_SEQUENCE = 1 # the form limit should be 2 for the first form and 1 for the second # submit the forms client.post( "/[email protected]", headers={"referer": "http://somewhere.com"}, data={"name": "john"}, ) form_grandfathered = Form.get_with(host="somewhere.com", email="*****@*****.**") client.post( "/[email protected]", headers={"referer": "http://somewhere.com"}, data={"name": "john"}, ) form_new = Form.get_with(host="somewhere.com", email="*****@*****.**") # confirm formS form_grandfathered.confirmed = True DB.session.add(form_grandfathered) form_new.confirmed = True DB.session.add(form_new) DB.session.commit() # submit each form 3 times msend.reset_mock() for i in range(3): client.post( "/[email protected]", headers={"referer": "http://somewhere.com"}, data={ "_replyto": "*****@*****.**", "name": "johann", "value": "v%s" % i }, ) assert len(msend.call_args_list) == 4 assert "*****@*****.**" == msend.call_args_list[-4][1]["to"] assert "90%" in msend.call_args_list[-4][1]["text"] assert "*****@*****.**" == msend.call_args_list[-3][1]["to"] assert "v0" in msend.call_args_list[-3][1]["text"] assert "*****@*****.**" == msend.call_args_list[-2][1]["to"] assert "v1" in msend.call_args_list[-2][1]["text"] assert "*****@*****.**" == msend.call_args_list[-1][1]["to"] assert "limit" in msend.call_args_list[-1][1]["text"] msend.reset_mock() for i in range(3): client.post( "/[email protected]", headers={"referer": "http://somewhere.com"}, data={ "_replyto": "*****@*****.**", "name": "johann", "value": "v%s" % i }, ) assert len(msend.call_args_list) == 3 assert "*****@*****.**" == msend.call_args_list[-3][1]["to"] assert "v0" in msend.call_args_list[-3][1]["text"] assert "*****@*****.**" == msend.call_args_list[-2][1]["to"] assert "limit" in msend.call_args_list[-2][1]["text"] assert "*****@*****.**" == msend.call_args_list[-1][1]["to"] assert "limit" in msend.call_args_list[-1][1]["text"]