| /* |
| * 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 "TermsAndConditionsProvider.h" |
| |
| #include <lib/core/CHIPError.h> |
| #include <lib/core/TLV.h> |
| #include <lib/core/TLVTypes.h> |
| #include <lib/support/CodeUtils.h> |
| #include <lib/support/DefaultStorageKeyAllocator.h> |
| #include <lib/support/SafeInt.h> |
| #include <protocols/Protocols.h> |
| #include <protocols/interaction_model/StatusCode.h> |
| |
| 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 kSerializationSchemaMinimumVersion = 1; |
| constexpr uint8_t kSerializationSchemaCurrentVersion = 1; |
| |
| constexpr size_t kEstimatedTlvBufferSize = chip::TLV::EstimateStructOverhead(sizeof(uint8_t), // SerializationVersion |
| sizeof(uint16_t), // AcceptedAcknowledgements |
| sizeof(uint16_t) // AcceptedAcknowledgementsVersion |
| ) * |
| 4; // Extra space for rollback compatibility |
| } // namespace |
| |
| CHIP_ERROR chip::app::DefaultTermsAndConditionsStorageDelegate::Init(PersistentStorageDelegate * inPersistentStorageDelegate) |
| { |
| VerifyOrReturnValue(nullptr != inPersistentStorageDelegate, CHIP_ERROR_INVALID_ARGUMENT); |
| |
| mStorageDelegate = inPersistentStorageDelegate; |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR chip::app::DefaultTermsAndConditionsStorageDelegate::Delete() |
| { |
| VerifyOrReturnValue(nullptr != mStorageDelegate, CHIP_ERROR_UNINITIALIZED); |
| |
| const chip::StorageKeyName kStorageKey = chip::DefaultStorageKeyAllocator::TermsAndConditionsAcceptance(); |
| ReturnErrorOnFailure(mStorageDelegate->SyncDeleteKeyValue(kStorageKey.KeyName())); |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR chip::app::DefaultTermsAndConditionsStorageDelegate::Get(Optional<TermsAndConditions> & outTermsAndConditions) |
| { |
| VerifyOrReturnValue(nullptr != mStorageDelegate, CHIP_ERROR_UNINITIALIZED); |
| |
| 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); |
| |
| const chip::StorageKeyName kStorageKey = chip::DefaultStorageKeyAllocator::TermsAndConditionsAcceptance(); |
| |
| CHIP_ERROR err = mStorageDelegate->SyncGetKeyValue(kStorageKey.KeyName(), &buffer, bufferSize); |
| VerifyOrReturnValue(CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND == err || CHIP_NO_ERROR == err, err); |
| |
| if (CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND == err) |
| { |
| outTermsAndConditions.ClearValue(); |
| return CHIP_NO_ERROR; |
| } |
| |
| 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)); |
| |
| if (serializationVersion < kSerializationSchemaMinimumVersion) |
| { |
| ChipLogError(AppServer, "The terms and conditions datastore schema (%hhu) is newer than oldest compatible schema (%hhu)", |
| serializationVersion, kSerializationSchemaMinimumVersion); |
| return CHIP_IM_GLOBAL_STATUS(ConstraintError); |
| } |
| |
| if (serializationVersion != kSerializationSchemaCurrentVersion) |
| { |
| ChipLogDetail(AppServer, "The terms and conditions datastore schema (%hhu) differs from current schema (%hhu)", |
| serializationVersion, kSerializationSchemaCurrentVersion); |
| } |
| |
| ReturnErrorOnFailure(tlvReader.Next(kAcceptedAcknowledgementsTag)); |
| ReturnErrorOnFailure(tlvReader.Get(acknowledgements)); |
| ReturnErrorOnFailure(tlvReader.Next(kAcceptedAcknowledgementsVersionTag)); |
| ReturnErrorOnFailure(tlvReader.Get(acknowledgementsVersion)); |
| ReturnErrorOnFailure(tlvReader.ExitContainer(tlvContainer)); |
| |
| outTermsAndConditions = Optional<TermsAndConditions>(TermsAndConditions(acknowledgements, acknowledgementsVersion)); |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR chip::app::DefaultTermsAndConditionsStorageDelegate::Set(const TermsAndConditions & inTermsAndConditions) |
| { |
| uint8_t buffer[kEstimatedTlvBufferSize] = { 0 }; |
| chip::TLV::TLVWriter tlvWriter; |
| chip::TLV::TLVType tlvContainer; |
| |
| VerifyOrReturnValue(nullptr != mStorageDelegate, CHIP_ERROR_UNINITIALIZED); |
| |
| tlvWriter.Init(buffer); |
| ReturnErrorOnFailure(tlvWriter.StartContainer(chip::TLV::AnonymousTag(), chip::TLV::kTLVType_Structure, tlvContainer)); |
| ReturnErrorOnFailure(tlvWriter.Put(kSerializationVersionTag, kSerializationSchemaCurrentVersion)); |
| ReturnErrorOnFailure(tlvWriter.Put(kAcceptedAcknowledgementsTag, inTermsAndConditions.GetValue())); |
| ReturnErrorOnFailure(tlvWriter.Put(kAcceptedAcknowledgementsVersionTag, inTermsAndConditions.GetVersion())); |
| ReturnErrorOnFailure(tlvWriter.EndContainer(tlvContainer)); |
| ReturnErrorOnFailure(tlvWriter.Finalize()); |
| |
| const chip::StorageKeyName kStorageKey = chip::DefaultStorageKeyAllocator::TermsAndConditionsAcceptance(); |
| ReturnErrorOnFailure( |
| mStorageDelegate->SyncSetKeyValue(kStorageKey.KeyName(), buffer, static_cast<uint16_t>(tlvWriter.GetLengthWritten()))); |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR chip::app::DefaultTermsAndConditionsProvider::Init( |
| TermsAndConditionsStorageDelegate * inStorageDelegate, |
| const chip::Optional<chip::app::TermsAndConditions> & inRequiredTermsAndConditions) |
| { |
| VerifyOrReturnValue(nullptr != inStorageDelegate, CHIP_ERROR_INVALID_ARGUMENT); |
| |
| mTermsAndConditionsStorageDelegate = inStorageDelegate; |
| mRequiredAcknowledgements = inRequiredTermsAndConditions; |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR chip::app::DefaultTermsAndConditionsProvider::CommitAcceptance() |
| { |
| VerifyOrReturnValue(nullptr != mTermsAndConditionsStorageDelegate, CHIP_ERROR_UNINITIALIZED); |
| |
| // No terms and conditions to commit |
| VerifyOrReturnValue(mTemporalAcceptance.HasValue(), CHIP_NO_ERROR); |
| |
| ReturnErrorOnFailure(mTermsAndConditionsStorageDelegate->Set(mTemporalAcceptance.Value())); |
| ChipLogProgress(AppServer, "Terms and conditions have been committed"); |
| mTemporalAcceptance.ClearValue(); |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR chip::app::DefaultTermsAndConditionsProvider::GetAcceptance(Optional<TermsAndConditions> & outTermsAndConditions) const |
| { |
| VerifyOrReturnValue(nullptr != mTermsAndConditionsStorageDelegate, CHIP_ERROR_UNINITIALIZED); |
| |
| // Return the in-memory acceptance state |
| if (mTemporalAcceptance.HasValue()) |
| { |
| outTermsAndConditions = mTemporalAcceptance; |
| return CHIP_NO_ERROR; |
| } |
| |
| // Otherwise, try to get the persisted acceptance state |
| CHIP_ERROR err = mTermsAndConditionsStorageDelegate->Get(outTermsAndConditions); |
| VerifyOrReturnValue(CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND == err || CHIP_NO_ERROR == err, err); |
| |
| if (CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND == err) |
| { |
| ChipLogError(AppServer, "No terms and conditions have been accepted"); |
| outTermsAndConditions.ClearValue(); |
| return CHIP_NO_ERROR; |
| } |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR chip::app::DefaultTermsAndConditionsProvider::GetAcknowledgementsRequired(bool & outAcknowledgementsRequired) const |
| { |
| Optional<TermsAndConditions> requiredTermsAndConditionsMaybe; |
| ReturnErrorOnFailure(GetRequirements(requiredTermsAndConditionsMaybe)); |
| |
| if (!requiredTermsAndConditionsMaybe.HasValue()) |
| { |
| outAcknowledgementsRequired = false; |
| return CHIP_NO_ERROR; |
| } |
| |
| Optional<TermsAndConditions> acceptedTermsAndConditionsMaybe; |
| ReturnErrorOnFailure(GetAcceptance(acceptedTermsAndConditionsMaybe)); |
| |
| if (!acceptedTermsAndConditionsMaybe.HasValue()) |
| { |
| outAcknowledgementsRequired = true; |
| return CHIP_NO_ERROR; |
| } |
| |
| TermsAndConditions requiredTermsAndConditions = requiredTermsAndConditionsMaybe.Value(); |
| TermsAndConditions acceptedTermsAndConditions = acceptedTermsAndConditionsMaybe.Value(); |
| |
| bool requiredTermsAndConditionsAreAccepted = requiredTermsAndConditions.Validate(acceptedTermsAndConditions); |
| outAcknowledgementsRequired = !requiredTermsAndConditionsAreAccepted; |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR chip::app::DefaultTermsAndConditionsProvider::GetRequirements(Optional<TermsAndConditions> & outTermsAndConditions) const |
| { |
| outTermsAndConditions = mRequiredAcknowledgements; |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR |
| chip::app::DefaultTermsAndConditionsProvider::GetUpdateAcceptanceDeadline(Optional<uint32_t> & outUpdateAcceptanceDeadline) const |
| { |
| // No-op stub implementation. This feature is not implemented in this default implementation. |
| outUpdateAcceptanceDeadline = Optional<uint32_t>(); |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR chip::app::DefaultTermsAndConditionsProvider::ResetAcceptance() |
| { |
| VerifyOrReturnValue(nullptr != mTermsAndConditionsStorageDelegate, CHIP_ERROR_UNINITIALIZED); |
| |
| (void) mTermsAndConditionsStorageDelegate->Delete(); |
| ReturnErrorOnFailure(RevertAcceptance()); |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR chip::app::DefaultTermsAndConditionsProvider::RevertAcceptance() |
| { |
| mTemporalAcceptance.ClearValue(); |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR chip::app::DefaultTermsAndConditionsProvider::SetAcceptance(const Optional<TermsAndConditions> & inTermsAndConditions) |
| { |
| mTemporalAcceptance = inTermsAndConditions; |
| return CHIP_NO_ERROR; |
| } |