mirror of
https://github.com/iv-org/invidious.git
synced 2025-08-29 07:58:35 +00:00
Split logic for captchas from sign up
This commit is contained in:
parent
cd6c551c05
commit
3b77a2eac1
@ -124,90 +124,14 @@ module Invidious::Routes::Login
|
|||||||
password = password.byte_slice(0, 55)
|
password = password.byte_slice(0, 55)
|
||||||
|
|
||||||
if CONFIG.captcha_enabled
|
if CONFIG.captcha_enabled
|
||||||
captcha_type = env.params.body["captcha_type"]?
|
# Fetch information needed to initially display captcha
|
||||||
answer = env.params.body["answer"]?
|
#
|
||||||
change_type = env.params.body["change_type"]?
|
# We start out with an image captcha.
|
||||||
|
captcha, captcha_type, change_type = Invidious::User::Captcha.get_captcha_display_info(env, "image")
|
||||||
if !captcha_type || change_type
|
return templated "user/captcha"
|
||||||
if change_type
|
|
||||||
captcha_type = change_type
|
|
||||||
end
|
|
||||||
captcha_type ||= "image"
|
|
||||||
|
|
||||||
account_type = "invidious"
|
|
||||||
|
|
||||||
if captcha_type == "image"
|
|
||||||
captcha = Invidious::User::Captcha.generate_image(HMAC_KEY)
|
|
||||||
else
|
|
||||||
captcha = Invidious::User::Captcha.generate_text(HMAC_KEY)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return templated "user/register"
|
self.register_user(env, email, password)
|
||||||
end
|
|
||||||
|
|
||||||
tokens = env.params.body.select { |k, _| k.match(/^token\[\d+\]$/) }.map { |_, v| v }
|
|
||||||
|
|
||||||
answer ||= ""
|
|
||||||
captcha_type ||= "image"
|
|
||||||
case captcha_type
|
|
||||||
when "image"
|
|
||||||
answer = answer.lstrip('0')
|
|
||||||
answer = OpenSSL::HMAC.hexdigest(:sha256, HMAC_KEY, answer)
|
|
||||||
|
|
||||||
begin
|
|
||||||
validate_request(tokens[0], answer, env.request, HMAC_KEY, locale)
|
|
||||||
rescue ex
|
|
||||||
return error_template(400, ex)
|
|
||||||
end
|
|
||||||
else # "text"
|
|
||||||
answer = Digest::MD5.hexdigest(answer)
|
|
||||||
|
|
||||||
if tokens.empty?
|
|
||||||
return error_template(500, "Erroneous CAPTCHA")
|
|
||||||
end
|
|
||||||
|
|
||||||
found_valid_captcha = false
|
|
||||||
error_exception = Exception.new
|
|
||||||
tokens.each do |tok|
|
|
||||||
begin
|
|
||||||
validate_request(tok, answer, env.request, HMAC_KEY, locale)
|
|
||||||
found_valid_captcha = true
|
|
||||||
rescue ex
|
|
||||||
error_exception = ex
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if !found_valid_captcha
|
|
||||||
return error_template(500, error_exception)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
sid = Base64.urlsafe_encode(Random::Secure.random_bytes(32))
|
|
||||||
user, sid = create_user(sid, email, password)
|
|
||||||
|
|
||||||
if language_header = env.request.headers["Accept-Language"]?
|
|
||||||
if language = ANG.language_negotiator.best(language_header, LOCALES.keys)
|
|
||||||
user.preferences.locale = language.header
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
Invidious::Database::Users.insert(user)
|
|
||||||
Invidious::Database::SessionIDs.insert(sid, email)
|
|
||||||
|
|
||||||
view_name = "subscriptions_#{sha256(user.email)}"
|
|
||||||
PG_DB.exec("CREATE MATERIALIZED VIEW #{view_name} AS #{MATERIALIZED_VIEW_SQL.call(user.email)}")
|
|
||||||
|
|
||||||
env.response.cookies["SID"] = Invidious::User::Cookies.sid(CONFIG.domain, sid)
|
|
||||||
|
|
||||||
if env.request.cookies["PREFS"]?
|
|
||||||
user.preferences = env.get("preferences").as(Preferences)
|
|
||||||
Invidious::Database::Users.update_preferences(user)
|
|
||||||
|
|
||||||
cookie = env.request.cookies["PREFS"]
|
|
||||||
cookie.expires = Time.utc(1990, 1, 1)
|
|
||||||
env.response.cookies << cookie
|
|
||||||
end
|
|
||||||
|
|
||||||
env.redirect referer
|
env.redirect referer
|
||||||
end
|
end
|
||||||
@ -242,4 +166,103 @@ module Invidious::Routes::Login
|
|||||||
|
|
||||||
env.redirect referer
|
env.redirect referer
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.captcha(env)
|
||||||
|
if !CONFIG.captcha_enabled
|
||||||
|
error_template(403, "Administrator has disabled this endpoint")
|
||||||
|
end
|
||||||
|
|
||||||
|
begin
|
||||||
|
email = env.params.body["email"]?
|
||||||
|
password = env.params.body["password"]?
|
||||||
|
rescue ex : KeyError
|
||||||
|
return error_template(400, "Invalid request")
|
||||||
|
end
|
||||||
|
|
||||||
|
email = email.not_nil!
|
||||||
|
password = password.not_nil!
|
||||||
|
|
||||||
|
locale = env.get("preferences").as(Preferences).locale
|
||||||
|
referer = get_referer(env, "/feed/subscriptions")
|
||||||
|
|
||||||
|
captcha_type = env.params.body["captcha_type"]?
|
||||||
|
answer = env.params.body["answer"]?
|
||||||
|
change_type = env.params.body["change_type"]?
|
||||||
|
|
||||||
|
# User requests to change captcha
|
||||||
|
if !captcha_type || change_type
|
||||||
|
LOGGER.trace("User requests to change Captcha")
|
||||||
|
|
||||||
|
captcha, captcha_type, change_type = Invidious::User::Captcha.get_captcha_display_info(env, change_type)
|
||||||
|
return templated "user/captcha"
|
||||||
|
end
|
||||||
|
|
||||||
|
LOGGER.trace("Validating new user with Captcha")
|
||||||
|
|
||||||
|
tokens = env.params.body.select { |k, _| k.match(/^token\[\d+\]$/) }.map { |_, v| v }
|
||||||
|
|
||||||
|
answer ||= ""
|
||||||
|
captcha_type ||= "image"
|
||||||
|
case captcha_type
|
||||||
|
when "image"
|
||||||
|
answer = answer.lstrip('0')
|
||||||
|
answer = OpenSSL::HMAC.hexdigest(:sha256, HMAC_KEY, answer)
|
||||||
|
|
||||||
|
begin
|
||||||
|
validate_request(tokens[0], answer, env.request, HMAC_KEY, locale)
|
||||||
|
rescue ex
|
||||||
|
return error_template(400, "Erroneous CAPTCHA")
|
||||||
|
end
|
||||||
|
else # "text"
|
||||||
|
answer = Digest::MD5.hexdigest(answer)
|
||||||
|
|
||||||
|
if tokens.empty?
|
||||||
|
return error_template(400, "Erroneous CAPTCHA")
|
||||||
|
end
|
||||||
|
|
||||||
|
found_valid_captcha = false
|
||||||
|
tokens.each do |tok|
|
||||||
|
begin
|
||||||
|
validate_request(tok, answer, env.request, HMAC_KEY, locale)
|
||||||
|
found_valid_captcha = true
|
||||||
|
rescue ex
|
||||||
|
return error_template(400, "Erroneous CAPTCHA")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
self.register_user(env, email, password)
|
||||||
|
|
||||||
|
env.redirect referer
|
||||||
|
end
|
||||||
|
|
||||||
|
private def self.register_user(env, email, password)
|
||||||
|
sid = Base64.urlsafe_encode(Random::Secure.random_bytes(32))
|
||||||
|
user, sid = create_user(sid, email, password)
|
||||||
|
|
||||||
|
if language_header = env.request.headers["Accept-Language"]?
|
||||||
|
if language = ANG.language_negotiator.best(language_header, LOCALES.keys)
|
||||||
|
user.preferences.locale = language.header
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
Invidious::Database::Users.insert(user)
|
||||||
|
Invidious::Database::SessionIDs.insert(sid, email)
|
||||||
|
|
||||||
|
view_name = "subscriptions_#{sha256(user.email)}"
|
||||||
|
PG_DB.exec("CREATE MATERIALIZED VIEW #{view_name} AS #{MATERIALIZED_VIEW_SQL.call(user.email)}")
|
||||||
|
|
||||||
|
env.response.cookies["SID"] = Invidious::User::Cookies.sid(CONFIG.domain, sid)
|
||||||
|
|
||||||
|
if env.request.cookies["PREFS"]?
|
||||||
|
user.preferences = env.get("preferences").as(Preferences)
|
||||||
|
Invidious::Database::Users.update_preferences(user)
|
||||||
|
|
||||||
|
cookie = env.request.cookies["PREFS"]
|
||||||
|
cookie.expires = Time.utc(1990, 1, 1)
|
||||||
|
env.response.cookies << cookie
|
||||||
|
end
|
||||||
|
|
||||||
|
LOGGER.debug("A new user has been registered")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
@ -60,6 +60,8 @@ module Invidious::Routing
|
|||||||
get "/signup", Routes::Login, :signup_page
|
get "/signup", Routes::Login, :signup_page
|
||||||
post "/signup", Routes::Login, :signup
|
post "/signup", Routes::Login, :signup
|
||||||
|
|
||||||
|
post "/captcha", Routes::Login, :captcha
|
||||||
|
|
||||||
post "/signout", Routes::Login, :signout
|
post "/signout", Routes::Login, :signout
|
||||||
|
|
||||||
# User preferences
|
# User preferences
|
||||||
|
@ -74,5 +74,22 @@ struct Invidious::User
|
|||||||
tokens: tokens,
|
tokens: tokens,
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def get_captcha_display_info(env, change_type)
|
||||||
|
if change_type
|
||||||
|
captcha_type = change_type
|
||||||
|
end
|
||||||
|
captcha_type ||= "image"
|
||||||
|
|
||||||
|
account_type = "invidious"
|
||||||
|
|
||||||
|
if captcha_type == "image"
|
||||||
|
captcha = Invidious::User::Captcha.generate_image(HMAC_KEY)
|
||||||
|
else
|
||||||
|
captcha = Invidious::User::Captcha.generate_text(HMAC_KEY)
|
||||||
|
end
|
||||||
|
|
||||||
|
return captcha, captcha_type, change_type
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
67
src/invidious/views/user/captcha.ecr
Normal file
67
src/invidious/views/user/captcha.ecr
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
<% content_for "header" do %>
|
||||||
|
<title><%= translate(locale, "sign_up_page_title") %> - Invidious</title>
|
||||||
|
<link rel="stylesheet" href="/css/signin-signout.css?v=<%= ASSET_COMMIT %>">
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<div class="pure-g">
|
||||||
|
<div class="pure-u-1 pure-u-lg-1-5"></div>
|
||||||
|
<div class="pure-u-1 pure-u-lg-3-5">
|
||||||
|
<div class="h-box">
|
||||||
|
<form class="pure-form pure-form-stacked" action="/captcha?referer=<%= URI.encode_www_form(referer) %>&type=invidious" method="post">
|
||||||
|
<fieldset>
|
||||||
|
|
||||||
|
<input name="email" type="hidden" value="<%= HTML.escape(email) %>">
|
||||||
|
<input name="password" type="hidden" value="<%= HTML.escape(password) %>">
|
||||||
|
|
||||||
|
<% if captcha %>
|
||||||
|
<% case captcha_type when %>
|
||||||
|
<% when "image" %>
|
||||||
|
<% captcha = captcha.not_nil! %>
|
||||||
|
<img style="width:50%" src='<%= captcha[:question] %>'/>
|
||||||
|
<% captcha[:tokens].each_with_index do |token, i| %>
|
||||||
|
<input type="hidden" name="token[<%= i %>]" value="<%= HTML.escape(token) %>">
|
||||||
|
<% end %>
|
||||||
|
<input type="hidden" name="captcha_type" value="image">
|
||||||
|
<label for="answer"><%= translate(locale, "Time (h:mm:ss):") %></label>
|
||||||
|
<input type="text" name="answer" type="text" placeholder="h:mm:ss">
|
||||||
|
<% else # "text" %>
|
||||||
|
<% captcha = captcha.not_nil! %>
|
||||||
|
<% captcha[:tokens].each_with_index do |token, i| %>
|
||||||
|
<input type="hidden" name="token[<%= i %>]" value="<%= HTML.escape(token) %>">
|
||||||
|
<% end %>
|
||||||
|
<input type="hidden" name="captcha_type" value="text">
|
||||||
|
<label for="answer"><%= captcha[:question] %></label>
|
||||||
|
<input type="text" name="answer" type="text" placeholder="<%= translate(locale, "Answer") %>">
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<button type="submit" name="action" value="signup" class="pure-button pure-button-primary">
|
||||||
|
<%= translate(locale, "Sign Up") %>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<% case captcha_type when %>
|
||||||
|
<% when "image" %>
|
||||||
|
<label>
|
||||||
|
<button type="submit" name="change_type" class="pure-button pure-button-primary" value="text">
|
||||||
|
<%= translate(locale, "Text CAPTCHA") %>
|
||||||
|
</button>
|
||||||
|
</label>
|
||||||
|
<% else # "text" %>
|
||||||
|
<label>
|
||||||
|
<button type="submit" name="change_type" class="pure-button pure-button-primary" value="image">
|
||||||
|
<%= translate(locale, "Image CAPTCHA") %>
|
||||||
|
</button>
|
||||||
|
</label>
|
||||||
|
<% end %>
|
||||||
|
<% else %>
|
||||||
|
<div class="account-page-action-bar">
|
||||||
|
<button type="submit" name="action" value="signup" class="pure-button pure-button-primary" id="signup-button">
|
||||||
|
<%= translate(locale, "Sign Up") %>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
</fieldset>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="pure-u-1 pure-u-lg-1-5"></div>
|
||||||
|
</div>
|
@ -23,52 +23,11 @@
|
|||||||
<input required class="pure-input-1" name="password" type="password" placeholder="<%= translate(locale, "Password") %>">
|
<input required class="pure-input-1" name="password" type="password" placeholder="<%= translate(locale, "Password") %>">
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<% if captcha %>
|
|
||||||
<% case captcha_type when %>
|
|
||||||
<% when "image" %>
|
|
||||||
<% captcha = captcha.not_nil! %>
|
|
||||||
<img style="width:50%" src='<%= captcha[:question] %>'/>
|
|
||||||
<% captcha[:tokens].each_with_index do |token, i| %>
|
|
||||||
<input type="hidden" name="token[<%= i %>]" value="<%= HTML.escape(token) %>">
|
|
||||||
<% end %>
|
|
||||||
<input type="hidden" name="captcha_type" value="image">
|
|
||||||
<label for="answer"><%= translate(locale, "Time (h:mm:ss):") %></label>
|
|
||||||
<input type="text" name="answer" type="text" placeholder="h:mm:ss">
|
|
||||||
<% else # "text" %>
|
|
||||||
<% captcha = captcha.not_nil! %>
|
|
||||||
<% captcha[:tokens].each_with_index do |token, i| %>
|
|
||||||
<input type="hidden" name="token[<%= i %>]" value="<%= HTML.escape(token) %>">
|
|
||||||
<% end %>
|
|
||||||
<input type="hidden" name="captcha_type" value="text">
|
|
||||||
<label for="answer"><%= captcha[:question] %></label>
|
|
||||||
<input type="text" name="answer" type="text" placeholder="<%= translate(locale, "Answer") %>">
|
|
||||||
<% end %>
|
|
||||||
|
|
||||||
<button type="submit" name="action" value="signup" class="pure-button pure-button-primary">
|
|
||||||
<%= translate(locale, "Sign Up") %>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<% case captcha_type when %>
|
|
||||||
<% when "image" %>
|
|
||||||
<label>
|
|
||||||
<button type="submit" name="change_type" class="pure-button pure-button-primary" value="text">
|
|
||||||
<%= translate(locale, "Text CAPTCHA") %>
|
|
||||||
</button>
|
|
||||||
</label>
|
|
||||||
<% else # "text" %>
|
|
||||||
<label>
|
|
||||||
<button type="submit" name="change_type" class="pure-button pure-button-primary" value="image">
|
|
||||||
<%= translate(locale, "Image CAPTCHA") %>
|
|
||||||
</button>
|
|
||||||
</label>
|
|
||||||
<% end %>
|
|
||||||
<% else %>
|
|
||||||
<div class="account-page-action-bar">
|
<div class="account-page-action-bar">
|
||||||
<button type="submit" name="action" value="signup" class="pure-button pure-button-primary" id="signup-button">
|
<button type="submit" name="action" value="signup" class="pure-button pure-button-primary" id="signup-button">
|
||||||
<%= translate(locale, "Sign Up") %>
|
<%= translate(locale, "Sign Up") %>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
Reference in New Issue
Block a user