34constexpr const char* kIti18SoapAction =
35 "urn:ihe:iti:2007:RegistryStoredQuery";
41constexpr const char* kSoapContentType =
42 "application/soap+xml; charset=UTF-8; "
43 "action=\"urn:ihe:iti:2007:RegistryStoredQuery\"";
51bool contains_slot_literal_metachar(std::string_view v) {
53 if (c ==
'\'' || c ==
'(' || c ==
')' || c ==
',')
return true;
54 if (
static_cast<unsigned char>(c) < 0x20)
return true;
62int map_signature_error(
int raw) {
65 return static_cast<int>(
82 std::shared_ptr<kcenon::pacs::security::xds_audit_sink> sink) {
87 const std::string& patient_id,
95 if (patient_id.empty()) {
96 return do_audit(kcenon::common::make_error<registry_query_result>(
99 "find_documents: patient_id is empty",
102 if (contains_slot_literal_metachar(patient_id)) {
103 return do_audit(kcenon::common::make_error<registry_query_result>(
106 "find_documents: patient_id contains a character that is "
107 "not legal inside an ebRIM StoredQuery slot literal "
108 "('(),\\' or control character)",
112 if (contains_slot_literal_metachar(s)) {
114 kcenon::common::make_error<registry_query_result>(
117 "find_documents: options.status_values contains a "
118 "slot-literal metacharacter",
124 return do_audit(kcenon::common::make_error<registry_query_result>(
127 "find_documents: options.creation_time_* contains a "
128 "slot-literal metacharacter",
141 const std::vector<std::string>& uuids) {
149 return do_audit(kcenon::common::make_error<registry_query_result>(
152 "get_documents: uuids is empty",
155 for (
const auto& u : uuids) {
158 kcenon::common::make_error<registry_query_result>(
161 "get_documents: uuids contains an empty element",
164 if (contains_slot_literal_metachar(u)) {
166 kcenon::common::make_error<registry_query_result>(
169 "get_documents: uuids contains an element with a "
170 "slot-literal metacharacter",
187 if (env_result.is_err()) {
188 return kcenon::common::make_error<registry_query_result>(
191 auto env = env_result.value();
196 if (sign_result.is_err()) {
197 return kcenon::common::make_error<registry_query_result>(
198 sign_result.error());
210 if (http_result.is_err()) {
211 return kcenon::common::make_error<registry_query_result>(
212 http_result.error());
214 auto response = http_result.value();
222 if (verify.is_err()) {
223 auto err = verify.error();
224 err.code = map_signature_error(err.code);
225 return kcenon::common::make_error<registry_query_result>(err);
234 kcenon::pacs::security::xds_iti18_event e;
237 kcenon::pacs::security::xds_iti18_event::kind::find_documents;
238 e.patient_id = patient_id;
244 e.failure_description = r.error().message;
252 kcenon::pacs::security::xds_iti18_event e;
255 kcenon::pacs::security::xds_iti18_event::kind::get_documents;
256 e.document_uuids = uuids;
262 e.failure_description = r.error().message;
269 std::shared_ptr<kcenon::pacs::security::xds_audit_sink>
audit_sink_;
273 : impl_(std::make_unique<
impl>(std::move(opts), std::move(signing))) {}
286 const std::vector<std::string>& uuids) {
291 std::shared_ptr<kcenon::pacs::security::xds_audit_sink> sink) {
kcenon::common::Result< registry_query_result > find_documents(const std::string &patient_id, const registry_query_options &options)
void audit_find(const std::string &patient_id, const kcenon::common::Result< registry_query_result > &r)
void audit_get(const std::vector< std::string > &uuids, const kcenon::common::Result< registry_query_result > &r)
std::shared_ptr< kcenon::pacs::security::xds_audit_sink > audit_sink_
impl(http_options opts, signing_material signing)
void set_audit_sink(std::shared_ptr< kcenon::pacs::security::xds_audit_sink > sink)
kcenon::common::Result< registry_query_result > get_documents(const std::vector< std::string > &uuids)
kcenon::common::Result< registry_query_result > execute(const registry_query_request &req, detail::stored_query_kind kind)
signing_material signing_
ITI-18 Registry Query actor.
kcenon::common::Result< registry_query_result > get_documents(const std::vector< std::string > &uuids)
Execute the GetDocuments stored query (urn:uuid:5737b14c-8a1a-4539-b659-e03a34a5e1e4).
registry_query(http_options opts, signing_material signing)
Construct an ITI-18 Registry Query actor bound to a registry endpoint and a signing credential.
void set_audit_sink(std::shared_ptr< kcenon::pacs::security::xds_audit_sink > sink)
Install an ATNA audit sink.
std::unique_ptr< impl > impl_
kcenon::common::Result< registry_query_result > find_documents(const std::string &patient_id, const registry_query_options &options={})
Execute the FindDocuments stored query (urn:uuid:14d4debf-8f97-4251-9a1e-a3a9d68b2a6f).
libcurl-backed HTTPS POST client for ITI-41 transport.
kcenon::common::Result< built_envelope > build_iti18_envelope(const registry_query_request &req, stored_query_kind kind)
Build a SOAP envelope for ITI-18 from a registry_query_request.
kcenon::common::Result< http_response > http_post(const http_options &opts, const std::string &content_type, const std::string &body)
POST body to opts.endpoint with the given Content-Type.
stored_query_kind
Discriminator for the two stored queries this actor supports.
kcenon::common::Result< bool > sign_envelope(built_envelope &env, std::string_view cert_pem, std::string_view key_pem, std::string_view key_password)
Sign the body + timestamp of env in place.
kcenon::common::Result< registry_query_result > parse_query_response(const std::string &root_xml)
Parse an AdhocQueryResponse envelope into a registry_query_result.
kcenon::common::Result< bool > verify_envelope_integrity(std::string_view signed_xml)
Verify the structural integrity of a previously signed envelope against the certificate embedded in i...
constexpr std::string_view error_source
Short identifier used as the "source" field of make_error.
@ registry_query_missing_patient_id
@ consumer_signature_verification_failed
@ consumer_signature_missing
@ registry_query_empty_uuid_list
@ registry_query_invalid_patient_id
@ registry_query_signature_verification_failed
@ registry_query_signature_missing
@ registry_query_missing_document_uuid
SOAP 1.2 envelope builder for IHE XDS.b ITI-18 RegistryStoredQuery.
Parser for IHE XDS.b ITI-18 AdhocQueryResponse.
IHE XDS.b Registry Query Actor (ITI-18 RegistryStoredQuery).
SOAP 1.2 envelope builder for IHE XDS.b ITI-41 requests.
HTTPS transport options for the Document Source actor.
Optional creation-time window for ITI-18 FindDocuments queries.
std::vector< std::string > status_values
std::string creation_time_from
std::string creation_time_to
ITI-18 RegistryStoredQuery request target.
registry_query_options options
std::vector< std::string > document_uuids
ITI-18 RegistryStoredQueryResponse payload.
Signing material for WS-Security XML-DSig.
std::string private_key_password
std::string certificate_pem
std::string private_key_pem
WS-Security XML-DSig signer backed by OpenSSL EVP_DigestSign.
ATNA audit event helpers for the IHE XDS.b actors (ITI-18/41/43).