blob: 740e94f733d9e122ff6f64c25dba45c71bbe54fd [file]
#include "component_integrity.hpp"
#include <memory>
#include <string>
#include <system_error> // NOLINT
#include <unordered_set>
#include <vector>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "boost/beast/http/message.hpp" // NOLINT
#include "dbus_utility.hpp"
#include "test/redfish-core/lib/snapshot_fixture.hpp"
#include <nlohmann/json.hpp>
#include "managed_store.hpp"
#include "managed_store_types.hpp"
#include "test/g3/mock_managed_store.hpp"
#include "test/g3/mock_managed_store_test.hpp"
#include "sdbusplus/message/native_types.hpp"
#ifdef SNAPSHOT_FIXTURE_G3
#include "devtools/build/runtime/get_runfiles_dir.h"
#endif
namespace redfish {
namespace {
using ::testing::Contains;
using ::testing::NotNull;
constexpr const char* fakeComponentIntegrity = "fakeComponentIntegrity";
class SnapshotFixture_ComponentIntegrity : public redfish::SnapshotFixture {
protected:
static void SetUpTestSuite() {
::managedStore::MockManagedStoreTest::SetUpTestSuite();
#ifdef SNAPSHOT_FIXTURE_G3
std::string snapshotFilepath_platform22Local =
::devtools_build::GetDataDependencyFilepath(
"google3/third_party/openbmc_build_scripts/additional/"
"scrubbed_gBMCwebManagedStore_platform22_0_2024_08_13.json");
EXPECT_TRUE(managedStore::GetManagedObjectStore()->deserialize(
snapshotFilepath_platform22Local));
#else
EXPECT_TRUE(managedStore::GetManagedObjectStore()->deserialize(
snapshotFilepath_platform22));
#endif
}
};
TEST_F(
SnapshotFixture_ComponentIntegrity,
GetComponentIntegritySPDMGetSignedMeasurementsActionInfoReturnsCorrectResponse) { // NOLINT
handleSPDMGetSignedMeasurementsActionInfoGet(
app_, CreateRequest(), share_async_resp_, fakeComponentIntegrity);
RunIoUntilDone();
nlohmann::json& json = share_async_resp_->res.jsonValue;
EXPECT_EQ(json["@odata.id"],
"/redfish/v1/ComponentIntegrity/fakeComponentIntegrity/"
"SPDMGetSignedMeasurementsActionInfo");
EXPECT_EQ(json["@odata.type"], "#ActionInfo.v1_3_0.ActionInfo");
EXPECT_EQ(json["Name"], "SPDMGetSignedMeasurements Action Info");
EXPECT_EQ(json["Id"], "SPDMGetSignedMeasurementsActionInfo");
EXPECT_EQ(json["Parameters"][0]["Name"], "MeasurementIndices");
EXPECT_EQ(json["Parameters"][0]["Required"], false);
EXPECT_EQ(json["Parameters"][0]["DataType"], "NumberArray");
EXPECT_EQ(json["Parameters"][1]["Name"], "Nonce");
EXPECT_EQ(json["Parameters"][1]["Required"], false);
EXPECT_EQ(json["Parameters"][1]["DataType"], "String");
EXPECT_EQ(json["Parameters"][2]["Name"], "SlotId");
EXPECT_EQ(json["Parameters"][2]["Required"], false);
EXPECT_EQ(json["Parameters"][2]["DataType"], "Number");
EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok);
}
TEST_F(SnapshotFixture_ComponentIntegrity,
GetComponentIntegrityCollectionTestReturnsCorrectResponse) {
handleComponentIntegrityCollectionGet(app_, CreateRequest(),
share_async_resp_);
RunIoUntilDone();
nlohmann::json& json = share_async_resp_->res.jsonValue;
EXPECT_EQ(json["@odata.id"], "/redfish/v1/ComponentIntegrity");
EXPECT_EQ(json["@odata.type"],
"#ComponentIntegrityCollection.ComponentIntegrityCollection");
EXPECT_EQ(json["Name"], "Component Integrity Collection");
EXPECT_EQ(json["Members@odata.count"], json["Members"].size());
std::unordered_set<std::string> component_integrity_set{{
"/redfish/v1/ComponentIntegrity/CI_name6_7_1_name7_1_NIC_1",
"/redfish/v1/ComponentIntegrity/CI_name6_7_2_name7_1_NIC_1",
"/redfish/v1/ComponentIntegrity/CI_name6_7_3_name7_1_NIC_1",
"/redfish/v1/ComponentIntegrity/CI_name6_7_4_name7_1_NIC_1",
"/redfish/v1/ComponentIntegrity/CI_name6_7_5_name7_1_NIC_1",
"/redfish/v1/ComponentIntegrity/CI_name6_7_6_name7_1_NIC_1",
"/redfish/v1/ComponentIntegrity/CI_name6_7_7_name7_1_NIC_1",
"/redfish/v1/ComponentIntegrity/CI_name6_7_8_name7_1_NIC_1",
}};
for (const auto& member : json["Members"]) {
ASSERT_TRUE(member.contains("@odata.id"));
const std::string* ci = member["@odata.id"].get_ptr<const std::string*>();
ASSERT_THAT(ci, NotNull());
EXPECT_THAT(component_integrity_set, Contains(*ci));
}
EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok);
}
TEST_F(SnapshotFixture_ComponentIntegrity,
GetComponentIntegrityTestReturnsCorrectResponse) {
handleComponentIntegrityGet(app_, CreateRequest(), share_async_resp_,
"CI_name6_7_1_name7_1_NIC_1");
RunIoUntilDone();
nlohmann::json& json = share_async_resp_->res.jsonValue;
EXPECT_EQ(json["@odata.id"],
"/redfish/v1/ComponentIntegrity/CI_name6_7_1_name7_1_NIC_1");
EXPECT_EQ(json["@odata.type"],
"#ComponentIntegrity.v1_2_0.ComponentIntegrity");
EXPECT_EQ(json["Actions"]["#ComponentIntegrity.SPDMGetSignedMeasurements"]
["@Redfish.ActionInfo"],
"/redfish/v1/ComponentIntegrity/CI_name6_7_1_name7_1_NIC_1/"
"SPDMGetSignedMeasurementsActionInfo");
EXPECT_EQ(json["Actions"]["#ComponentIntegrity.SPDMGetSignedMeasurements"]
["target"],
"/redfish/v1/ComponentIntegrity/CI_name6_7_1_name7_1_NIC_1/Actions/"
"ComponentIntegrity.SPDMGetSignedMeasurements");
EXPECT_EQ(json["Name"], "Component Integrity");
EXPECT_EQ(json["Id"], "CI_name6_7_1_name7_1_NIC_1");
EXPECT_EQ(json["LastUpdated"], "20230308T201000Z");
EXPECT_EQ(json["ComponentIntegrityType"], "SPDM");
EXPECT_EQ(json["SPDM"]["Requester"]["@odata.id"], "/redfish/v1/Managers/bmc");
EXPECT_EQ(
json["SPDM"]["IdentityAuthentication"]["ResponderAuthentication"]
["ComponentCertificate"]["@odata.id"],
"/redfish/v1/Chassis/name6_7_1/Certificates/name7_1_NIC_1_PemCertChain");
EXPECT_EQ(json["SPDM"]["IdentityAuthentication"]["ResponderAuthentication"]
["VerificationStatus"],
"Success");
EXPECT_EQ(json["TargetComponentURI"],
"/redfish/v1/Chassis/name6_7_1/TrustedComponents/TC_name7_1_NIC_1");
EXPECT_EQ(json["ComponentIntegrityTypeVersion"], "1.1");
EXPECT_EQ(json["Description"],
"SPDM-capable RoT physically attached to a device.");
EXPECT_EQ(json["Links"]["ComponentsProtected"][0]["@odata.id"],
"/redfish/v1/Chassis/name6_7_1/NetworkAdapters/name7_1_NIC_1");
EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok);
}
TEST_F(SnapshotFixture_ComponentIntegrity,
GetComponentIntegrityComponentItemReturnsCorrectResponse) {
dbus::utility::MapperGetObject componentMapperObj{
{{"xyz.openbmc_project.SPDM",
{"xyz.openbmc_project.Inventory.Item.Component"}}}};
// Use name6_7_2 rather than name6_7_1. The mock managed store is a
// process-wide static created in MockManagedStoreTest::SetUpTestSuite() and
// is never reset between tests, so this upsert is permanent for the rest of
// the binary. name6_7_1 is the object
// GetComponentIntegrityTestReturnsCorrectResponse expects to still be a
// NetworkAdapter, and overriding it here made that test fail whenever it
// happened to run afterwards
// (repro: --gtest_shuffle --gtest_random_seed=1). Nothing asserts on the
// contents of name6_7_2.
managedStore::KeyType keyGetObject(
managedStore::ManagedType::kManagedMapperObject,
"xyz.openbmc_project.SPDM",
sdbusplus::message::object_path(
"/xyz/openbmc_project/inventory/system/board/name6_7_2/"
"name7_1_NIC_1"),
"xyz.openbmc_project.Inventory.Item.Component", "Component");
std::shared_ptr<managedStore::ValueType> mockedObj =
CreateValueType(componentMapperObj);
EXPECT_TRUE(
dynamic_cast<managedStore::MockSerializedManagedObjectStore*>(
managedStore::GetManagedObjectStore())
->upsertMockObjectIntoManagedStore(keyGetObject, mockedObj)
.ok());
managedStore::KeyType keyContainedBy(
managedStore::ManagedType::kManagedProperty,
"xyz.openbmc_project.ObjectMapper",
sdbusplus::message::object_path(
"/xyz/openbmc_project/inventory/system/board/name6_7_2/"
"name7_1_NIC_1/contained_by"),
"xyz.openbmc_project.Association", "endpoints");
// keyContainedBy is a kManagedProperty key, so the value has to be built
// from a DbusVariantType. Passing a bare std::vector<std::string> selects
// the MapperGetSubTreePathsResponse overload of ValueType instead, and the
// upsert is then rejected with "Key and Value type are not compatible".
std::shared_ptr<managedStore::ValueType> mockedContainedBy =
CreateValueType(dbus::utility::DbusVariantType(
dbus::utility::MapperEndPoints{
"/xyz/openbmc_project/inventory/system/board/chassis_name"}));
EXPECT_TRUE(
dynamic_cast<managedStore::MockSerializedManagedObjectStore*>(
managedStore::GetManagedObjectStore())
->upsertMockObjectIntoManagedStore(keyContainedBy, mockedContainedBy)
.ok());
handleComponentIntegrityGet(app_, CreateRequest(), share_async_resp_,
"CI_name6_7_2_name7_1_NIC_1");
RunIoUntilDone();
nlohmann::json& json = share_async_resp_->res.jsonValue;
EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok);
EXPECT_EQ(json["Links"]["ComponentsProtected"][0]["@odata.id"],
"/redfish/v1/Chassis/chassis_name");
}
TEST_F(SnapshotFixture_ComponentIntegrity,
GetComponentIntegrityComponentItemWithoutContainedByUsesParentPath) {
dbus::utility::MapperGetObject componentMapperObj{
{{"xyz.openbmc_project.SPDM",
{"xyz.openbmc_project.Inventory.Item.Component"}}}};
// name6_7_3 is used here for the same reason name6_7_2 is used above: the
// mock managed store is never reset between tests, so this upsert is
// permanent. Nothing asserts on the contents of name6_7_3.
managedStore::KeyType keyGetObject(
managedStore::ManagedType::kManagedMapperObject,
"xyz.openbmc_project.SPDM",
sdbusplus::message::object_path(
"/xyz/openbmc_project/inventory/system/board/name6_7_3/"
"name7_1_NIC_1"),
"xyz.openbmc_project.Inventory.Item.Component", "Component");
std::shared_ptr<managedStore::ValueType> mockedObj =
CreateValueType(componentMapperObj);
EXPECT_TRUE(
dynamic_cast<managedStore::MockSerializedManagedObjectStore*>(
managedStore::GetManagedObjectStore())
->upsertMockObjectIntoManagedStore(keyGetObject, mockedObj)
.ok());
// No contained_by association is upserted for this object, and the snapshot
// does not carry one either. The store runs in snapshot mode, so the lookup
// fails with an error instead of reading through to D-Bus, which is the
// inventory-without-association case the fallback exists for. The chassis id
// must then come from the parent of the component's own inventory path.
handleComponentIntegrityGet(app_, CreateRequest(), share_async_resp_,
"CI_name6_7_3_name7_1_NIC_1");
RunIoUntilDone();
nlohmann::json& json = share_async_resp_->res.jsonValue;
EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok);
EXPECT_EQ(json["Links"]["ComponentsProtected"][0]["@odata.id"],
"/redfish/v1/Chassis/name6_7_3");
}
// Verifies that when a ComponentIntegrity D-Bus object authenticates an
// inventory item implementing xyz.openbmc_project.Inventory.Item.Drive, the
// Redfish response populates Links.ComponentsProtected with the Drive Redfish
// URI.
TEST_F(
SnapshotFixture_ComponentIntegrity,
GetComponentIntegrityDriveProtectedComponentsTestReturnsCorrectResponse) {
using ::dbus::utility::DbusVariantType;
using ::dbus::utility::MapperGetObject;
using ::managedStore::KeyType;
using ::managedStore::ManagedType;
using ::managedStore::ValueType;
// Use CI_name6_7_4_name7_1_NIC_1 / name6_7_4 so we do not mutate the shared
// static mock entries used by CI_name6_7_1..3 in sibling tests.
KeyType keyDriveMapperObject(
ManagedType::kManagedMapperObject,
"/xyz/openbmc_project/inventory/system/board/name6_7_4/Drive_1",
std::vector<std::string>{});
std::shared_ptr<ValueType> driveMapperValue =
CreateValueType(MapperGetObject({{
"xyz.openbmc_project.SPDM",
{"xyz.openbmc_project.Inventory.Item.Drive"},
}}));
EXPECT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>(
managedStore::GetManagedObjectStore())
->upsertMockObjectIntoManagedStore(keyDriveMapperObject,
driveMapperValue)
.ok());
// Override the authenticating association endpoints to point to the Drive
// object path.
KeyType keyAuthenticating(ManagedType::kManagedProperty,
"xyz.openbmc_project.ObjectMapper",
sdbusplus::message::object_path(
"/xyz/openbmc_project/ComponentIntegrity/"
"CI_name6_7_4_name7_1_NIC_1/authenticating"),
"xyz.openbmc_project.Association", "endpoints");
std::shared_ptr<ValueType> authenticatingValue =
CreateValueType(DbusVariantType(std::vector<std::string>{
{"/xyz/openbmc_project/inventory/system/board/name6_7_4/Drive_1"}}));
EXPECT_TRUE(dynamic_cast<managedStore::MockSerializedManagedObjectStore*>(
managedStore::GetManagedObjectStore())
->upsertMockObjectIntoManagedStore(keyAuthenticating,
authenticatingValue)
.ok());
handleComponentIntegrityGet(app_, CreateRequest(), share_async_resp_,
"CI_name6_7_4_name7_1_NIC_1");
RunIoUntilDone();
// Verify that ComponentsProtected links to the expected Redfish Drive
// resource.
nlohmann::json& json = share_async_resp_->res.jsonValue;
EXPECT_EQ(json["@odata.id"],
"/redfish/v1/ComponentIntegrity/CI_name6_7_4_name7_1_NIC_1");
ASSERT_TRUE(json["Links"].contains("ComponentsProtected"));
ASSERT_EQ(json["Links"]["ComponentsProtected"].size(), 1);
EXPECT_EQ(json["Links"]["ComponentsProtected"][0]["@odata.id"],
"/redfish/v1/Chassis/name6_7_4/Drives/Drive_1");
EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok);
}
TEST_F(SnapshotFixture_ComponentIntegrity,
GetComponentIntegrityMctpUsbDeviceReturnsCorrectResponse) {
dbus::utility::MapperGetObject mctpMapperObj{
{{"xyz.openbmc_project.EntityManager",
{"xyz.openbmc_project.Configuration.MCTPUSBDevice"}}}};
managedStore::KeyType keyGetObject(
managedStore::ManagedType::kManagedMapperObject,
"xyz.openbmc_project.EntityManager",
sdbusplus::message::object_path(
"/xyz/openbmc_project/inventory/system/board/name6_7_5/"
"name7_1_NIC_1"),
"xyz.openbmc_project.Configuration.MCTPUSBDevice", "MCTPUSBDevice");
std::shared_ptr<managedStore::ValueType> mockedObj =
CreateValueType(mctpMapperObj);
EXPECT_TRUE(
dynamic_cast<managedStore::MockSerializedManagedObjectStore*>(
managedStore::GetManagedObjectStore())
->upsertMockObjectIntoManagedStore(keyGetObject, mockedObj)
.ok());
managedStore::KeyType keyContainedBy(
managedStore::ManagedType::kManagedProperty,
"xyz.openbmc_project.ObjectMapper",
sdbusplus::message::object_path(
"/xyz/openbmc_project/inventory/system/board/name6_7_5/"
"name7_1_NIC_1/contained_by"),
"xyz.openbmc_project.Association", "endpoints");
std::shared_ptr<managedStore::ValueType> mockedContainedBy =
CreateValueType(dbus::utility::DbusVariantType(
dbus::utility::MapperEndPoints{
"/xyz/openbmc_project/inventory/system/board/chassis_name"}));
EXPECT_TRUE(
dynamic_cast<managedStore::MockSerializedManagedObjectStore*>(
managedStore::GetManagedObjectStore())
->upsertMockObjectIntoManagedStore(keyContainedBy, mockedContainedBy)
.ok());
handleComponentIntegrityGet(app_, CreateRequest(), share_async_resp_,
"CI_name6_7_5_name7_1_NIC_1");
RunIoUntilDone();
nlohmann::json& json = share_async_resp_->res.jsonValue;
EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok);
EXPECT_EQ(json["Links"]["ComponentsProtected"][0]["@odata.id"],
"/redfish/v1/Chassis/chassis_name");
}
TEST_F(SnapshotFixture_ComponentIntegrity,
GetComponentIntegrityMctpUsbDeviceWithoutContainedByUsesParentPath) {
dbus::utility::MapperGetObject mctpMapperObj{
{{"xyz.openbmc_project.EntityManager",
{"xyz.openbmc_project.Configuration.MCTPUSBDevice"}}}};
managedStore::KeyType keyGetObject(
managedStore::ManagedType::kManagedMapperObject,
"xyz.openbmc_project.EntityManager",
sdbusplus::message::object_path(
"/xyz/openbmc_project/inventory/system/board/name6_7_6/"
"name7_1_NIC_1"),
"xyz.openbmc_project.Configuration.MCTPUSBDevice", "MCTPUSBDevice");
std::shared_ptr<managedStore::ValueType> mockedObj =
CreateValueType(mctpMapperObj);
EXPECT_TRUE(
dynamic_cast<managedStore::MockSerializedManagedObjectStore*>(
managedStore::GetManagedObjectStore())
->upsertMockObjectIntoManagedStore(keyGetObject, mockedObj)
.ok());
handleComponentIntegrityGet(app_, CreateRequest(), share_async_resp_,
"CI_name6_7_6_name7_1_NIC_1");
RunIoUntilDone();
nlohmann::json& json = share_async_resp_->res.jsonValue;
EXPECT_EQ(share_async_resp_->res.result(), boost::beast::http::status::ok);
EXPECT_EQ(json["Links"]["ComponentsProtected"][0]["@odata.id"],
"/redfish/v1/Chassis/name6_7_6");
}
} // namespace
} // namespace redfish