Skip to content

Commit

Permalink
Kv policy verification (#72)
Browse files Browse the repository at this point in the history
* verify the upstream user has policy associated with KV item for read or delete.

* Add test of kv_write; verify policy creation

* add kv_delete test

* Add advisory lock to prevent policies race
  • Loading branch information
suprjinx authored Nov 5, 2024
1 parent 093c4f7 commit 8da7da1
Show file tree
Hide file tree
Showing 5 changed files with 33 additions and 6 deletions.
3 changes: 3 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ gem "jbuilder"
gem "faraday"
gem "faraday-retry"

# Use with_advisory_lock for multiprocess mutex
gem "with_advisory_lock"

group :development, :test do
# See https://guides.rubyonrails.org/debugging_rails_applications.html#debugging-with-the-debug-gem
gem "debug", platforms: %i[ mri mswin ], require: "debug/prelude"
Expand Down
4 changes: 4 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,9 @@ GEM
websocket-driver (0.7.6)
websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.5)
with_advisory_lock (5.1.0)
activerecord (>= 6.1)
zeitwerk (>= 2.6)
zeitwerk (2.7.1)

PLATFORMS
Expand Down Expand Up @@ -304,6 +307,7 @@ DEPENDENCIES
sqlite3 (>= 1.4)
tzinfo-data
vault
with_advisory_lock

BUNDLED WITH
2.5.11
3 changes: 2 additions & 1 deletion app/lib/clients/vault/key_value.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ module KeyValue
extend Policy

def kv_read(identity, path)
verify_policy(identity, policy_path(path))
client.kv(kv_mount).read(path)
end

Expand All @@ -14,6 +15,7 @@ def kv_write(identity, path, data)
end

def kv_delete(identity, path)
verify_policy(identity, policy_path(path))
client.logical.delete("#{kv_mount}/data/#{path}")
end

Expand All @@ -33,7 +35,6 @@ def kv_engine_type
"kv-v2"
end


def create_kv_policy(path)
client.sys.put_policy(policy_path(path), kv_policy(path))
end
Expand Down
18 changes: 14 additions & 4 deletions app/lib/clients/vault/policy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,20 @@ def rotate_token
def assign_policy(identity, policy_name)
sub = identity.sub
email = identity.email
policies, metadata = get_entity_data(sub)
policies.append(policy_name).uniq!
put_entity(sub, policies, metadata)
put_entity_alias(sub, email, "oidc")
Domain.with_advisory_lock(sub) do
policies, metadata = get_entity_data(sub)
policies.append(policy_name).uniq!
put_entity(sub, policies, metadata)
put_entity_alias(sub, email, "oidc")
end
end

def verify_policy(identity, policy_name)
sub = identity.sub
policies, _ = get_entity_data(sub)
unless policies.any? { |p| p == policy_name }
raise AuthError.new("Policy has not been granted to the identity")
end
end

private
Expand Down
11 changes: 10 additions & 1 deletion test/lib/clients/vault_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -98,14 +98,23 @@ class VaultTest < ActiveSupport::TestCase
entity = @client.read_entity(@identity.sub)
assert_equal "kv_policy/#{path}", entity.data[:policies][0]

# check kv_read denied to other identity
alt_identity = Identity.new
alt_identity.sub = SecureRandom.hex(4)
err = assert_raises { @client.kv_read(alt_identity, path) }
assert_kind_of AuthError, err

# check kv_delete denied to other identity
err = assert_raises { @client.kv_delete(alt_identity, path) }
assert_kind_of AuthError, err

# check kv_delete
del_secret = @client.kv_delete(@identity, path)
assert del_secret
read_secret = @client.kv_read(@identity, path)
assert_nil read_secret
end


test "entity_alias methods" do
# confirm no entity yet
err = assert_raises RuntimeError do
Expand Down

0 comments on commit 8da7da1

Please sign in to comment.