Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

config policy for users when they make a cert request #66

Merged
merged 13 commits into from
Nov 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion app/interactors/obtain_cert.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
class ObtainCert < ApplicationInteractor
def call
if cert = Services::Certificate.issue_cert(context.request)
if cert = Services::Certificate.issue_cert(context.identity, context.request)
context.cert = cert
else
context.fail!(message: "Failed to issue certificate")
Expand Down
41 changes: 40 additions & 1 deletion app/lib/clients/vault/certificate.rb
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
module Clients
class Vault
module Certificate
def issue_cert(cert_issue_request)
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)
OpenStruct.new tls_cert.data
end

Expand All @@ -15,8 +16,20 @@ def configure_pki
enable_ca
sign_cert
configure_ca
create_generic_cert_policy
end

def config_user(identity)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this be in private section?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left it public to be able to test it.

Initially, I was going to add my test to the unit test for the enclosing method, "issue_cert()" , but it seems like that method is only tested in an integration test here: test/integration/certificates_test.rb

I was uncomfortable adding my test to an integration test, but will if you think that is a better way to handle it. (or I could add a unit test for issue_cert.)

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"
suprjinx marked this conversation as resolved.
Show resolved Hide resolved

private

def intermediate_ca_mount
Expand Down Expand Up @@ -117,6 +130,32 @@ def configure_ca
ocsp_servers: "{{cluster_path}}/ocsp",
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

def generic_cert_policy
policy = <<-EOH

path "#{cert_path}" {
capabilities = ["create", "update"]
}

path "#{intermediate_ca_mount}/revoke-with-key" {
capabilities = ["update"]
}
EOH
end
end
end
end
5 changes: 3 additions & 2 deletions app/lib/clients/vault/entity.rb
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
module Clients
class Vault
module Entity
def put_entity(name, policies)
def put_entity(name, policies, metadata = {})
client.logical.write("identity/entity",
name: name,
policies: policies)
policies: policies,
metadata: metadata)
end

def read_entity(name)
Expand Down
4 changes: 2 additions & 2 deletions app/lib/services/certificate.rb
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
module Services
class Certificate
class << self
def issue_cert(cert_issue_request)
impl.issue_cert(cert_issue_request)
def issue_cert(identity, cert_issue_request)
impl.issue_cert(identity, cert_issue_request)
end

private
Expand Down
1 change: 1 addition & 0 deletions app/lib/utils/oidc_provider.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ def configure

def get_client_info
app = vault_client.logical.read(WEBAPP_NAME)
raise "oidc provider not configured." if app.nil?
@client_id = app.data[:client_id]
@client_secret = app.data[:client_secret]
[ @client_id, @client_secret ]
Expand Down
1 change: 1 addition & 0 deletions app/models/identity.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ class Identity
attribute :groups, array: :string, default: []

alias_attribute :sub, :subject
alias_attribute :email, :subject
alias_attribute :roles, :groups
end
3 changes: 2 additions & 1 deletion config/astral.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,13 @@ shared:

initial_user_name: test
initial_user_password: test
initial_user_email: test@example.com
initial_user_email: john.doe@example.com

test:
cert_ttl: <%= 24.hours.in_seconds %>

development:
cert_ttl: <%= 24.hours.in_seconds %>

production:
vault_create_root: false
Expand Down
10 changes: 6 additions & 4 deletions test/interactors/obtain_cert_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,23 @@ def setup

test ".call success" do
request = Requests::CertIssueRequest.new
identity = Identity.new
mock = Minitest::Mock.new
mock.expect :call, @cert, [ request ]
mock.expect :call, @cert, [ identity, request ]
Services::Certificate.stub :issue_cert, mock do
context = @interactor.call(request: request)
context = @interactor.call(identity: identity, request: request)
assert context.success?
assert_equal @cert, context.cert
end
end

test ".call failure" do
request = Requests::CertIssueRequest.new
identity = Identity.new
mock = Minitest::Mock.new
mock.expect :call, nil, [ request ]
mock.expect :call, nil, [ identity, request ]
Services::Certificate.stub :issue_cert, mock do
context = @interactor.call(request: request)
context = @interactor.call({ identity: identity, request: request })
assert context.failure?
assert_nil context.cert
end
Expand Down
11 changes: 11 additions & 0 deletions test/lib/clients/vault_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ class VaultTest < ActiveSupport::TestCase
@policies = SecureRandom.hex(4)
@entity_name = SecureRandom.hex(4)
@alias_name = SecureRandom.hex(4)
@identity = Identity.new
@identity.sub = SecureRandom.hex(4)
end

teardown do
Expand Down Expand Up @@ -110,6 +112,15 @@ class VaultTest < ActiveSupport::TestCase
assert_match /no such alias/, err.message
end

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

private

def vault_client
Expand Down