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,33 @@
|
|
1
|
+
module Conjur::Policy::Types
|
2
|
+
class Deny < Base
|
3
|
+
|
4
|
+
self.description = %(
|
5
|
+
Deny privilege(s) on a [Resource](#reference/resource) to a role.
|
6
|
+
Once a privilege is denied, permission checks performed by the role
|
7
|
+
will return `false`.
|
8
|
+
|
9
|
+
If the role does not hold the privilege, this statement is a nop.
|
10
|
+
|
11
|
+
See also: [Revoke](#reference/revoke) for [Roles](#reference/role)
|
12
|
+
)
|
13
|
+
|
14
|
+
self.example = %(
|
15
|
+
- !variable secret
|
16
|
+
- !user rando
|
17
|
+
- !deny
|
18
|
+
role: !user rando
|
19
|
+
privilege: read
|
20
|
+
resource: !variable secret
|
21
|
+
)
|
22
|
+
|
23
|
+
attribute :role, kind: :role, dsl_accessor: true
|
24
|
+
attribute :privilege, kind: :string, dsl_accessor: true
|
25
|
+
attribute :resource, dsl_accessor: true
|
26
|
+
|
27
|
+
include ResourceMemberDSL
|
28
|
+
|
29
|
+
def to_s
|
30
|
+
"Deny #{role} to '#{privilege}' #{resource}"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Conjur::Policy::Types
|
2
|
+
class Give < Base
|
3
|
+
attribute :resource, kind: :resource
|
4
|
+
attribute :owner, kind: :role
|
5
|
+
|
6
|
+
self.description = %(
|
7
|
+
Give ownership of a resource to a [Role](#reference/role).
|
8
|
+
|
9
|
+
When the owner role performs a permission check on an owned resource, the
|
10
|
+
result is always `true`.
|
11
|
+
|
12
|
+
[More](/key_concepts/rbac.html) on role-based access control in Conjur.
|
13
|
+
)
|
14
|
+
|
15
|
+
self.example = %(
|
16
|
+
- !user Link
|
17
|
+
- !secret song-of-storms
|
18
|
+
|
19
|
+
- !give
|
20
|
+
resource: !secret song-of-storms
|
21
|
+
owner: !user Link
|
22
|
+
)
|
23
|
+
|
24
|
+
def to_s
|
25
|
+
"Give #{resource} to #{owner}"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module Conjur::Policy::Types
|
2
|
+
class Grant < Base
|
3
|
+
attribute :role, dsl_accessor: true
|
4
|
+
attribute :member
|
5
|
+
attribute :replace, kind: :boolean, singular: true, dsl_accessor: true
|
6
|
+
|
7
|
+
include RoleMemberDSL
|
8
|
+
include AutomaticRoleDSL
|
9
|
+
|
10
|
+
self.description = %(
|
11
|
+
Grant one [Role](#reference/role) to another. When role A is granted to role B,
|
12
|
+
then role B is said to "have" role A. The set of all memberships of role B
|
13
|
+
will include A. The set of direct members of role A will include role B.
|
14
|
+
|
15
|
+
If the role is granted with `admin` option, then the grantee (role B),
|
16
|
+
in addition to having the role, can also grant and revoke the role
|
17
|
+
to other roles.
|
18
|
+
|
19
|
+
The only limitation on role grants is that there may never be a cycle
|
20
|
+
in the role graph. For example, if role A is granted to role B, then role B
|
21
|
+
cannot be granted to role A.
|
22
|
+
|
23
|
+
Several types of Conjur records are roles. For example, Users, Groups,
|
24
|
+
Hosts and Layers are all roles. This means they can be granted to and
|
25
|
+
revoked from each other. For example, when a Group is granted to a User,
|
26
|
+
the User gains all the privileges of the Group. (Note: "Adding" a User to
|
27
|
+
a Group is just another way to say that the Group role is granted to the User).
|
28
|
+
|
29
|
+
Some `grant` operations have additional semantics beyond the role grant:
|
30
|
+
|
31
|
+
* When a Layer is granted to a Host, the automatic roles on the Layer are granted
|
32
|
+
privileges on the Host. Specifically, the `observe` role is given `read` privilege,
|
33
|
+
`use_host` is given `execute`, and `admin_host` is given `update`. The `admin`
|
34
|
+
option is ignored.
|
35
|
+
|
36
|
+
[More](/key_concepts/rbac.html) on role-based access control in Conjur.
|
37
|
+
|
38
|
+
See also: [Permit](#reference/permit) for [Resources](#reference/resource)
|
39
|
+
)
|
40
|
+
|
41
|
+
self.example = %(
|
42
|
+
- !user Link
|
43
|
+
- !user Navi
|
44
|
+
|
45
|
+
- !grant
|
46
|
+
role: !user Navi
|
47
|
+
member: !user Link
|
48
|
+
)
|
49
|
+
|
50
|
+
def to_s
|
51
|
+
role_str = if role.kind_of?(Array)
|
52
|
+
role.join(', ')
|
53
|
+
else
|
54
|
+
role
|
55
|
+
end
|
56
|
+
member_str = if member.kind_of?(Array)
|
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})
|
70
|
+
end
|
71
|
+
end
|
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
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Conjur::Policy::Types
|
2
|
+
class Member < Base
|
3
|
+
def initialize role = nil
|
4
|
+
self.role = role
|
5
|
+
end
|
6
|
+
|
7
|
+
attribute :role
|
8
|
+
attribute :admin, kind: :boolean, singular: true
|
9
|
+
|
10
|
+
self.description = %(
|
11
|
+
Designate the members of a [Role](#reference/role) such as a [Group](#reference/group).
|
12
|
+
|
13
|
+
The member indicates the "grantee" (which role will gain the role grant), as well as the
|
14
|
+
`admin` option which determines whether the grantee can grant/revoke the role to other roles.
|
15
|
+
|
16
|
+
The default value for `admin` is `false`.
|
17
|
+
)
|
18
|
+
|
19
|
+
self.example = %(
|
20
|
+
- !user dee
|
21
|
+
- !user dum
|
22
|
+
- !group brothers
|
23
|
+
|
24
|
+
- !grant
|
25
|
+
role: !group brothers
|
26
|
+
members:
|
27
|
+
- !user dee
|
28
|
+
- !member dum
|
29
|
+
role: !user dum
|
30
|
+
admin: true
|
31
|
+
)
|
32
|
+
|
33
|
+
def to_s
|
34
|
+
"#{role} #{admin ? 'with' : 'without'} admin option"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module Conjur
|
2
|
+
module Policy
|
3
|
+
module Types
|
4
|
+
class Permit < Base
|
5
|
+
attribute :role, kind: :member
|
6
|
+
attribute :privilege, kind: :string, dsl_accessor: true
|
7
|
+
attribute :resource, dsl_accessor: true
|
8
|
+
attribute :replace, kind: :boolean, singular: true, dsl_accessor: true
|
9
|
+
|
10
|
+
self.description = %(
|
11
|
+
Give permissions on a [Resource](#reference/resource) to a [Role](#reference/role).
|
12
|
+
|
13
|
+
Once a privilege is given, permission checks performed by the role
|
14
|
+
will return `true`.
|
15
|
+
|
16
|
+
Note that permissions are not "inherited" in the same way that roles are.
|
17
|
+
If role A is granted to role B, then role B "inherits" all the privileges held
|
18
|
+
by role A. If role A can `execute` a variable, then role B can as well.
|
19
|
+
The privileges on each resource are distinct, regardless of how they are named.
|
20
|
+
If role A has `execute` privilege on a resource called `dev`, the role does **not**
|
21
|
+
gain any privileges on a resource called `dev/password`. Role-based access control
|
22
|
+
is explicit in this way to avoid unintendend side-effects from the way that
|
23
|
+
resources are named.
|
24
|
+
|
25
|
+
[More](/key_concepts/rbac.html) on role-based access control in Conjur.
|
26
|
+
|
27
|
+
See also: [Deny](#reference/deny)
|
28
|
+
)
|
29
|
+
|
30
|
+
self.example = %(
|
31
|
+
- !variable answer
|
32
|
+
- !user deep_thought
|
33
|
+
|
34
|
+
- !permit
|
35
|
+
role: !user deep_thought
|
36
|
+
privileges: [ read, execute, update ]
|
37
|
+
resource: !variable answer
|
38
|
+
)
|
39
|
+
|
40
|
+
include ResourceMemberDSL
|
41
|
+
|
42
|
+
def initialize privilege = nil
|
43
|
+
self.privilege = privilege
|
44
|
+
end
|
45
|
+
|
46
|
+
def to_s
|
47
|
+
if Array === role
|
48
|
+
role_string = role.map &:role
|
49
|
+
admin = false
|
50
|
+
else
|
51
|
+
role_string = role.role
|
52
|
+
admin = role.admin
|
53
|
+
end
|
54
|
+
"Permit #{role_string} to [#{Array(privilege).join(', ')}] on #{Array(resource).join(', ')}#{admin ? ' with grant option' : ''}"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,180 @@
|
|
1
|
+
module Conjur
|
2
|
+
module Policy
|
3
|
+
module Types
|
4
|
+
class YAMLList < Array
|
5
|
+
def tag
|
6
|
+
[ "!", self.class.name.split("::")[-1].underscore ].join
|
7
|
+
end
|
8
|
+
|
9
|
+
def encode_with coder
|
10
|
+
coder.represent_seq tag, self
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
module Tagless
|
15
|
+
def tag; nil; end
|
16
|
+
end
|
17
|
+
|
18
|
+
module CustomStatement
|
19
|
+
def custom_statement handler, &block
|
20
|
+
record = yield
|
21
|
+
class << record
|
22
|
+
include RecordReferenceFactory
|
23
|
+
end
|
24
|
+
push record
|
25
|
+
do_scope record, &handler
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
module Grants
|
30
|
+
include CustomStatement
|
31
|
+
|
32
|
+
def grant &block
|
33
|
+
custom_statement(block) do
|
34
|
+
Conjur::Policy::Types::Grant.new
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def revoke &block
|
39
|
+
custom_statement(block) do
|
40
|
+
Conjur::Policy::Types::Revoke.new
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
module Permissions
|
46
|
+
include CustomStatement
|
47
|
+
|
48
|
+
def permit privilege, &block
|
49
|
+
custom_statement(block) do
|
50
|
+
Conjur::Policy::Types::Permit.new(privilege)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def give &block
|
55
|
+
custom_statement(block) do
|
56
|
+
Conjur::Policy::Types::Give.new
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def retire &block
|
61
|
+
custom_statement(block) do
|
62
|
+
Conjur::Policy::Types::Retire.new
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# Entitlements will allow creation of any record, as well as declaration
|
68
|
+
# of permit, deny, grant and revoke.
|
69
|
+
class Entitlements < YAMLList
|
70
|
+
include Tagless
|
71
|
+
include Grants
|
72
|
+
include Permissions
|
73
|
+
|
74
|
+
def policy id=nil, &block
|
75
|
+
policy = Policy.new
|
76
|
+
policy.id(id) unless id.nil?
|
77
|
+
push policy
|
78
|
+
|
79
|
+
do_scope policy, &block
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
class Body < YAMLList
|
84
|
+
include Grants
|
85
|
+
include Permissions
|
86
|
+
end
|
87
|
+
|
88
|
+
# Policy includes the functionality of Entitlements, wrapped in a
|
89
|
+
# policy role, policy resource, policy id and policy version.
|
90
|
+
class Policy < Record
|
91
|
+
include ActsAsResource
|
92
|
+
include ActsAsRole
|
93
|
+
|
94
|
+
self.description = %(
|
95
|
+
Create a policy. A policy is composed of the following:
|
96
|
+
|
97
|
+
* **id** A unique id, which can be prefixed by a `namespace`.
|
98
|
+
* **body** A set of records such as variables, groups and layers which are "owned" by the policy.
|
99
|
+
|
100
|
+
Under the hood, a Policy is actually a role *and* a resource.
|
101
|
+
The role is a role whose kind is "policy", and it has the specified `id`. By default
|
102
|
+
the policy role is granted, with `admin` option, to the `--as-group` or `--as-role` option which is specified
|
103
|
+
when the policy is loaded. The policy resource is a resource whose kind is "policy", and
|
104
|
+
whose owner is the policy role.
|
105
|
+
|
106
|
+
All the records declared in the `body` of the policy are also owned by the policy role
|
107
|
+
by default. As a result, the role specified by `--as-group` or `--as-role` has full
|
108
|
+
ownership and management of everything defined in the policy.
|
109
|
+
|
110
|
+
Policies should be self-contained; they should not generally make any reference to
|
111
|
+
records from outside the policy. This way, the policy can be loaded with different
|
112
|
+
`--as-group`, `--as-role`, and `--namespace` options to serve different functions in the workflow.
|
113
|
+
For example, if a policy is loaded into the `dev` namespace with `--as-group dev-admin`,
|
114
|
+
then a "dev" version of the policy is created with full management assigned to the `dev-admin` group.
|
115
|
+
|
116
|
+
[See above](#example) for an example of a complete policy.
|
117
|
+
)
|
118
|
+
|
119
|
+
self.example = %(
|
120
|
+
- !policy
|
121
|
+
id: example/v1
|
122
|
+
body:
|
123
|
+
- &secrets
|
124
|
+
- !variable secret
|
125
|
+
|
126
|
+
- !layer
|
127
|
+
|
128
|
+
- !grant
|
129
|
+
role: !layer
|
130
|
+
permissions: [ read, execute ]
|
131
|
+
resources: *secrets
|
132
|
+
)
|
133
|
+
|
134
|
+
def role
|
135
|
+
raise "account is nil" unless account
|
136
|
+
@role ||= Role.new("#{account}:policy:#{id}").tap do |role|
|
137
|
+
role.owner = Role.new(owner.roleid)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def resource
|
142
|
+
raise "account is nil" unless account
|
143
|
+
@resource ||= Resource.new("#{account}:policy:#{id}").tap do |resource|
|
144
|
+
resource.owner = Role.new(role.roleid)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
# Body is handled specially.
|
149
|
+
def referenced_records
|
150
|
+
super - Array(@body)
|
151
|
+
end
|
152
|
+
|
153
|
+
def body &block
|
154
|
+
if block_given?
|
155
|
+
singleton :body, lambda { Body.new }, &block
|
156
|
+
end
|
157
|
+
@body
|
158
|
+
end
|
159
|
+
|
160
|
+
def body= body
|
161
|
+
@body = body
|
162
|
+
end
|
163
|
+
|
164
|
+
protected
|
165
|
+
|
166
|
+
def singleton id, factory, &block
|
167
|
+
object = instance_variable_get("@#{id}")
|
168
|
+
unless object
|
169
|
+
object = factory.call
|
170
|
+
class << object
|
171
|
+
include Tagless
|
172
|
+
end
|
173
|
+
instance_variable_set("@#{id}", object)
|
174
|
+
end
|
175
|
+
do_scope object, &block
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|