Line data Source code
1 : /*
2 : *
3 : * Copyright (c) 2021 Project CHIP Authors
4 : * All rights reserved.
5 : *
6 : * Licensed under the Apache License, Version 2.0 (the "License");
7 : * you may not use this file except in compliance with the License.
8 : * You may obtain a copy of the License at
9 : *
10 : * http://www.apache.org/licenses/LICENSE-2.0
11 : *
12 : * Unless required by applicable law or agreed to in writing, software
13 : * distributed under the License is distributed on an "AS IS" BASIS,
14 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 : * See the License for the specific language governing permissions and
16 : * limitations under the License.
17 : */
18 :
19 : #pragma once
20 :
21 : #include <app/AppConfig.h>
22 : #include <app/AttributePathParams.h>
23 : #include <app/ConcreteAttributePath.h>
24 : #include <app/InteractionModelDelegatePointers.h>
25 : #include <app/MessageDef/WriteResponseMessage.h>
26 : #include <app/data-model-provider/ActionReturnStatus.h>
27 : #include <app/data-model-provider/Provider.h>
28 : #include <lib/core/CHIPCore.h>
29 : #include <lib/core/TLVDebug.h>
30 : #include <lib/support/BitFlags.h>
31 : #include <lib/support/CodeUtils.h>
32 : #include <lib/support/DLLUtil.h>
33 : #include <lib/support/logging/CHIPLogging.h>
34 : #include <messaging/ExchangeHolder.h>
35 : #include <messaging/ExchangeMgr.h>
36 : #include <messaging/Flags.h>
37 : #include <protocols/Protocols.h>
38 : #include <protocols/interaction_model/Constants.h>
39 : #include <protocols/interaction_model/StatusCode.h>
40 : #include <system/SystemPacketBuffer.h>
41 : #include <system/TLVPacketBufferBackingStore.h>
42 :
43 : namespace chip {
44 : namespace app {
45 :
46 : class WriteHandler;
47 :
48 : class WriteHandlerDelegate
49 : {
50 : public:
51 163 : virtual ~WriteHandlerDelegate() = default;
52 :
53 : /**
54 : * Returns whether the write operation to the given path is in conflict with another write operation.
55 : * (i.e. another write transaction is in the middle of processing a chunked write to the given path.)
56 : */
57 : virtual bool HasConflictWriteRequests(const WriteHandler * apWriteHandler, const ConcreteAttributePath & aPath) = 0;
58 : };
59 :
60 : /**
61 : * @brief The write handler is responsible for processing a write request and sending a write reply.
62 : */
63 : class WriteHandler : public Messaging::ExchangeDelegate
64 : {
65 : public:
66 656 : WriteHandler() : mExchangeCtx(*this) {}
67 :
68 : /**
69 : * Initialize the WriteHandler. Within the lifetime
70 : * of this instance, this method is invoked once after object
71 : * construction until a call to Close is made to terminate the
72 : * instance.
73 : *
74 : * @param[in] apProvider A valid pointer to the model used to forward writes towards
75 : * @param[in] apWriteHandlerDelegate A Valid pointer to the WriteHandlerDelegate.
76 : *
77 : * @retval #CHIP_ERROR_INVALID_ARGUMENT on invalid pointers
78 : * @retval #CHIP_ERROR_INCORRECT_STATE If the state is not equal to
79 : * kState_NotInitialized.
80 : * @retval #CHIP_NO_ERROR On success.
81 : *
82 : */
83 : CHIP_ERROR Init(DataModel::Provider * apProvider, WriteHandlerDelegate * apWriteHandlerDelegate);
84 :
85 : /**
86 : * Process a write request. Parts of the processing may end up being asynchronous, but the WriteHandler
87 : * guarantees that it will call Close on itself when processing is done (including if OnWriteRequest
88 : * returns an error).
89 : *
90 : * @param[in] apExchangeContext A pointer to the ExchangeContext.
91 : * @param[in] aPayload A payload that has read request data
92 : * @param[in] aIsTimedWrite Whether write is part of a timed interaction.
93 : *
94 : * @retval Status. Callers are expected to send a status response if the
95 : * return status is not Status::Success.
96 : */
97 : Protocols::InteractionModel::Status OnWriteRequest(Messaging::ExchangeContext * apExchangeContext,
98 : System::PacketBufferHandle && aPayload, bool aIsTimedWrite);
99 :
100 : /**
101 : * Clean up state when we are done sending the write response.
102 : */
103 : void Close();
104 :
105 23312 : bool IsFree() const { return mState == State::Uninitialized; }
106 :
107 656 : ~WriteHandler() override = default;
108 :
109 : CHIP_ERROR ProcessAttributeDataIBs(TLV::TLVReader & aAttributeDataIBsReader);
110 : CHIP_ERROR ProcessGroupAttributeDataIBs(TLV::TLVReader & aAttributeDataIBsReader);
111 :
112 : CHIP_ERROR AddStatus(const ConcreteDataAttributePath & aPath, const Protocols::InteractionModel::ClusterStatusCode & aStatus);
113 0 : CHIP_ERROR AddStatus(const ConcreteDataAttributePath & aPath, const Protocols::InteractionModel::Status aStatus)
114 : {
115 0 : return AddStatus(aPath, Protocols::InteractionModel::ClusterStatusCode{ aStatus });
116 : }
117 :
118 : CHIP_ERROR AddClusterSpecificSuccess(const ConcreteDataAttributePath & aAttributePathParams, ClusterStatus aClusterStatus);
119 : CHIP_ERROR AddClusterSpecificFailure(const ConcreteDataAttributePath & aAttributePathParams, ClusterStatus aClusterStatus);
120 :
121 : FabricIndex GetAccessingFabricIndex() const;
122 :
123 : /**
124 : * Check whether the WriteRequest we are handling is a timed write.
125 : */
126 10400 : bool IsTimedWrite() const { return mStateFlags.Has(StateBits::kIsTimedRequest); }
127 :
128 : bool MatchesExchangeContext(Messaging::ExchangeContext * apExchangeContext) const
129 : {
130 : return !IsFree() && mExchangeCtx.Get() == apExchangeContext;
131 : }
132 :
133 22 : bool IsCurrentlyProcessingWritePath(const ConcreteAttributePath & aPath)
134 : {
135 22 : return mProcessingAttributePath.HasValue() && mProcessingAttributePath.Value() == aPath;
136 : }
137 :
138 : private:
139 : friend class TestWriteInteraction;
140 : enum class State : uint8_t
141 : {
142 : Uninitialized = 0, // The handler has not been initialized
143 : Initialized, // The handler has been initialized and is ready
144 : AddStatus, // The handler has added status code
145 : Sending, // The handler has sent out the write response
146 : };
147 : using Status = Protocols::InteractionModel::Status;
148 : Status ProcessWriteRequest(System::PacketBufferHandle && aPayload, bool aIsTimedWrite);
149 : Status HandleWriteRequestMessage(Messaging::ExchangeContext * apExchangeContext, System::PacketBufferHandle && aPayload,
150 : bool aIsTimedWrite);
151 :
152 : CHIP_ERROR FinalizeMessage(System::PacketBufferTLVWriter && aMessageWriter, System::PacketBufferHandle & packet);
153 : CHIP_ERROR SendWriteResponse(System::PacketBufferTLVWriter && aMessageWriter);
154 :
155 : void MoveToState(const State aTargetState);
156 : const char * GetStateStr() const;
157 :
158 : void DeliverListWriteBegin(const ConcreteAttributePath & aPath);
159 : void DeliverListWriteEnd(const ConcreteAttributePath & aPath, bool writeWasSuccessful);
160 :
161 : // Deliver the signal that we have delivered all list entries to the AttributeAccessInterface. This function will be called
162 : // after handling the last chunk of a series of write requests. Or the write handler was shutdown (usually due to transport
163 : // timeout).
164 : // This function will become no-op on group writes, since DeliverFinalListWriteEndForGroupWrite will clear the
165 : // mProcessingAttributePath after processing the AttributeDataIBs from the request.
166 : void DeliverFinalListWriteEnd(bool writeWasSuccessful);
167 :
168 : // Deliver the signal that we have delivered all list entries to the AttributeAccessInterface. This function will be called
169 : // after handling the last attribute in a group write request (since group writes will never be chunked writes). Or we failed to
170 : // process the group write request (usually due to malformed messages). This function should only be called by
171 : // ProcessGroupAttributeDataIBs.
172 : CHIP_ERROR DeliverFinalListWriteEndForGroupWrite(bool writeWasSuccessful);
173 :
174 : CHIP_ERROR AddStatusInternal(const ConcreteDataAttributePath & aPath, const StatusIB & aStatus);
175 :
176 : // ExchangeDelegate
177 : CHIP_ERROR OnMessageReceived(Messaging::ExchangeContext * apExchangeContext, const PayloadHeader & aPayloadHeader,
178 : System::PacketBufferHandle && aPayload) override;
179 : void OnResponseTimeout(Messaging::ExchangeContext * apExchangeContext) override;
180 :
181 : /// Validate that a write is acceptable on the given path.
182 : ///
183 : /// Validates that ACL, writability and Timed interaction settings are ok.
184 : ///
185 : /// Returns a success status if all is ok, failure otherwise.
186 : DataModel::ActionReturnStatus CheckWriteAllowed(const Access::SubjectDescriptor & aSubject,
187 : const ConcreteDataAttributePath & aPath);
188 :
189 : /// Validate that a write on the given path has aRequiredPrivilege.
190 : ///
191 : /// Returns a success status if the ACL check is successful, otherwise the relevant status will be returned.
192 : Status CheckWriteAccess(const Access::SubjectDescriptor & aSubject, const ConcreteAttributePath & aPath,
193 : const Access::Privilege aRequiredPrivilege);
194 :
195 : // Write the given data to the given path
196 : CHIP_ERROR WriteClusterData(const Access::SubjectDescriptor & aSubject, const ConcreteDataAttributePath & aPath,
197 : TLV::TLVReader & aData);
198 :
199 : /// Checks whether the given path corresponds to a list attribute
200 : /// Return values:
201 : /// true/false: valid attribute path, known if list or not
202 : /// std::nulloptr - path not available/valid, unknown if attribute is a list or not
203 : std::optional<bool> IsListAttributePath(const ConcreteAttributePath & path);
204 :
205 : Messaging::ExchangeHolder mExchangeCtx;
206 : WriteResponseMessage::Builder mWriteResponseBuilder;
207 : Optional<ConcreteAttributePath> mProcessingAttributePath;
208 :
209 : DataModel::Provider * mDataModelProvider = nullptr;
210 :
211 : // Required for legacy-encoded ACL list writes; lets CheckWriteAccess skip the re-check on repeated same-path writes
212 : // Legacy-encoded ACL list write produces, in one WriteRequestMessage:
213 : // #1 ReplaceAll([]) — empties ACL, removing the writer's own admin entry
214 : // #2 AppendItem(item) — same path; its ACL re-check would be denied if not for
215 : // mLastSuccessfullyWrittenPath cache.
216 : std::optional<ConcreteAttributePath> mLastSuccessfullyWrittenPath;
217 :
218 : // This may be a "fake" pointer or a real delegate pointer, depending
219 : // on CHIP_CONFIG_STATIC_GLOBAL_INTERACTION_MODEL_ENGINE setting.
220 : //
221 : // When this is not a real pointer, it checks that the value is always
222 : // set to the global InteractionModelEngine and the size of this
223 : // member is 1 byte.
224 : InteractionModelDelegatePointer<WriteHandlerDelegate> mDelegate;
225 :
226 : // bit level enums to save storage for this object. InteractionModelEngine maintains
227 : // several of these objects, so every bit of storage multiplies storage usage.
228 : enum class StateBits : uint8_t
229 : {
230 : kIsTimedRequest = 0x01,
231 : kSuppressResponse = 0x02,
232 : kHasMoreChunks = 0x04,
233 : kProcessingAttributeIsList = 0x08,
234 : // We record the Status when AddStatus is called to determine whether all data of a list write is accepted.
235 : // This value will be used by DeliverListWriteEnd and DeliverFinalListWriteEnd but it won't be used by group writes based on
236 : // the fact that the errors that won't be delivered to AttributeAccessInterface are:
237 : // (1) Attribute not found
238 : // (2) Access control failed
239 : // (3) Write request to a read-only attribute
240 : // (4) Data version mismatch
241 : // (5) Not using timed write.
242 : // Where (1)-(3) will be consistent among the whole list write request, while (4) and (5) are not appliable to group
243 : // writes.
244 : kAttributeWriteSuccessful = 0x10,
245 : };
246 :
247 : BitFlags<StateBits> mStateFlags;
248 : State mState = State::Uninitialized;
249 : };
250 :
251 : } // namespace app
252 : } // namespace chip
|