diff --git a/src/app/BUILD.gn b/src/app/BUILD.gn index 53dd3876c12410..79a20235132133 100644 --- a/src/app/BUILD.gn +++ b/src/app/BUILD.gn @@ -60,6 +60,19 @@ declare_args() { current_os != "android" } +config("enhanced_setup_flow_config") { + defines = [] + if (chip_config_tc_required) { + defines += [ + "CHIP_CONFIG_TC_REQUIRED=1", + "CHIP_CONFIG_TC_REQUIRED_ACKNOWLEDGEMENTS=${chip_config_tc_required_acknowledgements}", + "CHIP_CONFIG_TC_REQUIRED_ACKNOWLEDGEMENTS_VERSION=${chip_config_tc_required_acknowledgements_version}", + ] + } else { + defines += [ "CHIP_CONFIG_TC_REQUIRED=0" ] + } +} + buildconfig_header("app_buildconfig") { header = "AppBuildConfig.h" header_dir = "app" @@ -519,5 +532,8 @@ static_library("app") { cflags = [ "-Wconversion" ] - public_configs = [ "${chip_root}/src:includes" ] + public_configs = [ + ":enhanced_setup_flow_config", + "${chip_root}/src:includes", + ] } diff --git a/src/app/FailSafeContext.cpp b/src/app/FailSafeContext.cpp index 95a5b267f2aa5e..9cb9fb411cc2e0 100644 --- a/src/app/FailSafeContext.cpp +++ b/src/app/FailSafeContext.cpp @@ -86,9 +86,14 @@ void FailSafeContext::ScheduleFailSafeCleanup(FabricIndex fabricIndex, bool addN SetFailSafeArmed(false); ChipDeviceEvent event{ .Type = DeviceEventType::kFailSafeTimerExpired, - .FailSafeTimerExpired = { .fabricIndex = fabricIndex, - .addNocCommandHasBeenInvoked = addNocCommandInvoked, - .updateNocCommandHasBeenInvoked = updateNocCommandInvoked } }; + .FailSafeTimerExpired = { + .fabricIndex = fabricIndex, + .addNocCommandHasBeenInvoked = addNocCommandInvoked, + .updateNocCommandHasBeenInvoked = updateNocCommandInvoked, +#if CHIP_CONFIG_TC_REQUIRED + .updateTermsAndConditionsHasBeenInvoked = mUpdateTermsAndConditionsHasBeenInvoked, +#endif + } }; CHIP_ERROR status = PlatformMgr().PostEvent(&event); if (status != CHIP_NO_ERROR) diff --git a/src/app/FailSafeContext.h b/src/app/FailSafeContext.h index 48e11e0845395b..421774a521450a 100644 --- a/src/app/FailSafeContext.h +++ b/src/app/FailSafeContext.h @@ -56,7 +56,9 @@ class FailSafeContext void SetUpdateNocCommandInvoked() { mUpdateNocCommandHasBeenInvoked = true; } void SetAddTrustedRootCertInvoked() { mAddTrustedRootCertHasBeenInvoked = true; } void SetCsrRequestForUpdateNoc(bool isForUpdateNoc) { mIsCsrRequestForUpdateNoc = isForUpdateNoc; } - +#if CHIP_CONFIG_TC_REQUIRED + void SetUpdateTermsAndConditionsHasBeenInvoked() { mUpdateTermsAndConditionsHasBeenInvoked = true; } +#endif /** * @brief * Schedules a work to cleanup the FailSafe Context asynchronously after various cleanup work @@ -91,6 +93,9 @@ class FailSafeContext bool UpdateNocCommandHasBeenInvoked() const { return mUpdateNocCommandHasBeenInvoked; } bool AddTrustedRootCertHasBeenInvoked() const { return mAddTrustedRootCertHasBeenInvoked; } bool IsCsrRequestForUpdateNoc() const { return mIsCsrRequestForUpdateNoc; } +#if CHIP_CONFIG_TC_REQUIRED + bool UpdateTermsAndConditionsHasBeenInvoked() { return mUpdateTermsAndConditionsHasBeenInvoked; } +#endif FabricIndex GetFabricIndex() const { @@ -110,7 +115,10 @@ class FailSafeContext bool mAddTrustedRootCertHasBeenInvoked = false; // The fact of whether a CSR occurred at all is stored elsewhere. bool mIsCsrRequestForUpdateNoc = false; - FabricIndex mFabricIndex = kUndefinedFabricIndex; +#if CHIP_CONFIG_TC_REQUIRED + bool mUpdateTermsAndConditionsHasBeenInvoked = false; +#endif + FabricIndex mFabricIndex = kUndefinedFabricIndex; /** * @brief @@ -145,6 +153,9 @@ class FailSafeContext mAddTrustedRootCertHasBeenInvoked = false; mFailSafeBusy = false; mIsCsrRequestForUpdateNoc = false; +#if CHIP_CONFIG_TC_REQUIRED + mUpdateTermsAndConditionsHasBeenInvoked = false; +#endif } void FailSafeTimerExpired(); diff --git a/src/app/clusters/general-commissioning-server/general-commissioning-server.cpp b/src/app/clusters/general-commissioning-server/general-commissioning-server.cpp index 4bf97face53740..6c1465873de433 100644 --- a/src/app/clusters/general-commissioning-server/general-commissioning-server.cpp +++ b/src/app/clusters/general-commissioning-server/general-commissioning-server.cpp @@ -1,6 +1,6 @@ /** * - * Copyright (c) 2021 Project CHIP Authors + * Copyright (c) 2021-2024 Project CHIP Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,6 +27,9 @@ #include #include #include +#if CHIP_CONFIG_TC_REQUIRED +#include +#endif #include #include #include @@ -69,6 +72,9 @@ class GeneralCommissioningAttrAccess : public AttributeAccessInterface CHIP_ERROR ReadIfSupported(CHIP_ERROR (ConfigurationManager::*getter)(uint8_t &), AttributeValueEncoder & aEncoder); CHIP_ERROR ReadBasicCommissioningInfo(AttributeValueEncoder & aEncoder); CHIP_ERROR ReadSupportsConcurrentConnection(AttributeValueEncoder & aEncoder); + template + CHIP_ERROR ReadFromProvider(Provider * const aProvider, CHIP_ERROR (Provider::*const aConstGetter)(T &) const, + AttributeValueEncoder & aEncoder); }; GeneralCommissioningAttrAccess gAttrAccess; @@ -95,6 +101,28 @@ CHIP_ERROR GeneralCommissioningAttrAccess::Read(const ConcreteReadAttributePath case SupportsConcurrentConnection::Id: { return ReadSupportsConcurrentConnection(aEncoder); } +#if CHIP_CONFIG_TC_REQUIRED + case TCAcceptedVersion::Id: { + auto provider = Server::GetInstance().GetEnhancedSetupFlowProvider(); + auto getter = &EnhancedSetupFlowProvider::GetTermsAndConditionsAcceptedAcknowledgementsVersion; + return ReadFromProvider(provider, getter, aEncoder); + } + case TCMinRequiredVersion::Id: { + auto provider = Server::GetInstance().GetEnhancedSetupFlowProvider(); + auto getter = &EnhancedSetupFlowProvider::GetTermsAndConditionsRequiredAcknowledgementsVersion; + return ReadFromProvider(provider, getter, aEncoder); + } + case TCAcknowledgements::Id: { + auto provider = Server::GetInstance().GetEnhancedSetupFlowProvider(); + auto getter = &EnhancedSetupFlowProvider::GetTermsAndConditionsAcceptedAcknowledgements; + return ReadFromProvider(provider, getter, aEncoder); + } + case TCAcknowledgementsRequired::Id: { + auto provider = Server::GetInstance().GetEnhancedSetupFlowProvider(); + auto getter = &EnhancedSetupFlowProvider::IsTermsAndConditionsAcceptanceRequired; + return ReadFromProvider(provider, getter, aEncoder); + } +#endif default: { break; } @@ -144,6 +172,94 @@ CHIP_ERROR GeneralCommissioningAttrAccess::ReadSupportsConcurrentConnection(Attr return aEncoder.Encode(supportsConcurrentConnection); } +template +CHIP_ERROR GeneralCommissioningAttrAccess::ReadFromProvider(Provider * const aProvider, + CHIP_ERROR (Provider::*const aConstGetter)(T &) const, + AttributeValueEncoder & aEncoder) +{ + if (nullptr == aProvider) + { + return CHIP_ERROR_PERSISTED_STORAGE_FAILED; + } + + T value; + CHIP_ERROR err = (aProvider->*aConstGetter)(value); + if (err != CHIP_NO_ERROR) + { + return err; + } + + return aEncoder.Encode(value); +} + +#if CHIP_CONFIG_TC_REQUIRED +CHIP_ERROR checkTermsAndConditionsAcknowledgementsState(CommissioningErrorEnum & errorCode) +{ + EnhancedSetupFlowProvider * enhancedSetupFlowProvider = Server::GetInstance().GetEnhancedSetupFlowProvider(); + + CHIP_ERROR err; + + uint16_t termsAndConditionsAcceptedAcknowledgements; + bool hasAnyAcknowledgements; + bool hasRequiredTermAccepted; + bool hasRequiredTermVersionAccepted; + + err = enhancedSetupFlowProvider->HasReceivedTermsAndConditionscknowledgements(hasAnyAcknowledgements); + if (!::chip::ChipError::IsSuccess(err)) + { + ChipLogError(AppServer, "Failed to HasReceivedTermsAndConditionscknowledgements"); + errorCode = CommissioningErrorEnum::kTCAcknowledgementsNotReceived; + return err; + } + + err = enhancedSetupFlowProvider->GetTermsAndConditionsAcceptedAcknowledgements(termsAndConditionsAcceptedAcknowledgements); + if (!::chip::ChipError::IsSuccess(err)) + { + ChipLogError(AppServer, "Failed to GetTermsAndConditionsAcceptedAcknowledgements"); + return err; + } + + err = enhancedSetupFlowProvider->HasTermsAndConditionsRequiredAcknowledgementsBeenAccepted(hasRequiredTermAccepted); + if (!::chip::ChipError::IsSuccess(err)) + { + ChipLogError(AppServer, "Failed to HasTermsAndConditionsRequiredAcknowledgementsBeenAccepted"); + return err; + } + + err = + enhancedSetupFlowProvider->HasTermsAndConditionsRequiredAcknowledgementsVersionBeenAccepted(hasRequiredTermVersionAccepted); + if (!::chip::ChipError::IsSuccess(err)) + { + ChipLogError(AppServer, "Failed to HasTermsAndConditionsRequiredAcknowledgementsVersionBeenAccepted"); + return err; + } + + if (!hasRequiredTermVersionAccepted) + { + uint16_t requiredAcknowledgementsVersion = 0; + (void) enhancedSetupFlowProvider->GetTermsAndConditionsRequiredAcknowledgementsVersion(requiredAcknowledgementsVersion); + ChipLogProgress(AppServer, "Minimum terms and conditions version, 0x%04x, has not been accepted", + requiredAcknowledgementsVersion); + errorCode = CommissioningErrorEnum::kTCMinVersionNotMet; + return CHIP_NO_ERROR; + } + + if (!hasRequiredTermAccepted) + { + uint16_t requiredAcknowledgements = 0; + (void) enhancedSetupFlowProvider->GetTermsAndConditionsRequiredAcknowledgements(requiredAcknowledgements); + + ChipLogProgress(AppServer, "Required terms and conditions, 0x%04x,have not been accepted", requiredAcknowledgements); + errorCode = (0 == termsAndConditionsAcceptedAcknowledgements) ? CommissioningErrorEnum::kTCAcknowledgementsNotReceived + : CommissioningErrorEnum::kRequiredTCNotAccepted; + return CHIP_NO_ERROR; + } + + errorCode = CommissioningErrorEnum::kOk; + return CHIP_NO_ERROR; +} +#endif + } // anonymous namespace bool emberAfGeneralCommissioningClusterArmFailSafeCallback(app::CommandHandler * commandObj, @@ -239,6 +355,10 @@ bool emberAfGeneralCommissioningClusterCommissioningCompleteCallback( } else { +#if CHIP_CONFIG_TC_REQUIRED + CheckSuccess(checkTermsAndConditionsAcknowledgementsState(response.errorCode), Failure); +#endif + if (failSafe.NocCommandHasBeenInvoked()) { CHIP_ERROR err = fabricTable.CommitPendingFabricData(); @@ -328,6 +448,30 @@ bool emberAfGeneralCommissioningClusterSetRegulatoryConfigCallback(app::CommandH return true; } +bool emberAfGeneralCommissioningClusterSetTCAcknowledgementsCallback( + chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath, + const chip::app::Clusters::GeneralCommissioning::Commands::SetTCAcknowledgements::DecodableType & commandData) +{ +#if !CHIP_CONFIG_TC_REQUIRED + return false; + +#else + auto & failSafeContext = Server::GetInstance().GetFailSafeContext(); + + MATTER_TRACE_SCOPE("SetTCAcknowledgements", "GeneralCommissioning"); + Commands::SetTCAcknowledgementsResponse::Type response; + EnhancedSetupFlowProvider * const enhancedSetupFlowProvider = Server::GetInstance().GetEnhancedSetupFlowProvider(); + uint16_t acknowledgements = commandData.TCUserResponse; + uint16_t acknowledgementsVersion = commandData.TCVersion; + CheckSuccess(enhancedSetupFlowProvider->SetTermsAndConditionsAcceptance(acknowledgements, acknowledgementsVersion), Failure); + CheckSuccess(checkTermsAndConditionsAcknowledgementsState(response.errorCode), Failure); + failSafeContext.SetUpdateTermsAndConditionsHasBeenInvoked(); + commandObj->AddResponse(commandPath, response); + return true; + +#endif +} + namespace { void OnPlatformEventHandler(const DeviceLayer::ChipDeviceEvent * event, intptr_t arg) { @@ -335,6 +479,26 @@ void OnPlatformEventHandler(const DeviceLayer::ChipDeviceEvent * event, intptr_t { // Spec says to reset Breadcrumb attribute to 0. Breadcrumb::Set(0, 0); + +#if CHIP_CONFIG_TC_REQUIRED + if (event->FailSafeTimerExpired.updateTermsAndConditionsHasBeenInvoked) + { + // Clear terms and conditions acceptance on failsafe timer expiration + Server::GetInstance().GetEnhancedSetupFlowProvider()->RevertTermsAndConditionsAcceptance(); + } +#endif + } + + if (event->Type == DeviceLayer::DeviceEventType::kCommissioningComplete) + { +#if CHIP_CONFIG_TC_REQUIRED + auto & failSafeContext = Server::GetInstance().GetFailSafeContext(); + if (failSafeContext.UpdateTermsAndConditionsHasBeenInvoked()) + { + // Commit terms and conditions acceptance on commissioning complete + Server::GetInstance().GetEnhancedSetupFlowProvider()->CommitTermsAndConditionsAcceptance(); + } +#endif } } diff --git a/src/app/common_flags.gni b/src/app/common_flags.gni index be1149b2b67eb5..34e8549a35d1cd 100644 --- a/src/app/common_flags.gni +++ b/src/app/common_flags.gni @@ -18,6 +18,10 @@ declare_args() { chip_enable_read_client = true chip_build_controller_dynamic_server = false + chip_config_tc_required = false + chip_config_tc_required_acknowledgements = 0 + chip_config_tc_required_acknowledgements_version = 0 + # Flag that controls whether the time-to-wait from BUSY responses is # communicated to OperationalSessionSetup API consumers. chip_enable_busy_handling_for_operational_session_setup = true diff --git a/src/app/server/BUILD.gn b/src/app/server/BUILD.gn index 401356d7b753a4..3fb9bc5e405762 100644 --- a/src/app/server/BUILD.gn +++ b/src/app/server/BUILD.gn @@ -1,4 +1,4 @@ -# Copyright (c) 2020 Project CHIP Authors +# Copyright (c) 2020-2024 Project CHIP Authors # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -24,6 +24,28 @@ config("server_config") { } } +source_set("enhanced_setup_flow_interface") { + sources = [ + "DefaultEnhancedSetupFlowProvider.h", + "DefaultTermsAndConditionsProvider.h", + "EnhancedSetupFlowProvider.h", + "TermsAndConditionsProvider.h", + ] + + public_configs = [ "${chip_root}/src/app:enhanced_setup_flow_config" ] + + public_deps = [ "${chip_root}/src/lib/core" ] +} + +source_set("enhanced_setup_flow") { + sources = [ + "DefaultEnhancedSetupFlowProvider.cpp", + "DefaultTermsAndConditionsProvider.cpp", + ] + + deps = [ ":enhanced_setup_flow_interface" ] +} + static_library("server") { output_name = "libCHIPAppServer" @@ -51,6 +73,7 @@ static_library("server") { cflags = [ "-Wconversion" ] public_deps = [ + ":enhanced_setup_flow_interface", "${chip_root}/src/app", "${chip_root}/src/app:test-event-trigger", "${chip_root}/src/app/icd/server:check-in-back-off", @@ -79,4 +102,8 @@ static_library("server") { [ "${chip_root}/src/app/icd/server:default-check-in-back-off" ] } } + + if (chip_config_tc_required) { + public_deps += [ ":enhanced_setup_flow" ] + } } diff --git a/src/app/server/DefaultEnhancedSetupFlowProvider.cpp b/src/app/server/DefaultEnhancedSetupFlowProvider.cpp new file mode 100644 index 00000000000000..9bcd0e4dc1acbc --- /dev/null +++ b/src/app/server/DefaultEnhancedSetupFlowProvider.cpp @@ -0,0 +1,173 @@ +/* + * + * Copyright (c) 2024 Project CHIP Authors + * All rights reserved. + * + * 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. + */ + +#include "DefaultEnhancedSetupFlowProvider.h" + +#include +#include + +CHIP_ERROR chip::app::DefaultEnhancedSetupFlowProvider::Init(TermsAndConditionsProvider * const inTermsAndConditionsProvider) +{ + VerifyOrReturnError(nullptr != inTermsAndConditionsProvider, CHIP_ERROR_INVALID_ARGUMENT); + + mTermsAndConditionsProvider = inTermsAndConditionsProvider; + + return CHIP_NO_ERROR; +} + +CHIP_ERROR chip::app::DefaultEnhancedSetupFlowProvider::HasReceivedTermsAndConditionscknowledgements(bool & outReceived) const +{ + VerifyOrReturnError(nullptr != mTermsAndConditionsProvider, CHIP_ERROR_UNINITIALIZED); + ReturnErrorOnFailure(mTermsAndConditionsProvider->HasAcceptance(outReceived)); + return CHIP_NO_ERROR; +} + +CHIP_ERROR +chip::app::DefaultEnhancedSetupFlowProvider::HasTermsAndConditionsRequiredAcknowledgementsBeenAccepted(bool & outAccepted) const +{ + uint16_t requiredAcknowledgements; + uint16_t requiredAcknowledgementsVersion; + uint16_t acceptedAcknowledgements; + uint16_t acceptedAcknowledgementsVersion; + + VerifyOrReturnError(nullptr != mTermsAndConditionsProvider, CHIP_ERROR_UNINITIALIZED); + ReturnErrorOnFailure(mTermsAndConditionsProvider->GetRequirements(requiredAcknowledgements, requiredAcknowledgementsVersion)); + + if (0 == requiredAcknowledgements) + { + outAccepted = true; + return CHIP_NO_ERROR; + } + + ReturnErrorOnFailure(mTermsAndConditionsProvider->GetAcceptance(acceptedAcknowledgements, acceptedAcknowledgementsVersion)); + + outAccepted = ((requiredAcknowledgements & acceptedAcknowledgements) == requiredAcknowledgements); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR chip::app::DefaultEnhancedSetupFlowProvider::HasTermsAndConditionsRequiredAcknowledgementsVersionBeenAccepted( + bool & outAccepted) const +{ + uint16_t requiredAcknowledgements; + uint16_t requiredAcknowledgementsVersion; + uint16_t acceptedAcknowledgements; + uint16_t acceptedAcknowledgementsVersion; + + VerifyOrReturnError(nullptr != mTermsAndConditionsProvider, CHIP_ERROR_UNINITIALIZED); + ReturnErrorOnFailure(mTermsAndConditionsProvider->GetRequirements(requiredAcknowledgements, requiredAcknowledgementsVersion)); + + if (0 == requiredAcknowledgementsVersion) + { + outAccepted = true; + return CHIP_NO_ERROR; + } + + ReturnErrorOnFailure(mTermsAndConditionsProvider->GetAcceptance(acceptedAcknowledgements, acceptedAcknowledgementsVersion)); + + outAccepted = (acceptedAcknowledgementsVersion >= requiredAcknowledgementsVersion); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR chip::app::DefaultEnhancedSetupFlowProvider::IsTermsAndConditionsAcceptanceRequired(bool & outValue) const +{ + // Default implementation requires terms and conditions check only if not previously accepted. Other implementations may skip + // requiring a terms and conditions check on secondary commissioning, in the case that the required terms and conditions may + // have changed. + return HasTermsAndConditionsRequiredAcknowledgementsVersionBeenAccepted(outValue); +} + +CHIP_ERROR chip::app::DefaultEnhancedSetupFlowProvider::GetTermsAndConditionsRequiredAcknowledgements(uint16_t & outValue) const +{ + uint16_t requiredAcknowledgements; + uint16_t requiredAcknowledgementsVersion; + + VerifyOrReturnError(nullptr != mTermsAndConditionsProvider, CHIP_ERROR_UNINITIALIZED); + ReturnErrorOnFailure(mTermsAndConditionsProvider->GetRequirements(requiredAcknowledgements, requiredAcknowledgementsVersion)); + + outValue = requiredAcknowledgements; + + return CHIP_NO_ERROR; +} + +CHIP_ERROR +chip::app::DefaultEnhancedSetupFlowProvider::GetTermsAndConditionsRequiredAcknowledgementsVersion(uint16_t & outValue) const +{ + uint16_t requiredAcknowledgements; + uint16_t requiredAcknowledgementsVersion; + + VerifyOrReturnError(nullptr != mTermsAndConditionsProvider, CHIP_ERROR_UNINITIALIZED); + ReturnErrorOnFailure(mTermsAndConditionsProvider->GetRequirements(requiredAcknowledgements, requiredAcknowledgementsVersion)); + + outValue = requiredAcknowledgementsVersion; + + return CHIP_NO_ERROR; +} + +CHIP_ERROR chip::app::DefaultEnhancedSetupFlowProvider::GetTermsAndConditionsAcceptedAcknowledgements(uint16_t & outValue) const +{ + uint16_t acceptedAcknowledgements; + uint16_t acceptedAcknowledgementsVersion; + + VerifyOrReturnError(nullptr != mTermsAndConditionsProvider, CHIP_ERROR_UNINITIALIZED); + ReturnErrorOnFailure(mTermsAndConditionsProvider->GetAcceptance(acceptedAcknowledgements, acceptedAcknowledgementsVersion)); + + outValue = acceptedAcknowledgements; + + return CHIP_NO_ERROR; +} + +CHIP_ERROR +chip::app::DefaultEnhancedSetupFlowProvider::GetTermsAndConditionsAcceptedAcknowledgementsVersion(uint16_t & outValue) const +{ + uint16_t acceptedAcknowledgements; + uint16_t acceptedAcknowledgementsVersion; + + VerifyOrReturnError(nullptr != mTermsAndConditionsProvider, CHIP_ERROR_UNINITIALIZED); + ReturnErrorOnFailure(mTermsAndConditionsProvider->GetAcceptance(acceptedAcknowledgements, acceptedAcknowledgementsVersion)); + + outValue = acceptedAcknowledgementsVersion; + + return CHIP_NO_ERROR; +} + +CHIP_ERROR chip::app::DefaultEnhancedSetupFlowProvider::SetTermsAndConditionsAcceptance(uint16_t inTCAcknowledgementsValue, + uint16_t inTCAcknowledgementsVersionValue) +{ + VerifyOrReturnError(nullptr != mTermsAndConditionsProvider, CHIP_ERROR_UNINITIALIZED); + ReturnErrorOnFailure(mTermsAndConditionsProvider->SetAcceptance(inTCAcknowledgementsValue, inTCAcknowledgementsVersionValue)); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR chip::app::DefaultEnhancedSetupFlowProvider::RevertTermsAndConditionsAcceptance() +{ + VerifyOrReturnError(nullptr != mTermsAndConditionsProvider, CHIP_ERROR_UNINITIALIZED); + ReturnErrorOnFailure(mTermsAndConditionsProvider->RevertAcceptance()); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR chip::app::DefaultEnhancedSetupFlowProvider::CommitTermsAndConditionsAcceptance() +{ + VerifyOrReturnError(nullptr != mTermsAndConditionsProvider, CHIP_ERROR_UNINITIALIZED); + ReturnErrorOnFailure(mTermsAndConditionsProvider->CommitAcceptance()); + + return CHIP_NO_ERROR; +} diff --git a/src/app/server/DefaultEnhancedSetupFlowProvider.h b/src/app/server/DefaultEnhancedSetupFlowProvider.h new file mode 100644 index 00000000000000..01a04269723b79 --- /dev/null +++ b/src/app/server/DefaultEnhancedSetupFlowProvider.h @@ -0,0 +1,69 @@ +/* + * + * Copyright (c) 2024 Project CHIP Authors + * All rights reserved. + * + * 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. + */ + +#pragma once + +#include "EnhancedSetupFlowProvider.h" + +#include + +#include +#include + +#include "TermsAndConditionsProvider.h" + +namespace chip { +namespace app { +class DefaultEnhancedSetupFlowProvider : public EnhancedSetupFlowProvider +{ +public: + /** + * @brief Initializes the EnhancedSetupFlowProvider. + * + * @param[in] inTermsAndConditionsProvider The terms and conditions provide dependency. + */ + CHIP_ERROR Init(TermsAndConditionsProvider * const inTermsAndConditionsProvider); + + CHIP_ERROR HasReceivedTermsAndConditionscknowledgements(bool & outReceived) const override; + + CHIP_ERROR HasTermsAndConditionsRequiredAcknowledgementsBeenAccepted(bool & outAccepted) const override; + + CHIP_ERROR HasTermsAndConditionsRequiredAcknowledgementsVersionBeenAccepted(bool & outAccepted) const override; + + CHIP_ERROR IsTermsAndConditionsAcceptanceRequired(bool & outValue) const override; + + CHIP_ERROR GetTermsAndConditionsRequiredAcknowledgements(uint16_t & outValue) const override; + + CHIP_ERROR GetTermsAndConditionsRequiredAcknowledgementsVersion(uint16_t & outValue) const override; + + CHIP_ERROR GetTermsAndConditionsAcceptedAcknowledgements(uint16_t & outValue) const override; + + CHIP_ERROR GetTermsAndConditionsAcceptedAcknowledgementsVersion(uint16_t & outValue) const override; + + CHIP_ERROR SetTermsAndConditionsAcceptance(uint16_t aTCAcknowledgements, uint16_t inTCAcknowledgementsVersionValue) override; + + CHIP_ERROR CommitTermsAndConditionsAcceptance() override; + + CHIP_ERROR RevertTermsAndConditionsAcceptance() override; + +private: + TermsAndConditionsProvider * mTermsAndConditionsProvider; +}; + +} // namespace app +} // namespace chip diff --git a/src/app/server/DefaultTermsAndConditionsProvider.cpp b/src/app/server/DefaultTermsAndConditionsProvider.cpp new file mode 100644 index 00000000000000..b32a507b3c52fa --- /dev/null +++ b/src/app/server/DefaultTermsAndConditionsProvider.cpp @@ -0,0 +1,223 @@ +/* + * + * Copyright (c) 2024 Project CHIP Authors + * All rights reserved. + * + * 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. + */ + +#include "DefaultTermsAndConditionsProvider.h" + +#include +#include +#include +#include +#include +#include + +namespace { +constexpr chip::TLV::Tag kSerializationVersionTag = chip::TLV::ContextTag(1); +constexpr chip::TLV::Tag kAcceptedAcknowledgementsTag = chip::TLV::ContextTag(2); +constexpr chip::TLV::Tag kAcceptedAcknowledgementsVersionTag = chip::TLV::ContextTag(3); +constexpr uint8_t kSerializationVersion = 1; + +constexpr size_t kEstimatedTlvBufferSize = chip::TLV::EstimateStructOverhead(sizeof(uint8_t), // SerializationVersion + sizeof(uint16_t), // AcceptedAcknowledgements + sizeof(uint16_t) // AcceptedAcknowledgementsVersion + ) * + 2; // Extra space for rollback compatibility +} // namespace + +CHIP_ERROR chip::app::DefaultTermsAndConditionsProvider::Init(chip::PersistentStorageDelegate * const inPersistentStorageDelegate, + uint16_t inRequiredAcknowledgementsValue, + uint16_t inRequiredAcknowledgementsVersionValue) +{ + VerifyOrReturnError(nullptr != inPersistentStorageDelegate, CHIP_ERROR_INVALID_ARGUMENT); + + mPersistentStorageDelegate = inPersistentStorageDelegate; + mRequiredAcknowledgementsValue = inRequiredAcknowledgementsValue; + mRequiredAcknowledgementsVersionValue = inRequiredAcknowledgementsVersionValue; + + uint16_t acknowledgementsAcceptanceValue = 0; + uint16_t acknowledgementsAcceptanceVersionValue = 0; + + if (CHIP_NO_ERROR == LoadAcceptance(acknowledgementsAcceptanceValue, acknowledgementsAcceptanceVersionValue)) + { + mLatchedAcknowledgementsAcceptanceValue.SetValue(acknowledgementsAcceptanceValue); + mLatchedAcknowledgementsAcceptanceVersionValue.SetValue(acknowledgementsAcceptanceVersionValue); + mTemporaryAcknowledgementsAcceptanceValue = mLatchedAcknowledgementsAcceptanceValue; + mTemporaryAcknowledgementsAcceptanceVersionValue = mLatchedAcknowledgementsAcceptanceVersionValue; + } + + return CHIP_NO_ERROR; +} + +CHIP_ERROR chip::app::DefaultTermsAndConditionsProvider::RevertAcceptance() +{ + mTemporaryAcknowledgementsAcceptanceValue = mLatchedAcknowledgementsAcceptanceValue; + mTemporaryAcknowledgementsAcceptanceVersionValue = mLatchedAcknowledgementsAcceptanceVersionValue; + + return CHIP_NO_ERROR; +} + +CHIP_ERROR chip::app::DefaultTermsAndConditionsProvider::CommitAcceptance() +{ + VerifyOrReturnError(mLatchedAcknowledgementsAcceptanceValue.HasValue() || mTemporaryAcknowledgementsAcceptanceValue.HasValue(), + CHIP_ERROR_INCORRECT_STATE); + VerifyOrReturnError(mLatchedAcknowledgementsAcceptanceVersionValue.HasValue() || + mTemporaryAcknowledgementsAcceptanceVersionValue.HasValue(), + CHIP_ERROR_INCORRECT_STATE); + + uint16_t acknowledgementsAcceptanceValue = + mTemporaryAcknowledgementsAcceptanceValue.ValueOr(mLatchedAcknowledgementsAcceptanceValue.HasValue()); + uint16_t acknowledgementsAcceptanceVersionValue = + mTemporaryAcknowledgementsAcceptanceVersionValue.ValueOr(mLatchedAcknowledgementsAcceptanceVersionValue.HasValue()); + + CHIP_ERROR err = StoreAcceptance(acknowledgementsAcceptanceValue, acknowledgementsAcceptanceVersionValue); + + if (CHIP_NO_ERROR == err) + { + mLatchedAcknowledgementsAcceptanceValue.SetValue(acknowledgementsAcceptanceValue); + mLatchedAcknowledgementsAcceptanceVersionValue.SetValue(acknowledgementsAcceptanceVersionValue); + } + + return err; +} + +CHIP_ERROR chip::app::DefaultTermsAndConditionsProvider::GetAcceptance(uint16_t & outAcknowledgementsValue, + uint16_t & outAcknowledgementsVersionValue) const +{ + VerifyOrReturnError(mTemporaryAcknowledgementsAcceptanceValue.HasValue(), CHIP_ERROR_INCORRECT_STATE); + VerifyOrReturnError(mTemporaryAcknowledgementsAcceptanceVersionValue.HasValue(), CHIP_ERROR_INCORRECT_STATE); + + outAcknowledgementsValue = mTemporaryAcknowledgementsAcceptanceValue.Value(); + outAcknowledgementsVersionValue = mTemporaryAcknowledgementsAcceptanceVersionValue.Value(); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR chip::app::DefaultTermsAndConditionsProvider::GetRequirements(uint16_t & outAcknowledgementsValue, + uint16_t & outAcknowledgementsVersionValue) const +{ + outAcknowledgementsValue = mRequiredAcknowledgementsValue; + outAcknowledgementsVersionValue = mRequiredAcknowledgementsVersionValue; + + return CHIP_NO_ERROR; +} + +CHIP_ERROR chip::app::DefaultTermsAndConditionsProvider::HasAcceptance(bool & outHasAcceptance) const +{ + outHasAcceptance = + mTemporaryAcknowledgementsAcceptanceValue.HasValue() && mTemporaryAcknowledgementsAcceptanceVersionValue.HasValue(); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR chip::app::DefaultTermsAndConditionsProvider::ResetAcceptance() +{ + VerifyOrReturnError(nullptr != mPersistentStorageDelegate, CHIP_ERROR_UNINITIALIZED); + + const chip::StorageKeyName storageKey = DefaultStorageKeyAllocator::TermsAndConditionsAcceptance(); + + ReturnErrorOnFailure(mPersistentStorageDelegate->SyncDeleteKeyValue(storageKey.KeyName())); + mLatchedAcknowledgementsAcceptanceValue.ClearValue(); + mLatchedAcknowledgementsAcceptanceVersionValue.ClearValue(); + ReturnErrorOnFailure(RevertAcceptance()); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR chip::app::DefaultTermsAndConditionsProvider::SetAcceptance(uint16_t inAcceptedAcknowledgementsValue, + uint16_t inAcceptedAcknowledgementsVersionValue) +{ + mTemporaryAcknowledgementsAcceptanceValue.SetValue(inAcceptedAcknowledgementsValue); + mTemporaryAcknowledgementsAcceptanceVersionValue.SetValue(inAcceptedAcknowledgementsVersionValue); + return CHIP_NO_ERROR; +} + +CHIP_ERROR chip::app::DefaultTermsAndConditionsProvider::LoadAcceptance(uint16_t & outAcknowledgementsValue, + uint16_t & outAcknowledgementsVersionValue) +{ + uint8_t serializationVersion = 0; + uint16_t acknowledgements = 0; + uint16_t acknowledgementsVersion = 0; + + chip::TLV::TLVReader tlvReader; + chip::TLV::TLVType tlvContainer; + + uint8_t buffer[kEstimatedTlvBufferSize] = { 0 }; + uint16_t bufferSize = sizeof(buffer); + + VerifyOrReturnError(nullptr != mPersistentStorageDelegate, CHIP_ERROR_UNINITIALIZED); + + const chip::StorageKeyName storageKey = DefaultStorageKeyAllocator::TermsAndConditionsAcceptance(); + CHIP_ERROR err = mPersistentStorageDelegate->SyncGetKeyValue(storageKey.KeyName(), &buffer, bufferSize); + if (CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND == err) + { + outAcknowledgementsValue = acknowledgements; + outAcknowledgementsVersionValue = acknowledgementsVersion; + + return CHIP_NO_ERROR; + } + + VerifyOrReturnError(CHIP_NO_ERROR == err, err); + + tlvReader.Init(buffer, bufferSize); + ReturnErrorOnFailure(tlvReader.Next(chip::TLV::kTLVType_Structure, chip::TLV::AnonymousTag())); + ReturnErrorOnFailure(tlvReader.EnterContainer(tlvContainer)); + ReturnErrorOnFailure(tlvReader.Next(kSerializationVersionTag)); + ReturnErrorOnFailure(tlvReader.Get(serializationVersion)); + ReturnErrorOnFailure(tlvReader.Next(kAcceptedAcknowledgementsTag)); + ReturnErrorOnFailure(tlvReader.Get(acknowledgements)); + ReturnErrorOnFailure(tlvReader.Next(kAcceptedAcknowledgementsVersionTag)); + ReturnErrorOnFailure(tlvReader.Get(acknowledgementsVersion)); + ReturnErrorOnFailure(tlvReader.ExitContainer(tlvContainer)); + + if (kSerializationVersion != serializationVersion) + { + return CHIP_ERROR_VERSION_MISMATCH; + } + + outAcknowledgementsValue = acknowledgements; + outAcknowledgementsVersionValue = acknowledgementsVersion; + + return CHIP_NO_ERROR; +} + +CHIP_ERROR chip::app::DefaultTermsAndConditionsProvider::StoreAcceptance(uint16_t inAcknowledgementsValue, + uint16_t inAcknowledgementsVersionValue) +{ + uint8_t buffer[kEstimatedTlvBufferSize] = { 0 }; + chip::TLV::TLVWriter tlvWriter; + chip::TLV::TLVType tlvContainer; + + VerifyOrReturnError(nullptr != mPersistentStorageDelegate, CHIP_ERROR_UNINITIALIZED); + VerifyOrReturnError(mTemporaryAcknowledgementsAcceptanceValue.HasValue(), CHIP_ERROR_INCORRECT_STATE); + VerifyOrReturnError(mTemporaryAcknowledgementsAcceptanceVersionValue.HasValue(), CHIP_ERROR_INCORRECT_STATE); + + tlvWriter.Init(buffer, sizeof(buffer)); + ReturnErrorOnFailure(tlvWriter.StartContainer(chip::TLV::AnonymousTag(), chip::TLV::kTLVType_Structure, tlvContainer)); + ReturnErrorOnFailure(tlvWriter.Put(kSerializationVersionTag, kSerializationVersion)); + ReturnErrorOnFailure(tlvWriter.Put(kAcceptedAcknowledgementsTag, inAcknowledgementsValue)); + ReturnErrorOnFailure(tlvWriter.Put(kAcceptedAcknowledgementsVersionTag, inAcknowledgementsVersionValue)); + ReturnErrorOnFailure(tlvWriter.EndContainer(tlvContainer)); + ReturnErrorOnFailure(tlvWriter.Finalize()); + uint32_t lengthWritten = tlvWriter.GetLengthWritten(); + VerifyOrReturnError(CanCastTo(lengthWritten), CHIP_ERROR_BUFFER_TOO_SMALL); + + const chip::StorageKeyName storageKey = DefaultStorageKeyAllocator::TermsAndConditionsAcceptance(); + ReturnErrorOnFailure( + mPersistentStorageDelegate->SyncSetKeyValue(storageKey.KeyName(), buffer, static_cast(lengthWritten))); + + return CHIP_NO_ERROR; +} diff --git a/src/app/server/DefaultTermsAndConditionsProvider.h b/src/app/server/DefaultTermsAndConditionsProvider.h new file mode 100644 index 00000000000000..c68293d5143642 --- /dev/null +++ b/src/app/server/DefaultTermsAndConditionsProvider.h @@ -0,0 +1,75 @@ +/* + * + * Copyright (c) 2024 Project CHIP Authors + * All rights reserved. + * + * 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. + */ + +#pragma once + +#include "TermsAndConditionsProvider.h" + +#include + +#include +#include +#include + +namespace chip { +namespace app { +class DefaultTermsAndConditionsProvider : public TermsAndConditionsProvider +{ +public: + /** + * @brief Initializes the TermsAndConditionsProvider. + * + * @param[in] inPersistentStorageDelegate Persistent storage delegate dependency. + * @param[in] inRequiredAcknowledgementsValue The bitmask of required acknowledgements. + * @param[in] inRequiredAcknowledgementsVersionValue The version of the required acknowledgements. + */ + CHIP_ERROR Init(PersistentStorageDelegate * const inPersistentStorageDelegate, uint16_t inRequiredAcknowledgementsValue, + uint16_t inRequiredAcknowledgementsVersionValue); + + CHIP_ERROR CommitAcceptance() override; + + CHIP_ERROR GetAcceptance(uint16_t & outAcknowledgementsValue, uint16_t & outAcknowledgementsVersionValue) const override; + + CHIP_ERROR GetRequirements(uint16_t & outAcknowledgementsValue, uint16_t & outAcknowledgementsVersionValue) const override; + + CHIP_ERROR HasAcceptance(bool & outHasAcceptance) const override; + + CHIP_ERROR ResetAcceptance() override; + + CHIP_ERROR RevertAcceptance() override; + + CHIP_ERROR SetAcceptance(uint16_t inAcknowledgementsValue, uint16_t inAcknowledgementsVersionValue) override; + +private: + CHIP_ERROR LoadAcceptance(uint16_t & outAcknowledgementsValue, uint16_t & outAcknowledgementsVersionValue); + + CHIP_ERROR StoreAcceptance(uint16_t inAcknowledgementsValue, uint16_t inAcknowledgementsVersionValue); + + PersistentStorageDelegate * mPersistentStorageDelegate; + uint16_t mRequiredAcknowledgementsValue; + uint16_t mRequiredAcknowledgementsVersionValue; + + Optional mLatchedAcknowledgementsAcceptanceValue; + Optional mLatchedAcknowledgementsAcceptanceVersionValue; + + Optional mTemporaryAcknowledgementsAcceptanceValue; + Optional mTemporaryAcknowledgementsAcceptanceVersionValue; +}; + +} // namespace app +} // namespace chip diff --git a/src/app/server/EnhancedSetupFlowProvider.h b/src/app/server/EnhancedSetupFlowProvider.h new file mode 100644 index 00000000000000..fd9862fd69ef2c --- /dev/null +++ b/src/app/server/EnhancedSetupFlowProvider.h @@ -0,0 +1,91 @@ +/* + * + * Copyright (c) 2024 Project CHIP Authors + * All rights reserved. + * + * 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. + */ + +#pragma once + +#include + +#include + +namespace chip { +namespace app { + +/** + * This class provides access to the state of the Enhanced Setup Flow feature. + */ +class EnhancedSetupFlowProvider +{ +public: + virtual ~EnhancedSetupFlowProvider() = default; + + virtual CHIP_ERROR HasReceivedTermsAndConditionscknowledgements(bool & outReceived) const = 0; + + /** + * @param[out] outAccepted true if the required acknowledgements have been accepted, false otherwise. + */ + virtual CHIP_ERROR HasTermsAndConditionsRequiredAcknowledgementsBeenAccepted(bool & outAccepted) const = 0; + + /** + * @param[out] outAccepted true if the required acknowledgements version has been accepted, false otherwise. + */ + virtual CHIP_ERROR HasTermsAndConditionsRequiredAcknowledgementsVersionBeenAccepted(bool & outAccepted) const = 0; + + /** + * @param[out] outValue true if terms and conditions acceptance is required before commissioning complete may succeed, false + * otherwise. + */ + virtual CHIP_ERROR IsTermsAndConditionsAcceptanceRequired(bool & outValue) const = 0; + + /** + * @param[out] outValue The version of the required acknowledgements. + */ + virtual CHIP_ERROR GetTermsAndConditionsRequiredAcknowledgements(uint16_t & outValue) const = 0; + + /** + * @param[out] outValue The outValue of the required acknowledgements version. + */ + virtual CHIP_ERROR GetTermsAndConditionsRequiredAcknowledgementsVersion(uint16_t & outValue) const = 0; + + /** + * @param[out] outValue The outValue of the accepted acknowledgements. + */ + virtual CHIP_ERROR GetTermsAndConditionsAcceptedAcknowledgements(uint16_t & outValue) const = 0; + + /** + * @param[out] outValue The outValue of the accepted acknowledgements version. + */ + virtual CHIP_ERROR GetTermsAndConditionsAcceptedAcknowledgementsVersion(uint16_t & outValue) const = 0; + + /** + * @param[in] inTCAcknowledgements The acknowledgements to accept. + * @param[in] inTCAcknowledgementsVersionValue The version of the acknowledgements to accept. + */ + virtual CHIP_ERROR SetTermsAndConditionsAcceptance(uint16_t inTCAcknowledgementsValue, + uint16_t inTCAcknowledgementsVersionValue) = 0; + + /** + * Reset the terms and conditions acceptance. The terms and conditions should be cleared on factory reset or if failure occurs + * during a failsafe context. + */ + virtual CHIP_ERROR RevertTermsAndConditionsAcceptance() = 0; + + virtual CHIP_ERROR CommitTermsAndConditionsAcceptance() = 0; +}; + +} // namespace app +} // namespace chip diff --git a/src/app/server/Server.cpp b/src/app/server/Server.cpp index 22cd274ba87e39..25d03d1645b914 100644 --- a/src/app/server/Server.cpp +++ b/src/app/server/Server.cpp @@ -1,6 +1,6 @@ /* * - * Copyright (c) 2021 Project CHIP Authors + * Copyright (c) 2021-2024 Project CHIP Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -185,6 +185,11 @@ CHIP_ERROR Server::Init(const ServerInitParams & initParams) mReportScheduler = initParams.reportScheduler; +#if CHIP_CONFIG_TC_REQUIRED + mTermsAndConditionsProvider = initParams.termsAndConditionsProvider; + mEnhancedSetupFlowProvider = initParams.enhancedSetupFlowProvider; +#endif + mTestEventTriggerDelegate = initParams.testEventTriggerDelegate; if (mTestEventTriggerDelegate == nullptr) { @@ -586,6 +591,9 @@ void Server::ScheduleFactoryReset() GetInstance().GetFabricTable().DeleteAllFabrics(); PlatformMgr().HandleServerShuttingDown(); ConfigurationMgr().InitiateFactoryReset(); +#if CHIP_CONFIG_TC_REQUIRED + // Clear accepted terms and conditions +#endif }); } diff --git a/src/app/server/Server.h b/src/app/server/Server.h index 2f6126a4ace635..cb8ab12f40f547 100644 --- a/src/app/server/Server.h +++ b/src/app/server/Server.h @@ -1,6 +1,6 @@ /* * - * Copyright (c) 2020 Project CHIP Authors + * Copyright (c) 2020-2024 Project CHIP Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -33,6 +33,12 @@ #include #include #include +#if CHIP_CONFIG_TC_REQUIRED +#include +#include +#include +#include +#endif #include #include #include @@ -179,6 +185,12 @@ struct ServerInitParams // Optional. Support for the ICD Check-In BackOff strategy. Must be initialized before being provided. // If the ICD Check-In protocol use-case is supported and no strategy is provided, server will use the default strategy. app::ICDCheckInBackOffStrategy * icdCheckInBackOffStrategy = nullptr; +#if CHIP_CONFIG_TC_REQUIRED + // Optional. Enhanced setup flow provider to support terms and conditions acceptance check. + app::EnhancedSetupFlowProvider * enhancedSetupFlowProvider = nullptr; + // Optional. Terms and conditions provider to support enhanced setup flow feature. + app::TermsAndConditionsProvider * termsAndConditionsProvider = nullptr; +#endif }; /** @@ -300,6 +312,24 @@ struct CommonCaseDeviceServerInitParams : public ServerInitParams } #endif +#if CHIP_CONFIG_TC_REQUIRED + static app::DefaultEnhancedSetupFlowProvider sDefaultEnhancedSetupFlowProviderInstance; + static app::DefaultTermsAndConditionsProvider sDefaultTermsAndConditionsProviderInstance; + if (this->termsAndConditionsProvider == nullptr) + { + ReturnErrorOnFailure(sDefaultTermsAndConditionsProviderInstance.Init(this->persistentStorageDelegate, + CHIP_CONFIG_TC_REQUIRED_ACKNOWLEDGEMENTS, + CHIP_CONFIG_TC_REQUIRED_ACKNOWLEDGEMENTS_VERSION)); + this->termsAndConditionsProvider = &sDefaultTermsAndConditionsProviderInstance; + } + + if (this->enhancedSetupFlowProvider == nullptr) + { + ReturnErrorOnFailure(sDefaultEnhancedSetupFlowProviderInstance.Init(this->termsAndConditionsProvider)); + this->enhancedSetupFlowProvider = &sDefaultEnhancedSetupFlowProviderInstance; + } +#endif + return CHIP_NO_ERROR; } @@ -402,6 +432,10 @@ class Server app::reporting::ReportScheduler * GetReportScheduler() { return mReportScheduler; } +#if CHIP_CONFIG_TC_REQUIRED + app::EnhancedSetupFlowProvider * GetEnhancedSetupFlowProvider() { return mEnhancedSetupFlowProvider; } +#endif + #if CHIP_CONFIG_ENABLE_ICD_SERVER app::ICDManager & GetICDManager() { return mICDManager; } @@ -675,6 +709,10 @@ class Server GroupDataProviderListener mListener; ServerFabricDelegate mFabricDelegate; app::reporting::ReportScheduler * mReportScheduler; +#if CHIP_CONFIG_TC_REQUIRED + app::EnhancedSetupFlowProvider * mEnhancedSetupFlowProvider; + app::TermsAndConditionsProvider * mTermsAndConditionsProvider; +#endif Access::AccessControl mAccessControl; app::AclStorage * mAclStorage; diff --git a/src/app/server/TermsAndConditionsProvider.h b/src/app/server/TermsAndConditionsProvider.h new file mode 100644 index 00000000000000..45268d78baf23f --- /dev/null +++ b/src/app/server/TermsAndConditionsProvider.h @@ -0,0 +1,85 @@ +/* + * + * Copyright (c) 2024 Project CHIP Authors + * All rights reserved. + * + * 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. + */ + +#pragma once + +#include + +#include +#include + +namespace chip { +namespace app { + +/** + * @brief Data access layer for the required terms and conditions and the store for the user acceptance. + */ +class TermsAndConditionsProvider +{ +public: + virtual ~TermsAndConditionsProvider() = default; + + /** + * @brief Clear the temporary acceptance status of the required terms and conditions. + */ + virtual CHIP_ERROR RevertAcceptance() = 0; + + /** + * @brief Commit the persistent acceptance status of the required terms and conditions. + */ + virtual CHIP_ERROR CommitAcceptance() = 0; + + /** + * @brief Retrieves the latest acceptance status of the required terms and conditions. + * + * @param[out] outAcknowledgementsValue The bitmask of acknowledgements accepted. + * @param[out] outAcknowledgementsVersionValue The version of the accepted acknowledgements. + */ + virtual CHIP_ERROR GetAcceptance(uint16_t & outAcknowledgementsValue, uint16_t & outAcknowledgementsVersionValue) const = 0; + + /** + * @brief Retrieves the requirements of the terms and conditions. + * + * @param[out] outAcknowledgementsValue The bitmask of required acknowledgements. + * @param[out] outAcknowledgementsVersionValue The version of the required acknowledgements. + */ + virtual CHIP_ERROR GetRequirements(uint16_t & outAcknowledgementsValue, uint16_t & outAcknowledgementsVersionValue) const = 0; + + /** + * @brief Retrieve if any terms and conditions has been set. + * + * @param[out] outHasAcceptance True if any acceptance has been set. + */ + virtual CHIP_ERROR HasAcceptance(bool & outHasAcceptance) const = 0; + + /** + * @brief Reset the persisted acceptance status of the required terms and conditions. + */ + virtual CHIP_ERROR ResetAcceptance() = 0; + + /** + * @brief Sets the acceptance status of the required terms and conditions. + * + * @param[in] inAcknowledgementsValue The bitmask of acknowledgements that was accepted. + * @param[in] inAcknowledgementsVersionValue The version of the acknowledgements that was accepted. + */ + virtual CHIP_ERROR SetAcceptance(uint16_t inAcknowledgementsValue, uint16_t inAcknowledgementsVersionValue) = 0; +}; + +} // namespace app +} // namespace chip diff --git a/src/app/tests/BUILD.gn b/src/app/tests/BUILD.gn index 104a57a2fc019a..40cf05ecda478a 100644 --- a/src/app/tests/BUILD.gn +++ b/src/app/tests/BUILD.gn @@ -1,4 +1,4 @@ -# Copyright (c) 2020 Project CHIP Authors +# Copyright (c) 2020-2024 Project CHIP Authors # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -199,7 +199,9 @@ chip_test_suite("tests") { "TestCommandPathParams.cpp", "TestConcreteAttributePath.cpp", "TestDataModelSerialization.cpp", + "TestDefaultEnhancedSetupFlowProvider.cpp", "TestDefaultOTARequestorStorage.cpp", + "TestDefaultTermsAndConditionsProvider.cpp", "TestDefaultThreadNetworkDirectoryStorage.cpp", "TestEventLoggingNoUTCTime.cpp", "TestEventOverflow.cpp", @@ -237,6 +239,8 @@ chip_test_suite("tests") { "${chip_root}/src/app/codegen-data-model-provider:instance-header", "${chip_root}/src/app/common:cluster-objects", "${chip_root}/src/app/icd/client:manager", + "${chip_root}/src/app/server:enhanced_setup_flow", + "${chip_root}/src/app/server:server", "${chip_root}/src/app/tests:helpers", "${chip_root}/src/app/util/mock:mock_codegen_data_model", "${chip_root}/src/app/util/mock:mock_ember", diff --git a/src/app/tests/TestDefaultEnhancedSetupFlowProvider.cpp b/src/app/tests/TestDefaultEnhancedSetupFlowProvider.cpp new file mode 100644 index 00000000000000..8ef94119ef54ee --- /dev/null +++ b/src/app/tests/TestDefaultEnhancedSetupFlowProvider.cpp @@ -0,0 +1,310 @@ +/* + * Copyright (c) 2024 Project CHIP Authors + * All rights reserved. + * + * 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. + */ + +#include "app/server/DefaultEnhancedSetupFlowProvider.h" + +#include +#include +#include + +class FakeTermsAndConditionsProvider : public chip::app::TermsAndConditionsProvider +{ +public: + FakeTermsAndConditionsProvider(uint16_t inAcceptedAcknowledgements, uint16_t inAcceptedAcknowledgementsVersion, + uint16_t inRequiredAcknowledgements, uint16_t inRequiredAcknowledgementsVersion) : + mAcceptedAcknowledgements(inAcceptedAcknowledgements), + mAcceptedAcknowledgementsVersion(inAcceptedAcknowledgementsVersion), mRequiredAcknowledgements(inRequiredAcknowledgements), + mRequiredAcknowledgementsVersion(inRequiredAcknowledgementsVersion) + {} + + CHIP_ERROR RevertAcceptance() override + { + mAcceptedAcknowledgements = 0; + mAcceptedAcknowledgementsVersion = 0; + return CHIP_NO_ERROR; + } + + CHIP_ERROR CommitAcceptance() override { return CHIP_NO_ERROR; } + + CHIP_ERROR GetAcceptance(uint16_t & outAcknowledgements, uint16_t & outAcknowledgementsVersion) const override + { + outAcknowledgements = mAcceptedAcknowledgements; + outAcknowledgementsVersion = mAcceptedAcknowledgementsVersion; + return CHIP_NO_ERROR; + } + + CHIP_ERROR GetRequirements(uint16_t & outAcknowledgements, uint16_t & outAcknowledgementsVersion) const override + { + outAcknowledgements = mRequiredAcknowledgements; + outAcknowledgementsVersion = mRequiredAcknowledgementsVersion; + return CHIP_NO_ERROR; + } + + CHIP_ERROR SetAcceptance(uint16_t inAcknowledgements, uint16_t inAcknowledgementsVersion) override + { + mAcceptedAcknowledgements = inAcknowledgements; + mAcceptedAcknowledgementsVersion = inAcknowledgementsVersion; + return CHIP_NO_ERROR; + } + + CHIP_ERROR ResetAcceptance() override { return CHIP_NO_ERROR; } + +private: + uint16_t mAcceptedAcknowledgements; + uint16_t mAcceptedAcknowledgementsVersion; + uint16_t mRequiredAcknowledgements; + uint16_t mRequiredAcknowledgementsVersion; +}; + +TEST(DefaultEnhancedSetupFlowProvider, TestNoAcceptanceRequiredCheckAcknowledgementsAcceptedSuccess) +{ + CHIP_ERROR err; + bool hasTermsBeenAccepted; + + FakeTermsAndConditionsProvider tncProvider(0, 0, 0, 0); + chip::app::DefaultEnhancedSetupFlowProvider esfProvider; + + err = esfProvider.Init(&tncProvider); + EXPECT_EQ(CHIP_NO_ERROR, err); + + err = esfProvider.HasTermsAndConditionsRequiredAcknowledgementsBeenAccepted(hasTermsBeenAccepted); + EXPECT_EQ(CHIP_NO_ERROR, err); + EXPECT_TRUE(hasTermsBeenAccepted); +} + +TEST(DefaultEnhancedSetupFlowProvider, TestNoAcceptanceRequiredCheckAcknowledgementsVersionAcceptedSuccess) +{ + CHIP_ERROR err; + bool hasTermsVersionBeenAccepted; + + FakeTermsAndConditionsProvider tncProvider(0, 0, 0, 0); + chip::app::DefaultEnhancedSetupFlowProvider esfProvider; + + err = esfProvider.Init(&tncProvider); + EXPECT_EQ(CHIP_NO_ERROR, err); + + err = esfProvider.HasTermsAndConditionsRequiredAcknowledgementsVersionBeenAccepted(hasTermsVersionBeenAccepted); + EXPECT_EQ(CHIP_NO_ERROR, err); + EXPECT_TRUE(hasTermsVersionBeenAccepted); +} + +TEST(DefaultEnhancedSetupFlowProvider, TestAcceptanceRequiredNoTermsAcceptedCheckAcknowledgementsAcceptedFailure) +{ + CHIP_ERROR err; + bool hasTermsBeenAccepted; + + FakeTermsAndConditionsProvider tncProvider(0, 0, 1, 1); + chip::app::DefaultEnhancedSetupFlowProvider esfProvider; + + err = esfProvider.Init(&tncProvider); + EXPECT_EQ(CHIP_NO_ERROR, err); + + err = esfProvider.HasTermsAndConditionsRequiredAcknowledgementsBeenAccepted(hasTermsBeenAccepted); + EXPECT_EQ(CHIP_NO_ERROR, err); + EXPECT_FALSE(hasTermsBeenAccepted); +} + +TEST(DefaultEnhancedSetupFlowProvider, + TestAcceptanceRequiredTermsAcceptedTermsVersionOutdatedCheckAcknowledgementsVersionAcceptedFailure) +{ + CHIP_ERROR err; + bool hasTermsBeenAccepted; + bool hasTermsVersionBeenAccepted; + + FakeTermsAndConditionsProvider tncProvider(0, 0, 1, 1); + chip::app::DefaultEnhancedSetupFlowProvider esfProvider; + + err = esfProvider.Init(&tncProvider); + EXPECT_EQ(CHIP_NO_ERROR, err); + + err = esfProvider.SetTermsAndConditionsAcceptance(1, 0); + EXPECT_EQ(CHIP_NO_ERROR, err); + + err = esfProvider.HasTermsAndConditionsRequiredAcknowledgementsBeenAccepted(hasTermsBeenAccepted); + EXPECT_EQ(CHIP_NO_ERROR, err); + EXPECT_TRUE(hasTermsBeenAccepted); + + err = esfProvider.HasTermsAndConditionsRequiredAcknowledgementsVersionBeenAccepted(hasTermsVersionBeenAccepted); + EXPECT_EQ(CHIP_NO_ERROR, err); + EXPECT_FALSE(hasTermsVersionBeenAccepted); +} + +TEST(DefaultEnhancedSetupFlowProvider, TestAcceptanceRequiredTermsAcceptedFutureVersionCheckAcknowledgementsAcceptedSuccess) +{ + CHIP_ERROR err; + bool hasTermsBeenAccepted; + bool hasTermsVersionBeenAccepted; + + uint16_t acceptedTerms = 1; + uint16_t requiredTerms = 1; + uint16_t acceptedTermsVersion = 2; + uint16_t requiredTermsVersion = 1; + + FakeTermsAndConditionsProvider tncProvider(acceptedTerms, acceptedTermsVersion, requiredTerms, requiredTermsVersion); + chip::app::DefaultEnhancedSetupFlowProvider esfProvider; + + err = esfProvider.Init(&tncProvider); + EXPECT_EQ(CHIP_NO_ERROR, err); + + err = esfProvider.HasTermsAndConditionsRequiredAcknowledgementsBeenAccepted(hasTermsBeenAccepted); + EXPECT_EQ(CHIP_NO_ERROR, err); + EXPECT_TRUE(hasTermsBeenAccepted); + + err = esfProvider.HasTermsAndConditionsRequiredAcknowledgementsVersionBeenAccepted(hasTermsVersionBeenAccepted); + EXPECT_EQ(CHIP_NO_ERROR, err); + EXPECT_TRUE(hasTermsVersionBeenAccepted); +} + +TEST(DefaultEnhancedSetupFlowProvider, TestAcceptanceRequiredTermsAcceptedSuccess) +{ + CHIP_ERROR err; + bool hasTermsBeenAccepted; + bool hasTermsVersionBeenAccepted; + + FakeTermsAndConditionsProvider tncProvider(0, 0, 1, 1); + chip::app::DefaultEnhancedSetupFlowProvider esfProvider; + + err = esfProvider.Init(&tncProvider); + EXPECT_EQ(CHIP_NO_ERROR, err); + + err = esfProvider.SetTermsAndConditionsAcceptance(1, 1); + EXPECT_EQ(CHIP_NO_ERROR, err); + + err = esfProvider.HasTermsAndConditionsRequiredAcknowledgementsBeenAccepted(hasTermsBeenAccepted); + EXPECT_EQ(CHIP_NO_ERROR, err); + EXPECT_TRUE(hasTermsBeenAccepted); + + err = esfProvider.HasTermsAndConditionsRequiredAcknowledgementsVersionBeenAccepted(hasTermsVersionBeenAccepted); + EXPECT_EQ(CHIP_NO_ERROR, err); + EXPECT_TRUE(hasTermsVersionBeenAccepted); +} + +TEST(DefaultEnhancedSetupFlowProvider, TestAcceptanceRequiredTermsMissingFailure) +{ + CHIP_ERROR err; + bool hasTermsBeenAccepted; + bool hasTermsVersionBeenAccepted; + + uint16_t acceptedTerms = 0b0111'1111'1111'1111; + uint16_t requiredTerms = 0b1111'1111'1111'1111; + uint16_t acceptedTermsVersion = 1; + uint16_t requiredTermsVersion = 1; + + FakeTermsAndConditionsProvider tncProvider(acceptedTerms, acceptedTermsVersion, requiredTerms, requiredTermsVersion); + chip::app::DefaultEnhancedSetupFlowProvider esfProvider; + + err = esfProvider.Init(&tncProvider); + EXPECT_EQ(CHIP_NO_ERROR, err); + + err = esfProvider.HasTermsAndConditionsRequiredAcknowledgementsBeenAccepted(hasTermsBeenAccepted); + EXPECT_EQ(CHIP_NO_ERROR, err); + EXPECT_FALSE(hasTermsBeenAccepted); + + err = esfProvider.HasTermsAndConditionsRequiredAcknowledgementsVersionBeenAccepted(hasTermsVersionBeenAccepted); + EXPECT_EQ(CHIP_NO_ERROR, err); + EXPECT_TRUE(hasTermsVersionBeenAccepted); +} + +TEST(DefaultEnhancedSetupFlowProvider, TestAcceptanceRequiredAllTermsAcceptedCheckAcknowledgementsAcceptedSuccess) +{ + CHIP_ERROR err; + bool hasTermsBeenAccepted; + bool hasTermsVersionBeenAccepted; + + uint16_t acceptedTerms = 0b1111'1111'1111'1111; + uint16_t requiredTerms = 0b1111'1111'1111'1111; + uint16_t acceptedTermsVersion = 1; + uint16_t requiredTermsVersion = 1; + + FakeTermsAndConditionsProvider tncProvider(acceptedTerms, acceptedTermsVersion, requiredTerms, requiredTermsVersion); + chip::app::DefaultEnhancedSetupFlowProvider esfProvider; + + err = esfProvider.Init(&tncProvider); + EXPECT_EQ(CHIP_NO_ERROR, err); + + err = esfProvider.HasTermsAndConditionsRequiredAcknowledgementsBeenAccepted(hasTermsBeenAccepted); + EXPECT_EQ(CHIP_NO_ERROR, err); + EXPECT_TRUE(hasTermsBeenAccepted); + + err = esfProvider.HasTermsAndConditionsRequiredAcknowledgementsVersionBeenAccepted(hasTermsVersionBeenAccepted); + EXPECT_EQ(CHIP_NO_ERROR, err); + EXPECT_TRUE(hasTermsVersionBeenAccepted); +} + +TEST(DefaultEnhancedSetupFlowProvider, TestRevertAcceptanceRetainsRequirements) +{ + CHIP_ERROR err; + + uint16_t initialAcceptedTermsAndConditions = 0; + uint16_t initialRequiredTermsAndConditions = 0b1111'1111'1111'1111; + uint16_t initialAcceptedTermsAndConditionsVersion = 0; + uint16_t initialRequiredTermsAndConditionsVersion = 1; + + uint16_t outAcceptedTermsAndConditions; + uint16_t outRequiredTermsAndConditions; + uint16_t outAcceptedTermsAndConditionsVersion; + uint16_t outRequiredTermsAndConditionsVersion; + + uint16_t updatedAcceptedTermsAndConditions = 0b1111'1111'1111'1111; + uint16_t updatedAcceptedTermsAndConditionsVersion = 1; + + FakeTermsAndConditionsProvider tncProvider(initialAcceptedTermsAndConditions, initialAcceptedTermsAndConditionsVersion, + initialRequiredTermsAndConditions, initialRequiredTermsAndConditionsVersion); + + chip::app::DefaultEnhancedSetupFlowProvider esfProvider; + + err = esfProvider.Init(&tncProvider); + EXPECT_EQ(CHIP_NO_ERROR, err); + + err = esfProvider.SetTermsAndConditionsAcceptance(updatedAcceptedTermsAndConditions, updatedAcceptedTermsAndConditionsVersion); + EXPECT_EQ(CHIP_NO_ERROR, err); + + err = esfProvider.GetTermsAndConditionsRequiredAcknowledgements(outRequiredTermsAndConditions); + EXPECT_EQ(CHIP_NO_ERROR, err); + EXPECT_EQ(outRequiredTermsAndConditions, initialRequiredTermsAndConditions); + + err = esfProvider.GetTermsAndConditionsRequiredAcknowledgementsVersion(outRequiredTermsAndConditionsVersion); + EXPECT_EQ(CHIP_NO_ERROR, err); + EXPECT_EQ(outRequiredTermsAndConditionsVersion, initialRequiredTermsAndConditionsVersion); + + err = esfProvider.GetTermsAndConditionsAcceptedAcknowledgements(outAcceptedTermsAndConditions); + EXPECT_EQ(CHIP_NO_ERROR, err); + EXPECT_EQ(outAcceptedTermsAndConditions, updatedAcceptedTermsAndConditions); + + err = esfProvider.GetTermsAndConditionsAcceptedAcknowledgementsVersion(outAcceptedTermsAndConditionsVersion); + EXPECT_EQ(CHIP_NO_ERROR, err); + EXPECT_EQ(outAcceptedTermsAndConditionsVersion, updatedAcceptedTermsAndConditionsVersion); + + err = esfProvider.RevertTermsAndConditionsAcceptance(); + EXPECT_EQ(CHIP_NO_ERROR, err); + + err = esfProvider.GetTermsAndConditionsRequiredAcknowledgements(outRequiredTermsAndConditions); + EXPECT_EQ(CHIP_NO_ERROR, err); + EXPECT_EQ(outRequiredTermsAndConditions, initialRequiredTermsAndConditions); + + err = esfProvider.GetTermsAndConditionsRequiredAcknowledgementsVersion(outRequiredTermsAndConditionsVersion); + EXPECT_EQ(CHIP_NO_ERROR, err); + EXPECT_EQ(outRequiredTermsAndConditionsVersion, initialRequiredTermsAndConditionsVersion); + + err = esfProvider.GetTermsAndConditionsAcceptedAcknowledgements(outAcceptedTermsAndConditions); + EXPECT_EQ(CHIP_NO_ERROR, err); + EXPECT_EQ(outAcceptedTermsAndConditions, 0); + + err = esfProvider.GetTermsAndConditionsAcceptedAcknowledgementsVersion(outAcceptedTermsAndConditionsVersion); + EXPECT_EQ(CHIP_NO_ERROR, err); + EXPECT_EQ(outAcceptedTermsAndConditionsVersion, 0); +} diff --git a/src/app/tests/TestDefaultTermsAndConditionsProvider.cpp b/src/app/tests/TestDefaultTermsAndConditionsProvider.cpp new file mode 100644 index 00000000000000..474dda7a6b2785 --- /dev/null +++ b/src/app/tests/TestDefaultTermsAndConditionsProvider.cpp @@ -0,0 +1,231 @@ +/* + * + * Copyright (c) 2024 Project CHIP Authors + * All rights reserved. + * + * 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. + */ + +#include "app/server/DefaultTermsAndConditionsProvider.h" + +#include +#include +#include +#include + +TEST(DefaultTermsAndConditionsProvider, TestInitSuccess) +{ + CHIP_ERROR err; + + chip::TestPersistentStorageDelegate storageDelegate; + chip::app::DefaultTermsAndConditionsProvider tncProvider; + + uint16_t requiredAcknowledgements = 1; + uint16_t requiredAcknowledgementsVersion = 1; + err = tncProvider.Init(&storageDelegate, requiredAcknowledgements, requiredAcknowledgementsVersion); + EXPECT_EQ(CHIP_NO_ERROR, err); +} + +TEST(DefaultTermsAndConditionsProvider, TestNoRequirementsGetRequirementsSuccess) +{ + CHIP_ERROR err; + + chip::TestPersistentStorageDelegate storageDelegate; + chip::app::DefaultTermsAndConditionsProvider tncProvider; + + uint16_t requiredAcknowledgements = 0; + uint16_t requiredAcknowledgementsVersion = 0; + err = tncProvider.Init(&storageDelegate, requiredAcknowledgements, requiredAcknowledgementsVersion); + EXPECT_EQ(CHIP_NO_ERROR, err); + + uint16_t outAcceptance; + uint16_t outAcknowledgementsVersion; + err = tncProvider.GetAcceptance(outAcceptance, outAcknowledgementsVersion); + EXPECT_EQ(CHIP_NO_ERROR, err); + EXPECT_EQ(0, outAcceptance); + EXPECT_EQ(0, outAcknowledgementsVersion); +} + +TEST(DefaultTermsAndConditionsProvider, TestNeverAcceptanceGetAcceptanceSuccess) +{ + CHIP_ERROR err; + + chip::TestPersistentStorageDelegate storageDelegate; + chip::app::DefaultTermsAndConditionsProvider tncProvider; + + uint16_t requiredAcknowledgements = 0b1111'1111'1111'1111; + uint16_t requiredAcknowledgementsVersion = 1; + err = tncProvider.Init(&storageDelegate, requiredAcknowledgements, requiredAcknowledgementsVersion); + EXPECT_EQ(CHIP_NO_ERROR, err); + + uint16_t outAcceptance; + uint16_t outAcknowledgementsVersion; + err = tncProvider.GetAcceptance(outAcceptance, outAcknowledgementsVersion); + EXPECT_EQ(CHIP_NO_ERROR, err); + EXPECT_EQ(0, outAcceptance); + EXPECT_EQ(0, outAcknowledgementsVersion); +} + +TEST(DefaultTermsAndConditionsProvider, TestTermsAcceptedPersistsSuccess) +{ + CHIP_ERROR err; + + chip::TestPersistentStorageDelegate storageDelegate; + chip::app::DefaultTermsAndConditionsProvider tncProvider; + chip::app::DefaultTermsAndConditionsProvider anotherTncProvider; + + uint16_t requiredAcknowledgements = 1; + uint16_t requiredAcknowledgementsVersion = 1; + err = tncProvider.Init(&storageDelegate, requiredAcknowledgements, requiredAcknowledgementsVersion); + EXPECT_EQ(CHIP_NO_ERROR, err); + + uint16_t acceptedTermsAndConditions = 1; + uint16_t acceptedTermsAndConditionsVersion = 1; + err = tncProvider.SetAcceptance(acceptedTermsAndConditions, acceptedTermsAndConditionsVersion); + EXPECT_EQ(CHIP_NO_ERROR, err); + + uint16_t outAcceptance; + uint16_t outAcknowledgementsVersion; + err = tncProvider.GetAcceptance(outAcceptance, outAcknowledgementsVersion); + EXPECT_EQ(CHIP_NO_ERROR, err); + EXPECT_EQ(1, outAcceptance); + EXPECT_EQ(1, outAcknowledgementsVersion); + + err = tncProvider.CommitAcceptance(); + EXPECT_EQ(CHIP_NO_ERROR, err); + EXPECT_EQ(1, outAcceptance); + EXPECT_EQ(1, outAcknowledgementsVersion); + + err = anotherTncProvider.Init(&storageDelegate, requiredAcknowledgements, requiredAcknowledgementsVersion); + EXPECT_EQ(CHIP_NO_ERROR, err); + + err = anotherTncProvider.GetAcceptance(outAcceptance, outAcknowledgementsVersion); + EXPECT_EQ(CHIP_NO_ERROR, err); + EXPECT_EQ(1, outAcceptance); + EXPECT_EQ(1, outAcknowledgementsVersion); +} + +TEST(DefaultTermsAndConditionsProvider, TestTermsRequiredGetRequirementsSuccess) +{ + CHIP_ERROR err; + + chip::TestPersistentStorageDelegate storageDelegate; + chip::app::DefaultTermsAndConditionsProvider tncProvider; + + uint16_t initialRequiredAcknowledgements = 1; + uint16_t initialRequiredAcknowledgementsVersion = 1; + err = tncProvider.Init(&storageDelegate, initialRequiredAcknowledgements, initialRequiredAcknowledgementsVersion); + EXPECT_EQ(CHIP_NO_ERROR, err); + + uint16_t outRequiredAcknowledgements; + uint16_t outRequiredAcknowledgementsVersion; + err = tncProvider.GetRequirements(outRequiredAcknowledgements, outRequiredAcknowledgementsVersion); + EXPECT_EQ(CHIP_NO_ERROR, err); + EXPECT_EQ(1, outRequiredAcknowledgements); + EXPECT_EQ(1, outRequiredAcknowledgementsVersion); +} + +TEST(DefaultTermsAndConditionsProvider, TestSetAcceptanceGetAcceptanceSuccess) +{ + CHIP_ERROR err; + + chip::TestPersistentStorageDelegate storageDelegate; + chip::app::DefaultTermsAndConditionsProvider tncProvider; + + uint16_t requiredAcknowledgements = 1; + uint16_t requiredAcknowledgementsVersion = 1; + err = tncProvider.Init(&storageDelegate, requiredAcknowledgements, requiredAcknowledgementsVersion); + EXPECT_EQ(CHIP_NO_ERROR, err); + + uint16_t acceptedTermsAndConditions = 1; + uint16_t acceptedTermsAndConditionsVersion = 1; + err = tncProvider.SetAcceptance(acceptedTermsAndConditions, acceptedTermsAndConditionsVersion); + EXPECT_EQ(CHIP_NO_ERROR, err); + + uint16_t outAcceptance; + uint16_t outAcknowledgementsVersion; + err = tncProvider.GetAcceptance(outAcceptance, outAcknowledgementsVersion); + EXPECT_EQ(CHIP_NO_ERROR, err); + EXPECT_EQ(1, outAcceptance); + EXPECT_EQ(1, outAcknowledgementsVersion); +} + +TEST(DefaultTermsAndConditionsProvider, TestRevertAcceptanceGetAcceptanceSuccess) +{ + CHIP_ERROR err; + + chip::TestPersistentStorageDelegate storageDelegate; + chip::app::DefaultTermsAndConditionsProvider tncProvider; + + uint16_t requiredAcknowledgements = 1; + uint16_t requiredAcknowledgementsVersion = 1; + err = tncProvider.Init(&storageDelegate, requiredAcknowledgements, requiredAcknowledgementsVersion); + EXPECT_EQ(CHIP_NO_ERROR, err); + + uint16_t acceptedTermsAndConditions = 1; + uint16_t acceptedTermsAndConditionsVersion = 1; + err = tncProvider.SetAcceptance(acceptedTermsAndConditions, acceptedTermsAndConditionsVersion); + EXPECT_EQ(CHIP_NO_ERROR, err); + + uint16_t outAcceptance; + uint16_t outAcknowledgementsVersion; + err = tncProvider.GetAcceptance(outAcceptance, outAcknowledgementsVersion); + EXPECT_EQ(CHIP_NO_ERROR, err); + EXPECT_EQ(1, outAcceptance); + EXPECT_EQ(1, outAcknowledgementsVersion); + + err = tncProvider.RevertAcceptance(); + EXPECT_EQ(CHIP_NO_ERROR, err); + + uint16_t outAcceptance2; + uint16_t outAcknowledgementsVersion2; + err = tncProvider.GetAcceptance(outAcceptance2, outAcknowledgementsVersion2); + EXPECT_EQ(CHIP_NO_ERROR, err); + EXPECT_EQ(0, outAcceptance2); + EXPECT_EQ(0, outAcknowledgementsVersion2); +} + +TEST(DefaultTermsAndConditionsProvider, TestAcceptanceRequiredTermsMissingFailure) +{ + CHIP_ERROR err; + + chip::TestPersistentStorageDelegate storageDelegate; + chip::app::DefaultTermsAndConditionsProvider tncProvider; + + uint16_t requiredAcknowledgements = 1; + uint16_t requiredAcknowledgementsVersion = 1; + err = tncProvider.Init(&storageDelegate, requiredAcknowledgements, requiredAcknowledgementsVersion); + EXPECT_EQ(CHIP_NO_ERROR, err); + + uint16_t acceptedTermsAndConditions = 1; + uint16_t acceptedTermsAndConditionsVersion = 1; + err = tncProvider.SetAcceptance(acceptedTermsAndConditions, acceptedTermsAndConditionsVersion); + EXPECT_EQ(CHIP_NO_ERROR, err); + + uint16_t outAcceptance; + uint16_t outAcknowledgementsVersion; + err = tncProvider.GetAcceptance(outAcceptance, outAcknowledgementsVersion); + EXPECT_EQ(CHIP_NO_ERROR, err); + EXPECT_EQ(1, outAcceptance); + EXPECT_EQ(1, outAcknowledgementsVersion); + + err = tncProvider.RevertAcceptance(); + EXPECT_EQ(CHIP_NO_ERROR, err); + + uint16_t outRequiredAcknowledgements; + uint16_t outRequiredAcknowledgementsVersion; + err = tncProvider.GetRequirements(outRequiredAcknowledgements, outRequiredAcknowledgementsVersion); + EXPECT_EQ(CHIP_NO_ERROR, err); + EXPECT_EQ(1, outRequiredAcknowledgements); + EXPECT_EQ(1, outRequiredAcknowledgementsVersion); +} diff --git a/src/controller/CommissioningDelegate.h b/src/controller/CommissioningDelegate.h index 7a96939cf49434..9ef40a6e20e408 100644 --- a/src/controller/CommissioningDelegate.h +++ b/src/controller/CommissioningDelegate.h @@ -82,6 +82,8 @@ enum CommissioningStage : uint8_t kPrimaryOperationalNetworkFailed, ///< Indicate that the primary operational network (on root endpoint) failed, should disable ///< the primary network interface later. kDisablePrimaryNetworkInterface, ///< Send InterfaceEnabled write request to the device to disable network interface. + kGetTCAcknowledgments, ///< Waiting for the higher layer to provide terms and conditions acknowledgements. + kConfigureTCAcknowledgments, ///< Send SetTCAcknowledgements (0x30:6) command to the device }; enum class ICDRegistrationStrategy : uint8_t diff --git a/src/include/platform/CHIPDeviceEvent.h b/src/include/platform/CHIPDeviceEvent.h index 09f4c46b652920..5ed80079d4e284 100644 --- a/src/include/platform/CHIPDeviceEvent.h +++ b/src/include/platform/CHIPDeviceEvent.h @@ -534,6 +534,9 @@ struct ChipDeviceEvent final FabricIndex fabricIndex; bool addNocCommandHasBeenInvoked; bool updateNocCommandHasBeenInvoked; +#if CHIP_CONFIG_TC_REQUIRED + bool updateTermsAndConditionsHasBeenInvoked; +#endif } FailSafeTimerExpired; struct diff --git a/src/lib/core/CHIPConfig.h b/src/lib/core/CHIPConfig.h index e9c317dfc79d5c..d739017e401179 100644 --- a/src/lib/core/CHIPConfig.h +++ b/src/lib/core/CHIPConfig.h @@ -1785,6 +1785,60 @@ extern const char CHIP_NON_PRODUCTION_MARKER[]; #define CHIP_CONFIG_MAX_BDX_LOG_TRANSFERS 5 #endif // CHIP_CONFIG_MAX_BDX_LOG_TRANSFERS +/** + * @file + * Configuration settings for Terms and Conditions (TC) acknowledgements during device commissioning. + */ + +/** + * @def CHIP_CONFIG_TC_REQUIRED + * + * @brief Indicates whether terms and conditions are required during commissioning. + * + * This macro defines whether the device commissioning process requires the user to acknowledge terms and conditions. + * - 1: Terms and conditions are required. + * - 0: Terms and conditions are not required. + * + * If this is set to 1, both CHIP_CONFIG_TC_REQUIRED_ACKNOWLEDGEMENTS and + * CHIP_CONFIG_TC_REQUIRED_ACKNOWLEDGEMENTS_VERSION must be defined. + */ +#ifndef CHIP_CONFIG_TC_REQUIRED +#define CHIP_CONFIG_TC_REQUIRED (0) +#endif + +#if CHIP_CONFIG_TC_REQUIRED + +/** + * @def CHIP_CONFIG_TC_REQUIRED_ACKNOWLEDGEMENTS + * + * @brief Configures the required terms and conditions acknowledgements bitmask. + * + * This macro defines the required terms and conditions acknowledgements bitmask. The bit-field is 16 bits long, so the possible + * value range is [0, 65535). This setting can be used to require that terms and conditions are presented to the user during + * commissioning. + */ +#ifndef CHIP_CONFIG_TC_REQUIRED_ACKNOWLEDGEMENTS +#error "CHIP_CONFIG_TC_REQUIRED_ACKNOWLEDGEMENTS must be defined when CHIP_CONFIG_TC_REQUIRED is enabled." +#endif + +/** + * @def CHIP_CONFIG_TC_REQUIRED_ACKNOWLEDGEMENTS_VERSION + * + * @brief Configures the latest known version of the terms and conditions. + * + * This macro defines the version number of the latest terms and conditions. It allows the application to iterate on revisions of + * the terms and conditions. A value of 0 indicates that no specific version is required. This setting can be used to enforce + * version-specific terms and conditions acknowledgements in the application. When the set of terms and conditions needs to be + * changed, the version number should be monotonically increased. If the latest terms and conditions version is updated (most + * likely during an OTA), then this may signal to the Administrator that updated terms and conditions should be presented to the + * user. + */ +#ifndef CHIP_CONFIG_TC_REQUIRED_ACKNOWLEDGEMENTS_VERSION +#error "CHIP_CONFIG_TC_REQUIRED_ACKNOWLEDGEMENTS_VERSION must be defined when CHIP_CONFIG_TC_REQUIRED is enabled." +#endif + +#endif // CHIP_CONFIG_TC_REQUIRED + /** * @} */ diff --git a/src/lib/support/DefaultStorageKeyAllocator.h b/src/lib/support/DefaultStorageKeyAllocator.h index 9ed8a2f56cfd77..b0de78d085e48d 100644 --- a/src/lib/support/DefaultStorageKeyAllocator.h +++ b/src/lib/support/DefaultStorageKeyAllocator.h @@ -1,6 +1,6 @@ /* * - * Copyright (c) 2021 Project CHIP Authors + * Copyright (c) 2021-2024 Project CHIP Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -256,6 +256,10 @@ class DefaultStorageKeyAllocator // when new fabric is created, this list needs to be updated, // when client init DefaultICDClientStorage, this table needs to be loaded. static StorageKeyName ICDFabricList() { return StorageKeyName::FromConst("g/icdfl"); } + + // Terms and Conditions Acceptance Key + // Stores the terms and conditions acceptance including terms and conditions revision, TLV encoded + static StorageKeyName TermsAndConditionsAcceptance() { return StorageKeyName::FromConst("g/tc"); } }; } // namespace chip