SQLite3 および Rack Basic 認証ではユーザー名を実行パラメータとして使用できません
概要
次のコード スニペットを考えてみましょう。
require "sqlite3"
db = SQLite3::Database.new "my.db"
p db.execute("select * from user where name = ?", ["my_user_name"])
これにより、ユーザー名が「my username」であるエントリがデータベースに出力されます。これは意図したとおりに機能します。
ここで、次のコード スニペット (config.ru) について考えてみましょう。
require "rack/auth/basic"
require "sqlite3"
use Rack::Auth::Basic, "my authentication" do |username, password|
p username
p db.execute("select * from user where name = ?", [username])
end
run do |env|
[200, {}, []]
end
このサーバーにリクエストを送信すると、次のようになります。
curl -X POST -v -ujomy:abc123 localhost:9292 -d ''
(ラックアップを使用して) サーバーを起動したターミナルに次の出力が表示されます。
"my_user_name"
[]
そのため、SQL クエリから結果が返されません。
この行を db.execute ステートメントの前に挿入すると、次のようになります。
username = "my_user_name"
これで実際に結果が返されました。
ユーザー名を username.to_s に置き換えても機能しません。
ということは、Rack が提供するユーザー名変数の使用は sqlite では機能しないのでしょうか? username.class が String であるにもかかわらず、これはどのようにして可能でしょうか? username.size も “my_user_name”.size と等しくなります。
解決策
これはエンコードの問題です。このブロックでは、ユーザー名とパスワードが「ASCII-8BIT」としてエンコードされます。
use Rack::Auth::Basic, "my authentication" do |username, password|
p username
p db.execute("select * from user where name = ?", [username])
end
これを解決するには、次のように String#force_encoding を使用してエンコーディングを変更します。
use Rack::Auth::Basic, "my authentication" do |username, password|
p username
p db.execute("select * from user where name = ?", [username.force_encoding('UTF-8')])
end