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,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