conjur-api 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +18 -0
- data/.project +18 -0
- data/.rspec +2 -0
- data/.rvmrc +1 -0
- data/Gemfile +10 -0
- data/LICENSE +22 -0
- data/README.md +29 -0
- data/Rakefile +20 -0
- data/conjur-api.gemspec +28 -0
- data/features/enroll_server.feature +26 -0
- data/features/login.feature +13 -0
- data/features/ping_as_server.feature +16 -0
- data/features/ping_as_user.feature +9 -0
- data/lib/conjur-api/version.rb +5 -0
- data/lib/conjur/acts_as_resource.rb +21 -0
- data/lib/conjur/acts_as_role.rb +8 -0
- data/lib/conjur/acts_as_user.rb +13 -0
- data/lib/conjur/api.rb +22 -0
- data/lib/conjur/api/authn.rb +66 -0
- data/lib/conjur/api/das.rb +33 -0
- data/lib/conjur/api/groups.rb +18 -0
- data/lib/conjur/api/hosts.rb +37 -0
- data/lib/conjur/api/resources.rb +9 -0
- data/lib/conjur/api/roles.rb +18 -0
- data/lib/conjur/api/secrets.rb +23 -0
- data/lib/conjur/api/users.rb +23 -0
- data/lib/conjur/api/variables.rb +25 -0
- data/lib/conjur/authn-api.rb +22 -0
- data/lib/conjur/authz-api.rb +23 -0
- data/lib/conjur/base.rb +50 -0
- data/lib/conjur/core-api.rb +26 -0
- data/lib/conjur/das-api.rb +22 -0
- data/lib/conjur/env.rb +24 -0
- data/lib/conjur/escape.rb +31 -0
- data/lib/conjur/exists.rb +12 -0
- data/lib/conjur/group.rb +11 -0
- data/lib/conjur/has_attributes.rb +30 -0
- data/lib/conjur/has_id.rb +8 -0
- data/lib/conjur/has_identifier.rb +13 -0
- data/lib/conjur/host.rb +20 -0
- data/lib/conjur/log.rb +52 -0
- data/lib/conjur/log_source.rb +13 -0
- data/lib/conjur/resource.rb +81 -0
- data/lib/conjur/role.rb +52 -0
- data/lib/conjur/secret.rb +12 -0
- data/lib/conjur/user.rb +13 -0
- data/lib/conjur/variable.rb +27 -0
- data/spec/lib/api_spec.rb +98 -0
- data/spec/lib/das_spec.rb +33 -0
- data/spec/lib/resource_spec.rb +84 -0
- data/spec/lib/role_spec.rb +24 -0
- data/spec/lib/user_spec.rb +33 -0
- data/spec/spec_helper.rb +86 -0
- data/spec/vcr_cassettes/Conjur_Resource/_create/with_path-like_identifier.yml +87 -0
- data/spec/vcr_cassettes/Conjur_Resource/_create/with_un-encoded_path-like_identifier.yml +87 -0
- data/spec/vcr_cassettes/Conjur_Resource/_create/with_uuid_identifier.yml +87 -0
- metadata +266 -0
@@ -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
|
data/lib/conjur/role.rb
ADDED
@@ -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
|
data/lib/conjur/user.rb
ADDED
@@ -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
|