chef-zero 2.2.1 → 3.0.0.rc.1

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 (77) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -0
  3. data/Rakefile +5 -1
  4. data/bin/chef-zero +17 -0
  5. data/lib/chef_zero/chef_data/acl_path.rb +139 -0
  6. data/lib/chef_zero/chef_data/cookbook_data.rb +240 -0
  7. data/lib/chef_zero/chef_data/data_normalizer.rb +207 -0
  8. data/lib/chef_zero/chef_data/default_creator.rb +446 -0
  9. data/lib/chef_zero/data_store/default_facade.rb +149 -0
  10. data/lib/chef_zero/data_store/interface_v1.rb +18 -0
  11. data/lib/chef_zero/data_store/memory_store.rb +2 -1
  12. data/lib/chef_zero/data_store/memory_store_v2.rb +3 -36
  13. data/lib/chef_zero/data_store/raw_file_store.rb +147 -0
  14. data/lib/chef_zero/data_store/v1_to_v2_adapter.rb +39 -103
  15. data/lib/chef_zero/data_store/v2_to_v1_adapter.rb +1 -1
  16. data/lib/chef_zero/endpoints/acl_endpoint.rb +38 -0
  17. data/lib/chef_zero/endpoints/acls_endpoint.rb +29 -0
  18. data/lib/chef_zero/endpoints/actor_endpoint.rb +36 -10
  19. data/lib/chef_zero/endpoints/actors_endpoint.rb +38 -6
  20. data/lib/chef_zero/endpoints/authenticate_user_endpoint.rb +15 -9
  21. data/lib/chef_zero/endpoints/container_endpoint.rb +22 -0
  22. data/lib/chef_zero/endpoints/containers_endpoint.rb +13 -0
  23. data/lib/chef_zero/endpoints/cookbook_endpoint.rb +1 -1
  24. data/lib/chef_zero/endpoints/cookbook_version_endpoint.rb +15 -14
  25. data/lib/chef_zero/endpoints/cookbooks_base.rb +2 -2
  26. data/lib/chef_zero/endpoints/data_bag_endpoint.rb +4 -4
  27. data/lib/chef_zero/endpoints/data_bag_item_endpoint.rb +5 -5
  28. data/lib/chef_zero/endpoints/data_bags_endpoint.rb +5 -4
  29. data/lib/chef_zero/endpoints/environment_cookbook_endpoint.rb +2 -2
  30. data/lib/chef_zero/endpoints/environment_cookbook_versions_endpoint.rb +6 -6
  31. data/lib/chef_zero/endpoints/environment_cookbooks_endpoint.rb +2 -2
  32. data/lib/chef_zero/endpoints/environment_endpoint.rb +5 -5
  33. data/lib/chef_zero/endpoints/environment_nodes_endpoint.rb +2 -2
  34. data/lib/chef_zero/endpoints/environment_recipes_endpoint.rb +3 -3
  35. data/lib/chef_zero/endpoints/environment_role_endpoint.rb +2 -2
  36. data/lib/chef_zero/endpoints/file_store_file_endpoint.rb +1 -1
  37. data/lib/chef_zero/endpoints/group_endpoint.rb +20 -0
  38. data/lib/chef_zero/endpoints/groups_endpoint.rb +13 -0
  39. data/lib/chef_zero/endpoints/license_endpoint.rb +25 -0
  40. data/lib/chef_zero/endpoints/node_endpoint.rb +5 -5
  41. data/lib/chef_zero/endpoints/not_found_endpoint.rb +2 -2
  42. data/lib/chef_zero/endpoints/organization_association_request_endpoint.rb +22 -0
  43. data/lib/chef_zero/endpoints/organization_association_requests_endpoint.rb +29 -0
  44. data/lib/chef_zero/endpoints/organization_authenticate_user_endpoint.rb +26 -0
  45. data/lib/chef_zero/endpoints/organization_endpoint.rb +41 -0
  46. data/lib/chef_zero/endpoints/organization_user_endpoint.rb +48 -0
  47. data/lib/chef_zero/endpoints/organization_users_endpoint.rb +14 -0
  48. data/lib/chef_zero/endpoints/organization_validator_key_endpoint.rb +20 -0
  49. data/lib/chef_zero/endpoints/organizations_endpoint.rb +55 -0
  50. data/lib/chef_zero/endpoints/principal_endpoint.rb +15 -3
  51. data/lib/chef_zero/endpoints/rest_list_endpoint.rb +8 -6
  52. data/lib/chef_zero/endpoints/rest_object_endpoint.rb +12 -10
  53. data/lib/chef_zero/endpoints/role_endpoint.rb +5 -5
  54. data/lib/chef_zero/endpoints/role_environments_endpoint.rb +2 -2
  55. data/lib/chef_zero/endpoints/sandbox_endpoint.rb +2 -2
  56. data/lib/chef_zero/endpoints/sandboxes_endpoint.rb +4 -4
  57. data/lib/chef_zero/endpoints/search_endpoint.rb +10 -10
  58. data/lib/chef_zero/endpoints/system_recovery_endpoint.rb +30 -0
  59. data/lib/chef_zero/endpoints/user_association_request_endpoint.rb +40 -0
  60. data/lib/chef_zero/endpoints/user_association_requests_count_endpoint.rb +19 -0
  61. data/lib/chef_zero/endpoints/user_association_requests_endpoint.rb +19 -0
  62. data/lib/chef_zero/endpoints/user_organizations_endpoint.rb +22 -0
  63. data/lib/chef_zero/rest_base.rb +79 -13
  64. data/lib/chef_zero/rest_error_response.rb +1 -1
  65. data/lib/chef_zero/rest_request.rb +4 -0
  66. data/lib/chef_zero/rest_router.rb +1 -0
  67. data/lib/chef_zero/rspec.rb +55 -8
  68. data/lib/chef_zero/server.rb +87 -21
  69. data/lib/chef_zero/version.rb +1 -1
  70. data/spec/run_oc_pedant.rb +53 -0
  71. data/spec/{run.rb → run_pedant.rb} +13 -4
  72. data/spec/server_spec.rb +54 -0
  73. data/spec/support/oc_pedant.rb +134 -0
  74. data/spec/support/pedant.rb +1 -1
  75. metadata +54 -13
  76. data/lib/chef_zero/cookbook_data.rb +0 -236
  77. data/lib/chef_zero/data_normalizer.rb +0 -146
@@ -0,0 +1,207 @@
1
+ require 'chef_zero'
2
+ require 'chef_zero/rest_base'
3
+ require 'chef_zero/chef_data/default_creator'
4
+
5
+ module ChefZero
6
+ module ChefData
7
+ class DataNormalizer
8
+ def self.normalize_acls(acls)
9
+ ChefData::DefaultCreator::PERMISSIONS.each do |perm|
10
+ acls[perm] ||= {}
11
+ acls[perm]['actors'] ||= []
12
+ acls[perm]['groups'] ||= []
13
+ end
14
+ acls
15
+ end
16
+
17
+ def self.normalize_client(client, name)
18
+ client['name'] ||= name
19
+ client['admin'] ||= false
20
+ client['admin'] = !!client['admin']
21
+ client['public_key'] ||= PUBLIC_KEY
22
+ client['validator'] ||= false
23
+ client['validator'] = !!client['validator']
24
+ client['json_class'] ||= "Chef::ApiClient"
25
+ client['chef_type'] ||= "client"
26
+ client
27
+ end
28
+
29
+ def self.normalize_container(container, name)
30
+ container.delete('id')
31
+ container['containername'] = name
32
+ container['containerpath'] = name
33
+ container
34
+ end
35
+
36
+ def self.normalize_user(user, name, identity_keys, osc_compat, method=nil)
37
+ user[identity_keys.first] ||= name
38
+ user['public_key'] ||= PUBLIC_KEY
39
+ user['admin'] ||= false
40
+ user['admin'] = !!user['admin']
41
+ user['openid'] ||= nil
42
+ if !osc_compat
43
+ if method == 'GET'
44
+ user.delete('admin')
45
+ user.delete('password')
46
+ user.delete('openid')
47
+ end
48
+ user['email'] ||= nil
49
+ user['first_name'] ||= nil
50
+ user['last_name'] ||= nil
51
+ end
52
+ user
53
+ end
54
+
55
+ def self.normalize_data_bag_item(data_bag_item, data_bag_name, id, method)
56
+ if method == 'DELETE'
57
+ # TODO SERIOUSLY, WHO DOES THIS MANY EXCEPTIONS IN THEIR INTERFACE
58
+ if !(data_bag_item['json_class'] == 'Chef::DataBagItem' && data_bag_item['raw_data'])
59
+ data_bag_item['id'] ||= id
60
+ data_bag_item = { 'raw_data' => data_bag_item }
61
+ data_bag_item['chef_type'] ||= 'data_bag_item'
62
+ data_bag_item['json_class'] ||= 'Chef::DataBagItem'
63
+ data_bag_item['data_bag'] ||= data_bag_name
64
+ data_bag_item['name'] ||= "data_bag_item_#{data_bag_name}_#{id}"
65
+ end
66
+ else
67
+ # If it's not already wrapped with raw_data, wrap it.
68
+ if data_bag_item['json_class'] == 'Chef::DataBagItem' && data_bag_item['raw_data']
69
+ data_bag_item = data_bag_item['raw_data']
70
+ end
71
+ # Argh. We don't do this on GET, but we do on PUT and POST????
72
+ if %w(PUT POST).include?(method)
73
+ data_bag_item['chef_type'] ||= 'data_bag_item'
74
+ data_bag_item['data_bag'] ||= data_bag_name
75
+ end
76
+ data_bag_item['id'] ||= id
77
+ end
78
+ data_bag_item
79
+ end
80
+
81
+ def self.normalize_cookbook(endpoint, org_prefix, cookbook, name, version, base_uri, method)
82
+ # TODO I feel dirty
83
+ if method != 'PUT'
84
+ cookbook.each_pair do |key, value|
85
+ if value.is_a?(Array)
86
+ value.each do |file|
87
+ if file.is_a?(Hash) && file.has_key?('checksum')
88
+ file['url'] ||= endpoint.build_uri(base_uri, org_prefix + ['file_store', 'checksums', file['checksum']])
89
+ end
90
+ end
91
+ end
92
+ end
93
+ cookbook['name'] ||= "#{name}-#{version}"
94
+ # TODO this feels wrong, but the real chef server doesn't expand this default
95
+ # cookbook['version'] ||= version
96
+ cookbook['cookbook_name'] ||= name
97
+ cookbook['frozen?'] ||= false
98
+ cookbook['metadata'] ||= {}
99
+ cookbook['metadata']['version'] ||= version
100
+ # Sad to not be expanding defaults just because Chef doesn't :(
101
+ # cookbook['metadata']['name'] ||= name
102
+ # cookbook['metadata']['description'] ||= "A fabulous new cookbook"
103
+ cookbook['metadata']['long_description'] ||= ""
104
+ # cookbook['metadata']['maintainer'] ||= "YOUR_COMPANY_NAME"
105
+ # cookbook['metadata']['maintainer_email'] ||= "YOUR_EMAIL"
106
+ # cookbook['metadata']['license'] ||= "none"
107
+ cookbook['metadata']['dependencies'] ||= {}
108
+ cookbook['metadata']['attributes'] ||= {}
109
+ cookbook['metadata']['recipes'] ||= {}
110
+ end
111
+ cookbook['json_class'] ||= 'Chef::CookbookVersion'
112
+ cookbook['chef_type'] ||= 'cookbook_version'
113
+ if method == 'MIN'
114
+ cookbook['metadata'].delete('attributes')
115
+ cookbook['metadata'].delete('long_description')
116
+ end
117
+ cookbook
118
+ end
119
+
120
+ def self.normalize_environment(environment, name)
121
+ environment['name'] ||= name
122
+ environment['description'] ||= ''
123
+ environment['cookbook_versions'] ||= {}
124
+ environment['json_class'] ||= "Chef::Environment"
125
+ environment['chef_type'] ||= "environment"
126
+ environment['default_attributes'] ||= {}
127
+ environment['override_attributes'] ||= {}
128
+ environment
129
+ end
130
+
131
+ def self.normalize_group(group, name, orgname)
132
+ group.delete('id')
133
+ if group['actors'].is_a?(Hash)
134
+ group['users'] ||= group['actors']['users']
135
+ group['clients'] ||= group['actors']['clients']
136
+ group['groups'] ||= group['actors']['groups']
137
+ group['actors'] = nil
138
+ end
139
+ group['users'] ||= []
140
+ group['clients'] ||= []
141
+ group['actors'] ||= (group['clients'] + group['users'])
142
+ group['groups'] ||= []
143
+ group['orgname'] ||= orgname if orgname
144
+ group['name'] ||= name
145
+ group['groupname'] ||= name
146
+
147
+ group['users'].uniq!
148
+ group['clients'].uniq!
149
+ group['actors'].uniq!
150
+ group['groups'].uniq!
151
+ group
152
+ end
153
+
154
+ def self.normalize_node(node, name)
155
+ node['name'] ||= name
156
+ node['json_class'] ||= 'Chef::Node'
157
+ node['chef_type'] ||= 'node'
158
+ node['chef_environment'] ||= '_default'
159
+ node['override'] ||= {}
160
+ node['normal'] ||= {}
161
+ node['default'] ||= {}
162
+ node['automatic'] ||= {}
163
+ node['run_list'] ||= []
164
+ node['run_list'] = normalize_run_list(node['run_list'])
165
+ node
166
+ end
167
+
168
+ def self.normalize_organization(org, name)
169
+ org['name'] ||= name
170
+ org['full_name'] ||= name
171
+ org['org_type'] ||= 'Business'
172
+ org['clientname'] ||= "#{name}-validator"
173
+ org['billing_plan'] ||= 'platform-free'
174
+ org
175
+ end
176
+
177
+ def self.normalize_role(role, name)
178
+ role['name'] ||= name
179
+ role['description'] ||= ''
180
+ role['json_class'] ||= 'Chef::Role'
181
+ role['chef_type'] ||= 'role'
182
+ role['default_attributes'] ||= {}
183
+ role['override_attributes'] ||= {}
184
+ role['run_list'] ||= []
185
+ role['run_list'] = normalize_run_list(role['run_list'])
186
+ role['env_run_lists'] ||= {}
187
+ role['env_run_lists'].each_pair do |env, run_list|
188
+ role['env_run_lists'][env] = normalize_run_list(run_list)
189
+ end
190
+ role
191
+ end
192
+
193
+ def self.normalize_run_list(run_list)
194
+ run_list.map{|item|
195
+ case item
196
+ when /^recipe\[.*\]$/
197
+ item # explicit recipe
198
+ when /^role\[.*\]$/
199
+ item # explicit role
200
+ else
201
+ "recipe[#{item}]"
202
+ end
203
+ }.uniq
204
+ end
205
+ end
206
+ end
207
+ end
@@ -0,0 +1,446 @@
1
+ require 'chef_zero/chef_data/acl_path'
2
+
3
+ module ChefZero
4
+ module ChefData
5
+ #
6
+ # The DefaultCreator creates default values when you ask for them.
7
+ # - It relies on created and deleted being called when things get
8
+ # created and deleted, so that it knows the owners of said objects
9
+ # and knows to eliminate default values on delete.
10
+ # - get, list and exists? get data.
11
+ #
12
+ class DefaultCreator
13
+ def initialize(data, single_org, osc_compat, superusers = nil)
14
+ @data = data
15
+ @single_org = single_org
16
+ @osc_compat = osc_compat
17
+ @superusers = superusers || DEFAULT_SUPERUSERS
18
+ clear
19
+ end
20
+
21
+ attr_reader :data
22
+ attr_reader :single_org
23
+ attr_reader :osc_compat
24
+ attr_reader :creators
25
+ attr_reader :deleted
26
+
27
+ PERMISSIONS = %w(create read update delete grant)
28
+ DEFAULT_SUPERUSERS = %w(pivotal)
29
+
30
+ def clear
31
+ @creators = { [] => @superusers }
32
+ @deleted = {}
33
+ end
34
+
35
+ def deleted(path)
36
+ # acl deletes mean nothing, they are entirely subservient to their
37
+ # parent object
38
+ if path[0] == 'acls' || (path[0] == 'organizations' && path[2] == 'acls')
39
+ return false
40
+ end
41
+
42
+ result = exists?(path)
43
+ @deleted[path] = true
44
+ result
45
+ end
46
+
47
+ def deleted?(path)
48
+ 1.upto(path.size) do |index|
49
+ return true if @deleted[path[0..-index]]
50
+ end
51
+ false
52
+ end
53
+
54
+ def created(path, creator, create_parents)
55
+ # If a parent has been deleted, we will need to clear that.
56
+ deleted_index = nil
57
+ 0.upto(path.size-1) do |index|
58
+ deleted_index = index if @deleted[path[0..index]]
59
+ end
60
+
61
+ # Walk up the tree, setting the creator on anything that doesn't exist
62
+ # (anything that is either deleted or was never created)
63
+ while (deleted_index && path.size > deleted_index) || !@creators[path]
64
+ @creators[path] = creator ? [ creator ] : []
65
+ @deleted.delete(path)
66
+ # Only do this once if create_parents is false
67
+ break if !create_parents || path.size == 0
68
+
69
+ path = path[0..-2]
70
+ end
71
+ end
72
+
73
+ def superusers
74
+ @creators[[]]
75
+ end
76
+
77
+ def get(path)
78
+ return nil if deleted?(path)
79
+
80
+ result = case path[0]
81
+ when 'acls'
82
+ # /acls/*
83
+ object_path = AclPath.get_object_path(path)
84
+ if data_exists?(object_path)
85
+ default_acl(path)
86
+ end
87
+
88
+ when 'containers'
89
+ if path.size == 2 && exists?(path)
90
+ {}
91
+ end
92
+
93
+ when 'users'
94
+ if path.size == 2 && data.exists?(path)
95
+ # User is empty user
96
+ {}
97
+ end
98
+
99
+ when 'organizations'
100
+ if path.size >= 2
101
+ # /organizations/*/**
102
+ if data.exists_dir?(path[0..1])
103
+ get_org_default(path)
104
+ end
105
+ end
106
+ end
107
+
108
+ result
109
+ end
110
+
111
+ def list(path)
112
+ return nil if deleted?(path)
113
+
114
+ if path.size == 0
115
+ return %w(containers users organizations acls)
116
+ end
117
+
118
+ case path[0]
119
+ when 'acls'
120
+ if path.size == 1
121
+ [ 'root' ] + (data.list(path + [ 'containers' ]) - [ 'organizations' ])
122
+ else
123
+ data.list(AclPath.get_object_path(path))
124
+ end
125
+
126
+ when 'containers'
127
+ [ 'containers', 'users', 'organizations' ]
128
+
129
+ when 'users'
130
+ superusers
131
+
132
+ when 'organizations'
133
+ if path.size == 1
134
+ single_org ? [ single_org ] : []
135
+ elsif path.size >= 2 && data.exists_dir?(path[0..1])
136
+ list_org_default(path)
137
+ end
138
+ end
139
+ end
140
+
141
+ def exists?(path)
142
+ return true if path.size == 0
143
+ parent_list = list(path[0..-2])
144
+ parent_list && parent_list.include?(path[-1])
145
+ end
146
+
147
+ protected
148
+
149
+ DEFAULT_ORG_SPINE = {
150
+ 'clients' => {},
151
+ 'cookbooks' => {},
152
+ 'data' => {},
153
+ 'environments' => %w(_default),
154
+ 'file_store' => {
155
+ 'checksums' => {}
156
+ },
157
+ 'nodes' => {},
158
+ 'roles' => {},
159
+ 'sandboxes' => {},
160
+ 'users' => {},
161
+
162
+ 'org' => {},
163
+ 'containers' => %w(clients containers cookbooks data environments groups nodes roles sandboxes),
164
+ 'groups' => %w(admins billing-admins clients users),
165
+ 'association_requests' => {}
166
+ }
167
+
168
+ def list_org_default(path)
169
+ if path.size >= 3 && path[2] == 'acls'
170
+ if path.size == 3
171
+ # /organizations/ORG/acls
172
+ return [ 'root' ] + data.list(path[0..1] + [ 'containers' ])
173
+ elsif path.size == 4
174
+ # /organizations/ORG/acls/TYPE
175
+ return data.list(path[0..1] + [ path[3] ])
176
+ else
177
+ return nil
178
+ end
179
+ end
180
+
181
+ value = DEFAULT_ORG_SPINE
182
+ 2.upto(path.size-1) do |index|
183
+ value = nil if @deleted[path[0..index]]
184
+ break if !value
185
+ value = value[path[index]]
186
+ end
187
+
188
+ result = if value.is_a?(Hash)
189
+ value.keys
190
+ elsif value
191
+ value
192
+ end
193
+
194
+ if path.size == 3
195
+ if path[2] == 'clients'
196
+ result << "#{path[1]}-validator"
197
+ if osc_compat
198
+ result << "#{path[1]}-webui"
199
+ end
200
+ elsif path[2] == 'users'
201
+ if osc_compat
202
+ result << 'admin'
203
+ end
204
+ end
205
+ end
206
+
207
+ result
208
+ end
209
+
210
+ def get_org_default(path)
211
+ if path[2] == 'acls'
212
+ get_org_acl_default(path)
213
+
214
+ elsif path.size >= 4
215
+ if path[2] == 'containers' && path.size == 4
216
+ if exists?(path)
217
+ return {}
218
+ else
219
+ return nil
220
+ end
221
+ end
222
+
223
+ # /organizations/(*)/clients/\1-validator
224
+ # /organizations/*/environments/_default
225
+ # /organizations/*/groups/{admins,billing-admins,clients,users}
226
+ case path[2..-1].join('/')
227
+ when "clients/#{path[1]}-validator"
228
+ { 'validator' => 'true' }
229
+
230
+ when "clients/#{path[1]}-webui", "users/admin"
231
+ if osc_compat
232
+ { 'admin' => 'true' }
233
+ end
234
+
235
+ when "environments/_default"
236
+ { "description" => "The default Chef environment" }
237
+
238
+ when "groups/admins"
239
+ admins = data.list(path[0..1] + [ 'users' ]).select do |name|
240
+ user = JSON.parse(data.get(path[0..1] + [ 'users', name ]), :create_additions => false)
241
+ user['admin']
242
+ end
243
+ admins += data.list(path[0..1] + [ 'clients' ]).select do |name|
244
+ client = JSON.parse(data.get(path[0..1] + [ 'clients', name ]), :create_additions => false)
245
+ client['admin']
246
+ end
247
+ admins += @creators[path[0..1]] if @creators[path[0..1]]
248
+ { 'actors' => admins.uniq }
249
+
250
+ when "groups/billing-admins"
251
+ {}
252
+
253
+ when "groups/clients"
254
+ { 'clients' => data.list(path[0..1] + [ 'clients' ]) }
255
+
256
+ when "groups/users"
257
+ users = data.list(path[0..1] + [ 'users' ])
258
+ users |= @creators[path[0..1]] if @creators[path[0..1]]
259
+ { 'users' => users }
260
+
261
+ when "org"
262
+ {}
263
+
264
+ end
265
+ end
266
+ end
267
+
268
+ def get_org_acl_default(path)
269
+ object_path = AclPath.get_object_path(path)
270
+ # The actual things containers correspond to don't have to exist, as long as the container does
271
+ return nil if object_path[2] != 'containers' && !data_exists?(object_path)
272
+ basic_acl =
273
+ case path[3..-1].join('/')
274
+ when 'root', 'containers/containers', 'containers/groups'
275
+ {
276
+ 'create' => { 'groups' => %w(admins) },
277
+ 'read' => { 'groups' => %w(admins users) },
278
+ 'update' => { 'groups' => %w(admins) },
279
+ 'delete' => { 'groups' => %w(admins) },
280
+ 'grant' => { 'groups' => %w(admins) },
281
+ }
282
+ when 'containers/cookbooks', 'containers/environments', 'containers/roles'
283
+ {
284
+ 'create' => { 'groups' => %w(admins users) },
285
+ 'read' => { 'groups' => %w(admins users clients) },
286
+ 'update' => { 'groups' => %w(admins users) },
287
+ 'delete' => { 'groups' => %w(admins users) },
288
+ 'grant' => { 'groups' => %w(admins) },
289
+ }
290
+ when 'containers/cookbooks', 'containers/data'
291
+ {
292
+ 'create' => { 'groups' => %w(admins users clients) },
293
+ 'read' => { 'groups' => %w(admins users clients) },
294
+ 'update' => { 'groups' => %w(admins users clients) },
295
+ 'delete' => { 'groups' => %w(admins users clients) },
296
+ 'grant' => { 'groups' => %w(admins) },
297
+ }
298
+ when 'containers/nodes'
299
+ {
300
+ 'create' => { 'groups' => %w(admins users clients) },
301
+ 'read' => { 'groups' => %w(admins users clients) },
302
+ 'update' => { 'groups' => %w(admins users) },
303
+ 'delete' => { 'groups' => %w(admins users) },
304
+ 'grant' => { 'groups' => %w(admins) },
305
+ }
306
+ when 'containers/clients'
307
+ {
308
+ 'create' => { 'groups' => %w(admins) },
309
+ 'read' => { 'groups' => %w(admins users) },
310
+ 'update' => { 'groups' => %w(admins) },
311
+ 'delete' => { 'groups' => %w(admins users) },
312
+ 'grant' => { 'groups' => %w(admins) },
313
+ }
314
+ when 'containers/sandboxes'
315
+ {
316
+ 'create' => { 'groups' => %w(admins users) },
317
+ 'read' => { 'groups' => %w(admins) },
318
+ 'update' => { 'groups' => %w(admins) },
319
+ 'delete' => { 'groups' => %w(admins) },
320
+ 'grant' => { 'groups' => %w(admins) },
321
+ }
322
+ when 'groups/admins', 'groups/clients', 'groups/users'
323
+ {
324
+ 'create' => { 'groups' => %w(admins) },
325
+ 'read' => { 'groups' => %w(admins) },
326
+ 'update' => { 'groups' => %w(admins) },
327
+ 'delete' => { 'groups' => %w(admins) },
328
+ 'grant' => { 'groups' => %w(admins) },
329
+ }
330
+ when 'groups/billing-admins'
331
+ {
332
+ 'create' => { 'groups' => %w() },
333
+ 'read' => { 'groups' => %w(billing-admins) },
334
+ 'update' => { 'groups' => %w(billing-admins) },
335
+ 'delete' => { 'groups' => %w() },
336
+ 'grant' => { 'groups' => %w() },
337
+ }
338
+ else
339
+ {}
340
+ end
341
+
342
+ default_acl(path, basic_acl)
343
+ end
344
+
345
+ def get_owners(acl_path)
346
+ owners = []
347
+
348
+ path = AclPath.get_object_path(acl_path)
349
+ if path
350
+
351
+ # Non-validator clients own themselves.
352
+ if path.size == 4 && path[0] == 'organizations' && path[2] == 'clients'
353
+ begin
354
+ client = JSON.parse(data.get(path), :create_additions => false)
355
+ if !client['validator']
356
+ owners |= [ path[3] ]
357
+ end
358
+ rescue
359
+ owners |= [ path[3] ]
360
+ end
361
+
362
+ # Add creators as owners (except any validator clients).
363
+ if @creators[path]
364
+ @creators[path].each do |creator|
365
+ begin
366
+ client = JSON.parse(data.get(path[0..2] + [ creator ]), :create_additions => false)
367
+ next if client['validator']
368
+ rescue
369
+ end
370
+ owners |= [ creator ]
371
+ end
372
+ end
373
+ else
374
+ owners |= @creators[path] if @creators[path]
375
+ end
376
+
377
+ #ANGRY
378
+ # Non-default containers do not get superusers added to them,
379
+ # because reasons.
380
+ unless path.size == 4 && path[0] == 'organizations' && path[2] == 'containers' && !exists?(path)
381
+ owners |= superusers
382
+ end
383
+ end
384
+
385
+ owners.uniq
386
+ end
387
+
388
+ def default_acl(acl_path, acl={})
389
+ owners = nil
390
+ container_acl = nil
391
+ PERMISSIONS.each do |perm|
392
+ acl[perm] ||= {}
393
+ acl[perm]['actors'] ||= begin
394
+ owners ||= get_owners(acl_path)
395
+ end
396
+ acl[perm]['groups'] ||= begin
397
+ # When we create containers, we don't merge groups (not sure why).
398
+ if acl_path[0] == 'organizations' && acl_path[3] == 'containers'
399
+ []
400
+ else
401
+ container_acl ||= get_container_acl(acl_path) || {}
402
+ (container_acl[perm] ? container_acl[perm]['groups'] : []) || []
403
+ end
404
+ end
405
+ end
406
+ acl
407
+ end
408
+
409
+ def get_container_acl(acl_path)
410
+ parent_path = AclPath.parent_acl_data_path(acl_path)
411
+ if parent_path
412
+ JSON.parse(data.get(parent_path), :create_additions => false)
413
+ else
414
+ nil
415
+ end
416
+ end
417
+
418
+ def data_exists?(path)
419
+ if is_dir?(path)
420
+ data.exists_dir?(path)
421
+ else
422
+ data.exists?(path)
423
+ end
424
+ end
425
+
426
+ def is_dir?(path)
427
+ case path.size
428
+ when 0, 1
429
+ return true
430
+ when 2
431
+ return path[0] == 'organizations' || (path[0] == 'acls' && path[1] != 'root')
432
+ when 3
433
+ # If it has a container, it is a directory.
434
+ return path[0] == 'organizations' &&
435
+ (path[2] == 'acls' || data.exists?(path[0..1] + [ 'containers', path[2] ]))
436
+ when 4
437
+ return path[0] == 'organizations' && (
438
+ (path[2] == 'acls' && path[1] != 'root') ||
439
+ %w(cookbooks data).include?(path[2]))
440
+ else
441
+ return false
442
+ end
443
+ end
444
+ end
445
+ end
446
+ end