chef-zero 4.3.0 → 4.3.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 (100) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +201 -201
  3. data/README.md +155 -155
  4. data/Rakefile +31 -31
  5. data/bin/chef-zero +100 -100
  6. data/lib/chef_zero.rb +10 -10
  7. data/lib/chef_zero/chef_data/acl_path.rb +139 -139
  8. data/lib/chef_zero/chef_data/cookbook_data.rb +240 -240
  9. data/lib/chef_zero/chef_data/data_normalizer.rb +208 -207
  10. data/lib/chef_zero/chef_data/default_creator.rb +446 -446
  11. data/lib/chef_zero/data_store/data_already_exists_error.rb +29 -29
  12. data/lib/chef_zero/data_store/data_error.rb +31 -31
  13. data/lib/chef_zero/data_store/data_not_found_error.rb +28 -28
  14. data/lib/chef_zero/data_store/default_facade.rb +149 -149
  15. data/lib/chef_zero/data_store/interface_v1.rb +67 -67
  16. data/lib/chef_zero/data_store/interface_v2.rb +18 -18
  17. data/lib/chef_zero/data_store/memory_store.rb +33 -33
  18. data/lib/chef_zero/data_store/memory_store_v2.rb +155 -155
  19. data/lib/chef_zero/data_store/raw_file_store.rb +147 -147
  20. data/lib/chef_zero/data_store/v1_to_v2_adapter.rb +142 -142
  21. data/lib/chef_zero/data_store/v2_to_v1_adapter.rb +107 -107
  22. data/lib/chef_zero/endpoints/acl_endpoint.rb +38 -38
  23. data/lib/chef_zero/endpoints/acls_endpoint.rb +29 -29
  24. data/lib/chef_zero/endpoints/actor_endpoint.rb +94 -94
  25. data/lib/chef_zero/endpoints/actors_endpoint.rb +64 -64
  26. data/lib/chef_zero/endpoints/authenticate_user_endpoint.rb +31 -31
  27. data/lib/chef_zero/endpoints/container_endpoint.rb +22 -22
  28. data/lib/chef_zero/endpoints/containers_endpoint.rb +13 -13
  29. data/lib/chef_zero/endpoints/cookbook_endpoint.rb +39 -39
  30. data/lib/chef_zero/endpoints/cookbook_version_endpoint.rb +119 -119
  31. data/lib/chef_zero/endpoints/cookbooks_base.rb +65 -65
  32. data/lib/chef_zero/endpoints/cookbooks_endpoint.rb +19 -19
  33. data/lib/chef_zero/endpoints/data_bag_endpoint.rb +45 -45
  34. data/lib/chef_zero/endpoints/data_bag_item_endpoint.rb +25 -25
  35. data/lib/chef_zero/endpoints/data_bags_endpoint.rb +23 -23
  36. data/lib/chef_zero/endpoints/environment_cookbook_endpoint.rb +24 -24
  37. data/lib/chef_zero/endpoints/environment_cookbook_versions_endpoint.rb +123 -123
  38. data/lib/chef_zero/endpoints/environment_cookbooks_endpoint.rb +22 -22
  39. data/lib/chef_zero/endpoints/environment_endpoint.rb +33 -33
  40. data/lib/chef_zero/endpoints/environment_nodes_endpoint.rb +23 -23
  41. data/lib/chef_zero/endpoints/environment_recipes_endpoint.rb +22 -22
  42. data/lib/chef_zero/endpoints/environment_role_endpoint.rb +36 -36
  43. data/lib/chef_zero/endpoints/file_store_file_endpoint.rb +22 -22
  44. data/lib/chef_zero/endpoints/group_endpoint.rb +20 -20
  45. data/lib/chef_zero/endpoints/groups_endpoint.rb +13 -13
  46. data/lib/chef_zero/endpoints/license_endpoint.rb +25 -25
  47. data/lib/chef_zero/endpoints/node_endpoint.rb +17 -17
  48. data/lib/chef_zero/endpoints/node_identifiers_endpoint.rb +22 -22
  49. data/lib/chef_zero/endpoints/not_found_endpoint.rb +11 -11
  50. data/lib/chef_zero/endpoints/organization_association_request_endpoint.rb +22 -22
  51. data/lib/chef_zero/endpoints/organization_association_requests_endpoint.rb +30 -30
  52. data/lib/chef_zero/endpoints/organization_authenticate_user_endpoint.rb +26 -26
  53. data/lib/chef_zero/endpoints/organization_endpoint.rb +46 -46
  54. data/lib/chef_zero/endpoints/organization_user_base.rb +15 -15
  55. data/lib/chef_zero/endpoints/organization_user_endpoint.rb +26 -26
  56. data/lib/chef_zero/endpoints/organization_users_endpoint.rb +43 -43
  57. data/lib/chef_zero/endpoints/organization_validator_key_endpoint.rb +20 -20
  58. data/lib/chef_zero/endpoints/organizations_endpoint.rb +62 -62
  59. data/lib/chef_zero/endpoints/policies_endpoint.rb +151 -151
  60. data/lib/chef_zero/endpoints/principal_endpoint.rb +42 -42
  61. data/lib/chef_zero/endpoints/rest_list_endpoint.rb +42 -42
  62. data/lib/chef_zero/endpoints/rest_object_endpoint.rb +63 -63
  63. data/lib/chef_zero/endpoints/role_endpoint.rb +16 -16
  64. data/lib/chef_zero/endpoints/role_environments_endpoint.rb +14 -14
  65. data/lib/chef_zero/endpoints/sandbox_endpoint.rb +27 -27
  66. data/lib/chef_zero/endpoints/sandboxes_endpoint.rb +50 -50
  67. data/lib/chef_zero/endpoints/search_endpoint.rb +194 -194
  68. data/lib/chef_zero/endpoints/searches_endpoint.rb +18 -18
  69. data/lib/chef_zero/endpoints/server_api_version_endpoint.rb +14 -14
  70. data/lib/chef_zero/endpoints/system_recovery_endpoint.rb +30 -30
  71. data/lib/chef_zero/endpoints/user_association_request_endpoint.rb +40 -40
  72. data/lib/chef_zero/endpoints/user_association_requests_count_endpoint.rb +19 -19
  73. data/lib/chef_zero/endpoints/user_association_requests_endpoint.rb +19 -19
  74. data/lib/chef_zero/endpoints/user_organizations_endpoint.rb +22 -22
  75. data/lib/chef_zero/endpoints/version_endpoint.rb +12 -12
  76. data/lib/chef_zero/log.rb +7 -7
  77. data/lib/chef_zero/rest_base.rb +242 -242
  78. data/lib/chef_zero/rest_error_response.rb +11 -11
  79. data/lib/chef_zero/rest_request.rb +69 -69
  80. data/lib/chef_zero/rest_router.rb +45 -45
  81. data/lib/chef_zero/rspec.rb +308 -308
  82. data/lib/chef_zero/server.rb +642 -642
  83. data/lib/chef_zero/socketless_server_map.rb +92 -92
  84. data/lib/chef_zero/solr/query/binary_operator.rb +52 -52
  85. data/lib/chef_zero/solr/query/phrase.rb +23 -23
  86. data/lib/chef_zero/solr/query/range_query.rb +46 -46
  87. data/lib/chef_zero/solr/query/regexpable_query.rb +29 -29
  88. data/lib/chef_zero/solr/query/subquery.rb +37 -37
  89. data/lib/chef_zero/solr/query/term.rb +45 -45
  90. data/lib/chef_zero/solr/query/unary_operator.rb +43 -43
  91. data/lib/chef_zero/solr/solr_doc.rb +53 -53
  92. data/lib/chef_zero/solr/solr_parser.rb +203 -203
  93. data/lib/chef_zero/version.rb +3 -3
  94. data/spec/run_oc_pedant.rb +63 -63
  95. data/spec/search_spec.rb +32 -32
  96. data/spec/server_spec.rb +92 -92
  97. data/spec/socketless_server_map_spec.rb +76 -76
  98. data/spec/support/oc_pedant.rb +132 -132
  99. data/spec/support/stickywicket.pem +27 -27
  100. metadata +3 -3
@@ -1,30 +1,30 @@
1
- require 'ffi_yajl'
2
- require 'chef_zero/rest_base'
3
-
4
- module ChefZero
5
- module Endpoints
6
- # /system_recovery
7
- class SystemRecoveryEndpoint < RestBase
8
- def post(request)
9
- request_json = FFI_Yajl::Parser.parse(request.body, :create_additions => false)
10
- name = request_json['username']
11
- password = request_json['password']
12
- user = get_data(request, request.rest_path[0..-2] + ['users', name], :nil)
13
- if !user
14
- raise RestErrorResponse.new(403, "Nonexistent user")
15
- end
16
-
17
- user = FFI_Yajl::Parser.parse(user, :create_additions => false)
18
- user = ChefData::DataNormalizer.normalize_user(user, name, [ 'username' ], server.options[:osc_compat])
19
- if !user['recovery_authentication_enabled']
20
- raise RestErrorResponse.new(403, "Only users with recovery_authentication_enabled=true may use /system_recovery to log in")
21
- end
22
- if user['password'] != password
23
- raise RestErrorResponse.new(401, "Incorrect password")
24
- end
25
-
26
- json_response(200, user)
27
- end
28
- end
29
- end
30
- end
1
+ require 'ffi_yajl'
2
+ require 'chef_zero/rest_base'
3
+
4
+ module ChefZero
5
+ module Endpoints
6
+ # /system_recovery
7
+ class SystemRecoveryEndpoint < RestBase
8
+ def post(request)
9
+ request_json = FFI_Yajl::Parser.parse(request.body, :create_additions => false)
10
+ name = request_json['username']
11
+ password = request_json['password']
12
+ user = get_data(request, request.rest_path[0..-2] + ['users', name], :nil)
13
+ if !user
14
+ raise RestErrorResponse.new(403, "Nonexistent user")
15
+ end
16
+
17
+ user = FFI_Yajl::Parser.parse(user, :create_additions => false)
18
+ user = ChefData::DataNormalizer.normalize_user(user, name, [ 'username' ], server.options[:osc_compat])
19
+ if !user['recovery_authentication_enabled']
20
+ raise RestErrorResponse.new(403, "Only users with recovery_authentication_enabled=true may use /system_recovery to log in")
21
+ end
22
+ if user['password'] != password
23
+ raise RestErrorResponse.new(401, "Incorrect password")
24
+ end
25
+
26
+ json_response(200, user)
27
+ end
28
+ end
29
+ end
30
+ end
@@ -1,40 +1,40 @@
1
- require 'ffi_yajl'
2
- require 'chef_zero/rest_base'
3
-
4
- module ChefZero
5
- module Endpoints
6
- # /users/USER/association_requests/ID
7
- class UserAssociationRequestEndpoint < RestBase
8
- def put(request)
9
- username = request.rest_path[1]
10
- id = request.rest_path[3]
11
- if id !~ /^#{username}-(.+)/
12
- raise RestErrorResponse.new(400, "Association request #{id} is invalid. Must be #{username}-orgname.")
13
- end
14
- orgname = $1
15
-
16
- json = FFI_Yajl::Parser.parse(request.body, :create_additions => false)
17
- association_request_path = [ 'organizations', orgname, 'association_requests', username ]
18
- if json['response'] == 'accept'
19
- users = get_data(request, [ 'organizations', orgname, 'groups', 'users' ])
20
- users = FFI_Yajl::Parser.parse(users, :create_additions => false)
21
-
22
- delete_data(request, association_request_path)
23
- create_data(request, [ 'organizations', orgname, 'users' ], username, '{}')
24
-
25
- # Add the user to the users group if it isn't already there
26
- if !users['users'] || !users['users'].include?(username)
27
- users['users'] ||= []
28
- users['users'] |= [ username ]
29
- set_data(request, [ 'organizations', orgname, 'groups', 'users' ], FFI_Yajl::Encoder.encode(users, :pretty => true))
30
- end
31
- elsif json['response'] == 'reject'
32
- delete_data(request, association_request_path)
33
- else
34
- raise RestErrorResponse.new(400, "response parameter was missing or set to the wrong value (must be accept or reject)")
35
- end
36
- json_response(200, { 'organization' => { 'name' => orgname } })
37
- end
38
- end
39
- end
40
- end
1
+ require 'ffi_yajl'
2
+ require 'chef_zero/rest_base'
3
+
4
+ module ChefZero
5
+ module Endpoints
6
+ # /users/USER/association_requests/ID
7
+ class UserAssociationRequestEndpoint < RestBase
8
+ def put(request)
9
+ username = request.rest_path[1]
10
+ id = request.rest_path[3]
11
+ if id !~ /^#{username}-(.+)/
12
+ raise RestErrorResponse.new(400, "Association request #{id} is invalid. Must be #{username}-orgname.")
13
+ end
14
+ orgname = $1
15
+
16
+ json = FFI_Yajl::Parser.parse(request.body, :create_additions => false)
17
+ association_request_path = [ 'organizations', orgname, 'association_requests', username ]
18
+ if json['response'] == 'accept'
19
+ users = get_data(request, [ 'organizations', orgname, 'groups', 'users' ])
20
+ users = FFI_Yajl::Parser.parse(users, :create_additions => false)
21
+
22
+ delete_data(request, association_request_path)
23
+ create_data(request, [ 'organizations', orgname, 'users' ], username, '{}')
24
+
25
+ # Add the user to the users group if it isn't already there
26
+ if !users['users'] || !users['users'].include?(username)
27
+ users['users'] ||= []
28
+ users['users'] |= [ username ]
29
+ set_data(request, [ 'organizations', orgname, 'groups', 'users' ], FFI_Yajl::Encoder.encode(users, :pretty => true))
30
+ end
31
+ elsif json['response'] == 'reject'
32
+ delete_data(request, association_request_path)
33
+ else
34
+ raise RestErrorResponse.new(400, "response parameter was missing or set to the wrong value (must be accept or reject)")
35
+ end
36
+ json_response(200, { 'organization' => { 'name' => orgname } })
37
+ end
38
+ end
39
+ end
40
+ end
@@ -1,19 +1,19 @@
1
- require 'ffi_yajl'
2
- require 'chef_zero/rest_base'
3
-
4
- module ChefZero
5
- module Endpoints
6
- # /users/NAME/association_requests/count
7
- class UserAssociationRequestsCountEndpoint < RestBase
8
- def get(request)
9
- get_data(request, request.rest_path[0..-3])
10
-
11
- username = request.rest_path[1]
12
- result = list_data(request, [ 'organizations' ]).select do |org|
13
- exists_data?(request, [ 'organizations', org, 'association_requests', username ])
14
- end
15
- json_response(200, { "value" => result.size })
16
- end
17
- end
18
- end
19
- end
1
+ require 'ffi_yajl'
2
+ require 'chef_zero/rest_base'
3
+
4
+ module ChefZero
5
+ module Endpoints
6
+ # /users/NAME/association_requests/count
7
+ class UserAssociationRequestsCountEndpoint < RestBase
8
+ def get(request)
9
+ get_data(request, request.rest_path[0..-3])
10
+
11
+ username = request.rest_path[1]
12
+ result = list_data(request, [ 'organizations' ]).select do |org|
13
+ exists_data?(request, [ 'organizations', org, 'association_requests', username ])
14
+ end
15
+ json_response(200, { "value" => result.size })
16
+ end
17
+ end
18
+ end
19
+ end
@@ -1,19 +1,19 @@
1
- require 'ffi_yajl'
2
- require 'chef_zero/rest_base'
3
-
4
- module ChefZero
5
- module Endpoints
6
- # /users/USER/association_requests
7
- class UserAssociationRequestsEndpoint < RestBase
8
- def get(request)
9
- get_data(request, request.rest_path[0..-2])
10
- username = request.rest_path[1]
11
- result = list_data(request, [ 'organizations' ]).select do |org|
12
- exists_data?(request, [ 'organizations', org, 'association_requests', username ])
13
- end
14
- result = result.map { |org| { "id" => "#{username}-#{org}", "orgname" => org } }
15
- json_response(200, result)
16
- end
17
- end
18
- end
19
- end
1
+ require 'ffi_yajl'
2
+ require 'chef_zero/rest_base'
3
+
4
+ module ChefZero
5
+ module Endpoints
6
+ # /users/USER/association_requests
7
+ class UserAssociationRequestsEndpoint < RestBase
8
+ def get(request)
9
+ get_data(request, request.rest_path[0..-2])
10
+ username = request.rest_path[1]
11
+ result = list_data(request, [ 'organizations' ]).select do |org|
12
+ exists_data?(request, [ 'organizations', org, 'association_requests', username ])
13
+ end
14
+ result = result.map { |org| { "id" => "#{username}-#{org}", "orgname" => org } }
15
+ json_response(200, result)
16
+ end
17
+ end
18
+ end
19
+ end
@@ -1,22 +1,22 @@
1
- require 'ffi_yajl'
2
- require 'chef_zero/rest_base'
3
-
4
- module ChefZero
5
- module Endpoints
6
- # /users/USER/organizations
7
- class UserOrganizationsEndpoint < RestBase
8
- def get(request)
9
- username = request.rest_path[1]
10
- result = list_data(request, [ 'organizations' ]).select do |orgname|
11
- exists_data?(request, [ 'organizations', orgname, 'users', username ])
12
- end
13
- result = result.map do |orgname|
14
- org = get_data(request, [ 'organizations', orgname, 'org' ])
15
- org = FFI_Yajl::Parser.parse(org, :create_additions => false)
16
- { "organization" => ChefData::DataNormalizer.normalize_organization(org, orgname) }
17
- end
18
- json_response(200, result)
19
- end
20
- end
21
- end
22
- end
1
+ require 'ffi_yajl'
2
+ require 'chef_zero/rest_base'
3
+
4
+ module ChefZero
5
+ module Endpoints
6
+ # /users/USER/organizations
7
+ class UserOrganizationsEndpoint < RestBase
8
+ def get(request)
9
+ username = request.rest_path[1]
10
+ result = list_data(request, [ 'organizations' ]).select do |orgname|
11
+ exists_data?(request, [ 'organizations', orgname, 'users', username ])
12
+ end
13
+ result = result.map do |orgname|
14
+ org = get_data(request, [ 'organizations', orgname, 'org' ])
15
+ org = FFI_Yajl::Parser.parse(org, :create_additions => false)
16
+ { "organization" => ChefData::DataNormalizer.normalize_organization(org, orgname) }
17
+ end
18
+ json_response(200, result)
19
+ end
20
+ end
21
+ end
22
+ end
@@ -1,12 +1,12 @@
1
- require 'chef_zero/rest_base'
2
-
3
- module ChefZero
4
- module Endpoints
5
- # /version
6
- class VersionEndpoint < RestBase
7
- def get(request)
8
- text_response(200, "chef-zero #{ChefZero::VERSION}\n")
9
- end
10
- end
11
- end
12
- end
1
+ require 'chef_zero/rest_base'
2
+
3
+ module ChefZero
4
+ module Endpoints
5
+ # /version
6
+ class VersionEndpoint < RestBase
7
+ def get(request)
8
+ text_response(200, "chef-zero #{ChefZero::VERSION}\n")
9
+ end
10
+ end
11
+ end
12
+ end
data/lib/chef_zero/log.rb CHANGED
@@ -1,7 +1,7 @@
1
- require 'mixlib/log'
2
-
3
- module ChefZero
4
- class Log
5
- extend Mixlib::Log
6
- end
7
- end
1
+ require 'mixlib/log'
2
+
3
+ module ChefZero
4
+ class Log
5
+ extend Mixlib::Log
6
+ end
7
+ end
@@ -1,242 +1,242 @@
1
- require 'chef_zero/rest_request'
2
- require 'chef_zero/rest_error_response'
3
- require 'chef_zero/data_store/data_not_found_error'
4
- require 'chef_zero/chef_data/acl_path'
5
-
6
- module ChefZero
7
- class RestBase
8
- def initialize(server)
9
- @server = server
10
- end
11
-
12
- attr_reader :server
13
-
14
- def data_store
15
- server.data_store
16
- end
17
-
18
- def check_api_version(request)
19
- version = request.api_version
20
- return nil if version.nil? # Not present in headers
21
-
22
- if version.to_i.to_s != version.to_s # Version is not an Integer
23
- return json_response(406, { "username" => request.requestor }, -1, -1)
24
- elsif version.to_i > MAX_API_VERSION or version.to_i < MIN_API_VERSION
25
- response = {
26
- "error" => "invalid-x-ops-server-api-version",
27
- "message" => "Specified version #{version} not supported",
28
- "min_api_version" => MIN_API_VERSION,
29
- "max_api_version" => MAX_API_VERSION
30
- }
31
- return json_response(406, response, version, -1)
32
- else
33
- return nil
34
- end
35
- end
36
-
37
- def call(request)
38
- response = check_api_version(request)
39
- return response unless response.nil?
40
-
41
- method = request.method.downcase.to_sym
42
- if !self.respond_to?(method)
43
- accept_methods = [:get, :put, :post, :delete].select { |m| self.respond_to?(m) }
44
- accept_methods_str = accept_methods.map { |m| m.to_s.upcase }.join(', ')
45
- return [405, {"Content-Type" => "text/plain", "Allow" => accept_methods_str}, "Bad request method for '#{request.env['REQUEST_PATH']}': #{request.env['REQUEST_METHOD']}"]
46
- end
47
- if json_only && !accepts?(request, 'application', 'json')
48
- return [406, {"Content-Type" => "text/plain"}, "Must accept application/json"]
49
- end
50
- # Dispatch to get()/post()/put()/delete()
51
- begin
52
- self.send(method, request)
53
- rescue RestErrorResponse => e
54
- ChefZero::Log.debug("#{e.inspect}\n#{e.backtrace.join("\n")}")
55
- error(e.response_code, e.error)
56
- end
57
- end
58
-
59
- def json_only
60
- true
61
- end
62
-
63
- def accepts?(request, category, type)
64
- # If HTTP_ACCEPT is not sent at all, assume it accepts anything
65
- # This parses as per http://tools.ietf.org/html/rfc7231#section-5.3
66
- return true if !request.env['HTTP_ACCEPT']
67
- accepts = request.env['HTTP_ACCEPT'].split(/,\s*/).map { |x| x.split(';',2)[0].strip }
68
- return accepts.include?("#{category}/#{type}") || accepts.include?("#{category}/*") || accepts.include?('*/*')
69
- end
70
-
71
- def get_data(request, rest_path=nil, *options)
72
- rest_path ||= request.rest_path
73
- begin
74
- data_store.get(rest_path, request)
75
- rescue DataStore::DataNotFoundError
76
- if options.include?(:nil)
77
- nil
78
- elsif options.include?(:data_store_exceptions)
79
- raise
80
- else
81
- raise RestErrorResponse.new(404, "Object not found: #{build_uri(request.base_uri, rest_path)}")
82
- end
83
- end
84
- end
85
-
86
- def list_data(request, rest_path=nil, *options)
87
- rest_path ||= request.rest_path
88
- begin
89
- data_store.list(rest_path)
90
- rescue DataStore::DataNotFoundError
91
- if options.include?(:data_store_exceptions)
92
- raise
93
- else
94
- raise RestErrorResponse.new(404, "Object not found: #{build_uri(request.base_uri, rest_path)}")
95
- end
96
- end
97
- end
98
-
99
- def delete_data(request, rest_path=nil, *options)
100
- rest_path ||= request.rest_path
101
- begin
102
- data_store.delete(rest_path, *options)
103
- rescue DataStore::DataNotFoundError
104
- if options.include?(:data_store_exceptions)
105
- raise
106
- else
107
- raise RestErrorResponse.new(404, "Object not found: #{build_uri(request.base_uri, request.rest_path)}")
108
- end
109
- end
110
-
111
- begin
112
- acl_path = ChefData::AclPath.get_acl_data_path(rest_path)
113
- data_store.delete(acl_path) if acl_path
114
- rescue DataStore::DataNotFoundError
115
- end
116
- end
117
-
118
- def delete_data_dir(request, rest_path, *options)
119
- rest_path ||= request.rest_path
120
- begin
121
- data_store.delete_dir(rest_path, *options)
122
- rescue DataStore::DataNotFoundError
123
- if options.include?(:data_store_exceptions)
124
- raise
125
- else
126
- raise RestErrorResponse.new(404, "Object not found: #{build_uri(request.base_uri, request.rest_path)}")
127
- end
128
- end
129
-
130
- begin
131
- acl_path = ChefData::AclPath.get_acl_data_path(rest_path)
132
- data_store.delete(acl_path) if acl_path
133
- rescue DataStore::DataNotFoundError
134
- end
135
- end
136
-
137
- def set_data(request, rest_path, data, *options)
138
- rest_path ||= request.rest_path
139
- begin
140
- data_store.set(rest_path, data, *options, :requestor => request.requestor)
141
- rescue DataStore::DataNotFoundError
142
- if options.include?(:data_store_exceptions)
143
- raise
144
- else
145
- raise RestErrorResponse.new(404, "Object not found: #{build_uri(request.base_uri, request.rest_path)}")
146
- end
147
- end
148
- end
149
-
150
- def create_data_dir(request, rest_path, name, *options)
151
- rest_path ||= request.rest_path
152
- begin
153
- data_store.create_dir(rest_path, name, *options, :requestor => request.requestor)
154
- rescue DataStore::DataNotFoundError
155
- if options.include?(:data_store_exceptions)
156
- raise
157
- else
158
- raise RestErrorResponse.new(404, "Parent not found: #{build_uri(request.base_uri, request.rest_path)}")
159
- end
160
- rescue DataStore::DataAlreadyExistsError
161
- if options.include?(:data_store_exceptions)
162
- raise
163
- else
164
- raise RestErrorResponse.new(409, "Object already exists: #{build_uri(request.base_uri, request.rest_path + [name])}")
165
- end
166
- end
167
- end
168
-
169
- def create_data(request, rest_path, name, data, *options)
170
- rest_path ||= request.rest_path
171
- begin
172
- data_store.create(rest_path, name, data, *options, :requestor => request.requestor)
173
- rescue DataStore::DataNotFoundError
174
- if options.include?(:data_store_exceptions)
175
- raise
176
- else
177
- raise RestErrorResponse.new(404, "Parent not found: #{build_uri(request.base_uri, request.rest_path)}")
178
- end
179
- rescue DataStore::DataAlreadyExistsError
180
- if options.include?(:data_store_exceptions)
181
- raise
182
- else
183
- raise RestErrorResponse.new(409, "Object already exists: #{build_uri(request.base_uri, request.rest_path + [name])}")
184
- end
185
- end
186
- end
187
-
188
- def exists_data?(request, rest_path=nil)
189
- rest_path ||= request.rest_path
190
- data_store.exists?(rest_path)
191
- end
192
-
193
- def exists_data_dir?(request, rest_path=nil)
194
- rest_path ||= request.rest_path
195
- data_store.exists_dir?(rest_path)
196
- end
197
-
198
- def error(response_code, error)
199
- json_response(response_code, {"error" => [error]})
200
- end
201
-
202
- def json_response(response_code, json, request_version=0, response_version=0)
203
- already_json_response(response_code, FFI_Yajl::Encoder.encode(json, :pretty => true), request_version, response_version)
204
- end
205
-
206
- def text_response(response_code, text)
207
- [response_code, {"Content-Type" => "text/plain"}, text]
208
- end
209
-
210
- def already_json_response(response_code, json_text, request_version=0, response_version=0)
211
- header = { "min_version" => MIN_API_VERSION.to_s, "max_version" => MAX_API_VERSION.to_s,
212
- "request_version" => request_version.to_s,
213
- "response_version" => response_version.to_s }
214
- [ response_code,
215
- { "Content-Type" => "application/json",
216
- "X-Ops-Server-API-Version" => FFI_Yajl::Encoder.encode(header) },
217
- json_text ]
218
- end
219
-
220
- # To be called from inside rest endpoints
221
- def build_uri(base_uri, rest_path)
222
- if server.options[:single_org]
223
- # Strip off /organizations/chef if we are in single org mode
224
- if rest_path[0..1] != [ 'organizations', server.options[:single_org] ]
225
- raise "Unexpected URL #{rest_path[0..1]} passed to build_uri in single org mode"
226
- else
227
- "#{base_uri}/#{rest_path[2..-1].join('/')}"
228
- end
229
- else
230
- "#{base_uri}/#{rest_path.join('/')}"
231
- end
232
- end
233
-
234
- def self.build_uri(base_uri, rest_path)
235
- "#{base_uri}/#{rest_path.join('/')}"
236
- end
237
-
238
- def populate_defaults(request, response)
239
- response
240
- end
241
- end
242
- end
1
+ require 'chef_zero/rest_request'
2
+ require 'chef_zero/rest_error_response'
3
+ require 'chef_zero/data_store/data_not_found_error'
4
+ require 'chef_zero/chef_data/acl_path'
5
+
6
+ module ChefZero
7
+ class RestBase
8
+ def initialize(server)
9
+ @server = server
10
+ end
11
+
12
+ attr_reader :server
13
+
14
+ def data_store
15
+ server.data_store
16
+ end
17
+
18
+ def check_api_version(request)
19
+ version = request.api_version
20
+ return nil if version.nil? # Not present in headers
21
+
22
+ if version.to_i.to_s != version.to_s # Version is not an Integer
23
+ return json_response(406, { "username" => request.requestor }, -1, -1)
24
+ elsif version.to_i > MAX_API_VERSION or version.to_i < MIN_API_VERSION
25
+ response = {
26
+ "error" => "invalid-x-ops-server-api-version",
27
+ "message" => "Specified version #{version} not supported",
28
+ "min_api_version" => MIN_API_VERSION,
29
+ "max_api_version" => MAX_API_VERSION
30
+ }
31
+ return json_response(406, response, version, -1)
32
+ else
33
+ return nil
34
+ end
35
+ end
36
+
37
+ def call(request)
38
+ response = check_api_version(request)
39
+ return response unless response.nil?
40
+
41
+ method = request.method.downcase.to_sym
42
+ if !self.respond_to?(method)
43
+ accept_methods = [:get, :put, :post, :delete].select { |m| self.respond_to?(m) }
44
+ accept_methods_str = accept_methods.map { |m| m.to_s.upcase }.join(', ')
45
+ return [405, {"Content-Type" => "text/plain", "Allow" => accept_methods_str}, "Bad request method for '#{request.env['REQUEST_PATH']}': #{request.env['REQUEST_METHOD']}"]
46
+ end
47
+ if json_only && !accepts?(request, 'application', 'json')
48
+ return [406, {"Content-Type" => "text/plain"}, "Must accept application/json"]
49
+ end
50
+ # Dispatch to get()/post()/put()/delete()
51
+ begin
52
+ self.send(method, request)
53
+ rescue RestErrorResponse => e
54
+ ChefZero::Log.debug("#{e.inspect}\n#{e.backtrace.join("\n")}")
55
+ error(e.response_code, e.error)
56
+ end
57
+ end
58
+
59
+ def json_only
60
+ true
61
+ end
62
+
63
+ def accepts?(request, category, type)
64
+ # If HTTP_ACCEPT is not sent at all, assume it accepts anything
65
+ # This parses as per http://tools.ietf.org/html/rfc7231#section-5.3
66
+ return true if !request.env['HTTP_ACCEPT']
67
+ accepts = request.env['HTTP_ACCEPT'].split(/,\s*/).map { |x| x.split(';',2)[0].strip }
68
+ return accepts.include?("#{category}/#{type}") || accepts.include?("#{category}/*") || accepts.include?('*/*')
69
+ end
70
+
71
+ def get_data(request, rest_path=nil, *options)
72
+ rest_path ||= request.rest_path
73
+ begin
74
+ data_store.get(rest_path, request)
75
+ rescue DataStore::DataNotFoundError
76
+ if options.include?(:nil)
77
+ nil
78
+ elsif options.include?(:data_store_exceptions)
79
+ raise
80
+ else
81
+ raise RestErrorResponse.new(404, "Object not found: #{build_uri(request.base_uri, rest_path)}")
82
+ end
83
+ end
84
+ end
85
+
86
+ def list_data(request, rest_path=nil, *options)
87
+ rest_path ||= request.rest_path
88
+ begin
89
+ data_store.list(rest_path)
90
+ rescue DataStore::DataNotFoundError
91
+ if options.include?(:data_store_exceptions)
92
+ raise
93
+ else
94
+ raise RestErrorResponse.new(404, "Object not found: #{build_uri(request.base_uri, rest_path)}")
95
+ end
96
+ end
97
+ end
98
+
99
+ def delete_data(request, rest_path=nil, *options)
100
+ rest_path ||= request.rest_path
101
+ begin
102
+ data_store.delete(rest_path, *options)
103
+ rescue DataStore::DataNotFoundError
104
+ if options.include?(:data_store_exceptions)
105
+ raise
106
+ else
107
+ raise RestErrorResponse.new(404, "Object not found: #{build_uri(request.base_uri, request.rest_path)}")
108
+ end
109
+ end
110
+
111
+ begin
112
+ acl_path = ChefData::AclPath.get_acl_data_path(rest_path)
113
+ data_store.delete(acl_path) if acl_path
114
+ rescue DataStore::DataNotFoundError
115
+ end
116
+ end
117
+
118
+ def delete_data_dir(request, rest_path, *options)
119
+ rest_path ||= request.rest_path
120
+ begin
121
+ data_store.delete_dir(rest_path, *options)
122
+ rescue DataStore::DataNotFoundError
123
+ if options.include?(:data_store_exceptions)
124
+ raise
125
+ else
126
+ raise RestErrorResponse.new(404, "Object not found: #{build_uri(request.base_uri, request.rest_path)}")
127
+ end
128
+ end
129
+
130
+ begin
131
+ acl_path = ChefData::AclPath.get_acl_data_path(rest_path)
132
+ data_store.delete(acl_path) if acl_path
133
+ rescue DataStore::DataNotFoundError
134
+ end
135
+ end
136
+
137
+ def set_data(request, rest_path, data, *options)
138
+ rest_path ||= request.rest_path
139
+ begin
140
+ data_store.set(rest_path, data, *options, :requestor => request.requestor)
141
+ rescue DataStore::DataNotFoundError
142
+ if options.include?(:data_store_exceptions)
143
+ raise
144
+ else
145
+ raise RestErrorResponse.new(404, "Object not found: #{build_uri(request.base_uri, request.rest_path)}")
146
+ end
147
+ end
148
+ end
149
+
150
+ def create_data_dir(request, rest_path, name, *options)
151
+ rest_path ||= request.rest_path
152
+ begin
153
+ data_store.create_dir(rest_path, name, *options, :requestor => request.requestor)
154
+ rescue DataStore::DataNotFoundError
155
+ if options.include?(:data_store_exceptions)
156
+ raise
157
+ else
158
+ raise RestErrorResponse.new(404, "Parent not found: #{build_uri(request.base_uri, request.rest_path)}")
159
+ end
160
+ rescue DataStore::DataAlreadyExistsError
161
+ if options.include?(:data_store_exceptions)
162
+ raise
163
+ else
164
+ raise RestErrorResponse.new(409, "Object already exists: #{build_uri(request.base_uri, request.rest_path + [name])}")
165
+ end
166
+ end
167
+ end
168
+
169
+ def create_data(request, rest_path, name, data, *options)
170
+ rest_path ||= request.rest_path
171
+ begin
172
+ data_store.create(rest_path, name, data, *options, :requestor => request.requestor)
173
+ rescue DataStore::DataNotFoundError
174
+ if options.include?(:data_store_exceptions)
175
+ raise
176
+ else
177
+ raise RestErrorResponse.new(404, "Parent not found: #{build_uri(request.base_uri, request.rest_path)}")
178
+ end
179
+ rescue DataStore::DataAlreadyExistsError
180
+ if options.include?(:data_store_exceptions)
181
+ raise
182
+ else
183
+ raise RestErrorResponse.new(409, "Object already exists: #{build_uri(request.base_uri, request.rest_path + [name])}")
184
+ end
185
+ end
186
+ end
187
+
188
+ def exists_data?(request, rest_path=nil)
189
+ rest_path ||= request.rest_path
190
+ data_store.exists?(rest_path)
191
+ end
192
+
193
+ def exists_data_dir?(request, rest_path=nil)
194
+ rest_path ||= request.rest_path
195
+ data_store.exists_dir?(rest_path)
196
+ end
197
+
198
+ def error(response_code, error)
199
+ json_response(response_code, {"error" => [error]})
200
+ end
201
+
202
+ def json_response(response_code, json, request_version=0, response_version=0)
203
+ already_json_response(response_code, FFI_Yajl::Encoder.encode(json, :pretty => true), request_version, response_version)
204
+ end
205
+
206
+ def text_response(response_code, text)
207
+ [response_code, {"Content-Type" => "text/plain"}, text]
208
+ end
209
+
210
+ def already_json_response(response_code, json_text, request_version=0, response_version=0)
211
+ header = { "min_version" => MIN_API_VERSION.to_s, "max_version" => MAX_API_VERSION.to_s,
212
+ "request_version" => request_version.to_s,
213
+ "response_version" => response_version.to_s }
214
+ [ response_code,
215
+ { "Content-Type" => "application/json",
216
+ "X-Ops-Server-API-Version" => FFI_Yajl::Encoder.encode(header) },
217
+ json_text ]
218
+ end
219
+
220
+ # To be called from inside rest endpoints
221
+ def build_uri(base_uri, rest_path)
222
+ if server.options[:single_org]
223
+ # Strip off /organizations/chef if we are in single org mode
224
+ if rest_path[0..1] != [ 'organizations', server.options[:single_org] ]
225
+ raise "Unexpected URL #{rest_path[0..1]} passed to build_uri in single org mode"
226
+ else
227
+ "#{base_uri}/#{rest_path[2..-1].join('/')}"
228
+ end
229
+ else
230
+ "#{base_uri}/#{rest_path.join('/')}"
231
+ end
232
+ end
233
+
234
+ def self.build_uri(base_uri, rest_path)
235
+ "#{base_uri}/#{rest_path.join('/')}"
236
+ end
237
+
238
+ def populate_defaults(request, response)
239
+ response
240
+ end
241
+ end
242
+ end