Support hashed remoteStorage access tokens #14
No files matched your search
@@ -50,6 +50,16 @@ Running the app:
|
||||
|
||||
_TODO document options_
|
||||
|
||||
### Hashed access tokens
|
||||
|
||||
By default Liquor Cabinet looks up remoteStorage authorizations by the plaintext
|
||||
bearer token, using the Redis key `authorizations:<user>:<token>`.
|
||||
|
||||
Set `RS_HASHED_ACCESS_TOKENS=true` to hash the presented token and look up its
|
||||
SHA256 digest instead, using `authorizations:<user>:<sha256(token)>`. If no
|
||||
authorization is stored under the digest, the plaintext key is checked as a
|
||||
fallback.
|
||||
|
||||
## Contributing
|
||||
|
||||
We love pull requests. If you want to submit a patch:
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
development: &defaults
|
||||
maintenance: false
|
||||
hashed_access_tokens: <%= ENV["RS_HASHED_ACCESS_TOKENS"] == "true" %>
|
||||
redis:
|
||||
host: <%= ENV["REDIS_HOST"] || "localhost" %>
|
||||
port: <%= ENV["REDIS_PORT"] || 6379 %>
|
||||
|
||||
@@ -5,7 +5,7 @@ require "active_support/core_ext/time/conversions"
|
||||
require "active_support/core_ext/numeric/time"
|
||||
require "active_support/core_ext/hash"
|
||||
require "redis"
|
||||
require "digest/md5"
|
||||
require "digest"
|
||||
|
||||
module RemoteStorage
|
||||
module RestProvider
|
||||
@@ -26,7 +26,7 @@ module RemoteStorage
|
||||
|
||||
server.halt 401, "Unauthorized" if token.nil? || token.empty?
|
||||
|
||||
authorizations = redis.smembers("authorizations:#{user}:#{token}")
|
||||
authorizations = authorizations_for(user, token)
|
||||
permission = directory_permission(authorizations, directory)
|
||||
|
||||
server.halt 401, "Unauthorized" unless permission
|
||||
@@ -233,6 +233,15 @@ module RemoteStorage
|
||||
end
|
||||
end
|
||||
|
||||
def authorizations_for(user, token)
|
||||
if settings.respond_to?(:hashed_access_tokens) && settings.hashed_access_tokens
|
||||
digest = Digest::SHA256.hexdigest(token)
|
||||
permissions = redis.smembers("authorizations:#{user}:#{digest}")
|
||||
return permissions unless permissions.empty?
|
||||
end
|
||||
redis.smembers("authorizations:#{user}:#{token}")
|
||||
end
|
||||
|
||||
def directory_permission(authorizations, directory)
|
||||
authorizations = authorizations.map do |auth|
|
||||
auth.index(":") ? auth.split(":") : [auth, "rw"]
|
||||
|
||||
@@ -704,4 +704,50 @@ JFIFddDuckyA␍⎺␉␊␍
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "authorization with hashed access tokens" do
|
||||
before do
|
||||
redis.del "authorizations:phil:#{Digest::SHA256.hexdigest("amarillo")}"
|
||||
redis.del "authorizations:phil:amarillo"
|
||||
purge_redis
|
||||
header "Authorization", "Bearer amarillo"
|
||||
end
|
||||
|
||||
after do
|
||||
app.set :hashed_access_tokens, false
|
||||
end
|
||||
|
||||
context "when hashed access tokens are enabled" do
|
||||
before do
|
||||
app.set :hashed_access_tokens, true
|
||||
end
|
||||
|
||||
it "authorizes using the digest key" do
|
||||
redis.sadd "authorizations:phil:#{Digest::SHA256.hexdigest("amarillo")}", [":rw"]
|
||||
|
||||
put "/phil/food/aguacate", "si"
|
||||
|
||||
_(last_response.status).must_equal 201
|
||||
end
|
||||
|
||||
it "falls back to the plaintext key during the rollout" do
|
||||
redis.sadd "authorizations:phil:amarillo", [":rw"]
|
||||
|
||||
put "/phil/food/aguacate", "si"
|
||||
|
||||
_(last_response.status).must_equal 201
|
||||
end
|
||||
end
|
||||
|
||||
context "when hashed access tokens are disabled" do
|
||||
it "does not authorize a digest-only key" do
|
||||
redis.sadd "authorizations:phil:#{Digest::SHA256.hexdigest("amarillo")}", [":rw"]
|
||||
|
||||
put "/phil/food/aguacate", "si"
|
||||
|
||||
_(last_response.status).must_equal 401
|
||||
_(last_response.body).must_equal "Unauthorized"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in new issue
Block a user