conjur-policy-parser 0.12.0

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