vaultkit 1.1.3 → 1.1.4

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: af2c38a3f1d8d8e52aab3cc44f48989e5d9fdb9f1c7bcfd60f980436068f0046
4
- data.tar.gz: 8cf3f02c2e5ca5465659010588ccf4bed6e1d6fce77e8d20c1cd0f25c44bcbd6
3
+ metadata.gz: 64b133877fe2a21e147efaa74745e3322197ca6dda0ba89472d4fb61bd12d533
4
+ data.tar.gz: 83621550f7ae8fcb3b79fb3a67ba8bfffec44459c3e874c33894eefdcc8dfe24
5
5
  SHA512:
6
- metadata.gz: c4b2128ae5279ffcaef15f76e15df4f545b9d30fd683e16e03c47b2d8ddd9461f21f86ede0669929711916c68a0c60043ed2c9b2a68bb64900df41b768232424
7
- data.tar.gz: 75d5669946ca36e771ccb208099c7313baaadcf9195395b82874dc65ae0f084d31ed2940713c9730a30f131bfe9f2cc8f2b5fd390a24e8878a0e2a0dce68ed62
6
+ metadata.gz: 3d187229cc2d2e622fad6de0b48d4876d3de654bc20d949307c4d8b3ac74e6e5bdd0a73ef8b00b6dc2f0cc652aac1be04ccff889c961fe84994a2d228edc0a66
7
+ data.tar.gz: 522e340b74582402669aade2b1dc8d8581d8c1d1edd0409a07277522d1e564925a07c9d583d78766e581f957ccb8c03676332e1e513b1fc51040c051be07ec73
data/README.md CHANGED
@@ -443,17 +443,19 @@ vkit approval:approve <REQUEST_ID> [--ttl SECONDS] [--notes "..."]
443
443
  ```
444
444
 
445
445
  **Options:**
446
- - `--ttl` — Grant lifetime in seconds (default: 3600)
446
+ - `--ttl` — Grant lifetime in seconds. Defaults to the policy's TTL, which is also the maximum: a longer value is capped.
447
447
  - `--notes` — Approval notes (optional)
448
448
 
449
+ When a request needs several approver roles, each role signs off separately. Until the last one has, `approve` prints the roles still waiting instead of a grant.
450
+
449
451
  **Examples:**
450
452
  ```bash
451
453
  # Basic approval
452
454
  vkit approval:approve 42
453
455
 
454
- # With custom TTL and notes
456
+ # With a shorter TTL and notes
455
457
  vkit approval:approve 42 \
456
- --ttl 7200 \
458
+ --ttl 1800 \
457
459
  --notes "Approved for Q4 analysis. Limited to aggregate data only."
458
460
  ```
459
461
 
@@ -71,7 +71,7 @@ module Vkit
71
71
  end
72
72
 
73
73
  desc "approval:approve ID", "Approve a pending request"
74
- option :ttl, type: :numeric, default: 3600
74
+ option :ttl, type: :numeric, desc: "Grant lifetime in seconds (default and maximum: the policy's TTL)"
75
75
  define_method("approval:approve") do |id|
76
76
  Commands::ApprovalCommand.new.call_approve(
77
77
  id: id,
@@ -1,13 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "json"
4
+ require_relative "approval_roles"
4
5
 
5
6
  module Vkit
6
7
  module CLI
7
8
  module Commands
8
9
  class ApprovalCommand < BaseCommand
9
- DEFAULT_TTL = 3600
10
-
11
10
  def call_list(state: "pending")
12
11
  with_auth do
13
12
  user = credential_store.user
@@ -28,8 +27,11 @@ module Vkit
28
27
  puts "🆔 #{r["id"]}"
29
28
  puts "📂 Dataset: #{r["dataset"]}"
30
29
  puts "📄 Fields: #{Array(r["fields"]).join(', ')}"
31
- puts "👤 Requester: #{r["requester_email"]}"
32
- puts "🔐 Required: #{r["approver_role"] || 'n/a'}"
30
+ puts "👤 Requester: #{r["requester"] || r["requester_email"]}"
31
+ puts "🔐 Required: #{ApprovalRoles.describe(ApprovalRoles.required_roles(r))}"
32
+ if r["state"] == "pending" && Array(r["approved_roles"]).any?
33
+ puts "⏳ Waiting on: #{ApprovalRoles.describe(ApprovalRoles.outstanding_roles(r))}"
34
+ end
33
35
  puts "⚙️ Status: #{r["state"].capitalize}"
34
36
  puts "🔑 Grant Ref: #{r["grant_ref"] || 'n/a'}" if r["state"] == "approved"
35
37
  puts "💬 Reason: #{r["reason"]}"
@@ -39,19 +41,29 @@ module Vkit
39
41
  end
40
42
  end
41
43
 
42
- def call_approve(id:, ttl_seconds: DEFAULT_TTL)
44
+ # Without ttl_seconds the grant lasts as long as the policy allows; the
45
+ # control plane caps a longer request at the policy's TTL.
46
+ def call_approve(id:, ttl_seconds: nil)
43
47
  with_auth do
44
48
  user = credential_store.user
45
49
  org = user["organization_slug"]
46
50
 
51
+ body = ttl_seconds.nil? ? {} : { ttl_seconds: ttl_seconds }
47
52
  res = authenticated_client.post(
48
53
  "/api/v1/orgs/#{org}/approvals/#{id}/approve",
49
- body: { ttl_seconds: ttl_seconds }
54
+ body: body
50
55
  )
51
56
 
52
- puts "✅ Approved request #{id}"
53
- puts " → Grant Ref: #{res["grant_ref"] || res["grant_id"]}"
54
- puts " → Expires: #{res["expires_at"]}"
57
+ if res["status"] == "pending"
58
+ puts "✍️ Signed off on request #{id}"
59
+ puts " → Approved by: #{ApprovalRoles.describe(Array(res["approved_roles"]))}"
60
+ puts " → Waiting on: #{ApprovalRoles.describe(Array(res["outstanding_roles"]))}"
61
+ puts " The grant is issued once every role has approved."
62
+ else
63
+ puts "✅ Approved request #{id}"
64
+ puts " → Grant Ref: #{res["grant_ref"] || res["grant_id"]}"
65
+ puts " → Expires: #{res["expires_at"]}"
66
+ end
55
67
  end
56
68
  end
57
69
 
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Vkit
4
+ module CLI
5
+ module Commands
6
+ # Approver roles as the control plane reports them. A request can need a
7
+ # sign-off from several roles (approver_roles); outstanding_roles are the
8
+ # ones still missing. Older control planes send only approver_role.
9
+ module ApprovalRoles
10
+ module_function
11
+
12
+ def required_roles(approval)
13
+ roles = Array(approval["approver_roles"])
14
+ return roles if roles.any?
15
+
16
+ approval["approver_role"].to_s.split(",").map(&:strip).reject(&:empty?)
17
+ end
18
+
19
+ def outstanding_roles(approval)
20
+ return Array(approval["outstanding_roles"]) if approval.key?("outstanding_roles")
21
+
22
+ approval["state"].to_s == "pending" ? required_roles(approval) : []
23
+ end
24
+
25
+ def describe(roles, fallback: "n/a")
26
+ roles.empty? ? fallback : roles.join(", ")
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -2,6 +2,7 @@
2
2
 
3
3
  require "json"
4
4
  require "time"
5
+ require_relative "approval_roles"
5
6
 
6
7
  module Vkit
7
8
  module CLI
@@ -99,6 +100,8 @@ module Vkit
99
100
  fields: a["fields"],
100
101
  requester: a["requester"],
101
102
  approver_role: a["approver_role"],
103
+ approver_roles: ApprovalRoles.required_roles(a),
104
+ outstanding_roles: ApprovalRoles.outstanding_roles(a),
102
105
  created_at: a["created_at"]
103
106
  }
104
107
  end
@@ -110,7 +113,10 @@ module Vkit
110
113
  puts "Dataset: #{a["dataset"]}"
111
114
  puts "Fields: #{Array(a["fields"]).join(", ")}"
112
115
  puts "Requester: #{a["requester"]}"
113
- puts "Role Req: #{a["approver_role"] || "any"}"
116
+ puts "Role Req: #{ApprovalRoles.describe(ApprovalRoles.required_roles(a), fallback: "any")}"
117
+ if Array(a["approved_roles"]).any?
118
+ puts "Waiting: #{ApprovalRoles.describe(ApprovalRoles.outstanding_roles(a))}"
119
+ end
114
120
  puts "Created: #{format_time(a["created_at"])}"
115
121
  puts
116
122
  puts "▶ Approve: vkit approval approve #{a["id"]}"
data/lib/vkit/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Vkit
4
- VERSION = "1.1.3"
4
+ VERSION = "1.1.4"
5
5
  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: 1.1.3
4
+ version: 1.1.4
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-10-27 00:00:00.000000000 Z
11
+ date: 2026-10-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: thor
@@ -58,6 +58,7 @@ files:
58
58
  - lib/vkit/cli/commands/agent_tokens_list_command.rb
59
59
  - lib/vkit/cli/commands/agent_tokens_revoke_command.rb
60
60
  - lib/vkit/cli/commands/approval_command.rb
61
+ - lib/vkit/cli/commands/approval_roles.rb
61
62
  - lib/vkit/cli/commands/approval_watch_command.rb
62
63
  - lib/vkit/cli/commands/base_command.rb
63
64
  - lib/vkit/cli/commands/datasource_command.rb