Move DB queries related to session tokens in a separate module
This commit is contained in:
parent
c021b93b5c
commit
92eea3b18b
|
@ -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
|
||||||
|
|
46
src/invidious/database/nonces.cr
Normal file
46
src/invidious/database/nonces.cr
Normal 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
|
74
src/invidious/database/sessions.cr
Normal file
74
src/invidious/database/sessions.cr
Normal 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
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)}"
|
||||||
|
|
Loading…
Reference in a new issue