mirror of
https://github.com/hkalexling/Mango.git
synced 2026-04-24 00:03:12 -04:00
120 lines
3.8 KiB
Crystal
120 lines
3.8 KiB
Crystal
require "kemal"
|
|
require "../storage"
|
|
require "../util/*"
|
|
|
|
class AuthHandler < Kemal::Handler
|
|
# Some of the code is copied form kemalcr/kemal-basic-auth on GitHub
|
|
|
|
BASIC = "Basic"
|
|
BEARER = "Bearer"
|
|
AUTH = "Authorization"
|
|
AUTH_MESSAGE = "Could not verify your access level for that URL.\n" \
|
|
"You have to login with proper credentials"
|
|
HEADER_LOGIN_REQUIRED = "Basic realm=\"Login Required\""
|
|
|
|
def require_basic_auth(env)
|
|
env.response.status_code = 401
|
|
env.response.headers["WWW-Authenticate"] = HEADER_LOGIN_REQUIRED
|
|
env.response.print AUTH_MESSAGE
|
|
end
|
|
|
|
def require_auth(env)
|
|
if request_path_startswith env, ["/api"]
|
|
# Do not redirect API requests
|
|
env.response.status_code = 401
|
|
send_text env, "Unauthorized"
|
|
else
|
|
env.session.string "callback", env.request.path
|
|
redirect env, "/login"
|
|
end
|
|
end
|
|
|
|
def validate_token(env)
|
|
token = env.session.string? "token"
|
|
!token.nil? && Storage.default.verify_token token
|
|
end
|
|
|
|
def validate_token_admin(env)
|
|
token = env.session.string? "token"
|
|
!token.nil? && Storage.default.verify_admin token
|
|
end
|
|
|
|
def validate_auth_header(env)
|
|
if env.request.headers[AUTH]?
|
|
if value = env.request.headers[AUTH]
|
|
if value.starts_with? BASIC
|
|
token = verify_user value
|
|
return false if token.nil?
|
|
|
|
env.session.string "token", token
|
|
return true
|
|
end
|
|
if value.starts_with? BEARER
|
|
session_id = value.split(" ")[1]
|
|
token = Kemal::Session.get(session_id).try &.string? "token"
|
|
return !token.nil? && Storage.default.verify_token token
|
|
end
|
|
end
|
|
end
|
|
false
|
|
end
|
|
|
|
def verify_user(value)
|
|
username, password = Base64.decode_string(value[BASIC.size + 1..-1])
|
|
.split(":")
|
|
Storage.default.verify_user username, password
|
|
end
|
|
|
|
def call(env)
|
|
# OPTIONS requests do not require authentication
|
|
if env.request.method === "OPTIONS"
|
|
return call_next(env)
|
|
end
|
|
# Skip all authentication if requesting /login, /logout, /api/login,
|
|
# or a static file
|
|
if request_path_startswith(env, ["/login", "/logout", "/api/login"]) ||
|
|
requesting_static_file env
|
|
return call_next(env)
|
|
end
|
|
|
|
# Check user is logged in
|
|
if validate_token(env) || validate_auth_header(env)
|
|
# Skip if the request has a valid token (either from cookies or header)
|
|
elsif Config.current.disable_login
|
|
# Check default username if login is disabled
|
|
unless Storage.default.username_exists Config.current.default_username
|
|
Logger.warn "Default username #{Config.current.default_username} " \
|
|
"does not exist"
|
|
return require_auth env
|
|
end
|
|
elsif !Config.current.auth_proxy_header_name.empty?
|
|
# Check auth proxy if present
|
|
username = env.request.headers[Config.current.auth_proxy_header_name]?
|
|
unless username && Storage.default.username_exists username
|
|
Logger.warn "Header #{Config.current.auth_proxy_header_name} unset " \
|
|
"or is not a valid username"
|
|
return require_auth env
|
|
end
|
|
elsif request_path_startswith env, ["/opds"]
|
|
# Check auth header if requesting an opds page
|
|
unless validate_auth_header env
|
|
return require_basic_auth env
|
|
end
|
|
else
|
|
return require_auth env
|
|
end
|
|
|
|
# Check admin access when requesting an admin page
|
|
if request_path_startswith env, %w(/admin /api/admin /download)
|
|
unless is_admin? env
|
|
env.response.status_code = 403
|
|
return send_error_page "HTTP 403: You are not authorized to visit " \
|
|
"#{env.request.path}"
|
|
end
|
|
end
|
|
|
|
# Let the request go through if it passes the above checks
|
|
call_next env
|
|
end
|
|
end
|