diff options
author | Thom May <thom@chef.io> | 2017-02-01 15:10:25 +0000 |
---|---|---|
committer | Thom May <thom@chef.io> | 2017-02-14 15:41:30 -0800 |
commit | ed3071d2bbd4ca5cafef796b65fc09e564351143 (patch) | |
tree | 2f2b1f59398a07537e7fd2661dea1d328afbc2df /lib | |
parent | 50d0a6b6a0b55d038fceb3b4047b8b52e5fb110a (diff) | |
download | chef-tm/user_refactor.tar.gz |
Make UserV0 and UserV1 versioned api classestm/user_refactor
Signed-off-by: Thom May <thom@chef.io>
Diffstat (limited to 'lib')
-rw-r--r-- | lib/chef/user.rb | 185 | ||||
-rw-r--r-- | lib/chef/user/base.rb | 123 | ||||
-rw-r--r-- | lib/chef/user/v0.rb | 150 | ||||
-rw-r--r-- | lib/chef/user/v1.rb | 186 | ||||
-rw-r--r-- | lib/chef/user_v1.rb | 328 |
5 files changed, 470 insertions, 502 deletions
diff --git a/lib/chef/user.rb b/lib/chef/user.rb index fffaa106f8..3126a15100 100644 --- a/lib/chef/user.rb +++ b/lib/chef/user.rb @@ -1,5 +1,4 @@ # -# Author:: Steven Danna (steve@chef.io) # Copyright:: Copyright 2012-2016, Chef Software, Inc. # License:: Apache License, Version 2.0 # @@ -15,183 +14,21 @@ # 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/server_api" +require "chef/mixin/versioned_api" +require "chef/user/user_v0" +require "chef/user/user_v1" -# TODO -# DEPRECATION NOTE -# This class will be replaced by Chef::UserV1 in Chef 13. It is the code to support the User object -# corresponding to the Open Source Chef Server 11 and only still exists to support -# users still on OSC 11. -# -# Chef::UserV1 now supports Chef Server 12 and will be moved to this namespace in Chef 13. -# -# New development should occur in Chef::UserV1. -# This file and corresponding osc_user knife files -# should be removed once client support for Open Source Chef Server 11 expires. class Chef class User + extend Chef::Mixin::VersionedAPIFactory - include Chef::Mixin::FromFile - include Chef::Mixin::ParamsValidate - - def initialize - @name = "" - @public_key = nil - @private_key = nil - @password = nil - @admin = false - end - - def chef_rest_v0 - @chef_rest_v0 ||= Chef::ServerAPI.new(Chef::Config[:chef_server_url], { :api_version => "0" }) - 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_v0.delete("users/#{@name}") - end - - def create - payload = { :name => name, :admin => admin, :password => password } - payload[:public_key] = public_key if public_key - new_user = chef_rest_v0.post("users", payload) - Chef::User.from_hash(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_v0.put("users/#{name}", payload) - Chef::User.from_hash(to_hash.merge(updated_user)) - end - - def save(new_key = false) - create - rescue Net::HTTPServerException => e - if e.response.code == "409" - update(new_key) - else - raise e - end - end - - def reregister - 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[#{@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.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::User.from_hash(Chef::JSONCompat.from_json(json)) - end - - def self.json_create(json) - Chef.deprecated(:json_auto_inflate, "Auto inflation of JSON data is deprecated. Please use Chef::User#from_json or Chef::User#load.") - Chef::User.from_json(json) - end - - def self.list(inflate = false) - response = Chef::ServerAPI.new(Chef::Config[:chef_server_url], { :api_version => "0" }).get("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::User.load(name) - user_map - end - else - users - end - end - - 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 - - # 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 + add_versioned_api_class Chef::User::V0 + add_versioned_api_class Chef::User::V1 - private_class_method :transform_ohc_list_response + def_versioned_delegator :from_json + def_versioned_delegator :from_hash + def_versioned_delegator :list + def_versioned_delegator :load + def_versioned_delegator :transform_ohc_list_response end end diff --git a/lib/chef/user/base.rb b/lib/chef/user/base.rb new file mode 100644 index 0000000000..e12bda38e3 --- /dev/null +++ b/lib/chef/user/base.rb @@ -0,0 +1,123 @@ +# +# Copyright:: Copyright 2012-2017, 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. +# + +class Chef + class User + class Base + + 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 + @admin = false + end + + def chef_rest + @chef_rest ||= Chef::ServerAPI.new(Chef::Config[:chef_server_root], { :api_version => @minimum_api_version }) + end + + def username(arg = nil) + set_or_return(:username, arg, + :regex => /^[a-z0-9\-_]+$/) + end + alias name username + + def admin(arg = nil) + set_or_return(:admin, + arg, :kind_of => [TrueClass, FalseClass]) + 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 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_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.delete("users/#{@username}") + 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 to_s + "user[#{@username}]" + end + + end + end +end diff --git a/lib/chef/user/v0.rb b/lib/chef/user/v0.rb new file mode 100644 index 0000000000..dbc7d7c83f --- /dev/null +++ b/lib/chef/user/v0.rb @@ -0,0 +1,150 @@ +# +# Author:: Steven Danna (steve@chef.io) +# Copyright:: Copyright 2012-2016, 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/mixin/versioned_api" +require "chef/mash" +require "chef/json_compat" +require "chef/search/query" +require "chef/server_api" +require "chef/user/user_base" + +# TODO +# DEPRECATION NOTE +# This class will be replaced by Chef::UserV1 in Chef 13. It is the code to support the User object +# corresponding to the Open Source Chef Server 11 and only still exists to support +# users still on OSC 11. +# +# Chef::UserV1 now supports Chef Server 12 and will be moved to this namespace in Chef 13. +# +# New development should occur in Chef::UserV1. +# This file and corresponding osc_user knife files +# should be removed once client support for Open Source Chef Server 11 expires. +class Chef + class User + class V0 < Base + + include Chef::Mixin::FromFile + include Chef::Mixin::ParamsValidate + extend Chef::Mixin::VersionedAPI + + minimum_api_version 0 + + def initialize + # Chef.deprecated(:user_v0, "The User API was upgraded, and the old version is scheduled for removal. Please upgrade your Chef Server") + super + end + + def to_hash + result = { + "name" => @username, + "public_key" => @public_key, + "admin" => @admin, + } + result["private_key"] = @private_key if @private_key + result["password"] = @password if @password + result + 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.post("users", payload) + Chef::User::V0.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.put("users/#{name}", payload) + Chef::User::V0.from_hash(self.to_hash.merge(updated_user)) + end + + def reregister + reregistered_self = chef_rest.put("users/#{name}", { :name => name, :admin => admin, :private_key => true }) + private_key(reregistered_self["private_key"]) + self + 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::V0.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::User::V0.from_hash(Chef::JSONCompat.from_json(json)) + end + + def self.json_create(json) + Chef.deprecated(:json_auto_inflate, "Auto inflation of JSON data is deprecated. Please use Chef::User#from_json or Chef::User#load.") + Chef::User::V0.from_json(json) + end + + def self.list(inflate = false) + response = Chef::ServerAPI.new(Chef::Config[:chef_server_url], { :api_version => "0" }).get("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::User::V0.load(name) + user_map + end + else + users + end + end + + def self.load(name) + response = Chef::ServerAPI.new(Chef::Config[:chef_server_url], { :api_version => "0" }).get("users/#{name}") + Chef::User::V0.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 +end diff --git a/lib/chef/user/v1.rb b/lib/chef/user/v1.rb new file mode 100644 index 0000000000..9cd8363e14 --- /dev/null +++ b/lib/chef/user/v1.rb @@ -0,0 +1,186 @@ +# +# Author:: Steven Danna (steve@chef.io) +# Copyright:: Copyright 2012-2016, 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/mixin/versioned_api" +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" +require "chef/user/user_base" + +# 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 User + class V1 < Base + + include Chef::Mixin::FromFile + include Chef::Mixin::ParamsValidate + extend Chef::Mixin::VersionedAPI + + minimum_api_version 1 + + 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 create + 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_rest.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 + + Chef::User::V1.from_hash(self.to_hash.merge(new_user)) + end + + def update(new_key = false) + 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_rest.put("users/#{username}", payload) + Chef::User::V1.from_hash(self.to_hash.merge(updated_user)) + end + + # Note: remove after API v0 no longer supported by client (and knife command). + def reregister + error_msg = reregister_only_v0_supported_error_msg(max_version, min_version) + raise Chef::Exceptions::OnlyApiVersion0SupportedForAction.new(error_msg) + end + + # Class Methods + + def self.from_hash(user_hash) + user = Chef::User::V1.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::User::V1.from_hash(Chef::JSONCompat.from_json(json)) + end + + def self.json_create(json) + Chef.deprecated(:json_auto_inflate, "Auto inflation of JSON data is deprecated. Please use Chef::User::V1#from_json or Chef::User::V1#load.") + Chef::User::V1.from_json(json) + end + + def self.list(inflate = false) + response = Chef::ServerAPI.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::User::V1.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::ServerAPI.new(Chef::Config[:chef_server_url]).get("users/#{username}") + Chef::User::V1.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 +end diff --git a/lib/chef/user_v1.rb b/lib/chef/user_v1.rb deleted file mode 100644 index b408bbe667..0000000000 --- a/lib/chef/user_v1.rb +++ /dev/null @@ -1,328 +0,0 @@ -# -# Author:: Steven Danna (steve@chef.io) -# Copyright:: Copyright 2012-2016, 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/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 - 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 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::ServerAPI.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(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(to_hash.merge(updated_user)) - end - - def save(new_key = false) - create - rescue Net::HTTPServerException => e - if e.response.code == "409" - update(new_key) - else - raise e - end - end - - # Note: remove after API v0 no longer supported by client (and knife command). - def reregister - begin - payload = 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 - - def self.json_create(json) - Chef.deprecated(:json_auto_inflate, "Auto inflation of JSON data is deprecated. Please use Chef::UserV1#from_json or Chef::UserV1#load.") - Chef::UserV1.from_json(json) - end - - def self.list(inflate = false) - response = Chef::ServerAPI.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::ServerAPI.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 |