conjur-policy-parser 0.12.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.
@@ -0,0 +1,36 @@
1
+ module Conjur
2
+ module Policy
3
+ module Types
4
+ class Retire < Base
5
+ attribute :record, kind: :resource
6
+
7
+ self.description = %(
8
+ Move a Role or Resource to the attic.
9
+
10
+ When you no longer need a role or resource in Conjur, you `retire` it.
11
+ This is different than deleting it. When you retire an item, all of
12
+ its memberships and privileges are revoked and its ownership is
13
+ transferred to the `attic` user. This is a special user in Conjur that
14
+ is created when you first bootstrap your Conjur endpoint. By
15
+ retiring rather than deleting items, the integrity of the immutable
16
+ audit log is preserved.
17
+
18
+ You can unretire items by logging in as the
19
+ 'attic' user and transferring their ownership to another role. The
20
+ 'attic' user's API key is stored as a variable in Conjur at
21
+ `conjur/users/attic/api-key`. It is owned by the 'security_admin'
22
+ group. )
23
+
24
+ self.example = %(
25
+ - !retire
26
+ record: !user DoubleOhSeven
27
+ )
28
+
29
+ def to_s
30
+ "Retire #{record}"
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+
@@ -0,0 +1,32 @@
1
+ module Conjur
2
+ module Policy
3
+ module Types
4
+ class Revoke < Base
5
+ attribute :role, dsl_accessor: true
6
+ attribute :member, kind: :role, dsl_accessor: true
7
+
8
+ self.description = %(
9
+ Remove a [Role](#reference/role) grant. (contrast: [Grant](#reference/grant))
10
+
11
+ Some `revoke` operations have additional semantics beyond the role revocation:
12
+
13
+ * When a Layer is revoked from a Host, the automatic roles on the Layer are denied their
14
+ privileges on the Host. Specifically, the `observe` role is denied `read` privilege,
15
+ `use_host` is denied `execute`, and `admin_host` is denied `update`.
16
+
17
+ See also: [role-based access control guide](/key_concepts/rbac.html).
18
+ )
19
+
20
+ self.example = %(
21
+ - !revoke
22
+ role: !group soup_eaters
23
+ member: !user you
24
+ )
25
+
26
+ def to_s
27
+ "Revoke #{role} from #{member}"
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,36 @@
1
+ module Conjur::Policy::Types
2
+ class Update < Base
3
+ attribute :record
4
+
5
+ self.description = %(
6
+ Make explicit changes to an existing record's attributes and/or annotations.
7
+
8
+ For example, you can change annotations on a [Resource](#reference/resource), or the `uidnumber` of a [User](#reference/user).
9
+
10
+ Generally, Update is not used explicitly. Instead, an update is performed by the create-or-replace behavior of
11
+ statements such as User, Group, Host, Layer, etc.
12
+ )
13
+
14
+ self.example = %(
15
+ - !user wizard
16
+ annotations:
17
+ color: gray
18
+
19
+ - !update
20
+ record: !user wizard
21
+ annotations:
22
+ color: white
23
+ )
24
+
25
+ def to_s
26
+ messages = [ "Update #{record}" ]
27
+ (record.custom_attribute_names||[]).each do |k|
28
+ messages.push " Set field '#{k}'"
29
+ end
30
+ (record.annotations||{}).each do |k,v|
31
+ messages.push " Set annotation '#{k}'"
32
+ end
33
+ messages.join("\n")
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,392 @@
1
+ module Conjur
2
+ module Policy
3
+ module YAML
4
+ class Handler < Psych::Handler
5
+ include Conjur::Policy::Logger
6
+
7
+ attr_accessor :parser, :filename, :result
8
+
9
+ # An abstract Base handler. The handler will receive each document message within
10
+ # its particular context (sequence, mapping, etc).
11
+ #
12
+ # The handler can decide that the message is not allowed by not implementing the message.
13
+ #
14
+ class Base
15
+ attr_reader :parent, :anchor
16
+
17
+ def initialize parent, anchor
18
+ @parent = parent
19
+ @anchor = anchor
20
+ end
21
+
22
+ # Handlers are organized in a stack. Each handler can find the root Handler by traversing up the stack.
23
+ def handler
24
+ parent.handler
25
+ end
26
+
27
+ # Push this handler onto the stack.
28
+ def push_handler
29
+ handler.push_handler self
30
+ end
31
+
32
+ # Pop this handler off the stack, indicating that it's complete.
33
+ def pop_handler
34
+ handler.pop_handler
35
+ end
36
+
37
+ # An alias is encountered in the document. The value may be looked up in the root Handler +anchor+ hash.
38
+ def alias anchor
39
+ raise "Unexpected alias #{anchor}"
40
+ end
41
+
42
+ # Start a new mapping with the specified tag.
43
+ # If the handler wants to accept the message, it should return a new handler.
44
+ def start_mapping tag, anchor
45
+ raise "Unexpected mapping"
46
+ end
47
+
48
+ # Start a new sequence.
49
+ # If the handler wants to accept the message, it should return a new handler.
50
+ def start_sequence anchor
51
+ raise "Unexpected sequence"
52
+ end
53
+
54
+ # End the current sequence. The handler should populate the sequence into the parent handler.
55
+ def end_sequence
56
+ raise "Unexpected end of sequence"
57
+ end
58
+
59
+ # End the current mapping. The handler should populate the mapping into the parent handler.
60
+ def end_mapping
61
+ raise "Unexpected end of mapping"
62
+ end
63
+
64
+ # Process a scalar value. It may be a map key, a map value, or a sequence value.
65
+ def scalar value, tag, quoted, anchor
66
+ raise "Unexpected scalar"
67
+ end
68
+
69
+ protected
70
+
71
+ def scalar_value value, tag, quoted, record_type
72
+ if type = type_of(tag, record_type)
73
+ type.new.tap do |record|
74
+ record.id = value
75
+ end
76
+ else
77
+ SafeYAML::Transform.to_guessed_type(value, quoted, SafeYAML::OPTIONS)
78
+ end
79
+ end
80
+
81
+ def type_of tag, record_type
82
+ if tag && tag.match(/!(.*)/)
83
+ type_name = $1.underscore.camelize
84
+ begin
85
+ Conjur::Policy::Types.const_get(type_name)
86
+ rescue NameError
87
+ raise "Unrecognized data type '#{tag}'"
88
+ end
89
+ else
90
+ record_type
91
+ end
92
+ end
93
+ end
94
+
95
+ # Handles the root document, which should be a sequence.
96
+ class Root < Base
97
+ attr_reader :result, :handler
98
+
99
+ def initialize handler
100
+ super nil, nil
101
+
102
+ @handler = handler
103
+ @result = nil
104
+ end
105
+
106
+ def handler; @handler; end
107
+
108
+ def sequence seq
109
+ raise "Already got sequence result" if @result
110
+ @result = seq
111
+ end
112
+
113
+ # The document root is expected to start with a sequence.
114
+ # A Sequence handler is constructed with no implicit type. This
115
+ # sub-handler handles the message.
116
+ def start_sequence anchor
117
+ Sequence.new(self, anchor, nil).tap do |h|
118
+ h.push_handler
119
+ end
120
+ end
121
+
122
+ # Finish the sequence, and the document.
123
+ def end_sequence
124
+ pop_handler
125
+ end
126
+ end
127
+
128
+ # Handles a sequence. The sequence has:
129
+ # +record_type+ default record type, inferred from the field name on the parent record.
130
+ # +args+ the start_sequence arguments.
131
+ class Sequence < Base
132
+ attr_reader :record_type
133
+
134
+ def initialize parent, anchor, record_type
135
+ super parent, anchor
136
+
137
+ @record_type = record_type
138
+ @list = []
139
+ end
140
+
141
+ # Adds a mapping to the sequence.
142
+ def mapping value
143
+ handler.log { "#{handler.indent}Adding mapping #{value} to sequence" }
144
+ @list.push value
145
+ end
146
+
147
+ # Adds a sequence to the sequence.
148
+ def sequence value
149
+ handler.log { "#{handler.indent}Adding sequence #{value} to sequence" }
150
+ @list.push value
151
+ end
152
+
153
+ # When the sequence receives an alias, the alias should be mapped to the previously stored
154
+ # value and added to the result list.
155
+ def alias anchor
156
+ handler.log { "#{handler.indent}Adding alias *#{anchor} to sequence, whose value is #{handler.anchor(anchor)}" }
157
+ @list.push handler.anchor(anchor)
158
+ end
159
+
160
+ # When the sequence contains a mapping, a new record should be created corresponding to either:
161
+ #
162
+ # * The explicit stated type (tag) of the mapping
163
+ # * The implicit field type of the sequence
164
+ #
165
+ # If neither of these is available, it's an error.
166
+ def start_mapping tag, anchor
167
+ if type = type_of(tag, record_type)
168
+ Mapping.new(self, anchor, type).tap do |h|
169
+ h.push_handler
170
+ end
171
+ else
172
+ raise "No type given or inferred for sequence entry"
173
+ end
174
+ end
175
+
176
+ # Process a sequence within a sequence.
177
+ def start_sequence anchor
178
+ Sequence.new(self, anchor, record_type).tap do |h|
179
+ h.push_handler
180
+ end
181
+ end
182
+
183
+ # When the sequence contains a scalar, the value should be appended to the result.
184
+ def scalar value, tag, quoted, anchor
185
+ scalar_value(value, tag, quoted, record_type).tap do |value|
186
+ handler.log { "#{handler.indent}Adding scalar *#{value} to sequence" }
187
+ @list.push value
188
+ handler.anchor anchor, value if anchor
189
+ end
190
+ end
191
+
192
+ def end_sequence
193
+ parent.sequence @list
194
+ handler.anchor anchor, @list if anchor
195
+ pop_handler
196
+ end
197
+ end
198
+
199
+ # Handles a mapping, each of which will be parsed into a structured record.
200
+ class Mapping < Base
201
+ attr_reader :type
202
+
203
+ def initialize parent, anchor, type
204
+ super parent, anchor
205
+
206
+ @record = type.new
207
+ end
208
+
209
+ def map_entry key, value
210
+ handler.log { "#{handler.indent}Setting map entry #{key} = #{value}" }
211
+ if @record.respond_to?(:[]=)
212
+ @record.send(:[]=, key, value)
213
+ else
214
+ begin
215
+ @record.send("#{key}=", value)
216
+ rescue NoMethodError
217
+ raise "No such attribute '#{key}' on type #{@record.class.short_name}"
218
+ end
219
+ end
220
+ end
221
+
222
+ # Begins a mapping with the anchor value as the key.
223
+ def alias anchor
224
+ key = handler.anchor(anchor)
225
+ MapEntry.new(self, nil, @record, key).tap do |h|
226
+ h.push_handler
227
+ end
228
+ end
229
+
230
+ # Begins a new map entry.
231
+ def scalar value, tag, quoted, anchor
232
+ value = scalar_value(value, tag, quoted, type)
233
+ MapEntry.new(self, anchor, @record, value).tap do |h|
234
+ h.push_handler
235
+ end
236
+ end
237
+
238
+ def end_mapping
239
+ parent.mapping @record
240
+ handler.anchor anchor, @record if anchor
241
+ pop_handler
242
+ end
243
+ end
244
+
245
+ # Processes a map entry. At this point, the parent record and the map key are known.
246
+ class MapEntry < Base
247
+ attr_reader :record, :key
248
+
249
+ def initialize parent, anchor, record, key
250
+ super parent, anchor
251
+
252
+ @record = record
253
+ @key = key
254
+ end
255
+
256
+ def sequence value
257
+ value value
258
+ end
259
+
260
+ def mapping value
261
+ value value
262
+ end
263
+
264
+ def value value
265
+ parent.map_entry @key, value
266
+ handler.anchor anchor, value if anchor
267
+ pop_handler
268
+ end
269
+
270
+ # Interpret the alias as the map value and populate in the parent.
271
+ def alias anchor
272
+ value handler.anchor(anchor)
273
+ end
274
+
275
+ # Start a mapping as a map value.
276
+ def start_mapping tag, anchor
277
+ if type = type_of(tag, yaml_field_type(key))
278
+ Mapping.new(self, anchor, type).tap do |h|
279
+ h.push_handler
280
+ end
281
+ else
282
+ # We got a mapping on a simple type
283
+ raise "Attribute '#{key}' can't be a mapping"
284
+ end
285
+ end
286
+
287
+ # Start a sequence as a map value.
288
+ def start_sequence anchor
289
+ Sequence.new(self, anchor, yaml_field_type(key)).tap do |h|
290
+ h.push_handler
291
+ end
292
+ end
293
+
294
+ def scalar value, tag, quoted, anchor
295
+ value scalar_value(value, tag, quoted, yaml_field_type(key))
296
+ end
297
+
298
+ protected
299
+
300
+ def yaml_field_type key
301
+ record.class.respond_to?(:yaml_field_type) ? record.class.yaml_field_type(key) : nil
302
+ end
303
+ end
304
+
305
+ def initialize
306
+ @root = Root.new self
307
+ @handlers = [ @root ]
308
+ @anchors = {}
309
+ @filename = "<no-filename>"
310
+ end
311
+
312
+ def result
313
+ @root.result
314
+ end
315
+
316
+ def push_handler handler
317
+ @handlers.push handler
318
+ log {"#{indent}pushed handler #{handler.class}"}
319
+ end
320
+
321
+ def pop_handler
322
+ @handlers.pop
323
+ log {"#{indent}popped to handler #{handler.class}"}
324
+ end
325
+
326
+ # Get or set an anchor. Invoke with just the anchor name to get the value.
327
+ # Invoke with the anchor name and value to set the value.
328
+ def anchor *args
329
+ key, value, _ = args
330
+ if _
331
+ raise ArgumentError, "Expecting 1 or 2 arguments, got #{args.length}"
332
+ elsif key && value
333
+ raise "Duplicate anchor #{key}" if @anchors[key]
334
+ @anchors[key] = value
335
+ elsif key
336
+ @anchors[key]
337
+ else
338
+ nil
339
+ end
340
+ end
341
+
342
+ def handler; @handlers.last; end
343
+
344
+ def alias key
345
+ log {"#{indent}WARNING: anchor '#{key}' is not defined"} unless anchor(key)
346
+ log {"#{indent}anchor '#{key}'=#{anchor(key)}"}
347
+ handler.alias key
348
+ end
349
+
350
+ def start_mapping *args
351
+ log {"#{indent}start mapping #{args}"}
352
+ anchor, tag, _ = args
353
+ tag = "!automatic-role" if %w(!managed-role !managed_role).include?(tag)
354
+ handler.start_mapping tag, anchor
355
+ end
356
+
357
+ def start_sequence *args
358
+ log {"#{indent}start sequence : #{args}"}
359
+ anchor, _ = args
360
+ handler.start_sequence anchor
361
+ end
362
+
363
+ def end_sequence
364
+ log {"#{indent}end sequence"}
365
+ handler.end_sequence
366
+ end
367
+
368
+ def end_mapping
369
+ log {"#{indent}end mapping"}
370
+ handler.end_mapping
371
+ end
372
+
373
+ def scalar *args
374
+ # value, anchor, tag, plain, quoted, style
375
+ value, anchor, tag, _, quoted = args
376
+ log {"#{indent}got scalar #{tag ? tag + '=' : ''}#{value}#{anchor ? '#' + anchor : ''}"}
377
+ handler.scalar value, tag, quoted, anchor
378
+ end
379
+
380
+ def log &block
381
+ logger.debug('conjur/policy/handler') {
382
+ yield
383
+ }
384
+ end
385
+
386
+ def indent
387
+ " " * [ @handlers.length - 1, 0 ].max
388
+ end
389
+ end
390
+ end
391
+ end
392
+ end