conjur-api 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
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