chef_fixie 0.1.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.
Files changed (61) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +201 -0
  3. data/README.md +69 -0
  4. data/bin/bundler +16 -0
  5. data/bin/chef-apply +16 -0
  6. data/bin/chef-client +16 -0
  7. data/bin/chef-shell +16 -0
  8. data/bin/chef-solo +16 -0
  9. data/bin/chef-zero +16 -0
  10. data/bin/chef_fixie +5 -0
  11. data/bin/coderay +16 -0
  12. data/bin/edit_json.rb +16 -0
  13. data/bin/erubis +16 -0
  14. data/bin/ffi-yajl-bench +16 -0
  15. data/bin/fixie~ +231 -0
  16. data/bin/htmldiff +16 -0
  17. data/bin/knife +16 -0
  18. data/bin/ldiff +16 -0
  19. data/bin/net-dhcp +16 -0
  20. data/bin/ohai +16 -0
  21. data/bin/prettify_json.rb +16 -0
  22. data/bin/pry +16 -0
  23. data/bin/rackup +16 -0
  24. data/bin/rake +16 -0
  25. data/bin/rdoc +16 -0
  26. data/bin/restclient +16 -0
  27. data/bin/ri +16 -0
  28. data/bin/rspec +16 -0
  29. data/bin/s3sh +16 -0
  30. data/bin/sequel +16 -0
  31. data/bin/serverspec-init +16 -0
  32. data/doc/AccessingSQL.md +36 -0
  33. data/doc/AccessingSQL.md~ +32 -0
  34. data/doc/BulkFixup.md~ +28 -0
  35. data/doc/CommonTasks.md +20 -0
  36. data/doc/CommonTasks.md~ +0 -0
  37. data/doc/GETTING_STARTED.md +228 -0
  38. data/doc/GETTING_STARTED.md~ +6 -0
  39. data/fixie.conf.example +8 -0
  40. data/lib/chef_fixie.rb +27 -0
  41. data/lib/chef_fixie/authz_mapper.rb +143 -0
  42. data/lib/chef_fixie/authz_objects.rb +285 -0
  43. data/lib/chef_fixie/check_org_associations.rb +242 -0
  44. data/lib/chef_fixie/config.rb +139 -0
  45. data/lib/chef_fixie/console.rb +91 -0
  46. data/lib/chef_fixie/context.rb +72 -0
  47. data/lib/chef_fixie/sql.rb +74 -0
  48. data/lib/chef_fixie/sql_objects.rb +497 -0
  49. data/lib/chef_fixie/utility_helpers.rb +59 -0
  50. data/lib/chef_fixie/version.rb +3 -0
  51. data/spec/chef_fixie/acl_spec.rb +83 -0
  52. data/spec/chef_fixie/assoc_invite_spec.rb +47 -0
  53. data/spec/chef_fixie/assoc_invite_spec.rb~ +26 -0
  54. data/spec/chef_fixie/check_org_associations_spec.rb +140 -0
  55. data/spec/chef_fixie/check_org_associations_spec.rb~ +34 -0
  56. data/spec/chef_fixie/groups_spec.rb +34 -0
  57. data/spec/chef_fixie/org_spec.rb +26 -0
  58. data/spec/chef_fixie/org_spec.rb~ +53 -0
  59. data/spec/chef_fixie/orgs_spec.rb +53 -0
  60. data/spec/spec_helper.rb +41 -0
  61. metadata +252 -0
@@ -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
+
data/lib/chef_fixie.rb ADDED
@@ -0,0 +1,27 @@
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 'chef_fixie/config'
21
+ require 'chef_fixie/sql'
22
+ require 'chef_fixie/sql_objects'
23
+
24
+ # This doesn't work because of initialization order, figure it out.
25
+ require 'chef_fixie/check_org_associations'
26
+
27
+ Sequel.extension :inflector
@@ -0,0 +1,143 @@
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 'chef_fixie/config'
22
+ require 'chef_fixie/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
+ return :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
+ self.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] = self.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
142
+
143
+
@@ -0,0 +1,285 @@
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 'chef_fixie/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
+ def put(resource, data)
51
+ result = @rest.put(resource, self.json_helper(data),
52
+ 'Content-Type'=>'application/json',
53
+ 'Accept'=>'application/json',
54
+ 'X-Ops-Requesting-Actor-Id'=>@requestor_authz)
55
+ FFI_Yajl::Parser.parse(result)
56
+ end
57
+ def post(resource, data)
58
+ result = @rest.post(resource, self.json_helper(data),
59
+ 'Content-Type'=>'application/json',
60
+ 'Accept'=>'application/json',
61
+ 'X-Ops-Requesting-Actor-Id'=>@requestor_authz)
62
+ FFI_Yajl::Parser.parse(result)
63
+ end
64
+ def delete(resource)
65
+ result = @rest.delete(resource,
66
+ 'Content-Type'=>'application/json',
67
+ 'Accept'=>'application/json',
68
+ 'X-Ops-Requesting-Actor-Id'=>@requestor_authz)
69
+ FFI_Yajl::Parser.parse(result)
70
+ end
71
+
72
+ end
73
+
74
+ module AuthzUtils
75
+ Types = [:object,:actor,:group,:container] # order is an attempt to optimize by most probable.
76
+ Actions = [:create, :read, :update, :delete, :grant]
77
+
78
+ def to_resource(t)
79
+ # This is a rails thing... t.to_s.pluralize
80
+ t.to_s + "s" # hack
81
+ end
82
+
83
+ def get_type(id)
84
+ Types.each do |t|
85
+ begin
86
+ r = AuthzApi.get("#{self.to_resource(t)}/#{id}")
87
+ return t
88
+ rescue RestClient::ResourceNotFound=>e
89
+ # expected if not found
90
+ end
91
+ end
92
+ return :none
93
+ end
94
+
95
+ def check_action(action)
96
+ # TODO Improve; stack trace isn't the best way to communicate with the user
97
+ raise "#{action} not one of #{Actions.join(', ')} " if !Actions.member?(action)
98
+ end
99
+
100
+ def check_actor_or_group(a_or_g)
101
+ raise "#{a_or_g} not one of :actor or :group" if a_or_g != :actor && a_or_g != :group
102
+ end
103
+
104
+ def resourcify_actor_or_group(a_or_g)
105
+ return a_or_g if ["actors", "groups"].member?(a_or_g)
106
+ check_actor_or_group(a_or_g)
107
+ to_resource(a_or_g)
108
+ end
109
+
110
+ def get_authz_id(x)
111
+ return x.authz_id if x.respond_to?(:authz_id)
112
+ # if it quacks like an authz id
113
+ return x if x.is_a?(String) && x =~ /^[[:xdigit:]]{32}$/
114
+ raise "#{x} doesn't look like an authz_id"
115
+ end
116
+ end
117
+
118
+ #
119
+ module AuthzObjectMixin
120
+ include AuthzUtils # reconsider this mixin; maybe better to refer to those routines explictly
121
+
122
+ def self.included(base)
123
+ # pp :note=>"Include", :base=>base, :super=>(base.superclass rescue :nil)
124
+ # block = lambda { :object }
125
+ # base.send(:define_method, :type_me, block )
126
+ # pp :methods=>(base.methods.sort - Object.methods)
127
+ end
128
+
129
+ def type
130
+ :object
131
+ end
132
+
133
+ def authz_api
134
+ @@authz_apiAsSuperUser ||= AuthzApi.new
135
+ end
136
+
137
+
138
+ # we expect to be mixed in with a class that has the authz_id method
139
+ def prefix
140
+ "#{to_resource(type)}/#{authz_id}"
141
+ end
142
+
143
+ def is_authorized(action, actor)
144
+ result = authz_api.get("#{prefix}/acl/#{action}/ace/#{actor.authz_id}")
145
+ [:unparsed, result] # todo figure this out in more detail
146
+ end
147
+
148
+ def acl_raw
149
+ authz_api.get("#{prefix}/acl")
150
+ end
151
+ # Todo: filter this by scope and type
152
+ def acl
153
+ ChefFixie::AuthzMapper.struct_to_name(acl_raw)
154
+ end
155
+
156
+ def ace_get_util(action)
157
+ check_action(action)
158
+
159
+ resource = "#{prefix}/acl/#{action}"
160
+ ace = authz_api.get(resource)
161
+ [resource, ace]
162
+ end
163
+
164
+
165
+ def ace_raw(action)
166
+ resource,ace = ace_get_util(action)
167
+ ace
168
+ end
169
+ # Todo: filter this by scope and type
170
+ def ace(action)
171
+ ChefFixie::AuthzMapper.struct_to_name(ace_raw(action))
172
+ end
173
+
174
+ def expand_actions(action)
175
+ if action == :all
176
+ action = AuthzUtils::Actions
177
+ end
178
+ action.is_a?(Array) ? action : [action]
179
+ end
180
+
181
+
182
+
183
+ # add actor or group to acl
184
+ def ace_add_raw(action, actor_or_group, entity)
185
+ # groups or actors
186
+ a_or_g_resource = resourcify_actor_or_group(actor_or_group)
187
+ resource, ace = ace_get_util(action)
188
+
189
+ ace[a_or_g_resource] << get_authz_id(entity)
190
+ ace[a_or_g_resource].uniq!
191
+ authz_api.put("#{resource}", ace)
192
+ end
193
+ def ace_add(action, entity)
194
+ actions = expand_actions(action)
195
+ actions.each {|a| ace_add_raw(a, entity.type, entity) }
196
+ end
197
+
198
+ def ace_delete_raw(action, actor_or_group, entity)
199
+ # groups or actors
200
+ a_or_g_resource = resourcify_actor_or_group(actor_or_group)
201
+ resource, ace = ace_get_util(action)
202
+
203
+ ace[a_or_g_resource] -= [get_authz_id(entity)]
204
+ ace[a_or_g_resource].uniq!
205
+ authz_api.put("#{resource}", ace)
206
+ end
207
+
208
+ def ace_delete(action, entity)
209
+ actions = expand_actions(action)
210
+ actions.each {|a| ace_delete_raw(a, entity.type, entity) }
211
+ end
212
+
213
+ def ace_member?(action, entity)
214
+ a_or_g_resource = resourcify_actor_or_group(entity.type)
215
+ resource, ace = ace_get_util(action)
216
+ ace[a_or_g_resource].member?(entity.authz_id)
217
+ end
218
+
219
+
220
+ def acl_add_from_object(object)
221
+ src = object.acl_raw
222
+
223
+ # this could be made more efficient by refactoring ace_add_raw to split fetch and update, but this works
224
+ src.each do |action, ace|
225
+ ace.each do |type, list|
226
+ list.each do |item|
227
+ ace_add_raw(action.to_sym, type, item)
228
+ end
229
+ end
230
+ end
231
+ end
232
+
233
+ end
234
+
235
+ module AuthzActorMixin
236
+ include AuthzObjectMixin
237
+ def type
238
+ :actor
239
+ end
240
+ end
241
+ module AuthzContainerMixin
242
+ include AuthzObjectMixin
243
+ def type
244
+ :container
245
+ end
246
+ end
247
+ module AuthzGroupMixin
248
+ include AuthzObjectMixin
249
+ def type
250
+ :group
251
+ end
252
+
253
+ # Groups need a little more code to manage members.
254
+ def group_raw
255
+ authz_api.get("#{prefix}")
256
+ end
257
+ # Todo: filter this by scope and type
258
+ def group
259
+ ChefFixie::AuthzMapper.struct_to_name(group_raw)
260
+ end
261
+
262
+ def group_add_raw(actor_or_group, entity)
263
+ entity_resource = to_resource(actor_or_group)
264
+ authz_api.put("#{prefix}/#{entity_resource}/#{entity.authz_id}",{})
265
+ end
266
+ def group_add(entity)
267
+ group_add_raw(entity.type, entity)
268
+ end
269
+
270
+ def group_delete_raw(actor_or_group, entity)
271
+ entity_resource = to_resource(actor_or_group)
272
+ authz_api.delete("#{prefix}/#{entity_resource}/#{entity.authz_id}")
273
+ end
274
+
275
+ def group_delete(entity)
276
+ group_delete_raw(entity.type, entity)
277
+ end
278
+
279
+ def member?(entity)
280
+ members = group_raw
281
+ return members[resourcify_actor_or_group(entity.type)].member?(entity.authz_id)
282
+ end
283
+ end
284
+
285
+ end