diff --git a/examples/tv-app/android/java/AppImpl.cpp b/examples/tv-app/android/java/AppImpl.cpp index 90e88c86f3cd09..731a8d8fb50107 100644 --- a/examples/tv-app/android/java/AppImpl.cpp +++ b/examples/tv-app/android/java/AppImpl.cpp @@ -93,6 +93,11 @@ class MyPostCommissioningListener : public PostCommissioningListener MyPostCommissioningListener gMyPostCommissioningListener; ContentAppFactoryImpl gFactory; +ContentAppFactoryImpl * GetContentAppFactoryImpl() +{ + return &gFactory; +} + namespace chip { namespace AppPlatform { @@ -412,6 +417,24 @@ void ContentAppFactoryImpl::SendTestMessage(EndpointId epId, const char * messag } } +void ContentAppFactoryImpl::AddAdminVendorId(uint16_t vendorId) +{ + mAdminVendorIds.push_back(vendorId); +} + +Access::Privilege ContentAppFactoryImpl::GetVendorPrivilege(uint16_t vendorId) +{ + for (size_t i = 0; i < mAdminVendorIds.size(); ++i) + { + auto & vendor = mAdminVendorIds.at(i); + if (vendorId == vendor) + { + return Access::Privilege::kAdminister; + } + } + return Access::Privilege::kOperate; +} + } // namespace AppPlatform } // namespace chip @@ -448,6 +471,12 @@ CHIP_ERROR InitVideoPlayerPlatform(JNIMyUserPrompter * userPrompter, jobject con ChipLogProgress(AppServer, "Started commissioner"); #endif // CHIP_DEVICE_CONFIG_ENABLE_BOTH_COMMISSIONER_AND_COMMISSIONEE + + // Disable last fixed endpoint, which is used as a placeholder for all of the + // supported clusters so that ZAP will generated the requisite code. + ChipLogDetail(DeviceLayer, "TV App: Disabling Fixed Content App Endpoints"); + emberAfEndpointEnableDisable(3, false); + return CHIP_NO_ERROR; } diff --git a/examples/tv-app/android/java/AppImpl.h b/examples/tv-app/android/java/AppImpl.h index 6a5f919224ef0d..de3bbd6b5267af 100644 --- a/examples/tv-app/android/java/AppImpl.h +++ b/examples/tv-app/android/java/AppImpl.h @@ -142,6 +142,14 @@ class DLL_EXPORT ContentAppFactoryImpl : public ContentAppFactory // and then writes it to destinationApp CHIP_ERROR ConvertToPlatformCatalogVendorApp(const CatalogVendorApp & sourceApp, CatalogVendorApp * destinationApp) override; + // Get the privilege this vendorId should have on endpoints 1, 2, and content app endpoints + // In the case of casting video clients, this should usually be Access::Privilege::kOperate + // and for voice agents, this may be Access::Privilege::kAdminister + // When a vendor has admin privileges, it will get access to all clusters on ep1 + Access::Privilege GetVendorPrivilege(uint16_t vendorId) override; + + void AddAdminVendorId(uint16_t vendorId); + protected: std::vector mContentApps{ new ContentAppImpl("Vendor1", 1, "exampleid", 11, "Version1", "20202021", nullptr), @@ -149,9 +157,13 @@ class DLL_EXPORT ContentAppFactoryImpl : public ContentAppFactory new ContentAppImpl("Vendor3", 9050, "App3", 22, "Version3", "20202021", nullptr), new ContentAppImpl("TestSuiteVendor", 1111, "applicationId", 22, "v2", "20202021", nullptr) }; + + std::vector mAdminVendorIds{}; }; } // namespace AppPlatform } // namespace chip +chip::AppPlatform::ContentAppFactoryImpl * GetContentAppFactoryImpl(); + #endif // CHIP_DEVICE_CONFIG_APP_PLATFORM_ENABLED diff --git a/examples/tv-app/android/java/MediaPlaybackManager.cpp b/examples/tv-app/android/java/MediaPlaybackManager.cpp index 0bc9244b7b3d50..1c5fac5c2522ba 100644 --- a/examples/tv-app/android/java/MediaPlaybackManager.cpp +++ b/examples/tv-app/android/java/MediaPlaybackManager.cpp @@ -173,7 +173,7 @@ void MediaPlaybackManager::InitializeWithObjects(jobject managerObject) } mGetPositionMethod = - env->GetMethodID(mMediaPlaybackManagerClass, "getPosition", "()[Lcom/matter/tv/server/tvapp/MediaPlaybackPosition;"); + env->GetMethodID(mMediaPlaybackManagerClass, "getPosition", "()Lcom/matter/tv/server/tvapp/MediaPlaybackPosition;"); if (mGetPositionMethod == nullptr) { ChipLogError(Zcl, "Failed to access MediaPlaybackManager 'getPosition' method"); diff --git a/examples/tv-app/linux/AppImpl.cpp b/examples/tv-app/linux/AppImpl.cpp index 06c98290e8a5a9..101b4d03821883 100644 --- a/examples/tv-app/linux/AppImpl.cpp +++ b/examples/tv-app/linux/AppImpl.cpp @@ -119,6 +119,11 @@ class MyPostCommissioningListener : public PostCommissioningListener MyPostCommissioningListener gMyPostCommissioningListener; ContentAppFactoryImpl gFactory; +ContentAppFactoryImpl * GetContentAppFactoryImpl() +{ + return &gFactory; +} + namespace chip { namespace AppPlatform { @@ -383,6 +388,24 @@ ContentApp * ContentAppFactoryImpl::LoadContentApp(const CatalogVendorApp & vend return nullptr; } +void ContentAppFactoryImpl::AddAdminVendorId(uint16_t vendorId) +{ + mAdminVendorIds.push_back(vendorId); +} + +Access::Privilege ContentAppFactoryImpl::GetVendorPrivilege(uint16_t vendorId) +{ + for (size_t i = 0; i < mAdminVendorIds.size(); ++i) + { + auto & vendor = mAdminVendorIds.at(i); + if (vendorId == vendor) + { + return Access::Privilege::kAdminister; + } + } + return Access::Privilege::kOperate; +} + } // namespace AppPlatform } // namespace chip diff --git a/examples/tv-app/linux/AppImpl.h b/examples/tv-app/linux/AppImpl.h index a461ceac3e7c05..68c96d135e0630 100644 --- a/examples/tv-app/linux/AppImpl.h +++ b/examples/tv-app/linux/AppImpl.h @@ -125,6 +125,14 @@ class DLL_EXPORT ContentAppFactoryImpl : public ContentAppFactory // and then writes it to destinationApp CHIP_ERROR ConvertToPlatformCatalogVendorApp(const CatalogVendorApp & sourceApp, CatalogVendorApp * destinationApp) override; + // Get the privilege this vendorId should have on endpoints 1, 2, and content app endpoints + // In the case of casting video clients, this should usually be Access::Privilege::kOperate + // and for voice agents, this may be Access::Privilege::kAdminister + // When a vendor has admin privileges, it will get access to all clusters on ep1 + Access::Privilege GetVendorPrivilege(uint16_t vendorId) override; + + void AddAdminVendorId(uint16_t vendorId); + protected: ContentAppImpl mContentApps[APP_LIBRARY_SIZE] = { ContentAppImpl("Vendor1", 1, "exampleid", 11, "Version1", "34567890"), @@ -132,9 +140,13 @@ class DLL_EXPORT ContentAppFactoryImpl : public ContentAppFactory ContentAppImpl("Vendor3", 9050, "App3", 22, "Version3", "20202021"), ContentAppImpl("TestSuiteVendor", 1111, "applicationId", 22, "v2", "20202021") }; + + std::vector mAdminVendorIds{}; }; } // namespace AppPlatform } // namespace chip +chip::AppPlatform::ContentAppFactoryImpl * GetContentAppFactoryImpl(); + #endif // CHIP_DEVICE_CONFIG_APP_PLATFORM_ENABLED diff --git a/examples/tv-app/linux/AppPlatformShellCommands.cpp b/examples/tv-app/linux/AppPlatformShellCommands.cpp index 50cbc823d74e7d..b70c7b6f0d36b0 100644 --- a/examples/tv-app/linux/AppPlatformShellCommands.cpp +++ b/examples/tv-app/linux/AppPlatformShellCommands.cpp @@ -20,8 +20,10 @@ */ #include "AppPlatformShellCommands.h" +#include "AppImpl.h" #include "ControllerShellCommands.h" #include +#include #include #include #include @@ -39,6 +41,7 @@ using namespace ::chip::Controller; #if CHIP_DEVICE_CONFIG_APP_PLATFORM_ENABLED using namespace chip::AppPlatform; +using namespace chip::Access; #endif // CHIP_DEVICE_CONFIG_APP_PLATFORM_ENABLED using namespace chip::app::Clusters; @@ -103,6 +106,82 @@ static CHIP_ERROR pairApp(bool printHeader, size_t index) return CHIP_NO_ERROR; } +static CHIP_ERROR DumpAccessControlEntry(const Access::AccessControl::Entry & entry) +{ + CHIP_ERROR err; + + ChipLogDetail(DeviceLayer, "----- BEGIN ENTRY -----"); + + { + FabricIndex fabricIndex; + SuccessOrExit(err = entry.GetFabricIndex(fabricIndex)); + ChipLogDetail(DeviceLayer, "fabricIndex: %u", fabricIndex); + } + + { + Privilege privilege; + SuccessOrExit(err = entry.GetPrivilege(privilege)); + ChipLogDetail(DeviceLayer, "privilege: %d", to_underlying(privilege)); + } + + { + AuthMode authMode; + SuccessOrExit(err = entry.GetAuthMode(authMode)); + ChipLogDetail(DeviceLayer, "authMode: %d", to_underlying(authMode)); + } + + { + size_t count; + SuccessOrExit(err = entry.GetSubjectCount(count)); + if (count) + { + ChipLogDetail(DeviceLayer, "subjects: %u", static_cast(count)); + for (size_t i = 0; i < count; ++i) + { + NodeId subject; + SuccessOrExit(err = entry.GetSubject(i, subject)); + ChipLogDetail(DeviceLayer, " %u: 0x" ChipLogFormatX64, static_cast(i), ChipLogValueX64(subject)); + } + } + } + + { + size_t count; + SuccessOrExit(err = entry.GetTargetCount(count)); + if (count) + { + ChipLogDetail(DeviceLayer, "targets: %u", static_cast(count)); + for (size_t i = 0; i < count; ++i) + { + Access::AccessControl::Entry::Target target; + SuccessOrExit(err = entry.GetTarget(i, target)); + if (target.flags & Access::AccessControl::Entry::Target::kCluster) + { + ChipLogDetail(DeviceLayer, " %u: cluster: 0x" ChipLogFormatMEI, static_cast(i), + ChipLogValueMEI(target.cluster)); + } + if (target.flags & Access::AccessControl::Entry::Target::kEndpoint) + { + ChipLogDetail(DeviceLayer, " %u: endpoint: %u", static_cast(i), target.endpoint); + } + if (target.flags & Access::AccessControl::Entry::Target::kDeviceType) + { + ChipLogDetail(DeviceLayer, " %u: deviceType: 0x" ChipLogFormatMEI, static_cast(i), + ChipLogValueMEI(target.deviceType)); + } + } + } + } + + ChipLogDetail(DeviceLayer, "----- END ENTRY -----"); + + return CHIP_NO_ERROR; + +exit: + ChipLogError(DeviceLayer, "DumpAccessControlEntry: dump failed %" CHIP_ERROR_FORMAT, err.Format()); + return err; +} + static CHIP_ERROR PrintAllCommands() { streamer_t * sout = streamer_get(); @@ -114,6 +193,11 @@ static CHIP_ERROR PrintAllCommands() streamer_printf(sout, " commission Commission given udc-entry using given pincode from corresponding app. Usage: " "app commission 0\r\n"); + streamer_printf(sout, + " add-admin-vendor Add vendor ID to list which will receive admin privileges. Usage: app " + "add-admin-vendor 65521\r\n"); + streamer_printf(sout, " print-app-access Print all ACLs for app platform fabric. Usage: app print-app-access\r\n"); + streamer_printf(sout, " remove-app-access Remove all ACLs for app platform fabric. Usage: app remove-app-access\r\n"); streamer_printf(sout, "\r\n"); return CHIP_NO_ERROR; @@ -127,6 +211,22 @@ static CHIP_ERROR AppPlatformHandler(int argc, char ** argv) { return PrintAllCommands(); } + else if (strcmp(argv[0], "add-admin-vendor") == 0) + { + if (argc < 2) + { + return PrintAllCommands(); + } + char * eptr; + + uint16_t vid = (uint16_t) strtol(argv[1], &eptr, 10); + ContentAppFactoryImpl * factory = GetContentAppFactoryImpl(); + factory->AddAdminVendorId(vid); + + ChipLogProgress(DeviceLayer, "added admin-vendor"); + + return CHIP_NO_ERROR; + } else if (strcmp(argv[0], "add") == 0) { if (argc < 2) @@ -205,6 +305,23 @@ static CHIP_ERROR AppPlatformHandler(int argc, char ** argv) size_t index = (size_t) strtol(argv[1], &eptr, 10); return error = pairApp(true, index); } + else if (strcmp(argv[0], "print-app-access") == 0) + { + Access::AccessControl::EntryIterator iterator; + ReturnErrorOnFailure(Access::GetAccessControl().Entries(GetDeviceCommissioner()->GetFabricIndex(), iterator)); + + Access::AccessControl::Entry entry; + while (iterator.Next(entry) == CHIP_NO_ERROR) + { + DumpAccessControlEntry(entry); + } + return CHIP_NO_ERROR; + } + else if (strcmp(argv[0], "remove-app-access") == 0) + { + Access::GetAccessControl().DeleteAllEntriesForFabric(GetDeviceCommissioner()->GetFabricIndex()); + return CHIP_NO_ERROR; + } else { return CHIP_ERROR_INVALID_ARGUMENT; diff --git a/examples/tv-app/linux/main.cpp b/examples/tv-app/linux/main.cpp index 2236e7bee2d192..eec6a8054887b8 100644 --- a/examples/tv-app/linux/main.cpp +++ b/examples/tv-app/linux/main.cpp @@ -75,7 +75,16 @@ static TargetNavigatorManager targetNavigatorManager; static WakeOnLanManager wakeOnLanManager; } // namespace -void ApplicationInit() {} +void ApplicationInit() +{ + ChipLogProgress(Zcl, "TV Linux App: ApplicationInit()"); + + // Disable last fixed endpoint, which is used as a placeholder for all of the + // supported clusters so that ZAP will generated the requisite code. + ChipLogDetail(DeviceLayer, "TV Linux App: Warning - Fixed Content App Endpoint Not Disabled"); + // Can't disable this without breaking CI unit tests that act upon account login cluster (only available on ep3) + // emberAfEndpointEnableDisable(3, false); +} int main(int argc, char * argv[]) { diff --git a/src/app/app-platform/ContentAppPlatform.cpp b/src/app/app-platform/ContentAppPlatform.cpp index 222b52992246ab..5557c092dcb8b9 100644 --- a/src/app/app-platform/ContentAppPlatform.cpp +++ b/src/app/app-platform/ContentAppPlatform.cpp @@ -414,11 +414,13 @@ CHIP_ERROR ContentAppPlatform::ManageClientAccess(OperationalDeviceProxy * targe VerifyOrReturnError(successCb != nullptr, CHIP_ERROR_INVALID_ARGUMENT); VerifyOrReturnError(failureCb != nullptr, CHIP_ERROR_INVALID_ARGUMENT); + Access::Privilege vendorPrivilege = mContentAppFactory->GetVendorPrivilege(targetVendorId); + Access::AccessControl::Entry entry; ReturnErrorOnFailure(GetAccessControl().PrepareEntry(entry)); ReturnErrorOnFailure(entry.SetAuthMode(Access::AuthMode::kCase)); entry.SetFabricIndex(targetDeviceProxy->GetFabricIndex()); - ReturnErrorOnFailure(entry.SetPrivilege(Access::Privilege::kOperate)); + ReturnErrorOnFailure(entry.SetPrivilege(vendorPrivilege)); ReturnErrorOnFailure(entry.AddSubject(nullptr, targetDeviceProxy->GetDeviceId())); std::vector bindings; @@ -438,18 +440,31 @@ CHIP_ERROR ContentAppPlatform::ManageClientAccess(OperationalDeviceProxy * targe ChipLogProgress(Controller, "Create video player endpoint ACL and binding"); { - std::list allowedClusterList = { kClusterIdDescriptor, kClusterIdOnOff, kClusterIdWakeOnLAN, - kClusterIdMediaPlayback, kClusterIdLowPower, kClusterIdKeypadInput, - kClusterIdContentLauncher, kClusterIdAudioOutput }; - - for (const auto & clusterId : allowedClusterList) + if (vendorPrivilege == Access::Privilege::kAdminister) { - Access::AccessControl::Entry::Target target = { .flags = Access::AccessControl::Entry::Target::kCluster | - Access::AccessControl::Entry::Target::kEndpoint, - .cluster = clusterId, + ChipLogProgress(Controller, "ContentAppPlatform::ManageClientAccess Admin privilege granted"); + // a vendor with admin privilege gets access to all clusters on ep1 + Access::AccessControl::Entry::Target target = { .flags = Access::AccessControl::Entry::Target::kEndpoint, .endpoint = kLocalVideoPlayerEndpointId }; ReturnErrorOnFailure(entry.AddTarget(nullptr, target)); } + else + { + ChipLogProgress(Controller, "ContentAppPlatform::ManageClientAccess non-Admin privilege granted"); + // a vendor with non-admin privilege gets access to select clusters on ep1 + std::list allowedClusterList = { kClusterIdDescriptor, kClusterIdOnOff, kClusterIdWakeOnLAN, + kClusterIdMediaPlayback, kClusterIdLowPower, kClusterIdKeypadInput, + kClusterIdContentLauncher, kClusterIdAudioOutput }; + + for (const auto & clusterId : allowedClusterList) + { + Access::AccessControl::Entry::Target target = { .flags = Access::AccessControl::Entry::Target::kCluster | + Access::AccessControl::Entry::Target::kEndpoint, + .cluster = clusterId, + .endpoint = kLocalVideoPlayerEndpointId }; + ReturnErrorOnFailure(entry.AddTarget(nullptr, target)); + } + } bindings.push_back(Binding::Structs::TargetStruct::Type{ .node = MakeOptional(localNodeId), diff --git a/src/app/app-platform/ContentAppPlatform.h b/src/app/app-platform/ContentAppPlatform.h index af1e680c2000f2..4246914e223990 100644 --- a/src/app/app-platform/ContentAppPlatform.h +++ b/src/app/app-platform/ContentAppPlatform.h @@ -57,6 +57,12 @@ class DLL_EXPORT ContentAppFactory // Converts application (any catalog) into the platform's catalog Vendor // and then writes it to destinationApp virtual CHIP_ERROR ConvertToPlatformCatalogVendorApp(const CatalogVendorApp & sourceApp, CatalogVendorApp * destinationApp) = 0; + + // Get the privilege this vendorId should have on endpoints 1, 2, and content app endpoints + // In the case of casting video clients, this should usually be Access::Privilege::kOperate + // and for voice agents, this may be Access::Privilege::kAdminister + // When a vendor has admin privileges, it will get access to all clusters on ep1 + virtual Access::Privilege GetVendorPrivilege(uint16_t vendorId) = 0; }; class DLL_EXPORT ContentAppPlatform