blob: 4a05a7d3ff8b87438e5b0897c388d39656fec1db [file] [log] [blame]
/*
* 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;
}