vaultkit 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +906 -729
- data/lib/vkit/cli/base_cli.rb +1 -1
- data/lib/vkit/cli/commands/base_command.rb +2 -0
- data/lib/vkit/cli/commands/login_command.rb +7 -7
- data/lib/vkit/cli/commands/logout_command.rb +15 -0
- data/lib/vkit/cli/commands/policy_bundle_command.rb +44 -21
- data/lib/vkit/cli/commands/policy_deploy_command.rb +20 -1
- data/lib/vkit/cli/commands/whoami_command.rb +34 -4
- data/lib/vkit/core/auth_client.rb +16 -4
- data/lib/vkit/core/credential_store.rb +16 -0
- metadata +3 -3
data/lib/vkit/cli/base_cli.rb
CHANGED
|
@@ -157,7 +157,7 @@ module Vkit
|
|
|
157
157
|
|
|
158
158
|
desc "deploy", "Deploy a policy bundle to VaultKit"
|
|
159
159
|
option :bundle, type: :string, default: "dist/policy_bundle.json"
|
|
160
|
-
option :org, type: :string
|
|
160
|
+
option :org, type: :string
|
|
161
161
|
option :activate, type: :boolean, default: true
|
|
162
162
|
|
|
163
163
|
def deploy
|
|
@@ -31,7 +31,7 @@ module Vkit
|
|
|
31
31
|
result =
|
|
32
32
|
case auth
|
|
33
33
|
when "oidc"
|
|
34
|
-
oidc_flow(client
|
|
34
|
+
oidc_flow(client)
|
|
35
35
|
when "password"
|
|
36
36
|
password_flow(client)
|
|
37
37
|
when "token"
|
|
@@ -55,20 +55,20 @@ module Vkit
|
|
|
55
55
|
|
|
56
56
|
private
|
|
57
57
|
|
|
58
|
-
def oidc_flow(client
|
|
58
|
+
def oidc_flow(client)
|
|
59
59
|
start = client.start_cli_login
|
|
60
60
|
poll_token = start["poll_token"]
|
|
61
|
-
|
|
61
|
+
login_url = start["login_url"]
|
|
62
|
+
|
|
62
63
|
open_browser(login_url)
|
|
63
64
|
puts "⏳ Waiting for authentication to complete..."
|
|
64
|
-
|
|
65
|
+
|
|
65
66
|
loop do
|
|
66
67
|
res = client.poll_cli_login(poll_token)
|
|
67
|
-
|
|
68
|
+
|
|
68
69
|
case res.code.to_i
|
|
69
70
|
when 204
|
|
70
71
|
sleep 2
|
|
71
|
-
next
|
|
72
72
|
when 200
|
|
73
73
|
body = JSON.parse(res.body)
|
|
74
74
|
return {
|
|
@@ -83,7 +83,7 @@ module Vkit
|
|
|
83
83
|
raise "Unexpected response: #{res.code}"
|
|
84
84
|
end
|
|
85
85
|
end
|
|
86
|
-
end
|
|
86
|
+
end
|
|
87
87
|
|
|
88
88
|
def password_flow(client)
|
|
89
89
|
email = @email || prompt("Email")
|
|
@@ -3,6 +3,21 @@ module Vkit
|
|
|
3
3
|
module Commands
|
|
4
4
|
class LogoutCommand < BaseCommand
|
|
5
5
|
def call
|
|
6
|
+
token = credential_store.token
|
|
7
|
+
|
|
8
|
+
if token
|
|
9
|
+
begin
|
|
10
|
+
client = Vkit::Core::AuthClient.new(
|
|
11
|
+
base_url: credential_store.endpoint
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
client.logout(token)
|
|
15
|
+
rescue => e
|
|
16
|
+
warn "⚠️ Server logout failed: #{e.message}"
|
|
17
|
+
warn "⚠️ Continuing with local logout"
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
6
21
|
credential_store.clear_token!
|
|
7
22
|
puts "🧹 Logged out"
|
|
8
23
|
end
|
|
@@ -1,10 +1,13 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require "json"
|
|
4
|
+
require "fileutils"
|
|
2
5
|
require_relative "../../policy/bundle_compiler"
|
|
3
6
|
|
|
4
7
|
module Vkit
|
|
5
8
|
module CLI
|
|
6
9
|
module Commands
|
|
7
|
-
class PolicyBundleCommand
|
|
10
|
+
class PolicyBundleCommand < BaseCommand
|
|
8
11
|
def call(policies_dir:, registry_dir:, out:, org:, version:)
|
|
9
12
|
policies_dir = File.expand_path(policies_dir)
|
|
10
13
|
registry_dir = File.expand_path(registry_dir)
|
|
@@ -15,26 +18,46 @@ module Vkit
|
|
|
15
18
|
|
|
16
19
|
version ||= git_sha
|
|
17
20
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
21
|
+
with_auth do
|
|
22
|
+
derived_org = credential_store.user["organization_slug"]
|
|
23
|
+
|
|
24
|
+
raise "Unable to determine organization from credentials. Please login." \
|
|
25
|
+
if derived_org.nil? || derived_org.empty?
|
|
26
|
+
|
|
27
|
+
if org && org != derived_org
|
|
28
|
+
raise <<~MSG
|
|
29
|
+
Organization mismatch detected.
|
|
30
|
+
|
|
31
|
+
Authenticated organization: #{derived_org}
|
|
32
|
+
Provided via --org: #{org}
|
|
33
|
+
|
|
34
|
+
Refusing to continue to prevent cross-organization policy bundles.
|
|
35
|
+
MSG
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
org_slug = org || derived_org
|
|
39
|
+
|
|
40
|
+
bundle = Vkit::Policy::BundleCompiler.compile!(
|
|
41
|
+
org_slug: org_slug,
|
|
42
|
+
bundle_version: version,
|
|
43
|
+
policies_dir: policies_dir,
|
|
44
|
+
registry_dir: registry_dir,
|
|
45
|
+
source: {
|
|
46
|
+
repo: git_repo,
|
|
47
|
+
ref: git_ref,
|
|
48
|
+
commit_sha: version
|
|
49
|
+
}
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
FileUtils.mkdir_p(File.dirname(out))
|
|
53
|
+
File.write(out, JSON.pretty_generate(bundle))
|
|
54
|
+
|
|
55
|
+
puts "✅ Policy bundle created"
|
|
56
|
+
puts " Org: #{bundle.dig("bundle", "org_slug")}"
|
|
57
|
+
puts " Version: #{bundle.dig("bundle", "bundle_version")}"
|
|
58
|
+
puts " Checksum: #{bundle.dig("bundle", "checksum")}"
|
|
59
|
+
puts " Output: #{out}"
|
|
60
|
+
end
|
|
38
61
|
end
|
|
39
62
|
|
|
40
63
|
private
|
|
@@ -11,10 +11,28 @@ module Vkit
|
|
|
11
11
|
bundle_path = File.expand_path(bundle_path)
|
|
12
12
|
raise "Bundle not found: #{bundle_path}" unless File.exist?(bundle_path)
|
|
13
13
|
|
|
14
|
+
derived_org = credential_store.user["organization_slug"]
|
|
15
|
+
|
|
16
|
+
raise "Unable to determine organization from credentials. Please login." \
|
|
17
|
+
if derived_org.nil? || derived_org.empty?
|
|
18
|
+
|
|
19
|
+
if org && org != derived_org
|
|
20
|
+
raise <<~MSG
|
|
21
|
+
Organization mismatch detected.
|
|
22
|
+
|
|
23
|
+
Authenticated organization: #{derived_org}
|
|
24
|
+
Provided via --org: #{org}
|
|
25
|
+
|
|
26
|
+
Refusing to deploy policy bundle to a different organization.
|
|
27
|
+
MSG
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
org_slug = org || derived_org
|
|
31
|
+
|
|
14
32
|
bundle = JSON.parse(File.read(bundle_path))
|
|
15
33
|
|
|
16
34
|
response = authenticated_client.post(
|
|
17
|
-
"/api/v1/orgs/#{
|
|
35
|
+
"/api/v1/orgs/#{org_slug}/policy_bundles",
|
|
18
36
|
body: {
|
|
19
37
|
bundle: bundle,
|
|
20
38
|
activate: activate
|
|
@@ -22,6 +40,7 @@ module Vkit
|
|
|
22
40
|
)
|
|
23
41
|
|
|
24
42
|
puts "🚀 Policy bundle deployed"
|
|
43
|
+
puts " Org: #{org_slug}"
|
|
25
44
|
puts " Version: #{response['bundle_version']}"
|
|
26
45
|
puts " State: #{response['state']}"
|
|
27
46
|
end
|
|
@@ -3,10 +3,40 @@ module Vkit
|
|
|
3
3
|
module Commands
|
|
4
4
|
class WhoamiCommand < BaseCommand
|
|
5
5
|
def call
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
user = fetch_user_from_server_or_fallback
|
|
7
|
+
|
|
8
|
+
puts "👤 #{user['email']} " \
|
|
9
|
+
"(role: #{user['role']}, org: #{user['organization_slug']})"
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
private
|
|
13
|
+
|
|
14
|
+
def fetch_user_from_server_or_fallback
|
|
15
|
+
token = credential_store.token
|
|
16
|
+
endpoint = credential_store.endpoint
|
|
17
|
+
|
|
18
|
+
raise "Not logged in" if token.nil? || endpoint.nil?
|
|
19
|
+
|
|
20
|
+
client = Vkit::Core::AuthClient.new(base_url: endpoint)
|
|
21
|
+
server_user = client.whoami(token)
|
|
22
|
+
|
|
23
|
+
# keep cache in sync if server is authoritative
|
|
24
|
+
credential_store.save_user(server_user)
|
|
25
|
+
|
|
26
|
+
server_user
|
|
27
|
+
rescue => e
|
|
28
|
+
fallback_local_user(e)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def fallback_local_user(error)
|
|
32
|
+
user = credential_store.user
|
|
33
|
+
raise "Not logged in" if user.nil?
|
|
34
|
+
|
|
35
|
+
warn "⚠️ Unable to verify identity with server"
|
|
36
|
+
warn "⚠️ #{error.message}"
|
|
37
|
+
warn "⚠️ Showing cached identity"
|
|
38
|
+
|
|
39
|
+
user
|
|
10
40
|
end
|
|
11
41
|
end
|
|
12
42
|
end
|
|
@@ -29,11 +29,15 @@ module Vkit
|
|
|
29
29
|
end
|
|
30
30
|
|
|
31
31
|
def poll_cli_login(poll_token)
|
|
32
|
-
uri = uri_for("/auth/cli/poll
|
|
33
|
-
req = Net::HTTP::
|
|
34
|
-
|
|
32
|
+
uri = uri_for("/auth/cli/poll")
|
|
33
|
+
req = Net::HTTP::Post.new(uri)
|
|
34
|
+
req["Content-Type"] = "application/json"
|
|
35
|
+
req.body = JSON.dump(
|
|
36
|
+
poll_token: poll_token
|
|
37
|
+
)
|
|
38
|
+
|
|
35
39
|
http_request(uri, req, allow_non_200: true)
|
|
36
|
-
end
|
|
40
|
+
end
|
|
37
41
|
|
|
38
42
|
def password_login(email:, password:)
|
|
39
43
|
uri = uri_for("/api/users/sign_in")
|
|
@@ -66,6 +70,14 @@ module Vkit
|
|
|
66
70
|
body["user"]
|
|
67
71
|
end
|
|
68
72
|
|
|
73
|
+
def logout(token)
|
|
74
|
+
uri = uri_for("/api/users/sign_out")
|
|
75
|
+
req = Net::HTTP::Delete.new(uri)
|
|
76
|
+
req["Authorization"] = "Bearer #{token}"
|
|
77
|
+
|
|
78
|
+
http_request(uri, req, allow_non_200: true)
|
|
79
|
+
end
|
|
80
|
+
|
|
69
81
|
private
|
|
70
82
|
|
|
71
83
|
def uri_for(path)
|
|
@@ -33,6 +33,22 @@ module Vkit
|
|
|
33
33
|
true
|
|
34
34
|
end
|
|
35
35
|
|
|
36
|
+
def save_user(user)
|
|
37
|
+
payload = load_payload
|
|
38
|
+
return unless payload
|
|
39
|
+
|
|
40
|
+
payload["user"] = user
|
|
41
|
+
|
|
42
|
+
case
|
|
43
|
+
when mac?
|
|
44
|
+
mac_keychain_store(payload)
|
|
45
|
+
when linux? && secret_tool_available?
|
|
46
|
+
linux_secret_service_store(payload)
|
|
47
|
+
else
|
|
48
|
+
file_store(payload)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
36
52
|
def endpoint
|
|
37
53
|
load_payload&.dig("endpoint")
|
|
38
54
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: vaultkit
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Nnamdi Ogundu
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-01-
|
|
11
|
+
date: 2026-01-10 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: thor
|
|
@@ -68,7 +68,7 @@ files:
|
|
|
68
68
|
- lib/vkit/utils/logger.rb
|
|
69
69
|
homepage: https://vaultkit.io
|
|
70
70
|
licenses:
|
|
71
|
-
-
|
|
71
|
+
- Nonstandard
|
|
72
72
|
metadata:
|
|
73
73
|
rubygems_mfa_required: 'true'
|
|
74
74
|
source_code_uri: https://github.com/ndbaba1/vaultkitcli
|