vault 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +41 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +6 -0
  5. data/CHANGELOG.md +5 -0
  6. data/Gemfile +3 -0
  7. data/Gemfile.lock +32 -0
  8. data/LICENSE +362 -0
  9. data/README.md +80 -54
  10. data/Rakefile +4 -40
  11. data/lib/vault.rb +33 -46
  12. data/lib/vault/api.rb +9 -0
  13. data/lib/vault/api/auth_token.rb +90 -0
  14. data/lib/vault/api/help.rb +23 -0
  15. data/lib/vault/api/logical.rb +66 -0
  16. data/lib/vault/api/secret.rb +23 -0
  17. data/lib/vault/api/sys.rb +24 -0
  18. data/lib/vault/api/sys/audit.rb +60 -0
  19. data/lib/vault/api/sys/auth.rb +58 -0
  20. data/lib/vault/api/sys/init.rb +46 -0
  21. data/lib/vault/api/sys/leader.rb +25 -0
  22. data/lib/vault/api/sys/lease.rb +51 -0
  23. data/lib/vault/api/sys/mount.rb +75 -0
  24. data/lib/vault/api/sys/policy.rb +76 -0
  25. data/lib/vault/api/sys/seal.rb +49 -0
  26. data/lib/vault/client.rb +285 -0
  27. data/lib/vault/configurable.rb +48 -0
  28. data/lib/vault/defaults.rb +68 -0
  29. data/lib/vault/errors.rb +48 -0
  30. data/lib/vault/request.rb +19 -0
  31. data/lib/vault/response.rb +20 -0
  32. data/lib/vault/version.rb +1 -6
  33. data/vault.gemspec +25 -0
  34. metadata +97 -98
  35. data/MIT-LICENSE +0 -20
  36. data/lib/vault/associations.rb +0 -39
  37. data/lib/vault/attribute_accessors.rb +0 -29
  38. data/lib/vault/bulk_attributes.rb +0 -17
  39. data/lib/vault/dirty.rb +0 -37
  40. data/lib/vault/finders.rb +0 -24
  41. data/lib/vault/persistance.rb +0 -47
  42. data/lib/vault/properties.rb +0 -68
  43. data/lib/vault/scoping.rb +0 -64
  44. data/lib/vault/storage.rb +0 -4
  45. data/lib/vault/storage/in_memory_store.rb +0 -14
  46. data/lib/vault/storage/yaml_store.rb +0 -52
  47. data/lib/vault/validations.rb +0 -13
  48. data/spec/active_model_compliance_spec.rb +0 -33
  49. data/spec/spec_helper.rb +0 -8
  50. data/spec/support/helpers.rb +0 -16
  51. data/spec/support/storage_api.rb +0 -14
  52. data/spec/vault/associations_spec.rb +0 -73
  53. data/spec/vault/finders_spec.rb +0 -69
  54. data/spec/vault/persistance_spec.rb +0 -126
  55. data/spec/vault/properties_spec.rb +0 -59
  56. data/spec/vault/scoping_spec.rb +0 -53
  57. data/spec/vault/storage/in_memory_store_spec.rb +0 -5
  58. data/spec/vault/storage/yaml_store_spec.rb +0 -29
  59. data/spec/vault_spec.rb +0 -33
data/README.md CHANGED
@@ -1,73 +1,99 @@
1
- Vault, a lightweight Object Document Mapper
2
- ===========================================
1
+ Vault Ruby Client
2
+ =================
3
+ [![Build Status](https://secure.travis-ci.org/hashicorp/vault-ruby.png?branch=master)](http://travis-ci.org/hashicorp/vault-ruby)
3
4
 
4
- class User
5
- include Vault
5
+ Vault is the official Ruby client for interacting with [Vault](https://vaultproject.io) by HashiCorp.
6
6
 
7
- key :id
8
- property :name
9
- property :email
10
- end
7
+ Quick Start
8
+ -----------
9
+ Install via Rubygems:
11
10
 
12
- user = User.new(:id => 1, :name => "John", :email => "john@example.org")
13
- user.save
11
+ $ gem install vault
14
12
 
15
- User.find(1) #=> user
16
- User.all #=> [user]
17
- User.size #=> 1
13
+ or add it to your Gemfile if you're using Bundler:
18
14
 
19
- Storage
20
- -------
15
+ ```ruby
16
+ gem "vault", "~> 0.1"
17
+ ```
21
18
 
22
- By default Vault stores everything in an in-memory hash. Each model (class in
23
- which you included the `Vault` module) has its own, independent storage.
19
+ and then run the `bundle` command to install.
24
20
 
25
- Subclasses of a model share their storage with their parent.
21
+ Start a Vault client:
26
22
 
27
- To change the storage you can call `store_objects_in(store)` in the class
28
- definition. A store is any object that implements this API:
23
+ ```ruby
24
+ Vault.address = "http://127.0.0.1:8200" # Also reads from ENV["VAULT_ADDR"]
25
+ Vault.token = "abcd-1234" # Also reads from ENV["VAULT_TOKEN"]
29
26
 
30
- * `Store#size` returns an integer with the total amount of elements in the store
31
- * `Store#each(&block)` a store must implement `each` and include Enumerable
32
- * `Store#[](key)` receiving the key it should return a hash of all attributes
33
- **except for the key**.
34
- * `Store#[]=(key, attributes)` attributes will be a hash with the attributes
35
- **except** for the key.
36
- * `Store#delete(key)` shall delete the item without the given key.
37
- * `Store#filter(hash)` should return a new object of the same class as the
38
- original store, but only with objects whose properties match those of the
39
- argument.
27
+ Vault.sys.mounts #=> { :secret => #<struct Vault::Mount type="generic", description="generic secret storage"> }
28
+ ```
40
29
 
41
- This library provides two storage mechanisms out of the box:
30
+ Usage
31
+ -----
32
+ The following configuration options are available:
42
33
 
43
- * `Vault::Storage::InMemoryStore` is a simple hash storage (and the default)
44
- * `Vault::Storage::YamlStore` serializes the contents to disk as a YAML file
34
+ ```ruby
35
+ Vault::Client.configure do |config|
36
+ # The address of the Vault server, also read as ENV["VAULT_TOKEN"]
37
+ config.address = "https://127.0.0.1:8200"
45
38
 
46
- TODO
47
- ----
39
+ # The token to authenticate with Vault, also read as ENV["VAULT_TOKEN"]
40
+ config.token = "abcd-1234"
48
41
 
49
- * Relationships/Associations
42
+ # Proxy connection information, also read as ENV["VAULT_PROXY_(thing)"]
43
+ config.proxy_address = "..."
44
+ config.proxy_port = "..."
45
+ config.proxy_username = "..."
46
+ config.proxy_password = "..."
50
47
 
51
- More docs?
52
- ----------
48
+ # Custom SSL PEM, also read as ENV["VAULT_SSL_CERT"]
49
+ config.ssl_pem_file = "/path/on/disk.pem"
53
50
 
54
- I will get to it eventually. For now, read the specs and the source—it is a
55
- small library. Or help and write some docs :)
51
+ # Use SSL verification, also read as ENV["VAULT_SSL_VERIFY"]
52
+ config.ssl_verify = false
53
+ end
54
+ ```
56
55
 
57
- Note on Patches/Pull Requests
58
- -----------------------------
56
+ If you do not want the Vault singleton, of if you need to communicate with multiple Vault servers at once, you can create indepenent client objects:
59
57
 
60
- * Fork the project.
61
- * Make your feature addition or bug fix.
62
- * Add tests for it. This is important so I don’t break it in a future version
63
- unintentionally.
64
- * Commit, do not mess with Rakefile, version, or history. (if you want to have
65
- your own version, that is fine but bump version in a commit by itself I can
66
- ignore when I pull.)
67
- * Send me a pull request. Bonus points for topic branches.
58
+ ```ruby
59
+ client_1 = Vault::Client.new(address: "https://vault.mycompany.com")
60
+ client_2 = Vault::Client.new(address: "https://other-vault.mycompany.com")
61
+ ```
68
62
 
69
- Copyright
70
- ---------
63
+ ### Making requests
64
+ All of the methods and API calls are heavily documented with examples inline using YARD. In order to keep the examples versioned with the code, the README only lists a few examples for using the Vault gem. Please see the inline documentation for the full API documentation. The tests in the 'spec' directory are an additional source of examples.
71
65
 
72
- Copyright © 2010 [Nicolás Sanguinetti](http://github.com/foca), licensed under
73
- an MIT license. See MIT-LICENSE for details.
66
+ #### Seal Status
67
+ ```ruby
68
+ Vault.sys.seal_status
69
+ #=> #<Vault::SealStatus sealed=false, t=1, n=1, progress=0>
70
+ ```
71
+
72
+ #### Create a Secret
73
+ ```ruby
74
+ Vault.logical.write("secret/bacon", delicious: true, cooktime: "11")
75
+ #=> #<Vault::Secret lease_id="">
76
+ ```
77
+
78
+ #### Retrieve a Secret
79
+ ```ruby
80
+ Vault.logical.read("secret/bacon")
81
+ #=> #<Vault::Secret lease_id="">
82
+ ```
83
+
84
+ #### Seal the Vault
85
+ ```ruby
86
+ Vault.sys.seal #=> true
87
+ ```
88
+
89
+ Development
90
+ -----------
91
+ 1. Clone the project on GitHub
92
+ 2. Create a feature branch
93
+ 3. Submit a Pull Request
94
+
95
+ Important Notes:
96
+
97
+ - **All new features must include test coverage.** At a bare minimum, Unit tests are required. It is preferred if you include acceptance tests as well.
98
+ - **The tests must be be idempotent.** The HTTP calls made during a test should be able to be run over and over.
99
+ - **Tests are order independent.** The default RSpec configuration randomizes the test order, so this should not be a problem.
data/Rakefile CHANGED
@@ -1,42 +1,6 @@
1
- $LOAD_PATH.unshift File.expand_path("../lib", __FILE__)
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
2
3
 
3
- require "rake"
4
- require "rake/gempackagetask"
5
- require "rake/rdoctask"
4
+ RSpec::Core::RakeTask.new(:spec)
6
5
 
7
- require "vault/version"
8
-
9
- begin
10
- require "spec/rake/spectask"
11
-
12
- Spec::Rake::SpecTask.new(:spec) do |spec|
13
- spec.pattern = "spec/**/*_spec.rb"
14
- spec.spec_opts << "--color" << "--format specdoc"
15
- end
16
-
17
- task :default => ["spec"]
18
- rescue LoadError
19
- end
20
-
21
- spec = Gem::Specification.new do |s|
22
- s.name = "vault"
23
- s.version = Vault::Version::STRING
24
- s.summary = "Provides a very lightweight ODM"
25
- s.author = "Nicolás Sanguinetti"
26
- s.email = "hi@nicolassanguinetti.info"
27
- s.homepage = "http://github.com/foca/vault"
28
-
29
- s.has_rdoc = true
30
- s.extra_rdoc_files = %w(README.md MIT-LICENSE)
31
-
32
- s.files = Dir['Rakefile', '{bin,lib,man,test,spec}/**/*', 'README*', '*LICENSE*'] & `git ls-files -z`.split("\0")
33
-
34
- s.require_paths = ["lib"]
35
-
36
- s.add_dependency("activemodel", "3.0.0.beta.3")
37
- s.add_development_dependency("rspec", "~> 1.3")
38
- end
39
-
40
- Rake::GemPackageTask.new(spec) do |pkg|
41
- pkg.gem_spec = spec
42
- end
6
+ task default: :spec
@@ -1,53 +1,40 @@
1
- require "set"
2
- require "active_model"
3
- require "active_support/core_ext/hash/except"
4
- require "active_support/core_ext/module/delegation"
5
- require "active_support/core_ext/class/attribute_accessors"
6
-
7
1
  module Vault
8
- extend ActiveSupport::Concern
9
- extend ActiveSupport::Autoload
10
-
11
- autoload :Associations
12
- autoload :AttributeAccessors
13
- autoload :BulkAttributes
14
- autoload :Dirty
15
- autoload :Finders
16
- autoload :Persistance
17
- autoload :Properties
18
- autoload :Scoping
19
- autoload :Storage
20
- autoload :Validations
21
-
22
- module Storage
23
- extend ActiveSupport::Autoload
24
- autoload :InMemoryStore
25
- autoload :YamlStore
26
- end
27
-
28
- included do
29
- extend ActiveModel::Naming
30
- extend Properties
31
- extend Finders
32
- extend Scoping
33
- extend Associations
34
-
35
- include BulkAttributes
36
- include AttributeAccessors
37
- include Persistance
38
- include Dirty
39
- include Validations
40
-
41
- # Convenience methods to provide ActiveModel's API
42
- include ActiveModel::Conversion
2
+ require_relative "vault/client"
3
+ require_relative "vault/configurable"
4
+ require_relative "vault/defaults"
5
+ require_relative "vault/errors"
6
+ require_relative "vault/response"
7
+ require_relative "vault/version"
8
+
9
+ require_relative "vault/api"
10
+
11
+ extend Vault::Configurable
12
+
13
+ # API client object based off the configured options in {Configurable}.
14
+ #
15
+ # @return [Vault::Client]
16
+ def self.client
17
+ if !defined?(@client) || !@client.same_options?(options)
18
+ @client = Vault::Client.new(options)
19
+ end
20
+ @client
43
21
  end
44
22
 
45
- def key
46
- send(self.class.key)
23
+ # Delegate all methods to the client object, essentially making the module
24
+ # object behave like a {Client}.
25
+ def self.method_missing(m, *args, &block)
26
+ if client.respond_to?(m)
27
+ client.send(m, *args, &block)
28
+ else
29
+ super
30
+ end
47
31
  end
48
- alias_method :id, :key
49
32
 
50
- def ==(other)
51
- key == other.key
33
+ # Delegating +respond_to+ to the {Client}.
34
+ def self.respond_to_missing?(m, include_private = false)
35
+ client.respond_to?(m) || super
52
36
  end
53
37
  end
38
+
39
+ # Load the initial default values
40
+ Vault.setup!
@@ -0,0 +1,9 @@
1
+ module Vault
2
+ module API
3
+ require_relative "api/auth_token"
4
+ require_relative "api/help"
5
+ require_relative "api/logical"
6
+ require_relative "api/secret"
7
+ require_relative "api/sys"
8
+ end
9
+ end
@@ -0,0 +1,90 @@
1
+ require "json"
2
+
3
+ require_relative "secret"
4
+ require_relative "../client"
5
+ require_relative "../request"
6
+ require_relative "../response"
7
+
8
+ module Vault
9
+ class Client
10
+ # A proxy to the {AuthToken} methods.
11
+ # @return [AuthToken]
12
+ def auth_token
13
+ @auth_token ||= AuthToken.new(self)
14
+ end
15
+ end
16
+
17
+ class AuthToken < Request
18
+ # Create an authentication token.
19
+ #
20
+ # @example
21
+ # Vault.auth_token.create #=> #<Vault::Secret lease_id="">
22
+ #
23
+ # @param [Hash] options
24
+ #
25
+ # @return [Secret]
26
+ def create(options = {})
27
+ json = client.post("/v1/auth/token/create", JSON.fast_generate(options))
28
+ return Secret.decode(json)
29
+ end
30
+
31
+ # Renew the given authentication token.
32
+ #
33
+ # @example
34
+ # Vault.auth_token.renew("abcd-1234") #=> #<Vault::Secret lease_id="">
35
+ #
36
+ # @param [String] id
37
+ # the auth id
38
+ # @param [Fixnum] increment
39
+ #
40
+ # @return [Secret]
41
+ def renew(id, increment = 0)
42
+ json = client.put("/v1/auth/token/renew/#{id}", JSON.fast_generate(
43
+ increment: increment,
44
+ ))
45
+ return Secret.decode(json)
46
+ end
47
+
48
+ # Revoke exactly the orphans at the id.
49
+ #
50
+ # @example
51
+ # Vault.auth_token.revoke_orphan("abcd-1234") #=> true
52
+ #
53
+ # @param [String] id
54
+ # the auth id
55
+ #
56
+ # @return [true]
57
+ def revoke_orphan(id)
58
+ client.put("/v1/auth/token/revoke-orphan/#{id}", nil)
59
+ return true
60
+ end
61
+
62
+ # Revoke all auth at the given prefix.
63
+ #
64
+ # @example
65
+ # Vault.auth_token.revoke_prefix("abcd-1234") #=> true
66
+ #
67
+ # @param [String] id
68
+ # the auth id
69
+ #
70
+ # @return [true]
71
+ def revoke_prefix(prefix)
72
+ client.put("/v1/auth/token/revoke-prefix/#{prefix}", nil)
73
+ return true
74
+ end
75
+
76
+ # Revoke all auths in the tree.
77
+ #
78
+ # @example
79
+ # Vault.auth_token.revoke_tree("abcd-1234") #=> true
80
+ #
81
+ # @param [String] id
82
+ # the auth id
83
+ #
84
+ # @return [true]
85
+ def revoke_tree(id)
86
+ client.put("/v1/auth/token/revoke/#{id}", nil)
87
+ return true
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,23 @@
1
+ require_relative "../client"
2
+ require_relative "../response"
3
+
4
+ module Vault
5
+ # Help is the response from a help query.
6
+ class Help < Response.new(:help, :see_also); end
7
+
8
+ class Client
9
+ # Gets help for the given path.
10
+ #
11
+ # @example
12
+ # Vault.help #=> #<Vault::Help help="..." see_also="...">
13
+ #
14
+ # @param [String] path
15
+ # the path to get help for
16
+ #
17
+ # @return [Help]
18
+ def help(path)
19
+ json = self.get("/v1/#{path}", help: 1)
20
+ return Help.decode(json)
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,66 @@
1
+ require_relative "secret"
2
+ require_relative "../client"
3
+ require_relative "../request"
4
+ require_relative "../response"
5
+
6
+ module Vault
7
+ class Client
8
+ # A proxy to the {Logical} methods.
9
+ # @return [Logical]
10
+ def logical
11
+ @logical ||= Logical.new(self)
12
+ end
13
+ end
14
+
15
+ class Logical < Request
16
+ # Read the secret at the given path. If the secret does not exist, +nil+
17
+ # will be returned.
18
+ #
19
+ # @example
20
+ # Vault.logical.read("secret/password") #=> #<Vault::Secret lease_id="">
21
+ #
22
+ # @param [String] path
23
+ # the path to read
24
+ #
25
+ # @return [Secret, nil]
26
+ def read(path)
27
+ json = client.get("/v1/#{path}")
28
+ return Secret.decode(json)
29
+ rescue HTTPError => e
30
+ return nil if e.code == 404
31
+ raise
32
+ end
33
+
34
+ # Write the secret at the given path with the given data. Note that the
35
+ # data must be a {Hash}!
36
+ #
37
+ # @example
38
+ # Vault.logical.write("secret/password", value: "secret") #=> #<Vault::Secret lease_id="">
39
+ #
40
+ # @param [String] path
41
+ # the path to write
42
+ # @param [Hash] data
43
+ # the data to write
44
+ #
45
+ # @return [Secret]
46
+ def write(path, data = {})
47
+ client.put("/v1/#{path}", JSON.fast_generate(data))
48
+ return read(path)
49
+ end
50
+
51
+ # Delete the secret at the given path. If the secret does not exist, vault
52
+ # will still return true.
53
+ #
54
+ # @example
55
+ # Vault.logical.delete("secret/password") #=> true
56
+ #
57
+ # @param [String] path
58
+ # the path to delete
59
+ #
60
+ # @return [true]
61
+ def delete(path)
62
+ client.delete("/v1/#{path}")
63
+ return true
64
+ end
65
+ end
66
+ end