require 'spec_helper' describe Metasploit::Credential::Core do include_context 'Mdm::Workspace' subject(:core) do described_class.new end it_should_behave_like 'Metasploit::Concern.run' context 'associations' do it { should have_and_belong_to_many(:tasks).class_name('Mdm::Task') } it { should have_many(:logins).class_name('Metasploit::Credential::Login').dependent(:destroy) } it { should belong_to(:origin) } it { should belong_to(:private).class_name('Metasploit::Credential::Private') } it { should belong_to(:public).class_name('Metasploit::Credential::Public') } it { should belong_to(:realm).class_name('Metasploit::Credential::Realm') } it { should belong_to(:workspace).class_name('Mdm::Workspace') } end context 'database' do context 'columns' do context 'foreign keys' do context 'polymorphic origin' do it { should have_db_column(:origin_id).of_type(:integer).with_options(null: false) } it { should have_db_column(:origin_type).of_type(:string).with_options(null: false) } end it { should have_db_column(:private_id).of_type(:integer).with_options(null: true) } it { should have_db_column(:public_id).of_type(:integer).with_options(null: true) } it { should have_db_column(:realm_id).of_type(:integer).with_options(null: true) } it { should have_db_column(:workspace_id).of_type(:integer).with_options(null: false) } end it_should_behave_like 'timestamp database columns' end context 'indices' do context 'foreign keys' do it { should have_db_index([:origin_type, :origin_id]) } it { should have_db_index(:private_id) } it { should have_db_index(:public_id) } it { should have_db_index(:realm_id) } it { should have_db_index(:workspace_id) } context 'partial' do # # lets # let(:metasploit_credential_origin) { FactoryGirl.create(:metasploit_credential_origin_manual) } let(:workspace) { FactoryGirl.create(:mdm_workspace) } let(:second_metasploit_credential_core) { FactoryGirl.build( :metasploit_credential_core, origin: metasploit_credential_origin, private: private, public: public, workspace: workspace ) } # # let!s # let!(:first_metasploit_credential_core) { FactoryGirl.create( :metasploit_credential_core, origin: metasploit_credential_origin, private: private, public: public, workspace: workspace ) } context '#private_id' do context 'with nil' do let(:private) { nil } context '#public_id' do context 'without nil' do let(:public) { FactoryGirl.create(:metasploit_credential_public) } it 'does not allow duplicates' do expect { second_metasploit_credential_core.save(validate: false) }.to raise_error(ActiveRecord::RecordNotUnique) end end end end context 'without nil' do let(:private) do FactoryGirl.create(:metasploit_credential_password) end context '#public_id' do context 'with nil' do let(:public) { nil } it 'does not allow duplicates' do expect { second_metasploit_credential_core.save(validate: false) }.to raise_error(ActiveRecord::RecordNotUnique) end end context 'without nil' do let(:public) { FactoryGirl.create(:metasploit_credential_public) } it 'does not allow duplicates' do expect { second_metasploit_credential_core.save(validate: false) }.to raise_error(ActiveRecord::RecordNotUnique) end end end end end end end end end context 'scopes' do context '.workspace_id' do let(:query) { described_class.workspace_id(workspace_id) } subject(:metasploit_credential_core) do FactoryGirl.create(:metasploit_credential_core) end context 'when given a valid workspace id' do let(:workspace_id) { metasploit_credential_core.workspace_id } it 'returns the correct Core' do expect(query).to eq [metasploit_credential_core] end end context 'when given an invalid workspace id' do let(:workspace_id) { -1 } it 'returns an empty collection' do expect(query).to be_empty end end end context '.login_host_id' do let(:query) { described_class.login_host_id(host_id) } let(:login) { FactoryGirl.create(:metasploit_credential_login) } subject(:metasploit_credential_core) { login.core } context 'when given a valid host id' do let(:host_id) { metasploit_credential_core.logins.first.service.host.id } it 'returns the correct Core' do expect(query).to eq [metasploit_credential_core] end end context 'when given an invalid host id' do let(:host_id) { -1 } it 'returns an empty collection' do expect(query).to be_empty end end end context '.origin_service_host_id' do let(:query) { described_class.origin_service_host_id(host_id) } let(:workspace) { FactoryGirl.create(:mdm_workspace) } subject(:metasploit_credential_core) do FactoryGirl.create(:metasploit_credential_core_service) end context 'when given a valid host id' do let(:host_id) { metasploit_credential_core.origin.service.host.id } it 'returns the correct Core' do expect(query).to eq [metasploit_credential_core] end end context 'when given an invalid host id' do let(:host_id) { -1 } it 'returns an empty collection' do expect(query).to be_empty end end end context '.origin_session_host_id' do let(:query) { described_class.origin_session_host_id(host_id) } subject(:metasploit_credential_core) do FactoryGirl.create(:metasploit_credential_core_session) end context 'when given a valid host id' do let(:host_id) { metasploit_credential_core.origin.session.host.id } it 'returns the correct Core' do expect(query).to eq [metasploit_credential_core] end end context 'when given an invalid host id' do let(:host_id) { -1 } it 'returns an empty collection' do expect(query).to be_empty end end end context '.originating_host_id' do let(:query) { described_class.originating_host_id(host_id) } # Create a couple Cores that are related to the host via session let(:metasploit_credential_core_sessions) do FactoryGirl.create_list(:metasploit_credential_core_session, 2) end # Create a couple Cores that are related to the host via service let(:metasploit_credential_core_services) do FactoryGirl.create_list(:metasploit_credential_core_service, 2) end # Create an unrelated Core let(:unrelated_metasploit_credential_core) do FactoryGirl.create(:metasploit_credential_core_service) end before do # make sure they are all related to the same host # ideally this would be done in the factory, but one look at the factories and i am punting. init_host_id = metasploit_credential_core_services.first.origin.service.host.id metasploit_credential_core_services.each do |core| core.origin.service.host_id = init_host_id core.origin.service.save end metasploit_credential_core_sessions.each do |core| core.origin.session.host_id = init_host_id core.origin.session.save end # Make sure the unrelated core is actually created unrelated_metasploit_credential_core end context 'when given a valid host id' do let(:host_id) { metasploit_credential_core_sessions.first.origin.session.host.id } it 'returns an ActiveRecord::Relation' do expect(query).to be_an ActiveRecord::Relation end it 'returns the correct Cores' do expect(query).to match_array metasploit_credential_core_sessions + metasploit_credential_core_services end end context 'when given an invalid host id' do let(:host_id) { -1 } it 'returns an ActiveRecord::Relation' do expect(query).to be_an ActiveRecord::Relation end it 'returns an empty collection' do expect(query).to be_empty end end end end context 'search' do let(:base_class) { described_class } context 'associations' do it_should_behave_like 'search_association', :logins it_should_behave_like 'search_association', :private it_should_behave_like 'search_association', :public it_should_behave_like 'search_association', :realm end end context 'factories' do context 'metasploit_credential_core' do subject(:metasploit_credential_core) do FactoryGirl.build(:metasploit_credential_core) end let(:origin) do metasploit_credential_core.origin end it { should be_valid } context 'with origin_factory' do subject(:metasploit_credential_core) do FactoryGirl.build( :metasploit_credential_core, origin_factory: origin_factory ) end context ':metasploit_credential_origin_import' do let(:origin_factory) do :metasploit_credential_origin_import end it { should be_valid } end context ':metasploit_credential_origin_manual' do let(:origin_factory) do :metasploit_credential_origin_manual end it { should be_valid } context '#origin' do subject(:origin) do metasploit_credential_core.origin end it { should be_a Metasploit::Credential::Origin::Manual } end context '#workspace' do subject(:workspace) do metasploit_credential_core.workspace end it { should_not be_nil } end end context ':metasploit_credential_origin_service' do let(:origin_factory) do :metasploit_credential_origin_service end it { should be_valid } context '#workspace' do subject(:workspace) do metasploit_credential_core.workspace end it 'is origin.service.host.workspace' do expect(workspace).not_to be_nil expect(workspace).to eq(origin.service.host.workspace) end end end context ':metasploit_credential_origin_session' do let(:origin_factory) do :metasploit_credential_origin_session end it { should be_valid } context '#workspace' do subject(:workspace) do metasploit_credential_core.workspace end it 'is origin.session.host.workspace' do expect(workspace).not_to be_nil expect(workspace).to eq(origin.session.host.workspace) end end end end end context 'metasploit_credential_core_import' do subject(:metasploit_credential_core_import) do FactoryGirl.build(:metasploit_credential_core_import) end it { should be_valid } end context 'metasploit_credential_core_manual' do subject(:metasploit_credential_core_manual) do FactoryGirl.build(:metasploit_credential_core_manual) end it { should be_valid } context '#workspace' do subject(:workspace) do metasploit_credential_core_manual.workspace end it { should_not be_nil } end end context 'metasploit_credential_core_service' do subject(:metasploit_credential_core_service) do FactoryGirl.build(:metasploit_credential_core_service) end it { should be_valid } context '#workspace' do subject(:workspace) do metasploit_credential_core_service.workspace end let(:origin) do metasploit_credential_core_service.origin end it 'is origin.service.host.workspace' do expect(workspace).not_to be_nil expect(workspace).to eq(origin.service.host.workspace) end end end context 'metasploit_credential_core_session' do subject(:metasploit_credential_core_session) do FactoryGirl.build(:metasploit_credential_core_session) end it { should be_valid } context '#workspace' do subject(:workspace) do metasploit_credential_core_session.workspace end let(:origin) do metasploit_credential_core_session.origin end it 'is origin.session.host.workspace' do expect(workspace).not_to be_nil expect(workspace).to eq(origin.session.host.workspace) end end end end context 'validations' do it { should validate_presence_of :origin } it { should validate_presence_of :workspace } context 'of uniqueness' do # # lets # let(:metasploit_credential_origin) { FactoryGirl.create(:metasploit_credential_origin_manual) } let(:workspace) { FactoryGirl.create(:mdm_workspace) } let(:second_metasploit_credential_core) { FactoryGirl.build( :metasploit_credential_core, origin: metasploit_credential_origin, private: private, public: public, workspace: workspace ) } let(:taken_error) { I18n.translate!('activerecord.errors.messages.taken') } # # let!s # let!(:first_metasploit_credential_core) { FactoryGirl.create( :metasploit_credential_core, origin: metasploit_credential_origin, private: private, public: public, workspace: workspace ) } # # Callbacks # before(:each) do second_metasploit_credential_core.valid? end context '#private_id' do context 'with nil' do let(:private) { nil } context '#public_id' do context 'without nil' do let(:public) { FactoryGirl.create(:metasploit_credential_public) } it 'does not allow duplicates on #public_id' do expect(second_metasploit_credential_core.errors[:public_id]).to include(taken_error) end end end end context 'without nil' do let(:private) do FactoryGirl.create(:metasploit_credential_password) end context '#public_id' do context 'with nil' do let(:public) { nil } it 'does not allow duplicates on #private_id' do expect(second_metasploit_credential_core.errors[:private_id]).to include(taken_error) end end context 'without nil' do let(:public) { FactoryGirl.create(:metasploit_credential_public) } it 'does not allow duplicates on #private_id' do expect(second_metasploit_credential_core.errors[:private_id]).to include(taken_error) end end end end end end context '#consistent_workspaces' do subject(:workspace_errors) do core.errors[:workspace] end # # lets # let(:core) do FactoryGirl.build( :metasploit_credential_core, origin: origin, workspace: workspace ) end let(:workspace) do FactoryGirl.create(:mdm_workspace) end # # Callbacks # before(:each) do core.valid? end context '#origin' do context 'with Metasploit::Credential::Origin::Manual' do let(:error) do I18n.translate!('activerecord.errors.models.metasploit/credential/core.attributes.workspace.origin_user_workspaces') end let(:origin) do FactoryGirl.build( :metasploit_credential_origin_manual, user: user ) end context 'with Metasploit::Credential::Origin::Manual#user' do let(:user) do FactoryGirl.build( :mdm_user, admin: admin ) end context 'with Mdm::User#admin' do let(:admin) do true end it { should_not include error } end context 'without Mdm::User#admin' do let(:admin) do false end context 'with #workspace in Mdm::User#workspaces' do let(:user) do super().tap { |user| user.workspaces << workspace } end context 'with persisted' do let(:user) do super().tap { |user| user.save! } end it { should_not include error } end context 'without persisted' do it { should_not include error } end end context 'without #workspace in Mdm::User#workspaces' do it { should include error } end end end context 'without Metasploit::Credential::Origin::Manual#user' do let(:user) do nil end it { should include error } end end context 'with Metasploit::Credential::Origin::Service' do let(:error) do I18n.translate!('activerecord.errors.models.metasploit/credential/core.attributes.workspace.origin_service_host_workspace') end let(:origin) do FactoryGirl.build( :metasploit_credential_origin_service, service: service ) end context 'with Metasploit::Credential::Origin::Service#service' do let(:service) do FactoryGirl.build( :mdm_service, host: host ) end context 'with Mdm::Service#host' do let(:host) do FactoryGirl.build( :mdm_host, workspace: host_workspace ) end context 'same as #workspace' do let(:host_workspace) do workspace end it { should_not include error } end context 'different than #workspace' do let(:host_workspace) do FactoryGirl.create(:mdm_workspace) end it { should include error } end end context 'without Mdm::Service#host' do let(:host) do nil end it { should include error } end end context 'without Metasploit::Credential::Origin::Service#service' do let(:service) do nil end it { should include error } end end context 'with Metasploit::Credential::Origin::Session' do let(:error) do I18n.translate!('activerecord.errors.models.metasploit/credential/core.attributes.workspace.origin_session_host_workspace') end let(:origin) do FactoryGirl.build( :metasploit_credential_origin_session, session: session ) end context 'with Metasploit::Credential::Origin::Session#session' do let(:session) do FactoryGirl.build( :mdm_session, host: host ) end context 'with Mdm::Session#host' do let(:host) do FactoryGirl.build( :mdm_host, workspace: host_workspace ) end context 'with Mdm::Host#workspace' do context 'same as #workspace' do let(:host_workspace) do workspace end it { should_not include error } end context 'different than #workspace' do let(:host_workspace) do FactoryGirl.create(:mdm_workspace) end it { should include error } end end context 'without Mdm::Host#workspace' do let(:host_workspace) do nil end it { should include error } end end context 'without Mdm::Session#host' do let(:host) do nil end it { should include error } end end context 'without Metasploit::Credential::Origin::Session#session' do let(:session) do nil end it { should include error } end end end end context '#minimum_presence' do subject(:base_errors) do core.errors[:base] end # # lets # let(:core) do FactoryGirl.build( :metasploit_credential_core, private: private, public: public, realm: realm ) end let(:error) do I18n.translate!('activerecord.errors.models.metasploit/credential/core.attributes.base.minimum_presence') end # # Callbacks # before(:each) do core.valid? end context 'with #private' do let(:private) do FactoryGirl.build(private_factory) end let(:private_factory) do FactoryGirl.generate :metasploit_credential_core_private_factory end context 'with #public' do let(:public) do FactoryGirl.build(:metasploit_credential_public) end context 'with #realm' do let(:realm) do FactoryGirl.build(realm_factory) end let(:realm_factory) do FactoryGirl.generate :metasploit_credential_core_realm_factory end it { should_not include(error) } end context 'without #realm' do let(:realm) do nil end it { should_not include(error) } end end context 'without #public' do let(:public) do nil end context 'with #realm' do let(:realm) do FactoryGirl.build(realm_factory) end let(:realm_factory) do FactoryGirl.generate :metasploit_credential_core_realm_factory end it { should_not include(error) } end context 'without #realm' do let(:realm) do nil end it { should_not include(error) } end end end context 'without #private' do let(:private) do nil end context 'with #public' do let(:public) do FactoryGirl.build(:metasploit_credential_public) end context 'with #realm' do let(:realm) do FactoryGirl.build(realm_factory) end let(:realm_factory) do FactoryGirl.generate :metasploit_credential_core_realm_factory end it { should_not include(error) } end context 'without #realm' do let(:realm) do nil end it { should_not include(error) } end end context 'without #public' do let(:public) do nil end context 'with #realm' do let(:realm) do FactoryGirl.build(realm_factory) end let(:realm_factory) do FactoryGirl.generate :metasploit_credential_core_realm_factory end it { should include(error) } end end end end context "#public_for_ssh_key" do let(:error) do I18n.translate!('activerecord.errors.models.metasploit/credential/core.attributes.base.public_for_ssh_key') end let(:core) do FactoryGirl.build( :metasploit_credential_core, private: FactoryGirl.build(:metasploit_credential_ssh_key), public: FactoryGirl.build(:metasploit_credential_public) ) end it { core.should be_valid } context "when the Public is missing" do before(:each) do core.public = nil end it 'should not be valid if Private is an SSHKey and Public is missing' do core.should_not be_valid end it 'should show the proper error' do core.valid? core.errors[:base].should include(error) end end end end end