vaultez-cli 0.1.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 +7 -0
- data/bin/vaultez +5 -0
- data/lib/vaultez/cli.rb +59 -0
- data/lib/vaultez/client.rb +84 -0
- data/lib/vaultez/commands/auth.rb +35 -0
- data/lib/vaultez/commands/config_command.rb +30 -0
- data/lib/vaultez/commands/fetch.rb +121 -0
- data/lib/vaultez/config.rb +52 -0
- data/lib/vaultez/errors.rb +7 -0
- data/lib/vaultez/version.rb +3 -0
- data/lib/vaultez.rb +5 -0
- metadata +64 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: '07770618a022bbabf6a89511dbeb1366ead244d90fc938435aba94bb9e7ccbbd'
|
|
4
|
+
data.tar.gz: 03afca8c6b3846442c78791cddda0232b9882d37104708b31ce8e38e4e985a11
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 5346e840aa94d43ea44fb8c6a0394bc511e1d139bc812f2fcf95c4616619e35a210aa8b94b2d7616b7dfaa271a13bb8759674ae5b752c8d0239fa6742d9a50d4
|
|
7
|
+
data.tar.gz: 2dda8d7e5fb79f72d3b8b5b65dc338a5af379149b28cd949984a4ce24ad6db86b5d7b9d138eec86f331278d83246da738c5aa0235fcfa723b13c7f32db6a6614
|
data/bin/vaultez
ADDED
data/lib/vaultez/cli.rb
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
require "thor"
|
|
2
|
+
require_relative "commands/auth"
|
|
3
|
+
require_relative "commands/fetch"
|
|
4
|
+
require_relative "commands/config_command"
|
|
5
|
+
|
|
6
|
+
module Vaultez
|
|
7
|
+
class CLI < Thor
|
|
8
|
+
include Vaultez::Commands::Auth
|
|
9
|
+
include Vaultez::Commands::Fetch
|
|
10
|
+
include Vaultez::Commands::ConfigCommand
|
|
11
|
+
|
|
12
|
+
desc "login", "Authenticate with Vaultez"
|
|
13
|
+
long_desc <<~DESC, wrap: false
|
|
14
|
+
Authenticate with your Vaultez account. You will be prompted for your
|
|
15
|
+
email and password. Your session token is stored in ~/.vaultez/config.yml.
|
|
16
|
+
|
|
17
|
+
Example:
|
|
18
|
+
vaultez login
|
|
19
|
+
DESC
|
|
20
|
+
def login; super; end
|
|
21
|
+
|
|
22
|
+
desc "logout", "Log out and revoke your token"
|
|
23
|
+
long_desc <<~DESC, wrap: false
|
|
24
|
+
Revokes your session token on the server and clears your local credentials.
|
|
25
|
+
|
|
26
|
+
Example:
|
|
27
|
+
vaultez logout
|
|
28
|
+
DESC
|
|
29
|
+
def logout; super; end
|
|
30
|
+
|
|
31
|
+
desc "fetch", "Fetch companies, projects, or secrets"
|
|
32
|
+
long_desc <<~DESC, wrap: false
|
|
33
|
+
Fetch resources from Vaultez. The --company flag is optional when you
|
|
34
|
+
have a default company set or only belong to one company.
|
|
35
|
+
|
|
36
|
+
Examples:
|
|
37
|
+
vaultez fetch --companies
|
|
38
|
+
vaultez fetch --company="Acme" --projects
|
|
39
|
+
vaultez fetch --company="Acme" --project="Backend"
|
|
40
|
+
vaultez fetch --company="Acme" --project="Backend" --secret="DATABASE_URL"
|
|
41
|
+
DESC
|
|
42
|
+
option :companies, type: :boolean, desc: "List all your companies"
|
|
43
|
+
option :company, type: :string, desc: "Company name"
|
|
44
|
+
option :projects, type: :boolean, desc: "List projects in a company"
|
|
45
|
+
option :project, type: :string, desc: "Project name"
|
|
46
|
+
option :secret, type: :string, desc: "Secret name"
|
|
47
|
+
def fetch; super; end
|
|
48
|
+
|
|
49
|
+
desc "config", "Configure Vaultez CLI settings"
|
|
50
|
+
long_desc <<~DESC, wrap: false
|
|
51
|
+
Update your local Vaultez CLI configuration.
|
|
52
|
+
|
|
53
|
+
Examples:
|
|
54
|
+
vaultez config --default-company="Acme"
|
|
55
|
+
DESC
|
|
56
|
+
option :"default-company", type: :string, desc: "Set the default company"
|
|
57
|
+
def config; super; end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
require "net/http"
|
|
2
|
+
require "json"
|
|
3
|
+
require "uri"
|
|
4
|
+
|
|
5
|
+
module Vaultez
|
|
6
|
+
class Client
|
|
7
|
+
def initialize
|
|
8
|
+
@api_url = Vaultez::Config.api_url
|
|
9
|
+
@token = Vaultez::Config.token
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def login(email, password)
|
|
13
|
+
post("/api/v1/auth/login", { email: email, password: password }, authenticated: false)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def logout
|
|
17
|
+
delete("/api/v1/auth/logout")
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def companies
|
|
21
|
+
get("/api/v1/companies")
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def projects(company_id)
|
|
25
|
+
get("/api/v1/companies/#{company_id}/projects")
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def secrets(project_id)
|
|
29
|
+
get("/api/v1/projects/#{project_id}/secrets")
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
def get(path)
|
|
35
|
+
request(:get, path)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def post(path, body, authenticated: true)
|
|
39
|
+
request(:post, path, body, authenticated: authenticated)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def delete(path)
|
|
43
|
+
request(:delete, path)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def request(method, path, body = nil, authenticated: true)
|
|
47
|
+
uri = URI("#{@api_url}#{path}")
|
|
48
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
49
|
+
http.use_ssl = uri.scheme == "https"
|
|
50
|
+
|
|
51
|
+
req = build_request(method, uri)
|
|
52
|
+
req["Content-Type"] = "application/json"
|
|
53
|
+
req["Accept"] = "application/json"
|
|
54
|
+
|
|
55
|
+
if authenticated
|
|
56
|
+
raise Vaultez::NotAuthenticatedError, "Not logged in. Run `vaultez login` first." unless @token
|
|
57
|
+
req["Authorization"] = "Bearer #{@token}"
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
req.body = body.to_json if body
|
|
61
|
+
|
|
62
|
+
parse_response(http.request(req))
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def build_request(method, uri)
|
|
66
|
+
case method
|
|
67
|
+
when :get then Net::HTTP::Get.new(uri)
|
|
68
|
+
when :post then Net::HTTP::Post.new(uri)
|
|
69
|
+
when :delete then Net::HTTP::Delete.new(uri)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def parse_response(response)
|
|
74
|
+
body = JSON.parse(response.body) rescue {}
|
|
75
|
+
|
|
76
|
+
case response.code.to_i
|
|
77
|
+
when 200, 201 then body
|
|
78
|
+
when 401 then raise Vaultez::AuthenticationError, body["error"] || "Authentication failed"
|
|
79
|
+
when 404 then raise Vaultez::NotFoundError, body["error"] || "Not found"
|
|
80
|
+
else raise Vaultez::ApiError, body["error"] || "API error (#{response.code})"
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
module Vaultez
|
|
2
|
+
module Commands
|
|
3
|
+
module Auth
|
|
4
|
+
def login
|
|
5
|
+
puts "Email: "
|
|
6
|
+
email = $stdin.gets.chomp
|
|
7
|
+
|
|
8
|
+
puts "Password: "
|
|
9
|
+
system("stty -echo")
|
|
10
|
+
password = $stdin.gets.chomp
|
|
11
|
+
system("stty echo")
|
|
12
|
+
puts
|
|
13
|
+
|
|
14
|
+
client = Vaultez::Client.new
|
|
15
|
+
response = client.login(email, password)
|
|
16
|
+
|
|
17
|
+
Vaultez::Config.set("token", response["token"])
|
|
18
|
+
puts "Logged in successfully."
|
|
19
|
+
rescue Vaultez::AuthenticationError => error
|
|
20
|
+
puts "Error: #{error.message}"
|
|
21
|
+
exit 1
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def logout
|
|
25
|
+
client = Vaultez::Client.new
|
|
26
|
+
client.logout
|
|
27
|
+
Vaultez::Config.clear
|
|
28
|
+
puts "Logged out successfully."
|
|
29
|
+
rescue Vaultez::NotAuthenticatedError => error
|
|
30
|
+
puts "Error: #{error.message}"
|
|
31
|
+
exit 1
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
module Vaultez
|
|
2
|
+
module Commands
|
|
3
|
+
module ConfigCommand
|
|
4
|
+
def config
|
|
5
|
+
if options[:"default-company"]
|
|
6
|
+
client = Vaultez::Client.new
|
|
7
|
+
companies = client.companies
|
|
8
|
+
company = companies.find { |company| company["name"] == options[:"default-company"] }
|
|
9
|
+
|
|
10
|
+
unless company
|
|
11
|
+
puts "Error: company \"#{options[:"default-company"]}\" not found."
|
|
12
|
+
exit 1
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
Vaultez::Config.set("default_company", company["name"])
|
|
16
|
+
puts "Default company set to \"#{company["name"]}\"."
|
|
17
|
+
else
|
|
18
|
+
puts "No config option provided. See `vaultez help config`."
|
|
19
|
+
exit 1
|
|
20
|
+
end
|
|
21
|
+
rescue Vaultez::NotAuthenticatedError => error
|
|
22
|
+
puts "Error: #{error.message}"
|
|
23
|
+
exit 1
|
|
24
|
+
rescue Vaultez::ApiError => error
|
|
25
|
+
puts "Error: #{error.message}"
|
|
26
|
+
exit 1
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
module Vaultez
|
|
2
|
+
module Commands
|
|
3
|
+
module Fetch
|
|
4
|
+
def fetch
|
|
5
|
+
client = Vaultez::Client.new
|
|
6
|
+
|
|
7
|
+
if options[:companies]
|
|
8
|
+
fetch_companies(client)
|
|
9
|
+
elsif options[:projects]
|
|
10
|
+
fetch_projects(client)
|
|
11
|
+
elsif options[:project] && options[:secret]
|
|
12
|
+
fetch_secret(client)
|
|
13
|
+
elsif options[:project]
|
|
14
|
+
fetch_secrets(client)
|
|
15
|
+
else
|
|
16
|
+
puts "Error: not enough options. See `vaultez help fetch`."
|
|
17
|
+
exit 1
|
|
18
|
+
end
|
|
19
|
+
rescue Vaultez::NotAuthenticatedError => error
|
|
20
|
+
puts "Error: #{error.message}"
|
|
21
|
+
exit 1
|
|
22
|
+
rescue Vaultez::NotFoundError => error
|
|
23
|
+
puts "Error: #{error.message}"
|
|
24
|
+
exit 1
|
|
25
|
+
rescue Vaultez::ApiError => error
|
|
26
|
+
puts "Error: #{error.message}"
|
|
27
|
+
exit 1
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
|
|
32
|
+
def fetch_companies(client)
|
|
33
|
+
companies = client.companies
|
|
34
|
+
if companies.empty?
|
|
35
|
+
puts "No companies found."
|
|
36
|
+
return
|
|
37
|
+
end
|
|
38
|
+
puts "Companies:"
|
|
39
|
+
companies.each do |company|
|
|
40
|
+
puts " #{company["name"]} (#{company["role"]})"
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def fetch_projects(client)
|
|
45
|
+
company = resolve_company(client)
|
|
46
|
+
projects = client.projects(company["id"])
|
|
47
|
+
if projects.empty?
|
|
48
|
+
puts "No projects found in #{company["name"]}."
|
|
49
|
+
return
|
|
50
|
+
end
|
|
51
|
+
puts "Projects in #{company["name"]}:"
|
|
52
|
+
projects.each do |project|
|
|
53
|
+
puts " #{project["name"]} (#{project["role"]})"
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def fetch_secrets(client)
|
|
58
|
+
company = resolve_company(client)
|
|
59
|
+
project = resolve_project(client, company)
|
|
60
|
+
secrets = client.secrets(project["id"])
|
|
61
|
+
if secrets.empty?
|
|
62
|
+
puts "No secrets found in #{project["name"]}."
|
|
63
|
+
return
|
|
64
|
+
end
|
|
65
|
+
secrets.each do |secret|
|
|
66
|
+
puts "#{secret["name"]}=#{secret["value"]}"
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def fetch_secret(client)
|
|
71
|
+
company = resolve_company(client)
|
|
72
|
+
project = resolve_project(client, company)
|
|
73
|
+
secrets = client.secrets(project["id"])
|
|
74
|
+
secret = secrets.find { |secret| secret["name"] == options[:secret] }
|
|
75
|
+
|
|
76
|
+
unless secret
|
|
77
|
+
puts "Error: secret \"#{options[:secret]}\" not found in #{project["name"]}."
|
|
78
|
+
exit 1
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
print secret["value"]
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def resolve_company(client)
|
|
85
|
+
companies = client.companies
|
|
86
|
+
|
|
87
|
+
if options[:company]
|
|
88
|
+
company = companies.find { |company| company["name"] == options[:company] }
|
|
89
|
+
unless company
|
|
90
|
+
puts "Error: company \"#{options[:company]}\" not found."
|
|
91
|
+
exit 1
|
|
92
|
+
end
|
|
93
|
+
return company
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
default_name = Vaultez::Config.default_company
|
|
97
|
+
if default_name
|
|
98
|
+
company = companies.find { |company| company["name"] == default_name }
|
|
99
|
+
return company if company
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
if companies.size == 1
|
|
103
|
+
return companies.first
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
puts "Error: multiple companies found. Specify one with --company or set a default with `vaultez config --default-company`."
|
|
107
|
+
exit 1
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def resolve_project(client, company)
|
|
111
|
+
projects = client.projects(company["id"])
|
|
112
|
+
project = projects.find { |project| project["name"] == options[:project] }
|
|
113
|
+
unless project
|
|
114
|
+
puts "Error: project \"#{options[:project]}\" not found in #{company["name"]}."
|
|
115
|
+
exit 1
|
|
116
|
+
end
|
|
117
|
+
project
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
require "yaml"
|
|
2
|
+
require "fileutils"
|
|
3
|
+
|
|
4
|
+
module Vaultez
|
|
5
|
+
class Config
|
|
6
|
+
CONFIG_PATH = File.expand_path("~/.vaultez/config.yml")
|
|
7
|
+
|
|
8
|
+
def self.get(key)
|
|
9
|
+
read[key.to_s]
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def self.set(key, value)
|
|
13
|
+
data = read
|
|
14
|
+
data[key.to_s] = value
|
|
15
|
+
write(data)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def self.delete(key)
|
|
19
|
+
data = read
|
|
20
|
+
data.delete(key.to_s)
|
|
21
|
+
write(data)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def self.clear
|
|
25
|
+
FileUtils.rm_f(CONFIG_PATH)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def self.token
|
|
29
|
+
get("token")
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def self.api_url
|
|
33
|
+
get("api_url") || "https://vaultez.app"
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def self.default_company
|
|
37
|
+
get("default_company")
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
private
|
|
41
|
+
|
|
42
|
+
def self.read
|
|
43
|
+
return {} unless File.exist?(CONFIG_PATH)
|
|
44
|
+
YAML.safe_load(File.read(CONFIG_PATH)) || {}
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def self.write(data)
|
|
48
|
+
FileUtils.mkdir_p(File.dirname(CONFIG_PATH))
|
|
49
|
+
File.write(CONFIG_PATH, data.to_yaml)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
data/lib/vaultez.rb
ADDED
metadata
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: vaultez-cli
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Nur Ketene
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: thor
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - "~>"
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '1.0'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - "~>"
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '1.0'
|
|
26
|
+
email:
|
|
27
|
+
- nur@vaultez.app
|
|
28
|
+
executables:
|
|
29
|
+
- vaultez
|
|
30
|
+
extensions: []
|
|
31
|
+
extra_rdoc_files: []
|
|
32
|
+
files:
|
|
33
|
+
- bin/vaultez
|
|
34
|
+
- lib/vaultez.rb
|
|
35
|
+
- lib/vaultez/cli.rb
|
|
36
|
+
- lib/vaultez/client.rb
|
|
37
|
+
- lib/vaultez/commands/auth.rb
|
|
38
|
+
- lib/vaultez/commands/config_command.rb
|
|
39
|
+
- lib/vaultez/commands/fetch.rb
|
|
40
|
+
- lib/vaultez/config.rb
|
|
41
|
+
- lib/vaultez/errors.rb
|
|
42
|
+
- lib/vaultez/version.rb
|
|
43
|
+
homepage: https://vaultez.app
|
|
44
|
+
licenses:
|
|
45
|
+
- MIT
|
|
46
|
+
metadata: {}
|
|
47
|
+
rdoc_options: []
|
|
48
|
+
require_paths:
|
|
49
|
+
- lib
|
|
50
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - ">="
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: 3.0.0
|
|
55
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
56
|
+
requirements:
|
|
57
|
+
- - ">="
|
|
58
|
+
- !ruby/object:Gem::Version
|
|
59
|
+
version: '0'
|
|
60
|
+
requirements: []
|
|
61
|
+
rubygems_version: 3.6.9
|
|
62
|
+
specification_version: 4
|
|
63
|
+
summary: CLI tool for Vaultez — manage your secrets from the terminal
|
|
64
|
+
test_files: []
|