diff options
author | snehaldwivedi <sdwivedi@msystechnologies.com> | 2020-07-22 01:57:39 -0700 |
---|---|---|
committer | snehaldwivedi <sdwivedi@msystechnologies.com> | 2021-02-16 02:45:12 -0800 |
commit | 1f655be8219c9e20dffd68adc2ff97c29e2c29b3 (patch) | |
tree | 61989f31856029fea9534cc99cc7473bdd5454e5 | |
parent | 3f4ef1b91f7a24d2c533eb4791ae099f8de49f4f (diff) | |
download | chef-1f655be8219c9e20dffd68adc2ff97c29e2c29b3.tar.gz |
Merge this knife plugin into Chef directly
Signed-off-by: snehaldwivedi <sdwivedi@msystechnologies.com>
27 files changed, 1678 insertions, 0 deletions
diff --git a/lib/chef/knife/opc_org_create.rb b/lib/chef/knife/opc_org_create.rb new file mode 100644 index 0000000000..4e016f2630 --- /dev/null +++ b/lib/chef/knife/opc_org_create.rb @@ -0,0 +1,67 @@ +# +# Author:: Steven Danna (<steve@chef.io>) +# Copyright:: Copyright 2011-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. +# + +module Opc + class OpcOrgCreate < Chef::Knife + category "CHEF ORGANIZATION MANAGEMENT" + banner "knife opc org create ORG_SHORT_NAME ORG_FULL_NAME (options)" + + option :filename, + long: "--filename FILENAME", + short: "-f FILENAME", + description: "Write validator private key to FILENAME rather than STDOUT" + + option :association_user, + long: "--association_user USERNAME", + short: "-a USERNAME", + description: "Invite USERNAME to the new organization after creation" + + attr_accessor :org_name, :org_full_name + + deps do + require_relative "../org" + require_relative "../org/group_operations" + end + + def run + @org_name, @org_full_name = @name_args + + if !org_name || !org_full_name + ui.fatal "You must specify an ORG_NAME and an ORG_FULL_NAME" + show_usage + exit 1 + end + + org = Chef::Org.from_hash({ "name" => org_name, + "full_name" => org_full_name }).create + if config[:filename] + File.open(config[:filename], "w") do |f| + f.print(org.private_key) + end + else + ui.msg org.private_key + end + + if config[:association_user] + org.associate_user(config[:association_user]) + org.add_user_to_group("admins", config[:association_user]) + org.add_user_to_group("billing-admins", config[:association_user]) + end + end + end +end diff --git a/lib/chef/knife/opc_org_delete.rb b/lib/chef/knife/opc_org_delete.rb new file mode 100644 index 0000000000..ff30c4b6dc --- /dev/null +++ b/lib/chef/knife/opc_org_delete.rb @@ -0,0 +1,33 @@ +# +# Author:: Steven Danna (<steve@chef.io>) +# Copyright:: Copyright 2011-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_relative "../mixin/root_rest" + +module Opc + class OpcOrgDelete < Chef::Knife + category "CHEF ORGANIZATION MANAGEMENT" + banner "knife opc org delete ORG_NAME" + + include Chef::Mixin::RootRestv0 + + def run + org_name = @name_args[0] + ui.confirm "Do you want to delete the organization #{org_name}" + ui.output root_rest.delete("organizations/#{org_name}") + end + end +end diff --git a/lib/chef/knife/opc_org_edit.rb b/lib/chef/knife/opc_org_edit.rb new file mode 100644 index 0000000000..202369707d --- /dev/null +++ b/lib/chef/knife/opc_org_edit.rb @@ -0,0 +1,49 @@ +# +# Author:: Steven Danna (<steve@chef.io>) +# Copyright:: Copyright 2011-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_relative "../mixin/root_rest" + +module Opc + class OpcOrgEdit < Chef::Knife + category "CHEF ORGANIZATION MANAGEMENT" + banner "knife opc org edit ORG" + + def run + org_name = @name_args[0] + + if org_name.nil? + show_usage + ui.fatal("You must specify an organization name") + exit 1 + end + + include Chef::Mixin::RootRestv0 + + original_org = root_rest.get("organizations/#{org_name}") + edited_org = edit_data(original_org) + + if original_org == edited_org + ui.msg("Organization unchanged, not saving.") + exit + end + + ui.msg edited_org + root_rest.put("organizations/#{org_name}", edited_org) + ui.msg("Saved #{org_name}.") + end + end +end diff --git a/lib/chef/knife/opc_org_list.rb b/lib/chef/knife/opc_org_list.rb new file mode 100644 index 0000000000..2220b3e737 --- /dev/null +++ b/lib/chef/knife/opc_org_list.rb @@ -0,0 +1,45 @@ +# +# Author:: Steven Danna (<steve@chef.io>) +# Copyright:: Copyright 2011-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_relative "../mixin/root_rest" + +module Opc + class OpcOrgList < Chef::Knife + category "CHEF ORGANIZATION MANAGEMENT" + banner "knife opc org list" + + option :with_uri, + long: "--with-uri", + short: "-w", + description: "Show corresponding URIs" + + option :all_orgs, + long: "--all-orgs", + short: "-a", + description: "Show auto-generated hidden orgs in output" + + include Chef::Mixin::RootRestv0 + + def run + results = root_rest.get("organizations") + unless config[:all_orgs] + results = results.select { |k, v| !(k.length == 20 && k =~ /^[a-z]+$/) } + end + ui.output(ui.format_list_for_display(results)) + end + end +end diff --git a/lib/chef/knife/opc_org_show.rb b/lib/chef/knife/opc_org_show.rb new file mode 100644 index 0000000000..ee9c0c6bb3 --- /dev/null +++ b/lib/chef/knife/opc_org_show.rb @@ -0,0 +1,32 @@ +# +# Author:: Steven Danna (<steve@chef.io>) +# Copyright:: Copyright 2011-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_relative "../mixin/root_rest" + +module Opc + class OpcOrgShow < Chef::Knife + category "CHEF ORGANIZATION MANAGEMENT" + banner "knife opc org show ORGNAME" + + include Chef::Mixin::RootRestv0 + + def run + org_name = @name_args[0] + ui.output root_rest.get("organizations/#{org_name}") + end + end +end diff --git a/lib/chef/knife/opc_org_user_add.rb b/lib/chef/knife/opc_org_user_add.rb new file mode 100644 index 0000000000..2209fdd4f9 --- /dev/null +++ b/lib/chef/knife/opc_org_user_add.rb @@ -0,0 +1,61 @@ +# +# Author:: Marc Paradise (<marc@chef.io>) +# Copyright:: Copyright 2014-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. +# + +module Opc + class OpcOrgUserAdd < Chef::Knife + category "CHEF ORGANIZATION MANAGEMENT" + banner "knife opc org user add ORG_NAME USER_NAME" + attr_accessor :org_name, :username + + option :admin, + long: "--admin", + short: "-a", + description: "Add user to admin group" + + deps do + require_relative "../org" + require_relative "../org/group_operations" + end + + def run + @org_name, @username = @name_args + + if !org_name || !username + ui.fatal "You must specify an ORG_NAME and USER_NAME" + show_usage + exit 1 + end + + org = Chef::Org.new(@org_name) + begin + org.associate_user(@username) + rescue Net::HTTPServerException => e + if e.response.code == "409" + ui.msg "User #{username} already associated with organization #{org_name}" + else + raise e + end + end + if config[:admin] + org.add_user_to_group("admins", @username) + org.add_user_to_group("billing-admins", @username) + ui.msg "User #{username} is added to admins and billing-admins group" + end + end + end +end diff --git a/lib/chef/knife/opc_org_user_remove.rb b/lib/chef/knife/opc_org_user_remove.rb new file mode 100644 index 0000000000..fd3c518e9c --- /dev/null +++ b/lib/chef/knife/opc_org_user_remove.rb @@ -0,0 +1,103 @@ +# +# Author:: Marc Paradise (<marc@getchef.com>) +# Copyright:: Copyright 2014-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. +# + +module Opc + class OpcOrgUserRemove < Chef::Knife + category "CHEF ORGANIZATION MANAGEMENT" + banner "knife opc org user remove ORG_NAME USER_NAME" + attr_accessor :org_name, :username + + option :force_remove_from_admins, + long: "--force", + short: "-f", + description: "Force removal of user from the organization's admins and billing-admins group." + + deps do + require_relative "../org" + require_relative "../org/group_operations" + require "chef/json_compat" + end + + def run + @org_name, @username = @name_args + + if !org_name || !username + ui.fatal "You must specify an ORG_NAME and USER_NAME" + show_usage + exit 1 + end + + org = Chef::Org.new(@org_name) + + if config[:force_remove_from_admins] + if org.actor_delete_would_leave_admins_empty? + failure_error_message(org_name, username) + ui.msg <<~EOF + You ran with --force which force removes the user from the admins and billing-admins groups. + However, removing #{username} from the admins group would leave it empty, which breaks the org. + Please add another user to org #{org_name} admins group and try again. + EOF + exit 1 + end + remove_user_from_admin_group(org, org_name, username, "admins") + remove_user_from_admin_group(org, org_name, username, "billing-admins") + end + + begin + org.dissociate_user(@username) + rescue Net::HTTPServerException => e + if e.response.code == "404" + ui.msg "User #{username} is not associated with organization #{org_name}" + exit 1 + elsif e.response.code == "403" + body = Chef::JSONCompat.from_json(e.response.body) + if body.key?("error") && body["error"] == "Please remove #{username} from this organization's admins group before removing him or her from the organization." + failure_error_message(org_name, username) + ui.msg <<~EOF + User #{username} is in the organization's admin group. Removing users from an organization without removing them from the admins group is not allowed. + Re-run this command with --force to remove this user from the admins prior to removing it from the organization. + EOF + exit 1 + else + raise e + end + else + raise e + end + end + end + + def failure_error_message(org_name, username) + ui.error "Error removing user #{username} from organization #{org_name}." + end + + def remove_user_from_admin_group(org, org_name, username, admin_group_string) + org.remove_user_from_group(admin_group_string, username) + rescue Net::HTTPServerException => e + if e.response.code == "404" + ui.warn <<~EOF + User #{username} is not in the #{admin_group_string} group for organization #{org_name}. + You probably don't need to pass --force. + EOF + else + raise e + end + end + + end +end diff --git a/lib/chef/knife/opc_user_create.rb b/lib/chef/knife/opc_user_create.rb new file mode 100644 index 0000000000..48fd4eba8f --- /dev/null +++ b/lib/chef/knife/opc_user_create.rb @@ -0,0 +1,101 @@ +# +# Author:: Steven Danna (<steve@chef.io>) +# Copyright:: Copyright 2011-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_relative "../mixin/root_rest" + +module Opc + class OpcUserCreate < Chef::Knife + category "CHEF ORGANIZATION MANAGEMENT" + banner "knife opc user create USERNAME FIRST_NAME [MIDDLE_NAME] LAST_NAME EMAIL PASSWORD" + + option :filename, + long: "--filename FILENAME", + short: "-f FILENAME", + description: "Write private key to FILENAME rather than STDOUT" + + option :orgname, + long: "--orgname ORGNAME", + short: "-o ORGNAME", + description: "Associate new user to an organization matching ORGNAME" + + option :passwordprompt, + long: "--prompt-for-password", + short: "-p", + description: "Prompt for user password" + + include Chef::Mixin::RootRestv0 + + def run + case @name_args.count + when 6 + username, first_name, middle_name, last_name, email, password = @name_args + when 5 + username, first_name, last_name, email, password = @name_args + when 4 + username, first_name, last_name, email = @name_args + else + ui.fatal "Wrong number of arguments" + show_usage + exit 1 + end + password = prompt_for_password if config[:passwordprompt] + unless password + ui.fatal "You must either provide a password or use the --prompt-for-password (-p) option" + exit 1 + end + middle_name ||= "" + + user_hash = { + username: username, + first_name: first_name, + middle_name: middle_name, + last_name: last_name, + display_name: "#{first_name} #{last_name}", + email: email, + password: password, + } + + # Check the file before creating the user so the api is more transactional. + if config[:filename] + file = config[:filename] + unless File.exist?(file) ? File.writable?(file) : File.writable?(File.dirname(file)) + ui.fatal "File #{config[:filename]} is not writable. Check permissions." + exit 1 + end + end + + result = root_rest.post("users/", user_hash) + if config[:filename] + File.open(config[:filename], "w") do |f| + f.print(result["private_key"]) + end + else + ui.msg result["private_key"] + end + if config[:orgname] + request_body = { user: username } + response = root_rest.post("organizations/#{config[:orgname]}/association_requests", request_body) + association_id = response["uri"].split("/").last + root_rest.put("users/#{username}/association_requests/#{association_id}", { response: "accept" }) + end + end + + def prompt_for_password + ui.ask("Please enter the user's password: ") { |q| q.echo = false } + end + end +end diff --git a/lib/chef/knife/opc_user_delete.rb b/lib/chef/knife/opc_user_delete.rb new file mode 100644 index 0000000000..b749b8a5d4 --- /dev/null +++ b/lib/chef/knife/opc_user_delete.rb @@ -0,0 +1,144 @@ +# +# Author:: Steven Danna (<steve@chef.io>) +# Copyright:: Copyright 2011-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_relative "../mixin/root_rest" + +module Opc + class OpcUserDelete < Chef::Knife + category "CHEF ORGANIZATION MANAGEMENT" + banner "knife opc user delete USERNAME [-d] [-R]" + + option :no_disassociate_user, + long: "--no-disassociate-user", + short: "-d", + description: "Don't disassociate the user first" + + option :remove_from_admin_groups, + long: "--remove-from-admin-groups", + short: "-R", + description: "If the user is a member of any org admin groups, attempt to remove from those groups. Ignored if --no-disassociate-user is set." + + attr_reader :username + include Chef::Mixin::RootRestv0 + + deps do + require_relative "../org" + require_relative "../org/group_operations" + end + + def run + @username = @name_args[0] + admin_memberships = [] + unremovable_memberships = [] + + ui.confirm "Do you want to delete the user #{username}" + + unless config[:no_disassociate_user] + ui.stderr.puts("Checking organization memberships...") + orgs = org_memberships(username) + if orgs.length > 0 + ui.stderr.puts("Checking admin group memberships for #{orgs.length} org(s).") + admin_memberships, unremovable_memberships = admin_group_memberships(orgs, username) + end + + unless admin_memberships.empty? + unless config[:remove_from_admin_groups] + error_exit_admin_group_member!(username, admin_memberships) + end + + unless unremovable_memberships.empty? + error_exit_cant_remove_admin_membership!(username, unremovable_memberships) + end + remove_from_admin_groups(admin_memberships, username) + end + disassociate_user(orgs, username) + end + + delete_user(username) + end + + def disassociate_user(orgs, username) + orgs.each { |org| org.dissociate_user(username) } + end + + def org_memberships(username) + org_data = root_rest.get("users/#{username}/organizations") + org_data.map { |org| Chef::Org.new(org["organization"]["name"]) } + end + + def remove_from_admin_groups(admin_of, username) + admin_of.each do |org| + ui.stderr.puts "Removing #{username} from admins group of '#{org.name}'" + org.remove_user_from_group("admins", username) + end + end + + def admin_group_memberships(orgs, username) + admin_of = [] + unremovable = [] + orgs.each do |org| + if org.user_member_of_group?(username, "admins") + admin_of << org + if org.actor_delete_would_leave_admins_empty? + unremovable << org + end + end + end + [admin_of, unremovable] + end + + def delete_user(username) + ui.stderr.puts "Deleting user #{username}." + root_rest.delete("users/#{username}") + end + + # Error message that says how to removed from org + # admin groups before deleting + # Further + def error_exit_admin_group_member!(username, admin_of) + message = "#{username} is in the 'admins' group of the following organization(s):\n\n" + admin_of.each { |org| message << "- #{org.name}\n" } + message << <<~EOM + + Run this command again with the --remove-from-admin-groups option to + remove the user from these admin group(s) automatically. + + EOM + ui.fatal message + exit 1 + end + + def error_exit_cant_remove_admin_membership!(username, only_admin_of) + message = <<~EOM + + #{username} is the only member of the 'admins' group of the + following organization(s): + + EOM + only_admin_of.each { |org| message << "- #{org.name}\n" } + message << <<~EOM + + Removing the only administrator of an organization can break it. + Assign additional users or groups to the admin group(s) before + deleting this user. + + EOM + ui.fatal message + exit 1 + end + end +end diff --git a/lib/chef/knife/opc_user_edit.rb b/lib/chef/knife/opc_user_edit.rb new file mode 100644 index 0000000000..26118bce9f --- /dev/null +++ b/lib/chef/knife/opc_user_edit.rb @@ -0,0 +1,69 @@ +# +# Author:: Steven Danna (<steve@chef.io>) +# Copyright:: Copyright 2011-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_relative "../mixin/root_rest" + +module Opc + class OpcUserEdit < Chef::Knife + category "CHEF ORGANIZATION MANAGEMENT" + banner "knife opc user edit USERNAME" + + option :input, + long: "--input FILENAME", + short: "-i FILENAME", + description: "Name of file to use for PUT or POST" + + option :filename, + long: "--filename FILENAME", + short: "-f FILENAME", + description: "Write private key to FILENAME rather than STDOUT" + + include Chef::Mixin::RootRestv0 + + def run + user_name = @name_args[0] + + if user_name.nil? + show_usage + ui.fatal("You must specify a user name") + exit 1 + end + + original_user = root_rest.get("users/#{user_name}") + if config[:input] + edited_user = JSON.parse(IO.read(config[:input])) + else + edited_user = edit_data(original_user) + end + if original_user != edited_user + result = root_rest.put("users/#{user_name}", edited_user) + ui.msg("Saved #{user_name}.") + unless result["private_key"].nil? + if config[:filename] + File.open(config[:filename], "w") do |f| + f.print(result["private_key"]) + end + else + ui.msg result["private_key"] + end + end + else + ui.msg("User unchanged, not saving.") + end + end + end +end diff --git a/lib/chef/knife/opc_user_list.rb b/lib/chef/knife/opc_user_list.rb new file mode 100644 index 0000000000..33402175ec --- /dev/null +++ b/lib/chef/knife/opc_user_list.rb @@ -0,0 +1,37 @@ +# +# Author:: Steven Danna (<steve@chef.io>) +# Copyright:: Copyright 2011-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_relative "../mixin/root_rest" + +module Opc + class OpcUserList < Chef::Knife + category "CHEF ORGANIZATION MANAGEMENT" + banner "knife opc user list" + + option :with_uri, + long: "--with-uri", + short: "-w", + description: "Show corresponding URIs" + + include Chef::Mixin::RootRestv0 + + def run + results = root_rest.get("users") + ui.output(ui.format_list_for_display(results)) + end + end +end diff --git a/lib/chef/knife/opc_user_password.rb b/lib/chef/knife/opc_user_password.rb new file mode 100644 index 0000000000..b9b087324e --- /dev/null +++ b/lib/chef/knife/opc_user_password.rb @@ -0,0 +1,72 @@ +# +# Author:: Tyler Cloke (<tyler@getchef.com>) +# Copyright:: Copyright 2014-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_relative "../mixin/root_rest" + +module Opc + class OpcUserPassword < Chef::Knife + category "CHEF ORGANIZATION MANAGEMENT" + banner "knife opc user password USERNAME [PASSWORD | --enable-external-auth]" + + option :enable_external_auth, + long: "--enable-external-auth", + short: "-e", + description: "Enable external authentication for this user (such as LDAP)" + + include Chef::Mixin::RootRestv0 + + def run + # check that correct number of args was passed, should be either + # USERNAME PASSWORD or USERNAME --enable-external-auth + # + # note that you can't pass USERNAME PASSWORD --enable-external-auth + unless (@name_args.length == 2 && !config[:enable_external_auth]) || (@name_args.length == 1 && config[:enable_external_auth]) + show_usage + ui.fatal("You must pass two arguments") + ui.fatal("Note that --enable-external-auth cannot be passed with a password") + exit 1 + end + + user_name = @name_args[0] + + # note that this will be nil if config[:enable_external_auth] is true + password = @name_args[1] + + # since the API does not pass back whether recovery_authentication_enabled is + # true or false, there is no way of knowing if the user is using ldap or not, + # so we will update the user every time, instead of checking if we are actually + # changing anything before we PUT. + user = root_rest.get("users/#{user_name}") + + user["password"] = password unless password.nil? + + # if --enable-external-auth was passed, enable it, else disable it. + # there is never a situation where we would want to enable ldap + # AND change the password. changing the password means that the user + # wants to disable ldap and put user in recover (if they are using ldap). + user["recovery_authentication_enabled"] = !config[:enable_external_auth] + + begin + root_rest.put("users/#{user_name}", user) + rescue => e + raise e + end + + ui.msg("Authentication info updated for #{user_name}.") + end + end +end diff --git a/lib/chef/knife/opc_user_show.rb b/lib/chef/knife/opc_user_show.rb new file mode 100644 index 0000000000..26b3b04474 --- /dev/null +++ b/lib/chef/knife/opc_user_show.rb @@ -0,0 +1,41 @@ +# +# Author:: Steven Danna (<steve@chef.io>) +# Copyright:: Copyright 2011-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_relative "../mixin/root_rest" + +module Opc + class OpcUserShow < Chef::Knife + category "CHEF ORGANIZATION MANAGEMENT" + banner "knife opc user show USERNAME" + + option :with_orgs, + long: "--with-orgs", + short: "-l" + + include Chef::Mixin::RootRestv0 + + def run + user_name = @name_args[0] + results = root_rest.get("users/#{user_name}") + if config[:with_orgs] + orgs = root_rest.get("users/#{user_name}/organizations") + results["organizations"] = orgs.map { |o| o["organization"]["name"] } + end + ui.output results + end + end +end diff --git a/lib/chef/mixin/root_rest.rb b/lib/chef/mixin/root_rest.rb new file mode 100644 index 0000000000..967213a16c --- /dev/null +++ b/lib/chef/mixin/root_rest.rb @@ -0,0 +1,31 @@ +# +# Author:: Steven Danna (<steve@chef.io>) +# Copyright:: Copyright 2011-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/server_api" +class Chef + module Mixin + module RootRestv0 + def root_rest + # Use v0 API for now + # Rather than upgrade all of this code to move to v1, the goal is to remove the + # need for this plugin. See + # https://github.com/chef/chef/issues/3517 + @root_rest ||= Chef::ServerAPI.new(Chef::Config[:chef_server_root], { api_version: "0" }) + end + end + end +end diff --git a/lib/chef/org/group_operations.rb b/lib/chef/org/group_operations.rb new file mode 100644 index 0000000000..fd75e50f41 --- /dev/null +++ b/lib/chef/org/group_operations.rb @@ -0,0 +1,60 @@ +require_relative "../org" + +class Chef + class Org + module GroupOperations + def group(groupname) + @group ||= {} + @group[groupname] ||= chef_rest.get_rest "organizations/#{name}/groups/#{groupname}" + end + + def user_member_of_group?(username, groupname) + group = group(groupname) + group["actors"].include? username + end + + def add_user_to_group(groupname, username) + group = group(groupname) + body_hash = { + groupname: "#{groupname}", + actors: { + "users" => group["actors"].concat([username]), + "groups" => group["groups"], + }, + } + chef_rest.put_rest "organizations/#{name}/groups/#{groupname}", body_hash + end + + def remove_user_from_group(groupname, username) + group = group(groupname) + group["actors"].delete(username) + body_hash = { + groupname: "#{groupname}", + actors: { + "users" => group["actors"], + "groups" => group["groups"], + }, + } + chef_rest.put_rest "organizations/#{name}/groups/#{groupname}", body_hash + end + + def actor_delete_would_leave_admins_empty? + admins = group("admins") + if admins["groups"].empty? + # exclude 'pivotal' but don't mutate the group since we're caching it + if admins["actors"].include? "pivotal" + admins["actors"].length <= 2 + else + admins["actors"].length <= 1 + end + else + # We don't check recursively. If the admins group contains a group, + # and the user is the only member of that group, + # we'll still turn up a 'safe to delete'. + false + end + end + end + include Chef::Org::GroupOperations + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 5d49d54cd3..c1507e3203 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -331,5 +331,9 @@ module WEBrick end end +class Chef::Knife + include Opc +end + # Enough stuff needs json serialization that I'm just adding it here for equality asserts require "chef/json_compat" diff --git a/spec/unit/knife/opc_org_create_spec.rb b/spec/unit/knife/opc_org_create_spec.rb new file mode 100644 index 0000000000..5d61b5ed2b --- /dev/null +++ b/spec/unit/knife/opc_org_create_spec.rb @@ -0,0 +1,61 @@ +require "spec_helper" +require "chef/org" +require "chef/org/group_operations" +require "chef/knife/opc_org_create" + +describe Opc::OpcOrgCreate do + before :each do + Chef::Knife::OpcOrgCreate.load_deps + @knife = Chef::Knife::OpcOrgCreate.new + @org = double("Chef::Org") + allow(Chef::Org).to receive(:new).and_return(@org) + @key = "You don't come into cooking to get rich - Ramsay" + allow(@org).to receive(:private_key).and_return(@key) + @org_name = "ss" + @org_full_name = "secretsauce" + end + + let(:org_args) do + { + name: @org_name, + full_name: @org_full_name, + } + end + + describe "with no org_name and org_fullname" do + it "fails with an informative message" do + expect(@knife.ui).to receive(:fatal).with("You must specify an ORG_NAME and an ORG_FULL_NAME") + expect(@knife).to receive(:show_usage) + expect { @knife.run }.to raise_error(SystemExit) + end + end + + describe "with org_name and org_fullname" do + before :each do + @knife.name_args << @org_name << @org_full_name + end + + it "creates an org" do + expect(@org).to receive(:create).and_return(@org) + expect(@org).to receive(:full_name).with("secretsauce") + expect(@knife.ui).to receive(:msg).with(@key) + @knife.run + end + + context "with --assocation-user" do + before :each do + @knife.config[:association_user] = "ramsay" + end + + it "creates an org, associates a user, and adds it to the admins group" do + expect(@org).to receive(:full_name).with("secretsauce") + expect(@org).to receive(:create).and_return(@org) + expect(@org).to receive(:associate_user).with("ramsay") + expect(@org).to receive(:add_user_to_group).with("admins", "ramsay") + expect(@org).to receive(:add_user_to_group).with("billing-admins", "ramsay") + expect(@knife.ui).to receive(:msg).with(@key) + @knife.run + end + end + end +end diff --git a/spec/unit/knife/opc_org_delete_spec.rb b/spec/unit/knife/opc_org_delete_spec.rb new file mode 100644 index 0000000000..bac082790b --- /dev/null +++ b/spec/unit/knife/opc_org_delete_spec.rb @@ -0,0 +1,42 @@ +# +# Author:: Snehal Dwivedi (<sdwivedi@msystechnologies.com>) +# Copyright:: Copyright (c) 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 "spec_helper" + +describe Chef::Knife::OpcOrgDelete do + + before :each do + @knife = Chef::Knife::OpcOrgDelete.new + @org_name = "foobar" + @knife.name_args << @org_name + end + + let(:rest) do + Chef::Config[:chef_server_root] = "http://www.example.com" + root_rest = double("rest") + allow(Chef::ServerAPI).to receive(:new).and_return(root_rest) + end + + it "should confirm that you want to delete and then delete organizations" do + allow(@knife).to receive(:root_rest).and_return(rest) + expect(@knife.ui).to receive(:confirm).with("Do you want to delete the organization #{@org_name}") + expect(rest).to receive(:delete).with("organizations/#{@org_name}") + expect(@knife.ui).to receive(:output) + @knife.run + end +end diff --git a/spec/unit/knife/opc_org_list_spec.rb b/spec/unit/knife/opc_org_list_spec.rb new file mode 100644 index 0000000000..00401f1c3c --- /dev/null +++ b/spec/unit/knife/opc_org_list_spec.rb @@ -0,0 +1,39 @@ +require "spec_helper" +require "chef/knife/opc_org_list" + +describe Opc::OpcOrgList do + + let(:orgs) do + { + "org1" => "first", + "org2" => "second", + "hiddenhiddenhiddenhi" => "hidden", + } + end + + before :each do + @rest = double("Chef::ServerAPI") + allow(Chef::ServerAPI).to receive(:new).and_return(@rest) + allow(@rest).to receive(:get).with("organizations").and_return(orgs) + @knife = Chef::Knife::OpcOrgList.new + end + + describe "with no arguments" do + it "lists all non hidden orgs" do + expect(@knife.ui).to receive(:output).with(%w{org1 org2}) + @knife.run + end + + end + + describe "with all_orgs argument" do + before do + @knife.config[:all_orgs] = true + end + + it "lists all orgs including hidden orgs" do + expect(@knife.ui).to receive(:output).with(%w{hiddenhiddenhiddenhi org1 org2}) + @knife.run + end + end +end diff --git a/spec/unit/knife/opc_org_show_spec.rb b/spec/unit/knife/opc_org_show_spec.rb new file mode 100644 index 0000000000..7561119b21 --- /dev/null +++ b/spec/unit/knife/opc_org_show_spec.rb @@ -0,0 +1,48 @@ +# +# Author:: Snehal Dwivedi (<sdwivedi@msystechnologies.com>) +# Copyright:: Copyright (c) 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 "spec_helper" +require "chef/org" + +describe Chef::Knife::OpcOrgShow do + + before :each do + @knife = Chef::Knife::OpcOrgShow.new + @org_name = "foobar" + @knife.name_args << @org_name + end + + let(:rest) do + Chef::Config[:chef_server_root] = "http://www.example.com" + root_rest = double("rest") + allow(Chef::ServerAPI).to receive(:new).and_return(root_rest) + end + + it "should load the organisation" do + allow(@knife).to receive(:root_rest).and_return(rest) + expect(rest).to receive(:get).with("organizations/#{@org_name}") + @knife.run + end + + it "should pretty print the output organisation" do + allow(@knife).to receive(:root_rest).and_return(rest) + expect(rest).to receive(:get).with("organizations/#{@org_name}") + expect(@knife.ui).to receive(:output) + @knife.run + end +end diff --git a/spec/unit/knife/opc_org_user_add_spec.rb b/spec/unit/knife/opc_org_user_add_spec.rb new file mode 100644 index 0000000000..37ac4274e3 --- /dev/null +++ b/spec/unit/knife/opc_org_user_add_spec.rb @@ -0,0 +1,24 @@ +require "spec_helper" +require "chef/org" +require "chef/org/group_operations" +require "chef/knife/opc_org_user_add" + +describe Opc::OpcOrgUserAdd do + context "with --admin" do + subject(:knife) { Chef::Knife::OpcOrgUserAdd.new } + let(:org) { double("Chef::Org") } + + it "adds the user to admins and billing-admins groups" do + allow(Chef::Org).to receive(:new).and_return(org) + + knife.config[:admin] = true + knife.name_args = %w{testorg testuser} + + expect(org).to receive(:associate_user).with("testuser") + expect(org).to receive(:add_user_to_group).with("admins", "testuser") + expect(org).to receive(:add_user_to_group).with("billing-admins", "testuser") + + knife.run + end + end +end diff --git a/spec/unit/knife/opc_user_create_spec.rb b/spec/unit/knife/opc_user_create_spec.rb new file mode 100644 index 0000000000..e614015c3c --- /dev/null +++ b/spec/unit/knife/opc_user_create_spec.rb @@ -0,0 +1,121 @@ +# +# Author:: Snehal Dwivedi (<sdwivedi@msystechnologies.com>) +# Copyright:: Copyright (c) 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 "spec_helper" + +describe Opc::OpcUserCreate do + + before :each do + @knife = Chef::Knife::OpcUserCreate.new + @user_name = "foobar" + @first_name = "foo" + @last_name = "bar" + @email = "abc@test.com" + @password = "t123" + @knife.config[:yes] = true + end + + let(:rest) do + Chef::Config[:chef_server_root] = "http://www.example.com" + root_rest = double("rest") + allow(Chef::ServerAPI).to receive(:new).and_return(root_rest) + end + + describe "with no user_name and user_fullname" do + + before :each do + @knife.config[:orgname] = "ramsay" + end + + it "fails with an informative message" do + expect(@knife.ui).to receive(:fatal).with("Wrong number of arguments") + expect(@knife).to receive(:show_usage) + expect { @knife.run }.to raise_error(SystemExit) + end + end + + describe "with user_name, first_name, last_name, email and password" do + before :each do + @user = double("Chef::User") + allow(Chef::User).to receive(:new).and_return(@user) + @key = "You don't come into cooking to get rich - Ramsay" + allow(@user).to receive(:[]).with("private_key").and_return(@key) + @knife.name_args << @user_name << @first_name << @last_name << @email << @password + end + + it "creates an user" do + allow(@knife).to receive(:root_rest).and_return(rest) + expect(rest).to receive(:post).and_return(@user) + expect(@knife.ui).to receive(:msg).with(@key) + @knife.run + end + + context "with --orgname" do + before :each do + @knife.config[:orgname] = "ramsay" + @uri = "http://www.example.com/1" + allow(@user).to receive(:[]).with("uri").and_return(@uri) + end + + let(:request_body) { + { user: @user_name } + } + + it "creates an user, associates a user, and adds it to the admins group" do + allow(@knife).to receive(:root_rest).and_return(rest) + expect(rest).to receive(:post).and_return(@user) + expect(rest).to receive(:post).with("organizations/ramsay/association_requests", request_body).and_return(@user) + expect(rest).to receive(:put).with("users/foobar/association_requests/1", { response: "accept" }) + @knife.run + end + end + end + + describe "with prompt password" do + before :each do + @user = double("Chef::User") + allow(Chef::User).to receive(:new).and_return(@user) + @key = "You don't come into cooking to get rich - Ramsay" + allow(@user).to receive(:[]).with("private_key").and_return(@key) + @knife.config[:passwordprompt] = true + @knife.name_args << @user_name << @first_name << @last_name << @email + end + + it "creates an user" do + allow(@knife).to receive(:root_rest).and_return(rest) + expect(rest).to receive(:post).and_return(@user) + expect(@knife.ui).to receive(:msg).with(@key) + @knife.run + end + end + + describe "should raise prompt password error" do + before :each do + @user = double("Chef::User") + allow(Chef::User).to receive(:new).and_return(@user) + @key = "You don't come into cooking to get rich - Ramsay" + allow(@user).to receive(:[]).with("private_key").and_return(@key) + @knife.name_args << @user_name << @first_name << @last_name << @email + end + + it "fails with an informative message" do + expect(@knife.ui).to receive(:fatal).with("You must either provide a password or use the --prompt-for-password (-p) option") + expect { @knife.run }.to raise_error(SystemExit) + end + end +end diff --git a/spec/unit/knife/opc_user_delete_spec.rb b/spec/unit/knife/opc_user_delete_spec.rb new file mode 100644 index 0000000000..7eddbc7a99 --- /dev/null +++ b/spec/unit/knife/opc_user_delete_spec.rb @@ -0,0 +1,157 @@ +require "spec_helper" +require "chef/org" +require "chef/org/group_operations" +require "chef/knife/opc_user_delete" + +describe Opc::OpcUserDelete do + subject(:knife) { Chef::Knife::OpcUserDelete.new } + + let(:non_admin_member_org) { Chef::Org.new("non-admin-member") } + let(:solo_admin_member_org) { Chef::Org.new("solo-admin-member") } + let(:shared_admin_member_org) { Chef::Org.new("shared-admin-member") } + + let(:removable_orgs) { [non_admin_member_org, shared_admin_member_org] } + let(:non_removable_orgs) { [solo_admin_member_org] } + + let(:admin_memberships) { [ removable_orgs, non_removable_orgs ] } + let(:username) { "test_user" } + + let(:rest) { double("Chef::ServerAPI") } + let(:orgs) { [non_admin_member_org, solo_admin_member_org, shared_admin_member_org] } + let(:knife) { Chef::Knife::OpcUserDelete.new } + + let(:orgs_data) do + [{ "organization" => { "name" => "non-admin-member" } }, + { "organization" => { "name" => "solo-admin-member" } }, + { "organization" => { "name" => "shared-admin-member" } }, + ] + end + + before(:each) do + allow(Chef::ServerAPI).to receive(:new).and_return(rest) + knife.name_args << username + knife.config[:yes] = true + end + + context "when invoked" do + before do + allow(knife).to receive(:admin_group_memberships).and_return(admin_memberships) + end + + context "with --no-disassociate-user" do + before(:each) do + knife.config[:no_disassociate_user] = true + end + + it "should bypass all checks and go directly to user deletion" do + expect(knife).to receive(:delete_user).with(username) + knife.run + end + end + + context "without --no-disassociate-user" do + before do + allow(knife).to receive(:org_memberships).and_return(orgs) + end + + context "and with --remove-from-admin-groups" do + let(:non_removable_orgs) { [ solo_admin_member_org ] } + before(:each) do + knife.config[:remove_from_admin_groups] = true + end + + context "when an associated user the only organization admin" do + let(:non_removable_orgs) { [ solo_admin_member_org ] } + + it "refuses to proceed with because the user is the only admin" do + expect(knife).to receive(:error_exit_cant_remove_admin_membership!).and_call_original + expect { knife.run }.to raise_error SystemExit + end + end + + context "when an associated user is one of many organization admins" do + let(:non_removable_orgs) { [] } + + it "should remove the user from the group, the org, and then and delete the user" do + expect(knife).to receive(:disassociate_user) + expect(knife).to receive(:remove_from_admin_groups) + expect(knife).to receive(:delete_user) + expect(knife).to receive(:error_exit_cant_remove_admin_membership!).exactly(0).times + expect(knife).to receive(:error_exit_admin_group_member!).exactly(0).times + knife.run + end + + end + end + + context "and without --remove-from-admin-groups" do + before(:each) do + knife.config[:remove_from_admin_groups] = false + end + + context "when an associated user is in admins group" do + let(:removable_orgs) { [ shared_admin_member_org ] } + let(:non_removable_orgs) { [ ] } + it "refuses to proceed with because the user is an admin" do + # Default setup + expect(knife).to receive(:error_exit_admin_group_member!).and_call_original + expect { knife.run }.to raise_error SystemExit + end + end + end + + end + context "without --remove-from-admin-groups" do + + end + + end + + context "#admin_group_memberships" do + before do + expect(non_admin_member_org).to receive(:user_member_of_group?).and_return false + + expect(solo_admin_member_org).to receive(:user_member_of_group?).and_return true + expect(solo_admin_member_org).to receive(:actor_delete_would_leave_admins_empty?).and_return true + + expect(shared_admin_member_org).to receive(:user_member_of_group?).and_return true + expect(shared_admin_member_org).to receive(:actor_delete_would_leave_admins_empty?).and_return false + + end + + it "returns an array of organizations in which the user is an admin, and an array of orgs which block removal" do + expect(knife.admin_group_memberships(orgs, username)).to eq [ [solo_admin_member_org, shared_admin_member_org], [solo_admin_member_org]] + end + end + + context "#delete_user" do + it "attempts to delete the user from the system via DELETE to the /users endpoint" do + expect(rest).to receive(:delete).with("users/#{username}") + knife.delete_user(username) + end + end + + context "#disassociate_user" do + it "attempts to remove dissociate the user from each org" do + removable_orgs.each { |org| expect(org).to receive(:dissociate_user).with(username) } + knife.disassociate_user(removable_orgs, username) + end + end + + context "#remove_from_admin_groups" do + it "attempts to remove the given user from the organizations' groups" do + removable_orgs.each { |org| expect(org).to receive(:remove_user_from_group).with("admins", username) } + knife.remove_from_admin_groups(removable_orgs, username) + end + end + + context "#org_memberships" do + it "should make a REST request to return the list of organizations that the user is a member of" do + expect(rest).to receive(:get).with("users/test_user/organizations").and_return orgs_data + result = knife.org_memberships(username) + result.each_with_index do |v, x| + expect(v.to_hash).to eq(orgs[x].to_hash) + end + end + end +end diff --git a/spec/unit/knife/opc_user_list_spec.rb b/spec/unit/knife/opc_user_list_spec.rb new file mode 100644 index 0000000000..f6f2d1a89a --- /dev/null +++ b/spec/unit/knife/opc_user_list_spec.rb @@ -0,0 +1,56 @@ +# +# Author:: Snehal Dwivedi (<sdwivedi@msystechnologies.com>) +# Copyright:: Copyright (c) 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 "spec_helper" +require "chef/knife/opc_user_list" + +describe Opc::OpcUserList do + + let(:users) do + { + "user1" => "first", + "user2" => "second", + } + end + + before :each do + @rest = double("Chef::ServerAPI") + allow(Chef::ServerAPI).to receive(:new).and_return(@rest) + allow(@rest).to receive(:get).with("users").and_return(users) + @knife = Chef::Knife::OpcUserList.new + end + + describe "with no arguments" do + it "lists all non users" do + expect(@knife.ui).to receive(:output).with(%w{user1 user2}) + @knife.run + end + + end + + describe "with all_users argument" do + before do + @knife.config[:all_users] = true + end + + it "lists all users including hidden users" do + expect(@knife.ui).to receive(:output).with(%w{user1 user2}) + @knife.run + end + end +end diff --git a/spec/unit/knife/opc_user_password_spec.rb b/spec/unit/knife/opc_user_password_spec.rb new file mode 100644 index 0000000000..0ca7394f93 --- /dev/null +++ b/spec/unit/knife/opc_user_password_spec.rb @@ -0,0 +1,67 @@ +# +# Author:: Snehal Dwivedi (<sdwivedi@msystechnologies.com>) +# Copyright:: Copyright (c) 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 "spec_helper" +require "chef/org" + +describe Chef::Knife::OpcUserPassword do + + before :each do + @knife = Chef::Knife::OpcUserPassword.new + @user_name = "foobar" + @password = "abc123" + @user = double("Chef::User") + allow(Chef::User).to receive(:new).and_return(@user) + @key = "You don't come into cooking to get rich - Ramsay" + end + + let(:rest) do + Chef::Config[:chef_server_root] = "http://www.example.com" + root_rest = double("rest") + allow(Chef::ServerAPI).to receive(:new).and_return(root_rest) + end + + describe "should change user's password" do + before :each do + @knife.name_args << @user_name << @password + end + + it "with username and password" do + result = { "password" => [], "recovery_authentication_enabled" => true } + + allow(@knife).to receive(:root_rest).and_return(rest) + allow(@user).to receive(:[]).with("organization") + + expect(rest).to receive(:get).with("users/#{@user_name}").and_return(result) + expect(rest).to receive(:put).with("users/#{@user_name}", result) + expect(@knife.ui).to receive(:msg).with("Authentication info updated for #{@user_name}.") + + @knife.run + end + end + + describe "should not change user's password" do + + it "ails with an informative message" do + expect(@knife).to receive(:show_usage) + expect(@knife.ui).to receive(:fatal).with("You must pass two arguments") + expect(@knife.ui).to receive(:fatal).with("Note that --enable-external-auth cannot be passed with a password") + expect { @knife.run }.to raise_error(SystemExit) + end + end +end diff --git a/spec/unit/knife/opc_user_show_spec.rb b/spec/unit/knife/opc_user_show_spec.rb new file mode 100644 index 0000000000..224d801cb2 --- /dev/null +++ b/spec/unit/knife/opc_user_show_spec.rb @@ -0,0 +1,68 @@ +# +# Author:: Snehal Dwivedi (<sdwivedi@msystechnologies.com>) +# Copyright:: Copyright (c) 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 "spec_helper" +require "chef/org" + +describe Chef::Knife::OpcUserShow do + + before :each do + @knife = Chef::Knife::OpcUserShow.new + @user_name = "foobar" + @knife.name_args << @user_name + @org = double("Chef::Org") + allow(Chef::Org).to receive(:new).and_return(@org) + @key = "You don't come into cooking to get rich - Ramsay" + end + + let(:rest) do + Chef::Config[:chef_server_root] = "http://www.example.com" + root_rest = double("rest") + allow(Chef::ServerAPI).to receive(:new).and_return(root_rest) + end + + let(:orgs) do + [@org] + end + + it "should load the user" do + allow(@knife).to receive(:root_rest).and_return(rest) + expect(rest).to receive(:get).with("users/#{@user_name}") + @knife.run + end + + it "should pretty print the output user" do + allow(@knife).to receive(:root_rest).and_return(rest) + expect(rest).to receive(:get).with("users/#{@user_name}") + expect(@knife.ui).to receive(:output) + @knife.run + end + + it "should load the user with organisation" do + @org_name = "abc_org" + @knife.name_args << @user_name << @org_name + result = { "organizations" => [] } + @knife.config[:with_orgs] = true + + allow(@knife).to receive(:root_rest).and_return(rest) + allow(@org).to receive(:[]).with("organization").and_return({ "name" => "test" }) + expect(rest).to receive(:get).with("users/#{@user_name}").and_return(result) + expect(rest).to receive(:get).with("users/#{@user_name}/organizations").and_return(orgs) + @knife.run + end +end diff --git a/spec/unit/org_group_spec.rb b/spec/unit/org_group_spec.rb new file mode 100644 index 0000000000..0d72228f22 --- /dev/null +++ b/spec/unit/org_group_spec.rb @@ -0,0 +1,46 @@ + +# 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/org" +require "chef/org/group_operations" + +describe Chef::Org do + let(:org) { Chef::Org.new("myorg") } + + describe "API Interactions" do + before(:each) do + Chef::Config[:chef_server_root] = "http://www.example.com" + @rest = double("rest") + allow(Chef::ServerAPI).to receive(:new).and_return(@rest) + end + + describe "group" do + it "should load group data when it's not loaded." do + expect(@rest).to receive(:get_rest).with("organizations/myorg/groups/admins").and_return({}) + org.group("admins") + end + + it "should not load group data a second time when it's already loaded." do + expect(@rest).to receive(:get_rest) + .with("organizations/myorg/groups/admins") + .and_return({ anything: "goes" }) + .exactly(:once) + admin1 = org.group("admins") + admin2 = org.group("admins") + expect(admin1).to eq admin2 + end + end + end +end |