summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJordan Running <jr@getchef.com>2016-01-14 16:56:12 -0600
committerJordan Running <jr@getchef.com>2016-01-29 15:30:21 -0600
commit6307a675f0b84f47b65b76ca76ab8bcdf3e4d543 (patch)
treeacba667fe490e410482a00b2cf8cf37e21d17b6a
parent2c71be878c318116adbf406717bf99ecaebe05d7 (diff)
downloadchef-zero-jr/pedant-keys.tar.gz
Make user keys endpoint specs passjr/pedant-keys
- Add UserKeyEndpoint; handles GET/DELETE/PUT `/user_keys/USERNAME/keys/KEY_NAME`. - Add UserKeysEndpoint; handles GET, POST `/user_keys/USERNAME`. - RestBase - Add some docs. - #json_response - Move `request_version` and `response_version` params into options hash. - Accept `:headers` option for additional headers to be set on response. - #already_json_response: #json_response - RestObjectEndpoint - Add some docs. - Move some repeated logic into methods (`identity_key_value`, `is_rename?`). - #patch_request_body: Add `:except` option to allow skipping keys not wanted in output. - ActorsEndpoint - #post: Store `public_key` in store under `user_keys/USERNAME/keys/default` instead of with user. - ActorEndpoint - #delete: Delete user keys when user is deleted. - #put: Store `public_key` in store under `user_keys` as above. - #populate_defaults: Retrieve user's default `public_key` from store and merge with user response. - OrganizationUserEndpoint - #get: Retrieve user's default `public_key` from store and merge with user response. - PrincipalEndpont - #get - Retrieve user's default `public_key` from store and merge with user response. - Refactor complex nested `if`s.
-rw-r--r--lib/chef_zero/chef_data/data_normalizer.rb1
-rw-r--r--lib/chef_zero/endpoints/actor_endpoint.rb124
-rw-r--r--lib/chef_zero/endpoints/actors_endpoint.rb40
-rw-r--r--lib/chef_zero/endpoints/organization_user_endpoint.rb19
-rw-r--r--lib/chef_zero/endpoints/principal_endpoint.rb99
-rw-r--r--lib/chef_zero/endpoints/rest_object_endpoint.rb42
-rw-r--r--lib/chef_zero/endpoints/server_api_version_endpoint.rb2
-rw-r--r--lib/chef_zero/endpoints/user_key_endpoint.rb32
-rw-r--r--lib/chef_zero/endpoints/user_keys_endpoint.rb80
-rw-r--r--lib/chef_zero/rest_base.rb83
-rw-r--r--lib/chef_zero/server.rb4
-rw-r--r--spec/run_oc_pedant.rb4
12 files changed, 444 insertions, 86 deletions
diff --git a/lib/chef_zero/chef_data/data_normalizer.rb b/lib/chef_zero/chef_data/data_normalizer.rb
index 95f3daa..554bd70 100644
--- a/lib/chef_zero/chef_data/data_normalizer.rb
+++ b/lib/chef_zero/chef_data/data_normalizer.rb
@@ -36,7 +36,6 @@ module ChefZero
def self.normalize_user(user, name, identity_keys, osc_compat, method=nil)
user[identity_keys.first] ||= name
- user['public_key'] ||= PUBLIC_KEY
user['admin'] ||= false
user['admin'] = !!user['admin']
user['openid'] ||= nil
diff --git a/lib/chef_zero/endpoints/actor_endpoint.rb b/lib/chef_zero/endpoints/actor_endpoint.rb
index 1572ac1..a491965 100644
--- a/lib/chef_zero/endpoints/actor_endpoint.rb
+++ b/lib/chef_zero/endpoints/actor_endpoint.rb
@@ -8,15 +8,26 @@ module ChefZero
# /organizations/ORG/users/NAME
# /users/NAME
class ActorEndpoint < RestObjectEndpoint
+ DEFAULT_PUBLIC_KEY_NAME = "default"
+ DEFAULT_PUBLIC_KEY_EXPIRATION_DATE = "infinity"
+
def delete(request)
result = super
+ username = request.rest_path[1]
+
if request.rest_path[0] == 'users'
list_data(request, [ 'organizations' ]).each do |org|
begin
- delete_data(request, [ 'organizations', org, 'users', request.rest_path[1] ], :data_store_exceptions)
+ delete_data(request, [ 'organizations', org, 'users', username ], :data_store_exceptions)
rescue DataStore::DataNotFoundError
end
end
+
+ begin
+ path = [ 'user_keys', username ]
+ delete_data_dir(request, path, :data_store_exceptions)
+ rescue DataStore::DataNotFoundError
+ end
end
result
end
@@ -24,43 +35,46 @@ module ChefZero
def put(request)
# Find out if we're updating the public key.
request_body = FFI_Yajl::Parser.parse(request.body, :create_additions => false)
- if request_body['public_key'].nil?
+
+ public_key = request_body.delete('public_key')
+
+ if public_key.nil?
# If public_key is null, then don't overwrite it. Weird patchiness.
body_modified = true
- request_body.delete('public_key')
else
updating_public_key = true
end
# Generate private_key if requested.
- if request_body.has_key?('private_key')
+ if request_body.key?('private_key')
body_modified = true
- if request_body['private_key']
+
+ if request_body.delete('private_key')
private_key, public_key = server.gen_key_pair
updating_public_key = true
- request_body['public_key'] = public_key
end
- request_body.delete('private_key')
end
- # Save request
+ # Put modified body back in `request.body`
request.body = FFI_Yajl::Encoder.encode(request_body, :pretty => true) if body_modified
# PUT /clients is patchy
- request.body = patch_request_body(request)
+ request.body = patch_request_body(request, except: :public_key)
result = super(request)
# Inject private_key into response, delete public_key/password if applicable
if result[0] == 200 || result[0] == 201
+ username = identity_key_value(request) || request.rest_path[-1]
+
+ # TODO Implement for clients
+ if request.rest_path[2] != 'clients' && is_rename?(request)
+ rename_user_keys!(request, username)
+ end
+
if request.rest_path[0] == 'users'
- key = nil
- identity_keys.each do |identity_key|
- key ||= request_body[identity_key]
- end
- key ||= request.rest_path[-1]
response = {
- 'uri' => build_uri(request.base_uri, [ 'users', key ])
+ 'uri' => build_uri(request.base_uri, [ 'users', username ])
}
else
response = FFI_Yajl::Parser.parse(result[2], :create_additions => false)
@@ -70,25 +84,93 @@ module ChefZero
response['private_key'] = private_key ? private_key : false
else
response['private_key'] = private_key if private_key
+
+ if updating_public_key
+ update_user_default_key!(request, public_key)
+ end
+ end
+
+ if request.rest_path[2] == 'users' && !updating_public_key
+ response.delete('public_key')
end
- response.delete('public_key') if !updating_public_key && request.rest_path[2] == 'users'
response.delete('password')
+
json_response(result[0], response)
else
result
end
end
+ private
+
def populate_defaults(request, response_json)
response = FFI_Yajl::Parser.parse(response_json, :create_additions => false)
- if request.rest_path[2] == 'clients'
- response = ChefData::DataNormalizer.normalize_client(response,request.rest_path[3], request.rest_path[1])
- else
- response = ChefData::DataNormalizer.normalize_user(response, request.rest_path[3], identity_keys, server.options[:osc_compat], request.method)
- end
+
+ response =
+ if request.rest_path[2] == 'clients'
+ ChefData::DataNormalizer.normalize_client(response, request.rest_path[3], request.rest_path[1])
+ else
+ public_key = get_user_default_public_key(request, response['username'])
+
+ if public_key
+ response['public_key'] = public_key
+ end
+
+ ChefData::DataNormalizer.normalize_user(response, request.rest_path[3], identity_keys, server.options[:osc_compat], request.method)
+ end
+
FFI_Yajl::Encoder.encode(response, :pretty => true)
end
+
+ # Returns the user's default public_key from user_keys store
+ def get_user_default_public_key(request, username)
+ path = [ "user_keys", username, "keys", DEFAULT_PUBLIC_KEY_NAME ]
+ key_json = get_data(request, path, :nil)
+ return unless key_json
+
+ key_data = FFI_Yajl::Parser.parse(key_json, create_additions: false)
+ key_data && key_data["public_key"]
+ end
+
+ # Move key data to new path
+ def rename_user_keys!(request, new_username)
+ orig_username = request.rest_path[-1]
+ orig_user_keys_path = [ 'user_keys', orig_username, 'keys' ]
+ new_user_keys_path = [ 'user_keys', new_username, 'keys' ]
+
+ user_key_names = list_data(request, orig_user_keys_path, :data_store_exceptions)
+
+ user_key_names.each do |key_name|
+ # Get old data
+ orig_path = orig_user_keys_path + [ key_name ]
+ data = get_data(request, orig_path, :data_store_exceptions)
+
+ # Copy data to new path
+ create_data(
+ request,
+ new_user_keys_path, key_name,
+ data,
+ :create_dir
+ )
+ end
+
+ # Delete original data
+ delete_data_dir(request, orig_user_keys_path, :data_store_exceptions)
+ end
+
+ def update_user_default_key!(request, public_key)
+ username = request.rest_path[1]
+ path = [ "user_keys", username, "keys", DEFAULT_PUBLIC_KEY_NAME ]
+
+ data = FFI_Yajl::Encoder.encode(
+ "name" => DEFAULT_PUBLIC_KEY_NAME,
+ "public_key" => public_key,
+ "expiration_date" => DEFAULT_PUBLIC_KEY_EXPIRATION_DATE
+ )
+
+ set_data(request, path, data, :create, :data_store_exceptions)
+ end
end
end
end
diff --git a/lib/chef_zero/endpoints/actors_endpoint.rb b/lib/chef_zero/endpoints/actors_endpoint.rb
index c6c676f..df6111c 100644
--- a/lib/chef_zero/endpoints/actors_endpoint.rb
+++ b/lib/chef_zero/endpoints/actors_endpoint.rb
@@ -5,6 +5,8 @@ module ChefZero
module Endpoints
# /users, /organizations/ORG/clients or /organizations/ORG/users
class ActorsEndpoint < RestListEndpoint
+ DEFAULT_PUBLIC_KEY_NAME = "default"
+
def get(request)
response = super(request)
@@ -37,19 +39,29 @@ module ChefZero
end
def post(request)
- # First, find out if the user actually posted a public key. If not, make
- # one.
request_body = FFI_Yajl::Parser.parse(request.body, :create_additions => false)
- public_key = request_body['public_key']
- if !public_key
+ username = request_body['username']
+
+ public_key = request_body["public_key"]
+
+ # Did the user post a public_key? If not, generate one.
+ unless public_key
private_key, public_key = server.gen_key_pair
+ end
+
+ if request.rest_path[2] == "clients"
request_body['public_key'] = public_key
- request.body = FFI_Yajl::Encoder.encode(request_body, :pretty => true)
+ else
+ request_body.delete('public_key')
end
+ request.body = FFI_Yajl::Encoder.encode(request_body, :pretty => true)
result = super(request)
if result[0] == 201
+ # Store the received or generated public key
+ create_user_default_key!(request, username, public_key)
+
# If we generated a key, stuff it in the response.
response = FFI_Yajl::Parser.parse(result[2], :create_additions => false)
response['private_key'] = private_key if private_key
@@ -59,6 +71,24 @@ module ChefZero
result
end
end
+
+ private
+
+ # Store the public key in user_keys
+ def create_user_default_key!(request, username, public_key)
+ # TODO Implement for clients
+ return if request.rest_path[2] == "clients"
+
+ path = [ "user_keys", username, "keys" ]
+
+ data = FFI_Yajl::Encoder.encode(
+ "name" => DEFAULT_PUBLIC_KEY_NAME,
+ "public_key" => public_key,
+ "expiration_date" => "infinity"
+ )
+
+ create_data(request, path, DEFAULT_PUBLIC_KEY_NAME, data, :create_dir)
+ end
end
end
end
diff --git a/lib/chef_zero/endpoints/organization_user_endpoint.rb b/lib/chef_zero/endpoints/organization_user_endpoint.rb
index 8fd97d4..d0c5b3c 100644
--- a/lib/chef_zero/endpoints/organization_user_endpoint.rb
+++ b/lib/chef_zero/endpoints/organization_user_endpoint.rb
@@ -5,11 +5,17 @@ module ChefZero
module Endpoints
# /organizations/ORG/users/NAME
class OrganizationUserEndpoint < RestBase
+ DEFAULT_PUBLIC_KEY_NAME = "default"
+
def get(request)
username = request.rest_path[3]
get_data(request) # 404 if user is not in org
+
user = get_data(request, [ 'users', username ])
user = FFI_Yajl::Parser.parse(user, :create_additions => false)
+
+ user["public_key"] = get_user_default_public_key(request, username)
+
json_response(200, ChefData::DataNormalizer.normalize_user(user, username, ['username'], server.options[:osc_compat], request.method))
end
@@ -20,7 +26,18 @@ module ChefZero
json_response(200, ChefData::DataNormalizer.normalize_user(user, request.rest_path[3], ['username'], server.options[:osc_compat]))
end
- # Note: post to a named org user is not permitted, alllow invalid method handling (405)
+ # Note: post to a named org user is not permitted, allow invalid method handling (405)
+
+ private
+ # Returns the user's default public_key from user_keys store
+ def get_user_default_public_key(request, username)
+ path = [ "user_keys", username, "keys", DEFAULT_PUBLIC_KEY_NAME ]
+ key_json = get_data(request, path, :nil)
+ return unless key_json
+
+ key_data = FFI_Yajl::Parser.parse(key_json, create_additions: false)
+ key_data && key_data["public_key"]
+ end
end
end
end
diff --git a/lib/chef_zero/endpoints/principal_endpoint.rb b/lib/chef_zero/endpoints/principal_endpoint.rb
index b1f4efb..2dcec1b 100644
--- a/lib/chef_zero/endpoints/principal_endpoint.rb
+++ b/lib/chef_zero/endpoints/principal_endpoint.rb
@@ -6,40 +6,79 @@ module ChefZero
module Endpoints
# /principals/NAME
class PrincipalEndpoint < RestBase
+ DEFAULT_PUBLIC_KEY_NAME = "default"
+
def get(request)
name = request.rest_path[-1]
- # If /organizations/ORG/users/NAME exists, use this user (only org members have precedence over clients). hey are an org member.
- json = get_data(request, request.rest_path[0..1] + [ 'users', name ], :nil)
- if json
- type = 'user'
- org_member = true
- else
- # If /organizations/ORG/clients/NAME exists, use the client.
- json = get_data(request, request.rest_path[0..1] + [ 'clients', name ], :nil)
- if json
- type = 'client'
- org_member = true
- else
- # If there is no client with that name, check for a user (/users/NAME) and return that with
- # org_member = false.
- json = get_data(request, [ 'users', name ], :nil)
- if json
- type = 'user'
- org_member = false
- end
- end
- end
- if json
- json_response(200, {
- 'name' => name,
- 'type' => type,
- 'public_key' => FFI_Yajl::Parser.parse(json)['public_key'] || PUBLIC_KEY,
+ data = get_principal_data(request, name)
+
+ if data
+ return json_response(200, data.merge(
'authz_id' => '0'*32,
- 'org_member' => org_member
- })
- else
- error(404, 'Principal not found')
+ 'name' => name,
+ ))
end
+
+ error(404, 'Principal not found')
+ end
+
+ private
+
+ def get_principal_data(request, name)
+ # If /organizations/ORG/users/NAME exists, use this user (only org members have precedence over clients). hey are an org member.
+ get_org_users_data(request, name) ||
+ # If /organizations/ORG/clients/NAME exists, use the client.
+ get_clients_data(request, name) ||
+ # If there is no client with that name, check for a user (/users/NAME) and return that with
+ # org_member = false.
+ get_users_data(request, name)
+ end
+
+ def get_org_users_data(request, name)
+ path = [ *request.rest_path[0..1], 'users', name ]
+ return if get_data(request, path, :nil).nil?
+
+ user_keys_json = get_data(request,
+ [ 'user_keys', name, 'keys', DEFAULT_PUBLIC_KEY_NAME ],
+ :data_store_exceptions
+ )
+
+ public_key = FFI_Yajl::Parser.parse(user_keys_json)['public_key']
+
+ { "type" => "user",
+ "org_member" => true,
+ "public_key" => public_key
+ }
+ end
+
+ def get_clients_data(request, name)
+ path = [ *request.rest_path[0..1], 'clients', name ]
+ json = get_data(request, path, :nil)
+ return if json.nil?
+
+ public_key = FFI_Yajl::Parser.parse(json)['public_key']
+
+ { "type" => "client",
+ "org_member" => true,
+ "public_key" => public_key || PUBLIC_KEY
+ }
+ end
+
+ def get_users_data(request, name)
+ path = [ 'users', name ]
+ return if get_data(request, path, :nil).nil?
+
+ user_keys_json = get_data(request,
+ [ 'user_keys', name, 'keys', DEFAULT_PUBLIC_KEY_NAME ],
+ :data_store_exceptions
+ )
+
+ public_key = FFI_Yajl::Parser.parse(user_keys_json)['public_key']
+
+ { "type" => "user",
+ "org_member" => false,
+ "public_key" => public_key
+ }
end
end
end
diff --git a/lib/chef_zero/endpoints/rest_object_endpoint.rb b/lib/chef_zero/endpoints/rest_object_endpoint.rb
index 9e978b4..efadf82 100644
--- a/lib/chef_zero/endpoints/rest_object_endpoint.rb
+++ b/lib/chef_zero/endpoints/rest_object_endpoint.rb
@@ -21,12 +21,11 @@ module ChefZero
def put(request)
# We grab the old body to trigger a 404 if it doesn't exist
old_body = get_data(request)
- request_json = FFI_Yajl::Parser.parse(request.body, :create_additions => false)
- key = identity_keys.map { |k| request_json[k] }.select { |v| v }.first
- key ||= request.rest_path[-1]
+
# If it's a rename, check for conflict and delete the old value
- rename = key != request.rest_path[-1]
- if rename
+ if is_rename?(request)
+ key = identity_key_value(request)
+
begin
create_data(request, request.rest_path[0..-2], key, request.body, :data_store_exceptions)
rescue DataStore::DataAlreadyExistsError
@@ -46,18 +45,49 @@ module ChefZero
already_json_response(200, populate_defaults(request, result))
end
- def patch_request_body(request)
+ # Merge the request body with the existing data, if one exists in
+ # the data store
+ #
+ # @param [ChefZero::RestRequest] request
+ # @param [Hash] options
+ # @option options [Symbol, Array<Symbol>] :except (nil) Key(s) from
+ # the existing data to discard
+ def patch_request_body(request, options={})
existing_value = get_data(request, nil, :nil)
+
if existing_value
request_json = FFI_Yajl::Parser.parse(request.body, :create_additions => false)
existing_json = FFI_Yajl::Parser.parse(existing_value, :create_additions => false)
+
+ if options[:except]
+ Array(options[:except]).each do |except_key|
+ existing_json.delete(except_key)
+ end
+ end
+
merged_json = existing_json.merge(request_json)
+
if merged_json.size > request_json.size
return FFI_Yajl::Encoder.encode(merged_json, :pretty => true)
end
end
+
request.body
end
+
+ private
+
+ # Get the value of the (first existing) identity key from the request body or nil
+ def identity_key_value(request)
+ request_json = FFI_Yajl::Parser.parse(request.body, :create_additions => false)
+ identity_keys.map { |k| request_json[k] }.compact.first
+ end
+
+ # Does this request change the value of the identity key?
+ def is_rename?(request)
+ return false unless key = identity_key_value(request)
+ key != request.rest_path[-1]
+ end
end
end
end
diff --git a/lib/chef_zero/endpoints/server_api_version_endpoint.rb b/lib/chef_zero/endpoints/server_api_version_endpoint.rb
index 631f105..8ddeaba 100644
--- a/lib/chef_zero/endpoints/server_api_version_endpoint.rb
+++ b/lib/chef_zero/endpoints/server_api_version_endpoint.rb
@@ -7,7 +7,7 @@ module ChefZero
API_VERSION = 1
def get(request)
json_response(200, {"min_api_version"=>MIN_API_VERSION, "max_api_version"=>MAX_API_VERSION},
- request.api_version, API_VERSION)
+ request_version: request.api_version, response_version: API_VERSION)
end
end
end
diff --git a/lib/chef_zero/endpoints/user_key_endpoint.rb b/lib/chef_zero/endpoints/user_key_endpoint.rb
new file mode 100644
index 0000000..4e4872b
--- /dev/null
+++ b/lib/chef_zero/endpoints/user_key_endpoint.rb
@@ -0,0 +1,32 @@
+require 'ffi_yajl'
+require 'chef_zero/rest_base'
+
+module ChefZero
+ module Endpoints
+ # /users/USER/keys/NAME
+ class UserKeyEndpoint < RestBase
+ def get(request)
+ path = [ "user_keys", *request.rest_path[1..-1] ]
+ already_json_response(200, get_data(request, path))
+ end
+
+ def delete(request)
+ path = [ "user_keys", *request.rest_path[1..-1] ]
+
+ data = get_data(request, path)
+ delete_data(request, path)
+
+ already_json_response(200, data)
+ end
+
+ def put(request)
+ path = [ "user_keys", *request.rest_path[1..-1] ]
+
+ # We grab the old data to trigger a 404 if it doesn't exist
+ get_data(request, path)
+
+ set_data(request, path, request.body)
+ end
+ end
+ end
+end
diff --git a/lib/chef_zero/endpoints/user_keys_endpoint.rb b/lib/chef_zero/endpoints/user_keys_endpoint.rb
new file mode 100644
index 0000000..c6aea07
--- /dev/null
+++ b/lib/chef_zero/endpoints/user_keys_endpoint.rb
@@ -0,0 +1,80 @@
+require 'ffi_yajl'
+require 'chef_zero/rest_base'
+
+module ChefZero
+ module Endpoints
+ # /users/USER/keys
+
+ class UserKeysEndpoint < RestBase
+ DATE_FORMAT = "%FT%TZ" # e.g. 2015-12-24T21:00:00Z
+
+ def get(request)
+ username = request.rest_path[1]
+ path = [ "user_keys", username, "keys" ]
+
+ result = list_data(request, path).map do |key_name|
+ list_key(request, [ *path, key_name ])
+ end
+
+ json_response(200, result)
+ end
+
+ def post(request)
+ username = request.rest_path[1]
+ request_body = FFI_Yajl::Parser.parse(request.body)
+
+ validate_user!(request)
+
+ generate_keys = request_body["public_key"].nil?
+
+ if generate_keys
+ private_key, public_key = server.gen_key_pair
+ else
+ public_key = request_body['public_key']
+ end
+
+ key_name = request_body["name"]
+ path = [ "user_keys", username, "keys" ]
+
+ data = FFI_Yajl::Encoder.encode(
+ "name" => key_name,
+ "public_key" => public_key,
+ "expiration_date" => request_body["expiration_date"]
+ )
+
+ create_data(request, path, key_name, data, :create_dir)
+
+ response_body = {
+ "uri" => build_uri(request.base_uri,
+ [ "users", username, "keys", key_name ])
+ }
+ response_body["private_key"] = private_key if generate_keys
+
+ json_response(201, response_body,
+ headers: { "Location" => response_body["uri"] })
+ end
+
+ private
+
+ def list_key(request, data_path)
+ data = FFI_Yajl::Parser.parse(get_data(request, data_path), create_additions: false)
+ uri = build_uri(request.base_uri, [ "users", *data_path[1..-1] ])
+
+ expiration_date = if data["expiration_date"] == "infinity"
+ Float::INFINITY
+ else
+ DateTime.strptime(data["expiration_date"], DATE_FORMAT)
+ end
+
+ { "name" => data_path[-1],
+ "uri" => uri,
+ "expired" => DateTime.now > expiration_date }
+ end
+
+ def validate_user!(request)
+ # Try loading the user so a 404 is returned if the user doesn't
+ get_data(request, request.rest_path[0, 2])
+ end
+ end
+ end
+end
diff --git a/lib/chef_zero/rest_base.rb b/lib/chef_zero/rest_base.rb
index a03f4aa..f276426 100644
--- a/lib/chef_zero/rest_base.rb
+++ b/lib/chef_zero/rest_base.rb
@@ -5,6 +5,9 @@ require 'chef_zero/chef_data/acl_path'
module ChefZero
class RestBase
+ DEFAULT_REQUEST_VERSION = 0
+ DEFAULT_RESPONSE_VERSION = 0
+
def initialize(server)
@server = server
end
@@ -16,21 +19,28 @@ module ChefZero
end
def check_api_version(request)
- version = request.api_version
- return nil if version.nil? # Not present in headers
+ return if request.api_version.nil? # Not present in headers
+ version = request.api_version.to_i
+
+ unless version.to_s == request.api_version.to_s # Version is not an Integer
+ return json_response(406,
+ { "username" => request.requestor },
+ request_version: -1, response_version: -1
+ )
+ end
- if version.to_i.to_s != version.to_s # Version is not an Integer
- return json_response(406, { "username" => request.requestor }, -1, -1)
- elsif version.to_i > MAX_API_VERSION or version.to_i < MIN_API_VERSION
+ if version > MAX_API_VERSION || version < MIN_API_VERSION
response = {
"error" => "invalid-x-ops-server-api-version",
"message" => "Specified version #{version} not supported",
"min_api_version" => MIN_API_VERSION,
"max_api_version" => MAX_API_VERSION
}
- return json_response(406, response, version, -1)
- else
- return nil
+
+ return json_response(406,
+ response,
+ request_version: version, response_version: -1
+ )
end
end
@@ -196,26 +206,59 @@ module ChefZero
end
def error(response_code, error, opts={})
- json_response(response_code, {"error" => [error]}, 0, 0, opts)
+ json_response(response_code, { "error" => [ error ] }, opts)
end
- def json_response(response_code, json, request_version=0, response_version=0, opts={pretty: true})
- do_pretty_json = !!opts[:pretty] # make sure we have a proper Boolean.
- already_json_response(response_code, FFI_Yajl::Encoder.encode(json, :pretty => do_pretty_json), request_version, response_version)
+ # Serializes `data` to JSON and returns an Array with the
+ # response code, HTTP headers and JSON body.
+ #
+ # @param [Fixnum] response_code HTTP response code
+ # @param [Hash] data The data for the response body as a Hash
+ # @param [Hash] options
+ # @option options [Hash] :headers (see #already_json_response)
+ # @option options [Boolean] :pretty (true) Pretty-format the JSON
+ # @option options [Fixnum] :request_version (see #already_json_response)
+ # @option options [Fixnum] :response_version (see #already_json_response)
+ #
+ # @return (see #already_json_response)
+ #
+ def json_response(response_code, data, options={})
+ options = { pretty: true }.merge(options)
+ do_pretty_json = !!options.delete(:pretty) # make sure we have a proper Boolean.
+ json = FFI_Yajl::Encoder.encode(data, pretty: do_pretty_json)
+ already_json_response(response_code, json, options)
end
def text_response(response_code, text)
[response_code, {"Content-Type" => "text/plain"}, text]
end
- def already_json_response(response_code, json_text, request_version=0, response_version=0)
- header = { "min_version" => MIN_API_VERSION.to_s, "max_version" => MAX_API_VERSION.to_s,
- "request_version" => request_version.to_s,
- "response_version" => response_version.to_s }
- [ response_code,
- { "Content-Type" => "application/json",
- "X-Ops-Server-API-Version" => FFI_Yajl::Encoder.encode(header) },
- json_text ]
+ # Returns an Array with the response code, HTTP headers, and JSON body.
+ #
+ # @param [Fixnum] response_code The HTTP response code
+ # @param [String] json_text The JSON body for the response
+ # @param [Hash] options
+ # @option options [Hash] :headers ({}) HTTP headers (may override default headers)
+ # @option options [Fixnum] :request_version (0) Request API version
+ # @option options [Fixnum] :response_version (0) Response API version
+ #
+ # @return [Array(Fixnum, Hash{String => String}, String)]
+ #
+ def already_json_response(response_code, json_text, options={})
+ version_header = FFI_Yajl::Encoder.encode(
+ "min_version" => MIN_API_VERSION.to_s,
+ "max_version" => MAX_API_VERSION.to_s,
+ "request_version" => options[:request_version] || DEFAULT_REQUEST_VERSION.to_s,
+ "response_version" => options[:response_version] || DEFAULT_RESPONSE_VERSION.to_s
+ )
+
+ headers = {
+ "Content-Type" => "application/json",
+ "X-Ops-Server-API-Version" => version_header
+ }
+ headers.merge!(options[:headers]) if options[:headers]
+
+ [ response_code, headers, json_text ]
end
# To be called from inside rest endpoints
diff --git a/lib/chef_zero/server.rb b/lib/chef_zero/server.rb
index d6f27c4..f0ad9b1 100644
--- a/lib/chef_zero/server.rb
+++ b/lib/chef_zero/server.rb
@@ -94,6 +94,8 @@ require 'chef_zero/endpoints/system_recovery_endpoint'
require 'chef_zero/endpoints/user_association_requests_endpoint'
require 'chef_zero/endpoints/user_association_requests_count_endpoint'
require 'chef_zero/endpoints/user_association_request_endpoint'
+require 'chef_zero/endpoints/user_key_endpoint'
+require 'chef_zero/endpoints/user_keys_endpoint'
require 'chef_zero/endpoints/user_organizations_endpoint'
require 'chef_zero/endpoints/file_store_file_endpoint'
require 'chef_zero/endpoints/not_found_endpoint'
@@ -537,6 +539,8 @@ module ChefZero
[ "/users/*/association_requests", UserAssociationRequestsEndpoint.new(self) ],
[ "/users/*/association_requests/count", UserAssociationRequestsCountEndpoint.new(self) ],
[ "/users/*/association_requests/*", UserAssociationRequestEndpoint.new(self) ],
+ [ "/users/*/keys", UserKeysEndpoint.new(self) ],
+ [ "/users/*/keys/*", UserKeyEndpoint.new(self) ],
[ "/users/*/organizations", UserOrganizationsEndpoint.new(self) ],
[ "/authenticate_user", AuthenticateUserEndpoint.new(self) ],
[ "/system_recovery", SystemRecoveryEndpoint.new(self) ],
diff --git a/spec/run_oc_pedant.rb b/spec/run_oc_pedant.rb
index 92ef136..25b5ea9 100644
--- a/spec/run_oc_pedant.rb
+++ b/spec/run_oc_pedant.rb
@@ -104,7 +104,9 @@ begin
# are turned off" - @jkeiser
#
# ...but we're not there yet
- '--skip-keys',
+ '--skip-client-keys',
+ '--skip-controls',
+ '--skip-acl',
# Chef Zero does not intend to support validation the way erchef does.
'--skip-validation',