# # Author:: Tyler Cloke (tyler@chef.io) # Copyright:: Copyright 2015-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 "spec_helper" require "chef/key" describe Chef::Key do # whether user or client irrelevent to these tests let(:key) { Chef::Key.new("original_actor", "user") } let(:public_key_string) do < "turtle", "name" => "key_name", "public_key" => public_key_string, "private_key" => "some_private_key", "expiration_date" => "infinity" } Chef::Key.from_json(o.to_json) end let(:key_with_create_key_field) do o = { "user" => "turtle", "create_key" => true } Chef::Key.from_json(o.to_json) end end end context "when deserializing a key for a client" do it_should_behave_like "a deserializable object" do let(:key) do o = { "client" => "turtle", "name" => "key_name", "public_key" => public_key_string, "private_key" => "some_private_key", "expiration_date" => "infinity" } Chef::Key.from_json(o.to_json) end let(:key_with_create_key_field) do o = { "client" => "turtle", "create_key" => true } Chef::Key.from_json(o.to_json) end end end end # when deserializing from JSON describe "API Interactions" do let(:rest) do Chef::Config[:chef_server_root] = "http://www.example.com" Chef::Config[:chef_server_url] = "http://www.example.com/organizations/test_org" r = double("rest") allow(Chef::ServerAPI).to receive(:new).and_return(r) r end let(:user_key) do o = Chef::Key.new("foobar", "user") o end let(:client_key) do o = Chef::Key.new("foobar", "client") o end describe "list" do context "when listing keys for a user" do let(:response) { [{ "uri" => "http://www.example.com/users/keys/foobar", "name" => "foobar", "expired" => false }] } let(:inflated_response) { { "foobar" => user_key } } it "lists all keys" do expect(rest).to receive(:get).with("users/#{user_key.actor}/keys").and_return(response) expect(Chef::Key.list_by_user("foobar")).to eq(response) end it "inflate all keys" do allow(Chef::Key).to receive(:load_by_user).with(user_key.actor, "foobar").and_return(user_key) expect(rest).to receive(:get).with("users/#{user_key.actor}/keys").and_return(response) expect(Chef::Key.list_by_user("foobar", true)).to eq(inflated_response) end end context "when listing keys for a client" do let(:response) { [{ "uri" => "http://www.example.com/users/keys/foobar", "name" => "foobar", "expired" => false }] } let(:inflated_response) { { "foobar" => client_key } } it "lists all keys" do expect(rest).to receive(:get).with("clients/#{client_key.actor}/keys").and_return(response) expect(Chef::Key.list_by_client("foobar")).to eq(response) end it "inflate all keys" do allow(Chef::Key).to receive(:load_by_client).with(client_key.actor, "foobar").and_return(client_key) expect(rest).to receive(:get).with("clients/#{user_key.actor}/keys").and_return(response) expect(Chef::Key.list_by_client("foobar", true)).to eq(inflated_response) end end end describe "create" do shared_examples_for "create key" do context "when a field is missing" do it "should raise a MissingKeyAttribute" do expect { key.create }.to raise_error(Chef::Exceptions::MissingKeyAttribute) end end context "when the name field is missing" do before do key.public_key public_key_string key.expiration_date "2020-12-24T21:00:00Z" end it "creates a new key via the API with the fingerprint as the name" do expect(rest).to receive(:post).with(url, { "name" => "12:3e:33:73:0b:f4:ec:72:dc:f0:4c:51:62:27:08:76:96:24:f4:4a", "public_key" => key.public_key, "expiration_date" => key.expiration_date }).and_return({}) key.create end end context "when every field is populated" do before do key.name "key_name" key.public_key public_key_string key.expiration_date "2020-12-24T21:00:00Z" key.create_key false end context "when create_key is false" do it "creates a new key via the API" do expect(rest).to receive(:post).with(url, { "name" => key.name, "public_key" => key.public_key, "expiration_date" => key.expiration_date }).and_return({}) key.create end end context "when create_key is true and public_key is nil" do before do key.delete_public_key key.create_key true $expected_output = { actor_type => "foobar", "name" => key.name, "create_key" => true, "expiration_date" => key.expiration_date, } $expected_input = { "name" => key.name, "create_key" => true, "expiration_date" => key.expiration_date, } end it "should create a new key via the API" do expect(rest).to receive(:post).with(url, $expected_input).and_return({}) key.create end context "when the server returns the private_key via key.create" do before do allow(rest).to receive(:post).with(url, $expected_input).and_return({ "private_key" => "this_private_key" }) end it "key.create returns the original key plus the private_key" do expect(key.create.to_hash).to eq($expected_output.merge({ "private_key" => "this_private_key" })) end end end context "when create_key is false and public_key is nil" do before do key.delete_public_key key.create_key false end it "should raise an InvalidKeyArgument" do expect { key.create }.to raise_error(Chef::Exceptions::MissingKeyAttribute) end end end end context "when creating a user key" do it_should_behave_like "create key" do let(:url) { "users/#{key.actor}/keys" } let(:key) { user_key } let(:actor_type) { "user" } end end context "when creating a client key" do it_should_behave_like "create key" do let(:url) { "clients/#{client_key.actor}/keys" } let(:key) { client_key } let(:actor_type) { "client" } end end end # create describe "update" do shared_examples_for "update key" do context "when name is missing and no argument was passed to update" do it "should raise an MissingKeyAttribute" do expect { key.update }.to raise_error(Chef::Exceptions::MissingKeyAttribute) end end context "when some fields are populated" do before do key.name "key_name" key.expiration_date "2020-12-24T21:00:00Z" end it "should update the key via the API" do expect(rest).to receive(:put).with(url, key.to_hash).and_return({}) key.update end end context "when @name is not nil and a arg is passed to update" do before do key.name "new_name" end it "passes @name in the body and the arg in the PUT URL" do expect(rest).to receive(:put).with(update_name_url, key.to_hash).and_return({}) key.update("old_name") end end context "when the server returns a public_key and create_key is true" do before do key.name "key_name" key.create_key true allow(rest).to receive(:put).with(url, key.to_hash).and_return({ "key" => "key_name", "public_key" => public_key_string, }) end it "returns a key with public_key populated" do new_key = key.update expect(new_key.public_key).to eq(public_key_string) end it "returns a key without create_key set" do new_key = key.update expect(new_key.create_key).to be_nil end end end context "when updating a user key" do it_should_behave_like "update key" do let(:url) { "users/#{key.actor}/keys/#{key.name}" } let(:update_name_url) { "users/#{key.actor}/keys/old_name" } let(:key) { user_key } end end context "when updating a client key" do it_should_behave_like "update key" do let(:url) { "clients/#{client_key.actor}/keys/#{key.name}" } let(:update_name_url) { "clients/#{client_key.actor}/keys/old_name" } let(:key) { client_key } end end end #update describe "load" do shared_examples_for "load" do it "should load a named key from the API" do expect(rest).to receive(:get).with(url).and_return({ "user" => "foobar", "name" => "test_key_name", "public_key" => public_key_string, "expiration_date" => "infinity" }) key = Chef::Key.send(load_method, "foobar", "test_key_name") expect(key.actor).to eq("foobar") expect(key.name).to eq("test_key_name") expect(key.public_key).to eq(public_key_string) expect(key.expiration_date).to eq("infinity") end end describe "load_by_user" do it_should_behave_like "load" do let(:load_method) { :load_by_user } let(:url) { "users/foobar/keys/test_key_name" } end end describe "load_by_client" do it_should_behave_like "load" do let(:load_method) { :load_by_client } let(:url) { "clients/foobar/keys/test_key_name" } end end end #load describe "destroy" do shared_examples_for "destroy key" do context "when name is missing" do it "should raise an MissingKeyAttribute" do expect { Chef::Key.new("username", "user").destroy }.to raise_error(Chef::Exceptions::MissingKeyAttribute) end end before do key.name "key_name" end context "when name is not missing" do it "should delete the key via the API" do expect(rest).to receive(:delete).with(url).and_return({}) key.destroy end end end context "when destroying a user key" do it_should_behave_like "destroy key" do let(:url) { "users/#{key.actor}/keys/#{key.name}" } let(:key) { user_key } end end context "when destroying a client key" do it_should_behave_like "destroy key" do let(:url) { "clients/#{client_key.actor}/keys/#{key.name}" } let(:key) { client_key } end end end end # API Interactions end