conjur-api 2.0.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.
Files changed (57) hide show
  1. data/.gitignore +18 -0
  2. data/.project +18 -0
  3. data/.rspec +2 -0
  4. data/.rvmrc +1 -0
  5. data/Gemfile +10 -0
  6. data/LICENSE +22 -0
  7. data/README.md +29 -0
  8. data/Rakefile +20 -0
  9. data/conjur-api.gemspec +28 -0
  10. data/features/enroll_server.feature +26 -0
  11. data/features/login.feature +13 -0
  12. data/features/ping_as_server.feature +16 -0
  13. data/features/ping_as_user.feature +9 -0
  14. data/lib/conjur-api/version.rb +5 -0
  15. data/lib/conjur/acts_as_resource.rb +21 -0
  16. data/lib/conjur/acts_as_role.rb +8 -0
  17. data/lib/conjur/acts_as_user.rb +13 -0
  18. data/lib/conjur/api.rb +22 -0
  19. data/lib/conjur/api/authn.rb +66 -0
  20. data/lib/conjur/api/das.rb +33 -0
  21. data/lib/conjur/api/groups.rb +18 -0
  22. data/lib/conjur/api/hosts.rb +37 -0
  23. data/lib/conjur/api/resources.rb +9 -0
  24. data/lib/conjur/api/roles.rb +18 -0
  25. data/lib/conjur/api/secrets.rb +23 -0
  26. data/lib/conjur/api/users.rb +23 -0
  27. data/lib/conjur/api/variables.rb +25 -0
  28. data/lib/conjur/authn-api.rb +22 -0
  29. data/lib/conjur/authz-api.rb +23 -0
  30. data/lib/conjur/base.rb +50 -0
  31. data/lib/conjur/core-api.rb +26 -0
  32. data/lib/conjur/das-api.rb +22 -0
  33. data/lib/conjur/env.rb +24 -0
  34. data/lib/conjur/escape.rb +31 -0
  35. data/lib/conjur/exists.rb +12 -0
  36. data/lib/conjur/group.rb +11 -0
  37. data/lib/conjur/has_attributes.rb +30 -0
  38. data/lib/conjur/has_id.rb +8 -0
  39. data/lib/conjur/has_identifier.rb +13 -0
  40. data/lib/conjur/host.rb +20 -0
  41. data/lib/conjur/log.rb +52 -0
  42. data/lib/conjur/log_source.rb +13 -0
  43. data/lib/conjur/resource.rb +81 -0
  44. data/lib/conjur/role.rb +52 -0
  45. data/lib/conjur/secret.rb +12 -0
  46. data/lib/conjur/user.rb +13 -0
  47. data/lib/conjur/variable.rb +27 -0
  48. data/spec/lib/api_spec.rb +98 -0
  49. data/spec/lib/das_spec.rb +33 -0
  50. data/spec/lib/resource_spec.rb +84 -0
  51. data/spec/lib/role_spec.rb +24 -0
  52. data/spec/lib/user_spec.rb +33 -0
  53. data/spec/spec_helper.rb +86 -0
  54. data/spec/vcr_cassettes/Conjur_Resource/_create/with_path-like_identifier.yml +87 -0
  55. data/spec/vcr_cassettes/Conjur_Resource/_create/with_un-encoded_path-like_identifier.yml +87 -0
  56. data/spec/vcr_cassettes/Conjur_Resource/_create/with_uuid_identifier.yml +87 -0
  57. metadata +266 -0
@@ -0,0 +1,13 @@
1
+ module Conjur
2
+ module LogSource
3
+ def log(&block)
4
+ if Conjur.log
5
+ Conjur.log << "["
6
+ Conjur.log << username
7
+ Conjur.log << "] "
8
+ yield Conjur.log
9
+ Conjur.log << "\n"
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,81 @@
1
+ module Conjur
2
+ class Resource < RestClient::Resource
3
+ include Exists
4
+ include HasAttributes
5
+
6
+ def kind
7
+ match_path(0..0)
8
+ end
9
+
10
+ def identifier
11
+ match_path(1..-1)
12
+ end
13
+
14
+ def create(options = {})
15
+ log do |logger|
16
+ logger << "Creating resource #{kind} : #{identifier}"
17
+ unless options.empty?
18
+ logger << " with options #{options.to_json}"
19
+ end
20
+ end
21
+ self.put(options)
22
+ end
23
+
24
+ # Lists roles that have a specified permission on the resource.
25
+ def permitted_roles(permission, options = {})
26
+ JSON.parse RestClient::Resource.new(Conjur::Authz::API.host, self.options)["/roles/allowed_to/#{permission}/#{path_escape kind}/#{path_escape identifier}"].get(options)
27
+ end
28
+
29
+ # Changes the owner of a resource
30
+ def give_to(owner, options = {})
31
+ self.put(options.merge(owner: owner))
32
+ end
33
+
34
+ def delete(options = {})
35
+ log do |logger|
36
+ logger << "Deleting resource #{kind} : #{identifier}"
37
+ unless options.empty?
38
+ logger << " with options #{options.to_json}"
39
+ end
40
+ end
41
+ super.delete(options)
42
+ end
43
+
44
+ def permit(privilege, role, options = {})
45
+ eachable(privilege).each do |p|
46
+ log do |logger|
47
+ logger << "Permitting #{p} on resource #{kind} : #{identifier} by #{role}"
48
+ unless options.empty?
49
+ logger << " with options #{options.to_json}"
50
+ end
51
+ end
52
+
53
+ self["?grant&privilege=#{query_escape p}&role=#{query_escape role}"].post(options)
54
+ end
55
+ end
56
+
57
+ def deny(privilege, role, options = {})
58
+ eachable(privilege).each do |p|
59
+ log do |logger|
60
+ logger << "Denying #{p} on resource #{kind} : #{identifier} by #{role}"
61
+ unless options.empty?
62
+ logger << " with options #{options.to_json}"
63
+ end
64
+ end
65
+ self["?revoke&privilege=#{query_escape p}&role=#{query_escape role}"].post(options)
66
+ end
67
+ end
68
+
69
+ protected
70
+
71
+ def eachable(item)
72
+ item.respond_to?(:each) ? item : [ item ]
73
+ end
74
+
75
+ def match_path(range)
76
+ require 'uri'
77
+ tokens = URI.parse(self.url).path[1..-1].split('/')[range]
78
+ tokens.map{|t| URI.unescape(t)}.join('/')
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,52 @@
1
+ module Conjur
2
+ class Role < RestClient::Resource
3
+ include Exists
4
+ include HasId
5
+
6
+ def create(options = {})
7
+ log do |logger|
8
+ logger << "Creating role #{id}"
9
+ unless options.empty?
10
+ logger << " with options #{options.to_json}"
11
+ end
12
+ end
13
+ self.put(options)
14
+ end
15
+
16
+ def all(options = {})
17
+ JSON.parse(self["/all"].get(options)).collect do |id|
18
+ Role.new("#{Conjur::Authz::API.host}/roles/#{path_escape id}", self.options)
19
+ end
20
+ end
21
+
22
+ def grant_to(member, admin_option = false, options = {})
23
+ log do |logger|
24
+ logger << "Granting role #{id} to #{member}"
25
+ if admin_option
26
+ logger << " with admin option"
27
+ end
28
+ unless options.empty?
29
+ logger << " and extended options #{options.to_json}"
30
+ end
31
+ end
32
+ self["/members/#{path_escape member}?admin_option=#{query_escape admin_option}"].put(options)
33
+ end
34
+
35
+ def revoke_from(member, options = {})
36
+ log do |logger|
37
+ logger << "Revoking role #{id} from #{member}"
38
+ unless options.empty?
39
+ logger << " with options #{options.to_json}"
40
+ end
41
+ end
42
+ self["/members/#{path_escape member}"].delete(options)
43
+ end
44
+
45
+ def permitted?(resource_kind, resource_id, privilege, options = {})
46
+ self["/permitted?resource_kind=#{query_escape resource_kind}&resource_id=#{query_escape resource_id}&privilege=#{query_escape privilege}"].get(options)
47
+ true
48
+ rescue RestClient::ResourceNotFound
49
+ false
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,12 @@
1
+ module Conjur
2
+ class Secret < RestClient::Resource
3
+ include ActsAsResource
4
+ include HasAttributes
5
+ include Exists
6
+ include HasId
7
+
8
+ def value
9
+ self['/value'].get.body
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,13 @@
1
+ module Conjur
2
+ class InvalidToken < Exception
3
+ end
4
+
5
+ class User < RestClient::Resource
6
+ include HasId
7
+ include HasAttributes
8
+ include ActsAsResource
9
+ include ActsAsUser
10
+
11
+ alias login id
12
+ end
13
+ end
@@ -0,0 +1,27 @@
1
+ module Conjur
2
+ class Variable < RestClient::Resource
3
+ include ActsAsResource
4
+ include HasAttributes
5
+ include Exists
6
+ include HasId
7
+
8
+ def add_value value
9
+ log do |logger|
10
+ logger << "Adding #{value} to variable #{id}"
11
+ end
12
+ invalidate do
13
+ self['values'].post value: value
14
+ end
15
+ end
16
+
17
+ def version_count
18
+ self.attributes['versions']
19
+ end
20
+
21
+ def value(version = nil)
22
+ url = 'value'
23
+ url << "?version=#{version}" if version
24
+ self[url].get.body
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,98 @@
1
+ require 'spec_helper'
2
+
3
+ require 'conjur/api'
4
+
5
+ shared_examples_for "API endpoint" do
6
+ subject { api }
7
+ let(:service_name) { api.name.split('::')[-2].downcase }
8
+ context "in development" do
9
+ before(:each) do
10
+ Conjur.stub(:env).and_return "development"
11
+ end
12
+ its "default_host" do
13
+ should == "http://localhost:#{Conjur.service_base_port + port_offset}"
14
+ end
15
+ end
16
+ context "in stage" do
17
+ before(:each) do
18
+ Conjur.stub(:env).and_return "stage"
19
+ end
20
+ its "default_host" do
21
+ should == "https://#{service_name}-stage-conjur.herokuapp.com"
22
+ end
23
+ end
24
+ context "in ci" do
25
+ before(:each) do
26
+ Conjur.stub(:env).and_return "ci"
27
+ end
28
+ its "default_host" do
29
+ should == "https://#{service_name}-ci-conjur.herokuapp.com"
30
+ end
31
+ end
32
+ context "in production" do
33
+ before(:each) do
34
+ Conjur.stub(:env).and_return "production"
35
+ end
36
+ its "default_host" do
37
+ should == "https://#{service_name}-v2-conjur.herokuapp.com"
38
+ end
39
+ end
40
+ context "in named production version" do
41
+ before(:each) do
42
+ Conjur.stub(:env).and_return "production"
43
+ Conjur.stub(:stack).and_return "waffle"
44
+ end
45
+ its "default_host" do
46
+ should == "https://#{service_name}-waffle-conjur.herokuapp.com"
47
+ end
48
+ end
49
+ end
50
+
51
+ describe Conjur::API do
52
+ context "host construction" do
53
+ context "of authn service" do
54
+ let(:port_offset) { 0 }
55
+ let(:api) { Conjur::Authn::API }
56
+ it_should_behave_like "API endpoint"
57
+ end
58
+ context "of authz service" do
59
+ let(:port_offset) { 100 }
60
+ let(:api) { Conjur::Authz::API }
61
+ it_should_behave_like "API endpoint"
62
+ end
63
+ context "of das service" do
64
+ let(:port_offset) { 200 }
65
+ let(:api) { Conjur::DAS::API }
66
+ it_should_behave_like "API endpoint"
67
+ end
68
+ context "of core service" do
69
+ let(:port_offset) { 300 }
70
+ let(:api) { Conjur::Core::API }
71
+ it_should_behave_like "API endpoint"
72
+ end
73
+ end
74
+ context ".class" do
75
+ describe '#token_valid?' do
76
+ subject { Conjur::API }
77
+ it "raises KeyError when there's no authn key in the db" do
78
+ require 'slosilo'
79
+ Slosilo.stub(:[]).with(:authn).and_return nil
80
+ expect { subject.token_valid? :whatever }.to raise_error(KeyError)
81
+ end
82
+ end
83
+ end
84
+ context "credential handling" do
85
+ let(:login) { "bob" }
86
+ subject { api }
87
+ context "from token" do
88
+ let(:token) { { 'data' => login } }
89
+ let(:api) { Conjur::API.new_from_token(token) }
90
+ its(:credentials) { should == { headers: { authorization: "Token token=\"#{Base64.strict_encode64(token.to_json)}\"" }, username: login } }
91
+ end
92
+ context "from api key" do
93
+ let(:api_key) { "theapikey" }
94
+ let(:api) { Conjur::API.new_from_key(login, api_key) }
95
+ its(:credentials) { should == { user: login, password: api_key } }
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,33 @@
1
+ require 'spec_helper'
2
+
3
+ require 'conjur/api'
4
+
5
+ describe Conjur::API do
6
+ context "data_access_service_url" do
7
+ let(:account) { "the-account" }
8
+ let(:path) { "upload" }
9
+ subject { Conjur::API.data_access_service_url(account, path, params) }
10
+ context "to test environment" do
11
+ before(:each) do
12
+ Conjur.stub(:env).and_return "development"
13
+ end
14
+ context "with empty params" do
15
+ let(:params) { {} }
16
+ it { should == "http://localhost:5200/data/the-account/inscitiv/upload" }
17
+ end
18
+ context "with params" do
19
+ let(:params) { { "foo" => "b/r" } }
20
+ it { should == "http://localhost:5200/data/the-account/inscitiv/upload?foo=b%2Fr" }
21
+ end
22
+ end
23
+ context "to production environment" do
24
+ before(:each) do
25
+ Conjur.stub(:env).and_return "production"
26
+ end
27
+ context "with empty params" do
28
+ let(:params) { {} }
29
+ it { should == "https://das-v2-conjur.herokuapp.com/data/the-account/inscitiv/upload" }
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,84 @@
1
+ require 'spec_helper'
2
+
3
+ require 'conjur/api'
4
+
5
+ describe Conjur::Resource do
6
+ let(:user) { 'admin' }
7
+ let(:api_key) { '^6feWZpr' }
8
+
9
+ def conjur_api
10
+ Conjur::API.new_from_key(user, api_key)
11
+ end
12
+
13
+ def self.it_creates_with code
14
+ it "should create with status #{code}" do
15
+ resource = conjur_api.resource("spec", identifier)
16
+ resource.create
17
+ resource.should exist
18
+ conjur_api.resource("spec", identifier).kind.should == "spec"
19
+ conjur_api.resource("spec", identifier).identifier.should == identifier
20
+ end
21
+ end
22
+
23
+ def self.it_fails_with code
24
+ it "should fail with status #{code}" do
25
+ expect { conjur_api.resource("spec", identifier).create }.to raise_error { |error|
26
+ error.should be_a(RestClient::Exception)
27
+ error.http_code.should == code
28
+ }
29
+ end
30
+ end
31
+
32
+ let(:uuid) { "ddd1f59a-494d-48fb-b045-0374c4a6eef9" }
33
+
34
+ context "identifier" do
35
+ include Conjur::Escape
36
+ let(:resource) { Conjur::Resource.new("#{Conjur::Authz::API.host}/#{kind}/#{path_escape identifier}") }
37
+
38
+ context "Object with an #id" do
39
+ let(:kind) { "host" }
40
+ let(:identifier) do
41
+ Conjur::Host.new("#{Conjur::Core::API.host}/hosts/foobar", {})
42
+ end
43
+ it "identifier should obtained from the id" do
44
+ resource.identifier.should == "foobar"
45
+ end
46
+ end
47
+
48
+ [ [ "foo", "bar/baz" ], [ "f:o", "bar" ], [ "@f", "bar.baz" ], [ "@f", "bar baz" ], [ "@f", "bar?baz" ] ].each do |p|
49
+ context "of /#{p[0]}/#{p[1]}" do
50
+ let(:kind) { p[0] }
51
+ let(:identifier) { p[1] }
52
+ context "resource_kind" do
53
+ subject { resource.kind }
54
+ specify { should == p[0] }
55
+ end
56
+ context "resource_id" do
57
+ subject { resource.identifier }
58
+ specify { should == ( p[1] ) }
59
+ end
60
+ end
61
+ end
62
+ end
63
+ context "#create" do
64
+ context "with uuid identifier" do
65
+ use_vcr_cassette
66
+ let(:identifier) { uuid }
67
+ it_creates_with 204
68
+ it "is findable" do
69
+ conjur_api.resource("spec", identifier).create
70
+ conjur_api.resource("spec", identifier).should exist
71
+ end
72
+ end
73
+ context "with path-like identifier" do
74
+ use_vcr_cassette
75
+ let(:identifier) { [ uuid, "xxx" ].join("/") }
76
+ it_creates_with 204
77
+ end
78
+ context "with un-encoded path-like identifier" do
79
+ use_vcr_cassette
80
+ let(:identifier) { [ uuid, "+?!!?+/xxx" ].join("/") }
81
+ it_creates_with 204
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+
3
+ require 'conjur/api'
4
+
5
+ shared_examples_for "properties" do
6
+ subject { role }
7
+ its(:id) { should == id }
8
+ end
9
+
10
+ describe Conjur::Role do
11
+ context "#new" do
12
+ let(:url) { "#{Conjur::Authz::API.host}/roles/#{id}" }
13
+ let(:credentials) { mock(:credentials) }
14
+ let(:role) { Conjur::Role.new(url, credentials) }
15
+ context "with plain id" do
16
+ let(:id) { "foo" }
17
+ it_should_behave_like "properties"
18
+ end
19
+ context "with more complex id" do
20
+ let(:id) { "@foo;bar" }
21
+ it_should_behave_like "properties"
22
+ end
23
+ end
24
+ end