From b9c29bf537e3b5a64c8df7a29377e06596a7aee4 Mon Sep 17 00:00:00 2001 From: Omar Roth Date: Thu, 8 Nov 2018 00:12:14 -0600 Subject: [PATCH] Add option for user to delete their account --- src/invidious.cr | 76 ++++++++++++++++++++- src/invidious/helpers/helpers.cr | 48 +++++++++++++ src/invidious/views/clear_watch_history.ecr | 17 +++++ src/invidious/views/delete_account.ecr | 17 +++++ src/invidious/views/preferences.ecr | 8 ++- 5 files changed, 161 insertions(+), 5 deletions(-) create mode 100644 src/invidious/views/clear_watch_history.ecr create mode 100644 src/invidious/views/delete_account.ecr diff --git a/src/invidious.cr b/src/invidious.cr index 3c251d960..7cf605314 100644 --- a/src/invidious.cr +++ b/src/invidious.cr @@ -896,6 +896,7 @@ post "/login" do |env| end end +# TODO: Update this with using the same method for /clear_watch_history to prevent CSRF get "/signout" do |env| referer = get_referer(env) @@ -910,7 +911,7 @@ get "/signout" do |env| end env.request.cookies.add_response_headers(env.response.headers) - env.redirect URI.unescape(referer) + env.redirect referer end get "/preferences" do |env| @@ -1402,14 +1403,83 @@ get "/subscription_ajax" do |env| env.redirect referer end -get "/clear_watch_history" do |env| +get "/delete_account" do |env| user = env.get? "user" - referer = get_referer(env) if user user = user.as(User) + challenge, token = create_response(user.email, "delete_account", HMAC_KEY) + + templated "delete_account" + else + env.redirect referer + end +end + +post "/delete_account" do |env| + user = env.get? "user" + referer = get_referer(env) + + if user + user = user.as(User) + + challenge = env.params.body["challenge"]? + token = env.params.body["token"]? + + begin + validate_response(challenge, token, "delete_account", HMAC_KEY) + rescue ex + error_message = ex.message + next templated "error" + end + + view_name = "subscriptions_#{sha256(user.email)[0..7]}" + PG_DB.exec("DROP MATERIALIZED VIEW #{view_name}") + PG_DB.exec("DELETE FROM users * WHERE email = $1", user.email) + + env.request.cookies.each do |cookie| + cookie.expires = Time.new(1990, 1, 1) + end + env.request.cookies.add_response_headers(env.response.headers) + end + + env.redirect referer +end + +get "/clear_watch_history" do |env| + user = env.get? "user" + referer = get_referer(env) + + if user + user = user.as(User) + + challenge, token = create_response(user.email, "clear_watch_history", HMAC_KEY) + + templated "clear_watch_history" + else + env.redirect referer + end +end + +post "/clear_watch_history" do |env| + user = env.get? "user" + referer = get_referer(env) + + if user + user = user.as(User) + + challenge = env.params.body["challenge"]? + token = env.params.body["token"]? + + begin + validate_response(challenge, token, "clear_watch_history", HMAC_KEY) + rescue ex + error_message = ex.message + next templated "error" + end + PG_DB.exec("UPDATE users SET watched = '{}' WHERE email = $1", user.email) end diff --git a/src/invidious/helpers/helpers.cr b/src/invidious/helpers/helpers.cr index 92a2e1b15..65493790d 100644 --- a/src/invidious/helpers/helpers.cr +++ b/src/invidious/helpers/helpers.cr @@ -389,3 +389,51 @@ def extract_items(nodeset, ucid = nil) return items end + +def create_response(user_id, operation, key) + nonce = Random::Secure.hex(4) + expire = Time.now + 6.hours + + challenge = "#{expire.to_unix}-#{nonce}-#{user_id}-#{operation}" + token = OpenSSL::HMAC.digest(:sha256, key, challenge) + + challenge = Base64.urlsafe_encode(challenge) + token = Base64.urlsafe_encode(token) + + return challenge, token +end + +def validate_response(challenge, token, action, key) + if !challenge + raise "Hidden field \"challenge\" is a required field" + end + + if !token + raise "Hidden field \"token\" is a required field" + end + + challenge = Base64.decode_string(challenge) + if challenge.split("-").size == 4 + expire, nonce, user_id, operation = challenge.split("-") + + expire = expire.to_i? + expire ||= 0 + else + raise "Invalid challenge" + end + + challenge = OpenSSL::HMAC.digest(:sha256, HMAC_KEY, challenge) + challenge = Base64.urlsafe_encode(challenge) + + if challenge != token + raise "Invalid token" + end + + if operation != action + raise "Invalid token" + end + + if expire < Time.now.to_unix + raise "Token is expired, please try again" + end +end diff --git a/src/invidious/views/clear_watch_history.ecr b/src/invidious/views/clear_watch_history.ecr new file mode 100644 index 000000000..9a726a68d --- /dev/null +++ b/src/invidious/views/clear_watch_history.ecr @@ -0,0 +1,17 @@ +
+
+ Clear watch history? + +
+
+ +
+
+ No +
+
+ + + +
+
diff --git a/src/invidious/views/delete_account.ecr b/src/invidious/views/delete_account.ecr new file mode 100644 index 000000000..8f2b61d6c --- /dev/null +++ b/src/invidious/views/delete_account.ecr @@ -0,0 +1,17 @@ +
+
+ Delete account? + +
+
+ +
+
+ No +
+
+ + + +
+
diff --git a/src/invidious/views/preferences.ecr b/src/invidious/views/preferences.ecr index be15a54da..72b1d6093 100644 --- a/src/invidious/views/preferences.ecr +++ b/src/invidious/views/preferences.ecr @@ -150,17 +150,21 @@ function update_value(element) { Data preferences
- Clear watch history + Clear watch history
- Import/Export data + Import/Export data
Manage subscriptions
+
+ Delete account +
+