Skip to content

Commit

Permalink
KV policy creation (#70)
Browse files Browse the repository at this point in the history
* Add better auth error propogation from interactor to controller; added
interactor unit test

* init

* reorg

* user_config

* removed data

* basic policy creation working

* removed extraneous policies

* changed intial email

* moved policy creation

* fixed test

* added test

* rubocop

* removed user_config file

* cleanup

* add identity to interactor calls

* move policy/entity methods to matching modules

* fix client unit test

* Simplify policy/entity usage

* remove config_user from Cert (using assign_policy now); fix test

* Add test of kv_write; verify policy creation

* add kv_delete test

* PR changes

---------

Co-authored-by: George Jahad <[email protected]>
  • Loading branch information
suprjinx and George Jahad authored Nov 4, 2024
1 parent d73f139 commit 093c4f7
Show file tree
Hide file tree
Showing 9 changed files with 86 additions and 39 deletions.
2 changes: 1 addition & 1 deletion app/interactors/delete_secret.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
class DeleteSecret < ApplicationInteractor
def call
Services::KeyValue.delete(context.request.path)
Services::KeyValue.delete(context.identity, context.request.path)
ensure
audit_log
end
Expand Down
2 changes: 1 addition & 1 deletion app/interactors/read_secret.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
class ReadSecret < ApplicationInteractor
def call
if secret = Services::KeyValue.read(context.request.path)
if secret = Services::KeyValue.read(context.identity, context.request.path)
context.secret = secret
else
context.fail!(message: "Failed to read secret: #{context.request.path}")
Expand Down
2 changes: 1 addition & 1 deletion app/interactors/write_secret.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
class WriteSecret < ApplicationInteractor
def call
if secret = Services::KeyValue.write(context.request.path, context.request.data)
if secret = Services::KeyValue.write(context.identity, context.request.path, context.request.data)
context.secret = secret
else
context.fail!(message: "Failed to store secret")
Expand Down
26 changes: 5 additions & 21 deletions app/lib/clients/vault/certificate.rb
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
module Clients
class Vault
module Certificate
extend Policy

GENERIC_CERT_POLICY_NAME = "astral-generic-cert-policy"

def issue_cert(identity, cert_issue_request)
opts = cert_issue_request.attributes
# Generate the TLS certificate using the intermediate CA
tls_cert = client.logical.write(cert_path, opts)
config_user(identity)
assign_policy(identity, GENERIC_CERT_POLICY_NAME)
OpenStruct.new tls_cert.data
end

Expand All @@ -19,17 +23,6 @@ def configure_pki
create_generic_cert_policy
end

def config_user(identity)
sub = identity.sub
email = identity.email
policies, metadata = get_entity_data(sub)
policies.append(Certificate::GENERIC_CERT_POLICY_NAME).to_set.to_a
put_entity(sub, policies, metadata)
put_entity_alias(sub, email, "oidc")
end

GENERIC_CERT_POLICY_NAME = "astral-generic-cert-policy"

private

def intermediate_ca_mount
Expand Down Expand Up @@ -131,15 +124,6 @@ def configure_ca
enable_templating: true)
end

def get_entity_data(sub)
entity = read_entity(sub)
if entity.nil?
[ [], nil ]
else
[ entity.data[:policies], entity.data[:metadata] ]
end
end

def create_generic_cert_policy
client.sys.put_policy(GENERIC_CERT_POLICY_NAME, generic_cert_policy)
end
Expand Down
9 changes: 9 additions & 0 deletions app/lib/clients/vault/entity.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,15 @@ def read_entity(name)
def delete_entity(name)
client.logical.delete("identity/entity/name/#{name}")
end

def get_entity_data(sub)
entity = read_entity(sub)
if entity.nil?
[ [], nil ]
else
[ entity.data[:policies], entity.data[:metadata] ]
end
end
end
end
end
27 changes: 24 additions & 3 deletions app/lib/clients/vault/key_value.rb
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
module Clients
class Vault
module KeyValue
def kv_read(path)
extend Policy

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

def kv_write(path, data)
def kv_write(identity, path, data)
create_kv_policy(path)
assign_policy(identity, policy_path(path))
client.logical.write("#{kv_mount}/data/#{path}", data: data)
end

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

Expand All @@ -28,6 +32,23 @@ def kv_mount
def kv_engine_type
"kv-v2"
end


def create_kv_policy(path)
client.sys.put_policy(policy_path(path), kv_policy(path))
end

def policy_path(path)
"kv_policy/#{path}"
end

def kv_policy(path)
policy = <<-EOH
path "#{path}" {
capabilities = ["create", "read", "update", "delete"]
}
EOH
end
end
end
end
11 changes: 11 additions & 0 deletions app/lib/clients/vault/policy.rb
Original file line number Diff line number Diff line change
@@ -1,12 +1,23 @@
module Clients
class Vault
module Policy
extend Entity

def rotate_token
create_astral_policy
token = create_astral_token
Clients::Vault.token = token
end

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")
end

private

def create_astral_policy
Expand Down
12 changes: 6 additions & 6 deletions app/lib/services/key_value.rb
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
module Services
class KeyValue
class << self
def read(path)
impl.kv_read(path)
def read(identity, path)
impl.kv_read(identity, path)
end

def write(path, data)
impl.kv_write(path, data)
def write(identity, path, data)
impl.kv_write(identity, path, data)
end

def delete(path)
impl.kv_delete(path)
def delete(identity, path)
impl.kv_delete(identity, path)
end

private
Expand Down
34 changes: 28 additions & 6 deletions test/lib/clients/vault_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,10 @@ class VaultTest < ActiveSupport::TestCase
# now has a new token
assert_not_equal vault_token, @client.token
# ensure we can write with the new token
assert_instance_of Vault::Secret, @client.kv_write("testing/secret", { password: "sicr3t" })
assert_instance_of Vault::Secret, @client.kv_write(@identity, "testing/secret", { password: "sicr3t" })
end

test "#entity" do
test "entity methods" do
entity = @client.read_entity(@entity_name)
assert_nil entity

Expand All @@ -84,7 +84,29 @@ class VaultTest < ActiveSupport::TestCase
assert_nil entity
end

test "#entity_alias" do
test "kv methods" do
# check kv_write
path = "test/path/#{SecureRandom.hex}"
secret = @client.kv_write(@identity, path, { data: "data" })
assert_kind_of Vault::Secret, secret

# check kv_read
read_secret = @client.kv_read(@identity, path)
assert_kind_of Vault::Secret, read_secret

# check policy is created
entity = @client.read_entity(@identity.sub)
assert_equal "kv_policy/#{path}", entity.data[:policies][0]

# 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
@client.read_entity_alias(@entity_name, @alias_name)
Expand Down Expand Up @@ -112,11 +134,11 @@ class VaultTest < ActiveSupport::TestCase
assert_match /no such alias/, err.message
end

test "#config_user creates valid entity" do
@client.config_user(@identity)
test ".assign_policy creates valid entity" do
@client.assign_policy(@identity, "test_path")
entity = @client.read_entity(@identity.sub)
assert entity.data[:policies].any? { |p|
p == @client::Certificate::GENERIC_CERT_POLICY_NAME }
p == "test_path" }
assert entity.data[:aliases].any? { |a|
a[:mount_type] == "oidc" && a[:name] == @identity.sub }
end
Expand Down

0 comments on commit 093c4f7

Please sign in to comment.