Move DB queries related to session tokens in a separate module

This commit is contained in:
Samantaz Fox 2021-12-02 23:57:13 +01:00
parent c021b93b5c
commit 92eea3b18b
No known key found for this signature in database
GPG Key ID: F42821059186176E
8 changed files with 140 additions and 22 deletions

View File

@ -247,7 +247,7 @@ before_all do |env|
# Invidious users only have SID # Invidious users only have SID
if !env.request.cookies.has_key? "SSID" if !env.request.cookies.has_key? "SSID"
if email = PG_DB.query_one?("SELECT email FROM session_ids WHERE id = $1", sid, as: String) if email = Invidious::Database::SessionIDs.select_email(sid)
user = PG_DB.query_one("SELECT * FROM users WHERE email = $1", email, as: User) user = PG_DB.query_one("SELECT * FROM users WHERE email = $1", email, as: User)
csrf_token = generate_response(sid, { csrf_token = generate_response(sid, {
":authorize_token", ":authorize_token",
@ -633,6 +633,7 @@ get "/subscription_manager" do |env|
end end
user = user.as(User) user = user.as(User)
sid = sid.as(String)
if !user.password if !user.password
# Refresh account # Refresh account
@ -1008,7 +1009,7 @@ post "/delete_account" do |env|
view_name = "subscriptions_#{sha256(user.email)}" view_name = "subscriptions_#{sha256(user.email)}"
PG_DB.exec("DELETE FROM users * WHERE email = $1", user.email) PG_DB.exec("DELETE FROM users * WHERE email = $1", user.email)
PG_DB.exec("DELETE FROM session_ids * WHERE email = $1", user.email) Invidious::Database::SessionIDs.delete(email: user.email)
PG_DB.exec("DROP MATERIALIZED VIEW #{view_name}") PG_DB.exec("DROP MATERIALIZED VIEW #{view_name}")
env.request.cookies.each do |cookie| env.request.cookies.each do |cookie|
@ -1150,8 +1151,7 @@ get "/token_manager" do |env|
end end
user = user.as(User) user = user.as(User)
tokens = Invidious::Database::SessionIDs.select_all(user.email)
tokens = PG_DB.query_all("SELECT id, issued FROM session_ids WHERE email = $1 ORDER BY issued DESC", user.email, as: {session: String, issued: Time})
templated "token_manager" templated "token_manager"
end end
@ -1200,7 +1200,7 @@ post "/token_ajax" do |env|
case action case action
when .starts_with? "action_revoke_token" when .starts_with? "action_revoke_token"
PG_DB.exec("DELETE FROM session_ids * WHERE id = $1 AND email = $2", session, user.email) Invidious::Database::SessionIDs.delete(sid: session, email: user.email)
else else
next error_json(400, "Unsupported action #{action}") next error_json(400, "Unsupported action #{action}")
end end

View File

@ -0,0 +1,46 @@
require "./base.cr"
module Invidious::Database::Nonces
extend self
# -------------------
# Insert
# -------------------
def insert(nonce : String, expire : Time)
request = <<-SQL
INSERT INTO nonces
VALUES ($1, $2)
ON CONFLICT DO NOTHING
SQL
PG_DB.exec(request, nonce, expire)
end
# -------------------
# Update
# -------------------
def update_set_expired(nonce : String)
request = <<-SQL
UPDATE nonces
SET expire = $1
WHERE nonce = $2
SQL
PG_DB.exec(request, Time.utc(1990, 1, 1), nonce)
end
# -------------------
# Select
# -------------------
def select(nonce : String) : Tuple(String, Time)?
request = <<-SQL
SELECT * FROM nonces
WHERE nonce = $1
SQL
return PG_DB.query_one?(request, nonce, as: {String, Time})
end
end

View File

@ -0,0 +1,74 @@
require "./base.cr"
module Invidious::Database::SessionIDs
extend self
# -------------------
# Insert
# -------------------
def insert(sid : String, email : String, handle_conflicts : Bool = false)
request = <<-SQL
INSERT INTO session_ids
VALUES ($1, $2, $3)
SQL
request += " ON CONFLICT (id) DO NOTHING" if handle_conflicts
PG_DB.exec(request, sid, email, Time.utc)
end
# -------------------
# Delete
# -------------------
def delete(*, sid : String)
request = <<-SQL
DELETE FROM session_ids *
WHERE id = $1
SQL
PG_DB.exec(request, sid)
end
def delete(*, email : String)
request = <<-SQL
DELETE FROM session_ids *
WHERE email = $1
SQL
PG_DB.exec(request, email)
end
def delete(*, sid : String, email : String)
request = <<-SQL
DELETE FROM session_ids *
WHERE id = $1 AND email = $2
SQL
PG_DB.exec(request, sid, email)
end
# -------------------
# Select
# -------------------
def select_email(sid : String) : String?
request = <<-SQL
SELECT email FROM session_ids
WHERE id = $1
SQL
PG_DB.query_one?(request, sid, as: String)
end
def select_all(email : String) : Array({session: String, issued: Time})
request = <<-SQL
SELECT id, issued FROM session_ids
WHERE email = $1
ORDER BY issued DESC
SQL
PG_DB.query_all(request, email, as: {session: String, issued: Time})
end
end

View File

@ -99,7 +99,7 @@ class AuthHandler < Kemal::Handler
session = URI.decode_www_form(token["session"].as_s) session = URI.decode_www_form(token["session"].as_s)
scopes, expire, signature = validate_request(token, session, env.request, HMAC_KEY, PG_DB, nil) scopes, expire, signature = validate_request(token, session, env.request, HMAC_KEY, PG_DB, nil)
if email = PG_DB.query_one?("SELECT email FROM session_ids WHERE id = $1", session, as: String) if email = Invidious::Database::SessionIDs.select_email(session)
user = PG_DB.query_one("SELECT * FROM users WHERE email = $1", email, as: User) user = PG_DB.query_one("SELECT * FROM users WHERE email = $1", email, as: User)
end end
elsif sid = env.request.cookies["SID"]?.try &.value elsif sid = env.request.cookies["SID"]?.try &.value
@ -107,7 +107,7 @@ class AuthHandler < Kemal::Handler
raise "Cannot use token as SID" raise "Cannot use token as SID"
end end
if email = PG_DB.query_one?("SELECT email FROM session_ids WHERE id = $1", sid, as: String) if email = Invidious::Database::SessionIDs.select_email(sid)
user = PG_DB.query_one("SELECT * FROM users WHERE email = $1", email, as: User) user = PG_DB.query_one("SELECT * FROM users WHERE email = $1", email, as: User)
end end

View File

@ -2,7 +2,7 @@ require "crypto/subtle"
def generate_token(email, scopes, expire, key, db) def generate_token(email, scopes, expire, key, db)
session = "v1:#{Base64.urlsafe_encode(Random::Secure.random_bytes(32))}" session = "v1:#{Base64.urlsafe_encode(Random::Secure.random_bytes(32))}"
PG_DB.exec("INSERT INTO session_ids VALUES ($1, $2, $3)", session, email, Time.utc) Invidious::Database::SessionIDs.insert(session, email)
token = { token = {
"session" => session, "session" => session,
@ -30,7 +30,7 @@ def generate_response(session, scopes, key, db, expire = 6.hours, use_nonce = fa
if use_nonce if use_nonce
nonce = Random::Secure.hex(16) nonce = Random::Secure.hex(16)
db.exec("INSERT INTO nonces VALUES ($1, $2) ON CONFLICT DO NOTHING", nonce, expire) Invidious::Database::Nonces.insert(nonce, expire)
token["nonce"] = nonce token["nonce"] = nonce
end end
@ -92,9 +92,9 @@ def validate_request(token, session, request, key, db, locale = nil)
raise InfoException.new("Invalid signature") raise InfoException.new("Invalid signature")
end end
if token["nonce"]? && (nonce = db.query_one?("SELECT * FROM nonces WHERE nonce = $1", token["nonce"], as: {String, Time})) if token["nonce"]? && (nonce = Invidious::Database::Nonces.select(token["nonce"].as_s))
if nonce[1] > Time.utc if nonce[1] > Time.utc
db.exec("UPDATE nonces SET expire = $1 WHERE nonce = $2", Time.utc(1990, 1, 1), nonce[0]) Invidious::Database::Nonces.update_set_expired(nonce[0])
else else
raise InfoException.new("Erroneous token") raise InfoException.new("Erroneous token")
end end

View File

@ -312,7 +312,7 @@ module Invidious::Routes::API::V1::Authenticated
user = env.get("user").as(User) user = env.get("user").as(User)
scopes = env.get("scopes").as(Array(String)) scopes = env.get("scopes").as(Array(String))
tokens = PG_DB.query_all("SELECT id, issued FROM session_ids WHERE email = $1", user.email, as: {session: String, issued: Time}) tokens = Invidious::Database::SessionIDs.select_all(user.email)
JSON.build do |json| JSON.build do |json|
json.array do json.array do
@ -400,9 +400,9 @@ module Invidious::Routes::API::V1::Authenticated
# Allow tokens to revoke other tokens with correct scope # Allow tokens to revoke other tokens with correct scope
if session == env.get("session").as(String) if session == env.get("session").as(String)
PG_DB.exec("DELETE FROM session_ids * WHERE id = $1", session) Invidious::Database::SessionIDs.delete(sid: session)
elsif scopes_include_scope(scopes, "GET:tokens") elsif scopes_include_scope(scopes, "GET:tokens")
PG_DB.exec("DELETE FROM session_ids * WHERE id = $1", session) Invidious::Database::SessionIDs.delete(sid: session)
else else
return error_json(400, "Cannot revoke session #{session}") return error_json(400, "Cannot revoke session #{session}")
end end

View File

@ -336,7 +336,7 @@ module Invidious::Routes::Login
if Crypto::Bcrypt::Password.new(user.password.not_nil!).verify(password.byte_slice(0, 55)) if Crypto::Bcrypt::Password.new(user.password.not_nil!).verify(password.byte_slice(0, 55))
sid = Base64.urlsafe_encode(Random::Secure.random_bytes(32)) sid = Base64.urlsafe_encode(Random::Secure.random_bytes(32))
PG_DB.exec("INSERT INTO session_ids VALUES ($1, $2, $3)", sid, email, Time.utc) Invidious::Database::SessionIDs.insert(sid, email)
if Kemal.config.ssl || CONFIG.https_only if Kemal.config.ssl || CONFIG.https_only
secure = true secure = true
@ -455,7 +455,7 @@ module Invidious::Routes::Login
args = arg_array(user_array) args = arg_array(user_array)
PG_DB.exec("INSERT INTO users VALUES (#{args})", args: user_array) PG_DB.exec("INSERT INTO users VALUES (#{args})", args: user_array)
PG_DB.exec("INSERT INTO session_ids VALUES ($1, $2, $3)", sid, email, Time.utc) Invidious::Database::SessionIDs.insert(sid, email)
view_name = "subscriptions_#{sha256(user.email)}" view_name = "subscriptions_#{sha256(user.email)}"
PG_DB.exec("CREATE MATERIALIZED VIEW #{view_name} AS #{MATERIALIZED_VIEW_SQL.call(user.email)}") PG_DB.exec("CREATE MATERIALIZED VIEW #{view_name} AS #{MATERIALIZED_VIEW_SQL.call(user.email)}")
@ -511,7 +511,7 @@ module Invidious::Routes::Login
return error_template(400, ex) return error_template(400, ex)
end end
PG_DB.exec("DELETE FROM session_ids * WHERE id = $1", sid) Invidious::Database::SessionIDs.delete(sid: sid)
env.request.cookies.each do |cookie| env.request.cookies.each do |cookie|
cookie.expires = Time.utc(1990, 1, 1) cookie.expires = Time.utc(1990, 1, 1)

View File

@ -30,7 +30,7 @@ struct User
end end
def get_user(sid, headers, db, refresh = true) def get_user(sid, headers, db, refresh = true)
if email = db.query_one?("SELECT email FROM session_ids WHERE id = $1", sid, as: String) if email = Invidious::Database::SessionIDs.select_email(sid)
user = db.query_one("SELECT * FROM users WHERE email = $1", email, as: User) user = db.query_one("SELECT * FROM users WHERE email = $1", email, as: User)
if refresh && Time.utc - user.updated > 1.minute if refresh && Time.utc - user.updated > 1.minute
@ -42,8 +42,7 @@ def get_user(sid, headers, db, refresh = true)
db.exec("INSERT INTO users VALUES (#{args}) \ db.exec("INSERT INTO users VALUES (#{args}) \
ON CONFLICT (email) DO UPDATE SET updated = $1, subscriptions = $3", args: user_array) ON CONFLICT (email) DO UPDATE SET updated = $1, subscriptions = $3", args: user_array)
db.exec("INSERT INTO session_ids VALUES ($1,$2,$3) \ Invidious::Database::SessionIDs.insert(sid, user.email, handle_conflicts: true)
ON CONFLICT (id) DO NOTHING", sid, user.email, Time.utc)
begin begin
view_name = "subscriptions_#{sha256(user.email)}" view_name = "subscriptions_#{sha256(user.email)}"
@ -60,8 +59,7 @@ def get_user(sid, headers, db, refresh = true)
db.exec("INSERT INTO users VALUES (#{args}) \ db.exec("INSERT INTO users VALUES (#{args}) \
ON CONFLICT (email) DO UPDATE SET updated = $1, subscriptions = $3", args: user_array) ON CONFLICT (email) DO UPDATE SET updated = $1, subscriptions = $3", args: user_array)
db.exec("INSERT INTO session_ids VALUES ($1,$2,$3) \ Invidious::Database::SessionIDs.insert(sid, user.email, handle_conflicts: true)
ON CONFLICT (id) DO NOTHING", sid, user.email, Time.utc)
begin begin
view_name = "subscriptions_#{sha256(user.email)}" view_name = "subscriptions_#{sha256(user.email)}"