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