From 392c7ae69e30bd07ad8fda0933d0d75c31cf7dd4 Mon Sep 17 00:00:00 2001 From: "Jeremy \"EndenDragon\" Zhang" Date: Thu, 21 Sep 2017 00:12:49 -0700 Subject: [PATCH] Add recaptcha v2 to verify guest logins (#55) --- webapp/config.example.py | 4 ++ webapp/titanembeds/blueprints/api/api.py | 16 +++++ webapp/titanembeds/blueprints/embed/embed.py | 4 +- webapp/titanembeds/static/css/embedstyle.css | 5 ++ webapp/titanembeds/static/js/embed.js | 66 +++++++++++++------- webapp/titanembeds/templates/embed.html.j2 | 9 +++ 6 files changed, 81 insertions(+), 23 deletions(-) diff --git a/webapp/config.example.py b/webapp/config.example.py index fe8ae22..dcd9a04 100644 --- a/webapp/config.example.py +++ b/webapp/config.example.py @@ -8,6 +8,10 @@ config = { # Rest API in https://developer.paypal.com/developer/applications 'paypal-client-id': "Paypal client id", 'paypal-client-secret': "Paypal client secret", + + # V2 reCAPTCHA from https://www.google.com/recaptcha/admin + 'recaptcha-site-key': "reCAPTCHA v2 Site Key", + 'recaptcha-secret-key': "reCAPTCHA v2 Secret Key", 'app-location': "/var/www/Titan/webapp/", 'app-secret': "Type something random here, go wild.", diff --git a/webapp/titanembeds/blueprints/api/api.py b/webapp/titanembeds/blueprints/api/api.py index 3f8c6a8..5e6b3c9 100644 --- a/webapp/titanembeds/blueprints/api/api.py +++ b/webapp/titanembeds/blueprints/api/api.py @@ -3,12 +3,14 @@ from titanembeds.decorators import valid_session_required, discord_users_only from titanembeds.utils import check_guild_existance, guild_accepts_visitors, guild_query_unauth_users_bool, get_client_ipaddr, discord_api, rate_limiter, channel_ratelimit_key, guild_ratelimit_key, user_unauthenticated, checkUserRevoke, checkUserBanned, update_user_status, check_user_in_guild, get_guild_channels, guild_webhooks_enabled from titanembeds.oauth import user_has_permission, generate_avatar_url, check_user_can_administrate_guild from flask import Blueprint, abort, jsonify, session, request, url_for +from flask import current_app as app from flask_socketio import emit from sqlalchemy import and_ import random import json import datetime import re +import requests from config import config api = Blueprint("api", __name__) @@ -270,6 +272,17 @@ def post(): response.status_code = status_code return response +def verify_captcha_request(captcha_response, ip_address): + payload = { + "secret": config["recaptcha-secret-key"], + "response": captcha_response, + "remoteip": ip_address, + } + if app.config["DEBUG"]: + del payload["remoteip"] + r = requests.post("https://www.google.com/recaptcha/api/siteverify", data=payload).json() + return r["success"] + @api.route("/create_unauthenticated_user", methods=["POST"]) @rate_limiter.limit("3 per 30 minute", key_func=guild_ratelimit_key) def create_unauthenticated_user(): @@ -278,6 +291,9 @@ def create_unauthenticated_user(): guild_id = request.form['guild_id'] ip_address = get_client_ipaddr() username = username.strip() + captcha_response = request.form['captcha_response'] + if not verify_captcha_request(captcha_response, request.remote_addr): + abort(412) if len(username) < 2 or len(username) > 32: abort(406) if not all(x.isalnum() or x.isspace() or "-" == x or "_" == x for x in username): diff --git a/webapp/titanembeds/blueprints/embed/embed.py b/webapp/titanembeds/blueprints/embed/embed.py index 9eca5c3..943dd58 100644 --- a/webapp/titanembeds/blueprints/embed/embed.py +++ b/webapp/titanembeds/blueprints/embed/embed.py @@ -65,11 +65,13 @@ def guild_embed(guild_id): customcss = get_custom_css() return render_template("embed.html.j2", login_greeting=get_logingreeting(), - guild_id=guild_id, guild=guild_dict, + guild_id=guild_id, + guild=guild_dict, generate_guild_icon=generate_guild_icon_url, unauth_enabled=guild_query_unauth_users_bool(guild_id), visitors_enabled=guild_accepts_visitors(guild_id), client_id=config['client-id'], + recaptcha_site_key=config["recaptcha-site-key"], css=customcss, cssvariables=parse_css_variable(customcss) ) diff --git a/webapp/titanembeds/static/css/embedstyle.css b/webapp/titanembeds/static/css/embedstyle.css index 1126213..4f83817 100644 --- a/webapp/titanembeds/static/css/embedstyle.css +++ b/webapp/titanembeds/static/css/embedstyle.css @@ -490,6 +490,11 @@ p.mentioned span.chatmessage { top: -5px; } +#google-recaptcha { + margin: 0 auto; + width: 302px; +} + /* CSS Variables */ :root { /*--: */ diff --git a/webapp/titanembeds/static/js/embed.js b/webapp/titanembeds/static/js/embed.js index 6f80436..d0aea34 100644 --- a/webapp/titanembeds/static/js/embed.js +++ b/webapp/titanembeds/static/js/embed.js @@ -11,6 +11,7 @@ /* global io */ /* global twemoji */ /* global jQuery */ +/* global grecaptcha */ (function () { const theme_options = ["DiscordDark", "BetterTitan"]; // All the avaliable theming names @@ -73,12 +74,12 @@ return funct.promise(); } - function create_unauthenticated_user(username) { + function create_unauthenticated_user(username, captchaResponse) { var funct = $.ajax({ method: "POST", dataType: "json", url: "/api/create_unauthenticated_user", - data: {"username": username, "guild_id": guild_id} + data: {"username": username, "guild_id": guild_id, "captcha_response": captchaResponse} }); return funct.promise(); } @@ -142,6 +143,14 @@ } ); $('#loginmodal').modal('open'); + $("#recaptchamodal").modal({ + dismissible: true, + opacity: .5, + inDuration: 400, + outDuration: 400, + startingTop: '40%', + endingTop: '30%', + }); $("#userembedmodal").modal({ dismissible: true, opacity: .5, @@ -948,7 +957,7 @@ lock_login_fields(); wait_for_discord_login(); }); - + $("#custom_username_field").keyup(function(event){ if (event.keyCode == 13) { if (!(new RegExp(/^[a-z\d\-_\s]+$/i).test($(this).val()))) { @@ -956,28 +965,36 @@ return; } if($(this).val().length >= 2 && $(this).val().length <= 32) { - lock_login_fields(); - var usr = create_unauthenticated_user($(this).val()); - usr.done(function(data) { - setVisitorMode(false); - initialize_embed(); - }); - usr.fail(function(data) { - if (data.status == 429) { - Materialize.toast('Sorry! You are allowed to log in as a guest three times in a span of 30 minutes.', 10000); - } else if (data.status == 403) { - Materialize.toast('Authentication error! You have been banned.', 10000); - } else if (data.status == 406) { - Materialize.toast('Illegal username provided! Only alphanumeric, spaces, dashes, and underscores allowed in usernames.', 10000); - } else if (data.status == 422) { - Materialize.toast("Attempting to add you into the server has failed. Either you are banned, reached 100 servers in Discord, or something else bad has happened.", 10000); - } - unlock_login_fields(); - setVisitorMode(true); - }); + $("#custom_username_field").blur(); + $('#recaptchamodal').modal('open'); } } }); + + $("#submit-unauthenticated-captcha-btn").click(function(){ + lock_login_fields(); + var usr = create_unauthenticated_user($("#custom_username_field").val(), grecaptcha.getResponse()); + usr.done(function(data) { + grecaptcha.reset(); + setVisitorMode(false); + initialize_embed(); + }); + usr.fail(function(data) { + if (data.status == 429) { + Materialize.toast('Sorry! You are allowed to log in as a guest three times in a span of 30 minutes.', 10000); + } else if (data.status == 403) { + Materialize.toast('Authentication error! You have been banned.', 10000); + } else if (data.status == 406) { + Materialize.toast('Illegal username provided! Only alphanumeric, spaces, dashes, and underscores allowed in usernames.', 10000); + } else if (data.status == 422) { + Materialize.toast("Attempting to add you into the server has failed. Either you are banned, reached 100 servers in Discord, or something else bad has happened.", 10000); + } else if (data.status == 412) { + Materialize.toast("reCAPTCHA reponse has failed. Try again?", 10000); + } + unlock_login_fields(); + setVisitorMode(true); + }); + }); $("#change_username_field").keyup(function(event){ if (event.keyCode == 13) { @@ -1283,3 +1300,8 @@ } } })(); + +function submit_unauthenticated_captcha() { // To be invoked when recaptcha is completed + $('#recaptchamodal').modal('close'); + $("#submit-unauthenticated-captcha-btn").click(); +} \ No newline at end of file diff --git a/webapp/titanembeds/templates/embed.html.j2 b/webapp/titanembeds/templates/embed.html.j2 index 4ecd5e4..0b7e672 100644 --- a/webapp/titanembeds/templates/embed.html.j2 +++ b/webapp/titanembeds/templates/embed.html.j2 @@ -18,6 +18,7 @@ {{ guild['name'] }} - Embed - Titan Embeds for Discord {% include 'google_analytics.html.j2' %} +