CCF
Loading...
Searching...
No Matches
service_state.h
Go to the documentation of this file.
1// Copyright (c) Microsoft Corporation. All rights reserved.
2// Licensed under the Apache 2.0 License.
3#pragma once
4
7
9{
11 const ccf::MemberId& member_id,
12 const ccf::MemberDetails& member_details,
13 ccf::MemberCerts::ReadOnlyHandle* member_certs_handle,
15 {
16 auto member = nlohmann::json::object();
17
18 member["memberId"] = member_id;
19 member["status"] = member_details.status;
20 member["memberData"] = member_details.member_data;
21
22 const auto cert = member_certs_handle->get(member_id);
23 if (cert.has_value())
24 {
25 member["certificate"] = cert.value().str();
26 }
27 else
28 {
29 GOV_INFO_FMT("Member {} has no certificate", member_id);
30 }
31
32 const auto enc_key = member_enc_keys_handle->get(member_id);
33 if (enc_key.has_value())
34 {
35 member["publicEncryptionKey"] = enc_key.value().str();
36 }
37
38 ccf::MemberRecoveryRole recovery_role =
40 if (member_details.recovery_role.has_value())
41 {
42 recovery_role = member_details.recovery_role.value();
43 }
44 else if (enc_key.has_value())
45 {
47 }
48
49 member["recoveryRole"] = recovery_role;
50
51 return member;
52 }
53
55 const ccf::UserId& user_id,
56 const ccf::crypto::Pem& user_cert,
57 ccf::UserInfo::ReadOnlyHandle* user_info_handle)
58 {
59 auto user = nlohmann::json::object();
60
61 user["userId"] = user_id;
62 user["certificate"] = user_cert.str();
63
64 const auto user_info = user_info_handle->get(user_id);
65 // For consistency with other *Data fields, we always insert this, even if
66 // it iss nullopt (JSON null)
67 user["userData"] = user_info;
68
69 return user;
70 }
71
73 const ccf::NodeId& node_id,
74 const ccf::NodeInfo& node_info,
75 ccf::NodeEndorsedCertificates::ReadOnlyHandle* node_endorsed_certs_handle)
76 {
77 auto node = nlohmann::json::object();
78
79 node["nodeId"] = node_id;
80 node["status"] = node_info.status;
81 node["nodeData"] = node_info.node_data;
82
83 const auto endorsed_cert = node_endorsed_certs_handle->get(node_id);
84 if (endorsed_cert.has_value())
85 {
86 node["certificate"] = endorsed_cert.value().str();
87 }
88 else
89 {
90 GOV_INFO_FMT("Node {} has no endorsed certificate", node_id);
91 }
92
93 node["retiredCommitted"] = node_info.retired_committed;
94
95 auto quote_info = nlohmann::json::object();
96 switch (node_info.quote_info.format)
97 {
99 {
100 quote_info["format"] = "OE_SGX_v1";
101 quote_info["quote"] =
103 quote_info["endorsements"] =
105 break;
106 }
108 {
109 quote_info["format"] = "Insecure_Virtual";
110 quote_info["rawQuote"] = node_info.quote_info.quote;
111
112 {
113 const auto details =
114 nlohmann::json::parse(node_info.quote_info.quote);
115 auto j_details = nlohmann::json::object();
116 j_details["measurement"] = details["measurement"];
117 j_details["reportData"] = details["report_data"];
118 j_details["hostData"] = details["host_data"];
119 quote_info["details"] = j_details;
120 }
121
122 break;
123 }
125 {
126 quote_info["format"] = "AMD_SEV_SNP_v1";
127 if (node_info.quote_info.uvm_endorsements.has_value())
128 {
129 quote_info["uvmEndorsements"] = ccf::crypto::b64_from_raw(
130 node_info.quote_info.uvm_endorsements.value());
131 }
132 if (node_info.quote_info.endorsed_tcb.has_value())
133 {
134 quote_info["endorsedTcb"] = ccf::crypto::b64_from_raw(
135 ds::from_hex(node_info.quote_info.endorsed_tcb.value()));
136 }
137 break;
138 }
139 }
140 node["quoteInfo"] = quote_info;
141
142 auto rpc_interfaces = nlohmann::json::object();
143 for (const auto& [interface_id, net_interface] : node_info.rpc_interfaces)
144 {
145 auto rpc_interface = nlohmann::json::object();
146
147 rpc_interface["publishedAddress"] = net_interface.published_address;
148 if (net_interface.app_protocol.has_value())
149 {
150 rpc_interface["protocol"] = net_interface.app_protocol.value();
151 }
152 else
153 {
154 GOV_INFO_FMT("RPC interface {} has no protocol", interface_id);
155 }
156 rpc_interfaces[interface_id] = rpc_interface;
157 }
158
159 node["rpcInterfaces"] = rpc_interfaces;
160
161 return node;
162 }
163
165 {
166 auto get_constitution = [&](auto& ctx, ApiVersion api_version) {
167 switch (api_version)
168 {
170 case ApiVersion::v1:
171 default:
172 {
173 auto constitution_handle =
174 ctx.tx.template ro<ccf::Constitution>(ccf::Tables::CONSTITUTION);
175 auto constitution = constitution_handle->get();
176
177 if (!constitution.has_value())
178 {
179 detail::set_gov_error(
180 ctx.rpc_ctx,
181 HTTP_STATUS_NOT_FOUND,
182 ccf::errors::ResourceNotFound,
183 "Constitution not found");
184 return;
185 }
186
187 // Return raw JS constitution in body
188 ctx.rpc_ctx->set_response_status(HTTP_STATUS_OK);
189 ctx.rpc_ctx->set_response_body(std::move(constitution.value()));
190 ctx.rpc_ctx->set_response_header(
191 ccf::http::headers::CONTENT_TYPE,
192 http::headervalues::contenttype::JAVASCRIPT);
193 return;
194 }
195 }
196 };
197 registry
199 "/service/constitution",
200 HTTP_GET,
201 api_version_adapter(get_constitution),
202 no_auth_required)
203 .set_openapi_hidden(true)
204 .install();
205
206 auto get_service_info = [&](auto& ctx, ApiVersion api_version) {
207 switch (api_version)
208 {
210 case ApiVersion::v1:
211 default:
212 {
213 auto response_body = nlohmann::json::object();
214
215 auto service_info_handle =
216 ctx.tx.template ro<ccf::Service>(ccf::Tables::SERVICE);
217 auto service_info = service_info_handle->get();
218
219 if (!service_info.has_value())
220 {
221 detail::set_gov_error(
222 ctx.rpc_ctx,
223 HTTP_STATUS_NOT_FOUND,
224 ccf::errors::ResourceNotFound,
225 "Service info not yet available");
226 return;
227 }
228
229 response_body["status"] = service_info->status;
230 response_body["certificate"] = service_info->cert.str();
231 response_body["recoveryCount"] =
232 service_info->recovery_count.value_or(0);
233
234 if (service_info->current_service_create_txid.has_value())
235 {
236 response_body["creationTransactionId"] =
237 service_info->current_service_create_txid.value();
238 }
239 else
240 {
241 GOV_INFO_FMT("No recorded current_service_create_txid");
242 }
243
244 if (service_info->previous_service_identity_version.has_value())
245 {
247 service_info->previous_service_identity_version.value();
249 // Note: deliberately ignoring errors. Prefer to return single
250 // invalid field than convert entire response to error.
252 response_body["previousServiceCreationTransactionId"] =
253 ccf::TxID{.view = view, .seqno = seqno};
254 }
255
256 response_body["serviceData"] = service_info->service_data;
257
258 {
259 auto config_handle = ctx.tx.template ro<ccf::Configuration>(
260 ccf::Tables::CONFIGURATION);
261
262 auto config = config_handle->get();
263 if (config.has_value())
264 {
265 auto configuration = nlohmann::json::object();
266 configuration["recoveryThreshold"] = config->recovery_threshold;
267 configuration["maximumNodeCertificateValidityDays"] =
268 config->maximum_node_certificate_validity_days.value_or(
269 ccf::default_node_cert_validity_period_days);
270 configuration["maximumServiceCertificateValidityDays"] =
271 config->maximum_service_certificate_validity_days.value_or(
272 ccf::default_service_cert_validity_period_days);
273 configuration["recentCoseProposalsWindowSize"] =
274 config->recent_cose_proposals_window_size.value_or(
275 ccf::default_recent_cose_proposals_window_size);
276 response_body["configuration"] = configuration;
277 }
278 else
279 {
280 GOV_INFO_FMT("No service configuration available");
281 }
282 }
283
284 ctx.rpc_ctx->set_response_json(response_body, HTTP_STATUS_OK);
285 return;
286 }
287 }
288 };
289 registry
291 "/service/info",
292 HTTP_GET,
293 api_version_adapter(get_service_info),
294 no_auth_required)
295 .set_openapi_hidden(true)
296 .install();
297
298 auto get_javascript_app = [&](auto& ctx, ApiVersion api_version) {
299 switch (api_version)
300 {
302 case ApiVersion::v1:
303 default:
304 {
305 auto response_body = nlohmann::json::object();
306
307 // Describe JS endpoints
308 {
309 auto endpoints = nlohmann::json::object();
310
311 bool original_case = false;
312 {
313 const auto parsed_query =
314 ccf::http::parse_query(ctx.rpc_ctx->get_request_query());
315 std::string error_reason;
316 const auto case_opt = ccf::http::get_query_value_opt<std::string>(
317 parsed_query, "case", error_reason);
318
319 if (case_opt.has_value())
320 {
321 if (case_opt.value() != "original")
322 {
323 ctx.rpc_ctx->set_error(
324 HTTP_STATUS_BAD_REQUEST,
325 ccf::errors::InvalidQueryParameterValue,
326 "Accepted values for the 'case' query parameter are: "
327 "original");
328 return;
329 }
330
331 original_case = true;
332 }
333 }
334
335 auto js_endpoints_handle =
336 ctx.tx.template ro<ccf::endpoints::EndpointsMap>(
337 ccf::endpoints::Tables::ENDPOINTS);
338
339 using RawEndpointsMap = ccf::kv::RawCopySerialisedMap<
341 std::vector<uint8_t>>;
342 auto raw_js_endpoints_handle = ctx.tx.template ro<RawEndpointsMap>(
343 ccf::endpoints::Tables::ENDPOINTS);
344
345 js_endpoints_handle->foreach(
346 [&endpoints, &raw_js_endpoints_handle, original_case](
348 const ccf::endpoints::EndpointProperties& properties) {
349 auto ib =
350 endpoints.emplace(key.uri_path, nlohmann::json::object());
351 auto& operations = *ib.first;
352
353 auto operation = nlohmann::json::object();
354
355 if (original_case)
356 {
357 const auto raw_value_opt = raw_js_endpoints_handle->get(key);
358 if (!raw_value_opt.has_value())
359 {
360 throw std::runtime_error(
361 "Table inconsistency: Cannot access key via raw handle?");
362 }
363 const auto& raw_value = raw_value_opt.value();
364 operation =
365 nlohmann::json::parse(raw_value.begin(), raw_value.end());
366 }
367 else
368 {
369 operation["jsModule"] = properties.js_module;
370 operation["jsFunction"] = properties.js_function;
371 operation["forwardingRequired"] =
372 properties.forwarding_required;
373 operation["redirectionStrategy"] =
374 properties.redirection_strategy;
375
376 auto policies = nlohmann::json::array();
377 for (const auto& policy : properties.authn_policies)
378 {
379 policies.push_back(policy);
380 }
381 operation["authnPolicies"] = policies;
382
383 operation["mode"] = properties.mode;
384 operation["openApi"] = properties.openapi;
385 }
386
387 operations[key.verb.c_str()] = operation;
388
389 return true;
390 });
391
392 response_body["endpoints"] = endpoints;
393 }
394
395 ctx.rpc_ctx->set_response_json(response_body, HTTP_STATUS_OK);
396 return;
397 }
398 }
399 };
400 registry
402 "/service/javascript-app",
403 HTTP_GET,
404 api_version_adapter(get_javascript_app, ApiVersion::v1),
405 no_auth_required)
406 .add_query_parameter<std::string>("case")
407 .set_openapi_hidden(true)
408 .install();
409
410 auto get_javascript_modules = [&](auto& ctx, ApiVersion api_version) {
411 switch (api_version)
412 {
414 case ApiVersion::v1:
415 default:
416 {
417 auto response_body = nlohmann::json::object();
418
419 {
420 auto module_list = nlohmann::json::array();
421
422 auto modules_handle =
423 ctx.tx.template ro<ccf::Modules>(ccf::Tables::MODULES);
424
425 modules_handle->foreach_key(
426 [&module_list](const std::string& module_name) {
427 auto entry = nlohmann::json::object();
428 entry["moduleName"] = module_name;
429 module_list.push_back(entry);
430 return true;
431 });
432
433 response_body["value"] = module_list;
434 }
435
436 ctx.rpc_ctx->set_response_json(response_body, HTTP_STATUS_OK);
437 return;
438 }
439 }
440 };
441 registry
443 "/service/javascript-modules",
444 HTTP_GET,
445 api_version_adapter(get_javascript_modules, ApiVersion::v1),
446 no_auth_required)
447 .set_openapi_hidden(true)
448 .install();
449
450 auto get_javascript_module_by_name =
451 [&](auto& ctx, ApiVersion api_version) {
452 switch (api_version)
453 {
455 case ApiVersion::v1:
456 default:
457 {
458 std::string module_name;
459 {
460 std::string error;
462 ctx.rpc_ctx->get_request_path_params(),
463 "moduleName",
464 module_name,
465 error))
466 {
467 detail::set_gov_error(
468 ctx.rpc_ctx,
469 HTTP_STATUS_BAD_REQUEST,
470 ccf::errors::InvalidResourceName,
471 std::move(error));
472 return;
473 }
474 }
475
476 module_name = ::http::url_decode(module_name);
477
478 auto modules_handle =
479 ctx.tx.template ro<ccf::Modules>(ccf::Tables::MODULES);
480 auto module = modules_handle->get(module_name);
481
482 if (!module.has_value())
483 {
484 detail::set_gov_error(
485 ctx.rpc_ctx,
486 HTTP_STATUS_NOT_FOUND,
487 ccf::errors::ResourceNotFound,
488 fmt::format("Module {} does not exist.", module_name));
489 return;
490 }
491
492 // Return raw JS module content in body
493 ctx.rpc_ctx->set_response_status(HTTP_STATUS_OK);
494 ctx.rpc_ctx->set_response_body(std::move(module.value()));
495 ctx.rpc_ctx->set_response_header(
496 ccf::http::headers::CONTENT_TYPE,
497 http::headervalues::contenttype::JAVASCRIPT);
498 return;
499 }
500 }
501 };
502 registry
504 "/service/javascript-modules/{moduleName}",
505 HTTP_GET,
506 api_version_adapter(get_javascript_module_by_name, ApiVersion::v1),
507 no_auth_required)
508 .set_openapi_hidden(true)
509 .install();
510
511 auto get_join_policy = [&](auto& ctx, ApiVersion api_version) {
512 switch (api_version)
513 {
515 case ApiVersion::v1:
516 default:
517 {
518 auto response_body = nlohmann::json::object();
519
520 // Describe SGX join policy
521 {
522 auto sgx_policy = nlohmann::json::object();
523
524 auto sgx_measurements = nlohmann::json::array();
525 auto code_ids_handle =
526 ctx.tx.template ro<ccf::CodeIDs>(ccf::Tables::NODE_CODE_IDS);
527 code_ids_handle->foreach(
528 [&sgx_measurements](
529 const ccf::pal::SgxAttestationMeasurement& measurement,
530 const ccf::CodeStatus& status) {
532 {
533 sgx_measurements.push_back(measurement.hex_str());
534 }
535 return true;
536 });
537 sgx_policy["measurements"] = sgx_measurements;
538
539 response_body["sgx"] = sgx_policy;
540 }
541
542 // Describe Virtual join policy
543 {
544 auto virtual_policy = nlohmann::json::object();
545
546 auto virtual_measurements = nlohmann::json::array();
547 auto measurements_handle =
548 ctx.tx.template ro<ccf::VirtualMeasurements>(
549 ccf::Tables::NODE_VIRTUAL_MEASUREMENTS);
550 measurements_handle->foreach(
551 [&virtual_measurements](
552 const pal::VirtualAttestationMeasurement& measurement,
553 const ccf::CodeStatus& status) {
555 {
556 virtual_measurements.push_back(measurement);
557 }
558 return true;
559 });
560 virtual_policy["measurements"] = virtual_measurements;
561
562 auto virtual_host_data = nlohmann::json::array();
563 auto host_data_handle = ctx.tx.template ro<ccf::VirtualHostDataMap>(
564 ccf::Tables::VIRTUAL_HOST_DATA);
565 host_data_handle->foreach(
566 [&virtual_host_data](const HostData& host_data) {
567 virtual_host_data.push_back(host_data.hex_str());
568 return true;
569 });
570 virtual_policy["hostData"] = virtual_host_data;
571
572 response_body["virtual"] = virtual_policy;
573 }
574
575 // Describe SNP join policy
576 {
577 auto snp_policy = nlohmann::json::object();
578
579 auto snp_measurements = nlohmann::json::array();
580 auto measurements_handle = ctx.tx.template ro<ccf::SnpMeasurements>(
581 ccf::Tables::NODE_SNP_MEASUREMENTS);
582 measurements_handle->foreach(
583 [&snp_measurements](
584 const pal::SnpAttestationMeasurement& measurement,
585 const ccf::CodeStatus& status) {
587 {
588 snp_measurements.push_back(measurement.hex_str());
589 }
590 return true;
591 });
592 snp_policy["measurements"] = snp_measurements;
593
594 auto snp_host_data = nlohmann::json::object();
595 auto host_data_handle =
596 ctx.tx.template ro<ccf::SnpHostDataMap>(ccf::Tables::HOST_DATA);
597 host_data_handle->foreach(
598 [&snp_host_data](
599 const HostData& host_data, const HostDataMetadata& metadata) {
600 snp_host_data[host_data.hex_str()] = metadata;
601 return true;
602 });
603 snp_policy["hostData"] = snp_host_data;
604
605 auto snp_endorsements = nlohmann::json::object();
606 auto endorsements_handle =
607 ctx.tx.template ro<ccf::SNPUVMEndorsements>(
608 ccf::Tables::NODE_SNP_UVM_ENDORSEMENTS);
609 endorsements_handle->foreach(
610 [&snp_endorsements](
611 const ccf::DID& did,
612 const ccf::FeedToEndorsementsDataMap& feed_info) {
613 snp_endorsements[did] = feed_info;
614 return true;
615 });
616 snp_policy["uvmEndorsements"] = snp_endorsements;
617
618 auto snp_tcb_versions = nlohmann::json::object();
619 auto tcb_versions_handle =
620 ctx.tx.template ro<ccf::SnpTcbVersionMap>(
621 ccf::Tables::SNP_TCB_VERSIONS);
622
623 tcb_versions_handle->foreach(
624 [&snp_tcb_versions](
625 const std::string& cpuid,
626 const pal::snp::TcbVersionPolicy& tcb_policy) {
627 snp_tcb_versions[cpuid] = tcb_policy;
628 return true;
629 });
630 snp_policy["tcbVersions"] = snp_tcb_versions;
631
632 response_body["snp"] = snp_policy;
633 }
634
635 ctx.rpc_ctx->set_response_json(response_body, HTTP_STATUS_OK);
636 return;
637 }
638 }
639 };
640 registry
642 "/service/join-policy",
643 HTTP_GET,
644 api_version_adapter(get_join_policy),
645 no_auth_required)
646 .set_openapi_hidden(true)
647 .install();
648
649 auto get_jwk = [&](auto& ctx, ApiVersion api_version) {
650 switch (api_version)
651 {
653 case ApiVersion::v1:
654 default:
655 {
656 auto response_body = nlohmann::json::object();
657
658 // Populate issuers field
659 {
660 auto issuers = nlohmann::json::object();
661
662 auto jwt_issuers_handle =
663 ctx.tx.template ro<ccf::JwtIssuers>(ccf::Tables::JWT_ISSUERS);
664 jwt_issuers_handle->foreach(
665 [&issuers](
666 const ccf::JwtIssuer& issuer_id,
667 const ccf::JwtIssuerMetadata& metadata) {
668 auto jwt_issuer = nlohmann::json::object();
669
670 jwt_issuer["autoRefresh"] = metadata.auto_refresh;
671
672 if (metadata.ca_cert_bundle_name.has_value())
673 {
674 jwt_issuer["caCertBundleName"] =
675 metadata.ca_cert_bundle_name.value();
676 }
677
678 issuers[issuer_id] = jwt_issuer;
679 return true;
680 });
681
682 response_body["issuers"] = issuers;
683 }
684
685 // Populate keys field
686 {
687 auto keys = nlohmann::json::object();
688
689 auto jwt_keys_handle =
690 ctx.tx.template ro<ccf::JwtPublicSigningKeysMetadata>(
691 ccf::Tables::JWT_PUBLIC_SIGNING_KEYS_METADATA);
692
693 jwt_keys_handle->foreach(
694 [&keys](
695 const ccf::JwtKeyId& k,
696 const std::vector<OpenIDJWKMetadata>& v) {
697 auto keys_info = nlohmann::json::array();
698 for (const auto& metadata : v)
699 {
700 auto info = nlohmann::json::object();
701
702 info["publicKey"] =
703 ccf::crypto::make_rsa_public_key(metadata.public_key)
704 ->public_key_pem()
705 .str();
706 info["issuer"] = metadata.issuer;
707 info["constraint"] = metadata.constraint;
708
709 keys_info.push_back(info);
710 }
711
712 keys[k] = keys_info;
713 return true;
714 });
715
716 response_body["keys"] = keys;
717 }
718
719 // Populate caCertBundles field
720 {
721 auto cert_bundles = nlohmann::json::object();
722
723 auto cert_bundles_handle =
724 ctx.tx.template ro<ccf::CACertBundlePEMs>(
725 ccf::Tables::CA_CERT_BUNDLE_PEMS);
726 cert_bundles_handle->foreach([&cert_bundles](
727 const std::string& bundle_name,
728 const std::string& bundle_value) {
729 cert_bundles[bundle_name] = bundle_value;
730 return true;
731 });
732
733 response_body["caCertBundles"] = cert_bundles;
734 }
735
736 ctx.rpc_ctx->set_response_json(response_body, HTTP_STATUS_OK);
737 return;
738 }
739 }
740 };
741 registry
743 "/service/jwk",
744 HTTP_GET,
745 api_version_adapter(get_jwk),
746 no_auth_required)
747 .set_openapi_hidden(true)
748 .install();
749
750 auto get_members = [&](auto& ctx, ApiVersion api_version) {
751 switch (api_version)
752 {
754 case ApiVersion::v1:
755 default:
756 {
757 auto response_body = nlohmann::json::object();
758
759 {
760 auto member_list = nlohmann::json::array();
761
762 auto member_info_handle =
763 ctx.tx.template ro<ccf::MemberInfo>(ccf::Tables::MEMBER_INFO);
764 auto member_certs_handle =
765 ctx.tx.template ro<ccf::MemberCerts>(ccf::Tables::MEMBER_CERTS);
766 auto member_enc_keys_handle =
767 ctx.tx.template ro<ccf::MemberPublicEncryptionKeys>(
768 ccf::Tables::MEMBER_ENCRYPTION_PUBLIC_KEYS);
769
770 member_info_handle->foreach(
771 [&member_list, member_certs_handle, member_enc_keys_handle](
772 const ccf::MemberId& member_id,
773 const ccf::MemberDetails& member_details) {
774 member_list.push_back(produce_member_description(
775 member_id,
776 member_details,
777 member_certs_handle,
778 member_enc_keys_handle));
779 return true;
780 });
781
782 response_body["value"] = member_list;
783 }
784
785 ctx.rpc_ctx->set_response_json(response_body, HTTP_STATUS_OK);
786 return;
787 }
788 }
789 };
790 registry
792 "/service/members",
793 HTTP_GET,
794 api_version_adapter(get_members),
795 no_auth_required)
796 .set_openapi_hidden(true)
797 .install();
798
799 auto get_member_by_id = [&](auto& ctx, ApiVersion api_version) {
800 switch (api_version)
801 {
803 case ApiVersion::v1:
804 default:
805 {
806 ccf::MemberId member_id;
807 if (!detail::try_parse_member_id(ctx.rpc_ctx, member_id))
808 {
809 return;
810 }
811
812 auto member_info_handle =
813 ctx.tx.template ro<ccf::MemberInfo>(ccf::Tables::MEMBER_INFO);
814 const auto member_info = member_info_handle->get(member_id);
815 if (!member_info.has_value())
816 {
817 detail::set_gov_error(
818 ctx.rpc_ctx,
819 HTTP_STATUS_NOT_FOUND,
820 ccf::errors::ResourceNotFound,
821 fmt::format("Member {} does not exist.", member_id));
822 return;
823 }
824
825 auto member_certs_handle =
826 ctx.tx.template ro<ccf::MemberCerts>(ccf::Tables::MEMBER_CERTS);
827 auto member_enc_keys_handle =
828 ctx.tx.template ro<ccf::MemberPublicEncryptionKeys>(
829 ccf::Tables::MEMBER_ENCRYPTION_PUBLIC_KEYS);
830
831 const auto member = produce_member_description(
832 member_id,
833 member_info.value(),
834 member_certs_handle,
835 member_enc_keys_handle);
836
837 ctx.rpc_ctx->set_response_json(member, HTTP_STATUS_OK);
838 return;
839 }
840 }
841 };
842 registry
844 "/service/members/{memberId}",
845 HTTP_GET,
846 api_version_adapter(get_member_by_id),
847 no_auth_required)
848 .set_openapi_hidden(true)
849 .install();
850
851 auto get_users = [&](auto& ctx, ApiVersion api_version) {
852 switch (api_version)
853 {
855 case ApiVersion::v1:
856 default:
857 {
858 auto response_body = nlohmann::json::object();
859
860 {
861 auto user_list = nlohmann::json::array();
862
863 auto user_certs_handle =
864 ctx.tx.template ro<ccf::UserCerts>(ccf::Tables::USER_CERTS);
865 auto user_info_handle =
866 ctx.tx.template ro<ccf::UserInfo>(ccf::Tables::USER_INFO);
867
868 user_certs_handle->foreach([&user_list, user_info_handle](
869 const ccf::UserId& user_id,
870 const ccf::crypto::Pem& user_cert) {
871 user_list.push_back(
872 produce_user_description(user_id, user_cert, user_info_handle));
873 return true;
874 });
875
876 response_body["value"] = user_list;
877 }
878
879 ctx.rpc_ctx->set_response_json(response_body, HTTP_STATUS_OK);
880 return;
881 }
882 }
883 };
884 registry
886 "/service/users",
887 HTTP_GET,
888 api_version_adapter(get_users),
889 no_auth_required)
890 .set_openapi_hidden(true)
891 .install();
892
893 auto get_user_by_id = [&](auto& ctx, ApiVersion api_version) {
894 switch (api_version)
895 {
897 case ApiVersion::v1:
898 default:
899 {
900 ccf::UserId user_id;
901 if (!detail::try_parse_user_id(ctx.rpc_ctx, user_id))
902 {
903 return;
904 }
905
906 auto user_certs_handle =
907 ctx.tx.template ro<ccf::UserCerts>(ccf::Tables::USER_CERTS);
908
909 const auto user_cert = user_certs_handle->get(user_id);
910 if (!user_cert.has_value())
911 {
912 detail::set_gov_error(
913 ctx.rpc_ctx,
914 HTTP_STATUS_NOT_FOUND,
915 ccf::errors::ResourceNotFound,
916 fmt::format("User {} does not exist.", user_id));
917 return;
918 }
919
920 auto user_info_handle =
921 ctx.tx.template ro<ccf::UserInfo>(ccf::Tables::USER_INFO);
922
923 const auto user = produce_user_description(
924 user_id, user_cert.value(), user_info_handle);
925
926 ctx.rpc_ctx->set_response_json(user, HTTP_STATUS_OK);
927 return;
928 }
929 }
930 };
931 registry
933 "/service/users/{userId}",
934 HTTP_GET,
935 api_version_adapter(get_user_by_id),
936 no_auth_required)
937 .set_openapi_hidden(true)
938 .install();
939
940 auto get_nodes = [&](auto& ctx, ApiVersion api_version) {
941 switch (api_version)
942 {
944 case ApiVersion::v1:
945 default:
946 {
947 auto response_body = nlohmann::json::object();
948
949 {
950 auto node_list = nlohmann::json::array();
951
952 auto node_info_handle =
953 ctx.tx.template ro<ccf::Nodes>(ccf::Tables::NODES);
954 auto node_endorsed_certs_handle =
955 ctx.tx.template ro<ccf::NodeEndorsedCertificates>(
956 ccf::Tables::NODE_ENDORSED_CERTIFICATES);
957
958 node_info_handle->foreach(
959 [&node_list, node_endorsed_certs_handle](
960 const ccf::NodeId& node_id, const ccf::NodeInfo& node_info) {
961 node_list.push_back(produce_node_description(
962 node_id, node_info, node_endorsed_certs_handle));
963 return true;
964 });
965
966 response_body["value"] = node_list;
967 }
968
969 ctx.rpc_ctx->set_response_json(response_body, HTTP_STATUS_OK);
970 return;
971 }
972 }
973 };
974 registry
976 "/service/nodes",
977 HTTP_GET,
978 api_version_adapter(get_nodes),
979 no_auth_required)
980 .set_openapi_hidden(true)
981 .install();
982
983 auto get_node_by_id = [&](auto& ctx, ApiVersion api_version) {
984 switch (api_version)
985 {
987 case ApiVersion::v1:
988 default:
989 {
990 ccf::NodeId node_id;
991 if (!detail::try_parse_node_id(ctx.rpc_ctx, node_id))
992 {
993 return;
994 }
995
996 auto node_info_handle =
997 ctx.tx.template ro<ccf::Nodes>(ccf::Tables::NODES);
998 const auto node_info = node_info_handle->get(node_id);
999 if (!node_info.has_value())
1000 {
1001 detail::set_gov_error(
1002 ctx.rpc_ctx,
1003 HTTP_STATUS_NOT_FOUND,
1004 ccf::errors::ResourceNotFound,
1005 fmt::format("Node {} does not exist.", node_id));
1006 return;
1007 }
1008
1009 auto node_endorsed_certs_handle =
1010 ctx.tx.template ro<ccf::NodeEndorsedCertificates>(
1011 ccf::Tables::NODE_ENDORSED_CERTIFICATES);
1012 const auto node = produce_node_description(
1013 node_id, node_info.value(), node_endorsed_certs_handle);
1014
1015 ctx.rpc_ctx->set_response_json(node, HTTP_STATUS_OK);
1016 return;
1017 }
1018 }
1019 };
1020 registry
1022 "/service/nodes/{nodeId}",
1023 HTTP_GET,
1024 api_version_adapter(get_node_by_id),
1025 no_auth_required)
1026 .set_openapi_hidden(true)
1027 .install();
1028 }
1029}
Definition base_endpoint_registry.h:121
ApiResult get_view_for_seqno_v1(ccf::SeqNo seqno, ccf::View &view)
Definition base_endpoint_registry.cpp:201
Definition pem.h:18
const std::string & str() const
Definition pem.h:46
Definition sha256_hash.h:16
std::string hex_str() const
Definition sha256_hash.cpp:61
virtual Endpoint make_read_only_endpoint(const std::string &method, RESTVerb verb, const ReadOnlyEndpointFunction &f, const AuthnPolicies &ap)
Definition endpoint_registry.cpp:235
Definition map_handle.h:13
std::optional< V > get(const K &key)
Definition map_handle.h:45
Definition map.h:30
K Key
Definition map.h:44
#define GOV_INFO_FMT
Definition gov_logging.h:10
std::string HostDataMetadata
Definition host_data.h:10
std::string b64_from_raw(const uint8_t *data, size_t size)
Definition base64.cpp:41
RSAPublicKeyPtr make_rsa_public_key(const Pem &pem)
Definition rsa_key_pair.cpp:13
bool get_path_param(const ccf::PathParams &params, const std::string &param_name, T &value, std::string &error)
Definition endpoint_registry.h:64
bool try_parse_user_id(const std::shared_ptr< ccf::RpcContext > &rpc_ctx, ccf::UserId &user_id)
Definition helpers.h:123
bool try_parse_member_id(const std::shared_ptr< ccf::RpcContext > &rpc_ctx, ccf::MemberId &member_id)
Definition helpers.h:63
bool try_parse_node_id(const std::shared_ptr< ccf::RpcContext > &rpc_ctx, ccf::NodeId &node_id)
Definition helpers.h:225
Definition api_version.h:11
nlohmann::json produce_user_description(const ccf::UserId &user_id, const ccf::crypto::Pem &user_cert, ccf::UserInfo::ReadOnlyHandle *user_info_handle)
Definition service_state.h:54
nlohmann::json produce_member_description(const ccf::MemberId &member_id, const ccf::MemberDetails &member_details, ccf::MemberCerts::ReadOnlyHandle *member_certs_handle, ccf::MemberPublicEncryptionKeys::ReadOnlyHandle *member_enc_keys_handle)
Definition service_state.h:10
auto api_version_adapter(Fn &&f, ApiVersion min_accepted=ApiVersion::MIN)
Definition api_version.h:101
ApiVersion
Definition api_version.h:13
nlohmann::json produce_node_description(const ccf::NodeId &node_id, const ccf::NodeInfo &node_info, ccf::NodeEndorsedCertificates::ReadOnlyHandle *node_endorsed_certs_handle)
Definition service_state.h:72
void init_service_state_handlers(ccf::BaseEndpointRegistry &registry)
Definition service_state.h:164
std::string VirtualAttestationMeasurement
Definition measurement.h:96
std::string JwtIssuer
Definition jwt.h:35
@ error
Definition tls_session.h:24
MemberRecoveryRole
Definition members.h:26
view
Definition signatures.h:54
std::map< Feed, UVMEndorsementsData > FeedToEndorsementsDataMap
Definition uvm_endorsements.h:22
std::string DID
Definition uvm_endorsements.h:20
CodeStatus
Definition code_status.h:12
seqno
Definition signatures.h:54
uint64_t View
Definition tx_id.h:23
uint64_t SeqNo
Definition tx_id.h:36
std::string JwtKeyId
Definition jwt.h:36
Value & value()
Definition entity_id.h:60
Definition jwt.h:23
bool auto_refresh
Whether to auto-refresh keys from the issuer.
Definition jwt.h:27
std::optional< std::string > ca_cert_bundle_name
Optional CA bundle name used for authentication when auto-refreshing.
Definition jwt.h:25
Definition members.h:76
nlohmann::json member_data
Definition members.h:81
std::optional< MemberRecoveryRole > recovery_role
Optional recovery role of the member.
Definition members.h:84
MemberStatus status
Status of the member in the consortium.
Definition members.h:78
RpcInterfaces rpc_interfaces
RPC interfaces.
Definition node_info_network.h:150
Definition node_info.h:30
bool retired_committed
Definition node_info.h:74
QuoteInfo quote_info
Node enclave quote.
Definition node_info.h:32
NodeStatus status
Node status.
Definition node_info.h:36
nlohmann::json node_data
Definition node_info.h:57
QuoteFormat format
Quote format.
Definition quote_info.h:28
std::optional< std::string > endorsed_tcb
Endorsed TCB (hex-encoded) (SNP-only)
Definition quote_info.h:36
std::optional< std::vector< uint8_t > > uvm_endorsements
UVM endorsements (SNP-only)
Definition quote_info.h:34
std::vector< uint8_t > quote
Enclave quote.
Definition quote_info.h:30
std::vector< uint8_t > endorsements
Quote endorsements.
Definition quote_info.h:32
Definition tx_id.h:44
View view
Definition tx_id.h:45
Definition endpoint.h:20
Definition endpoint.h:161
nlohmann::json openapi
OpenAPI schema for endpoint.
Definition endpoint.h:171
std::string js_module
JavaScript module.
Definition endpoint.h:175
std::string js_function
JavaScript function name.
Definition endpoint.h:177
std::vector< nlohmann::json > authn_policies
Authentication policies.
Definition endpoint.h:169
ForwardingRequired forwarding_required
Endpoint forwarding policy.
Definition endpoint.h:165
RedirectionStrategy redirection_strategy
Endpoint redirection policy.
Definition endpoint.h:167
Mode mode
Endpoint mode.
Definition endpoint.h:163
Endpoint & set_openapi_hidden(bool hidden)
Definition endpoint.cpp:10
void install()
Definition endpoint.cpp:122
Definition measurement.h:17
std::string hex_str() const
Definition measurement.h:25
Definition attestation_sev_snp.h:119