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.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.project +18 -0
- data/.rspec +2 -0
- data/.travis.yml +4 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +41 -0
- data/Rakefile +10 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/ci/test.sh +6 -0
- data/conjur-policy-parser.gemspec +31 -0
- data/jenkins.sh +27 -0
- data/lib/conjur-policy-parser-version.rb +7 -0
- data/lib/conjur-policy-parser.rb +32 -0
- data/lib/conjur/policy/doc.rb +43 -0
- data/lib/conjur/policy/invalid.rb +12 -0
- data/lib/conjur/policy/logger.rb +12 -0
- data/lib/conjur/policy/resolver.rb +262 -0
- data/lib/conjur/policy/types/base.rb +417 -0
- data/lib/conjur/policy/types/create.rb +40 -0
- data/lib/conjur/policy/types/deny.rb +33 -0
- data/lib/conjur/policy/types/give.rb +28 -0
- data/lib/conjur/policy/types/grant.rb +72 -0
- data/lib/conjur/policy/types/include.rb +46 -0
- data/lib/conjur/policy/types/member.rb +37 -0
- data/lib/conjur/policy/types/permit.rb +59 -0
- data/lib/conjur/policy/types/policy.rb +180 -0
- data/lib/conjur/policy/types/records.rb +518 -0
- data/lib/conjur/policy/types/retire.rb +36 -0
- data/lib/conjur/policy/types/revoke.rb +32 -0
- data/lib/conjur/policy/types/update.rb +36 -0
- data/lib/conjur/policy/yaml/handler.rb +392 -0
- data/lib/conjur/policy/yaml/loader.rb +60 -0
- metadata +205 -0
@@ -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
|