summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJay Mundrawala <jdmundrawala@gmail.com>2015-07-06 16:08:59 -0700
committerJay Mundrawala <jdmundrawala@gmail.com>2015-07-06 16:08:59 -0700
commitc12c169b29882166d6bbc0f835825ef34ab94eba (patch)
treeca0635a3a67f9b33d600e2e9c38326d1133a43c8
parent320afd88bb6751b67573af982a0cf5d51a035af7 (diff)
parent65901ab70e498625cc2d07fec32f1eba3c8ca961 (diff)
downloadchef-c12c169b29882166d6bbc0f835825ef34ab94eba.tar.gz
Merge pull request #3630 from chef/tc/fix-chef-user
Move Chef::OscUser back to Chef::User namespace and new user code to Chef::UserV1.
-rw-r--r--CHANGELOG.md1
-rw-r--r--lib/chef/api_client.rb160
-rw-r--r--lib/chef/api_client_v1.rb325
-rw-r--r--lib/chef/chef_fs/file_system/chef_server_root_dir.rb4
-rw-r--r--lib/chef/knife/client_bulk_delete.rb4
-rw-r--r--lib/chef/knife/client_create.rb8
-rw-r--r--lib/chef/knife/client_delete.rb6
-rw-r--r--lib/chef/knife/client_edit.rb12
-rw-r--r--lib/chef/knife/client_list.rb4
-rw-r--r--lib/chef/knife/client_reregister.rb4
-rw-r--r--lib/chef/knife/client_show.rb4
-rw-r--r--lib/chef/knife/osc_user_create.rb6
-rw-r--r--lib/chef/knife/osc_user_delete.rb4
-rw-r--r--lib/chef/knife/osc_user_edit.rb6
-rw-r--r--lib/chef/knife/osc_user_list.rb4
-rw-r--r--lib/chef/knife/osc_user_reregister.rb4
-rw-r--r--lib/chef/knife/osc_user_show.rb4
-rw-r--r--lib/chef/knife/user_create.rb6
-rw-r--r--lib/chef/knife/user_delete.rb8
-rw-r--r--lib/chef/knife/user_edit.rb6
-rw-r--r--lib/chef/knife/user_list.rb4
-rw-r--r--lib/chef/knife/user_reregister.rb4
-rw-r--r--lib/chef/knife/user_show.rb4
-rw-r--r--lib/chef/osc_user.rb194
-rw-r--r--lib/chef/user.rb240
-rw-r--r--lib/chef/user_v1.rb335
-rw-r--r--spec/support/shared/unit/api_versioning.rb4
-rw-r--r--spec/unit/api_client_spec.rb223
-rw-r--r--spec/unit/api_client_v1_spec.rb457
-rw-r--r--spec/unit/knife/client_bulk_delete_spec.rb8
-rw-r--r--spec/unit/knife/client_create_spec.rb2
-rw-r--r--spec/unit/knife/client_delete_spec.rb6
-rw-r--r--spec/unit/knife/client_edit_spec.rb15
-rw-r--r--spec/unit/knife/client_list_spec.rb2
-rw-r--r--spec/unit/knife/client_reregister_spec.rb4
-rw-r--r--spec/unit/knife/client_show_spec.rb4
-rw-r--r--spec/unit/knife/osc_user_create_spec.rb10
-rw-r--r--spec/unit/knife/osc_user_delete_spec.rb2
-rw-r--r--spec/unit/knife/osc_user_edit_spec.rb2
-rw-r--r--spec/unit/knife/osc_user_list_spec.rb2
-rw-r--r--spec/unit/knife/osc_user_reregister_spec.rb2
-rw-r--r--spec/unit/knife/osc_user_show_spec.rb2
-rw-r--r--spec/unit/knife/user_create_spec.rb2
-rw-r--r--spec/unit/knife/user_delete_spec.rb4
-rw-r--r--spec/unit/knife/user_edit_spec.rb4
-rw-r--r--spec/unit/knife/user_list_spec.rb2
-rw-r--r--spec/unit/knife/user_reregister_spec.rb2
-rw-r--r--spec/unit/knife/user_show_spec.rb4
-rw-r--r--spec/unit/osc_user_spec.rb276
-rw-r--r--spec/unit/user_spec.rb502
-rw-r--r--spec/unit/user_v1_spec.rb584
51 files changed, 2009 insertions, 1477 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9cd9a16606..64ce0a0986 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -14,6 +14,7 @@
* [pr#3208](https://github.com/chef/chef/pull/3208) Missing require (require what you use).
* [pr#3449](https://github.com/chef/chef/pull/3449) correcting minor typo in user_edit knife action
* [pr#3572](https://github.com/chef/chef/pull/3572) Use windows paths without case-sensitivity.
+* [pr#3630](https://github.com/chef/chef/pull/3630) Restore Chef::User and Chef::ApiClient namespace to API V0 functionality and move new functionality into Chef::UserV1 and Chef::ApiClientV1 until Chef 13.
## 12.4.0
diff --git a/lib/chef/api_client.rb b/lib/chef/api_client.rb
index ad31fb7d7b..b7b9f7dc43 100644
--- a/lib/chef/api_client.rb
+++ b/lib/chef/api_client.rb
@@ -1,7 +1,7 @@
#
-# Author:: Adam Jacob (<adam@chef.io>)
-# Author:: Nuo Yan (<nuo@chef.io>)
-# Copyright:: Copyright (c) 2008, 2015 Chef Software, Inc.
+# Author:: Adam Jacob (<adam@opscode.com>)
+# Author:: Nuo Yan (<nuo@opscode.com>)
+# Copyright:: Copyright (c) 2008 Opscode, Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -23,18 +23,18 @@ require 'chef/mixin/from_file'
require 'chef/mash'
require 'chef/json_compat'
require 'chef/search/query'
-require 'chef/exceptions'
-require 'chef/mixin/api_version_request_handling'
require 'chef/server_api'
+# DEPRECATION NOTE
+#
+# This code will be removed in Chef 13 in favor of the code in Chef::ApiClientV1,
+# which will be moved to this namespace. New development should occur in
+# Chef::ApiClientV1 until the time before Chef 13.
class Chef
class ApiClient
include Chef::Mixin::FromFile
include Chef::Mixin::ParamsValidate
- include Chef::Mixin::ApiVersionRequestHandling
-
- SUPPORTED_API_VERSIONS = [0,1]
# Create a new Chef::ApiClient object.
def initialize
@@ -43,25 +43,6 @@ class Chef
@private_key = nil
@admin = false
@validator = false
- @create_key = nil
- end
-
- def chef_rest_v0
- @chef_rest_v0 ||= Chef::ServerAPI.new(Chef::Config[:chef_server_url], {:api_version => "0"})
- end
-
- def chef_rest_v1
- @chef_rest_v1 ||= Chef::ServerAPI.new(Chef::Config[:chef_server_url], {:api_version => "1"})
- end
-
- # will default to the current version (Chef::Authenticator::DEFAULT_SERVER_API_VERSION)
- def http_api
- @http_api ||= Chef::REST.new(Chef::Config[:chef_server_url])
- end
-
- # will default to the current version (Chef::Authenticator::DEFAULT_SERVER_API_VERSION)
- def self.http_api
- Chef::REST.new(Chef::Config[:chef_server_url])
end
# Gets or sets the client name.
@@ -113,8 +94,7 @@ class Chef
)
end
- # Private key. The server will return it as a string.
- # Set to true under API V0 to have the server regenerate the default key.
+ # Gets or sets the private key.
#
# @params [Optional String] The string representation of the private key.
# @return [String] The current value.
@@ -122,19 +102,7 @@ class Chef
set_or_return(
:private_key,
arg,
- :kind_of => [String, TrueClass, FalseClass]
- )
- end
-
- # Used to ask server to generate key pair under api V1
- #
- # @params [Optional True/False] Should be true or false - default is false.
- # @return [True/False] The current value
- def create_key(arg=nil)
- set_or_return(
- :create_key,
- arg,
- :kind_of => [ TrueClass, FalseClass ]
+ :kind_of => [String, FalseClass]
)
end
@@ -145,14 +113,13 @@ class Chef
def to_hash
result = {
"name" => @name,
+ "public_key" => @public_key,
"validator" => @validator,
"admin" => @admin,
'json_class' => self.class.name,
"chef_type" => "client"
}
- result["private_key"] = @private_key unless @private_key.nil?
- result["public_key"] = @public_key unless @public_key.nil?
- result["create_key"] = @create_key unless @create_key.nil?
+ result["private_key"] = @private_key if @private_key
result
end
@@ -166,11 +133,10 @@ class Chef
def self.from_hash(o)
client = Chef::ApiClient.new
client.name(o["name"] || o["clientname"])
+ client.private_key(o["private_key"]) if o.key?("private_key")
+ client.public_key(o["public_key"])
client.admin(o["admin"])
client.validator(o["validator"])
- client.private_key(o["private_key"]) if o.key?("private_key")
- client.public_key(o["public_key"]) if o.key?("public_key")
- client.create_key(o["create_key"]) if o.key?("create_key")
client
end
@@ -182,6 +148,10 @@ class Chef
from_hash(Chef::JSONCompat.parse(j))
end
+ def self.http_api
+ Chef::ServerAPI.new(Chef::Config[:chef_server_url], {:api_version => "0"})
+ end
+
def self.reregister(name)
api_client = load(name)
api_client.reregister
@@ -218,11 +188,11 @@ class Chef
# Save this client via the REST API, returns a hash including the private key
def save
begin
- update
+ http_api.put("clients/#{name}", { :name => self.name, :admin => self.admin, :validator => self.validator})
rescue Net::HTTPServerException => e
# If that fails, go ahead and try and update it
if e.response.code == "404"
- create
+ http_api.post("clients", {:name => self.name, :admin => self.admin, :validator => self.validator })
else
raise e
end
@@ -230,95 +200,18 @@ class Chef
end
def reregister
- # Try API V0 and if it fails due to V0 not being supported, raise the proper error message.
- # reregister only supported in API V0 or lesser.
- reregistered_self = chef_rest_v0.put("clients/#{name}", { :name => name, :admin => admin, :validator => validator, :private_key => true })
+ reregistered_self = http_api.put("clients/#{name}", { :name => name, :admin => admin, :validator => validator, :private_key => true })
if reregistered_self.respond_to?(:[])
private_key(reregistered_self["private_key"])
else
private_key(reregistered_self.private_key)
end
self
- rescue Net::HTTPServerException => e
- # if there was a 406 related to versioning, give error explaining that
- # only API version 0 is supported for reregister command
- if e.response.code == "406" && e.response["x-ops-server-api-version"]
- version_header = Chef::JSONCompat.from_json(e.response["x-ops-server-api-version"])
- min_version = version_header["min_version"]
- max_version = version_header["max_version"]
- error_msg = reregister_only_v0_supported_error_msg(max_version, min_version)
- raise Chef::Exceptions::OnlyApiVersion0SupportedForAction.new(error_msg)
- else
- raise e
- end
- end
-
- # Updates the client via the REST API
- def update
- # NOTE: API V1 dropped support for updating client keys via update (aka PUT),
- # but this code never supported key updating in the first place. Since
- # it was never implemented, we will simply ignore that functionality
- # as it is being deprecated.
- # Delete this comment after V0 support is dropped.
- payload = { :name => name }
- payload[:validator] = validator unless validator.nil?
-
- # DEPRECATION
- # This field is ignored in API V1, but left for backwards-compat,
- # can remove after API V0 is no longer supported.
- payload[:admin] = admin unless admin.nil?
-
- begin
- new_client = chef_rest_v1.put("clients/#{name}", payload)
- rescue Net::HTTPServerException => e
- # rescue API V0 if 406 and the server supports V0
- supported_versions = server_client_api_version_intersection(e, SUPPORTED_API_VERSIONS)
- raise e unless supported_versions && supported_versions.include?(0)
- new_client = chef_rest_v0.put("clients/#{name}", payload)
- end
-
- new_client
end
# Create the client via the REST API
def create
- payload = {
- :name => name,
- :validator => validator,
- # this field is ignored in API V1, but left for backwards-compat,
- # can remove after OSC 11 support is finished?
- :admin => admin
- }
- begin
- # try API V1
- raise Chef::Exceptions::InvalidClientAttribute, "You cannot set both public_key and create_key for create." if !create_key.nil? && !public_key.nil?
-
- payload[:public_key] = public_key unless public_key.nil?
- payload[:create_key] = create_key unless create_key.nil?
-
- new_client = chef_rest_v1.post("clients", payload)
-
- # get the private_key out of the chef_key hash if it exists
- if new_client['chef_key']
- if new_client['chef_key']['private_key']
- new_client['private_key'] = new_client['chef_key']['private_key']
- end
- new_client['public_key'] = new_client['chef_key']['public_key']
- new_client.delete('chef_key')
- end
-
- rescue Net::HTTPServerException => e
- # rescue API V0 if 406 and the server supports V0
- supported_versions = server_client_api_version_intersection(e, SUPPORTED_API_VERSIONS)
- raise e unless supported_versions && supported_versions.include?(0)
-
- # under API V0, a key pair will always be created unless public_key is
- # passed on initial POST
- payload[:public_key] = public_key unless public_key.nil?
-
- new_client = chef_rest_v0.post("clients", payload)
- end
- Chef::ApiClient.from_hash(self.to_hash.merge(new_client))
+ http_api.post("clients", self)
end
# As a string
@@ -326,5 +219,14 @@ class Chef
"client[#{@name}]"
end
+ def inspect
+ "Chef::ApiClient name:'#{name}' admin:'#{admin.inspect}' validator:'#{validator}' " +
+ "public_key:'#{public_key}' private_key:'#{private_key}'"
+ end
+
+ def http_api
+ @http_api ||= Chef::ServerAPI.new(Chef::Config[:chef_server_url], {:api_version => "0"})
+ end
+
end
end
diff --git a/lib/chef/api_client_v1.rb b/lib/chef/api_client_v1.rb
new file mode 100644
index 0000000000..80f0d2517c
--- /dev/null
+++ b/lib/chef/api_client_v1.rb
@@ -0,0 +1,325 @@
+#
+# Author:: Adam Jacob (<adam@chef.io>)
+# Author:: Nuo Yan (<nuo@chef.io>)
+# Copyright:: Copyright (c) 2008, 2015 Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'chef/config'
+require 'chef/mixin/params_validate'
+require 'chef/mixin/from_file'
+require 'chef/mash'
+require 'chef/json_compat'
+require 'chef/search/query'
+require 'chef/exceptions'
+require 'chef/mixin/api_version_request_handling'
+require 'chef/server_api'
+require 'chef/api_client'
+
+# COMPATIBILITY NOTE
+#
+# This ApiClientV1 code attempts to make API V1 requests and falls back to
+# API V0 requests when it fails. New development should occur here instead
+# of Chef::ApiClient as this will replace that namespace when Chef 13 is released.
+#
+# If you need to default to API V0 behavior (i.e. you need GET client to return
+# a public key, etc), please use Chef::ApiClient and update your code to support
+# API V1 before you pull in Chef 13.
+class Chef
+ class ApiClientV1
+
+ include Chef::Mixin::FromFile
+ include Chef::Mixin::ParamsValidate
+ include Chef::Mixin::ApiVersionRequestHandling
+
+ SUPPORTED_API_VERSIONS = [0,1]
+
+ # Create a new Chef::ApiClientV1 object.
+ def initialize
+ @name = ''
+ @public_key = nil
+ @private_key = nil
+ @admin = false
+ @validator = false
+ @create_key = nil
+ end
+
+ def chef_rest_v0
+ @chef_rest_v0 ||= Chef::ServerAPI.new(Chef::Config[:chef_server_url], {:api_version => "0", :inflate_json_class => false})
+ end
+
+ def chef_rest_v1
+ @chef_rest_v1 ||= Chef::ServerAPI.new(Chef::Config[:chef_server_url], {:api_version => "1", :inflate_json_class => false})
+ end
+
+ def self.http_api
+ Chef::ServerAPI.new(Chef::Config[:chef_server_url], {:api_version => "1", :inflate_json_class => false})
+ end
+
+ # Gets or sets the client name.
+ #
+ # @params [Optional String] The name must be alpha-numeric plus - and _.
+ # @return [String] The current value of the name.
+ def name(arg=nil)
+ set_or_return(
+ :name,
+ arg,
+ :regex => /^[\-[:alnum:]_\.]+$/
+ )
+ end
+
+ # Gets or sets whether this client is an admin.
+ #
+ # @params [Optional True/False] Should be true or false - default is false.
+ # @return [True/False] The current value
+ def admin(arg=nil)
+ set_or_return(
+ :admin,
+ arg,
+ :kind_of => [ TrueClass, FalseClass ]
+ )
+ end
+
+ # Gets or sets the public key.
+ #
+ # @params [Optional String] The string representation of the public key.
+ # @return [String] The current value.
+ def public_key(arg=nil)
+ set_or_return(
+ :public_key,
+ arg,
+ :kind_of => String
+ )
+ end
+
+ # Gets or sets whether this client is a validator.
+ #
+ # @params [Boolean] whether or not the client is a validator. If
+ # `nil`, retrieves the already-set value.
+ # @return [Boolean] The current value
+ def validator(arg=nil)
+ set_or_return(
+ :validator,
+ arg,
+ :kind_of => [TrueClass, FalseClass]
+ )
+ end
+
+ # Private key. The server will return it as a string.
+ # Set to true under API V0 to have the server regenerate the default key.
+ #
+ # @params [Optional String] The string representation of the private key.
+ # @return [String] The current value.
+ def private_key(arg=nil)
+ set_or_return(
+ :private_key,
+ arg,
+ :kind_of => [String, TrueClass, FalseClass]
+ )
+ end
+
+ # Used to ask server to generate key pair under api V1
+ #
+ # @params [Optional True/False] Should be true or false - default is false.
+ # @return [True/False] The current value
+ def create_key(arg=nil)
+ set_or_return(
+ :create_key,
+ arg,
+ :kind_of => [ TrueClass, FalseClass ]
+ )
+ end
+
+ # The hash representation of the object. Includes the name and public_key.
+ # Private key is included if available.
+ #
+ # @return [Hash]
+ def to_hash
+ result = {
+ "name" => @name,
+ "validator" => @validator,
+ "admin" => @admin,
+ "chef_type" => "client"
+ }
+ result["private_key"] = @private_key unless @private_key.nil?
+ result["public_key"] = @public_key unless @public_key.nil?
+ result["create_key"] = @create_key unless @create_key.nil?
+ result
+ end
+
+ # The JSON representation of the object.
+ #
+ # @return [String] the JSON string.
+ def to_json(*a)
+ Chef::JSONCompat.to_json(to_hash, *a)
+ end
+
+ def self.from_hash(o)
+ client = Chef::ApiClientV1.new
+ client.name(o["name"] || o["clientname"])
+ client.admin(o["admin"])
+ client.validator(o["validator"])
+ client.private_key(o["private_key"]) if o.key?("private_key")
+ client.public_key(o["public_key"]) if o.key?("public_key")
+ client.create_key(o["create_key"]) if o.key?("create_key")
+ client
+ end
+
+ def self.from_json(j)
+ Chef::ApiClientV1.from_hash(Chef::JSONCompat.from_json(j))
+ end
+
+ def self.reregister(name)
+ api_client = Chef::ApiClientV1.load(name)
+ api_client.reregister
+ end
+
+ def self.list(inflate=false)
+ if inflate
+ response = Hash.new
+ Chef::Search::Query.new.search(:client) do |n|
+ n = self.from_hash(n) if n.instance_of?(Hash)
+ response[n.name] = n
+ end
+ response
+ else
+ http_api.get("clients")
+ end
+ end
+
+ # Load a client by name via the API
+ def self.load(name)
+ response = http_api.get("clients/#{name}")
+ Chef::ApiClientV1.from_hash(response)
+ end
+
+ # Remove this client via the REST API
+ def destroy
+ chef_rest_v1.delete("clients/#{@name}")
+ end
+
+ # Save this client via the REST API, returns a hash including the private key
+ def save
+ begin
+ update
+ rescue Net::HTTPServerException => e
+ # If that fails, go ahead and try and update it
+ if e.response.code == "404"
+ create
+ else
+ raise e
+ end
+ end
+ end
+
+ def reregister
+ # Try API V0 and if it fails due to V0 not being supported, raise the proper error message.
+ # reregister only supported in API V0 or lesser.
+ reregistered_self = chef_rest_v0.put("clients/#{name}", { :name => name, :admin => admin, :validator => validator, :private_key => true })
+ if reregistered_self.respond_to?(:[])
+ private_key(reregistered_self["private_key"])
+ else
+ private_key(reregistered_self.private_key)
+ end
+ self
+ rescue Net::HTTPServerException => e
+ # if there was a 406 related to versioning, give error explaining that
+ # only API version 0 is supported for reregister command
+ if e.response.code == "406" && e.response["x-ops-server-api-version"]
+ version_header = Chef::JSONCompat.from_json(e.response["x-ops-server-api-version"])
+ min_version = version_header["min_version"]
+ max_version = version_header["max_version"]
+ error_msg = reregister_only_v0_supported_error_msg(max_version, min_version)
+ raise Chef::Exceptions::OnlyApiVersion0SupportedForAction.new(error_msg)
+ else
+ raise e
+ end
+ end
+
+ # Updates the client via the REST API
+ def update
+ # NOTE: API V1 dropped support for updating client keys via update (aka PUT),
+ # but this code never supported key updating in the first place. Since
+ # it was never implemented, we will simply ignore that functionality
+ # as it is being deprecated.
+ # Delete this comment after V0 support is dropped.
+ payload = { :name => name }
+ payload[:validator] = validator unless validator.nil?
+
+ # DEPRECATION
+ # This field is ignored in API V1, but left for backwards-compat,
+ # can remove after API V0 is no longer supported.
+ payload[:admin] = admin unless admin.nil?
+
+ begin
+ new_client = chef_rest_v1.put("clients/#{name}", payload)
+ rescue Net::HTTPServerException => e
+ # rescue API V0 if 406 and the server supports V0
+ supported_versions = server_client_api_version_intersection(e, SUPPORTED_API_VERSIONS)
+ raise e unless supported_versions && supported_versions.include?(0)
+ new_client = chef_rest_v0.put("clients/#{name}", payload)
+ end
+
+ Chef::ApiClientV1.from_hash(new_client)
+ end
+
+ # Create the client via the REST API
+ def create
+ payload = {
+ :name => name,
+ :validator => validator,
+ # this field is ignored in API V1, but left for backwards-compat,
+ # can remove after OSC 11 support is finished?
+ :admin => admin
+ }
+ begin
+ # try API V1
+ raise Chef::Exceptions::InvalidClientAttribute, "You cannot set both public_key and create_key for create." if !create_key.nil? && !public_key.nil?
+
+ payload[:public_key] = public_key unless public_key.nil?
+ payload[:create_key] = create_key unless create_key.nil?
+
+ new_client = chef_rest_v1.post("clients", payload)
+
+ # get the private_key out of the chef_key hash if it exists
+ if new_client['chef_key']
+ if new_client['chef_key']['private_key']
+ new_client['private_key'] = new_client['chef_key']['private_key']
+ end
+ new_client['public_key'] = new_client['chef_key']['public_key']
+ new_client.delete('chef_key')
+ end
+
+ rescue Net::HTTPServerException => e
+ # rescue API V0 if 406 and the server supports V0
+ supported_versions = server_client_api_version_intersection(e, SUPPORTED_API_VERSIONS)
+ raise e unless supported_versions && supported_versions.include?(0)
+
+ # under API V0, a key pair will always be created unless public_key is
+ # passed on initial POST
+ payload[:public_key] = public_key unless public_key.nil?
+
+ new_client = chef_rest_v0.post("clients", payload)
+ end
+ Chef::ApiClientV1.from_hash(self.to_hash.merge(new_client))
+ end
+
+ # As a string
+ def to_s
+ "client[#{@name}]"
+ end
+
+ end
+end
diff --git a/lib/chef/chef_fs/file_system/chef_server_root_dir.rb b/lib/chef/chef_fs/file_system/chef_server_root_dir.rb
index 370308ee0a..824325f31b 100644
--- a/lib/chef/chef_fs/file_system/chef_server_root_dir.rb
+++ b/lib/chef/chef_fs/file_system/chef_server_root_dir.rb
@@ -90,11 +90,11 @@ class Chef
end
def rest
- Chef::ServerAPI.new(chef_server_url, :client_name => chef_username, :signing_key_filename => chef_private_key, :raw_output => true)
+ Chef::ServerAPI.new(chef_server_url, :client_name => chef_username, :signing_key_filename => chef_private_key, :raw_output => true, :api_version => "0")
end
def get_json(path)
- Chef::ServerAPI.new(chef_server_url, :client_name => chef_username, :signing_key_filename => chef_private_key).get(path)
+ Chef::ServerAPI.new(chef_server_url, :client_name => chef_username, :signing_key_filename => chef_private_key, :api_version => "0").get(path)
end
def chef_rest
diff --git a/lib/chef/knife/client_bulk_delete.rb b/lib/chef/knife/client_bulk_delete.rb
index f2be772759..b439e6f995 100644
--- a/lib/chef/knife/client_bulk_delete.rb
+++ b/lib/chef/knife/client_bulk_delete.rb
@@ -23,7 +23,7 @@ class Chef
class ClientBulkDelete < Knife
deps do
- require 'chef/api_client'
+ require 'chef/api_client_v1'
require 'chef/json_compat'
end
@@ -39,7 +39,7 @@ class Chef
ui.fatal("You must supply a regular expression to match the results against")
exit 42
end
- all_clients = Chef::ApiClient.list(true)
+ all_clients = Chef::ApiClientV1.list(true)
matcher = /#{name_args[0]}/
clients_to_delete = {}
diff --git a/lib/chef/knife/client_create.rb b/lib/chef/knife/client_create.rb
index 570c1ee950..fa9a1a7e32 100644
--- a/lib/chef/knife/client_create.rb
+++ b/lib/chef/knife/client_create.rb
@@ -23,7 +23,7 @@ class Chef
class ClientCreate < Knife
deps do
- require 'chef/api_client'
+ require 'chef/api_client_v1'
require 'chef/json_compat'
end
@@ -57,12 +57,12 @@ class Chef
banner "knife client create CLIENTNAME (options)"
def client
- @client_field ||= Chef::ApiClient.new
+ @client_field ||= Chef::ApiClientV1.new
end
def create_client(client)
# should not be using save :( bad behavior
- client.save
+ Chef::ApiClientV1.from_hash(client).save
end
def run
@@ -93,7 +93,7 @@ class Chef
output = edit_data(client)
final_client = create_client(output)
- ui.info("Created #{output}")
+ ui.info("Created #{final_client}")
# output private_key if one
if final_client.private_key
diff --git a/lib/chef/knife/client_delete.rb b/lib/chef/knife/client_delete.rb
index d7d302ee1d..a49c0867a8 100644
--- a/lib/chef/knife/client_delete.rb
+++ b/lib/chef/knife/client_delete.rb
@@ -23,7 +23,7 @@ class Chef
class ClientDelete < Knife
deps do
- require 'chef/api_client'
+ require 'chef/api_client_v1'
require 'chef/json_compat'
end
@@ -43,8 +43,8 @@ class Chef
exit 1
end
- delete_object(Chef::ApiClient, @client_name, 'client') {
- object = Chef::ApiClient.load(@client_name)
+ delete_object(Chef::ApiClientV1, @client_name, 'client') {
+ object = Chef::ApiClientV1.load(@client_name)
if object.validator
unless config[:delete_validators]
ui.fatal("You must specify --delete-validators to delete the validator client #{@client_name}")
diff --git a/lib/chef/knife/client_edit.rb b/lib/chef/knife/client_edit.rb
index c81bce902a..5dcd8f212b 100644
--- a/lib/chef/knife/client_edit.rb
+++ b/lib/chef/knife/client_edit.rb
@@ -23,7 +23,7 @@ class Chef
class ClientEdit < Knife
deps do
- require 'chef/api_client'
+ require 'chef/api_client_v1'
require 'chef/json_compat'
end
@@ -38,7 +38,15 @@ class Chef
exit 1
end
- edit_object(Chef::ApiClient, @client_name)
+ original_data = Chef::ApiClientV1.load(@client_name).to_hash
+ edited_client = edit_data(original_data)
+ if original_data != edited_client
+ client = Chef::ApiClientV1.from_hash(edited_client)
+ client.save
+ ui.msg("Saved #{client}.")
+ else
+ ui.msg("Client unchanged, not saving.")
+ end
end
end
end
diff --git a/lib/chef/knife/client_list.rb b/lib/chef/knife/client_list.rb
index da0bf12dc3..d8a3698b6a 100644
--- a/lib/chef/knife/client_list.rb
+++ b/lib/chef/knife/client_list.rb
@@ -23,7 +23,7 @@ class Chef
class ClientList < Knife
deps do
- require 'chef/api_client'
+ require 'chef/api_client_v1'
require 'chef/json_compat'
end
@@ -35,7 +35,7 @@ class Chef
:description => "Show corresponding URIs"
def run
- output(format_list_for_display(Chef::ApiClient.list))
+ output(format_list_for_display(Chef::ApiClientV1.list))
end
end
end
diff --git a/lib/chef/knife/client_reregister.rb b/lib/chef/knife/client_reregister.rb
index 666fd09fd2..b94761e718 100644
--- a/lib/chef/knife/client_reregister.rb
+++ b/lib/chef/knife/client_reregister.rb
@@ -23,7 +23,7 @@ class Chef
class ClientReregister < Knife
deps do
- require 'chef/api_client'
+ require 'chef/api_client_v1'
require 'chef/json_compat'
end
@@ -43,7 +43,7 @@ class Chef
exit 1
end
- client = Chef::ApiClient.reregister(@client_name)
+ client = Chef::ApiClientV1.reregister(@client_name)
Chef::Log.debug("Updated client data: #{client.inspect}")
key = client.private_key
if config[:file]
diff --git a/lib/chef/knife/client_show.rb b/lib/chef/knife/client_show.rb
index 822848fdc2..bdac3f9758 100644
--- a/lib/chef/knife/client_show.rb
+++ b/lib/chef/knife/client_show.rb
@@ -25,7 +25,7 @@ class Chef
include Knife::Core::MultiAttributeReturnOption
deps do
- require 'chef/api_client'
+ require 'chef/api_client_v1'
require 'chef/json_compat'
end
@@ -40,7 +40,7 @@ class Chef
exit 1
end
- client = Chef::ApiClient.load(@client_name)
+ client = Chef::ApiClientV1.load(@client_name)
output(format_for_display(client))
end
diff --git a/lib/chef/knife/osc_user_create.rb b/lib/chef/knife/osc_user_create.rb
index c368296040..6c3415473f 100644
--- a/lib/chef/knife/osc_user_create.rb
+++ b/lib/chef/knife/osc_user_create.rb
@@ -27,7 +27,7 @@ class Chef
class OscUserCreate < Knife
deps do
- require 'chef/osc_user'
+ require 'chef/user'
require 'chef/json_compat'
end
@@ -69,7 +69,7 @@ class Chef
exit 1
end
- user = Chef::OscUser.new
+ user = Chef::User.new
user.name(@user_name)
user.admin(config[:admin])
user.password config[:user_password]
@@ -79,7 +79,7 @@ class Chef
end
output = edit_data(user)
- user = Chef::OscUser.from_hash(output).create
+ user = Chef::User.from_hash(output).create
ui.info("Created #{user}")
if user.private_key
diff --git a/lib/chef/knife/osc_user_delete.rb b/lib/chef/knife/osc_user_delete.rb
index d6fbd4a6a9..5cd4f10413 100644
--- a/lib/chef/knife/osc_user_delete.rb
+++ b/lib/chef/knife/osc_user_delete.rb
@@ -28,7 +28,7 @@ class Chef
class OscUserDelete < Knife
deps do
- require 'chef/osc_user'
+ require 'chef/user'
require 'chef/json_compat'
end
@@ -43,7 +43,7 @@ class Chef
exit 1
end
- delete_object(Chef::OscUser, @user_name)
+ delete_object(Chef::User, @user_name)
end
end
diff --git a/lib/chef/knife/osc_user_edit.rb b/lib/chef/knife/osc_user_edit.rb
index 4c38674d08..526475db05 100644
--- a/lib/chef/knife/osc_user_edit.rb
+++ b/lib/chef/knife/osc_user_edit.rb
@@ -28,7 +28,7 @@ class Chef
class OscUserEdit < Knife
deps do
- require 'chef/osc_user'
+ require 'chef/user'
require 'chef/json_compat'
end
@@ -43,10 +43,10 @@ class Chef
exit 1
end
- original_user = Chef::OscUser.load(@user_name).to_hash
+ original_user = Chef::User.load(@user_name).to_hash
edited_user = edit_data(original_user)
if original_user != edited_user
- user = Chef::OscUser.from_hash(edited_user)
+ user = Chef::User.from_hash(edited_user)
user.update
ui.msg("Saved #{user}.")
else
diff --git a/lib/chef/knife/osc_user_list.rb b/lib/chef/knife/osc_user_list.rb
index 92f049cd19..84fca31899 100644
--- a/lib/chef/knife/osc_user_list.rb
+++ b/lib/chef/knife/osc_user_list.rb
@@ -28,7 +28,7 @@ class Chef
class OscUserList < Knife
deps do
- require 'chef/osc_user'
+ require 'chef/user'
require 'chef/json_compat'
end
@@ -40,7 +40,7 @@ class Chef
:description => "Show corresponding URIs"
def run
- output(format_list_for_display(Chef::OscUser.list))
+ output(format_list_for_display(Chef::User.list))
end
end
end
diff --git a/lib/chef/knife/osc_user_reregister.rb b/lib/chef/knife/osc_user_reregister.rb
index a71e0aa677..163b286fe0 100644
--- a/lib/chef/knife/osc_user_reregister.rb
+++ b/lib/chef/knife/osc_user_reregister.rb
@@ -28,7 +28,7 @@ class Chef
class OscUserReregister < Knife
deps do
- require 'chef/osc_user'
+ require 'chef/user'
require 'chef/json_compat'
end
@@ -48,7 +48,7 @@ class Chef
exit 1
end
- user = Chef::OscUser.load(@user_name).reregister
+ user = Chef::User.load(@user_name).reregister
Chef::Log.debug("Updated user data: #{user.inspect}")
key = user.private_key
if config[:file]
diff --git a/lib/chef/knife/osc_user_show.rb b/lib/chef/knife/osc_user_show.rb
index 6a41ddae88..cb3a77585a 100644
--- a/lib/chef/knife/osc_user_show.rb
+++ b/lib/chef/knife/osc_user_show.rb
@@ -30,7 +30,7 @@ class Chef
include Knife::Core::MultiAttributeReturnOption
deps do
- require 'chef/osc_user'
+ require 'chef/user'
require 'chef/json_compat'
end
@@ -45,7 +45,7 @@ class Chef
exit 1
end
- user = Chef::OscUser.load(@user_name)
+ user = Chef::User.load(@user_name)
output(format_for_display(user))
end
diff --git a/lib/chef/knife/user_create.rb b/lib/chef/knife/user_create.rb
index e73f6be8b6..995573cd03 100644
--- a/lib/chef/knife/user_create.rb
+++ b/lib/chef/knife/user_create.rb
@@ -27,7 +27,7 @@ class Chef
attr_accessor :user_field
deps do
- require 'chef/user'
+ require 'chef/user_v1'
require 'chef/json_compat'
end
@@ -61,11 +61,11 @@ class Chef
banner "knife user create USERNAME DISPLAY_NAME FIRST_NAME LAST_NAME EMAIL PASSWORD (options)"
def user
- @user_field ||= Chef::User.new
+ @user_field ||= Chef::UserV1.new
end
def create_user_from_hash(hash)
- Chef::User.from_hash(hash).create
+ Chef::UserV1.from_hash(hash).create
end
def osc_11_warning
diff --git a/lib/chef/knife/user_delete.rb b/lib/chef/knife/user_delete.rb
index 803be6b90c..828cd51588 100644
--- a/lib/chef/knife/user_delete.rb
+++ b/lib/chef/knife/user_delete.rb
@@ -23,7 +23,7 @@ class Chef
class UserDelete < Knife
deps do
- require 'chef/user'
+ require 'chef/user_v1'
require 'chef/json_compat'
end
@@ -55,7 +55,7 @@ EOF
if Kernel.block_given?
object = block.call
else
- object = Chef::User.load(user_name)
+ object = Chef::UserV1.load(user_name)
object.destroy
end
@@ -77,10 +77,10 @@ EOF
# Below is modification of Chef::Knife.delete_object to detect OSC 11 server.
# When OSC 11 is deprecated, simply delete all this and go back to:
#
- # delete_object(Chef::User, @user_name)
+ # delete_object(Chef::UserV1, @user_name)
#
# Also delete our override of delete_object above
- object = Chef::User.load(@user_name)
+ object = Chef::UserV1.load(@user_name)
# OSC 11 case
if object.username.nil?
diff --git a/lib/chef/knife/user_edit.rb b/lib/chef/knife/user_edit.rb
index d194f6697b..c3a4326ee8 100644
--- a/lib/chef/knife/user_edit.rb
+++ b/lib/chef/knife/user_edit.rb
@@ -23,7 +23,7 @@ class Chef
class UserEdit < Knife
deps do
- require 'chef/user'
+ require 'chef/user_v1'
require 'chef/json_compat'
end
@@ -56,7 +56,7 @@ EOF
exit 1
end
- original_user = Chef::User.load(@user_name).to_hash
+ original_user = Chef::UserV1.load(@user_name).to_hash
# DEPRECATION NOTE
# Remove this if statement and corrosponding code post OSC 11 support.
#
@@ -68,7 +68,7 @@ EOF
else # EC / CS 12 user create
edited_user = edit_data(original_user)
if original_user != edited_user
- user = Chef::User.from_hash(edited_user)
+ user = Chef::UserV1.from_hash(edited_user)
user.update
ui.msg("Saved #{user}.")
else
diff --git a/lib/chef/knife/user_list.rb b/lib/chef/knife/user_list.rb
index 7ae43dadc9..6a130392b9 100644
--- a/lib/chef/knife/user_list.rb
+++ b/lib/chef/knife/user_list.rb
@@ -25,7 +25,7 @@ class Chef
class UserList < Knife
deps do
- require 'chef/user'
+ require 'chef/user_v1'
require 'chef/json_compat'
end
@@ -37,7 +37,7 @@ class Chef
:description => "Show corresponding URIs"
def run
- output(format_list_for_display(Chef::User.list))
+ output(format_list_for_display(Chef::UserV1.list))
end
end
diff --git a/lib/chef/knife/user_reregister.rb b/lib/chef/knife/user_reregister.rb
index eab2245025..09fd1cd2d6 100644
--- a/lib/chef/knife/user_reregister.rb
+++ b/lib/chef/knife/user_reregister.rb
@@ -23,7 +23,7 @@ class Chef
class UserReregister < Knife
deps do
- require 'chef/user'
+ require 'chef/user_v1'
require 'chef/json_compat'
end
@@ -61,7 +61,7 @@ EOF
exit 1
end
- user = Chef::User.load(@user_name)
+ user = Chef::UserV1.load(@user_name)
# DEPRECATION NOTE
# Remove this if statement and corrosponding code post OSC 11 support.
diff --git a/lib/chef/knife/user_show.rb b/lib/chef/knife/user_show.rb
index f5e81e9972..3a2443471a 100644
--- a/lib/chef/knife/user_show.rb
+++ b/lib/chef/knife/user_show.rb
@@ -25,7 +25,7 @@ class Chef
include Knife::Core::MultiAttributeReturnOption
deps do
- require 'chef/user'
+ require 'chef/user_v1'
require 'chef/json_compat'
end
@@ -58,7 +58,7 @@ EOF
exit 1
end
- user = Chef::User.load(@user_name)
+ user = Chef::UserV1.load(@user_name)
# DEPRECATION NOTE
# Remove this if statement and corrosponding code post OSC 11 support.
diff --git a/lib/chef/osc_user.rb b/lib/chef/osc_user.rb
deleted file mode 100644
index 52bfd11108..0000000000
--- a/lib/chef/osc_user.rb
+++ /dev/null
@@ -1,194 +0,0 @@
-#
-# Author:: Steven Danna (steve@opscode.com)
-# Copyright:: Copyright 2012 Opscode, Inc.
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-require 'chef/config'
-require 'chef/mixin/params_validate'
-require 'chef/mixin/from_file'
-require 'chef/mash'
-require 'chef/json_compat'
-require 'chef/search/query'
-
-# TODO
-# DEPRECATION NOTE
-# This class was previously Chef::User. It is the code to support the User object
-# corrosponding to the Open Source Chef Server 11 and only still exists to support
-# users still on OSC 11.
-#
-# Chef::User now supports Chef Server 12.
-#
-# New development should occur in Chef::User.
-# This file and corrosponding osc_user knife files
-# should be removed once client support for Open Source Chef Server 11 expires.
-class Chef
- class OscUser
-
- include Chef::Mixin::FromFile
- include Chef::Mixin::ParamsValidate
-
- def initialize
- @name = ''
- @public_key = nil
- @private_key = nil
- @password = nil
- @admin = false
- end
-
- def name(arg=nil)
- set_or_return(:name, arg,
- :regex => /^[a-z0-9\-_]+$/)
- end
-
- def admin(arg=nil)
- set_or_return(:admin,
- arg, :kind_of => [TrueClass, FalseClass])
- end
-
- def public_key(arg=nil)
- set_or_return(:public_key,
- arg, :kind_of => String)
- end
-
- def private_key(arg=nil)
- set_or_return(:private_key,
- arg, :kind_of => String)
- end
-
- def password(arg=nil)
- set_or_return(:password,
- arg, :kind_of => String)
- end
-
- def to_hash
- result = {
- "name" => @name,
- "public_key" => @public_key,
- "admin" => @admin
- }
- result["private_key"] = @private_key if @private_key
- result["password"] = @password if @password
- result
- end
-
- def to_json(*a)
- Chef::JSONCompat.to_json(to_hash, *a)
- end
-
- def destroy
- Chef::REST.new(Chef::Config[:chef_server_url]).delete_rest("users/#{@name}")
- end
-
- def create
- payload = {:name => self.name, :admin => self.admin, :password => self.password }
- payload[:public_key] = public_key if public_key
- new_user =Chef::REST.new(Chef::Config[:chef_server_url]).post_rest("users", payload)
- Chef::OscUser.from_hash(self.to_hash.merge(new_user))
- end
-
- def update(new_key=false)
- payload = {:name => name, :admin => admin}
- payload[:private_key] = new_key if new_key
- payload[:password] = password if password
- updated_user = Chef::REST.new(Chef::Config[:chef_server_url]).put_rest("users/#{name}", payload)
- Chef::OscUser.from_hash(self.to_hash.merge(updated_user))
- end
-
- def save(new_key=false)
- begin
- create
- rescue Net::HTTPServerException => e
- if e.response.code == "409"
- update(new_key)
- else
- raise e
- end
- end
- end
-
- def reregister
- r = Chef::REST.new(Chef::Config[:chef_server_url])
- reregistered_self = r.put_rest("users/#{name}", { :name => name, :admin => admin, :private_key => true })
- private_key(reregistered_self["private_key"])
- self
- end
-
- def to_s
- "user[#{@name}]"
- end
-
- def inspect
- "Chef::OscUser name:'#{name}' admin:'#{admin.inspect}'" +
- "public_key:'#{public_key}' private_key:#{private_key}"
- end
-
- # Class Methods
-
- def self.from_hash(user_hash)
- user = Chef::OscUser.new
- user.name user_hash['name']
- user.private_key user_hash['private_key'] if user_hash.key?('private_key')
- user.password user_hash['password'] if user_hash.key?('password')
- user.public_key user_hash['public_key']
- user.admin user_hash['admin']
- user
- end
-
- def self.from_json(json)
- Chef::OscUser.from_hash(Chef::JSONCompat.from_json(json))
- end
-
- class << self
- alias_method :json_create, :from_json
- end
-
- def self.list(inflate=false)
- response = Chef::REST.new(Chef::Config[:chef_server_url]).get_rest('users')
- users = if response.is_a?(Array)
- transform_ohc_list_response(response) # OHC/OPC
- else
- response # OSC
- end
- if inflate
- users.inject({}) do |user_map, (name, _url)|
- user_map[name] = Chef::OscUser.load(name)
- user_map
- end
- else
- users
- end
- end
-
- def self.load(name)
- response = Chef::REST.new(Chef::Config[:chef_server_url]).get_rest("users/#{name}")
- Chef::OscUser.from_hash(response)
- end
-
- # Gross. Transforms an API response in the form of:
- # [ { "user" => { "username" => USERNAME }}, ...]
- # into the form
- # { "USERNAME" => "URI" }
- def self.transform_ohc_list_response(response)
- new_response = Hash.new
- response.each do |u|
- name = u['user']['username']
- new_response[name] = Chef::Config[:chef_server_url] + "/users/#{name}"
- end
- new_response
- end
-
- private_class_method :transform_ohc_list_response
- end
-end
diff --git a/lib/chef/user.rb b/lib/chef/user.rb
index 717deb63c3..31ebeda86f 100644
--- a/lib/chef/user.rb
+++ b/lib/chef/user.rb
@@ -21,85 +21,45 @@ require 'chef/mixin/from_file'
require 'chef/mash'
require 'chef/json_compat'
require 'chef/search/query'
-require 'chef/mixin/api_version_request_handling'
-require 'chef/exceptions'
require 'chef/server_api'
-# OSC 11 BACKWARDS COMPATIBILITY NOTE (remove after OSC 11 support ends)
+# TODO
+# DEPRECATION NOTE
+# This class will be replaced by Chef::UserV1 in Chef 13. It is the code to support the User object
+# corrosponding to the Open Source Chef Server 11 and only still exists to support
+# users still on OSC 11.
#
-# In general, Chef::User is no longer expected to support Open Source Chef 11 Server requests.
-# The object that handles those requests has been moved to the Chef::OscUser namespace.
+# Chef::UserV1 now supports Chef Server 12 and will be moved to this namespace in Chef 13.
#
-# Exception: self.list is backwards compatible with OSC 11
+# New development should occur in Chef::UserV1.
+# This file and corrosponding osc_user knife files
+# should be removed once client support for Open Source Chef Server 11 expires.
class Chef
class User
include Chef::Mixin::FromFile
include Chef::Mixin::ParamsValidate
- include Chef::Mixin::ApiVersionRequestHandling
-
- SUPPORTED_API_VERSIONS = [0,1]
def initialize
- @username = nil
- @display_name = nil
- @first_name = nil
- @middle_name = nil
- @last_name = nil
- @email = nil
- @password = nil
+ @name = ''
@public_key = nil
@private_key = nil
- @create_key = nil
@password = nil
+ @admin = false
end
- def chef_root_rest_v0
- @chef_root_rest_v0 ||= Chef::ServerAPI.new(Chef::Config[:chef_server_root], {:api_version => "0"})
- end
-
- def chef_root_rest_v1
- @chef_root_rest_v1 ||= Chef::ServerAPI.new(Chef::Config[:chef_server_root], {:api_version => "1"})
+ def chef_rest_v0
+ @chef_rest_v0 ||= Chef::ServerAPI.new(Chef::Config[:chef_server_url], {:api_version => "0"})
end
- def username(arg=nil)
- set_or_return(:username, arg,
+ def name(arg=nil)
+ set_or_return(:name, arg,
:regex => /^[a-z0-9\-_]+$/)
end
- def display_name(arg=nil)
- set_or_return(:display_name,
- arg, :kind_of => String)
- end
-
- def first_name(arg=nil)
- set_or_return(:first_name,
- arg, :kind_of => String)
- end
-
- def middle_name(arg=nil)
- set_or_return(:middle_name,
- arg, :kind_of => String)
- end
-
- def last_name(arg=nil)
- set_or_return(:last_name,
- arg, :kind_of => String)
- end
-
- def email(arg=nil)
- set_or_return(:email,
- arg, :kind_of => String)
- end
-
- def password(arg=nil)
- set_or_return(:password,
- arg, :kind_of => String)
- end
-
- def create_key(arg=nil)
- set_or_return(:create_key, arg,
- :kind_of => [TrueClass, FalseClass])
+ def admin(arg=nil)
+ set_or_return(:admin,
+ arg, :kind_of => [TrueClass, FalseClass])
end
def public_key(arg=nil)
@@ -119,17 +79,12 @@ class Chef
def to_hash
result = {
- "username" => @username
+ "name" => @name,
+ "public_key" => @public_key,
+ "admin" => @admin
}
- result["display_name"] = @display_name unless @display_name.nil?
- result["first_name"] = @first_name unless @first_name.nil?
- result["middle_name"] = @middle_name unless @middle_name.nil?
- result["last_name"] = @last_name unless @last_name.nil?
- result["email"] = @email unless @email.nil?
- result["password"] = @password unless @password.nil?
- result["public_key"] = @public_key unless @public_key.nil?
- result["private_key"] = @private_key unless @private_key.nil?
- result["create_key"] = @create_key unless @create_key.nil?
+ result["private_key"] = @private_key if @private_key
+ result["password"] = @password if @password
result
end
@@ -138,86 +93,21 @@ class Chef
end
def destroy
- # will default to the current API version (Chef::Authenticator::DEFAULT_SERVER_API_VERSION)
- Chef::REST.new(Chef::Config[:chef_server_url]).delete("users/#{@username}")
+ chef_rest_v0.delete("users/#{@name}")
end
def create
- # try v1, fail back to v0 if v1 not supported
- begin
- payload = {
- :username => @username,
- :display_name => @display_name,
- :first_name => @first_name,
- :last_name => @last_name,
- :email => @email,
- :password => @password
- }
- payload[:public_key] = @public_key unless @public_key.nil?
- payload[:create_key] = @create_key unless @create_key.nil?
- payload[:middle_name] = @middle_name unless @middle_name.nil?
- raise Chef::Exceptions::InvalidUserAttribute, "You cannot set both public_key and create_key for create." if !@create_key.nil? && !@public_key.nil?
- new_user = chef_root_rest_v1.post("users", payload)
-
- # get the private_key out of the chef_key hash if it exists
- if new_user['chef_key']
- if new_user['chef_key']['private_key']
- new_user['private_key'] = new_user['chef_key']['private_key']
- end
- new_user['public_key'] = new_user['chef_key']['public_key']
- new_user.delete('chef_key')
- end
- rescue Net::HTTPServerException => e
- # rescue API V0 if 406 and the server supports V0
- supported_versions = server_client_api_version_intersection(e, SUPPORTED_API_VERSIONS)
- raise e unless supported_versions && supported_versions.include?(0)
- payload = {
- :username => @username,
- :display_name => @display_name,
- :first_name => @first_name,
- :last_name => @last_name,
- :email => @email,
- :password => @password
- }
- payload[:middle_name] = @middle_name unless @middle_name.nil?
- payload[:public_key] = @public_key unless @public_key.nil?
- # under API V0, the server will create a key pair if public_key isn't passed
- new_user = chef_root_rest_v0.post("users", payload)
- end
-
+ payload = {:name => self.name, :admin => self.admin, :password => self.password }
+ payload[:public_key] = public_key if public_key
+ new_user = chef_rest_v0.post("users", payload)
Chef::User.from_hash(self.to_hash.merge(new_user))
end
def update(new_key=false)
- begin
- payload = {:username => username}
- payload[:display_name] = display_name unless display_name.nil?
- payload[:first_name] = first_name unless first_name.nil?
- payload[:middle_name] = middle_name unless middle_name.nil?
- payload[:last_name] = last_name unless last_name.nil?
- payload[:email] = email unless email.nil?
- payload[:password] = password unless password.nil?
-
- # API V1 will fail if these key fields are defined, and try V0 below if relevant 400 is returned
- payload[:public_key] = public_key unless public_key.nil?
- payload[:private_key] = new_key if new_key
-
- updated_user = chef_root_rest_v1.put("users/#{username}", payload)
- rescue Net::HTTPServerException => e
- if e.response.code == "400"
- # if a 400 is returned but the error message matches the error related to private / public key fields, try V0
- # else, raise the 400
- error = Chef::JSONCompat.from_json(e.response.body)["error"].first
- error_match = /Since Server API v1, all keys must be updated via the keys endpoint/.match(error)
- if error_match.nil?
- raise e
- end
- else # for other types of errors, test for API versioning errors right away
- supported_versions = server_client_api_version_intersection(e, SUPPORTED_API_VERSIONS)
- raise e unless supported_versions && supported_versions.include?(0)
- end
- updated_user = chef_root_rest_v0.put("users/#{username}", payload)
- end
+ payload = {:name => name, :admin => admin}
+ payload[:private_key] = new_key if new_key
+ payload[:password] = password if password
+ updated_user = chef_rest_v0.put("users/#{name}", payload)
Chef::User.from_hash(self.to_hash.merge(updated_user))
end
@@ -233,47 +123,30 @@ class Chef
end
end
- # Note: remove after API v0 no longer supported by client (and knife command).
def reregister
- begin
- payload = self.to_hash.merge({"private_key" => true})
- reregistered_self = chef_root_rest_v0.put("users/#{username}", payload)
- private_key(reregistered_self["private_key"])
- # only V0 supported for reregister
- rescue Net::HTTPServerException => e
- # if there was a 406 related to versioning, give error explaining that
- # only API version 0 is supported for reregister command
- if e.response.code == "406" && e.response["x-ops-server-api-version"]
- version_header = Chef::JSONCompat.from_json(e.response["x-ops-server-api-version"])
- min_version = version_header["min_version"]
- max_version = version_header["max_version"]
- error_msg = reregister_only_v0_supported_error_msg(max_version, min_version)
- raise Chef::Exceptions::OnlyApiVersion0SupportedForAction.new(error_msg)
- else
- raise e
- end
- end
+ reregistered_self = chef_rest_v0.put("users/#{name}", { :name => name, :admin => admin, :private_key => true })
+ private_key(reregistered_self["private_key"])
self
end
def to_s
- "user[#{@username}]"
+ "user[#{@name}]"
+ end
+
+ def inspect
+ "Chef::User name:'#{name}' admin:'#{admin.inspect}'" +
+ "public_key:'#{public_key}' private_key:#{private_key}"
end
# Class Methods
def self.from_hash(user_hash)
user = Chef::User.new
- user.username user_hash['username']
- user.display_name user_hash['display_name'] if user_hash.key?('display_name')
- user.first_name user_hash['first_name'] if user_hash.key?('first_name')
- user.middle_name user_hash['middle_name'] if user_hash.key?('middle_name')
- user.last_name user_hash['last_name'] if user_hash.key?('last_name')
- user.email user_hash['email'] if user_hash.key?('email')
- user.password user_hash['password'] if user_hash.key?('password')
- user.public_key user_hash['public_key'] if user_hash.key?('public_key')
+ user.name user_hash['name']
user.private_key user_hash['private_key'] if user_hash.key?('private_key')
- user.create_key user_hash['create_key'] if user_hash.key?('create_key')
+ user.password user_hash['password'] if user_hash.key?('password')
+ user.public_key user_hash['public_key']
+ user.admin user_hash['admin']
user
end
@@ -286,19 +159,12 @@ class Chef
end
def self.list(inflate=false)
- response = Chef::REST.new(Chef::Config[:chef_server_url]).get('users')
+ response = Chef::ServerAPI.new(Chef::Config[:chef_server_url], {:api_version => "0"}).get('users')
users = if response.is_a?(Array)
- # EC 11 / CS 12 V0, V1
- # GET /organizations/<org>/users
- transform_list_response(response)
- else
- # OSC 11
- # GET /users
- # EC 11 / CS 12 V0, V1
- # GET /users
- response # OSC
- end
-
+ transform_ohc_list_response(response) # OHC/OPC
+ else
+ response # OSC
+ end
if inflate
users.inject({}) do |user_map, (name, _url)|
user_map[name] = Chef::User.load(name)
@@ -309,9 +175,8 @@ class Chef
end
end
- def self.load(username)
- # will default to the current API version (Chef::Authenticator::DEFAULT_SERVER_API_VERSION)
- response = Chef::REST.new(Chef::Config[:chef_server_url]).get("users/#{username}")
+ def self.load(name)
+ response = Chef::ServerAPI.new(Chef::Config[:chef_server_url], {:api_version => "0"}).get("users/#{name}")
Chef::User.from_hash(response)
end
@@ -319,7 +184,7 @@ class Chef
# [ { "user" => { "username" => USERNAME }}, ...]
# into the form
# { "USERNAME" => "URI" }
- def self.transform_list_response(response)
+ def self.transform_ohc_list_response(response)
new_response = Hash.new
response.each do |u|
name = u['user']['username']
@@ -328,7 +193,6 @@ class Chef
new_response
end
- private_class_method :transform_list_response
-
+ private_class_method :transform_ohc_list_response
end
end
diff --git a/lib/chef/user_v1.rb b/lib/chef/user_v1.rb
new file mode 100644
index 0000000000..31cb0576a2
--- /dev/null
+++ b/lib/chef/user_v1.rb
@@ -0,0 +1,335 @@
+#
+# Author:: Steven Danna (steve@opscode.com)
+# Copyright:: Copyright 2012 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+require 'chef/config'
+require 'chef/mixin/params_validate'
+require 'chef/mixin/from_file'
+require 'chef/mash'
+require 'chef/json_compat'
+require 'chef/search/query'
+require 'chef/mixin/api_version_request_handling'
+require 'chef/exceptions'
+require 'chef/server_api'
+
+# OSC 11 BACKWARDS COMPATIBILITY NOTE (remove after OSC 11 support ends)
+#
+# In general, Chef::UserV1 is no longer expected to support Open Source Chef 11 Server requests.
+# The object that handles those requests remain in the Chef::User namespace.
+# This code will be moved to the Chef::User namespace as of Chef 13.
+#
+# Exception: self.list is backwards compatible with OSC 11
+class Chef
+ class UserV1
+
+ include Chef::Mixin::FromFile
+ include Chef::Mixin::ParamsValidate
+ include Chef::Mixin::ApiVersionRequestHandling
+
+ SUPPORTED_API_VERSIONS = [0,1]
+
+ def initialize
+ @username = nil
+ @display_name = nil
+ @first_name = nil
+ @middle_name = nil
+ @last_name = nil
+ @email = nil
+ @password = nil
+ @public_key = nil
+ @private_key = nil
+ @create_key = nil
+ @password = nil
+ end
+
+ def chef_root_rest_v0
+ @chef_root_rest_v0 ||= Chef::ServerAPI.new(Chef::Config[:chef_server_root], {:api_version => "0"})
+ end
+
+ def chef_root_rest_v1
+ @chef_root_rest_v1 ||= Chef::ServerAPI.new(Chef::Config[:chef_server_root], {:api_version => "1"})
+ end
+
+ def username(arg=nil)
+ set_or_return(:username, arg,
+ :regex => /^[a-z0-9\-_]+$/)
+ end
+
+ def display_name(arg=nil)
+ set_or_return(:display_name,
+ arg, :kind_of => String)
+ end
+
+ def first_name(arg=nil)
+ set_or_return(:first_name,
+ arg, :kind_of => String)
+ end
+
+ def middle_name(arg=nil)
+ set_or_return(:middle_name,
+ arg, :kind_of => String)
+ end
+
+ def last_name(arg=nil)
+ set_or_return(:last_name,
+ arg, :kind_of => String)
+ end
+
+ def email(arg=nil)
+ set_or_return(:email,
+ arg, :kind_of => String)
+ end
+
+ def password(arg=nil)
+ set_or_return(:password,
+ arg, :kind_of => String)
+ end
+
+ def create_key(arg=nil)
+ set_or_return(:create_key, arg,
+ :kind_of => [TrueClass, FalseClass])
+ end
+
+ def public_key(arg=nil)
+ set_or_return(:public_key,
+ arg, :kind_of => String)
+ end
+
+ def private_key(arg=nil)
+ set_or_return(:private_key,
+ arg, :kind_of => String)
+ end
+
+ def password(arg=nil)
+ set_or_return(:password,
+ arg, :kind_of => String)
+ end
+
+ def to_hash
+ result = {
+ "username" => @username
+ }
+ result["display_name"] = @display_name unless @display_name.nil?
+ result["first_name"] = @first_name unless @first_name.nil?
+ result["middle_name"] = @middle_name unless @middle_name.nil?
+ result["last_name"] = @last_name unless @last_name.nil?
+ result["email"] = @email unless @email.nil?
+ result["password"] = @password unless @password.nil?
+ result["public_key"] = @public_key unless @public_key.nil?
+ result["private_key"] = @private_key unless @private_key.nil?
+ result["create_key"] = @create_key unless @create_key.nil?
+ result
+ end
+
+ def to_json(*a)
+ Chef::JSONCompat.to_json(to_hash, *a)
+ end
+
+ def destroy
+ # will default to the current API version (Chef::Authenticator::DEFAULT_SERVER_API_VERSION)
+ Chef::REST.new(Chef::Config[:chef_server_url]).delete("users/#{@username}")
+ end
+
+ def create
+ # try v1, fail back to v0 if v1 not supported
+ begin
+ payload = {
+ :username => @username,
+ :display_name => @display_name,
+ :first_name => @first_name,
+ :last_name => @last_name,
+ :email => @email,
+ :password => @password
+ }
+ payload[:public_key] = @public_key unless @public_key.nil?
+ payload[:create_key] = @create_key unless @create_key.nil?
+ payload[:middle_name] = @middle_name unless @middle_name.nil?
+ raise Chef::Exceptions::InvalidUserAttribute, "You cannot set both public_key and create_key for create." if !@create_key.nil? && !@public_key.nil?
+ new_user = chef_root_rest_v1.post("users", payload)
+
+ # get the private_key out of the chef_key hash if it exists
+ if new_user['chef_key']
+ if new_user['chef_key']['private_key']
+ new_user['private_key'] = new_user['chef_key']['private_key']
+ end
+ new_user['public_key'] = new_user['chef_key']['public_key']
+ new_user.delete('chef_key')
+ end
+ rescue Net::HTTPServerException => e
+ # rescue API V0 if 406 and the server supports V0
+ supported_versions = server_client_api_version_intersection(e, SUPPORTED_API_VERSIONS)
+ raise e unless supported_versions && supported_versions.include?(0)
+ payload = {
+ :username => @username,
+ :display_name => @display_name,
+ :first_name => @first_name,
+ :last_name => @last_name,
+ :email => @email,
+ :password => @password
+ }
+ payload[:middle_name] = @middle_name unless @middle_name.nil?
+ payload[:public_key] = @public_key unless @public_key.nil?
+ # under API V0, the server will create a key pair if public_key isn't passed
+ new_user = chef_root_rest_v0.post("users", payload)
+ end
+
+ Chef::UserV1.from_hash(self.to_hash.merge(new_user))
+ end
+
+ def update(new_key=false)
+ begin
+ payload = {:username => username}
+ payload[:display_name] = display_name unless display_name.nil?
+ payload[:first_name] = first_name unless first_name.nil?
+ payload[:middle_name] = middle_name unless middle_name.nil?
+ payload[:last_name] = last_name unless last_name.nil?
+ payload[:email] = email unless email.nil?
+ payload[:password] = password unless password.nil?
+
+ # API V1 will fail if these key fields are defined, and try V0 below if relevant 400 is returned
+ payload[:public_key] = public_key unless public_key.nil?
+ payload[:private_key] = new_key if new_key
+
+ updated_user = chef_root_rest_v1.put("users/#{username}", payload)
+ rescue Net::HTTPServerException => e
+ if e.response.code == "400"
+ # if a 400 is returned but the error message matches the error related to private / public key fields, try V0
+ # else, raise the 400
+ error = Chef::JSONCompat.from_json(e.response.body)["error"].first
+ error_match = /Since Server API v1, all keys must be updated via the keys endpoint/.match(error)
+ if error_match.nil?
+ raise e
+ end
+ else # for other types of errors, test for API versioning errors right away
+ supported_versions = server_client_api_version_intersection(e, SUPPORTED_API_VERSIONS)
+ raise e unless supported_versions && supported_versions.include?(0)
+ end
+ updated_user = chef_root_rest_v0.put("users/#{username}", payload)
+ end
+ Chef::UserV1.from_hash(self.to_hash.merge(updated_user))
+ end
+
+ def save(new_key=false)
+ begin
+ create
+ rescue Net::HTTPServerException => e
+ if e.response.code == "409"
+ update(new_key)
+ else
+ raise e
+ end
+ end
+ end
+
+ # Note: remove after API v0 no longer supported by client (and knife command).
+ def reregister
+ begin
+ payload = self.to_hash.merge({"private_key" => true})
+ reregistered_self = chef_root_rest_v0.put("users/#{username}", payload)
+ private_key(reregistered_self["private_key"])
+ # only V0 supported for reregister
+ rescue Net::HTTPServerException => e
+ # if there was a 406 related to versioning, give error explaining that
+ # only API version 0 is supported for reregister command
+ if e.response.code == "406" && e.response["x-ops-server-api-version"]
+ version_header = Chef::JSONCompat.from_json(e.response["x-ops-server-api-version"])
+ min_version = version_header["min_version"]
+ max_version = version_header["max_version"]
+ error_msg = reregister_only_v0_supported_error_msg(max_version, min_version)
+ raise Chef::Exceptions::OnlyApiVersion0SupportedForAction.new(error_msg)
+ else
+ raise e
+ end
+ end
+ self
+ end
+
+ def to_s
+ "user[#{@username}]"
+ end
+
+ # Class Methods
+
+ def self.from_hash(user_hash)
+ user = Chef::UserV1.new
+ user.username user_hash['username']
+ user.display_name user_hash['display_name'] if user_hash.key?('display_name')
+ user.first_name user_hash['first_name'] if user_hash.key?('first_name')
+ user.middle_name user_hash['middle_name'] if user_hash.key?('middle_name')
+ user.last_name user_hash['last_name'] if user_hash.key?('last_name')
+ user.email user_hash['email'] if user_hash.key?('email')
+ user.password user_hash['password'] if user_hash.key?('password')
+ user.public_key user_hash['public_key'] if user_hash.key?('public_key')
+ user.private_key user_hash['private_key'] if user_hash.key?('private_key')
+ user.create_key user_hash['create_key'] if user_hash.key?('create_key')
+ user
+ end
+
+ def self.from_json(json)
+ Chef::UserV1.from_hash(Chef::JSONCompat.from_json(json))
+ end
+
+ class << self
+ alias_method :json_create, :from_json
+ end
+
+ def self.list(inflate=false)
+ response = Chef::REST.new(Chef::Config[:chef_server_url]).get('users')
+ users = if response.is_a?(Array)
+ # EC 11 / CS 12 V0, V1
+ # GET /organizations/<org>/users
+ transform_list_response(response)
+ else
+ # OSC 11
+ # GET /users
+ # EC 11 / CS 12 V0, V1
+ # GET /users
+ response # OSC
+ end
+
+ if inflate
+ users.inject({}) do |user_map, (name, _url)|
+ user_map[name] = Chef::UserV1.load(name)
+ user_map
+ end
+ else
+ users
+ end
+ end
+
+ def self.load(username)
+ # will default to the current API version (Chef::Authenticator::DEFAULT_SERVER_API_VERSION)
+ response = Chef::REST.new(Chef::Config[:chef_server_url]).get("users/#{username}")
+ Chef::UserV1.from_hash(response)
+ end
+
+ # Gross. Transforms an API response in the form of:
+ # [ { "user" => { "username" => USERNAME }}, ...]
+ # into the form
+ # { "USERNAME" => "URI" }
+ def self.transform_list_response(response)
+ new_response = Hash.new
+ response.each do |u|
+ name = u['user']['username']
+ new_response[name] = Chef::Config[:chef_server_url] + "/users/#{name}"
+ end
+ new_response
+ end
+
+ private_class_method :transform_list_response
+
+ end
+end
diff --git a/spec/support/shared/unit/api_versioning.rb b/spec/support/shared/unit/api_versioning.rb
index a4f353de60..05a4117f8e 100644
--- a/spec/support/shared/unit/api_versioning.rb
+++ b/spec/support/shared/unit/api_versioning.rb
@@ -26,7 +26,7 @@ shared_examples_for "version handling" do
allow(rest_v1).to receive(http_verb).and_raise(exception_406)
end
- context "when the server does not support the min or max server API version that Chef::User supports" do
+ context "when the server does not support the min or max server API version that Chef::UserV1 supports" do
before do
allow(object).to receive(:server_client_api_version_intersection).and_return([])
end
@@ -34,7 +34,7 @@ shared_examples_for "version handling" do
it "raises the original exception" do
expect{ object.send(method) }.to raise_error(exception_406)
end
- end # when the server does not support the min or max server API version that Chef::User supports
+ end # when the server does not support the min or max server API version that Chef::UserV1 supports
end # version handling
shared_examples_for "user and client reregister" do
diff --git a/spec/unit/api_client_spec.rb b/spec/unit/api_client_spec.rb
index bc4b38848b..a0e399b470 100644
--- a/spec/unit/api_client_spec.rb
+++ b/spec/unit/api_client_spec.rb
@@ -21,6 +21,11 @@ require 'spec_helper'
require 'chef/api_client'
require 'tempfile'
+# DEPRECATION NOTE
+#
+# This code will be removed in Chef 13 in favor of the code in Chef::ApiClientV1,
+# which will be moved to this namespace. New development should occur in
+# Chef::ApiClientV1 until the time before Chef 13.
describe Chef::ApiClient do
before(:each) do
@client = Chef::ApiClient.new
@@ -53,20 +58,6 @@ describe Chef::ApiClient do
expect { @client.admin(Hash.new) }.to raise_error(ArgumentError)
end
- it "has an create_key flag attribute" do
- @client.create_key(true)
- expect(@client.create_key).to be_truthy
- end
-
- it "create_key defaults to false" do
- expect(@client.create_key).to be_falsey
- end
-
- it "allows only boolean values for the create_key flag" do
- expect { @client.create_key(false) }.not_to raise_error
- expect { @client.create_key(Hash.new) }.to raise_error(ArgumentError)
- end
-
it "has a 'validator' flag attribute" do
@client.validator(true)
expect(@client.validator).to be_truthy
@@ -129,12 +120,6 @@ describe Chef::ApiClient do
expect(@json).to include(%q{"validator":false})
end
- it "includes the 'create_key' flag when present" do
- @client.create_key(true)
- @json = @client.to_json
- expect(@json).to include(%q{"create_key":true})
- end
-
it "includes the private key when present" do
@client.private_key("monkeypants")
expect(@client.to_json).to include(%q{"private_key":"monkeypants"})
@@ -143,15 +128,11 @@ describe Chef::ApiClient do
it "does not include the private key if not present" do
expect(@json).not_to include("private_key")
end
-
- include_examples "to_json equivalent to Chef::JSONCompat.to_json" do
- let(:jsonable) { @client }
- end
end
describe "when deserializing from JSON (string) using ApiClient#from_json" do
let(:client_string) do
- "{\"name\":\"black\",\"public_key\":\"crowes\",\"private_key\":\"monkeypants\",\"admin\":true,\"validator\":true,\"create_key\":true}"
+ "{\"name\":\"black\",\"public_key\":\"crowes\",\"private_key\":\"monkeypants\",\"admin\":true,\"validator\":true}"
end
let(:client) do
@@ -178,10 +159,6 @@ describe Chef::ApiClient do
expect(client.admin).to be_truthy
end
- it "preserves the create_key status" do
- expect(client.create_key).to be_truthy
- end
-
it "preserves the 'validator' status" do
expect(client.validator).to be_truthy
end
@@ -199,7 +176,6 @@ describe Chef::ApiClient do
"private_key" => "monkeypants",
"admin" => true,
"validator" => true,
- "create_key" => true,
"json_class" => "Chef::ApiClient"
}
end
@@ -224,10 +200,6 @@ describe Chef::ApiClient do
expect(client.admin).to be_truthy
end
- it "preserves the create_key status" do
- expect(client.create_key).to be_truthy
- end
-
it "preserves the 'validator' status" do
expect(client.validator).to be_truthy
end
@@ -243,18 +215,16 @@ describe Chef::ApiClient do
before(:each) do
client = {
- "name" => "black",
- "clientname" => "black",
- "public_key" => "crowes",
- "private_key" => "monkeypants",
- "admin" => true,
- "create_key" => true,
- "validator" => true,
- "json_class" => "Chef::ApiClient"
+ "name" => "black",
+ "clientname" => "black",
+ "public_key" => "crowes",
+ "private_key" => "monkeypants",
+ "admin" => true,
+ "validator" => true,
+ "json_class" => "Chef::ApiClient"
}
-
- @http_client = double("Chef::REST mock")
- allow(Chef::REST).to receive(:new).and_return(@http_client)
+ @http_client = double("Chef::ServerAPI mock")
+ allow(Chef::ServerAPI).to receive(:new).and_return(@http_client)
expect(@http_client).to receive(:get).with("clients/black").and_return(client)
@client = Chef::ApiClient.load(client['name'])
end
@@ -275,10 +245,6 @@ describe Chef::ApiClient do
expect(@client.admin).to be_a_kind_of(TrueClass)
end
- it "preserves the create_key status" do
- expect(@client.create_key).to be_a_kind_of(TrueClass)
- end
-
it "preserves the 'validator' status" do
expect(@client.validator).to be_a_kind_of(TrueClass)
end
@@ -304,18 +270,13 @@ describe Chef::ApiClient do
File.open(Chef::Config[:client_key], "r") {|f| f.read.chomp }
end
- it "has an HTTP client configured with default credentials" do
- expect(@client.http_api).to be_a_kind_of(Chef::REST)
- expect(@client.http_api.client_name).to eq("silent-bob")
- expect(@client.http_api.signing_key.to_s).to eq(private_key_data)
- end
end
describe "when requesting a new key" do
before do
@http_client = double("Chef::REST mock")
- allow(Chef::REST).to receive(:new).and_return(@http_client)
+ allow(Chef::ServerAPI).to receive(:new).and_return(@http_client)
end
context "and the client does not exist on the server" do
@@ -332,34 +293,24 @@ describe Chef::ApiClient do
end
context "and the client exists" do
- let(:chef_rest_v0_mock) { double('chef rest root v0 object') }
- let(:payload) {
- {:name => "lost-my-key", :admin => false, :validator => false, :private_key => true}
- }
-
before do
@api_client_without_key = Chef::ApiClient.new
@api_client_without_key.name("lost-my-key")
- allow(@api_client_without_key).to receive(:chef_rest_v0).and_return(chef_rest_v0_mock)
- #allow(@api_client_with_key).to receive(:http_api).and_return(_api_mock)
-
- allow(chef_rest_v0_mock).to receive(:put).with("clients/lost-my-key", payload).and_return(@api_client_with_key)
- allow(chef_rest_v0_mock).to receive(:get).with("clients/lost-my-key").and_return(@api_client_without_key)
- allow(@http_client).to receive(:get).with("clients/lost-my-key").and_return(@api_client_without_key)
+ expect(@http_client).to receive(:get).with("clients/lost-my-key").and_return(@api_client_without_key)
end
+
context "and the client exists on a Chef 11-like server" do
before do
@api_client_with_key = Chef::ApiClient.new
@api_client_with_key.name("lost-my-key")
@api_client_with_key.private_key("the new private key")
- allow(@api_client_with_key).to receive(:chef_rest_v0).and_return(chef_rest_v0_mock)
+ expect(@http_client).to receive(:put).
+ with("clients/lost-my-key", :name => "lost-my-key", :admin => false, :validator => false, :private_key => true).
+ and_return(@api_client_with_key)
end
it "returns an ApiClient with a private key" do
- expect(chef_rest_v0_mock).to receive(:put).with("clients/lost-my-key", payload).
- and_return(@api_client_with_key)
-
response = Chef::ApiClient.reregister("lost-my-key")
# no sane == method for ApiClient :'(
expect(response).to eq(@api_client_without_key)
@@ -372,7 +323,7 @@ describe Chef::ApiClient do
context "and the client exists on a Chef 10-like server" do
before do
@api_client_with_key = {"name" => "lost-my-key", "private_key" => "the new private key"}
- expect(chef_rest_v0_mock).to receive(:put).
+ expect(@http_client).to receive(:put).
with("clients/lost-my-key", :name => "lost-my-key", :admin => false, :validator => false, :private_key => true).
and_return(@api_client_with_key)
end
@@ -390,134 +341,4 @@ describe Chef::ApiClient do
end
end
-
- describe "Versioned API Interactions" do
- let(:response_406) { OpenStruct.new(:code => '406') }
- let(:exception_406) { Net::HTTPServerException.new("406 Not Acceptable", response_406) }
- let(:payload) {
- {
- :name => "some_name",
- :validator => true,
- :admin => true
- }
- }
-
- before do
- @client = Chef::ApiClient.new
- allow(@client).to receive(:chef_rest_v0).and_return(double('chef rest root v0 object'))
- allow(@client).to receive(:chef_rest_v1).and_return(double('chef rest root v1 object'))
- @client.name "some_name"
- @client.validator true
- @client.admin true
- end
-
- describe "create" do
-
- # from spec/support/shared/unit/user_and_client_shared.rb
- it_should_behave_like "user or client create" do
- let(:object) { @client }
- let(:error) { Chef::Exceptions::InvalidClientAttribute }
- let(:rest_v0) { @client.chef_rest_v0 }
- let(:rest_v1) { @client.chef_rest_v1 }
- let(:url) { "clients" }
- end
-
- context "when API V1 is not supported by the server" do
- # from spec/support/shared/unit/api_versioning.rb
- it_should_behave_like "version handling" do
- let(:object) { @client }
- let(:method) { :create }
- let(:http_verb) { :post }
- let(:rest_v1) { @client.chef_rest_v1 }
- end
- end
-
- end # create
-
- describe "update" do
- context "when a valid client is defined" do
-
- shared_examples_for "client updating" do
- it "updates the client" do
- expect(rest). to receive(:put).with("clients/some_name", payload)
- @client.update
- end
-
- context "when only the name field exists" do
-
- before do
- # needed since there is no way to set to nil via code
- @client.instance_variable_set(:@validator, nil)
- @client.instance_variable_set(:@admin, nil)
- end
-
- after do
- @client.validator true
- @client.admin true
- end
-
- it "updates the client with only the name" do
- expect(rest). to receive(:put).with("clients/some_name", {:name => "some_name"})
- @client.update
- end
- end
-
- end
-
- context "when API V1 is supported by the server" do
-
- it_should_behave_like "client updating" do
- let(:rest) { @client.chef_rest_v1 }
- end
-
- end # when API V1 is supported by the server
-
- context "when API V1 is not supported by the server" do
- context "when no version is supported" do
- # from spec/support/shared/unit/api_versioning.rb
- it_should_behave_like "version handling" do
- let(:object) { @client }
- let(:method) { :create }
- let(:http_verb) { :post }
- let(:rest_v1) { @client.chef_rest_v1 }
- end
- end # when no version is supported
-
- context "when API V0 is supported" do
-
- before do
- allow(@client.chef_rest_v1).to receive(:put).and_raise(exception_406)
- allow(@client).to receive(:server_client_api_version_intersection).and_return([0])
- end
-
- it_should_behave_like "client updating" do
- let(:rest) { @client.chef_rest_v0 }
- end
-
- end
-
- end # when API V1 is not supported by the server
- end # when a valid client is defined
- end # update
-
- # DEPRECATION
- # This can be removed after API V0 support is gone
- describe "reregister" do
- context "when server API V0 is valid on the Chef Server receiving the request" do
- it "creates a new object via the API" do
- expect(@client.chef_rest_v0).to receive(:put).with("clients/#{@client.name}", payload.merge({:private_key => true})).and_return({})
- @client.reregister
- end
- end # when server API V0 is valid on the Chef Server receiving the request
-
- context "when server API V0 is not supported by the Chef Server" do
- # from spec/support/shared/unit/api_versioning.rb
- it_should_behave_like "user and client reregister" do
- let(:object) { @client }
- let(:rest_v0) { @client.chef_rest_v0 }
- end
- end # when server API V0 is not supported by the Chef Server
- end # reregister
-
- end
end
diff --git a/spec/unit/api_client_v1_spec.rb b/spec/unit/api_client_v1_spec.rb
new file mode 100644
index 0000000000..17aba8c3af
--- /dev/null
+++ b/spec/unit/api_client_v1_spec.rb
@@ -0,0 +1,457 @@
+#
+# Author:: Adam Jacob (<adam@opscode.com>)
+# Copyright:: Copyright (c) 2008 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+
+require 'chef/api_client_v1'
+require 'tempfile'
+
+describe Chef::ApiClientV1 do
+ before(:each) do
+ @client = Chef::ApiClientV1.new
+ end
+
+ it "has a name attribute" do
+ @client.name("ops_master")
+ expect(@client.name).to eq("ops_master")
+ end
+
+ it "does not allow spaces in the name" do
+ expect { @client.name "ops master" }.to raise_error(ArgumentError)
+ end
+
+ it "only allows string values for the name" do
+ expect { @client.name Hash.new }.to raise_error(ArgumentError)
+ end
+
+ it "has an admin flag attribute" do
+ @client.admin(true)
+ expect(@client.admin).to be_truthy
+ end
+
+ it "defaults to non-admin" do
+ expect(@client.admin).to be_falsey
+ end
+
+ it "allows only boolean values for the admin flag" do
+ expect { @client.admin(false) }.not_to raise_error
+ expect { @client.admin(Hash.new) }.to raise_error(ArgumentError)
+ end
+
+ it "has an create_key flag attribute" do
+ @client.create_key(true)
+ expect(@client.create_key).to be_truthy
+ end
+
+ it "create_key defaults to false" do
+ expect(@client.create_key).to be_falsey
+ end
+
+ it "allows only boolean values for the create_key flag" do
+ expect { @client.create_key(false) }.not_to raise_error
+ expect { @client.create_key(Hash.new) }.to raise_error(ArgumentError)
+ end
+
+ it "has a 'validator' flag attribute" do
+ @client.validator(true)
+ expect(@client.validator).to be_truthy
+ end
+
+ it "defaults to non-validator" do
+ expect(@client.validator).to be_falsey
+ end
+
+ it "allows only boolean values for the 'validator' flag" do
+ expect { @client.validator(false) }.not_to raise_error
+ expect { @client.validator(Hash.new) }.to raise_error(ArgumentError)
+ end
+
+ it "has a public key attribute" do
+ @client.public_key("super public")
+ expect(@client.public_key).to eq("super public")
+ end
+
+ it "accepts only String values for the public key" do
+ expect { @client.public_key "" }.not_to raise_error
+ expect { @client.public_key Hash.new }.to raise_error(ArgumentError)
+ end
+
+
+ it "has a private key attribute" do
+ @client.private_key("super private")
+ expect(@client.private_key).to eq("super private")
+ end
+
+ it "accepts only String values for the private key" do
+ expect { @client.private_key "" }.not_to raise_error
+ expect { @client.private_key Hash.new }.to raise_error(ArgumentError)
+ end
+
+ describe "when serializing to JSON" do
+ before(:each) do
+ @client.name("black")
+ @client.public_key("crowes")
+ @json = @client.to_json
+ end
+
+ it "serializes as a JSON object" do
+ expect(@json).to match(/^\{.+\}$/)
+ end
+
+ it "includes the name value" do
+ expect(@json).to include(%q{"name":"black"})
+ end
+
+ it "includes the public key value" do
+ expect(@json).to include(%{"public_key":"crowes"})
+ end
+
+ it "includes the 'admin' flag" do
+ expect(@json).to include(%q{"admin":false})
+ end
+
+ it "includes the 'validator' flag" do
+ expect(@json).to include(%q{"validator":false})
+ end
+
+ it "includes the 'create_key' flag when present" do
+ @client.create_key(true)
+ @json = @client.to_json
+ expect(@json).to include(%q{"create_key":true})
+ end
+
+ it "includes the private key when present" do
+ @client.private_key("monkeypants")
+ expect(@client.to_json).to include(%q{"private_key":"monkeypants"})
+ end
+
+ it "does not include the private key if not present" do
+ expect(@json).not_to include("private_key")
+ end
+
+ include_examples "to_json equivalent to Chef::JSONCompat.to_json" do
+ let(:jsonable) { @client }
+ end
+ end
+
+ describe "when deserializing from JSON (string) using ApiClient#from_json" do
+ let(:client_string) do
+ "{\"name\":\"black\",\"public_key\":\"crowes\",\"private_key\":\"monkeypants\",\"admin\":true,\"validator\":true,\"create_key\":true}"
+ end
+
+ let(:client) do
+ Chef::ApiClientV1.from_json(client_string)
+ end
+
+ it "does not require a 'json_class' string" do
+ expect(Chef::JSONCompat.parse(client_string)["json_class"]).to eq(nil)
+ end
+
+ it "should deserialize to a Chef::ApiClientV1 object" do
+ expect(client).to be_a_kind_of(Chef::ApiClientV1)
+ end
+
+ it "preserves the name" do
+ expect(client.name).to eq("black")
+ end
+
+ it "preserves the public key" do
+ expect(client.public_key).to eq("crowes")
+ end
+
+ it "preserves the admin status" do
+ expect(client.admin).to be_truthy
+ end
+
+ it "preserves the create_key status" do
+ expect(client.create_key).to be_truthy
+ end
+
+ it "preserves the 'validator' status" do
+ expect(client.validator).to be_truthy
+ end
+
+ it "includes the private key if present" do
+ expect(client.private_key).to eq("monkeypants")
+ end
+ end
+
+ describe "when deserializing from JSON (hash) using ApiClientV1#from_json" do
+ let(:client_hash) do
+ {
+ "name" => "black",
+ "public_key" => "crowes",
+ "private_key" => "monkeypants",
+ "admin" => true,
+ "validator" => true,
+ "create_key" => true
+ }
+ end
+
+ let(:client) do
+ Chef::ApiClientV1.from_json(Chef::JSONCompat.to_json(client_hash))
+ end
+
+ it "should deserialize to a Chef::ApiClientV1 object" do
+ expect(client).to be_a_kind_of(Chef::ApiClientV1)
+ end
+
+ it "preserves the name" do
+ expect(client.name).to eq("black")
+ end
+
+ it "preserves the public key" do
+ expect(client.public_key).to eq("crowes")
+ end
+
+ it "preserves the admin status" do
+ expect(client.admin).to be_truthy
+ end
+
+ it "preserves the create_key status" do
+ expect(client.create_key).to be_truthy
+ end
+
+ it "preserves the 'validator' status" do
+ expect(client.validator).to be_truthy
+ end
+
+ it "includes the private key if present" do
+ expect(client.private_key).to eq("monkeypants")
+ end
+ end
+
+ describe "when loading from JSON" do
+ before do
+ end
+
+ before(:each) do
+ client = {
+ "name" => "black",
+ "clientname" => "black",
+ "public_key" => "crowes",
+ "private_key" => "monkeypants",
+ "admin" => true,
+ "create_key" => true,
+ "validator" => true
+ }
+
+ @http_client = double("Chef::ServerAPI mock")
+ allow(Chef::ServerAPI).to receive(:new).and_return(@http_client)
+ expect(@http_client).to receive(:get).with("clients/black").and_return(client)
+ @client = Chef::ApiClientV1.load(client['name'])
+ end
+
+ it "should deserialize to a Chef::ApiClientV1 object" do
+ expect(@client).to be_a_kind_of(Chef::ApiClientV1)
+ end
+
+ it "preserves the name" do
+ expect(@client.name).to eq("black")
+ end
+
+ it "preserves the public key" do
+ expect(@client.public_key).to eq("crowes")
+ end
+
+ it "preserves the admin status" do
+ expect(@client.admin).to be_a_kind_of(TrueClass)
+ end
+
+ it "preserves the create_key status" do
+ expect(@client.create_key).to be_a_kind_of(TrueClass)
+ end
+
+ it "preserves the 'validator' status" do
+ expect(@client.validator).to be_a_kind_of(TrueClass)
+ end
+
+ it "includes the private key if present" do
+ expect(@client.private_key).to eq("monkeypants")
+ end
+
+ end
+
+ describe "with correctly configured API credentials" do
+ before do
+ Chef::Config[:node_name] = "silent-bob"
+ Chef::Config[:client_key] = File.expand_path('ssl/private_key.pem', CHEF_SPEC_DATA)
+ end
+
+ after do
+ Chef::Config[:node_name] = nil
+ Chef::Config[:client_key] = nil
+ end
+
+ let :private_key_data do
+ File.open(Chef::Config[:client_key], "r") {|f| f.read.chomp }
+ end
+
+ end
+
+
+ describe "when requesting a new key" do
+ before do
+ @http_client = double("Chef::ServerAPI mock")
+ allow(Chef::ServerAPI).to receive(:new).and_return(@http_client)
+ end
+
+ context "and the client does not exist on the server" do
+ before do
+ @a_404_response = Net::HTTPNotFound.new("404 not found and such", nil, nil)
+ @a_404_exception = Net::HTTPServerException.new("404 not found exception", @a_404_response)
+
+ expect(@http_client).to receive(:get).with("clients/lost-my-key").and_raise(@a_404_exception)
+ end
+
+ it "raises a 404 error" do
+ expect { Chef::ApiClientV1.reregister("lost-my-key") }.to raise_error(Net::HTTPServerException)
+ end
+ end
+ end
+
+ describe "Versioned API Interactions" do
+ let(:response_406) { OpenStruct.new(:code => '406') }
+ let(:exception_406) { Net::HTTPServerException.new("406 Not Acceptable", response_406) }
+ let(:payload) {
+ {
+ :name => "some_name",
+ :validator => true,
+ :admin => true
+ }
+ }
+
+ before do
+ @client = Chef::ApiClientV1.new
+ allow(@client).to receive(:chef_rest_v0).and_return(double('chef rest root v0 object'))
+ allow(@client).to receive(:chef_rest_v1).and_return(double('chef rest root v1 object'))
+ @client.name "some_name"
+ @client.validator true
+ @client.admin true
+ end
+
+ describe "create" do
+
+ # from spec/support/shared/unit/user_and_client_shared.rb
+ it_should_behave_like "user or client create" do
+ let(:object) { @client }
+ let(:error) { Chef::Exceptions::InvalidClientAttribute }
+ let(:rest_v0) { @client.chef_rest_v0 }
+ let(:rest_v1) { @client.chef_rest_v1 }
+ let(:url) { "clients" }
+ end
+
+ context "when API V1 is not supported by the server" do
+ # from spec/support/shared/unit/api_versioning.rb
+ it_should_behave_like "version handling" do
+ let(:object) { @client }
+ let(:method) { :create }
+ let(:http_verb) { :post }
+ let(:rest_v1) { @client.chef_rest_v1 }
+ end
+ end
+
+ end # create
+
+ describe "update" do
+ context "when a valid client is defined" do
+
+ shared_examples_for "client updating" do
+ it "updates the client" do
+ expect(rest). to receive(:put).with("clients/some_name", payload).and_return(payload)
+ @client.update
+ end
+
+ context "when only the name field exists" do
+
+ before do
+ # needed since there is no way to set to nil via code
+ @client.instance_variable_set(:@validator, nil)
+ @client.instance_variable_set(:@admin, nil)
+ end
+
+ after do
+ @client.validator true
+ @client.admin true
+ end
+
+ it "updates the client with only the name" do
+ expect(rest). to receive(:put).with("clients/some_name", {:name => "some_name"}).and_return({:name => "some_name"})
+ @client.update
+ end
+ end
+
+ end
+
+ context "when API V1 is supported by the server" do
+
+ it_should_behave_like "client updating" do
+ let(:rest) { @client.chef_rest_v1 }
+ end
+
+ end # when API V1 is supported by the server
+
+ context "when API V1 is not supported by the server" do
+ context "when no version is supported" do
+ # from spec/support/shared/unit/api_versioning.rb
+ it_should_behave_like "version handling" do
+ let(:object) { @client }
+ let(:method) { :create }
+ let(:http_verb) { :post }
+ let(:rest_v1) { @client.chef_rest_v1 }
+ end
+ end # when no version is supported
+
+ context "when API V0 is supported" do
+
+ before do
+ allow(@client.chef_rest_v1).to receive(:put).and_raise(exception_406)
+ allow(@client).to receive(:server_client_api_version_intersection).and_return([0])
+ end
+
+ it_should_behave_like "client updating" do
+ let(:rest) { @client.chef_rest_v0 }
+ end
+
+ end
+
+ end # when API V1 is not supported by the server
+ end # when a valid client is defined
+ end # update
+
+ # DEPRECATION
+ # This can be removed after API V0 support is gone
+ describe "reregister" do
+ context "when server API V0 is valid on the Chef Server receiving the request" do
+ it "creates a new object via the API" do
+ expect(@client.chef_rest_v0).to receive(:put).with("clients/#{@client.name}", payload.merge({:private_key => true})).and_return({})
+ @client.reregister
+ end
+ end # when server API V0 is valid on the Chef Server receiving the request
+
+ context "when server API V0 is not supported by the Chef Server" do
+ # from spec/support/shared/unit/api_versioning.rb
+ it_should_behave_like "user and client reregister" do
+ let(:object) { @client }
+ let(:rest_v0) { @client.chef_rest_v0 }
+ end
+ end # when server API V0 is not supported by the Chef Server
+ end # reregister
+
+ end
+end
diff --git a/spec/unit/knife/client_bulk_delete_spec.rb b/spec/unit/knife/client_bulk_delete_spec.rb
index 45bb4dd16c..1a6317ac00 100644
--- a/spec/unit/knife/client_bulk_delete_spec.rb
+++ b/spec/unit/knife/client_bulk_delete_spec.rb
@@ -45,7 +45,7 @@ describe Chef::Knife::ClientBulkDelete do
clients = Hash.new
nonvalidator_client_names.each do |client_name|
- client = Chef::ApiClient.new()
+ client = Chef::ApiClientV1.new()
client.name(client_name)
allow(client).to receive(:destroy).and_return(true)
clients[client_name] = client
@@ -59,7 +59,7 @@ describe Chef::Knife::ClientBulkDelete do
clients = Hash.new
validator_client_names.each do |validator_client_name|
- validator_client = Chef::ApiClient.new()
+ validator_client = Chef::ApiClientV1.new()
validator_client.name(validator_client_name)
allow(validator_client).to receive(:validator).and_return(true)
allow(validator_client).to receive(:destroy).and_return(true)
@@ -75,7 +75,7 @@ describe Chef::Knife::ClientBulkDelete do
}
before(:each) do
- allow(Chef::ApiClient).to receive(:list).and_return(clients)
+ allow(Chef::ApiClientV1).to receive(:list).and_return(clients)
end
describe "run" do
@@ -89,7 +89,7 @@ describe Chef::Knife::ClientBulkDelete do
describe "with any clients" do
it "should get the list of the clients" do
- expect(Chef::ApiClient).to receive(:list)
+ expect(Chef::ApiClientV1).to receive(:list)
knife.run
end
diff --git a/spec/unit/knife/client_create_spec.rb b/spec/unit/knife/client_create_spec.rb
index 8fecfc885f..a1dcc564e2 100644
--- a/spec/unit/knife/client_create_spec.rb
+++ b/spec/unit/knife/client_create_spec.rb
@@ -34,7 +34,7 @@ describe Chef::Knife::ClientCreate do
end
let(:client) do
- Chef::ApiClient.new
+ Chef::ApiClientV1.new
end
let(:knife) do
diff --git a/spec/unit/knife/client_delete_spec.rb b/spec/unit/knife/client_delete_spec.rb
index 0fb5e0bab7..619009979b 100644
--- a/spec/unit/knife/client_delete_spec.rb
+++ b/spec/unit/knife/client_delete_spec.rb
@@ -30,7 +30,7 @@ describe Chef::Knife::ClientDelete do
describe 'run' do
it 'should delete the client' do
- expect(@knife).to receive(:delete_object).with(Chef::ApiClient, 'adam', 'client')
+ expect(@knife).to receive(:delete_object).with(Chef::ApiClientV1, 'adam', 'client')
@knife.run
end
@@ -46,8 +46,8 @@ describe Chef::Knife::ClientDelete do
before(:each) do
allow(Chef::Knife::UI).to receive(:confirm).and_return(true)
allow(@knife).to receive(:confirm).and_return(true)
- @client = Chef::ApiClient.new
- expect(Chef::ApiClient).to receive(:load).and_return(@client)
+ @client = Chef::ApiClientV1.new
+ expect(Chef::ApiClientV1).to receive(:load).and_return(@client)
end
it 'should delete non-validator client if --delete-validators is not set' do
diff --git a/spec/unit/knife/client_edit_spec.rb b/spec/unit/knife/client_edit_spec.rb
index c040c5e2f2..ad56d9212d 100644
--- a/spec/unit/knife/client_edit_spec.rb
+++ b/spec/unit/knife/client_edit_spec.rb
@@ -17,16 +17,29 @@
#
require 'spec_helper'
+require 'chef/api_client_v1'
describe Chef::Knife::ClientEdit do
before(:each) do
@knife = Chef::Knife::ClientEdit.new
@knife.name_args = [ 'adam' ]
+ @knife.config[:disable_editing] = true
end
describe 'run' do
+ let(:data) {
+ {
+ "name" => "adam",
+ "validator" => false,
+ "admin" => false,
+ "chef_type" => "client",
+ "create_key" => true
+ }
+ }
+
it 'should edit the client' do
- expect(@knife).to receive(:edit_object).with(Chef::ApiClient, 'adam')
+ allow(Chef::ApiClientV1).to receive(:load).with('adam').and_return(data)
+ expect(@knife).to receive(:edit_data).with(data).and_return(data)
@knife.run
end
diff --git a/spec/unit/knife/client_list_spec.rb b/spec/unit/knife/client_list_spec.rb
index eff01da4e9..ce0fa4f5e8 100644
--- a/spec/unit/knife/client_list_spec.rb
+++ b/spec/unit/knife/client_list_spec.rb
@@ -26,7 +26,7 @@ describe Chef::Knife::ClientList do
describe 'run' do
it 'should list the clients' do
- expect(Chef::ApiClient).to receive(:list)
+ expect(Chef::ApiClientV1).to receive(:list)
expect(@knife).to receive(:format_list_for_display)
@knife.run
end
diff --git a/spec/unit/knife/client_reregister_spec.rb b/spec/unit/knife/client_reregister_spec.rb
index f1be4ed570..7e763242e4 100644
--- a/spec/unit/knife/client_reregister_spec.rb
+++ b/spec/unit/knife/client_reregister_spec.rb
@@ -41,7 +41,7 @@ describe Chef::Knife::ClientReregister do
context 'when not configured for file output' do
it 'reregisters the client and prints the key' do
- expect(Chef::ApiClient).to receive(:reregister).with('adam').and_return(@client_mock)
+ expect(Chef::ApiClientV1).to receive(:reregister).with('adam').and_return(@client_mock)
@knife.run
expect(@stdout.string).to match( /foo_key/ )
end
@@ -49,7 +49,7 @@ describe Chef::Knife::ClientReregister do
context 'when configured for file output' do
it 'should write the private key to a file' do
- expect(Chef::ApiClient).to receive(:reregister).with('adam').and_return(@client_mock)
+ expect(Chef::ApiClientV1).to receive(:reregister).with('adam').and_return(@client_mock)
@knife.config[:file] = '/tmp/monkeypants'
filehandle = StringIO.new
diff --git a/spec/unit/knife/client_show_spec.rb b/spec/unit/knife/client_show_spec.rb
index 8404e8d019..73a876cee0 100644
--- a/spec/unit/knife/client_show_spec.rb
+++ b/spec/unit/knife/client_show_spec.rb
@@ -27,7 +27,7 @@ describe Chef::Knife::ClientShow do
describe 'run' do
it 'should list the client' do
- expect(Chef::ApiClient).to receive(:load).with('adam').and_return(@client_mock)
+ expect(Chef::ApiClientV1).to receive(:load).with('adam').and_return(@client_mock)
expect(@knife).to receive(:format_for_display).with(@client_mock)
@knife.run
end
@@ -37,7 +37,7 @@ describe Chef::Knife::ClientShow do
@stdout = StringIO.new
allow(@knife.ui).to receive(:stdout).and_return(@stdout)
fake_client_contents = {"foo"=>"bar", "baz"=>"qux"}
- expect(Chef::ApiClient).to receive(:load).with('adam').and_return(fake_client_contents)
+ expect(Chef::ApiClientV1).to receive(:load).with('adam').and_return(fake_client_contents)
@knife.run
expect(@stdout.string).to eql("{\n \"foo\": \"bar\",\n \"baz\": \"qux\"\n}\n")
end
diff --git a/spec/unit/knife/osc_user_create_spec.rb b/spec/unit/knife/osc_user_create_spec.rb
index 1b17d0d22f..e4ed78fe2b 100644
--- a/spec/unit/knife/osc_user_create_spec.rb
+++ b/spec/unit/knife/osc_user_create_spec.rb
@@ -36,19 +36,19 @@ describe Chef::Knife::OscUserCreate do
@knife.name_args = [ 'a_user' ]
@knife.config[:user_password] = "foobar"
- @user = Chef::OscUser.new
+ @user = Chef::User.new
@user.name "a_user"
- @user_with_private_key = Chef::OscUser.new
+ @user_with_private_key = Chef::User.new
@user_with_private_key.name "a_user"
@user_with_private_key.private_key 'private_key'
allow(@user).to receive(:create).and_return(@user_with_private_key)
- allow(Chef::OscUser).to receive(:new).and_return(@user)
- allow(Chef::OscUser).to receive(:from_hash).and_return(@user)
+ allow(Chef::User).to receive(:new).and_return(@user)
+ allow(Chef::User).to receive(:from_hash).and_return(@user)
allow(@knife).to receive(:edit_data).and_return(@user.to_hash)
end
it "creates a new user" do
- expect(Chef::OscUser).to receive(:new).and_return(@user)
+ expect(Chef::User).to receive(:new).and_return(@user)
expect(@user).to receive(:create)
@knife.run
expect(@stderr.string).to match /created user.+a_user/i
diff --git a/spec/unit/knife/osc_user_delete_spec.rb b/spec/unit/knife/osc_user_delete_spec.rb
index 0e16393ffe..4a3ec4228f 100644
--- a/spec/unit/knife/osc_user_delete_spec.rb
+++ b/spec/unit/knife/osc_user_delete_spec.rb
@@ -31,7 +31,7 @@ describe Chef::Knife::OscUserDelete do
end
it 'deletes the user' do
- expect(@knife).to receive(:delete_object).with(Chef::OscUser, 'my_user')
+ expect(@knife).to receive(:delete_object).with(Chef::User, 'my_user')
@knife.run
end
diff --git a/spec/unit/knife/osc_user_edit_spec.rb b/spec/unit/knife/osc_user_edit_spec.rb
index 71a9192389..279f2e30ef 100644
--- a/spec/unit/knife/osc_user_edit_spec.rb
+++ b/spec/unit/knife/osc_user_edit_spec.rb
@@ -38,7 +38,7 @@ describe Chef::Knife::OscUserEdit do
it 'loads and edits the user' do
data = { :name => "my_user" }
- allow(Chef::OscUser).to receive(:load).with("my_user").and_return(data)
+ allow(Chef::User).to receive(:load).with("my_user").and_return(data)
expect(@knife).to receive(:edit_data).with(data).and_return(data)
@knife.run
end
diff --git a/spec/unit/knife/osc_user_list_spec.rb b/spec/unit/knife/osc_user_list_spec.rb
index 59a15be058..f496a414b8 100644
--- a/spec/unit/knife/osc_user_list_spec.rb
+++ b/spec/unit/knife/osc_user_list_spec.rb
@@ -30,7 +30,7 @@ describe Chef::Knife::OscUserList do
end
it 'lists the users' do
- expect(Chef::OscUser).to receive(:list)
+ expect(Chef::User).to receive(:list)
expect(@knife).to receive(:format_list_for_display)
@knife.run
end
diff --git a/spec/unit/knife/osc_user_reregister_spec.rb b/spec/unit/knife/osc_user_reregister_spec.rb
index 406bbf1f3e..989eb180f1 100644
--- a/spec/unit/knife/osc_user_reregister_spec.rb
+++ b/spec/unit/knife/osc_user_reregister_spec.rb
@@ -29,7 +29,7 @@ describe Chef::Knife::OscUserReregister do
@knife = Chef::Knife::OscUserReregister.new
@knife.name_args = [ 'a_user' ]
@user_mock = double('user_mock', :private_key => "private_key")
- allow(Chef::OscUser).to receive(:load).and_return(@user_mock)
+ allow(Chef::User).to receive(:load).and_return(@user_mock)
@stdout = StringIO.new
allow(@knife.ui).to receive(:stdout).and_return(@stdout)
end
diff --git a/spec/unit/knife/osc_user_show_spec.rb b/spec/unit/knife/osc_user_show_spec.rb
index 67b9b45809..18d2086099 100644
--- a/spec/unit/knife/osc_user_show_spec.rb
+++ b/spec/unit/knife/osc_user_show_spec.rb
@@ -32,7 +32,7 @@ describe Chef::Knife::OscUserShow do
end
it 'loads and displays the user' do
- expect(Chef::OscUser).to receive(:load).with('my_user').and_return(@user_mock)
+ expect(Chef::User).to receive(:load).with('my_user').and_return(@user_mock)
expect(@knife).to receive(:format_for_display).with(@user_mock)
@knife.run
end
diff --git a/spec/unit/knife/user_create_spec.rb b/spec/unit/knife/user_create_spec.rb
index 49d62cc2d7..fa5c8324b4 100644
--- a/spec/unit/knife/user_create_spec.rb
+++ b/spec/unit/knife/user_create_spec.rb
@@ -186,7 +186,7 @@ describe Chef::Knife::UserCreate do
context "when a private_key is returned" do
before do
- allow(knife).to receive(:create_user_from_hash).and_return(Chef::User.from_hash(knife.user.to_hash.merge({"private_key" => "some_private_key"})))
+ allow(knife).to receive(:create_user_from_hash).and_return(Chef::UserV1.from_hash(knife.user.to_hash.merge({"private_key" => "some_private_key"})))
end
context "when --file is passed" do
diff --git a/spec/unit/knife/user_delete_spec.rb b/spec/unit/knife/user_delete_spec.rb
index e49c781358..a24160624a 100644
--- a/spec/unit/knife/user_delete_spec.rb
+++ b/spec/unit/knife/user_delete_spec.rb
@@ -26,7 +26,7 @@ describe Chef::Knife::UserDelete do
before(:each) do
Chef::Knife::UserDelete.load_deps
knife.name_args = [ 'my_user' ]
- allow(Chef::User).to receive(:load).and_return(user)
+ allow(Chef::UserV1).to receive(:load).and_return(user)
allow(user).to receive(:username).and_return('my_user')
allow(knife.ui).to receive(:stderr).and_return(stdout)
allow(knife.ui).to receive(:stdout).and_return(stdout)
@@ -51,7 +51,7 @@ describe Chef::Knife::UserDelete do
end
it 'deletes the user' do
- #expect(knife).to receive(:delete_object).with(Chef::User, 'my_user')
+ #expect(knife).to receive(:delete_object).with(Chef::UserV1, 'my_user')
expect(knife).to receive(:delete_object).with('my_user')
knife.run
end
diff --git a/spec/unit/knife/user_edit_spec.rb b/spec/unit/knife/user_edit_spec.rb
index 15a7726b20..a21d982d29 100644
--- a/spec/unit/knife/user_edit_spec.rb
+++ b/spec/unit/knife/user_edit_spec.rb
@@ -36,7 +36,7 @@ describe Chef::Knife::UserEdit do
context "when the username field is not supported by the server" do
before do
allow(knife).to receive(:run_osc_11_user_edit).and_raise(SystemExit)
- allow(Chef::User).to receive(:load).and_return({"username" => nil})
+ allow(Chef::UserV1).to receive(:load).and_return({"username" => nil})
end
it "displays the osc warning" do
@@ -52,7 +52,7 @@ describe Chef::Knife::UserEdit do
it 'loads and edits the user' do
data = { "username" => "my_user" }
- allow(Chef::User).to receive(:load).with("my_user").and_return(data)
+ allow(Chef::UserV1).to receive(:load).with("my_user").and_return(data)
expect(knife).to receive(:edit_data).with(data).and_return(data)
knife.run
end
diff --git a/spec/unit/knife/user_list_spec.rb b/spec/unit/knife/user_list_spec.rb
index 9990cc802d..fa2bac426e 100644
--- a/spec/unit/knife/user_list_spec.rb
+++ b/spec/unit/knife/user_list_spec.rb
@@ -29,7 +29,7 @@ describe Chef::Knife::UserList do
end
it 'lists the users' do
- expect(Chef::User).to receive(:list)
+ expect(Chef::UserV1).to receive(:list)
expect(knife).to receive(:format_list_for_display)
knife.run
end
diff --git a/spec/unit/knife/user_reregister_spec.rb b/spec/unit/knife/user_reregister_spec.rb
index 412a6ec374..89aa6726cd 100644
--- a/spec/unit/knife/user_reregister_spec.rb
+++ b/spec/unit/knife/user_reregister_spec.rb
@@ -26,7 +26,7 @@ describe Chef::Knife::UserReregister do
before do
Chef::Knife::UserReregister.load_deps
knife.name_args = [ 'a_user' ]
- allow(Chef::User).to receive(:load).and_return(user_mock)
+ allow(Chef::UserV1).to receive(:load).and_return(user_mock)
allow(knife.ui).to receive(:stdout).and_return(stdout)
allow(knife.ui).to receive(:stderr).and_return(stdout)
allow(user_mock).to receive(:username).and_return('a_user')
diff --git a/spec/unit/knife/user_show_spec.rb b/spec/unit/knife/user_show_spec.rb
index 43392a3a5c..7c39e428c0 100644
--- a/spec/unit/knife/user_show_spec.rb
+++ b/spec/unit/knife/user_show_spec.rb
@@ -35,7 +35,7 @@ describe Chef::Knife::UserShow do
context "when the username field is not supported by the server" do
before do
allow(knife).to receive(:run_osc_11_user_show).and_raise(SystemExit)
- allow(Chef::User).to receive(:load).with('my_user').and_return(user_mock)
+ allow(Chef::UserV1).to receive(:load).with('my_user').and_return(user_mock)
allow(user_mock).to receive(:username).and_return(nil)
end
@@ -51,7 +51,7 @@ describe Chef::Knife::UserShow do
end
it 'loads and displays the user' do
- expect(Chef::User).to receive(:load).with('my_user').and_return(user_mock)
+ expect(Chef::UserV1).to receive(:load).with('my_user').and_return(user_mock)
expect(knife).to receive(:format_for_display).with(user_mock)
knife.run
end
diff --git a/spec/unit/osc_user_spec.rb b/spec/unit/osc_user_spec.rb
deleted file mode 100644
index 16731b47bd..0000000000
--- a/spec/unit/osc_user_spec.rb
+++ /dev/null
@@ -1,276 +0,0 @@
-#
-# Author:: Steven Danna (steve@opscode.com)
-# Copyright:: Copyright (c) 2012 Opscode, Inc.
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-# DEPRECATION NOTE
-# This code only remains to support users still operating with
-# Open Source Chef Server 11 and should be removed once support
-# for OSC 11 ends. New development should occur in user_spec.rb.
-
-require 'spec_helper'
-
-require 'chef/osc_user'
-require 'tempfile'
-
-describe Chef::OscUser do
- before(:each) do
- @user = Chef::OscUser.new
- end
-
- describe "initialize" do
- it "should be a Chef::OscUser" do
- expect(@user).to be_a_kind_of(Chef::OscUser)
- end
- end
-
- describe "name" do
- it "should let you set the name to a string" do
- expect(@user.name("ops_master")).to eq("ops_master")
- end
-
- it "should return the current name" do
- @user.name "ops_master"
- expect(@user.name).to eq("ops_master")
- end
-
- # It is not feasible to check all invalid characters. Here are a few
- # that we probably care about.
- it "should not accept invalid characters" do
- # capital letters
- expect { @user.name "Bar" }.to raise_error(ArgumentError)
- # slashes
- expect { @user.name "foo/bar" }.to raise_error(ArgumentError)
- # ?
- expect { @user.name "foo?" }.to raise_error(ArgumentError)
- # &
- expect { @user.name "foo&" }.to raise_error(ArgumentError)
- end
-
-
- it "should not accept spaces" do
- expect { @user.name "ops master" }.to raise_error(ArgumentError)
- end
-
- it "should throw an ArgumentError if you feed it anything but a string" do
- expect { @user.name Hash.new }.to raise_error(ArgumentError)
- end
- end
-
- describe "admin" do
- it "should let you set the admin bit" do
- expect(@user.admin(true)).to eq(true)
- end
-
- it "should return the current admin value" do
- @user.admin true
- expect(@user.admin).to eq(true)
- end
-
- it "should default to false" do
- expect(@user.admin).to eq(false)
- end
-
- it "should throw an ArgumentError if you feed it anything but true or false" do
- expect { @user.name Hash.new }.to raise_error(ArgumentError)
- end
- end
-
- describe "public_key" do
- it "should let you set the public key" do
- expect(@user.public_key("super public")).to eq("super public")
- end
-
- it "should return the current public key" do
- @user.public_key("super public")
- expect(@user.public_key).to eq("super public")
- end
-
- it "should throw an ArgumentError if you feed it something lame" do
- expect { @user.public_key Hash.new }.to raise_error(ArgumentError)
- end
- end
-
- describe "private_key" do
- it "should let you set the private key" do
- expect(@user.private_key("super private")).to eq("super private")
- end
-
- it "should return the private key" do
- @user.private_key("super private")
- expect(@user.private_key).to eq("super private")
- end
-
- it "should throw an ArgumentError if you feed it something lame" do
- expect { @user.private_key Hash.new }.to raise_error(ArgumentError)
- end
- end
-
- describe "when serializing to JSON" do
- before(:each) do
- @user.name("black")
- @user.public_key("crowes")
- @json = @user.to_json
- end
-
- it "serializes as a JSON object" do
- expect(@json).to match(/^\{.+\}$/)
- end
-
- it "includes the name value" do
- expect(@json).to include(%q{"name":"black"})
- end
-
- it "includes the public key value" do
- expect(@json).to include(%{"public_key":"crowes"})
- end
-
- it "includes the 'admin' flag" do
- expect(@json).to include(%q{"admin":false})
- end
-
- it "includes the private key when present" do
- @user.private_key("monkeypants")
- expect(@user.to_json).to include(%q{"private_key":"monkeypants"})
- end
-
- it "does not include the private key if not present" do
- expect(@json).not_to include("private_key")
- end
-
- it "includes the password if present" do
- @user.password "password"
- expect(@user.to_json).to include(%q{"password":"password"})
- end
-
- it "does not include the password if not present" do
- expect(@json).not_to include("password")
- end
-
- include_examples "to_json equivalent to Chef::JSONCompat.to_json" do
- let(:jsonable) { @user }
- end
- end
-
- describe "when deserializing from JSON" do
- before(:each) do
- user = { "name" => "mr_spinks",
- "public_key" => "turtles",
- "private_key" => "pandas",
- "password" => "password",
- "admin" => true }
- @user = Chef::OscUser.from_json(Chef::JSONCompat.to_json(user))
- end
-
- it "should deserialize to a Chef::OscUser object" do
- expect(@user).to be_a_kind_of(Chef::OscUser)
- end
-
- it "preserves the name" do
- expect(@user.name).to eq("mr_spinks")
- end
-
- it "preserves the public key" do
- expect(@user.public_key).to eq("turtles")
- end
-
- it "preserves the admin status" do
- expect(@user.admin).to be_truthy
- end
-
- it "includes the private key if present" do
- expect(@user.private_key).to eq("pandas")
- end
-
- it "includes the password if present" do
- expect(@user.password).to eq("password")
- end
-
- end
-
- describe "API Interactions" do
- before (:each) do
- @user = Chef::OscUser.new
- @user.name "foobar"
- @http_client = double("Chef::REST mock")
- allow(Chef::REST).to receive(:new).and_return(@http_client)
- end
-
- describe "list" do
- before(:each) do
- Chef::Config[:chef_server_url] = "http://www.example.com"
- @osc_response = { "admin" => "http://www.example.com/users/admin"}
- @ohc_response = [ { "user" => { "username" => "admin" }} ]
- allow(Chef::OscUser).to receive(:load).with("admin").and_return(@user)
- @osc_inflated_response = { "admin" => @user }
- end
-
- it "lists all clients on an OSC server" do
- allow(@http_client).to receive(:get_rest).with("users").and_return(@osc_response)
- expect(Chef::OscUser.list).to eq(@osc_response)
- end
-
- it "inflate all clients on an OSC server" do
- allow(@http_client).to receive(:get_rest).with("users").and_return(@osc_response)
- expect(Chef::OscUser.list(true)).to eq(@osc_inflated_response)
- end
-
- it "lists all clients on an OHC/OPC server" do
- allow(@http_client).to receive(:get_rest).with("users").and_return(@ohc_response)
- # We expect that Chef::OscUser.list will give a consistent response
- # so OHC API responses should be transformed to OSC-style output.
- expect(Chef::OscUser.list).to eq(@osc_response)
- end
-
- it "inflate all clients on an OHC/OPC server" do
- allow(@http_client).to receive(:get_rest).with("users").and_return(@ohc_response)
- expect(Chef::OscUser.list(true)).to eq(@osc_inflated_response)
- end
- end
-
- describe "create" do
- it "creates a new user via the API" do
- @user.password "password"
- expect(@http_client).to receive(:post_rest).with("users", {:name => "foobar", :admin => false, :password => "password"}).and_return({})
- @user.create
- end
- end
-
- describe "read" do
- it "loads a named user from the API" do
- expect(@http_client).to receive(:get_rest).with("users/foobar").and_return({"name" => "foobar", "admin" => true, "public_key" => "pubkey"})
- user = Chef::OscUser.load("foobar")
- expect(user.name).to eq("foobar")
- expect(user.admin).to eq(true)
- expect(user.public_key).to eq("pubkey")
- end
- end
-
- describe "update" do
- it "updates an existing user on via the API" do
- expect(@http_client).to receive(:put_rest).with("users/foobar", {:name => "foobar", :admin => false}).and_return({})
- @user.update
- end
- end
-
- describe "destroy" do
- it "deletes the specified user via the API" do
- expect(@http_client).to receive(:delete_rest).with("users/foobar")
- @user.destroy
- end
- end
- end
-end
diff --git a/spec/unit/user_spec.rb b/spec/unit/user_spec.rb
index 5222c951b3..97cc32eb3e 100644
--- a/spec/unit/user_spec.rb
+++ b/spec/unit/user_spec.rb
@@ -16,6 +16,11 @@
# limitations under the License.
#
+# DEPRECATION NOTE
+# This code only remains to support users still operating with
+# Open Source Chef Server 11 and should be removed once support
+# for OSC 11 ends. New development should occur in user_spec.rb.
+
require 'spec_helper'
require 'chef/user'
@@ -26,141 +31,98 @@ describe Chef::User do
@user = Chef::User.new
end
- shared_examples_for "string fields with no contraints" do
- it "should let you set the public key" do
- expect(@user.send(method, "some_string")).to eq("some_string")
- end
-
- it "should return the current public key" do
- @user.send(method, "some_string")
- expect(@user.send(method)).to eq("some_string")
- end
-
- it "should throw an ArgumentError if you feed it something lame" do
- expect { @user.send(method, Hash.new) }.to raise_error(ArgumentError)
- end
- end
-
- shared_examples_for "boolean fields with no constraints" do
- it "should let you set the field" do
- expect(@user.send(method, true)).to eq(true)
- end
-
- it "should return the current field value" do
- @user.send(method, true)
- expect(@user.send(method)).to eq(true)
- end
-
- it "should return the false value when false" do
- @user.send(method, false)
- expect(@user.send(method)).to eq(false)
- end
-
- it "should throw an ArgumentError if you feed it anything but true or false" do
- expect { @user.send(method, Hash.new) }.to raise_error(ArgumentError)
- end
- end
-
describe "initialize" do
it "should be a Chef::User" do
expect(@user).to be_a_kind_of(Chef::User)
end
end
- describe "username" do
- it "should let you set the username to a string" do
- expect(@user.username("ops_master")).to eq("ops_master")
+ describe "name" do
+ it "should let you set the name to a string" do
+ expect(@user.name("ops_master")).to eq("ops_master")
end
- it "should return the current username" do
- @user.username "ops_master"
- expect(@user.username).to eq("ops_master")
+ it "should return the current name" do
+ @user.name "ops_master"
+ expect(@user.name).to eq("ops_master")
end
# It is not feasible to check all invalid characters. Here are a few
# that we probably care about.
it "should not accept invalid characters" do
# capital letters
- expect { @user.username "Bar" }.to raise_error(ArgumentError)
+ expect { @user.name "Bar" }.to raise_error(ArgumentError)
# slashes
- expect { @user.username "foo/bar" }.to raise_error(ArgumentError)
+ expect { @user.name "foo/bar" }.to raise_error(ArgumentError)
# ?
- expect { @user.username "foo?" }.to raise_error(ArgumentError)
+ expect { @user.name "foo?" }.to raise_error(ArgumentError)
# &
- expect { @user.username "foo&" }.to raise_error(ArgumentError)
+ expect { @user.name "foo&" }.to raise_error(ArgumentError)
end
it "should not accept spaces" do
- expect { @user.username "ops master" }.to raise_error(ArgumentError)
+ expect { @user.name "ops master" }.to raise_error(ArgumentError)
end
it "should throw an ArgumentError if you feed it anything but a string" do
- expect { @user.username Hash.new }.to raise_error(ArgumentError)
+ expect { @user.name Hash.new }.to raise_error(ArgumentError)
end
end
- describe "boolean fields" do
- describe "create_key" do
- it_should_behave_like "boolean fields with no constraints" do
- let(:method) { :create_key }
- end
+ describe "admin" do
+ it "should let you set the admin bit" do
+ expect(@user.admin(true)).to eq(true)
end
- end
- describe "string fields" do
- describe "public_key" do
- it_should_behave_like "string fields with no contraints" do
- let(:method) { :public_key }
- end
+ it "should return the current admin value" do
+ @user.admin true
+ expect(@user.admin).to eq(true)
end
- describe "private_key" do
- it_should_behave_like "string fields with no contraints" do
- let(:method) { :private_key }
- end
+ it "should default to false" do
+ expect(@user.admin).to eq(false)
end
- describe "display_name" do
- it_should_behave_like "string fields with no contraints" do
- let(:method) { :display_name }
- end
+ it "should throw an ArgumentError if you feed it anything but true or false" do
+ expect { @user.name Hash.new }.to raise_error(ArgumentError)
end
+ end
- describe "first_name" do
- it_should_behave_like "string fields with no contraints" do
- let(:method) { :first_name }
- end
+ describe "public_key" do
+ it "should let you set the public key" do
+ expect(@user.public_key("super public")).to eq("super public")
end
- describe "middle_name" do
- it_should_behave_like "string fields with no contraints" do
- let(:method) { :middle_name }
- end
+ it "should return the current public key" do
+ @user.public_key("super public")
+ expect(@user.public_key).to eq("super public")
end
- describe "last_name" do
- it_should_behave_like "string fields with no contraints" do
- let(:method) { :last_name }
- end
+ it "should throw an ArgumentError if you feed it something lame" do
+ expect { @user.public_key Hash.new }.to raise_error(ArgumentError)
end
+ end
- describe "email" do
- it_should_behave_like "string fields with no contraints" do
- let(:method) { :email }
- end
+ describe "private_key" do
+ it "should let you set the private key" do
+ expect(@user.private_key("super private")).to eq("super private")
end
- describe "password" do
- it_should_behave_like "string fields with no contraints" do
- let(:method) { :password }
- end
+ it "should return the private key" do
+ @user.private_key("super private")
+ expect(@user.private_key).to eq("super private")
+ end
+
+ it "should throw an ArgumentError if you feed it something lame" do
+ expect { @user.private_key Hash.new }.to raise_error(ArgumentError)
end
end
describe "when serializing to JSON" do
before(:each) do
- @user.username("black")
+ @user.name("black")
+ @user.public_key("crowes")
@json = @user.to_json
end
@@ -168,62 +130,16 @@ describe Chef::User do
expect(@json).to match(/^\{.+\}$/)
end
- it "includes the username value" do
- expect(@json).to include(%q{"username":"black"})
- end
-
- it "includes the display name when present" do
- @user.display_name("get_displayed")
- expect(@user.to_json).to include(%{"display_name":"get_displayed"})
- end
-
- it "does not include the display name if not present" do
- expect(@json).not_to include("display_name")
- end
-
- it "includes the first name when present" do
- @user.first_name("char")
- expect(@user.to_json).to include(%{"first_name":"char"})
- end
-
- it "does not include the first name if not present" do
- expect(@json).not_to include("first_name")
- end
-
- it "includes the middle name when present" do
- @user.middle_name("man")
- expect(@user.to_json).to include(%{"middle_name":"man"})
- end
-
- it "does not include the middle name if not present" do
- expect(@json).not_to include("middle_name")
- end
-
- it "includes the last name when present" do
- @user.last_name("der")
- expect(@user.to_json).to include(%{"last_name":"der"})
- end
-
- it "does not include the last name if not present" do
- expect(@json).not_to include("last_name")
- end
-
- it "includes the email when present" do
- @user.email("charmander@pokemon.poke")
- expect(@user.to_json).to include(%{"email":"charmander@pokemon.poke"})
- end
-
- it "does not include the email if not present" do
- expect(@json).not_to include("email")
+ it "includes the name value" do
+ expect(@json).to include(%q{"name":"black"})
end
- it "includes the public key when present" do
- @user.public_key("crowes")
- expect(@user.to_json).to include(%{"public_key":"crowes"})
+ it "includes the public key value" do
+ expect(@json).to include(%{"public_key":"crowes"})
end
- it "does not include the public key if not present" do
- expect(@json).not_to include("public_key")
+ it "includes the 'admin' flag" do
+ expect(@json).to include(%q{"admin":false})
end
it "includes the private key when present" do
@@ -251,18 +167,11 @@ describe Chef::User do
describe "when deserializing from JSON" do
before(:each) do
- user = {
- "username" => "mr_spinks",
- "display_name" => "displayed",
- "first_name" => "char",
- "middle_name" => "man",
- "last_name" => "der",
- "email" => "charmander@pokemon.poke",
- "password" => "password",
+ user = { "name" => "mr_spinks",
"public_key" => "turtles",
"private_key" => "pandas",
- "create_key" => false
- }
+ "password" => "password",
+ "admin" => true }
@user = Chef::User.from_json(Chef::JSONCompat.to_json(user))
end
@@ -270,277 +179,34 @@ describe Chef::User do
expect(@user).to be_a_kind_of(Chef::User)
end
- it "preserves the username" do
- expect(@user.username).to eq("mr_spinks")
- end
-
- it "preserves the display name if present" do
- expect(@user.display_name).to eq("displayed")
+ it "preserves the name" do
+ expect(@user.name).to eq("mr_spinks")
end
- it "preserves the first name if present" do
- expect(@user.first_name).to eq("char")
- end
-
- it "preserves the middle name if present" do
- expect(@user.middle_name).to eq("man")
+ it "preserves the public key" do
+ expect(@user.public_key).to eq("turtles")
end
- it "preserves the last name if present" do
- expect(@user.last_name).to eq("der")
+ it "preserves the admin status" do
+ expect(@user.admin).to be_truthy
end
- it "preserves the email if present" do
- expect(@user.email).to eq("charmander@pokemon.poke")
+ it "includes the private key if present" do
+ expect(@user.private_key).to eq("pandas")
end
it "includes the password if present" do
expect(@user.password).to eq("password")
end
- it "preserves the public key if present" do
- expect(@user.public_key).to eq("turtles")
- end
-
- it "includes the private key if present" do
- expect(@user.private_key).to eq("pandas")
- end
-
- it "includes the create key status if not nil" do
- expect(@user.create_key).to be_falsey
- end
end
- describe "Versioned API Interactions" do
- let(:response_406) { OpenStruct.new(:code => '406') }
- let(:exception_406) { Net::HTTPServerException.new("406 Not Acceptable", response_406) }
-
- before (:each) do
- @user = Chef::User.new
- allow(@user).to receive(:chef_root_rest_v0).and_return(double('chef rest root v0 object'))
- allow(@user).to receive(:chef_root_rest_v1).and_return(double('chef rest root v1 object'))
- end
-
- describe "update" do
- before do
- # populate all fields that are valid between V0 and V1
- @user.username "some_username"
- @user.display_name "some_display_name"
- @user.first_name "some_first_name"
- @user.middle_name "some_middle_name"
- @user.last_name "some_last_name"
- @user.email "some_email"
- @user.password "some_password"
- end
-
- let(:payload) {
- {
- :username => "some_username",
- :display_name => "some_display_name",
- :first_name => "some_first_name",
- :middle_name => "some_middle_name",
- :last_name => "some_last_name",
- :email => "some_email",
- :password => "some_password"
- }
- }
-
- context "when server API V1 is valid on the Chef Server receiving the request" do
- context "when the user submits valid data" do
- it "properly updates the user" do
- expect(@user.chef_root_rest_v1).to receive(:put).with("users/some_username", payload).and_return({})
- @user.update
- end
- end
- end
-
- context "when server API V1 is not valid on the Chef Server receiving the request" do
- let(:payload) {
- {
- :username => "some_username",
- :display_name => "some_display_name",
- :first_name => "some_first_name",
- :middle_name => "some_middle_name",
- :last_name => "some_last_name",
- :email => "some_email",
- :password => "some_password",
- :public_key => "some_public_key"
- }
- }
-
- before do
- @user.public_key "some_public_key"
- allow(@user.chef_root_rest_v1).to receive(:put)
- end
-
- context "when the server returns a 400" do
- let(:response_400) { OpenStruct.new(:code => '400') }
- let(:exception_400) { Net::HTTPServerException.new("400 Bad Request", response_400) }
-
- context "when the 400 was due to public / private key fields no longer being supported" do
- let(:response_body_400) { '{"error":["Since Server API v1, all keys must be updated via the keys endpoint. "]}' }
-
- before do
- allow(response_400).to receive(:body).and_return(response_body_400)
- allow(@user.chef_root_rest_v1).to receive(:put).and_raise(exception_400)
- end
-
- it "proceeds with the V0 PUT since it can handle public / private key fields" do
- expect(@user.chef_root_rest_v0).to receive(:put).with("users/some_username", payload).and_return({})
- @user.update
- end
-
- it "does not call server_client_api_version_intersection, since we know to proceed with V0 in this case" do
- expect(@user).to_not receive(:server_client_api_version_intersection)
- allow(@user.chef_root_rest_v0).to receive(:put).and_return({})
- @user.update
- end
- end # when the 400 was due to public / private key fields
-
- context "when the 400 was NOT due to public / private key fields no longer being supported" do
- let(:response_body_400) { '{"error":["Some other error. "]}' }
-
- before do
- allow(response_400).to receive(:body).and_return(response_body_400)
- allow(@user.chef_root_rest_v1).to receive(:put).and_raise(exception_400)
- end
-
- it "will not proceed with the V0 PUT since the original bad request was not key related" do
- expect(@user.chef_root_rest_v0).to_not receive(:put).with("users/some_username", payload)
- expect { @user.update }.to raise_error(exception_400)
- end
-
- it "raises the original error" do
- expect { @user.update }.to raise_error(exception_400)
- end
-
- end
- end # when the server returns a 400
-
- context "when the server returns a 406" do
- # from spec/support/shared/unit/api_versioning.rb
- it_should_behave_like "version handling" do
- let(:object) { @user }
- let(:method) { :update }
- let(:http_verb) { :put }
- let(:rest_v1) { @user.chef_root_rest_v1 }
- end
-
- context "when the server supports API V0" do
- before do
- allow(@user).to receive(:server_client_api_version_intersection).and_return([0])
- allow(@user.chef_root_rest_v1).to receive(:put).and_raise(exception_406)
- end
-
- it "properly updates the user" do
- expect(@user.chef_root_rest_v0).to receive(:put).with("users/some_username", payload).and_return({})
- @user.update
- end
- end # when the server supports API V0
- end # when the server returns a 406
-
- end # when server API V1 is not valid on the Chef Server receiving the request
- end # update
-
- describe "create" do
- let(:payload) {
- {
- :username => "some_username",
- :display_name => "some_display_name",
- :first_name => "some_first_name",
- :last_name => "some_last_name",
- :email => "some_email",
- :password => "some_password"
- }
- }
- before do
- @user.username "some_username"
- @user.display_name "some_display_name"
- @user.first_name "some_first_name"
- @user.last_name "some_last_name"
- @user.email "some_email"
- @user.password "some_password"
- end
-
- # from spec/support/shared/unit/user_and_client_shared.rb
- it_should_behave_like "user or client create" do
- let(:object) { @user }
- let(:error) { Chef::Exceptions::InvalidUserAttribute }
- let(:rest_v0) { @user.chef_root_rest_v0 }
- let(:rest_v1) { @user.chef_root_rest_v1 }
- let(:url) { "users" }
- end
-
- context "when handling API V1" do
- it "creates a new user via the API with a middle_name when it exists" do
- @user.middle_name "some_middle_name"
- expect(@user.chef_root_rest_v1).to receive(:post).with("users", payload.merge({:middle_name => "some_middle_name"})).and_return({})
- @user.create
- end
- end # when server API V1 is valid on the Chef Server receiving the request
-
- context "when API V1 is not supported by the server" do
- # from spec/support/shared/unit/api_versioning.rb
- it_should_behave_like "version handling" do
- let(:object) { @user }
- let(:method) { :create }
- let(:http_verb) { :post }
- let(:rest_v1) { @user.chef_root_rest_v1 }
- end
- end
-
- context "when handling API V0" do
- before do
- allow(@user).to receive(:server_client_api_version_intersection).and_return([0])
- allow(@user.chef_root_rest_v1).to receive(:post).and_raise(exception_406)
- end
-
- it "creates a new user via the API with a middle_name when it exists" do
- @user.middle_name "some_middle_name"
- expect(@user.chef_root_rest_v0).to receive(:post).with("users", payload.merge({:middle_name => "some_middle_name"})).and_return({})
- @user.create
- end
- end # when server API V1 is not valid on the Chef Server receiving the request
-
- end # create
-
- # DEPRECATION
- # This can be removed after API V0 support is gone
- describe "reregister" do
- let(:payload) {
- {
- "username" => "some_username",
- }
- }
-
- before do
- @user.username "some_username"
- end
-
- context "when server API V0 is valid on the Chef Server receiving the request" do
- it "creates a new object via the API" do
- expect(@user.chef_root_rest_v0).to receive(:put).with("users/#{@user.username}", payload.merge({"private_key" => true})).and_return({})
- @user.reregister
- end
- end # when server API V0 is valid on the Chef Server receiving the request
-
- context "when server API V0 is not supported by the Chef Server" do
- # from spec/support/shared/unit/api_versioning.rb
- it_should_behave_like "user and client reregister" do
- let(:object) { @user }
- let(:rest_v0) { @user.chef_root_rest_v0 }
- end
- end # when server API V0 is not supported by the Chef Server
- end # reregister
-
- end # Versioned API Interactions
-
describe "API Interactions" do
before (:each) do
@user = Chef::User.new
- @user.username "foobar"
- @http_client = double("Chef::REST mock")
- allow(Chef::REST).to receive(:new).and_return(@http_client)
+ @user.name "foobar"
+ @http_client = double("Chef::ServerAPI mock")
+ allow(Chef::ServerAPI).to receive(:new).and_return(@http_client)
end
describe "list" do
@@ -552,6 +218,16 @@ describe Chef::User do
@osc_inflated_response = { "admin" => @user }
end
+ it "lists all clients on an OSC server" do
+ allow(@http_client).to receive(:get).with("users").and_return(@osc_response)
+ expect(Chef::User.list).to eq(@osc_response)
+ end
+
+ it "inflate all clients on an OSC server" do
+ allow(@http_client).to receive(:get).with("users").and_return(@osc_response)
+ expect(Chef::User.list(true)).to eq(@osc_inflated_response)
+ end
+
it "lists all clients on an OHC/OPC server" do
allow(@http_client).to receive(:get).with("users").and_return(@ohc_response)
# We expect that Chef::User.list will give a consistent response
@@ -565,15 +241,31 @@ describe Chef::User do
end
end
+ describe "create" do
+ it "creates a new user via the API" do
+ @user.password "password"
+ expect(@http_client).to receive(:post).with("users", {:name => "foobar", :admin => false, :password => "password"}).and_return({})
+ @user.create
+ end
+ end
+
describe "read" do
it "loads a named user from the API" do
- expect(@http_client).to receive(:get).with("users/foobar").and_return({"username" => "foobar", "admin" => true, "public_key" => "pubkey"})
+ expect(@http_client).to receive(:get).with("users/foobar").and_return({"name" => "foobar", "admin" => true, "public_key" => "pubkey"})
user = Chef::User.load("foobar")
- expect(user.username).to eq("foobar")
+ expect(user.name).to eq("foobar")
+ expect(user.admin).to eq(true)
expect(user.public_key).to eq("pubkey")
end
end
+ describe "update" do
+ it "updates an existing user on via the API" do
+ expect(@http_client).to receive(:put).with("users/foobar", {:name => "foobar", :admin => false}).and_return({})
+ @user.update
+ end
+ end
+
describe "destroy" do
it "deletes the specified user via the API" do
expect(@http_client).to receive(:delete).with("users/foobar")
diff --git a/spec/unit/user_v1_spec.rb b/spec/unit/user_v1_spec.rb
new file mode 100644
index 0000000000..8fd370a010
--- /dev/null
+++ b/spec/unit/user_v1_spec.rb
@@ -0,0 +1,584 @@
+#
+# Author:: Steven Danna (steve@opscode.com)
+# Copyright:: Copyright (c) 2012 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+
+require 'chef/user_v1'
+require 'tempfile'
+
+describe Chef::UserV1 do
+ before(:each) do
+ @user = Chef::UserV1.new
+ end
+
+ shared_examples_for "string fields with no contraints" do
+ it "should let you set the public key" do
+ expect(@user.send(method, "some_string")).to eq("some_string")
+ end
+
+ it "should return the current public key" do
+ @user.send(method, "some_string")
+ expect(@user.send(method)).to eq("some_string")
+ end
+
+ it "should throw an ArgumentError if you feed it something lame" do
+ expect { @user.send(method, Hash.new) }.to raise_error(ArgumentError)
+ end
+ end
+
+ shared_examples_for "boolean fields with no constraints" do
+ it "should let you set the field" do
+ expect(@user.send(method, true)).to eq(true)
+ end
+
+ it "should return the current field value" do
+ @user.send(method, true)
+ expect(@user.send(method)).to eq(true)
+ end
+
+ it "should return the false value when false" do
+ @user.send(method, false)
+ expect(@user.send(method)).to eq(false)
+ end
+
+ it "should throw an ArgumentError if you feed it anything but true or false" do
+ expect { @user.send(method, Hash.new) }.to raise_error(ArgumentError)
+ end
+ end
+
+ describe "initialize" do
+ it "should be a Chef::UserV1" do
+ expect(@user).to be_a_kind_of(Chef::UserV1)
+ end
+ end
+
+ describe "username" do
+ it "should let you set the username to a string" do
+ expect(@user.username("ops_master")).to eq("ops_master")
+ end
+
+ it "should return the current username" do
+ @user.username "ops_master"
+ expect(@user.username).to eq("ops_master")
+ end
+
+ # It is not feasible to check all invalid characters. Here are a few
+ # that we probably care about.
+ it "should not accept invalid characters" do
+ # capital letters
+ expect { @user.username "Bar" }.to raise_error(ArgumentError)
+ # slashes
+ expect { @user.username "foo/bar" }.to raise_error(ArgumentError)
+ # ?
+ expect { @user.username "foo?" }.to raise_error(ArgumentError)
+ # &
+ expect { @user.username "foo&" }.to raise_error(ArgumentError)
+ end
+
+
+ it "should not accept spaces" do
+ expect { @user.username "ops master" }.to raise_error(ArgumentError)
+ end
+
+ it "should throw an ArgumentError if you feed it anything but a string" do
+ expect { @user.username Hash.new }.to raise_error(ArgumentError)
+ end
+ end
+
+ describe "boolean fields" do
+ describe "create_key" do
+ it_should_behave_like "boolean fields with no constraints" do
+ let(:method) { :create_key }
+ end
+ end
+ end
+
+ describe "string fields" do
+ describe "public_key" do
+ it_should_behave_like "string fields with no contraints" do
+ let(:method) { :public_key }
+ end
+ end
+
+ describe "private_key" do
+ it_should_behave_like "string fields with no contraints" do
+ let(:method) { :private_key }
+ end
+ end
+
+ describe "display_name" do
+ it_should_behave_like "string fields with no contraints" do
+ let(:method) { :display_name }
+ end
+ end
+
+ describe "first_name" do
+ it_should_behave_like "string fields with no contraints" do
+ let(:method) { :first_name }
+ end
+ end
+
+ describe "middle_name" do
+ it_should_behave_like "string fields with no contraints" do
+ let(:method) { :middle_name }
+ end
+ end
+
+ describe "last_name" do
+ it_should_behave_like "string fields with no contraints" do
+ let(:method) { :last_name }
+ end
+ end
+
+ describe "email" do
+ it_should_behave_like "string fields with no contraints" do
+ let(:method) { :email }
+ end
+ end
+
+ describe "password" do
+ it_should_behave_like "string fields with no contraints" do
+ let(:method) { :password }
+ end
+ end
+ end
+
+ describe "when serializing to JSON" do
+ before(:each) do
+ @user.username("black")
+ @json = @user.to_json
+ end
+
+ it "serializes as a JSON object" do
+ expect(@json).to match(/^\{.+\}$/)
+ end
+
+ it "includes the username value" do
+ expect(@json).to include(%q{"username":"black"})
+ end
+
+ it "includes the display name when present" do
+ @user.display_name("get_displayed")
+ expect(@user.to_json).to include(%{"display_name":"get_displayed"})
+ end
+
+ it "does not include the display name if not present" do
+ expect(@json).not_to include("display_name")
+ end
+
+ it "includes the first name when present" do
+ @user.first_name("char")
+ expect(@user.to_json).to include(%{"first_name":"char"})
+ end
+
+ it "does not include the first name if not present" do
+ expect(@json).not_to include("first_name")
+ end
+
+ it "includes the middle name when present" do
+ @user.middle_name("man")
+ expect(@user.to_json).to include(%{"middle_name":"man"})
+ end
+
+ it "does not include the middle name if not present" do
+ expect(@json).not_to include("middle_name")
+ end
+
+ it "includes the last name when present" do
+ @user.last_name("der")
+ expect(@user.to_json).to include(%{"last_name":"der"})
+ end
+
+ it "does not include the last name if not present" do
+ expect(@json).not_to include("last_name")
+ end
+
+ it "includes the email when present" do
+ @user.email("charmander@pokemon.poke")
+ expect(@user.to_json).to include(%{"email":"charmander@pokemon.poke"})
+ end
+
+ it "does not include the email if not present" do
+ expect(@json).not_to include("email")
+ end
+
+ it "includes the public key when present" do
+ @user.public_key("crowes")
+ expect(@user.to_json).to include(%{"public_key":"crowes"})
+ end
+
+ it "does not include the public key if not present" do
+ expect(@json).not_to include("public_key")
+ end
+
+ it "includes the private key when present" do
+ @user.private_key("monkeypants")
+ expect(@user.to_json).to include(%q{"private_key":"monkeypants"})
+ end
+
+ it "does not include the private key if not present" do
+ expect(@json).not_to include("private_key")
+ end
+
+ it "includes the password if present" do
+ @user.password "password"
+ expect(@user.to_json).to include(%q{"password":"password"})
+ end
+
+ it "does not include the password if not present" do
+ expect(@json).not_to include("password")
+ end
+
+ include_examples "to_json equivalent to Chef::JSONCompat.to_json" do
+ let(:jsonable) { @user }
+ end
+ end
+
+ describe "when deserializing from JSON" do
+ before(:each) do
+ user = {
+ "username" => "mr_spinks",
+ "display_name" => "displayed",
+ "first_name" => "char",
+ "middle_name" => "man",
+ "last_name" => "der",
+ "email" => "charmander@pokemon.poke",
+ "password" => "password",
+ "public_key" => "turtles",
+ "private_key" => "pandas",
+ "create_key" => false
+ }
+ @user = Chef::UserV1.from_json(Chef::JSONCompat.to_json(user))
+ end
+
+ it "should deserialize to a Chef::UserV1 object" do
+ expect(@user).to be_a_kind_of(Chef::UserV1)
+ end
+
+ it "preserves the username" do
+ expect(@user.username).to eq("mr_spinks")
+ end
+
+ it "preserves the display name if present" do
+ expect(@user.display_name).to eq("displayed")
+ end
+
+ it "preserves the first name if present" do
+ expect(@user.first_name).to eq("char")
+ end
+
+ it "preserves the middle name if present" do
+ expect(@user.middle_name).to eq("man")
+ end
+
+ it "preserves the last name if present" do
+ expect(@user.last_name).to eq("der")
+ end
+
+ it "preserves the email if present" do
+ expect(@user.email).to eq("charmander@pokemon.poke")
+ end
+
+ it "includes the password if present" do
+ expect(@user.password).to eq("password")
+ end
+
+ it "preserves the public key if present" do
+ expect(@user.public_key).to eq("turtles")
+ end
+
+ it "includes the private key if present" do
+ expect(@user.private_key).to eq("pandas")
+ end
+
+ it "includes the create key status if not nil" do
+ expect(@user.create_key).to be_falsey
+ end
+ end
+
+ describe "Versioned API Interactions" do
+ let(:response_406) { OpenStruct.new(:code => '406') }
+ let(:exception_406) { Net::HTTPServerException.new("406 Not Acceptable", response_406) }
+
+ before (:each) do
+ @user = Chef::UserV1.new
+ allow(@user).to receive(:chef_root_rest_v0).and_return(double('chef rest root v0 object'))
+ allow(@user).to receive(:chef_root_rest_v1).and_return(double('chef rest root v1 object'))
+ end
+
+ describe "update" do
+ before do
+ # populate all fields that are valid between V0 and V1
+ @user.username "some_username"
+ @user.display_name "some_display_name"
+ @user.first_name "some_first_name"
+ @user.middle_name "some_middle_name"
+ @user.last_name "some_last_name"
+ @user.email "some_email"
+ @user.password "some_password"
+ end
+
+ let(:payload) {
+ {
+ :username => "some_username",
+ :display_name => "some_display_name",
+ :first_name => "some_first_name",
+ :middle_name => "some_middle_name",
+ :last_name => "some_last_name",
+ :email => "some_email",
+ :password => "some_password"
+ }
+ }
+
+ context "when server API V1 is valid on the Chef Server receiving the request" do
+ context "when the user submits valid data" do
+ it "properly updates the user" do
+ expect(@user.chef_root_rest_v1).to receive(:put).with("users/some_username", payload).and_return({})
+ @user.update
+ end
+ end
+ end
+
+ context "when server API V1 is not valid on the Chef Server receiving the request" do
+ let(:payload) {
+ {
+ :username => "some_username",
+ :display_name => "some_display_name",
+ :first_name => "some_first_name",
+ :middle_name => "some_middle_name",
+ :last_name => "some_last_name",
+ :email => "some_email",
+ :password => "some_password",
+ :public_key => "some_public_key"
+ }
+ }
+
+ before do
+ @user.public_key "some_public_key"
+ allow(@user.chef_root_rest_v1).to receive(:put)
+ end
+
+ context "when the server returns a 400" do
+ let(:response_400) { OpenStruct.new(:code => '400') }
+ let(:exception_400) { Net::HTTPServerException.new("400 Bad Request", response_400) }
+
+ context "when the 400 was due to public / private key fields no longer being supported" do
+ let(:response_body_400) { '{"error":["Since Server API v1, all keys must be updated via the keys endpoint. "]}' }
+
+ before do
+ allow(response_400).to receive(:body).and_return(response_body_400)
+ allow(@user.chef_root_rest_v1).to receive(:put).and_raise(exception_400)
+ end
+
+ it "proceeds with the V0 PUT since it can handle public / private key fields" do
+ expect(@user.chef_root_rest_v0).to receive(:put).with("users/some_username", payload).and_return({})
+ @user.update
+ end
+
+ it "does not call server_client_api_version_intersection, since we know to proceed with V0 in this case" do
+ expect(@user).to_not receive(:server_client_api_version_intersection)
+ allow(@user.chef_root_rest_v0).to receive(:put).and_return({})
+ @user.update
+ end
+ end # when the 400 was due to public / private key fields
+
+ context "when the 400 was NOT due to public / private key fields no longer being supported" do
+ let(:response_body_400) { '{"error":["Some other error. "]}' }
+
+ before do
+ allow(response_400).to receive(:body).and_return(response_body_400)
+ allow(@user.chef_root_rest_v1).to receive(:put).and_raise(exception_400)
+ end
+
+ it "will not proceed with the V0 PUT since the original bad request was not key related" do
+ expect(@user.chef_root_rest_v0).to_not receive(:put).with("users/some_username", payload)
+ expect { @user.update }.to raise_error(exception_400)
+ end
+
+ it "raises the original error" do
+ expect { @user.update }.to raise_error(exception_400)
+ end
+
+ end
+ end # when the server returns a 400
+
+ context "when the server returns a 406" do
+ # from spec/support/shared/unit/api_versioning.rb
+ it_should_behave_like "version handling" do
+ let(:object) { @user }
+ let(:method) { :update }
+ let(:http_verb) { :put }
+ let(:rest_v1) { @user.chef_root_rest_v1 }
+ end
+
+ context "when the server supports API V0" do
+ before do
+ allow(@user).to receive(:server_client_api_version_intersection).and_return([0])
+ allow(@user.chef_root_rest_v1).to receive(:put).and_raise(exception_406)
+ end
+
+ it "properly updates the user" do
+ expect(@user.chef_root_rest_v0).to receive(:put).with("users/some_username", payload).and_return({})
+ @user.update
+ end
+ end # when the server supports API V0
+ end # when the server returns a 406
+
+ end # when server API V1 is not valid on the Chef Server receiving the request
+ end # update
+
+ describe "create" do
+ let(:payload) {
+ {
+ :username => "some_username",
+ :display_name => "some_display_name",
+ :first_name => "some_first_name",
+ :last_name => "some_last_name",
+ :email => "some_email",
+ :password => "some_password"
+ }
+ }
+ before do
+ @user.username "some_username"
+ @user.display_name "some_display_name"
+ @user.first_name "some_first_name"
+ @user.last_name "some_last_name"
+ @user.email "some_email"
+ @user.password "some_password"
+ end
+
+ # from spec/support/shared/unit/user_and_client_shared.rb
+ it_should_behave_like "user or client create" do
+ let(:object) { @user }
+ let(:error) { Chef::Exceptions::InvalidUserAttribute }
+ let(:rest_v0) { @user.chef_root_rest_v0 }
+ let(:rest_v1) { @user.chef_root_rest_v1 }
+ let(:url) { "users" }
+ end
+
+ context "when handling API V1" do
+ it "creates a new user via the API with a middle_name when it exists" do
+ @user.middle_name "some_middle_name"
+ expect(@user.chef_root_rest_v1).to receive(:post).with("users", payload.merge({:middle_name => "some_middle_name"})).and_return({})
+ @user.create
+ end
+ end # when server API V1 is valid on the Chef Server receiving the request
+
+ context "when API V1 is not supported by the server" do
+ # from spec/support/shared/unit/api_versioning.rb
+ it_should_behave_like "version handling" do
+ let(:object) { @user }
+ let(:method) { :create }
+ let(:http_verb) { :post }
+ let(:rest_v1) { @user.chef_root_rest_v1 }
+ end
+ end
+
+ context "when handling API V0" do
+ before do
+ allow(@user).to receive(:server_client_api_version_intersection).and_return([0])
+ allow(@user.chef_root_rest_v1).to receive(:post).and_raise(exception_406)
+ end
+
+ it "creates a new user via the API with a middle_name when it exists" do
+ @user.middle_name "some_middle_name"
+ expect(@user.chef_root_rest_v0).to receive(:post).with("users", payload.merge({:middle_name => "some_middle_name"})).and_return({})
+ @user.create
+ end
+ end # when server API V1 is not valid on the Chef Server receiving the request
+
+ end # create
+
+ # DEPRECATION
+ # This can be removed after API V0 support is gone
+ describe "reregister" do
+ let(:payload) {
+ {
+ "username" => "some_username",
+ }
+ }
+
+ before do
+ @user.username "some_username"
+ end
+
+ context "when server API V0 is valid on the Chef Server receiving the request" do
+ it "creates a new object via the API" do
+ expect(@user.chef_root_rest_v0).to receive(:put).with("users/#{@user.username}", payload.merge({"private_key" => true})).and_return({})
+ @user.reregister
+ end
+ end # when server API V0 is valid on the Chef Server receiving the request
+
+ context "when server API V0 is not supported by the Chef Server" do
+ # from spec/support/shared/unit/api_versioning.rb
+ it_should_behave_like "user and client reregister" do
+ let(:object) { @user }
+ let(:rest_v0) { @user.chef_root_rest_v0 }
+ end
+ end # when server API V0 is not supported by the Chef Server
+ end # reregister
+
+ end # Versioned API Interactions
+
+ describe "API Interactions" do
+ before (:each) do
+ @user = Chef::UserV1.new
+ @user.username "foobar"
+ @http_client = double("Chef::REST mock")
+ allow(Chef::REST).to receive(:new).and_return(@http_client)
+ end
+
+ describe "list" do
+ before(:each) do
+ Chef::Config[:chef_server_url] = "http://www.example.com"
+ @osc_response = { "admin" => "http://www.example.com/users/admin"}
+ @ohc_response = [ { "user" => { "username" => "admin" }} ]
+ allow(Chef::UserV1).to receive(:load).with("admin").and_return(@user)
+ @osc_inflated_response = { "admin" => @user }
+ end
+
+ it "lists all clients on an OHC/OPC server" do
+ allow(@http_client).to receive(:get).with("users").and_return(@ohc_response)
+ # We expect that Chef::UserV1.list will give a consistent response
+ # so OHC API responses should be transformed to OSC-style output.
+ expect(Chef::UserV1.list).to eq(@osc_response)
+ end
+
+ it "inflate all clients on an OHC/OPC server" do
+ allow(@http_client).to receive(:get).with("users").and_return(@ohc_response)
+ expect(Chef::UserV1.list(true)).to eq(@osc_inflated_response)
+ end
+ end
+
+ describe "read" do
+ it "loads a named user from the API" do
+ expect(@http_client).to receive(:get).with("users/foobar").and_return({"username" => "foobar", "admin" => true, "public_key" => "pubkey"})
+ user = Chef::UserV1.load("foobar")
+ expect(user.username).to eq("foobar")
+ expect(user.public_key).to eq("pubkey")
+ end
+ end
+
+ describe "destroy" do
+ it "deletes the specified user via the API" do
+ expect(@http_client).to receive(:delete).with("users/foobar")
+ @user.destroy
+ end
+ end
+ end
+end