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