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 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
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative "../lib/vaultez"
4
+
5
+ Vaultez::CLI.start(ARGV)
@@ -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
@@ -0,0 +1,7 @@
1
+ module Vaultez
2
+ class Error < StandardError; end
3
+ class NotAuthenticatedError < Error; end
4
+ class AuthenticationError < Error; end
5
+ class NotFoundError < Error; end
6
+ class ApiError < Error; end
7
+ end
@@ -0,0 +1,3 @@
1
+ module Vaultez
2
+ VERSION = "0.1.0"
3
+ end
data/lib/vaultez.rb ADDED
@@ -0,0 +1,5 @@
1
+ require_relative "vaultez/version"
2
+ require_relative "vaultez/errors"
3
+ require_relative "vaultez/config"
4
+ require_relative "vaultez/client"
5
+ require_relative "vaultez/cli"
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: []