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 167 : 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 673 : 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 23402 : bool IsFree() const { return mState == State::Uninitialized; }
106 :
107 673 : ~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 10404 : 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 : friend class TestSessionRelease;
141 : enum class State : uint8_t
142 : {
143 : Uninitialized = 0, // The handler has not been initialized
144 : Initialized, // The handler has been initialized and is ready
145 : AddStatus, // The handler has added status code
146 : Sending, // The handler has sent out the write response
147 : };
148 : using Status = Protocols::InteractionModel::Status;
149 : Status ProcessWriteRequest(System::PacketBufferHandle && aPayload, bool aIsTimedWrite);
150 : Status HandleWriteRequestMessage(Messaging::ExchangeContext * apExchangeContext, System::PacketBufferHandle && aPayload,
151 : bool aIsTimedWrite);
152 :
153 : CHIP_ERROR FinalizeMessage(System::PacketBufferTLVWriter && aMessageWriter, System::PacketBufferHandle & packet);
154 : CHIP_ERROR SendWriteResponse(System::PacketBufferTLVWriter && aMessageWriter);
155 :
156 : void MoveToState(const State aTargetState);
157 : const char * GetStateStr() const;
158 :
159 : void DeliverListWriteBegin(const ConcreteAttributePath & aPath);
160 : void DeliverListWriteEnd(const ConcreteAttributePath & aPath, bool writeWasSuccessful);
161 :
162 : // Deliver the signal that we have delivered all list entries to the AttributeAccessInterface. This function will be called
163 : // after handling the last chunk of a series of write requests. Or the write handler was shutdown (usually due to transport
164 : // timeout).
165 : // This function will become no-op on group writes, since DeliverFinalListWriteEndForGroupWrite will clear the
166 : // mProcessingAttributePath after processing the AttributeDataIBs from the request.
167 : void DeliverFinalListWriteEnd(bool writeWasSuccessful);
168 :
169 : // Deliver the signal that we have delivered all list entries to the AttributeAccessInterface. This function will be called
170 : // after handling the last attribute in a group write request (since group writes will never be chunked writes). Or we failed to
171 : // process the group write request (usually due to malformed messages). This function should only be called by
172 : // ProcessGroupAttributeDataIBs.
173 : CHIP_ERROR DeliverFinalListWriteEndForGroupWrite(bool writeWasSuccessful);
174 :
175 : CHIP_ERROR AddStatusInternal(const ConcreteDataAttributePath & aPath, const StatusIB & aStatus);
176 :
177 : // ExchangeDelegate
178 : CHIP_ERROR OnMessageReceived(Messaging::ExchangeContext * apExchangeContext, const PayloadHeader & aPayloadHeader,
179 : System::PacketBufferHandle && aPayload) override;
180 : void OnResponseTimeout(Messaging::ExchangeContext * apExchangeContext) override;
181 :
182 : /// Validate that a write is acceptable on the given path.
183 : ///
184 : /// Validates that ACL, writability and Timed interaction settings are ok.
185 : ///
186 : /// Returns a success status if all is ok, failure otherwise.
187 : DataModel::ActionReturnStatus CheckWriteAllowed(const Access::SubjectDescriptor & aSubject,
188 : const ConcreteDataAttributePath & aPath);
189 :
190 : /// Validate that a write on the given path has aRequiredPrivilege.
191 : ///
192 : /// Returns a success status if the ACL check is successful, otherwise the relevant status will be returned.
193 : Status CheckWriteAccess(const Access::SubjectDescriptor & aSubject, const ConcreteAttributePath & aPath,
194 : const Access::Privilege aRequiredPrivilege);
195 :
196 : // Write the given data to the given path
197 : CHIP_ERROR WriteClusterData(const Access::SubjectDescriptor & aSubject, const ConcreteDataAttributePath & aPath,
198 : TLV::TLVReader & aData);
199 :
200 : /// Checks whether the given path corresponds to a list attribute
201 : /// Return values:
202 : /// true/false: valid attribute path, known if list or not
203 : /// std::nulloptr - path not available/valid, unknown if attribute is a list or not
204 : std::optional<bool> IsListAttributePath(const ConcreteAttributePath & path);
205 :
206 : Messaging::ExchangeHolder mExchangeCtx;
207 : WriteResponseMessage::Builder mWriteResponseBuilder;
208 : Optional<ConcreteAttributePath> mProcessingAttributePath;
209 :
210 : DataModel::Provider * mDataModelProvider = nullptr;
211 :
212 : // Required for legacy-encoded ACL list writes; lets CheckWriteAccess skip the re-check on repeated same-path writes
213 : // Legacy-encoded ACL list write produces, in one WriteRequestMessage:
214 : // #1 ReplaceAll([]) — empties ACL, removing the writer's own admin entry
215 : // #2 AppendItem(item) — same path; its ACL re-check would be denied if not for
216 : // mLastSuccessfullyWrittenPath cache.
217 : std::optional<ConcreteAttributePath> mLastSuccessfullyWrittenPath;
218 :
219 : // This may be a "fake" pointer or a real delegate pointer, depending
220 : // on CHIP_CONFIG_STATIC_GLOBAL_INTERACTION_MODEL_ENGINE setting.
221 : //
222 : // When this is not a real pointer, it checks that the value is always
223 : // set to the global InteractionModelEngine and the size of this
224 : // member is 1 byte.
225 : InteractionModelDelegatePointer<WriteHandlerDelegate> mDelegate;
226 :
227 : // bit level enums to save storage for this object. InteractionModelEngine maintains
228 : // several of these objects, so every bit of storage multiplies storage usage.
229 : enum class StateBits : uint8_t
230 : {
231 : kIsTimedRequest = 0x01,
232 : kSuppressResponse = 0x02,
233 : kHasMoreChunks = 0x04,
234 : kProcessingAttributeIsList = 0x08,
235 : // We record the Status when AddStatus is called to determine whether all data of a list write is accepted.
236 : // This value will be used by DeliverListWriteEnd and DeliverFinalListWriteEnd but it won't be used by group writes based on
237 : // the fact that the errors that won't be delivered to AttributeAccessInterface are:
238 : // (1) Attribute not found
239 : // (2) Access control failed
240 : // (3) Write request to a read-only attribute
241 : // (4) Data version mismatch
242 : // (5) Not using timed write.
243 : // Where (1)-(3) will be consistent among the whole list write request, while (4) and (5) are not appliable to group
244 : // writes.
245 : kAttributeWriteSuccessful = 0x10,
246 : };
247 :
248 : BitFlags<StateBits> mStateFlags;
249 : State mState = State::Uninitialized;
250 : };
251 :
252 : } // namespace app
253 : } // namespace chip
|