conjur-asset-policy 0.12.0 → 0.13.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 732a8fcb5a23935b82fc07265db3be863089812c
4
- data.tar.gz: 784b0e8d3b8046139505bd22e2b7391c3834e77a
3
+ metadata.gz: 6df5a543c035764e90d39bcb0b0c98c46a68b0be
4
+ data.tar.gz: edd5d3520bfc60c055b88f3f5db3d08ecc5efc9a
5
5
  SHA512:
6
- metadata.gz: bd97981df95275d082423425b57a31d298d101350f5660c7bb0172907848fec16028c50bc86d432e6770bd4b114f3fd3630df8d753fd17faff40b548af742877
7
- data.tar.gz: debefb8562d2cea4420b78b268e78732574046db85fd5b01039eb4f4cce79b4fa82ae1930f358761f4e84ae268e87f9fc0f2dc4f4b584a17c86858f73378b9e9
6
+ metadata.gz: cc86b5438e2eb8cd38bbf56bcef6294242030004c3d84a3db3fea4486642c673238a9d93e7eea5b59357d0bd201c442c3f07c48bb7d33ec0f8eab85d9e25bf40
7
+ data.tar.gz: 9ccc36084ce32575faeccbf24cc350ed0a440aa3b96c1ddbece721ab1b75ea9d23d21476efddb8722c75449af566f3f2aa602df9e5ad26d6190f0662fb3ec2e1
@@ -1,3 +1,7 @@
1
+ # 0.13.0
2
+
3
+ * Adds `!include` directive, which can be used to include sub-policies within a master Conjurfile.
4
+
1
5
  # 0.12.0
2
6
 
3
7
  * In annotation values, the string `$namespace` is replaced with the current namespace and policy id.
@@ -1,7 +1,7 @@
1
1
  module Conjur
2
2
  module Asset
3
3
  module Policy
4
- VERSION = "0.12.0"
4
+ VERSION = "0.13.0"
5
5
  end
6
6
  end
7
7
  end
@@ -19,6 +19,7 @@ require 'conjur/api/patches/user'
19
19
  require 'conjur/policy/logger'
20
20
  require 'conjur/policy/invalid'
21
21
  require 'conjur/policy/types/base'
22
+ require 'conjur/policy/types/include'
22
23
  require 'conjur/policy/types/records'
23
24
  require 'conjur/policy/types/member'
24
25
  require 'conjur/policy/types/grant'
@@ -147,7 +147,7 @@ module Conjur
147
147
  end
148
148
 
149
149
  def resolve_owner record, visited
150
- if record.respond_to?(:owner) && record.respond_to?(:owner) && record.owner.nil?
150
+ if record.respond_to?(:owner) && record.owner.nil?
151
151
  record.owner = Types::Role.new(@ownerid)
152
152
  end
153
153
  end
@@ -51,7 +51,7 @@ module Conjur
51
51
  # +test_function+ a class or function which will determine if the value is already the correct type.
52
52
  # +converter+ if the +test_function+ fails, the converter is called to coerce the type.
53
53
  # It should return +nil+ if its unable to do so.
54
- def expect_type value, type_name, test_function, converter = nil
54
+ def expect_type attr_name, value, type_name, test_function, converter = nil
55
55
  if test_function.is_a?(Class)
56
56
  cls = test_function
57
57
  test_function = lambda{ value.is_a?(cls) }
@@ -62,7 +62,7 @@ module Conjur
62
62
  v
63
63
  else
64
64
  name = value.class.respond_to?(:short_name) ? value.class.short_name : value.class.name
65
- raise "Expecting #{type_name}, got #{name}"
65
+ raise "Expected a #{type_name} for field '#{attr_name}', got #{name}"
66
66
  end
67
67
  end
68
68
 
@@ -77,80 +77,86 @@ module Conjur
77
77
  end
78
78
 
79
79
  # If it's a Record
80
- def expect_record value
81
- expect_type value, "Record", lambda{ value.is_a?(Record) }
80
+ def expect_record name, value
81
+ expect_type name, value, "Record", lambda{ value.is_a?(Record) }
82
82
  end
83
83
 
84
84
  # If it's a Layer
85
- def expect_layer value
86
- expect_type value, "Layer", lambda{ value.is_a?(Layer) }
85
+ def expect_layer name, value
86
+ expect_type name, value, "Layer", lambda{ value.is_a?(Layer) }
87
87
  end
88
88
 
89
89
  # If it looks like a resource.
90
- def expect_resource value
91
- expect_type value, "Resource", lambda{ test_resource value }
90
+ def expect_resource name, value
91
+ expect_type name, value, "Resource", lambda{ test_resource value }
92
92
  end
93
93
 
94
94
  # If it looks like a role.
95
- def expect_role value
96
- expect_type value, "Role", lambda{ test_role value }
95
+ def expect_role name, value
96
+ expect_type name, value, "Role", lambda{ test_role value }
97
97
  end
98
98
 
99
99
  # +value+ may be a Member; Roles can also be converted to Members.
100
- def expect_member value
101
- expect_type value,
100
+ def expect_member name, value
101
+ expect_type name,
102
+ value,
102
103
  "Member",
103
104
  Member,
104
105
  lambda{ Member.new(value) if test_role(value) }
105
106
  end
106
107
 
107
108
  # +value+ must be a Permission.
108
- def expect_permission value
109
- expect_type value,
109
+ def expect_permission name, value
110
+ expect_type name,
111
+ value,
110
112
  "Permission",
111
113
  Permission
112
114
  end
113
115
 
114
116
  # +value+ must be a String.
115
- def expect_string value
116
- expect_type value,
117
+ def expect_string name, value
118
+ expect_type name,
119
+ value,
117
120
  "string",
118
121
  String
119
122
  end
120
123
 
121
124
  # +value+ must be a Integer.
122
- def expect_integer value
123
- expect_type value,
125
+ def expect_integer name, value
126
+ expect_type name,
127
+ value,
124
128
  "integer",
125
129
  Integer
126
130
  end
127
131
 
128
132
  # +value+ can be a Hash, or an object which implements +to_h+.
129
- def expect_hash value
130
- expect_type value,
133
+ def expect_hash name, value
134
+ expect_type name,
135
+ value,
131
136
  "hash",
132
137
  lambda{ value.is_a?(Hash)},
133
138
  lambda{ value.to_h.stringify_keys if value.respond_to?(:to_h) }
134
139
  end
135
140
 
136
141
  # +v+ must be +true+ or +false+.
137
- def expect_boolean v
142
+ def expect_boolean name, v
138
143
  v = true if v == "true"
139
144
  v = false if v == "false"
140
- expect_type v,
145
+ expect_type name,
146
+ v,
141
147
  "boolean",
142
148
  lambda{ [ true, false ].member?(v) }
143
149
  end
144
150
 
145
151
  # +values+ can be an instance of +type+ (as determined by the type-checking methods), or
146
152
  # it must be an array of them.
147
- def expect_array kind, values
153
+ def expect_array name, kind, values
148
154
  # Hash gets converted to an array of key/value pairs by Array
149
155
  is_hash = values.kind_of?(Hash)
150
156
  values = [values] if is_hash
151
157
 
152
158
  result = Array(values).map do |v|
153
- send "expect_#{kind}", v
159
+ send "expect_#{kind}", name, v
154
160
  end
155
161
 
156
162
  (values.is_a?(Array) and not is_hash) ? result : result[0]
@@ -181,7 +187,7 @@ module Conjur
181
187
  else
182
188
  v
183
189
  end
184
- self.instance_variable_set("@#{attr}", self.class.expect_array(kind, value))
190
+ self.instance_variable_set("@#{attr}", self.class.expect_array(attr, kind, value))
185
191
  else
186
192
  self.instance_variable_get("@#{attr}")
187
193
  end
@@ -192,7 +198,7 @@ module Conjur
192
198
  end
193
199
  end
194
200
  define_method "#{attr}=" do |v|
195
- self.instance_variable_set("@#{attr}", self.class.expect_array(kind, v))
201
+ self.instance_variable_set("@#{attr}", self.class.expect_array(attr, kind, v))
196
202
  end
197
203
  end
198
204
 
@@ -229,6 +235,7 @@ module Conjur
229
235
  type ||= Conjur::Policy::Types.const_get(attr.to_s.capitalize)
230
236
  rescue NameError
231
237
  end
238
+ type = nil if type == String
232
239
  kind = options[:kind]
233
240
  kind ||= type.short_name.downcase.to_sym if type
234
241
 
@@ -49,18 +49,24 @@ See also: [Permit](#reference/permit) for [Resources](#reference/resource)
49
49
 
50
50
  def to_s
51
51
  role_str = if role.kind_of?(Array)
52
- then role.join(', ')
53
- else role
54
- end
52
+ role.join(', ')
53
+ else
54
+ role
55
+ end
55
56
  member_str = if member.kind_of?(Array)
56
- then member.map(&:role).join(', ')
57
- else member.role
58
- end
59
- admin = if member.kind_of?(Array)
60
- then member.map(&:admin).all?
61
- else member.admin
62
- end
63
- "Grant #{role_str} to #{member_str}#{replace ? ' with replacement ' : ''}#{admin ? ' with admin option' : ''}"
57
+ member.map(&:role).join(', ')
58
+ elsif member
59
+ member.role
60
+ end
61
+ admin = Array(member).map do |member|
62
+ member && member.admin
63
+ end
64
+ admin_str = if Array(member).count == admin.select{|admin| admin}.count
65
+ " with admin option"
66
+ elsif admin.any?
67
+ " with admin options: #{admin.join(', ')}"
68
+ end
69
+ %Q(Grant #{role_str} to #{member_str}#{replace ? ' with replacement ' : ''}#{admin_str})
64
70
  end
65
71
  end
66
72
  end
@@ -0,0 +1,46 @@
1
+ module Conjur::Policy::Types
2
+ # Include another policy into the policy.
3
+ class Include < Base
4
+ attribute :file, kind: :string, type: String, singular: true, dsl_accessor: true
5
+
6
+ self.description = %(
7
+ Includes the contents of another policy file.
8
+
9
+ By using this feature, policies for an entire organization can be
10
+ defined in one source repository, and then unified by a top-level
11
+ "Conjurfile".
12
+
13
+ Attributes:
14
+
15
+ * **file** path to the included policy file, relative to the including policy file.
16
+ This is the default attribute, so it can be specified in shorthand form as:
17
+ `- !include the-policy.yml`
18
+
19
+ Included policies inherit the namespace and owner of the enclosing
20
+ context. To include a policy with a different namespace and owner,
21
+ first define an enclosing policy record with the following attributes:
22
+
23
+ * **id** the name which is appended to the current namespace
24
+ * **owner** the desired owner
25
+
26
+ Then, within the body of that policy, include the additional
27
+ policy files.
28
+ )
29
+
30
+ self.example = %(
31
+ - !include groups.yml
32
+
33
+ - !policy
34
+ id: ops
35
+ owner: !group operations
36
+ body:
37
+ - !include jenkins-master.yml
38
+ - !include ansible.yml
39
+ - !include openvpn.yml
40
+ )
41
+
42
+ def id= value
43
+ self.file = value
44
+ end
45
+ end
46
+ end
@@ -12,17 +12,13 @@ module Conjur
12
12
  # The handler can decide that the message is not allowed by not implementing the message.
13
13
  #
14
14
  class Base
15
- attr_reader :parent
15
+ attr_reader :parent, :anchor
16
16
 
17
- def initialize parent
17
+ def initialize parent, anchor
18
18
  @parent = parent
19
+ @anchor = anchor
19
20
  end
20
21
 
21
- # Each handler should implement this method to return the result object (which may only be
22
- # partially constructed). This method is used by the root handler to associate the handler
23
- # result with an anchor (if applicable).
24
- def result; raise "Not implemented"; end
25
-
26
22
  # Handlers are organized in a stack. Each handler can find the root Handler by traversing up the stack.
27
23
  def handler
28
24
  parent.handler
@@ -45,13 +41,13 @@ module Conjur
45
41
 
46
42
  # Start a new mapping with the specified tag.
47
43
  # If the handler wants to accept the message, it should return a new handler.
48
- def start_mapping tag
44
+ def start_mapping tag, anchor
49
45
  raise "Unexpected mapping"
50
46
  end
51
47
 
52
48
  # Start a new sequence.
53
49
  # If the handler wants to accept the message, it should return a new handler.
54
- def start_sequence
50
+ def start_sequence anchor
55
51
  raise "Unexpected sequence"
56
52
  end
57
53
 
@@ -66,9 +62,7 @@ module Conjur
66
62
  end
67
63
 
68
64
  # Process a scalar value. It may be a map key, a map value, or a sequence value.
69
- # The handler should return a result from this method, so that the root Handler can
70
- # associate it with an anchor, if any.
71
- def scalar value, tag, quoted
65
+ def scalar value, tag, quoted, anchor
72
66
  raise "Unexpected scalar"
73
67
  end
74
68
 
@@ -103,7 +97,7 @@ module Conjur
103
97
  attr_reader :result, :handler
104
98
 
105
99
  def initialize handler
106
- super nil
100
+ super nil, nil
107
101
 
108
102
  @handler = handler
109
103
  @result = nil
@@ -119,10 +113,10 @@ module Conjur
119
113
  # The document root is expected to start with a sequence.
120
114
  # A Sequence handler is constructed with no implicit type. This
121
115
  # sub-handler handles the message.
122
- def start_sequence
123
- Sequence.new(self, nil).tap do |h|
116
+ def start_sequence anchor
117
+ Sequence.new(self, anchor, nil).tap do |h|
124
118
  h.push_handler
125
- end.result
119
+ end
126
120
  end
127
121
 
128
122
  # Finish the sequence, and the document.
@@ -137,28 +131,29 @@ module Conjur
137
131
  class Sequence < Base
138
132
  attr_reader :record_type
139
133
 
140
- def initialize parent, record_type
141
- super parent
134
+ def initialize parent, anchor, record_type
135
+ super parent, anchor
142
136
 
143
137
  @record_type = record_type
144
138
  @list = []
145
139
  end
146
140
 
147
- def result; @list; end
148
-
149
141
  # Adds a mapping to the sequence.
150
142
  def mapping value
143
+ handler.log { "#{handler.indent}Adding mapping #{value} to sequence" }
151
144
  @list.push value
152
145
  end
153
146
 
154
147
  # Adds a sequence to the sequence.
155
148
  def sequence value
149
+ handler.log { "#{handler.indent}Adding sequence #{value} to sequence" }
156
150
  @list.push value
157
151
  end
158
152
 
159
153
  # When the sequence receives an alias, the alias should be mapped to the previously stored
160
154
  # value and added to the result list.
161
155
  def alias anchor
156
+ handler.log { "#{handler.indent}Adding alias *#{anchor} to sequence, whose value is #{handler.anchor(anchor)}" }
162
157
  @list.push handler.anchor(anchor)
163
158
  end
164
159
 
@@ -168,32 +163,35 @@ module Conjur
168
163
  # * The implicit field type of the sequence
169
164
  #
170
165
  # If neither of these is available, it's an error.
171
- def start_mapping tag
166
+ def start_mapping tag, anchor
172
167
  if type = type_of(tag, record_type)
173
- Mapping.new(self, type).tap do |h|
168
+ Mapping.new(self, anchor, type).tap do |h|
174
169
  h.push_handler
175
- end.result
170
+ end
176
171
  else
177
172
  raise "No type given or inferred for sequence entry"
178
173
  end
179
174
  end
180
175
 
181
176
  # Process a sequence within a sequence.
182
- def start_sequence
183
- Sequence.new(self, record_type).tap do |h|
177
+ def start_sequence anchor
178
+ Sequence.new(self, anchor, record_type).tap do |h|
184
179
  h.push_handler
185
- end.result
180
+ end
186
181
  end
187
182
 
188
183
  # When the sequence contains a scalar, the value should be appended to the result.
189
- def scalar value, tag, quoted
184
+ def scalar value, tag, quoted, anchor
190
185
  scalar_value(value, tag, quoted, record_type).tap do |value|
186
+ handler.log { "#{handler.indent}Adding scalar *#{value} to sequence" }
191
187
  @list.push value
188
+ handler.anchor anchor, value if anchor
192
189
  end
193
190
  end
194
191
 
195
192
  def end_sequence
196
193
  parent.sequence @list
194
+ handler.anchor anchor, @list if anchor
197
195
  pop_handler
198
196
  end
199
197
  end
@@ -202,15 +200,14 @@ module Conjur
202
200
  class Mapping < Base
203
201
  attr_reader :type
204
202
 
205
- def initialize parent, type
206
- super parent
203
+ def initialize parent, anchor, type
204
+ super parent, anchor
207
205
 
208
206
  @record = type.new
209
207
  end
210
208
 
211
- def result; @record; end
212
-
213
209
  def map_entry key, value
210
+ handler.log { "#{handler.indent}Setting map entry #{key} = #{value}" }
214
211
  if @record.respond_to?(:[]=)
215
212
  @record.send(:[]=, key, value)
216
213
  else
@@ -225,21 +222,22 @@ module Conjur
225
222
  # Begins a mapping with the anchor value as the key.
226
223
  def alias anchor
227
224
  key = handler.anchor(anchor)
228
- MapEntry.new(self, @record, key).tap do |h|
225
+ MapEntry.new(self, nil, @record, key).tap do |h|
229
226
  h.push_handler
230
- end.result
227
+ end
231
228
  end
232
229
 
233
230
  # Begins a new map entry.
234
- def scalar value, tag, quoted
231
+ def scalar value, tag, quoted, anchor
235
232
  value = scalar_value(value, tag, quoted, type)
236
- MapEntry.new(self, @record, value).tap do |h|
233
+ MapEntry.new(self, anchor, @record, value).tap do |h|
237
234
  h.push_handler
238
- end.result
235
+ end
239
236
  end
240
237
 
241
238
  def end_mapping
242
239
  parent.mapping @record
240
+ handler.anchor anchor, @record if anchor
243
241
  pop_handler
244
242
  end
245
243
  end
@@ -248,15 +246,13 @@ module Conjur
248
246
  class MapEntry < Base
249
247
  attr_reader :record, :key
250
248
 
251
- def initialize parent, record, key
252
- super parent
249
+ def initialize parent, anchor, record, key
250
+ super parent, anchor
253
251
 
254
252
  @record = record
255
253
  @key = key
256
254
  end
257
255
 
258
- def result; nil; end
259
-
260
256
  def sequence value
261
257
  value value
262
258
  end
@@ -267,6 +263,7 @@ module Conjur
267
263
 
268
264
  def value value
269
265
  parent.map_entry @key, value
266
+ handler.anchor anchor, value if anchor
270
267
  pop_handler
271
268
  end
272
269
 
@@ -276,11 +273,11 @@ module Conjur
276
273
  end
277
274
 
278
275
  # Start a mapping as a map value.
279
- def start_mapping tag
276
+ def start_mapping tag, anchor
280
277
  if type = type_of(tag, yaml_field_type(key))
281
- Mapping.new(self, type).tap do |h|
278
+ Mapping.new(self, anchor, type).tap do |h|
282
279
  h.push_handler
283
- end.result
280
+ end
284
281
  else
285
282
  # We got a mapping on a simple type
286
283
  raise "Attribute '#{key}' can't be a mapping"
@@ -288,13 +285,13 @@ module Conjur
288
285
  end
289
286
 
290
287
  # Start a sequence as a map value.
291
- def start_sequence
292
- Sequence.new(self, yaml_field_type(key)).tap do |h|
288
+ def start_sequence anchor
289
+ Sequence.new(self, anchor, yaml_field_type(key)).tap do |h|
293
290
  h.push_handler
294
- end.result
291
+ end
295
292
  end
296
293
 
297
- def scalar value, tag, quoted
294
+ def scalar value, tag, quoted, anchor
298
295
  value scalar_value(value, tag, quoted, yaml_field_type(key))
299
296
  end
300
297
 
@@ -312,9 +309,13 @@ module Conjur
312
309
  @filename = "<no-filename>"
313
310
  end
314
311
 
312
+ def result
313
+ @root.result
314
+ end
315
+
315
316
  def push_handler handler
316
- log {"#{indent}pushing handler #{handler.class}"}
317
317
  @handlers.push handler
318
+ log {"#{indent}pushed handler #{handler.class}"}
318
319
  end
319
320
 
320
321
  def pop_handler
@@ -338,11 +339,10 @@ module Conjur
338
339
  end
339
340
  end
340
341
 
341
- def result; @root.result; end
342
-
343
342
  def handler; @handlers.last; end
344
343
 
345
344
  def alias key
345
+ log {"#{indent}WARNING: anchor '#{key}' is not defined"} unless anchor(key)
346
346
  log {"#{indent}anchor '#{key}'=#{anchor(key)}"}
347
347
  handler.alias key
348
348
  end
@@ -351,15 +351,13 @@ module Conjur
351
351
  log {"#{indent}start mapping #{args}"}
352
352
  anchor, tag, _ = args
353
353
  tag = "!automatic-role" if %w(!managed-role !managed_role).include?(tag)
354
- value = handler.start_mapping tag
355
- anchor anchor, value
354
+ handler.start_mapping tag, anchor
356
355
  end
357
356
 
358
357
  def start_sequence *args
359
358
  log {"#{indent}start sequence : #{args}"}
360
359
  anchor, _ = args
361
- value = handler.start_sequence
362
- anchor anchor, value
360
+ handler.start_sequence anchor
363
361
  end
364
362
 
365
363
  def end_sequence
@@ -376,8 +374,7 @@ module Conjur
376
374
  # value, anchor, tag, plain, quoted, style
377
375
  value, anchor, tag, _, quoted = args
378
376
  log {"#{indent}got scalar #{tag ? tag + '=' : ''}#{value}#{anchor ? '#' + anchor : ''}"}
379
- value = handler.scalar value, tag, quoted
380
- anchor anchor, value
377
+ handler.scalar value, tag, quoted, anchor
381
378
  end
382
379
 
383
380
  def log &block
@@ -6,6 +6,12 @@ module Conjur
6
6
  class Loader
7
7
  class << self
8
8
  def load yaml, filename = nil
9
+ dirname = if filename
10
+ File.dirname(filename)
11
+ else
12
+ '.'
13
+ end
14
+
9
15
  parser = Psych::Parser.new(handler = Handler.new)
10
16
  handler.filename = filename
11
17
  handler.parser = parser
@@ -16,12 +22,31 @@ module Conjur
16
22
  handler.log { $!.backtrace.join(" \n") }
17
23
  raise Invalid.new($!.message || "(no message)", filename, parser.mark)
18
24
  end
19
- handler.result
25
+ records = handler.result
26
+
27
+ parse_includes records, dirname
28
+
29
+ records
20
30
  end
21
31
 
22
32
  def load_file filename
23
33
  load File.read(filename), filename
24
34
  end
35
+
36
+ protected
37
+
38
+ def parse_includes records, dirname
39
+ records.each_with_index do |record, idx|
40
+ if record.is_a?(Array)
41
+ parse_includes record, dirname
42
+ elsif record.is_a?(Types::Policy)
43
+ parse_includes record.body, dirname
44
+ elsif record.is_a?(Types::Include)
45
+ included = load File.read(File.expand_path(record.file, dirname)), record.file
46
+ records[idx..idx] = included
47
+ end
48
+ end
49
+ end
25
50
  end
26
51
  end
27
52
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: conjur-asset-policy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.12.0
4
+ version: 0.13.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kevin Gilpin
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2016-06-20 00:00:00.000000000 Z
11
+ date: 2016-07-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: safe_yaml
@@ -218,6 +218,7 @@ files:
218
218
  - lib/conjur/policy/types/deny.rb
219
219
  - lib/conjur/policy/types/give.rb
220
220
  - lib/conjur/policy/types/grant.rb
221
+ - lib/conjur/policy/types/include.rb
221
222
  - lib/conjur/policy/types/member.rb
222
223
  - lib/conjur/policy/types/permit.rb
223
224
  - lib/conjur/policy/types/policy.rb