conjur-api 4.26.0 → 4.28.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e955d64cd6ce130071bad4809bfe207dc3fb8b6c
4
- data.tar.gz: b0f1cf6e8960332a9a06ec806cbb52866bd39ecc
3
+ metadata.gz: 038dcc4d85e30d07b34ca84dda11196b3cbc8a98
4
+ data.tar.gz: 7b47721f2a9af64c512c6c2fbc5a5644aa5d98e5
5
5
  SHA512:
6
- metadata.gz: 26bf9521c171451e4820aed4636071b735e7876e5b71707377e07e36d450a842d9e627ecd69e2456d8966708b3854c3bfbf9b7a7dea515067007e9ec969862f2
7
- data.tar.gz: cff05fb02234c561bbbfbf8d7130b48a72c741c9bc80db1d540212d02d7f3334b1abc3fdf72dbe6bc60d14a703713550d7670e5ce2f9e1a3f14814a2c37bef82
6
+ metadata.gz: 9d4dc629522aa37aeea760aa16ec9d9df7694754fe5490f45530de68bcba544a9b35de834288dd4576920a92b800be71fe3a03414397be0e24bd06a954929fe0
7
+ data.tar.gz: dafee7953f111a1723a22f9cc0b2a2dacd970e6c10fad46f20458380fb14951ff7ef0e83a09027aa9e3cb20d0fc993a39e6872bfce2fc46e536879ab356f2c73
@@ -1,3 +1,22 @@
1
+ # v4.28.0
2
+
3
+ * Add `Conjur::API#ldap_sync_policy` to fetch the policy to use to
4
+ bring Conjur and the LDAP server into sync.
5
+
6
+ * Remove `Conjur::API#ldap_sync_now` and `Conjur::API#ldap_sync_jobs`
7
+
8
+ # v4.27.0
9
+
10
+ * Add `Conjur::API#resources_permitted?"
11
+
12
+ * `Conjur::API#ldap_sync_now` now accepts an options Hash which will
13
+ be passed on to the `/sync` entrypoint. The old argument list is
14
+ maintained for backwards compatibility.
15
+
16
+ * `Conjur::Api#resources` now supports `:has_annotation` for
17
+ retrieving Conjur resources that have an annotation with the given
18
+ name.
19
+
1
20
  # v4.26.0
2
21
 
3
22
  * expose admin_option in the role graph (only populated by Conjur 4.8 and later)
@@ -17,6 +17,9 @@ Feature: conjur bootstrap
17
17
  Scenario: security_admin group has the expected members
18
18
  Then expressions "$conjur.role('group:security_admin').members.map(&:member).map(&:roleid).sort.join(',')" and "'cucumber:host:conjur/authn-tv,cucumber:host:conjur/expiration,cucumber:host:conjur/ldap-sync,cucumber:host:conjur/policy-loader,cucumber:host:conjur/secrets-rotator,cucumber:user:admin'" are equal
19
19
 
20
+ Scenario: security_admin group can update public keys
21
+ Then expression "$conjur.resource('service:pubkeys-1.0/public-keys').permitted_roles('update')" includes "$conjur.group('security_admin').roleid"
22
+
20
23
  Scenario: security_admin can 'elevate' and 'reveal'
21
24
  Then expression "$conjur.resource('!:!:conjur').permitted_roles('elevate')" includes "$conjur.group('security_admin').roleid"
22
25
  Then expression "$conjur.resource('!:!:conjur').permitted_roles('reveal')" includes "$conjur.group('security_admin').roleid"
@@ -19,6 +19,6 @@
19
19
 
20
20
  module Conjur
21
21
  class API
22
- VERSION = "4.26.0"
22
+ VERSION = "4.28.0"
23
23
  end
24
24
  end
@@ -18,36 +18,74 @@
18
18
  # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19
19
  # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
20
  #
21
+ require 'conjur/ldap_sync_job'
21
22
 
22
23
  module Conjur
23
24
  class API
24
- # @!group LDAP Sync Service
25
+ # @!group LDAP Sync Service
25
26
 
26
- # Trigger a LDAP sync with a given profile.
27
-
28
- # @param [String] config_name Saved profile to run sync with
29
- # @param [Boolean] dry_run Don't actually run sync, instead just report the state of the upstream LDAP.
30
- # @param [String] format Requested MIME type of the response, either 'text/yaml' or 'application/json'
31
- # @return [Hash] a hash mapping with keys 'ok' and 'result[:actions]'
32
- def ldap_sync_now(config_name, format, dry_run)
33
- opts = credentials.dup.tap{ |h|
34
- h[:headers][:accept] = format
27
+ # Fetch a Conjur policy that will bring Conjur into sync with the
28
+ # LDAP server specified by a profile.
29
+ #
30
+ # @param [String] profile the name of the LDAP server profile
31
+ # @param [Hash] options reserved for future use
32
+ def ldap_sync_policy profile, options = {}
33
+
34
+ headers = credentials.dup.tap {|h|
35
+ h[:headers][:accept] = 'text/event-stream'
35
36
  }
36
37
 
37
- dry_run = !!dry_run
38
+ options = options.merge(:config_name => profile)
39
+ url = Conjur.configuration.appliance_url + "/ldap-sync/policy?#{options.to_query}"
38
40
 
39
- resp = RestClient::Resource.new(Conjur.configuration.appliance_url, opts)['ldap-sync']['sync'].post({
40
- config_name: config_name,
41
- dry_run: dry_run
42
- })
43
-
44
- if format == 'text/yaml'
45
- resp.body
46
- elsif format == 'application/json'
47
- JSON.parse(resp.body)
48
- end
41
+ # Even though we're using SSE to return the policy, fetch the
42
+ # whole thing at once into a single response. Retrieving it in
43
+ # chunks doesn't buy us much of anything except more complicated
44
+ # client code.
45
+ response = RestClient::Resource.new(url, headers).get
46
+
47
+ json = if response.headers[:content_type] == 'text/event-stream'
48
+ find_policy_event(response) || find_error_events(response)
49
+ else
50
+ %Q({"error": {"message": "Unexpected response from server: #{response.body}"}})
51
+ end
52
+ JSON.parse(json)
53
+ end
54
+
55
+ # @api private
56
+ # Get an LDAP sync profile.
57
+
58
+ # @param [String] profile name
59
+ # @param [Hash] options reserved
60
+ def ldap_sync_show_profile(profile, options = {})
61
+ resp = RestClient::Resource.new(Conjur.configuration.appliance_url, credentials)['ldap-sync']['config'].get(options)
62
+ JSON.parse(resp.body)
63
+ end
64
+
65
+ # @api private
66
+ # Update an LDAP sync profile.
67
+ #
68
+ # ### Note
69
+ # DO NOT use this method and the UI to update an LDAP sync profile.
70
+ #
71
+ # @param [Hash] profile a hash containing the LDAP sync configuration
72
+ # @param [Hash] options reserved
73
+ def ldap_sync_update_profile(profile, options = {})
74
+ options[:json_config] = profile.to_json
75
+ resp = RestClient::Resource.new(Conjur.configuration.appliance_url, credentials)['ldap-sync']['config'].put(options.to_json, :content_type => 'application/json')
76
+ JSON.parse(resp.body)
77
+ end
78
+
79
+ # @!endgroup
80
+
81
+ private
82
+ def find_policy_event(response)
83
+ response.body.lines.find {|l| l =~ /^data: {"policy":/}.try(:[], 6..-1)
84
+ end
85
+
86
+ def find_error_events(response)
87
+ response.body.lines.collect {|l| l.match(/^data: ({"error":.*)/).try(:[], 1)}.compact.join("\n")
49
88
  end
50
89
 
51
- # @!endgroup
52
90
  end
53
91
  end
@@ -166,5 +166,32 @@ module Conjur
166
166
  def global_privilege_permitted? privilege
167
167
  resource(GLOBAL_PRIVILEGE_RESOURCE).permitted? privilege
168
168
  end
169
+
170
+ # Check to see if the logged-in role has the specified +privilege+
171
+ # on the resources specified by +kind+ and +identifiers+.
172
+ #
173
+ # @example
174
+ # secret1 = api.create_variable 'text/plain', 'secret1', id: 'secret1', value: 'my_first_secret'
175
+ # secret2 = api.create_variable 'text/plain', 'secret2', id: 'secret2', value: 'another_secret'
176
+ # all_permitted, results = api.resources_permitted? 'variable', ['secret1', 'secret2'], 'execute'
177
+
178
+ # @param [String] kind the kind of resources to check
179
+ # @param [Array<String>] identifiers the (unqualified) identifiers of the resources
180
+ # @param [String] privilege the privilege to check for
181
+ # @return [Array] first element is a Boolean, true if all checks passed, false otherwise.
182
+ # If some checks fail, second element is the check result for each resource.
183
+ def resources_permitted? kind, identifiers, privilege
184
+ options = {
185
+ privilege: privilege,
186
+ identifiers: identifiers
187
+ }
188
+ resp = RestClient::Resource.new(Conjur::Authz::API.host, credentials)["#{Conjur.account}/resources/#{kind}?check=true"].post(options)
189
+ if resp.code == 204
190
+ [true, []]
191
+ else
192
+ [false, JSON.parse(resp.body)]
193
+ end
194
+ end
195
+
169
196
  end
170
197
  end
@@ -31,8 +31,8 @@ module Conjur
31
31
  # @param [Array<Conjur::Role, String>, String, Conjur::Role] roles role or or array of roles
32
32
  # roles whose relationships we're interested in
33
33
  # @param [Hash] options options for the request
34
- # @option options [Boolean] :ancestors Whether to return ancestors of the given roles (true by default)
35
- # @option options [Boolean] :descendants Whether to return descendants of the given roles (true by default)
34
+ # @option options [Boolean] :ancestors Whether to return ancestors ("roles that your role has") of the given roles (true by default)
35
+ # @option options [Boolean] :descendants Whether to return descendants ("roles that have your role") of the given roles (true by default)
36
36
  # @option options [Conjur::Role, String] :as_role Only roles visible to this role will be included in the graph
37
37
  # @return [Conjur::Graph] An object representing the role memberships digraph
38
38
  def role_graph roles, options = {}
@@ -84,6 +84,7 @@ module Conjur
84
84
  class Pubkeys < Base
85
85
  def perform
86
86
  find_or_create_record key_managers, security_admin
87
+
87
88
  find_or_create_record pubkeys_layer, security_admin
88
89
  find_or_create_record pubkeys_host, security_admin do |record, options|
89
90
  api.create_host(id: record.id, ownerid: security_admin.roleid)
@@ -91,7 +92,11 @@ module Conjur
91
92
  pubkeys_layer.add_host pubkeys_host unless pubkeys_layer.hosts.map(&:roleid).member?(pubkeys_host.roleid)
92
93
 
93
94
  find_or_create_resource pubkeys_service, security_admin
95
+
94
96
  permit pubkeys_service, 'update', key_managers
97
+
98
+ # also permit security_admin to update public keys
99
+ permit pubkeys_service, 'update', security_admin
95
100
  end
96
101
 
97
102
  def pubkeys_layer
@@ -0,0 +1,89 @@
1
+ require 'conjur/event_source'
2
+
3
+ module Conjur
4
+ class LdapSyncJob
5
+
6
+ attr_reader :hash
7
+
8
+ # Creates a new `LdapSyncJob` from a Hash as returned
9
+ # by the LDAP sync service's `GET /jobs` route.
10
+ def self.new_from_json api, hash
11
+ new(api, hash)
12
+ end
13
+
14
+ def initialize api, hash
15
+ @api = api
16
+ @hash = hash.with_indifferent_access
17
+ end
18
+
19
+ def exclusive?
20
+ self.exclusive
21
+ end
22
+
23
+ def [](k)
24
+ @hash[k]
25
+ end
26
+
27
+ def method_missing(sym, *arguments, &block)
28
+ @hash[sym]
29
+ end
30
+
31
+ # Stop this job (if running) and remove it from the list of jobs.
32
+ def delete
33
+ job_resource.delete
34
+ end
35
+
36
+ # Receive output from this job and pass them to the given block.
37
+ def output &block
38
+ events = []
39
+ wrapper = lambda do |e|
40
+ events << e
41
+ block[e] if block
42
+ end
43
+
44
+ follow_job_output(&wrapper)
45
+
46
+ events
47
+ end
48
+
49
+ def to_s
50
+ "<LdapSyncJob #{id} type=#{type} state=#{state}#{exclusive? ? ' exclusive' : ''}>"
51
+ end
52
+
53
+ def to_h
54
+ @hash
55
+ end
56
+
57
+ alias as_json to_h
58
+
59
+ def to_json _unused
60
+ as_json.to_json
61
+ end
62
+ private
63
+
64
+ def follow_job_output &block
65
+ options = @api.credentials.dup.tap{|h| h[:headers][:accept] = 'text/event-stream'}
66
+
67
+ handle_response = lambda do |response|
68
+ response.error! unless response.code == '200'
69
+ es = EventSource.new
70
+ es.message{ |e| block[e.data] }
71
+
72
+ response.read_body do |chunk|
73
+ es.feed chunk
74
+ end
75
+ end
76
+
77
+ RestClient::Request.execute(
78
+ url: "#{job_resource['output'].url}",
79
+ headers: options[:headers],
80
+ method: :get,
81
+ block_response: handle_response
82
+ )
83
+ end
84
+
85
+ def job_resource
86
+ RestClient::Resource.new(Conjur.configuration.appliance_url, @api.credentials)['ldap-sync']['jobs'][id]
87
+ end
88
+ end
89
+ end
@@ -282,7 +282,7 @@ module Conjur
282
282
 
283
283
  path = "#{account}/resources"
284
284
  path += "/#{kind}" if kind
285
- query = opts.slice(:acting_as, :limit, :offset, :search)
285
+ query = opts.slice(:acting_as, :limit, :offset, :search, :has_annotation)
286
286
  path += "?#{query.to_query}" unless query.empty?
287
287
  resource = RestClient::Resource.new(host, credentials)[path]
288
288
 
@@ -76,8 +76,7 @@ module Conjur
76
76
  # so if `a` is a member of `b`, and `b` is a member of `c`, `a.all` will include `c`.
77
77
  #
78
78
  # ### Permissions
79
- # You must be a member of the role to call this method (note that the `admin` user is
80
- # a member of every role).
79
+ # You must be a member of the role to call this method.
81
80
  #
82
81
  # You can restrict the roles returned to one or more role ids. This feature is mainly useful
83
82
  # for checking whether this role is a member of any of a set of roles.
@@ -92,7 +91,7 @@ module Conjur
92
91
  # # => ["conjur:group:pubkeys-1.0/admin", "conjur:user:alice"]
93
92
  #
94
93
  # @example See if role `"conjur:user:alice"` is a member of either `"conjur:groups:developers"` or `"conjur:group:ops"`
95
- # is_member = not api.role('conjur:user:alice').all(filter: ['conjur:group:developers', 'conjur:group:ops']).empty?
94
+ # is_member = api.role('conjur:user:alice').all(filter: ['conjur:group:developers', 'conjur:group:ops']).any?
96
95
  #
97
96
  # @param [Hash] options options for the request
98
97
  # @option options [String, #roleid, Array<String, #roleid>] :filter only return roles in this list
@@ -12,64 +12,33 @@ require 'helpers/request_helpers'
12
12
 
13
13
  describe Conjur::API, api: :dummy do
14
14
  include RequestHelpers
15
-
16
- let(:appliance_url){ "http://example.com/api" }
17
- let(:ldapsync_url){ "#{appliance_url}/ldap-sync/sync" }
18
- let(:response_json){
19
- {
20
- :okay => true,
21
- :result => {
22
- :actions => [
23
- "Create user 'Guest'\n Set annotation 'ldap-sync/source'\n Set annotation 'ldap-sync/upstream-dn'"
24
- ]
25
- }
26
- }
27
- }
28
- let(:response){ double('response', body: response_json.to_json) }
29
- let(:dry_run) { true }
30
-
31
- before do
32
- allow(Conjur.configuration).to receive(:appliance_url).and_return appliance_url
33
- allow(Conjur::API).to receive_messages(ldap_sync_now: ldapsync_url)
34
- expect_request(
35
- url: ldapsync_url,
36
- method: :post,
37
- headers: credentials[:headers],
38
- payload: {config_name: 'default', dry_run: dry_run}
39
- ).and_return response
40
- end
41
-
42
- describe "#ldap_sync_now" do
43
- it "POSTs /sync" do
44
- api.ldap_sync_now('default', 'application/json', true)
15
+ describe 'LDAP policy methods' do
16
+ let(:appliance_url){ "http://example.com/api" }
17
+ before do
18
+ allow(Conjur.configuration).to receive(:appliance_url).and_return appliance_url
45
19
  end
46
- end
47
20
 
48
- describe "#ldap_sync_now" do
49
- let(:dry_run) { true }
50
- it "POSTs /sync with dry_run set to true" do
51
- api.ldap_sync_now('default', 'application/json', true)
21
+ describe '#ldap_sync_policy' do
22
+ let(:profile) { 'default' }
23
+ let(:url){ "#{appliance_url}/ldap-sync/policy?config_name=#{profile}" }
24
+ let(:policy_event){
25
+ %Q{data: {"policy": "a policy"}}
26
+ }
27
+
28
+ let(:response){ double('response', :body => policy_event, :headers => { :content_type => 'text/event-stream' }) }
29
+ subject{ api.ldap_sync_policy('default') }
30
+ before do
31
+ expect_request(
32
+ url: url,
33
+ method: :get,
34
+ headers: credentials[:headers]
35
+ ).and_return response
36
+ end
37
+
38
+ it 'returns a Hash with a policy' do
39
+ expect(subject).to be_kind_of Hash
40
+ end
52
41
  end
53
- end
54
42
 
55
- describe "#ldap_sync_now" do
56
- let(:dry_run) { false }
57
- it "POSTs /sync with dry_run set to false" do
58
- api.ldap_sync_now('default', 'application/json', false)
59
- end
60
- end
61
-
62
- describe "#ldap_sync_now" do
63
- let(:dry_run) { true }
64
- it "POSTs /sync with truthy dry_run value" do
65
- api.ldap_sync_now('default', 'application/json', 1)
66
- end
67
- end
68
-
69
- describe "#ldap_sync_now" do
70
- let(:dry_run) { false }
71
- it "POSTs /sync with falsey dry_run value" do
72
- api.ldap_sync_now('default', 'application/json', nil)
73
- end
74
43
  end
75
44
  end
@@ -44,4 +44,40 @@ describe Conjur::API, api: :dummy do
44
44
  expect(api.resources(kind: :chunky).map(&:url)).to eql(ids.map { |id| api.resource(id).url })
45
45
  end
46
46
  end
47
+
48
+ describe '#resources_permitted' do
49
+ let(:ids) { %w(foo bar baz) }
50
+ let(:kind) { 'variable' }
51
+ let(:priv) { 'execute' }
52
+
53
+ it 'creates the request correctly' do
54
+ expect_request(
55
+ method: :post,
56
+ url: "#{authz_host}/the-account/resources/#{kind}?check=true",
57
+ payload: {
58
+ :privilege => priv,
59
+ :identifiers => ids
60
+ }
61
+ ).and_return(double("response", :code => 204))
62
+
63
+ res = api.resources_permitted?(kind, ids, priv)
64
+ expect(res[0]).to be(true)
65
+ end
66
+
67
+ it 'signals failure' do
68
+ expect_request(
69
+ method: :post,
70
+ url: "#{authz_host}/the-account/resources/#{kind}?check=true",
71
+ payload: {
72
+ :privilege => priv,
73
+ :identifiers => ids
74
+ }
75
+ ).and_return(double("response", :code => 403, :body => '[]'))
76
+
77
+ res = api.resources_permitted?(kind, ids, priv)
78
+ expect(res[0]).to be(false)
79
+ end
80
+
81
+ end
82
+
47
83
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: conjur-api
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.26.0
4
+ version: 4.28.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rafal Rzepecki
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2016-07-01 00:00:00.000000000 Z
12
+ date: 2016-11-16 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rest-client
@@ -383,6 +383,7 @@ files:
383
383
  - lib/conjur/host_factory_token.rb
384
384
  - lib/conjur/layer-api.rb
385
385
  - lib/conjur/layer.rb
386
+ - lib/conjur/ldap_sync_job.rb
386
387
  - lib/conjur/log.rb
387
388
  - lib/conjur/log_source.rb
388
389
  - lib/conjur/path_based.rb