xref: /openbmc/bmcweb/redfish-core/lib/event_service.hpp (revision 62598e31d0988d589506d5091bd38f72d61faf5e)
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("Failed to validate and split destination url");
328             messages::propertyValueFormatError(asyncResp->res, destUrl,
329                                                "Destination");
330             return;
331         }
332 
333         if (protocol == "SNMPv2c")
334         {
335             if (context)
336             {
337                 messages::propertyValueConflict(asyncResp->res, "Context",
338                                                 "Protocol");
339                 return;
340             }
341             if (eventFormatType2)
342             {
343                 messages::propertyValueConflict(asyncResp->res,
344                                                 "EventFormatType", "Protocol");
345                 return;
346             }
347             if (retryPolicy)
348             {
349                 messages::propertyValueConflict(asyncResp->res, "RetryPolicy",
350                                                 "Protocol");
351                 return;
352             }
353             if (msgIds)
354             {
355                 messages::propertyValueConflict(asyncResp->res, "MessageIds",
356                                                 "Protocol");
357                 return;
358             }
359             if (regPrefixes)
360             {
361                 messages::propertyValueConflict(asyncResp->res,
362                                                 "RegistryPrefixes", "Protocol");
363                 return;
364             }
365             if (resTypes)
366             {
367                 messages::propertyValueConflict(asyncResp->res, "ResourceTypes",
368                                                 "Protocol");
369                 return;
370             }
371             if (headers)
372             {
373                 messages::propertyValueConflict(asyncResp->res, "HttpHeaders",
374                                                 "Protocol");
375                 return;
376             }
377             if (mrdJsonArray)
378             {
379                 messages::propertyValueConflict(
380                     asyncResp->res, "MetricReportDefinitions", "Protocol");
381                 return;
382             }
383 
384             addSnmpTrapClient(asyncResp, host, port);
385             return;
386         }
387 
388         if (path.empty())
389         {
390             path = "/";
391         }
392 
393         std::shared_ptr<Subscription> subValue = std::make_shared<Subscription>(
394             host, port, path, urlProto, app.ioContext());
395 
396         subValue->destinationUrl = destUrl;
397 
398         if (subscriptionType)
399         {
400             if (*subscriptionType != "RedfishEvent")
401             {
402                 messages::propertyValueNotInList(
403                     asyncResp->res, *subscriptionType, "SubscriptionType");
404                 return;
405             }
406             subValue->subscriptionType = *subscriptionType;
407         }
408         else
409         {
410             subValue->subscriptionType = "RedfishEvent"; // Default
411         }
412 
413         if (protocol != "Redfish")
414         {
415             messages::propertyValueNotInList(asyncResp->res, protocol,
416                                              "Protocol");
417             return;
418         }
419         subValue->protocol = protocol;
420 
421         if (eventFormatType2)
422         {
423             if (std::find(supportedEvtFormatTypes.begin(),
424                           supportedEvtFormatTypes.end(),
425                           *eventFormatType2) == supportedEvtFormatTypes.end())
426             {
427                 messages::propertyValueNotInList(
428                     asyncResp->res, *eventFormatType2, "EventFormatType");
429                 return;
430             }
431             subValue->eventFormatType = *eventFormatType2;
432         }
433         else
434         {
435             // If not specified, use default "Event"
436             subValue->eventFormatType = "Event";
437         }
438 
439         if (context)
440         {
441             // This value is selected aribitrarily.
442             constexpr const size_t maxContextSize = 256;
443             if (context->size() > maxContextSize)
444             {
445                 messages::stringValueTooLong(asyncResp->res, "Context",
446                                              maxContextSize);
447                 return;
448             }
449             subValue->customText = *context;
450         }
451 
452         if (headers)
453         {
454             size_t cumulativeLen = 0;
455 
456             for (const nlohmann::json& headerChunk : *headers)
457             {
458                 std::string hdr{headerChunk.dump(
459                     -1, ' ', true, nlohmann::json::error_handler_t::replace)};
460                 cumulativeLen += hdr.length();
461 
462                 // This value is selected to mirror http_connection.hpp
463                 constexpr const uint16_t maxHeaderSizeED = 8096;
464                 if (cumulativeLen > maxHeaderSizeED)
465                 {
466                     messages::arraySizeTooLong(asyncResp->res, "HttpHeaders",
467                                                maxHeaderSizeED);
468                     return;
469                 }
470                 for (const auto& item : headerChunk.items())
471                 {
472                     const std::string* value =
473                         item.value().get_ptr<const std::string*>();
474                     if (value == nullptr)
475                     {
476                         messages::propertyValueFormatError(
477                             asyncResp->res, item.value(),
478                             "HttpHeaders/" + item.key());
479                         return;
480                     }
481                     subValue->httpHeaders.set(item.key(), *value);
482                 }
483             }
484         }
485 
486         if (regPrefixes)
487         {
488             for (const std::string& it : *regPrefixes)
489             {
490                 if (std::find(supportedRegPrefixes.begin(),
491                               supportedRegPrefixes.end(),
492                               it) == supportedRegPrefixes.end())
493                 {
494                     messages::propertyValueNotInList(asyncResp->res, it,
495                                                      "RegistryPrefixes");
496                     return;
497                 }
498             }
499             subValue->registryPrefixes = *regPrefixes;
500         }
501 
502         if (resTypes)
503         {
504             for (const std::string& it : *resTypes)
505             {
506                 if (std::find(supportedResourceTypes.begin(),
507                               supportedResourceTypes.end(),
508                               it) == supportedResourceTypes.end())
509                 {
510                     messages::propertyValueNotInList(asyncResp->res, it,
511                                                      "ResourceTypes");
512                     return;
513                 }
514             }
515             subValue->resourceTypes = *resTypes;
516         }
517 
518         if (msgIds)
519         {
520             std::vector<std::string> registryPrefix;
521 
522             // If no registry prefixes are mentioned, consider all
523             // supported prefixes
524             if (subValue->registryPrefixes.empty())
525             {
526                 registryPrefix.assign(supportedRegPrefixes.begin(),
527                                       supportedRegPrefixes.end());
528             }
529             else
530             {
531                 registryPrefix = subValue->registryPrefixes;
532             }
533 
534             for (const std::string& id : *msgIds)
535             {
536                 bool validId = false;
537 
538                 // Check for Message ID in each of the selected Registry
539                 for (const std::string& it : registryPrefix)
540                 {
541                     const std::span<const redfish::registries::MessageEntry>
542                         registry =
543                             redfish::registries::getRegistryFromPrefix(it);
544 
545                     if (std::any_of(
546                             registry.begin(), registry.end(),
547                             [&id](const redfish::registries::MessageEntry&
548                                       messageEntry) {
549                         return id == messageEntry.first;
550                             }))
551                     {
552                         validId = true;
553                         break;
554                     }
555                 }
556 
557                 if (!validId)
558                 {
559                     messages::propertyValueNotInList(asyncResp->res, id,
560                                                      "MessageIds");
561                     return;
562                 }
563             }
564 
565             subValue->registryMsgIds = *msgIds;
566         }
567 
568         if (retryPolicy)
569         {
570             if (std::find(supportedRetryPolicies.begin(),
571                           supportedRetryPolicies.end(),
572                           *retryPolicy) == supportedRetryPolicies.end())
573             {
574                 messages::propertyValueNotInList(asyncResp->res, *retryPolicy,
575                                                  "DeliveryRetryPolicy");
576                 return;
577             }
578             subValue->retryPolicy = *retryPolicy;
579         }
580         else
581         {
582             // Default "TerminateAfterRetries"
583             subValue->retryPolicy = "TerminateAfterRetries";
584         }
585 
586         if (mrdJsonArray)
587         {
588             for (nlohmann::json& mrdObj : *mrdJsonArray)
589             {
590                 std::string mrdUri;
591 
592                 if (!json_util::readJson(mrdObj, asyncResp->res, "@odata.id",
593                                          mrdUri))
594 
595                 {
596                     return;
597                 }
598                 subValue->metricReportDefinitions.emplace_back(mrdUri);
599             }
600         }
601 
602         std::string id =
603             EventServiceManager::getInstance().addSubscription(subValue);
604         if (id.empty())
605         {
606             messages::internalError(asyncResp->res);
607             return;
608         }
609 
610         messages::created(asyncResp->res);
611         asyncResp->res.addHeader(
612             "Location", "/redfish/v1/EventService/Subscriptions/" + id);
613         });
614 }
615 
616 inline void requestRoutesEventDestination(App& app)
617 {
618     BMCWEB_ROUTE(app, "/redfish/v1/EventService/Subscriptions/<str>/")
619         .privileges(redfish::privileges::getEventDestination)
620         .methods(boost::beast::http::verb::get)(
621             [&app](const crow::Request& req,
622                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
623                    const std::string& param) {
624         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
625         {
626             return;
627         }
628 
629         if (param.starts_with("snmp"))
630         {
631             getSnmpTrapClient(asyncResp, param);
632             return;
633         }
634 
635         std::shared_ptr<Subscription> subValue =
636             EventServiceManager::getInstance().getSubscription(param);
637         if (subValue == nullptr)
638         {
639             asyncResp->res.result(boost::beast::http::status::not_found);
640             return;
641         }
642         const std::string& id = param;
643 
644         asyncResp->res.jsonValue["@odata.type"] =
645             "#EventDestination.v1_8_0.EventDestination";
646         asyncResp->res.jsonValue["Protocol"] = "Redfish";
647         asyncResp->res.jsonValue["@odata.id"] =
648             "/redfish/v1/EventService/Subscriptions/" + id;
649         asyncResp->res.jsonValue["Id"] = id;
650         asyncResp->res.jsonValue["Name"] = "Event Destination " + id;
651         asyncResp->res.jsonValue["Destination"] = subValue->destinationUrl;
652         asyncResp->res.jsonValue["Context"] = subValue->customText;
653         asyncResp->res.jsonValue["SubscriptionType"] =
654             subValue->subscriptionType;
655         asyncResp->res.jsonValue["HttpHeaders"] = nlohmann::json::array();
656         asyncResp->res.jsonValue["EventFormatType"] = subValue->eventFormatType;
657         asyncResp->res.jsonValue["RegistryPrefixes"] =
658             subValue->registryPrefixes;
659         asyncResp->res.jsonValue["ResourceTypes"] = subValue->resourceTypes;
660 
661         asyncResp->res.jsonValue["MessageIds"] = subValue->registryMsgIds;
662         asyncResp->res.jsonValue["DeliveryRetryPolicy"] = subValue->retryPolicy;
663 
664         nlohmann::json::array_t mrdJsonArray;
665         for (const auto& mdrUri : subValue->metricReportDefinitions)
666         {
667             nlohmann::json::object_t mdr;
668             mdr["@odata.id"] = mdrUri;
669             mrdJsonArray.emplace_back(std::move(mdr));
670         }
671         asyncResp->res.jsonValue["MetricReportDefinitions"] = mrdJsonArray;
672         });
673     BMCWEB_ROUTE(app, "/redfish/v1/EventService/Subscriptions/<str>/")
674         // The below privilege is wrong, it should be ConfigureManager OR
675         // ConfigureSelf
676         // https://github.com/openbmc/bmcweb/issues/220
677         //.privileges(redfish::privileges::patchEventDestination)
678         .privileges({{"ConfigureManager"}})
679         .methods(boost::beast::http::verb::patch)(
680             [&app](const crow::Request& req,
681                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
682                    const std::string& param) {
683         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
684         {
685             return;
686         }
687         std::shared_ptr<Subscription> subValue =
688             EventServiceManager::getInstance().getSubscription(param);
689         if (subValue == nullptr)
690         {
691             asyncResp->res.result(boost::beast::http::status::not_found);
692             return;
693         }
694 
695         std::optional<std::string> context;
696         std::optional<std::string> retryPolicy;
697         std::optional<std::vector<nlohmann::json>> headers;
698 
699         if (!json_util::readJsonPatch(req, asyncResp->res, "Context", context,
700                                       "DeliveryRetryPolicy", retryPolicy,
701                                       "HttpHeaders", headers))
702         {
703             return;
704         }
705 
706         if (context)
707         {
708             subValue->customText = *context;
709         }
710 
711         if (headers)
712         {
713             boost::beast::http::fields fields;
714             for (const nlohmann::json& headerChunk : *headers)
715             {
716                 for (const auto& it : headerChunk.items())
717                 {
718                     const std::string* value =
719                         it.value().get_ptr<const std::string*>();
720                     if (value == nullptr)
721                     {
722                         messages::propertyValueFormatError(
723                             asyncResp->res, it.value(),
724                             "HttpHeaders/" + it.key());
725                         return;
726                     }
727                     fields.set(it.key(), *value);
728                 }
729             }
730             subValue->httpHeaders = fields;
731         }
732 
733         if (retryPolicy)
734         {
735             if (std::find(supportedRetryPolicies.begin(),
736                           supportedRetryPolicies.end(),
737                           *retryPolicy) == supportedRetryPolicies.end())
738             {
739                 messages::propertyValueNotInList(asyncResp->res, *retryPolicy,
740                                                  "DeliveryRetryPolicy");
741                 return;
742             }
743             subValue->retryPolicy = *retryPolicy;
744         }
745 
746         EventServiceManager::getInstance().updateSubscriptionData();
747         });
748     BMCWEB_ROUTE(app, "/redfish/v1/EventService/Subscriptions/<str>/")
749         // The below privilege is wrong, it should be ConfigureManager OR
750         // ConfigureSelf
751         // https://github.com/openbmc/bmcweb/issues/220
752         //.privileges(redfish::privileges::deleteEventDestination)
753         .privileges({{"ConfigureManager"}})
754         .methods(boost::beast::http::verb::delete_)(
755             [&app](const crow::Request& req,
756                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
757                    const std::string& param) {
758         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
759         {
760             return;
761         }
762 
763         if (param.starts_with("snmp"))
764         {
765             deleteSnmpTrapClient(asyncResp, param);
766             EventServiceManager::getInstance().deleteSubscription(param);
767             return;
768         }
769 
770         if (!EventServiceManager::getInstance().isSubscriptionExist(param))
771         {
772             asyncResp->res.result(boost::beast::http::status::not_found);
773             return;
774         }
775         EventServiceManager::getInstance().deleteSubscription(param);
776         });
777 }
778 
779 } // namespace redfish
780