api: remove legacy password_hash login method.
Remove the ability to authenticate to the API with the `login` and
`password_hash` url parameters. This is a legacy authentication method
from Danbooru 1. How to actually generate the password_hash for this
method hasn't been fully documented for many years now. It required
taking the SHA1 hash of your password combined with an undocumented salt
value (i.e., password_hash = sha1("choujin-steiner--#{password}")).
This authentication method was also slow because it required checking
the password on every API call. Checking passwords is deliberately slow
because passwords are hashed with BCrypt. BCrypt takes about ~200ms per
request, so using this method effectively limited you to ~5 requests per
second in a single thread.
This commit is contained in:
@@ -42,7 +42,7 @@ class SessionLoader
|
|||||||
end
|
end
|
||||||
|
|
||||||
def has_api_authentication?
|
def has_api_authentication?
|
||||||
request.authorization.present? || params[:login].present? || params[:api_key].present? || params[:password_hash].present?
|
request.authorization.present? || params[:login].present? || params[:api_key].present?
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
@@ -57,8 +57,6 @@ class SessionLoader
|
|||||||
authenticate_basic_auth
|
authenticate_basic_auth
|
||||||
elsif params[:login].present? && params[:api_key].present?
|
elsif params[:login].present? && params[:api_key].present?
|
||||||
authenticate_api_key(params[:login], params[:api_key])
|
authenticate_api_key(params[:login], params[:api_key])
|
||||||
elsif params[:login].present? && params[:password_hash].present?
|
|
||||||
authenticate_legacy_api_key(params[:login], params[:password_hash])
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -74,14 +72,6 @@ class SessionLoader
|
|||||||
raise AuthenticationFailure unless Currentuser.user.present?
|
raise AuthenticationFailure unless Currentuser.user.present?
|
||||||
end
|
end
|
||||||
|
|
||||||
def authenticate_legacy_api_key(name, password_hash)
|
|
||||||
CurrentUser.user = User.authenticate_hash(name, password_hash)
|
|
||||||
|
|
||||||
if CurrentUser.user.nil?
|
|
||||||
raise AuthenticationFailure.new
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def load_param_user(signed_user_id)
|
def load_param_user(signed_user_id)
|
||||||
session[:user_id] = Danbooru::MessageVerifier.new(:login).verify(signed_user_id)
|
session[:user_id] = Danbooru::MessageVerifier.new(:login).verify(signed_user_id)
|
||||||
load_session_user
|
load_session_user
|
||||||
|
|||||||
@@ -181,17 +181,6 @@ class User < ApplicationRecord
|
|||||||
def hash_password(password)
|
def hash_password(password)
|
||||||
Digest::SHA1.hexdigest("choujin-steiner--#{password}--")
|
Digest::SHA1.hexdigest("choujin-steiner--#{password}--")
|
||||||
end
|
end
|
||||||
|
|
||||||
module ClassMethods
|
|
||||||
def authenticate_hash(name, hash)
|
|
||||||
user = find_by_name(name)
|
|
||||||
if user && user.bcrypt_password == hash
|
|
||||||
user
|
|
||||||
else
|
|
||||||
nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
module LevelMethods
|
module LevelMethods
|
||||||
|
|||||||
@@ -95,31 +95,6 @@ class ApplicationControllerTest < ActionDispatch::IntegrationTest
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context "using the password_hash parameter" do
|
|
||||||
should "succeed for password matches" do
|
|
||||||
get edit_user_path(@user), params: { login: @user.name, password_hash: User.sha1("password") }
|
|
||||||
assert_response :success
|
|
||||||
end
|
|
||||||
|
|
||||||
should "fail for password mismatches" do
|
|
||||||
get profile_path, as: :json, params: { login: @user.name }
|
|
||||||
assert_response 401
|
|
||||||
|
|
||||||
get profile_path, as: :json, params: { password_hash: User.sha1("password") }
|
|
||||||
assert_response 401
|
|
||||||
|
|
||||||
get profile_path, as: :json, params: { login: @user.name, password_hash: "bad" }
|
|
||||||
assert_response 401
|
|
||||||
end
|
|
||||||
|
|
||||||
should "succeed for non-GET requests without a CSRF token" do
|
|
||||||
assert_changes -> { @user.reload.enable_safe_mode }, from: false, to: true do
|
|
||||||
put user_path(@user), params: { login: @user.name, password_hash: User.sha1("password"), user: { enable_safe_mode: "true" } }, as: :json
|
|
||||||
assert_response :success
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "with cookie-based authentication" do
|
context "with cookie-based authentication" do
|
||||||
should "not allow non-GET requests without a CSRF token" do
|
should "not allow non-GET requests without a CSRF token" do
|
||||||
# get the csrf token from the login page so we can login
|
# get the csrf token from the login page so we can login
|
||||||
|
|||||||
Reference in New Issue
Block a user