chef-zero 4.2.3 → 4.3.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 (102) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +201 -201
  3. data/README.md +155 -150
  4. data/Rakefile +31 -31
  5. data/bin/chef-zero +100 -100
  6. data/lib/chef_zero.rb +10 -7
  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 +207 -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 -88
  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 -0
  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 -29
  52. data/lib/chef_zero/endpoints/organization_authenticate_user_endpoint.rb +26 -26
  53. data/lib/chef_zero/endpoints/organization_endpoint.rb +46 -41
  54. data/lib/chef_zero/endpoints/organization_user_base.rb +15 -0
  55. data/lib/chef_zero/endpoints/organization_user_endpoint.rb +26 -48
  56. data/lib/chef_zero/endpoints/organization_users_endpoint.rb +43 -14
  57. data/lib/chef_zero/endpoints/organization_validator_key_endpoint.rb +20 -20
  58. data/lib/chef_zero/endpoints/organizations_endpoint.rb +62 -55
  59. data/lib/chef_zero/endpoints/policies_endpoint.rb +151 -154
  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 -192
  68. data/lib/chef_zero/endpoints/searches_endpoint.rb +18 -18
  69. data/lib/chef_zero/endpoints/server_api_version_endpoint.rb +14 -0
  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 -214
  78. data/lib/chef_zero/rest_error_response.rb +11 -11
  79. data/lib/chef_zero/rest_request.rb +69 -65
  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 -637
  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 -56
  95. data/spec/search_spec.rb +32 -32
  96. data/spec/server_spec.rb +92 -91
  97. data/spec/socketless_server_map_spec.rb +76 -76
  98. data/spec/support/oc_pedant.rb +132 -134
  99. data/spec/support/stickywicket.pem +27 -27
  100. metadata +10 -15
  101. data/spec/run_pedant.rb +0 -103
  102. data/spec/support/pedant.rb +0 -129
@@ -1,18 +1,18 @@
1
- require 'chef_zero/rest_base'
2
-
3
- module ChefZero
4
- module Endpoints
5
- # /search
6
- class SearchesEndpoint < RestBase
7
- def get(request)
8
- # Get the result
9
- result_hash = {}
10
- indices = (%w(client environment node role) + data_store.list(request.rest_path[0..1] + ['data'])).sort
11
- indices.each do |index|
12
- result_hash[index] = build_uri(request.base_uri, request.rest_path + [index])
13
- end
14
- json_response(200, result_hash)
15
- end
16
- end
17
- end
18
- end
1
+ require 'chef_zero/rest_base'
2
+
3
+ module ChefZero
4
+ module Endpoints
5
+ # /search
6
+ class SearchesEndpoint < RestBase
7
+ def get(request)
8
+ # Get the result
9
+ result_hash = {}
10
+ indices = (%w(client environment node role) + data_store.list(request.rest_path[0..1] + ['data'])).sort
11
+ indices.each do |index|
12
+ result_hash[index] = build_uri(request.base_uri, request.rest_path + [index])
13
+ end
14
+ json_response(200, result_hash)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,14 @@
1
+ require 'chef_zero/rest_base'
2
+
3
+ module ChefZero
4
+ module Endpoints
5
+ # /server_api_version
6
+ class ServerAPIVersionEndpoint < RestBase
7
+ API_VERSION = 1
8
+ def get(request)
9
+ json_response(200, {"min_api_version"=>MIN_API_VERSION, "max_api_version"=>MAX_API_VERSION},
10
+ request.api_version, API_VERSION)
11
+ end
12
+ end
13
+ end
14
+ end
@@ -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
- 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,214 +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 call(request)
19
- method = request.method.downcase.to_sym
20
- if !self.respond_to?(method)
21
- accept_methods = [:get, :put, :post, :delete].select { |m| self.respond_to?(m) }
22
- accept_methods_str = accept_methods.map { |m| m.to_s.upcase }.join(', ')
23
- return [405, {"Content-Type" => "text/plain", "Allow" => accept_methods_str}, "Bad request method for '#{request.env['REQUEST_PATH']}': #{request.env['REQUEST_METHOD']}"]
24
- end
25
- if json_only && !accepts?(request, 'application', 'json')
26
- return [406, {"Content-Type" => "text/plain"}, "Must accept application/json"]
27
- end
28
- # Dispatch to get()/post()/put()/delete()
29
- begin
30
- self.send(method, request)
31
- rescue RestErrorResponse => e
32
- ChefZero::Log.debug("#{e.inspect}\n#{e.backtrace.join("\n")}")
33
- error(e.response_code, e.error)
34
- end
35
- end
36
-
37
- def json_only
38
- true
39
- end
40
-
41
- def accepts?(request, category, type)
42
- # If HTTP_ACCEPT is not sent at all, assume it accepts anything
43
- # This parses as per http://tools.ietf.org/html/rfc7231#section-5.3
44
- return true if !request.env['HTTP_ACCEPT']
45
- accepts = request.env['HTTP_ACCEPT'].split(/,\s*/).map { |x| x.split(';',2)[0].strip }
46
- return accepts.include?("#{category}/#{type}") || accepts.include?("#{category}/*") || accepts.include?('*/*')
47
- end
48
-
49
- def get_data(request, rest_path=nil, *options)
50
- rest_path ||= request.rest_path
51
- begin
52
- data_store.get(rest_path, request)
53
- rescue DataStore::DataNotFoundError
54
- if options.include?(:nil)
55
- nil
56
- elsif options.include?(:data_store_exceptions)
57
- raise
58
- else
59
- raise RestErrorResponse.new(404, "Object not found: #{build_uri(request.base_uri, rest_path)}")
60
- end
61
- end
62
- end
63
-
64
- def list_data(request, rest_path=nil, *options)
65
- rest_path ||= request.rest_path
66
- begin
67
- data_store.list(rest_path)
68
- rescue DataStore::DataNotFoundError
69
- if options.include?(:data_store_exceptions)
70
- raise
71
- else
72
- raise RestErrorResponse.new(404, "Object not found: #{build_uri(request.base_uri, rest_path)}")
73
- end
74
- end
75
- end
76
-
77
- def delete_data(request, rest_path=nil, *options)
78
- rest_path ||= request.rest_path
79
- begin
80
- data_store.delete(rest_path, *options)
81
- rescue DataStore::DataNotFoundError
82
- if options.include?(:data_store_exceptions)
83
- raise
84
- else
85
- raise RestErrorResponse.new(404, "Object not found: #{build_uri(request.base_uri, request.rest_path)}")
86
- end
87
- end
88
-
89
- begin
90
- acl_path = ChefData::AclPath.get_acl_data_path(rest_path)
91
- data_store.delete(acl_path) if acl_path
92
- rescue DataStore::DataNotFoundError
93
- end
94
- end
95
-
96
- def delete_data_dir(request, rest_path, *options)
97
- rest_path ||= request.rest_path
98
- begin
99
- data_store.delete_dir(rest_path, *options)
100
- rescue DataStore::DataNotFoundError
101
- if options.include?(:data_store_exceptions)
102
- raise
103
- else
104
- raise RestErrorResponse.new(404, "Object not found: #{build_uri(request.base_uri, request.rest_path)}")
105
- end
106
- end
107
-
108
- begin
109
- acl_path = ChefData::AclPath.get_acl_data_path(rest_path)
110
- data_store.delete(acl_path) if acl_path
111
- rescue DataStore::DataNotFoundError
112
- end
113
- end
114
-
115
- def set_data(request, rest_path, data, *options)
116
- rest_path ||= request.rest_path
117
- begin
118
- data_store.set(rest_path, data, *options, :requestor => request.requestor)
119
- rescue DataStore::DataNotFoundError
120
- if options.include?(:data_store_exceptions)
121
- raise
122
- else
123
- raise RestErrorResponse.new(404, "Object not found: #{build_uri(request.base_uri, request.rest_path)}")
124
- end
125
- end
126
- end
127
-
128
- def create_data_dir(request, rest_path, name, *options)
129
- rest_path ||= request.rest_path
130
- begin
131
- data_store.create_dir(rest_path, name, *options, :requestor => request.requestor)
132
- rescue DataStore::DataNotFoundError
133
- if options.include?(:data_store_exceptions)
134
- raise
135
- else
136
- raise RestErrorResponse.new(404, "Parent not found: #{build_uri(request.base_uri, request.rest_path)}")
137
- end
138
- rescue DataStore::DataAlreadyExistsError
139
- if options.include?(:data_store_exceptions)
140
- raise
141
- else
142
- raise RestErrorResponse.new(409, "Object already exists: #{build_uri(request.base_uri, request.rest_path + [name])}")
143
- end
144
- end
145
- end
146
-
147
- def create_data(request, rest_path, name, data, *options)
148
- rest_path ||= request.rest_path
149
- begin
150
- data_store.create(rest_path, name, data, *options, :requestor => request.requestor)
151
- rescue DataStore::DataNotFoundError
152
- if options.include?(:data_store_exceptions)
153
- raise
154
- else
155
- raise RestErrorResponse.new(404, "Parent not found: #{build_uri(request.base_uri, request.rest_path)}")
156
- end
157
- rescue DataStore::DataAlreadyExistsError
158
- if options.include?(:data_store_exceptions)
159
- raise
160
- else
161
- raise RestErrorResponse.new(409, "Object already exists: #{build_uri(request.base_uri, request.rest_path + [name])}")
162
- end
163
- end
164
- end
165
-
166
- def exists_data?(request, rest_path=nil)
167
- rest_path ||= request.rest_path
168
- data_store.exists?(rest_path)
169
- end
170
-
171
- def exists_data_dir?(request, rest_path=nil)
172
- rest_path ||= request.rest_path
173
- data_store.exists_dir?(rest_path)
174
- end
175
-
176
- def error(response_code, error)
177
- json_response(response_code, {"error" => [error]})
178
- end
179
-
180
- def json_response(response_code, json)
181
- already_json_response(response_code, FFI_Yajl::Encoder.encode(json, :pretty => true))
182
- end
183
-
184
- def text_response(response_code, text)
185
- [response_code, {"Content-Type" => "text/plain"}, text]
186
- end
187
-
188
- def already_json_response(response_code, json_text)
189
- [response_code, {"Content-Type" => "application/json"}, json_text]
190
- end
191
-
192
- # To be called from inside rest endpoints
193
- def build_uri(base_uri, rest_path)
194
- if server.options[:single_org]
195
- # Strip off /organizations/chef if we are in single org mode
196
- if rest_path[0..1] != [ 'organizations', server.options[:single_org] ]
197
- raise "Unexpected URL #{rest_path[0..1]} passed to build_uri in single org mode"
198
- else
199
- "#{base_uri}/#{rest_path[2..-1].join('/')}"
200
- end
201
- else
202
- "#{base_uri}/#{rest_path.join('/')}"
203
- end
204
- end
205
-
206
- def self.build_uri(base_uri, rest_path)
207
- "#{base_uri}/#{rest_path.join('/')}"
208
- end
209
-
210
- def populate_defaults(request, response)
211
- response
212
- end
213
- end
214
- 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