chef-zero 4.3.0 → 4.3.1

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