diff --git a/README.md b/README.md index 1f830de..19f907c 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,22 @@ get "/" do |env| end ``` +### accumulated entries + +When `basic_auth` is called in several times, the credentials are accumulated and shared by all pages. + +```crystal +basic_auth "user1", "123" +get "/members" do |env| + "restricted page" # both `user1` and `guest` can see this page. +end + +basic_auth "guest", "temp" +get "/trial" do |env| + "restricted page" # both `user1` and `guest` can see this page. +end +``` + ## Contributing 1. Fork it ( https://github.com/kemalcr/kemal-basic-auth/fork ) diff --git a/spec/credentials_spec.cr b/spec/credentials_spec.cr index b9f05cc..f58b1a6 100644 --- a/spec/credentials_spec.cr +++ b/spec/credentials_spec.cr @@ -6,12 +6,35 @@ describe "HTTPBasicAuth::Credentials" do "serdar" => "123", "dogruyol" => "abc", } - crendentials = HTTPBasicAuth::Credentials.new(entries) + credentials = HTTPBasicAuth::Credentials.new(entries) - crendentials.authorize?("serdar" , "123").should eq("serdar") - crendentials.authorize?("serdar" , "xxx").should eq(nil) - crendentials.authorize?("dogruyol", "abc").should eq("dogruyol") - crendentials.authorize?("dogruyol", "xxx").should eq(nil) - crendentials.authorize?("foo" , "bar").should eq(nil) + credentials.authorize?("serdar" , "123").should eq("serdar") + credentials.authorize?("serdar" , "xxx").should eq(nil) + credentials.authorize?("dogruyol", "abc").should eq("dogruyol") + credentials.authorize?("dogruyol", "xxx").should eq(nil) + credentials.authorize?("foo" , "bar").should eq(nil) + end + + describe "#update" do + credentials = HTTPBasicAuth::Credentials.new + + it "(String, String) adds a new entry" do + credentials.authorize?("serdar", "123").should eq(nil) + credentials.update("serdar", "123") + credentials.authorize?("serdar", "123").should eq("serdar") + credentials.authorize?("serdar", "xxx").should eq(nil) + end + + it "(Hash) adds new entries" do + credentials.update({"a" => "1", "b" => "2"}) + credentials.authorize?("a", "1").should eq("a") + credentials.authorize?("a", "x").should eq(nil) + credentials.authorize?("b", "2").should eq("b") + credentials.authorize?("c", "3").should eq(nil) + end + + it "preserves accumulated entries" do + credentials.authorize?("serdar", "123").should eq("serdar") + end end end diff --git a/spec/kemal-basic-auth_spec.cr b/spec/kemal-basic-auth_spec.cr index 95d1ab3..882e282 100644 --- a/spec/kemal-basic-auth_spec.cr +++ b/spec/kemal-basic-auth_spec.cr @@ -28,8 +28,23 @@ describe "HTTPBasicAuth" do context.kemal_authorized_username?.should eq(nil) end - it "adds HTTPBasicAuthHandler" do + it "adds HTTPBasicAuthHandler at most once" do basic_auth "serdar", "123" Kemal.config.handlers.size.should eq 6 + + basic_auth "dogruyol", "abc" + Kemal.config.handlers.size.should eq 6 + end + + describe ".runtime" do + it "returns singleton instance" do + HTTPBasicAuth.runtime.should be_a(HTTPBasicAuth) + end + + it "is affected by `basic_auth`" do + HTTPBasicAuth.runtime.authorize?("a", "1").should eq(nil) + basic_auth "a", "1" + HTTPBasicAuth.runtime.authorize?("a", "1").should eq("a") + end end end diff --git a/src/kemal-basic-auth.cr b/src/kemal-basic-auth.cr index 785ec51..d2cfe0f 100644 --- a/src/kemal-basic-auth.cr +++ b/src/kemal-basic-auth.cr @@ -20,7 +20,17 @@ class HTTPBasicAuth AUTH_MESSAGE = "Could not verify your access level for that URL.\nYou have to login with proper credentials" HEADER_LOGIN_REQUIRED = "Basic realm=\"Login Required\"" - def initialize(@credentials : Credentials) + # a lazy singleton instance which is automatically added to handler in first access + @@runtime : self? + def self.runtime + @@runtime ||= new.tap{|handler| add_handler handler} + @@runtime.not_nil! + end + + getter credentials + delegate update, to: credentials + + def initialize(@credentials : Credentials = Credentials.new) end # backward compatibility @@ -51,15 +61,19 @@ class HTTPBasicAuth def authorize?(value) : String? username, password = Base64.decode_string(value[BASIC.size + 1..-1]).split(":") + authorize?(username, password) + end + + def authorize?(username : String, password : String) : String? @credentials.authorize?(username, password) end end # Helper to easily add HTTP Basic Auth support. def basic_auth(username, password) - add_handler HTTPBasicAuth.new(username, password) + HTTPBasicAuth.runtime.update(username, password) end def basic_auth(crendentials : Hash(String, String)) - add_handler HTTPBasicAuth.new(crendentials) + HTTPBasicAuth.runtime.update(crendentials) end diff --git a/src/kemal-basic-auth/credentials.cr b/src/kemal-basic-auth/credentials.cr index 405ae1e..7996191 100644 --- a/src/kemal-basic-auth/credentials.cr +++ b/src/kemal-basic-auth/credentials.cr @@ -1,6 +1,8 @@ -class HTTPBasicAuth +class HTTPBasicAuth class Credentials - def initialize(@entries : Hash(String, String) = Hash(String, String).new) + alias Entries = Hash(String, String) + + def initialize(@entries : Entries = Entries.new) end def authorize?(username : String, password : String) : String? @@ -10,5 +12,13 @@ class HTTPBasicAuth nil end end + + def update(username : String, password : String) + @entries[username] = password + end + + def update(other : Entries) + @entries.merge!(other) + end end end