1 /*
2 // Copyright (c) 2020 Intel Corporation
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //      http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 */
16 #pragma once
17 #include "app.hpp"
18 #include "event_service_manager.hpp"
19 #include "http/utility.hpp"
20 #include "logging.hpp"
21 #include "query.hpp"
22 #include "registries/privilege_registry.hpp"
23 #include "snmp_trap_event_clients.hpp"
24 
25 #include <boost/beast/http/fields.hpp>
26 #include <boost/system/error_code.hpp>
27 #include <sdbusplus/unpack_properties.hpp>
28 #include <utils/dbus_utils.hpp>
29 
30 #include <charconv>
31 #include <memory>
32 #include <span>
33 #include <string>
34 
35 namespace redfish
36 {
37 
38 static constexpr const std::array<const char*, 2> supportedEvtFormatTypes = {
39     eventFormatType, metricReportFormatType};
40 static constexpr const std::array<const char*, 3> supportedRegPrefixes = {
41     "Base", "OpenBMC", "TaskEvent"};
42 static constexpr const std::array<const char*, 3> supportedRetryPolicies = {
43     "TerminateAfterRetries", "SuspendRetries", "RetryForever"};
44 
45 #ifdef BMCWEB_ENABLE_IBM_MANAGEMENT_CONSOLE
46 static constexpr const std::array<const char*, 2> supportedResourceTypes = {
47     "IBMConfigFile", "Task"};
48 #else
49 static constexpr const std::array<const char*, 1> supportedResourceTypes = {
50     "Task"};
51 #endif
52 
53 inline void requestRoutesEventService(App& app)
54 {
55     BMCWEB_ROUTE(app, "/redfish/v1/EventService/")
56         .privileges(redfish::privileges::getEventService)
57         .methods(boost::beast::http::verb::get)(
58             [&app](const crow::Request& req,
59                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
60         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
61         {
62             return;
63         }
64 
65         asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/EventService";
66         asyncResp->res.jsonValue["@odata.type"] =
67             "#EventService.v1_5_0.EventService";
68         asyncResp->res.jsonValue["Id"] = "EventService";
69         asyncResp->res.jsonValue["Name"] = "Event Service";
70         asyncResp->res.jsonValue["ServerSentEventUri"] =
71             "/redfish/v1/EventService/SSE";
72 
73         asyncResp->res.jsonValue["Subscriptions"]["@odata.id"] =
74             "/redfish/v1/EventService/Subscriptions";
75         asyncResp->res
76             .jsonValue["Actions"]["#EventService.SubmitTestEvent"]["target"] =
77             "/redfish/v1/EventService/Actions/EventService.SubmitTestEvent";
78 
79         const persistent_data::EventServiceConfig eventServiceConfig =
80             persistent_data::EventServiceStore::getInstance()
81                 .getEventServiceConfig();
82 
83         asyncResp->res.jsonValue["Status"]["State"] =
84             (eventServiceConfig.enabled ? "Enabled" : "Disabled");
85         asyncResp->res.jsonValue["ServiceEnabled"] = eventServiceConfig.enabled;
86         asyncResp->res.jsonValue["DeliveryRetryAttempts"] =
87             eventServiceConfig.retryAttempts;
88         asyncResp->res.jsonValue["DeliveryRetryIntervalSeconds"] =
89             eventServiceConfig.retryTimeoutInterval;
90         asyncResp->res.jsonValue["EventFormatTypes"] = supportedEvtFormatTypes;
91         asyncResp->res.jsonValue["RegistryPrefixes"] = supportedRegPrefixes;
92         asyncResp->res.jsonValue["ResourceTypes"] = supportedResourceTypes;
93 
94         nlohmann::json::object_t supportedSSEFilters;
95         supportedSSEFilters["EventFormatType"] = true;
96         supportedSSEFilters["MessageId"] = true;
97         supportedSSEFilters["MetricReportDefinition"] = true;
98         supportedSSEFilters["RegistryPrefix"] = true;
99         supportedSSEFilters["OriginResource"] = false;
100         supportedSSEFilters["ResourceType"] = false;
101 
102         asyncResp->res.jsonValue["SSEFilterPropertiesSupported"] =
103             std::move(supportedSSEFilters);
104         });
105 
106     BMCWEB_ROUTE(app, "/redfish/v1/EventService/")
107         .privileges(redfish::privileges::patchEventService)
108         .methods(boost::beast::http::verb::patch)(
109             [&app](const crow::Request& req,
110                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
111         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
112         {
113             return;
114         }
115         std::optional<bool> serviceEnabled;
116         std::optional<uint32_t> retryAttemps;
117         std::optional<uint32_t> retryInterval;
118 
119         if (!json_util::readJsonPatch(
120                 req, asyncResp->res, "ServiceEnabled", serviceEnabled,
121                 "DeliveryRetryAttempts", retryAttemps,
122                 "DeliveryRetryIntervalSeconds", retryInterval))
123         {
124             return;
125         }
126 
127         persistent_data::EventServiceConfig eventServiceConfig =
128             persistent_data::EventServiceStore::getInstance()
129                 .getEventServiceConfig();
130 
131         if (serviceEnabled)
132         {
133             eventServiceConfig.enabled = *serviceEnabled;
134         }
135 
136         if (retryAttemps)
137         {
138             // Supported range [1-3]
139             if ((*retryAttemps < 1) || (*retryAttemps > 3))
140             {
141                 messages::queryParameterOutOfRange(
142                     asyncResp->res, std::to_string(*retryAttemps),
143                     "DeliveryRetryAttempts", "[1-3]");
144             }
145             else
146             {
147                 eventServiceConfig.retryAttempts = *retryAttemps;
148             }
149         }
150 
151         if (retryInterval)
152         {
153             // Supported range [5 - 180]
154             if ((*retryInterval < 5) || (*retryInterval > 180))
155             {
156                 messages::queryParameterOutOfRange(
157                     asyncResp->res, std::to_string(*retryInterval),
158                     "DeliveryRetryIntervalSeconds", "[5-180]");
159             }
160             else
161             {
162                 eventServiceConfig.retryTimeoutInterval = *retryInterval;
163             }
164         }
165 
166         EventServiceManager::getInstance().setEventServiceConfig(
167             eventServiceConfig);
168         });
169 }
170 
171 inline void requestRoutesSubmitTestEvent(App& app)
172 {
173     BMCWEB_ROUTE(
174         app, "/redfish/v1/EventService/Actions/EventService.SubmitTestEvent/")
175         .privileges(redfish::privileges::postEventService)
176         .methods(boost::beast::http::verb::post)(
177             [&app](const crow::Request& req,
178                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
179         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
180         {
181             return;
182         }
183         if (!EventServiceManager::getInstance().sendTestEventLog())
184         {
185             messages::serviceDisabled(asyncResp->res,
186                                       "/redfish/v1/EventService/");
187             return;
188         }
189         asyncResp->res.result(boost::beast::http::status::no_content);
190         });
191 }
192 
193 inline void doSubscriptionCollection(
194     const boost::system::error_code& ec,
195     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
196     const dbus::utility::ManagedObjectType& resp)
197 {
198     if (ec)
199     {
200         if (ec.value() == EBADR)
201         {
202             // This is an optional process so just return if it isn't there
203             return;
204         }
205 
206         BMCWEB_LOG_ERROR << "D-Bus response error on GetManagedObjects " << ec;
207         messages::internalError(asyncResp->res);
208         return;
209     }
210     nlohmann::json& memberArray = asyncResp->res.jsonValue["Members"];
211     for (const auto& objpath : resp)
212     {
213         sdbusplus::message::object_path path(objpath.first);
214         const std::string snmpId = path.filename();
215         if (snmpId.empty())
216         {
217             BMCWEB_LOG_ERROR << "The SNMP client ID is wrong";
218             messages::internalError(asyncResp->res);
219             return;
220         }
221 
222         getSnmpSubscriptionList(asyncResp, snmpId, memberArray);
223     }
224 }
225 
226 inline void requestRoutesEventDestinationCollection(App& app)
227 {
228     BMCWEB_ROUTE(app, "/redfish/v1/EventService/Subscriptions/")
229         .privileges(redfish::privileges::getEventDestinationCollection)
230         .methods(boost::beast::http::verb::get)(
231             [&app](const crow::Request& req,
232                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
233         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
234         {
235             return;
236         }
237         asyncResp->res.jsonValue["@odata.type"] =
238             "#EventDestinationCollection.EventDestinationCollection";
239         asyncResp->res.jsonValue["@odata.id"] =
240             "/redfish/v1/EventService/Subscriptions";
241         asyncResp->res.jsonValue["Name"] = "Event Destination Collections";
242 
243         nlohmann::json& memberArray = asyncResp->res.jsonValue["Members"];
244 
245         std::vector<std::string> subscripIds =
246             EventServiceManager::getInstance().getAllIDs();
247         memberArray = nlohmann::json::array();
248         asyncResp->res.jsonValue["Members@odata.count"] = subscripIds.size();
249 
250         for (const std::string& id : subscripIds)
251         {
252             nlohmann::json::object_t member;
253             member["@odata.id"] = boost::urls::format(
254                 "/redfish/v1/EventService/Subscriptions/{}" + id);
255             memberArray.emplace_back(std::move(member));
256         }
257         crow::connections::systemBus->async_method_call(
258             [asyncResp](const boost::system::error_code& ec,
259                         const dbus::utility::ManagedObjectType& resp) {
260             doSubscriptionCollection(ec, asyncResp, resp);
261             },
262             "xyz.openbmc_project.Network.SNMP",
263             "/xyz/openbmc_project/network/snmp/manager",
264             "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
265         });
266 
267     BMCWEB_ROUTE(app, "/redfish/v1/EventService/Subscriptions/")
268         .privileges(redfish::privileges::postEventDestinationCollection)
269         .methods(boost::beast::http::verb::post)(
270             [&app](const crow::Request& req,
271                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
272         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
273         {
274             return;
275         }
276         if (EventServiceManager::getInstance().getNumberOfSubscriptions() >=
277             maxNoOfSubscriptions)
278         {
279             messages::eventSubscriptionLimitExceeded(asyncResp->res);
280             return;
281         }
282         std::string destUrl;
283         std::string protocol;
284         std::optional<std::string> context;
285         std::optional<std::string> subscriptionType;
286         std::optional<std::string> eventFormatType2;
287         std::optional<std::string> retryPolicy;
288         std::optional<std::vector<std::string>> msgIds;
289         std::optional<std::vector<std::string>> regPrefixes;
290         std::optional<std::vector<std::string>> resTypes;
291         std::optional<std::vector<nlohmann::json>> headers;
292         std::optional<std::vector<nlohmann::json>> mrdJsonArray;
293 
294         if (!json_util::readJsonPatch(
295                 req, asyncResp->res, "Destination", destUrl, "Context", context,
296                 "Protocol", protocol, "SubscriptionType", subscriptionType,
297                 "EventFormatType", eventFormatType2, "HttpHeaders", headers,
298                 "RegistryPrefixes", regPrefixes, "MessageIds", msgIds,
299                 "DeliveryRetryPolicy", retryPolicy, "MetricReportDefinitions",
300                 mrdJsonArray, "ResourceTypes", resTypes))
301         {
302             return;
303         }
304 
305         // https://stackoverflow.com/questions/417142/what-is-the-maximum-length-of-a-url-in-different-browsers
306         static constexpr const uint16_t maxDestinationSize = 2000;
307         if (destUrl.size() > maxDestinationSize)
308         {
309             messages::stringValueTooLong(asyncResp->res, "Destination",
310                                          maxDestinationSize);
311             return;
312         }
313 
314         if (regPrefixes && msgIds)
315         {
316             if (!regPrefixes->empty() && !msgIds->empty())
317             {
318                 messages::propertyValueConflict(asyncResp->res, "MessageIds",
319                                                 "RegistryPrefixes");
320                 return;
321             }
322         }
323 
324         std::string host;
325         std::string urlProto;
326         uint16_t port = 0;
327         std::string path;
328 
329         if (!crow::utility::validateAndSplitUrl(destUrl, urlProto, host, port,
330                                                 path))
331         {
332             BMCWEB_LOG_WARNING
333                 << "Failed to validate and split destination url";
334             messages::propertyValueFormatError(asyncResp->res, destUrl,
335                                                "Destination");
336             return;
337         }
338 
339         if (protocol == "SNMPv2c")
340         {
341             if (context)
342             {
343                 messages::propertyValueConflict(asyncResp->res, "Context",
344                                                 "Protocol");
345                 return;
346             }
347             if (eventFormatType2)
348             {
349                 messages::propertyValueConflict(asyncResp->res,
350                                                 "EventFormatType", "Protocol");
351                 return;
352             }
353             if (retryPolicy)
354             {
355                 messages::propertyValueConflict(asyncResp->res, "RetryPolicy",
356                                                 "Protocol");
357                 return;
358             }
359             if (msgIds)
360             {
361                 messages::propertyValueConflict(asyncResp->res, "MessageIds",
362                                                 "Protocol");
363                 return;
364             }
365             if (regPrefixes)
366             {
367                 messages::propertyValueConflict(asyncResp->res,
368                                                 "RegistryPrefixes", "Protocol");
369                 return;
370             }
371             if (resTypes)
372             {
373                 messages::propertyValueConflict(asyncResp->res, "ResourceTypes",
374                                                 "Protocol");
375                 return;
376             }
377             if (headers)
378             {
379                 messages::propertyValueConflict(asyncResp->res, "HttpHeaders",
380                                                 "Protocol");
381                 return;
382             }
383             if (mrdJsonArray)
384             {
385                 messages::propertyValueConflict(
386                     asyncResp->res, "MetricReportDefinitions", "Protocol");
387                 return;
388             }
389 
390             addSnmpTrapClient(asyncResp, host, port);
391             return;
392         }
393 
394         if (path.empty())
395         {
396             path = "/";
397         }
398 
399         std::shared_ptr<Subscription> subValue = std::make_shared<Subscription>(
400             host, port, path, urlProto, app.ioContext());
401 
402         subValue->destinationUrl = destUrl;
403 
404         if (subscriptionType)
405         {
406             if (*subscriptionType != "RedfishEvent")
407             {
408                 messages::propertyValueNotInList(
409                     asyncResp->res, *subscriptionType, "SubscriptionType");
410                 return;
411             }
412             subValue->subscriptionType = *subscriptionType;
413         }
414         else
415         {
416             subValue->subscriptionType = "RedfishEvent"; // Default
417         }
418 
419         if (protocol != "Redfish")
420         {
421             messages::propertyValueNotInList(asyncResp->res, protocol,
422                                              "Protocol");
423             return;
424         }
425         subValue->protocol = protocol;
426 
427         if (eventFormatType2)
428         {
429             if (std::find(supportedEvtFormatTypes.begin(),
430                           supportedEvtFormatTypes.end(),
431                           *eventFormatType2) == supportedEvtFormatTypes.end())
432             {
433                 messages::propertyValueNotInList(
434                     asyncResp->res, *eventFormatType2, "EventFormatType");
435                 return;
436             }
437             subValue->eventFormatType = *eventFormatType2;
438         }
439         else
440         {
441             // If not specified, use default "Event"
442             subValue->eventFormatType = "Event";
443         }
444 
445         if (context)
446         {
447             // This value is selected aribitrarily.
448             constexpr const size_t maxContextSize = 256;
449             if (context->size() > maxContextSize)
450             {
451                 messages::stringValueTooLong(asyncResp->res, "Context",
452                                              maxContextSize);
453                 return;
454             }
455             subValue->customText = *context;
456         }
457 
458         if (headers)
459         {
460             size_t cumulativeLen = 0;
461 
462             for (const nlohmann::json& headerChunk : *headers)
463             {
464                 std::string hdr{headerChunk.dump(
465                     -1, ' ', true, nlohmann::json::error_handler_t::replace)};
466                 cumulativeLen += hdr.length();
467 
468                 // This value is selected to mirror http_connection.hpp
469                 constexpr const uint16_t maxHeaderSizeED = 8096;
470                 if (cumulativeLen > maxHeaderSizeED)
471                 {
472                     messages::arraySizeTooLong(asyncResp->res, "HttpHeaders",
473                                                maxHeaderSizeED);
474                     return;
475                 }
476                 for (const auto& item : headerChunk.items())
477                 {
478                     const std::string* value =
479                         item.value().get_ptr<const std::string*>();
480                     if (value == nullptr)
481                     {
482                         messages::propertyValueFormatError(
483                             asyncResp->res, item.value(),
484                             "HttpHeaders/" + item.key());
485                         return;
486                     }
487                     subValue->httpHeaders.set(item.key(), *value);
488                 }
489             }
490         }
491 
492         if (regPrefixes)
493         {
494             for (const std::string& it : *regPrefixes)
495             {
496                 if (std::find(supportedRegPrefixes.begin(),
497                               supportedRegPrefixes.end(),
498                               it) == supportedRegPrefixes.end())
499                 {
500                     messages::propertyValueNotInList(asyncResp->res, it,
501                                                      "RegistryPrefixes");
502                     return;
503                 }
504             }
505             subValue->registryPrefixes = *regPrefixes;
506         }
507 
508         if (resTypes)
509         {
510             for (const std::string& it : *resTypes)
511             {
512                 if (std::find(supportedResourceTypes.begin(),
513                               supportedResourceTypes.end(),
514                               it) == supportedResourceTypes.end())
515                 {
516                     messages::propertyValueNotInList(asyncResp->res, it,
517                                                      "ResourceTypes");
518                     return;
519                 }
520             }
521             subValue->resourceTypes = *resTypes;
522         }
523 
524         if (msgIds)
525         {
526             std::vector<std::string> registryPrefix;
527 
528             // If no registry prefixes are mentioned, consider all
529             // supported prefixes
530             if (subValue->registryPrefixes.empty())
531             {
532                 registryPrefix.assign(supportedRegPrefixes.begin(),
533                                       supportedRegPrefixes.end());
534             }
535             else
536             {
537                 registryPrefix = subValue->registryPrefixes;
538             }
539 
540             for (const std::string& id : *msgIds)
541             {
542                 bool validId = false;
543 
544                 // Check for Message ID in each of the selected Registry
545                 for (const std::string& it : registryPrefix)
546                 {
547                     const std::span<const redfish::registries::MessageEntry>
548                         registry =
549                             redfish::registries::getRegistryFromPrefix(it);
550 
551                     if (std::any_of(
552                             registry.begin(), registry.end(),
553                             [&id](const redfish::registries::MessageEntry&
554                                       messageEntry) {
555                         return id == messageEntry.first;
556                             }))
557                     {
558                         validId = true;
559                         break;
560                     }
561                 }
562 
563                 if (!validId)
564                 {
565                     messages::propertyValueNotInList(asyncResp->res, id,
566                                                      "MessageIds");
567                     return;
568                 }
569             }
570 
571             subValue->registryMsgIds = *msgIds;
572         }
573 
574         if (retryPolicy)
575         {
576             if (std::find(supportedRetryPolicies.begin(),
577                           supportedRetryPolicies.end(),
578                           *retryPolicy) == supportedRetryPolicies.end())
579             {
580                 messages::propertyValueNotInList(asyncResp->res, *retryPolicy,
581                                                  "DeliveryRetryPolicy");
582                 return;
583             }
584             subValue->retryPolicy = *retryPolicy;
585         }
586         else
587         {
588             // Default "TerminateAfterRetries"
589             subValue->retryPolicy = "TerminateAfterRetries";
590         }
591 
592         if (mrdJsonArray)
593         {
594             for (nlohmann::json& mrdObj : *mrdJsonArray)
595             {
596                 std::string mrdUri;
597 
598                 if (!json_util::readJson(mrdObj, asyncResp->res, "@odata.id",
599                                          mrdUri))
600 
601                 {
602                     return;
603                 }
604                 subValue->metricReportDefinitions.emplace_back(mrdUri);
605             }
606         }
607 
608         std::string id =
609             EventServiceManager::getInstance().addSubscription(subValue);
610         if (id.empty())
611         {
612             messages::internalError(asyncResp->res);
613             return;
614         }
615 
616         messages::created(asyncResp->res);
617         asyncResp->res.addHeader(
618             "Location", "/redfish/v1/EventService/Subscriptions/" + id);
619         });
620 }
621 
622 inline void requestRoutesEventDestination(App& app)
623 {
624     BMCWEB_ROUTE(app, "/redfish/v1/EventService/Subscriptions/<str>/")
625         .privileges(redfish::privileges::getEventDestination)
626         .methods(boost::beast::http::verb::get)(
627             [&app](const crow::Request& req,
628                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
629                    const std::string& param) {
630         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
631         {
632             return;
633         }
634 
635         if (param.starts_with("snmp"))
636         {
637             getSnmpTrapClient(asyncResp, param);
638             return;
639         }
640 
641         std::shared_ptr<Subscription> subValue =
642             EventServiceManager::getInstance().getSubscription(param);
643         if (subValue == nullptr)
644         {
645             asyncResp->res.result(boost::beast::http::status::not_found);
646             return;
647         }
648         const std::string& id = param;
649 
650         asyncResp->res.jsonValue["@odata.type"] =
651             "#EventDestination.v1_8_0.EventDestination";
652         asyncResp->res.jsonValue["Protocol"] = "Redfish";
653         asyncResp->res.jsonValue["@odata.id"] =
654             "/redfish/v1/EventService/Subscriptions/" + id;
655         asyncResp->res.jsonValue["Id"] = id;
656         asyncResp->res.jsonValue["Name"] = "Event Destination " + id;
657         asyncResp->res.jsonValue["Destination"] = subValue->destinationUrl;
658         asyncResp->res.jsonValue["Context"] = subValue->customText;
659         asyncResp->res.jsonValue["SubscriptionType"] =
660             subValue->subscriptionType;
661         asyncResp->res.jsonValue["HttpHeaders"] = nlohmann::json::array();
662         asyncResp->res.jsonValue["EventFormatType"] = subValue->eventFormatType;
663         asyncResp->res.jsonValue["RegistryPrefixes"] =
664             subValue->registryPrefixes;
665         asyncResp->res.jsonValue["ResourceTypes"] = subValue->resourceTypes;
666 
667         asyncResp->res.jsonValue["MessageIds"] = subValue->registryMsgIds;
668         asyncResp->res.jsonValue["DeliveryRetryPolicy"] = subValue->retryPolicy;
669 
670         nlohmann::json::array_t mrdJsonArray;
671         for (const auto& mdrUri : subValue->metricReportDefinitions)
672         {
673             nlohmann::json::object_t mdr;
674             mdr["@odata.id"] = mdrUri;
675             mrdJsonArray.emplace_back(std::move(mdr));
676         }
677         asyncResp->res.jsonValue["MetricReportDefinitions"] = mrdJsonArray;
678         });
679     BMCWEB_ROUTE(app, "/redfish/v1/EventService/Subscriptions/<str>/")
680         // The below privilege is wrong, it should be ConfigureManager OR
681         // ConfigureSelf
682         // https://github.com/openbmc/bmcweb/issues/220
683         //.privileges(redfish::privileges::patchEventDestination)
684         .privileges({{"ConfigureManager"}})
685         .methods(boost::beast::http::verb::patch)(
686             [&app](const crow::Request& req,
687                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
688                    const std::string& param) {
689         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
690         {
691             return;
692         }
693         std::shared_ptr<Subscription> subValue =
694             EventServiceManager::getInstance().getSubscription(param);
695         if (subValue == nullptr)
696         {
697             asyncResp->res.result(boost::beast::http::status::not_found);
698             return;
699         }
700 
701         std::optional<std::string> context;
702         std::optional<std::string> retryPolicy;
703         std::optional<std::vector<nlohmann::json>> headers;
704 
705         if (!json_util::readJsonPatch(req, asyncResp->res, "Context", context,
706                                       "DeliveryRetryPolicy", retryPolicy,
707                                       "HttpHeaders", headers))
708         {
709             return;
710         }
711 
712         if (context)
713         {
714             subValue->customText = *context;
715         }
716 
717         if (headers)
718         {
719             boost::beast::http::fields fields;
720             for (const nlohmann::json& headerChunk : *headers)
721             {
722                 for (const auto& it : headerChunk.items())
723                 {
724                     const std::string* value =
725                         it.value().get_ptr<const std::string*>();
726                     if (value == nullptr)
727                     {
728                         messages::propertyValueFormatError(
729                             asyncResp->res, it.value(),
730                             "HttpHeaders/" + it.key());
731                         return;
732                     }
733                     fields.set(it.key(), *value);
734                 }
735             }
736             subValue->httpHeaders = fields;
737         }
738 
739         if (retryPolicy)
740         {
741             if (std::find(supportedRetryPolicies.begin(),
742                           supportedRetryPolicies.end(),
743                           *retryPolicy) == supportedRetryPolicies.end())
744             {
745                 messages::propertyValueNotInList(asyncResp->res, *retryPolicy,
746                                                  "DeliveryRetryPolicy");
747                 return;
748             }
749             subValue->retryPolicy = *retryPolicy;
750         }
751 
752         EventServiceManager::getInstance().updateSubscriptionData();
753         });
754     BMCWEB_ROUTE(app, "/redfish/v1/EventService/Subscriptions/<str>/")
755         // The below privilege is wrong, it should be ConfigureManager OR
756         // ConfigureSelf
757         // https://github.com/openbmc/bmcweb/issues/220
758         //.privileges(redfish::privileges::deleteEventDestination)
759         .privileges({{"ConfigureManager"}})
760         .methods(boost::beast::http::verb::delete_)(
761             [&app](const crow::Request& req,
762                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
763                    const std::string& param) {
764         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
765         {
766             return;
767         }
768 
769         if (param.starts_with("snmp"))
770         {
771             deleteSnmpTrapClient(asyncResp, param);
772             EventServiceManager::getInstance().deleteSubscription(param);
773             return;
774         }
775 
776         if (!EventServiceManager::getInstance().isSubscriptionExist(param))
777         {
778             asyncResp->res.result(boost::beast::http::status::not_found);
779             return;
780         }
781         EventServiceManager::getInstance().deleteSubscription(param);
782         });
783 }
784 
785 } // namespace redfish
786