chef_fixie_shahid 0.5.2

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,8 @@
1
+
2
+ Fixie.configure do |mapper|
3
+ mapper.authz_uri = "http://localhost:9463"
4
+ mapper.superuser_id = "33d88ba14277f812a3ef3989869fb393"
5
+ mapper.sql_database = 'postgres://opscode_chef:6c6e1a140828be4ac406848e0b6a2ae1e9845e45ac4f48c790168c681c1b4217dc27ab99599130449a2437ab0d2a300bed5d@localhost/opscode_chef'
6
+ end
7
+
8
+
@@ -0,0 +1,28 @@
1
+ #
2
+ # Copyright (c) 2014-2015 Chef Software Inc.
3
+ # License :: Apache License, Version 2.0
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ #
17
+ # Author: Mark Anderson <mark@chef.io>
18
+
19
+ require "sequel"
20
+ require_relative "chef_fixie/config"
21
+ require_relative "chef_fixie/sql"
22
+ require_relative "chef_fixie/sql_objects"
23
+
24
+ # This doesn't work because of initialization order, figure it out.
25
+ require_relative "chef_fixie/check_org_associations"
26
+ require_relative "chef_fixie/bulk_edit_permissions"
27
+
28
+ Sequel.extension :inflector
@@ -0,0 +1,141 @@
1
+ #
2
+ # Copyright (c) 2014-2015 Chef Software Inc.
3
+ # License :: Apache License, Version 2.0
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ #
17
+ # Author: Mark Anderson <mark@chef.io>
18
+ #
19
+
20
+ require "pp"
21
+ require_relative "config"
22
+ require_relative "authz_objects"
23
+
24
+ module ChefFixie
25
+ module AuthzMapper
26
+
27
+ #
28
+ # It would be really awesome if this was integrated with the
29
+ # AuthzObjectMixin so that when it was mixed in, we automatically
30
+ # added code to the reverse mapping
31
+ #
32
+ #
33
+ # Much of this might be better folded up into a sql stored procedure
34
+ #
35
+
36
+ def self.included(base)
37
+ base.extend(ClassMethods)
38
+ end
39
+
40
+ def authz_to_name(authz_id)
41
+ objects = by_authz_id(authz_id).all(1)
42
+ scope = :unknown
43
+ name = :unknown
44
+ if objects.count == 1
45
+ object = objects.first
46
+ name = object.name
47
+ scope =
48
+ if object.respond_to?(:org_id)
49
+ ChefFixie::Sql::Orgs.org_guid_to_name(object.org_id)
50
+ else
51
+ :global
52
+ end
53
+ [scope, name]
54
+ else
55
+ :unknown
56
+ end
57
+ end
58
+
59
+ class ReverseMapper
60
+ attr_reader :names, :by_type, :instance
61
+
62
+ def initialize
63
+ # name of object map
64
+ @names ||= {}
65
+ @by_type ||= { :actor => {}, :container => {}, :group => {}, :object => {} }
66
+ # maps class to a pre-created instance for efficiency
67
+ @instance ||= {}
68
+ end
69
+
70
+ def class_cache(klass)
71
+ instance[klass] ||= klass.new
72
+ end
73
+
74
+ def register(klass, name, type)
75
+ names[name] = klass
76
+ by_type[type][name] = klass
77
+ end
78
+
79
+ def dump
80
+ pp names
81
+ end
82
+
83
+ def authz_to_name(authz_id, ctype = nil)
84
+ types = if ctype.nil?
85
+ AuthzUtils::TYPES
86
+ else
87
+ [ctype]
88
+ end
89
+ types.each do |type|
90
+ by_type[type].each_pair do |name, klass|
91
+ result = class_cache(klass).authz_to_name(authz_id)
92
+ return result if result != :unknown
93
+ end
94
+ end
95
+ :unknown
96
+ end
97
+ end
98
+
99
+ def self.mapper
100
+ @mapper ||= ReverseMapper.new
101
+ end
102
+
103
+ def self.register(klass, name, type)
104
+ mapper.register(klass, name, type)
105
+ end
106
+
107
+ # Translates the json from authz for group membership and acls into a human readable form
108
+ # This makes some assumptions about the shape of the data structure, but works well enough to
109
+ # be quite useful
110
+ def self.struct_to_name(s)
111
+ mapper = AuthzMapper.mapper
112
+ if s.kind_of?(Hash)
113
+ s.keys.inject({}) do |h, k|
114
+ v = s[k]
115
+ if v.kind_of?(Array)
116
+ case k
117
+ when "actors"
118
+ h[k] = v.map { |a| mapper.authz_to_name(a, :actor) } #.sort We should sort these, but the way we're returning unknown causes sort
119
+ when "groups"
120
+ h[k] = v.map { |a| mapper.authz_to_name(a, :group) } #.sort to fail
121
+ else
122
+ h[k] = v
123
+ end
124
+ else
125
+ h[k] = struct_to_name(v)
126
+ end
127
+ h
128
+ end
129
+ end
130
+ end
131
+
132
+ module ClassMethods
133
+ # TODO: We should be able to automatically figure out the type somehow.
134
+ # At minimum should figure out a self check
135
+ def register_authz(name, type)
136
+ AuthzMapper.register(self, name, type)
137
+ end
138
+ end
139
+
140
+ end
141
+ end
@@ -0,0 +1,295 @@
1
+ #
2
+ # Copyright (c) 2014-2015 Chef Software Inc.
3
+ # License :: Apache License, Version 2.0
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ #
17
+ # Author: Mark Anderson <mark@chef.io>
18
+ #
19
+
20
+ require "pp"
21
+ require "ffi_yajl"
22
+ require "chef/http"
23
+
24
+ require_relative "config"
25
+
26
+ module ChefFixie
27
+
28
+ class AuthzApi
29
+ def initialize(user = nil)
30
+ @requestor_authz = user ? user : ChefFixie.configure { |x| x.superuser_id }
31
+ @auth_uri ||= ChefFixie.configure { |x| x.authz_uri }
32
+ @rest = Chef::HTTP.new(@auth_uri)
33
+ end
34
+
35
+ def json_helper(s)
36
+ if s.kind_of?(Hash)
37
+ FFI_Yajl::Encoder.encode(s)
38
+ else
39
+ s
40
+ end
41
+ end
42
+
43
+ def get(resource)
44
+ result = @rest.get(resource,
45
+ "Content-Type" => "application/json",
46
+ "Accept" => "application/json",
47
+ "X-Ops-Requesting-Actor-Id" => @requestor_authz)
48
+ FFI_Yajl::Parser.parse(result)
49
+ end
50
+
51
+ def put(resource, data)
52
+ result = @rest.put(resource, json_helper(data),
53
+ "Content-Type" => "application/json",
54
+ "Accept" => "application/json",
55
+ "X-Ops-Requesting-Actor-Id" => @requestor_authz)
56
+ FFI_Yajl::Parser.parse(result)
57
+ end
58
+
59
+ def post(resource, data)
60
+ result = @rest.post(resource, json_helper(data),
61
+ "Content-Type" => "application/json",
62
+ "Accept" => "application/json",
63
+ "X-Ops-Requesting-Actor-Id" => @requestor_authz)
64
+ FFI_Yajl::Parser.parse(result)
65
+ end
66
+
67
+ def delete(resource)
68
+ result = @rest.delete(resource,
69
+ "Content-Type" => "application/json",
70
+ "Accept" => "application/json",
71
+ "X-Ops-Requesting-Actor-Id" => @requestor_authz)
72
+ FFI_Yajl::Parser.parse(result)
73
+ end
74
+
75
+ end
76
+
77
+ module AuthzUtils
78
+ TYPES = [:object, :actor, :group, :container] # order is an attempt to optimize by most probable.
79
+ ACTIONS = [:create, :read, :update, :delete, :grant]
80
+
81
+ def to_resource(t)
82
+ # This is a rails thing... t.to_s.pluralize
83
+ t.to_s + "s" # hack
84
+ end
85
+
86
+ def get_type(id)
87
+ TYPES.each do |t|
88
+ begin
89
+ r = AuthzApi.get("#{to_resource(t)}/#{id}")
90
+ return t
91
+ rescue RestClient::ResourceNotFound => e
92
+ # expected if not found
93
+ end
94
+ end
95
+ :none
96
+ end
97
+
98
+ def check_action(action)
99
+ # TODO Improve; stack trace isn't the best way to communicate with the user
100
+ raise "#{action} not one of #{ACTIONS.join(', ')} " if !ACTIONS.member?(action)
101
+ end
102
+
103
+ def check_actor_or_group(a_or_g)
104
+ raise "#{a_or_g} not one of :actor or :group" if a_or_g != :actor && a_or_g != :group
105
+ end
106
+
107
+ def resourcify_actor_or_group(a_or_g)
108
+ return a_or_g if %w{actors groups}.member?(a_or_g)
109
+ check_actor_or_group(a_or_g)
110
+ to_resource(a_or_g)
111
+ end
112
+
113
+ def get_authz_id(x)
114
+ return x.authz_id if x.respond_to?(:authz_id)
115
+ # if it quacks like an authz id
116
+ return x if x.is_a?(String) && x =~ /^[[:xdigit:]]{32}$/
117
+ raise "#{x} doesn't look like an authz_id"
118
+ end
119
+ end
120
+
121
+ #
122
+ module AuthzObjectMixin
123
+ include AuthzUtils # reconsider this mixin; maybe better to refer to those routines explictly
124
+
125
+ def self.included(base)
126
+ # pp :note=>"Include", :base=>base, :super=>(base.superclass rescue :nil)
127
+ # block = lambda { :object }
128
+ # base.send(:define_method, :type_me, block )
129
+ # pp :methods=>(base.methods.sort - Object.methods)
130
+ end
131
+
132
+ def type
133
+ :object
134
+ end
135
+
136
+ def authz_api
137
+ @@authz_api_as_superuser ||= AuthzApi.new
138
+ end
139
+
140
+ # we expect to be mixed in with a class that has the authz_id method
141
+ def prefix
142
+ "#{to_resource(type)}/#{authz_id}"
143
+ end
144
+
145
+ def is_authorized(action, actor)
146
+ result = authz_api.get("#{prefix}/acl/#{action}/ace/#{actor.authz_id}")
147
+ [:unparsed, result] # todo figure this out in more detail
148
+ end
149
+
150
+ def authz_delete
151
+ authz_api.delete(prefix)
152
+ end
153
+
154
+ def acl_raw
155
+ authz_api.get("#{prefix}/acl")
156
+ end
157
+
158
+ # Todo: filter this by scope and type
159
+ def acl
160
+ ChefFixie::AuthzMapper.struct_to_name(acl_raw)
161
+ end
162
+
163
+ def ace_get_util(action)
164
+ check_action(action)
165
+
166
+ resource = "#{prefix}/acl/#{action}"
167
+ ace = authz_api.get(resource)
168
+ [resource, ace]
169
+ end
170
+
171
+ def ace_raw(action)
172
+ resource, ace = ace_get_util(action)
173
+ ace
174
+ end
175
+
176
+ # Todo: filter this by scope and type
177
+ def ace(action)
178
+ ChefFixie::AuthzMapper.struct_to_name(ace_raw(action))
179
+ end
180
+
181
+ def expand_actions(action)
182
+ if action == :all
183
+ action = AuthzUtils::ACTIONS
184
+ end
185
+ action.is_a?(Array) ? action : [action]
186
+ end # add actor or group to acl
187
+
188
+ def ace_add_raw(action, actor_or_group, entity)
189
+ # groups or actors
190
+ a_or_g_resource = resourcify_actor_or_group(actor_or_group)
191
+ resource, ace = ace_get_util(action)
192
+
193
+ ace[a_or_g_resource] << get_authz_id(entity)
194
+ ace[a_or_g_resource].uniq!
195
+ authz_api.put("#{resource}", ace)
196
+ end
197
+
198
+ def ace_add(action, entity)
199
+ actions = expand_actions(action)
200
+ actions.each { |a| ace_add_raw(a, entity.type, entity) }
201
+ end
202
+
203
+ def ace_delete_raw(action, actor_or_group, entity)
204
+ # groups or actors
205
+ a_or_g_resource = resourcify_actor_or_group(actor_or_group)
206
+ resource, ace = ace_get_util(action)
207
+
208
+ ace[a_or_g_resource] -= [get_authz_id(entity)]
209
+ ace[a_or_g_resource].uniq!
210
+ authz_api.put("#{resource}", ace)
211
+ end
212
+
213
+ def ace_delete(action, entity)
214
+ actions = expand_actions(action)
215
+ actions.each { |a| ace_delete_raw(a, entity.type, entity) }
216
+ end
217
+
218
+ def ace_member?(action, entity)
219
+ a_or_g_resource = resourcify_actor_or_group(entity.type)
220
+ resource, ace = ace_get_util(action)
221
+ ace[a_or_g_resource].member?(entity.authz_id)
222
+ end
223
+
224
+ def acl_add_from_object(object)
225
+ src = object.acl_raw
226
+
227
+ # this could be made more efficient by refactoring ace_add_raw to split fetch and update, but this works
228
+ src.each do |action, ace|
229
+ ace.each do |type, list|
230
+ list.each do |item|
231
+ ace_add_raw(action.to_sym, type, item)
232
+ end
233
+ end
234
+ end
235
+ end
236
+
237
+ end
238
+
239
+ module AuthzActorMixin
240
+ include AuthzObjectMixin
241
+ def type
242
+ :actor
243
+ end
244
+ end
245
+ module AuthzContainerMixin
246
+ include AuthzObjectMixin
247
+ def type
248
+ :container
249
+ end
250
+ end
251
+ module AuthzGroupMixin
252
+ include AuthzObjectMixin
253
+ def type
254
+ :group
255
+ end
256
+
257
+ # Groups need a little more code to manage members.
258
+ def group_raw
259
+ authz_api.get("#{prefix}")
260
+ end
261
+
262
+ # Todo: filter this by scope and type
263
+ def group
264
+ ChefFixie::AuthzMapper.struct_to_name(group_raw)
265
+ end
266
+
267
+ def list
268
+ group
269
+ end
270
+
271
+ def group_add_raw(actor_or_group, entity)
272
+ entity_resource = to_resource(actor_or_group)
273
+ authz_api.put("#{prefix}/#{entity_resource}/#{entity.authz_id}", {})
274
+ end
275
+
276
+ def group_add(entity)
277
+ group_add_raw(entity.type, entity)
278
+ end
279
+
280
+ def group_delete_raw(actor_or_group, entity)
281
+ entity_resource = to_resource(actor_or_group)
282
+ authz_api.delete("#{prefix}/#{entity_resource}/#{entity.authz_id}")
283
+ end
284
+
285
+ def group_delete(entity)
286
+ group_delete_raw(entity.type, entity)
287
+ end
288
+
289
+ def member?(entity)
290
+ members = group_raw
291
+ members[resourcify_actor_or_group(entity.type)].member?(entity.authz_id)
292
+ end
293
+ end
294
+
295
+ end