vaultez-cli 0.1.0 → 0.2.0
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/lib/vaultez/cli.rb +67 -9
- data/lib/vaultez/client.rb +16 -9
- data/lib/vaultez/commands/auth.rb +8 -1
- data/lib/vaultez/commands/fetch.rb +43 -8
- data/lib/vaultez/config.rb +1 -1
- data/lib/vaultez/errors.rb +6 -5
- data/lib/vaultez/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f2898cfe0e904ce07df993ad6cf78fd5c66b0843a6a90dccafe0fc21f94c462a
|
|
4
|
+
data.tar.gz: 1792ede773e3d10f26b7a233719690ec660402b21ce1e086e7d1a8b47f7706d9
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e920faa7ff226351174c5eb7ada3ce7c71e06a8cc0d7dabff2171d60bf7ce244afb52543763351c9705a9951474c3dfe66ae814c1af9b0a1bd089138eb573bea
|
|
7
|
+
data.tar.gz: 5efac1f679ae408baaf10bef7de70dd0c76601419f3b2b48daccf60fc1e36c635ca5546561623b55628292653e525d23e49c21fd0fbb73ffe075b10eb6d3037f
|
data/lib/vaultez/cli.rb
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
require "thor"
|
|
2
|
+
require_relative "version"
|
|
2
3
|
require_relative "commands/auth"
|
|
3
4
|
require_relative "commands/fetch"
|
|
4
5
|
require_relative "commands/config_command"
|
|
@@ -9,17 +10,30 @@ module Vaultez
|
|
|
9
10
|
include Vaultez::Commands::Fetch
|
|
10
11
|
include Vaultez::Commands::ConfigCommand
|
|
11
12
|
|
|
12
|
-
|
|
13
|
+
map %w[--version -v] => :__version
|
|
14
|
+
|
|
15
|
+
class_option :token, type: :string, banner: "TOKEN",
|
|
16
|
+
desc: "Use a project token instead of the saved session"
|
|
17
|
+
|
|
18
|
+
def initialize(*args)
|
|
19
|
+
super
|
|
20
|
+
ENV["VAULTEZ_TOKEN"] = options[:token] if options[:token]
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
desc "login", "Authenticate with email, password, and 2FA code"
|
|
13
24
|
long_desc <<~DESC, wrap: false
|
|
14
25
|
Authenticate with your Vaultez account. You will be prompted for your
|
|
15
|
-
email and
|
|
26
|
+
email, password, and a two-factor authentication code from your authenticator
|
|
27
|
+
app (or a backup code). Your session token is stored in ~/.vaultez/config.yml.
|
|
28
|
+
|
|
29
|
+
Two-factor authentication is required for all accounts.
|
|
16
30
|
|
|
17
31
|
Example:
|
|
18
32
|
vaultez login
|
|
19
33
|
DESC
|
|
20
34
|
def login; super; end
|
|
21
35
|
|
|
22
|
-
desc "logout", "
|
|
36
|
+
desc "logout", "Revoke the current session token"
|
|
23
37
|
long_desc <<~DESC, wrap: false
|
|
24
38
|
Revokes your session token on the server and clears your local credentials.
|
|
25
39
|
|
|
@@ -28,25 +42,36 @@ module Vaultez
|
|
|
28
42
|
DESC
|
|
29
43
|
def logout; super; end
|
|
30
44
|
|
|
31
|
-
desc "fetch", "Fetch
|
|
45
|
+
desc "fetch", "Fetch secrets from a project"
|
|
32
46
|
long_desc <<~DESC, wrap: false
|
|
33
|
-
Fetch resources from Vaultez.
|
|
34
|
-
|
|
47
|
+
Fetch resources from Vaultez.
|
|
48
|
+
|
|
49
|
+
USER SESSION (after `vaultez login`):
|
|
50
|
+
The --company flag is optional when you have a default company set or
|
|
51
|
+
only belong to one company. Use --project to scope to a project.
|
|
35
52
|
|
|
36
|
-
Examples:
|
|
37
53
|
vaultez fetch --companies
|
|
38
54
|
vaultez fetch --company="Acme" --projects
|
|
39
55
|
vaultez fetch --company="Acme" --project="Backend"
|
|
40
56
|
vaultez fetch --company="Acme" --project="Backend" --secret="DATABASE_URL"
|
|
57
|
+
|
|
58
|
+
PROJECT TOKEN (VAULTEZ_TOKEN env var or --token flag):
|
|
59
|
+
When a project token is active, it already knows which project to use.
|
|
60
|
+
No --project flag is needed.
|
|
61
|
+
|
|
62
|
+
VAULTEZ_TOKEN=vz_... vaultez fetch
|
|
63
|
+
vaultez fetch --token=vz_... --secret="DATABASE_URL"
|
|
64
|
+
|
|
65
|
+
Project tokens can be created in the Tokens tab of your project settings.
|
|
41
66
|
DESC
|
|
42
67
|
option :companies, type: :boolean, desc: "List all your companies"
|
|
43
68
|
option :company, type: :string, desc: "Company name"
|
|
44
69
|
option :projects, type: :boolean, desc: "List projects in a company"
|
|
45
70
|
option :project, type: :string, desc: "Project name"
|
|
46
|
-
option :secret, type: :string, desc: "Secret name"
|
|
71
|
+
option :secret, type: :string, desc: "Secret name (returns value only)"
|
|
47
72
|
def fetch; super; end
|
|
48
73
|
|
|
49
|
-
desc "config", "
|
|
74
|
+
desc "config", "Set default company or token"
|
|
50
75
|
long_desc <<~DESC, wrap: false
|
|
51
76
|
Update your local Vaultez CLI configuration.
|
|
52
77
|
|
|
@@ -55,5 +80,38 @@ module Vaultez
|
|
|
55
80
|
DESC
|
|
56
81
|
option :"default-company", type: :string, desc: "Set the default company"
|
|
57
82
|
def config; super; end
|
|
83
|
+
|
|
84
|
+
desc "__version", "Show the installed CLI version", hide: true
|
|
85
|
+
def __version
|
|
86
|
+
puts "vaultez-cli #{Vaultez::VERSION}"
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def help(command = nil, subcommand: false)
|
|
90
|
+
if command
|
|
91
|
+
super
|
|
92
|
+
else
|
|
93
|
+
puts "Vaultez CLI — secure secret management from your terminal"
|
|
94
|
+
puts
|
|
95
|
+
puts "Usage: vaultez <command> [options]"
|
|
96
|
+
puts
|
|
97
|
+
puts "Commands:"
|
|
98
|
+
puts " login Authenticate with email, password, and 2FA code"
|
|
99
|
+
puts " logout Revoke the current session token"
|
|
100
|
+
puts " fetch Fetch secrets from a project"
|
|
101
|
+
puts " config Set default company or token"
|
|
102
|
+
puts " help Show help for any command"
|
|
103
|
+
puts
|
|
104
|
+
puts "Options:"
|
|
105
|
+
puts " --token Use a project token instead of the saved session"
|
|
106
|
+
puts " --version Show the installed CLI version"
|
|
107
|
+
puts
|
|
108
|
+
puts "Examples:"
|
|
109
|
+
puts " vaultez login"
|
|
110
|
+
puts " vaultez fetch --project=\"Backend\""
|
|
111
|
+
puts " vaultez fetch --project=\"Backend\" --secret=\"DATABASE_URL\""
|
|
112
|
+
puts " vaultez fetch --token=\"vz_...\""
|
|
113
|
+
puts " vaultez fetch --token=\"vz_...\" --secret=\"DATABASE_URL\""
|
|
114
|
+
end
|
|
115
|
+
end
|
|
58
116
|
end
|
|
59
117
|
end
|
data/lib/vaultez/client.rb
CHANGED
|
@@ -5,18 +5,23 @@ require "uri"
|
|
|
5
5
|
module Vaultez
|
|
6
6
|
class Client
|
|
7
7
|
def initialize
|
|
8
|
-
@api_url
|
|
9
|
-
@token
|
|
8
|
+
@api_url = Vaultez::Config.api_url
|
|
9
|
+
@token = Vaultez::Config.token
|
|
10
|
+
@project_token = ENV["VAULTEZ_TOKEN"]
|
|
10
11
|
end
|
|
11
12
|
|
|
12
|
-
def login(email, password)
|
|
13
|
-
post("/api/v1/auth/login", { email: email, password: password }, authenticated: false)
|
|
13
|
+
def login(email, password, otp_code)
|
|
14
|
+
post("/api/v1/auth/login", { email: email, password: password, otp_code: otp_code }, authenticated: false)
|
|
14
15
|
end
|
|
15
16
|
|
|
16
17
|
def logout
|
|
17
18
|
delete("/api/v1/auth/logout")
|
|
18
19
|
end
|
|
19
20
|
|
|
21
|
+
def project_token_mode?
|
|
22
|
+
!@project_token.nil?
|
|
23
|
+
end
|
|
24
|
+
|
|
20
25
|
def companies
|
|
21
26
|
get("/api/v1/companies")
|
|
22
27
|
end
|
|
@@ -53,8 +58,9 @@ module Vaultez
|
|
|
53
58
|
req["Accept"] = "application/json"
|
|
54
59
|
|
|
55
60
|
if authenticated
|
|
56
|
-
|
|
57
|
-
|
|
61
|
+
active_token = @project_token || @token
|
|
62
|
+
raise Vaultez::NotAuthenticatedError, "Not logged in. Run `vaultez login` first, or set VAULTEZ_TOKEN." unless active_token
|
|
63
|
+
req["Authorization"] = "Bearer #{active_token}"
|
|
58
64
|
end
|
|
59
65
|
|
|
60
66
|
req.body = body.to_json if body
|
|
@@ -75,9 +81,10 @@ module Vaultez
|
|
|
75
81
|
|
|
76
82
|
case response.code.to_i
|
|
77
83
|
when 200, 201 then body
|
|
78
|
-
when 401 then raise Vaultez::AuthenticationError,
|
|
79
|
-
when
|
|
80
|
-
|
|
84
|
+
when 401 then raise Vaultez::AuthenticationError, body["error"] || "Authentication failed"
|
|
85
|
+
when 403 then raise Vaultez::TwoFactorRequiredError, body["error"] || "Two-factor authentication required"
|
|
86
|
+
when 404 then raise Vaultez::NotFoundError, body["error"] || "Not found"
|
|
87
|
+
else raise Vaultez::ApiError, body["error"] || "API error (#{response.code})"
|
|
81
88
|
end
|
|
82
89
|
end
|
|
83
90
|
end
|
|
@@ -11,11 +11,18 @@ module Vaultez
|
|
|
11
11
|
system("stty echo")
|
|
12
12
|
puts
|
|
13
13
|
|
|
14
|
+
puts "One-time code (Proton Authenticator / TOTP): "
|
|
15
|
+
otp_code = $stdin.gets.chomp
|
|
16
|
+
|
|
14
17
|
client = Vaultez::Client.new
|
|
15
|
-
response = client.login(email, password)
|
|
18
|
+
response = client.login(email, password, otp_code)
|
|
16
19
|
|
|
17
20
|
Vaultez::Config.set("token", response["token"])
|
|
18
21
|
puts "Logged in successfully."
|
|
22
|
+
rescue Vaultez::TwoFactorRequiredError => error
|
|
23
|
+
puts "Error: #{error.message}"
|
|
24
|
+
puts "Set up two-factor authentication at https://vaultez.app/two_factor/new"
|
|
25
|
+
exit 1
|
|
19
26
|
rescue Vaultez::AuthenticationError => error
|
|
20
27
|
puts "Error: #{error.message}"
|
|
21
28
|
exit 1
|
|
@@ -4,7 +4,9 @@ module Vaultez
|
|
|
4
4
|
def fetch
|
|
5
5
|
client = Vaultez::Client.new
|
|
6
6
|
|
|
7
|
-
if
|
|
7
|
+
if client.project_token_mode?
|
|
8
|
+
fetch_with_project_token(client)
|
|
9
|
+
elsif options[:companies]
|
|
8
10
|
fetch_companies(client)
|
|
9
11
|
elsif options[:projects]
|
|
10
12
|
fetch_projects(client)
|
|
@@ -29,6 +31,41 @@ module Vaultez
|
|
|
29
31
|
|
|
30
32
|
private
|
|
31
33
|
|
|
34
|
+
def fetch_with_project_token(client)
|
|
35
|
+
if options[:secret]
|
|
36
|
+
secrets = fetch_all_secrets_for_token(client)
|
|
37
|
+
secret = secrets.find { |s| s["name"] == options[:secret] }
|
|
38
|
+
unless secret
|
|
39
|
+
puts "Error: secret \"#{options[:secret]}\" not found."
|
|
40
|
+
exit 1
|
|
41
|
+
end
|
|
42
|
+
print secret["value"]
|
|
43
|
+
else
|
|
44
|
+
secrets = fetch_all_secrets_for_token(client)
|
|
45
|
+
if secrets.empty?
|
|
46
|
+
puts "No secrets found."
|
|
47
|
+
return
|
|
48
|
+
end
|
|
49
|
+
secrets.each { |s| puts "#{s["name"]}=#{s["value"]}" }
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def fetch_all_secrets_for_token(client)
|
|
54
|
+
companies = client.companies
|
|
55
|
+
company = companies.first
|
|
56
|
+
unless company
|
|
57
|
+
puts "Error: no company found for this token."
|
|
58
|
+
exit 1
|
|
59
|
+
end
|
|
60
|
+
projects = client.projects(company["id"])
|
|
61
|
+
project = projects.first
|
|
62
|
+
unless project
|
|
63
|
+
puts "Error: no project found for this token."
|
|
64
|
+
exit 1
|
|
65
|
+
end
|
|
66
|
+
client.secrets(project["id"])
|
|
67
|
+
end
|
|
68
|
+
|
|
32
69
|
def fetch_companies(client)
|
|
33
70
|
companies = client.companies
|
|
34
71
|
if companies.empty?
|
|
@@ -71,7 +108,7 @@ module Vaultez
|
|
|
71
108
|
company = resolve_company(client)
|
|
72
109
|
project = resolve_project(client, company)
|
|
73
110
|
secrets = client.secrets(project["id"])
|
|
74
|
-
secret = secrets.find { |
|
|
111
|
+
secret = secrets.find { |s| s["name"] == options[:secret] }
|
|
75
112
|
|
|
76
113
|
unless secret
|
|
77
114
|
puts "Error: secret \"#{options[:secret]}\" not found in #{project["name"]}."
|
|
@@ -85,7 +122,7 @@ module Vaultez
|
|
|
85
122
|
companies = client.companies
|
|
86
123
|
|
|
87
124
|
if options[:company]
|
|
88
|
-
company = companies.find { |
|
|
125
|
+
company = companies.find { |c| c["name"] == options[:company] }
|
|
89
126
|
unless company
|
|
90
127
|
puts "Error: company \"#{options[:company]}\" not found."
|
|
91
128
|
exit 1
|
|
@@ -95,13 +132,11 @@ module Vaultez
|
|
|
95
132
|
|
|
96
133
|
default_name = Vaultez::Config.default_company
|
|
97
134
|
if default_name
|
|
98
|
-
company = companies.find { |
|
|
135
|
+
company = companies.find { |c| c["name"] == default_name }
|
|
99
136
|
return company if company
|
|
100
137
|
end
|
|
101
138
|
|
|
102
|
-
if companies.size == 1
|
|
103
|
-
return companies.first
|
|
104
|
-
end
|
|
139
|
+
return companies.first if companies.size == 1
|
|
105
140
|
|
|
106
141
|
puts "Error: multiple companies found. Specify one with --company or set a default with `vaultez config --default-company`."
|
|
107
142
|
exit 1
|
|
@@ -109,7 +144,7 @@ module Vaultez
|
|
|
109
144
|
|
|
110
145
|
def resolve_project(client, company)
|
|
111
146
|
projects = client.projects(company["id"])
|
|
112
|
-
project = projects.find { |
|
|
147
|
+
project = projects.find { |p| p["name"] == options[:project] }
|
|
113
148
|
unless project
|
|
114
149
|
puts "Error: project \"#{options[:project]}\" not found in #{company["name"]}."
|
|
115
150
|
exit 1
|
data/lib/vaultez/config.rb
CHANGED
data/lib/vaultez/errors.rb
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
module Vaultez
|
|
2
|
-
class Error
|
|
3
|
-
class NotAuthenticatedError
|
|
4
|
-
class AuthenticationError
|
|
5
|
-
class
|
|
6
|
-
class
|
|
2
|
+
class Error < StandardError; end
|
|
3
|
+
class NotAuthenticatedError < Error; end
|
|
4
|
+
class AuthenticationError < Error; end
|
|
5
|
+
class TwoFactorRequiredError < Error; end
|
|
6
|
+
class NotFoundError < Error; end
|
|
7
|
+
class ApiError < Error; end
|
|
7
8
|
end
|
data/lib/vaultez/version.rb
CHANGED