chef_fixie_shahid 0.5.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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