conjur-asset-dsl2 0.3.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/.dockerignore +2 -0
- data/.gitignore +14 -0
- data/.project +18 -0
- data/.rspec +1 -0
- data/.travis.yml +4 -0
- data/CHANGELOG +1 -0
- data/Dockerfile.dev +19 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +248 -0
- data/Rakefile +18 -0
- data/backup.tar +0 -0
- data/bin/console +14 -0
- data/bin/setup +7 -0
- data/conjur-asset-dsl2.gemspec +32 -0
- data/jenkins.sh +36 -0
- data/lib/conjur/command/dsl2.rb +175 -0
- data/lib/conjur/dsl2/executor/base.rb +50 -0
- data/lib/conjur/dsl2/executor/create.rb +117 -0
- data/lib/conjur/dsl2/executor/deny.rb +13 -0
- data/lib/conjur/dsl2/executor/give.rb +12 -0
- data/lib/conjur/dsl2/executor/grant.rb +13 -0
- data/lib/conjur/dsl2/executor/permit.rb +16 -0
- data/lib/conjur/dsl2/executor/retire.rb +7 -0
- data/lib/conjur/dsl2/executor/revoke.rb +11 -0
- data/lib/conjur/dsl2/executor/update.rb +31 -0
- data/lib/conjur/dsl2/executor.rb +99 -0
- data/lib/conjur/dsl2/invalid.rb +12 -0
- data/lib/conjur/dsl2/plan.rb +49 -0
- data/lib/conjur/dsl2/planner/base.rb +215 -0
- data/lib/conjur/dsl2/planner/grants.rb +85 -0
- data/lib/conjur/dsl2/planner/permissions.rb +80 -0
- data/lib/conjur/dsl2/planner/record.rb +102 -0
- data/lib/conjur/dsl2/planner.rb +38 -0
- data/lib/conjur/dsl2/ruby/loader.rb +263 -0
- data/lib/conjur/dsl2/types/base.rb +376 -0
- data/lib/conjur/dsl2/types/create.rb +15 -0
- data/lib/conjur/dsl2/types/deny.rb +17 -0
- data/lib/conjur/dsl2/types/give.rb +14 -0
- data/lib/conjur/dsl2/types/grant.rb +24 -0
- data/lib/conjur/dsl2/types/member.rb +14 -0
- data/lib/conjur/dsl2/types/permit.rb +22 -0
- data/lib/conjur/dsl2/types/policy.rb +129 -0
- data/lib/conjur/dsl2/types/records.rb +243 -0
- data/lib/conjur/dsl2/types/retire.rb +14 -0
- data/lib/conjur/dsl2/types/revoke.rb +14 -0
- data/lib/conjur/dsl2/types/update.rb +16 -0
- data/lib/conjur/dsl2/yaml/handler.rb +400 -0
- data/lib/conjur/dsl2/yaml/loader.rb +29 -0
- data/lib/conjur-asset-dsl2-version.rb +7 -0
- data/lib/conjur-asset-dsl2.rb +27 -0
- data/syntax.md +147 -0
- metadata +237 -0
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'conjur/dsl2/planner/base'
|
2
|
+
|
3
|
+
module Conjur
|
4
|
+
module DSL2
|
5
|
+
module Planner
|
6
|
+
class Grant < Base
|
7
|
+
# Plans a role grant.
|
8
|
+
#
|
9
|
+
# The Grant record can list multiple roles and members. Each member should
|
10
|
+
# be granted every role. If the +replace+ option is set, then any existing
|
11
|
+
# grant on a role that is *not* given should be revoked, except for role admins.
|
12
|
+
def do_plan
|
13
|
+
|
14
|
+
roles = Array(record.roles)
|
15
|
+
members = Array(record.members)
|
16
|
+
given_grants = Hash.new { |hash, key| hash[key] = [] }
|
17
|
+
given_admins = Set.new
|
18
|
+
requested_grants = Hash.new { |hash, key| hash[key] = [] }
|
19
|
+
|
20
|
+
# Check all roles / members involved
|
21
|
+
(roles + members.map(&:role)).each do |role|
|
22
|
+
error("role not found: #{scoped_roleid(role)} in #{plan.roles_created.to_a}") unless role_exists?(role)
|
23
|
+
end
|
24
|
+
|
25
|
+
roles.each do |role|
|
26
|
+
grants = begin
|
27
|
+
api.role(scoped_roleid(role)).members
|
28
|
+
rescue RestClient::ResourceNotFound
|
29
|
+
[]
|
30
|
+
end
|
31
|
+
|
32
|
+
grants.each do |grant|
|
33
|
+
member_roleid = grant.member.roleid
|
34
|
+
given_grants[scoped_roleid(role)].push [ member_roleid, grant.admin_option ]
|
35
|
+
given_admins << member_roleid if grant.admin_option
|
36
|
+
end
|
37
|
+
members.each do |member|
|
38
|
+
requested_grants[scoped_roleid(role)].push [ scoped_roleid(member.role), !!member.admin ]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
roles.each do |role|
|
43
|
+
roleid = scoped_roleid(role)
|
44
|
+
given = given_grants[roleid]
|
45
|
+
requested = requested_grants[roleid]
|
46
|
+
|
47
|
+
(Set.new(requested) - Set.new(given)).each do |p|
|
48
|
+
member, admin = p
|
49
|
+
grant = Conjur::DSL2::Types::Grant.new
|
50
|
+
grant.role = role_record roleid
|
51
|
+
grant.member = Conjur::DSL2::Types::Member.new role_record(member)
|
52
|
+
grant.member.admin = true if admin
|
53
|
+
action grant
|
54
|
+
end
|
55
|
+
|
56
|
+
if record.replace
|
57
|
+
(Set.new(given) - Set.new(requested)).each do |p|
|
58
|
+
member, _ = p
|
59
|
+
member_roleid = role_record(member).roleid(account)
|
60
|
+
next if given_admins.member?(member_roleid)
|
61
|
+
revoke = Conjur::DSL2::Types::Revoke.new
|
62
|
+
revoke.role = role_record roleid
|
63
|
+
revoke.member = role_record(member)
|
64
|
+
action revoke
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
class Revoke < Base
|
72
|
+
def do_plan
|
73
|
+
Array(record.roles).each do |role|
|
74
|
+
Array(record.members).each do |member|
|
75
|
+
revoke = Conjur::DSL2::Types::Revoke.new
|
76
|
+
revoke.role = role
|
77
|
+
revoke.member = member
|
78
|
+
action revoke
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'conjur/dsl2/planner/base'
|
2
|
+
require 'set'
|
3
|
+
|
4
|
+
module Conjur
|
5
|
+
module DSL2
|
6
|
+
module Planner
|
7
|
+
# Plans a permission.
|
8
|
+
#
|
9
|
+
# The Permit record can list multiple roles, privileges, and resources. Each privilege should
|
10
|
+
# be allowed to each role on each resource. If the +replace+ option is set, then any existing
|
11
|
+
# privilege on an existing resource that is *not* given should be denied.
|
12
|
+
class Permit < Base
|
13
|
+
def do_plan
|
14
|
+
|
15
|
+
resources = Array(record.resources)
|
16
|
+
privileges = Array(record.privilege)
|
17
|
+
given_permissions = Hash.new { |hash, key| hash[key] = [] }
|
18
|
+
requested_permissions = Hash.new { |hash, key| hash[key] = [] }
|
19
|
+
|
20
|
+
|
21
|
+
|
22
|
+
resources.each do |resource|
|
23
|
+
permissions = begin
|
24
|
+
JSON.parse(api.resource(scoped_resourceid(resource)).get)['permissions']
|
25
|
+
rescue RestClient::ResourceNotFound
|
26
|
+
[]
|
27
|
+
end
|
28
|
+
|
29
|
+
permissions.each do |permission|
|
30
|
+
if privileges.member?(permission['privilege'])
|
31
|
+
given_permissions[[permission['privilege'], permission['resource']]].push [ permission['role'], permission['grant_option'] ]
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
privileges.each do |privilege|
|
36
|
+
Array(record.roles).each do |role|
|
37
|
+
requested_permissions[[privilege, scoped_resourceid(resource)]].push [ scoped_roleid(role.role), !!role.admin ]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
resources.each do |resource|
|
43
|
+
error("resource not found: #{resource}") unless resource_exists?(resource)
|
44
|
+
|
45
|
+
privileges.each do |privilege|
|
46
|
+
|
47
|
+
target = scoped_resourceid(resource)
|
48
|
+
given = given_permissions[[privilege, target]]
|
49
|
+
requested = requested_permissions[[privilege, target]]
|
50
|
+
|
51
|
+
(Set.new(requested) - Set.new(given)).each do |p|
|
52
|
+
role, admin = p
|
53
|
+
|
54
|
+
error("role not found: #{role}") unless role_exists?(role)
|
55
|
+
|
56
|
+
permit = Conjur::DSL2::Types::Permit.new
|
57
|
+
permit.resource = resource_record target
|
58
|
+
permit.privilege = privilege
|
59
|
+
permit.role = Conjur::DSL2::Types::Member.new role_record(role)
|
60
|
+
permit.role.admin = true if admin
|
61
|
+
action permit
|
62
|
+
end
|
63
|
+
|
64
|
+
if record.replace
|
65
|
+
(Set.new(given) - Set.new(requested)).each do |p|
|
66
|
+
role, admin = p
|
67
|
+
deny = Conjur::DSL2::Types::Deny.new
|
68
|
+
deny.resource = resource_record target
|
69
|
+
deny.privilege = privilege
|
70
|
+
deny.role = role_record(role)
|
71
|
+
action deny
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
require 'conjur/dsl2/planner/base'
|
2
|
+
|
3
|
+
module Conjur
|
4
|
+
module DSL2
|
5
|
+
module Planner
|
6
|
+
module ActsAsRecord
|
7
|
+
# Record objects sort before everything else
|
8
|
+
def <=> other
|
9
|
+
other.kind_of?(ActsAsRecord) ? 0 : -1
|
10
|
+
end
|
11
|
+
|
12
|
+
def do_plan
|
13
|
+
if object.exists?
|
14
|
+
update_record
|
15
|
+
else
|
16
|
+
create_record
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_s
|
21
|
+
"<#{self.class.name} #{record.to_s}>"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class Role < Base
|
26
|
+
include ActsAsRecord
|
27
|
+
|
28
|
+
alias object role
|
29
|
+
end
|
30
|
+
|
31
|
+
class Resource < Base
|
32
|
+
include ActsAsRecord
|
33
|
+
|
34
|
+
alias object resource
|
35
|
+
end
|
36
|
+
|
37
|
+
class Record < Base
|
38
|
+
include ActsAsRecord
|
39
|
+
|
40
|
+
def object
|
41
|
+
@object ||= api.send(record.resource_kind, scoped_id(record))
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
class Webservice < Resource
|
46
|
+
end
|
47
|
+
|
48
|
+
class Policy < Base
|
49
|
+
def do_plan
|
50
|
+
role = record.role(default_account)
|
51
|
+
Role.new(role, api).tap do |role|
|
52
|
+
role.plan = plan
|
53
|
+
role.do_plan
|
54
|
+
end
|
55
|
+
|
56
|
+
if record.body.nil?
|
57
|
+
error('missing body element in policy')
|
58
|
+
end
|
59
|
+
|
60
|
+
plan.ownerid = role.roleid(account)
|
61
|
+
resource = record.resource(default_account)
|
62
|
+
if record.annotations
|
63
|
+
resource.annotations = record.annotations
|
64
|
+
end
|
65
|
+
|
66
|
+
Resource.new(resource, api).tap do |resource|
|
67
|
+
resource.plan = plan
|
68
|
+
resource.do_plan
|
69
|
+
end
|
70
|
+
|
71
|
+
planners = record.body.map do |record|
|
72
|
+
Planner.planner_for(record, api)
|
73
|
+
end.sort
|
74
|
+
|
75
|
+
planners.each do |planner|
|
76
|
+
ownerid = plan.ownerid
|
77
|
+
begin
|
78
|
+
plan.policy = self.record
|
79
|
+
|
80
|
+
# Set the ownerid to the namespace-scoped roleid of the policy
|
81
|
+
ownerid = plan.policy.roleid(account)
|
82
|
+
if plan.namespace
|
83
|
+
account, kind, id = ownerid.split(':', 3)
|
84
|
+
ownerid = [ account, kind, [ plan.namespace, id ].join("/") ].join(":")
|
85
|
+
end
|
86
|
+
ownerid = ownerid
|
87
|
+
plan.ownerid = ownerid
|
88
|
+
|
89
|
+
planner.plan = plan
|
90
|
+
planner.trace("planning...")
|
91
|
+
planner.do_plan
|
92
|
+
planner.trace("ok!")
|
93
|
+
ensure
|
94
|
+
plan.policy = nil
|
95
|
+
plan.ownerid = ownerid
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'conjur/dsl2/plan'
|
2
|
+
require 'conjur/dsl2/planner/record'
|
3
|
+
require 'conjur/dsl2/planner/permissions'
|
4
|
+
require 'conjur/dsl2/planner/grants'
|
5
|
+
|
6
|
+
module Conjur
|
7
|
+
module DSL2
|
8
|
+
module Planner
|
9
|
+
class << self
|
10
|
+
def plan records, api, options = {}
|
11
|
+
namespace = options[:namespace]
|
12
|
+
ownerid = options[:ownerid]
|
13
|
+
Plan.new.tap do |plan|
|
14
|
+
plan.namespace = namespace if namespace
|
15
|
+
plan.ownerid = ownerid if ownerid
|
16
|
+
records.map{ |record| planner_for(record, api) }.sort.each do |planner|
|
17
|
+
planner.plan = plan
|
18
|
+
begin
|
19
|
+
planner.do_plan
|
20
|
+
ensure
|
21
|
+
planner.plan = nil
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def planner_for record, api
|
28
|
+
cls = begin
|
29
|
+
const_get record.class.name.split("::")[-1]
|
30
|
+
rescue NameError
|
31
|
+
Record
|
32
|
+
end
|
33
|
+
cls.new record, api
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,263 @@
|
|
1
|
+
class Object
|
2
|
+
# Dear Ruby, I wish instance variables order was stable, because if it was
|
3
|
+
# then YAML would always come out the same.
|
4
|
+
def to_yaml_properties
|
5
|
+
instance_variables.sort
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
module Conjur
|
10
|
+
module DSL2
|
11
|
+
module Ruby
|
12
|
+
module RecordLoader
|
13
|
+
def respond_to_missing? sym, include_all = false
|
14
|
+
super or Conjur::DSL2::Types.const_get sym.to_s.classify rescue nil
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# Implement +method_missing+ to reference basic types like Group, User, Layer, etc.
|
19
|
+
# Anything from Conjur::DSL2::Types is fair game.
|
20
|
+
module RecordReferenceFactory
|
21
|
+
include RecordLoader
|
22
|
+
|
23
|
+
# The record can have a constructor with 0 or 1 arguments. If it takes 1 argument,
|
24
|
+
# it will be populated with the first +args+, if any. It's assumed to be the id.
|
25
|
+
def method_missing sym, *args, &block
|
26
|
+
kind = Conjur::DSL2::Types.const_get sym.to_s.classify rescue nil
|
27
|
+
if kind
|
28
|
+
object = kind.new(*args)
|
29
|
+
raise "#{kind.short_name} is not createable here" unless object.role? || object.resource?
|
30
|
+
handle_object object, &block
|
31
|
+
object
|
32
|
+
else
|
33
|
+
super
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def handle_object object, &block
|
38
|
+
# pass
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Construct record properties in a block and yield the record to the loader.
|
43
|
+
module RecordFactory
|
44
|
+
include RecordReferenceFactory
|
45
|
+
|
46
|
+
def handle_object object, &block
|
47
|
+
push object
|
48
|
+
do_scope object, &block
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
class YAMLList < Array
|
53
|
+
def tag
|
54
|
+
[ "!", self.class.name.split("::")[-1].underscore ].join
|
55
|
+
end
|
56
|
+
|
57
|
+
def encode_with coder
|
58
|
+
coder.represent_seq tag, self
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
module Tagless
|
63
|
+
def tag; nil; end
|
64
|
+
end
|
65
|
+
|
66
|
+
module CustomStatement
|
67
|
+
def custom_statement handler, &block
|
68
|
+
record = yield
|
69
|
+
class << record
|
70
|
+
include RecordReferenceFactory
|
71
|
+
end
|
72
|
+
push record
|
73
|
+
do_scope record, &handler
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
module Grants
|
78
|
+
include CustomStatement
|
79
|
+
|
80
|
+
def grant &block
|
81
|
+
custom_statement(block) do
|
82
|
+
Conjur::DSL2::Types::Grant.new
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def revoke &block
|
87
|
+
custom_statement(block) do
|
88
|
+
Conjur::DSL2::Types::Revoke.new
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
module Permissions
|
94
|
+
include CustomStatement
|
95
|
+
|
96
|
+
def permit privilege, &block
|
97
|
+
custom_statement(block) do
|
98
|
+
Conjur::DSL2::Types::Permit.new(privilege)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def give &block
|
103
|
+
custom_statement(block) do
|
104
|
+
Conjur::DSL2::Types::Give.new
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def retire &block
|
109
|
+
custom_statement(block) do
|
110
|
+
Conjur::DSL2::Types::Retire.new
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
# Entitlements will allow creation of any record, as well as declaration
|
116
|
+
# of permit, deny, grant and revoke.
|
117
|
+
class Entitlements < YAMLList
|
118
|
+
include Tagless
|
119
|
+
include RecordFactory
|
120
|
+
include Grants
|
121
|
+
include Permissions
|
122
|
+
|
123
|
+
def policy id=nil, &block
|
124
|
+
policy = Policy.new
|
125
|
+
policy.id(id) unless id.nil?
|
126
|
+
push policy
|
127
|
+
|
128
|
+
do_scope policy, &block
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
class Body < YAMLList
|
133
|
+
include RecordFactory
|
134
|
+
include Grants
|
135
|
+
include Permissions
|
136
|
+
end
|
137
|
+
|
138
|
+
# Policy includes the functionality of Entitlements, wrapped in a
|
139
|
+
# policy role, policy resource, policy id and policy version.
|
140
|
+
class Policy < Conjur::DSL2::Types::Base
|
141
|
+
include Conjur::DSL2::Types::ActsAsResource
|
142
|
+
include Conjur::DSL2::Types::ActsAsRole
|
143
|
+
|
144
|
+
def body &block
|
145
|
+
singleton :body, lambda { Body.new }, &block
|
146
|
+
@body
|
147
|
+
end
|
148
|
+
|
149
|
+
def body= body
|
150
|
+
@body = body
|
151
|
+
end
|
152
|
+
|
153
|
+
protected
|
154
|
+
|
155
|
+
def singleton id, factory, &block
|
156
|
+
object = instance_variable_get("@#{id}")
|
157
|
+
unless object
|
158
|
+
object = factory.call
|
159
|
+
class << object
|
160
|
+
include Tagless
|
161
|
+
end
|
162
|
+
instance_variable_set("@#{id}", object)
|
163
|
+
end
|
164
|
+
do_scope object, &block
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
module Delegation
|
169
|
+
def respond_to? sym, include_all = false
|
170
|
+
super or scope.respond_to?(sym, include_all)
|
171
|
+
end
|
172
|
+
|
173
|
+
def method_missing(sym, *args, &block)
|
174
|
+
if scope.respond_to?(sym)
|
175
|
+
scope.send sym, *args, &block
|
176
|
+
else
|
177
|
+
raise NoMethodError, "undefined method `#{sym}` for #{scope}:#{scope.class}"
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
class Loader
|
183
|
+
include Delegation
|
184
|
+
|
185
|
+
attr_reader :script, :filename
|
186
|
+
|
187
|
+
class << self
|
188
|
+
def load_file filename
|
189
|
+
load File.read(filename), filename
|
190
|
+
end
|
191
|
+
|
192
|
+
def load yaml, filename = nil
|
193
|
+
create(yaml, filename).load
|
194
|
+
end
|
195
|
+
|
196
|
+
def create(yaml, filename=nil)
|
197
|
+
new(yaml, filename)
|
198
|
+
end
|
199
|
+
|
200
|
+
end
|
201
|
+
|
202
|
+
def initialize script, filename = nil
|
203
|
+
@script = script
|
204
|
+
@filename = filename
|
205
|
+
@scope = []
|
206
|
+
end
|
207
|
+
|
208
|
+
def loader
|
209
|
+
self
|
210
|
+
end
|
211
|
+
|
212
|
+
def load root = nil
|
213
|
+
args = [ script ]
|
214
|
+
args << filename if filename
|
215
|
+
root ||= Entitlements.new
|
216
|
+
do_scope root do
|
217
|
+
instance_eval(*args)
|
218
|
+
end
|
219
|
+
root
|
220
|
+
end
|
221
|
+
|
222
|
+
def push_scope obj
|
223
|
+
@scope.push obj
|
224
|
+
end
|
225
|
+
|
226
|
+
def scope
|
227
|
+
@scope.last
|
228
|
+
end
|
229
|
+
|
230
|
+
def pop_scope
|
231
|
+
@scope.pop
|
232
|
+
end
|
233
|
+
|
234
|
+
def do_scope obj, &block
|
235
|
+
push_scope obj
|
236
|
+
class << obj
|
237
|
+
attr_accessor :loader
|
238
|
+
|
239
|
+
def do_scope obj, &block
|
240
|
+
loader.do_scope obj, &block
|
241
|
+
end
|
242
|
+
|
243
|
+
def scope
|
244
|
+
loader.scope
|
245
|
+
end
|
246
|
+
|
247
|
+
def to_yaml_properties
|
248
|
+
super - [ :"@loader" ]
|
249
|
+
end
|
250
|
+
end
|
251
|
+
obj.loader = self
|
252
|
+
begin
|
253
|
+
yield if block_given?
|
254
|
+
ensure
|
255
|
+
pop_scope
|
256
|
+
end
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
Psych.add_tag "!policy", Policy
|
261
|
+
end
|
262
|
+
end
|
263
|
+
end
|