Skip to main content

cloud_sdk/operation/prepared/
read_only_post.rs

1//! Closed approval registry for provider protocols that query through `POST`.
2
3use crate::authentication::{AuthenticationScopePolicy, ScopeRequirement};
4use crate::operation::{
5    CostIntent, OperationId, OperationImpact, OperationMetadata, RequestIdPolicy, RequestSemantics,
6    RetryEligibility,
7};
8use crate::transport::{EndpointIdentity, EndpointPolicy, EndpointScheme, TransportRequest};
9use crate::{Method, operation_id};
10
11use super::{ProviderService, RequestBodySensitivity};
12
13/// Closed registry of reviewed read-only queries carried by `POST`.
14///
15/// A variant is only an approval selector. Construction still validates every
16/// bound identity and wire property owned by that entry.
17#[derive(Clone, Copy, Debug, Eq, PartialEq)]
18#[non_exhaustive]
19pub enum ApprovedReadOnlyPostQuery {
20    /// Hetzner Robot's source-locked `POST /traffic` query.
21    HetznerRobotTraffic,
22}
23
24impl ApprovedReadOnlyPostQuery {
25    pub(super) fn validate(
26        self,
27        request: TransportRequest<'_>,
28        service: ProviderService<'_>,
29        metadata: OperationMetadata,
30        authentication: AuthenticationScopePolicy<'_>,
31        body_sensitivity: RequestBodySensitivity,
32    ) -> Option<OperationId> {
33        match self {
34            Self::HetznerRobotTraffic => {
35                let endpoint = EndpointIdentity::new(
36                    EndpointScheme::Https,
37                    "robot-ws.your-server.de",
38                    443,
39                    "/",
40                )
41                .ok()?;
42                let expected_authentication = AuthenticationScopePolicy::new(
43                    ScopeRequirement::Required(service.provider_id()),
44                    ScopeRequirement::Required(service.service_id()),
45                    ScopeRequirement::Required(endpoint),
46                    ScopeRequirement::Forbidden,
47                    ScopeRequirement::Forbidden,
48                    ScopeRequirement::Forbidden,
49                );
50                let headers = request.headers();
51                let accept = headers.get("accept")?;
52                let content_type = headers.get("content-type")?;
53                (request.method() == Method::Post
54                    && request.target().as_str() == "/traffic"
55                    && !request.body().is_empty()
56                    && headers.as_slice().len() == 2
57                    && accept.value().as_str() == "application/json"
58                    && content_type.value().as_str() == "application/x-www-form-urlencoded"
59                    && service.provider_id().as_str() == "hetzner"
60                    && service.service_id().as_str() == "robot"
61                    && service.endpoint_policy() == EndpointPolicy::fixed(endpoint)
62                    && authentication == expected_authentication
63                    && metadata.impact() == OperationImpact::ReadOnly
64                    && metadata.semantics() == RequestSemantics::Safe
65                    && metadata.retry_eligibility() == RetryEligibility::ExplicitPolicy
66                    && metadata.cost_intent() == CostIntent::NoKnownCost
67                    && metadata.request_id_policy() == RequestIdPolicy::Discard
68                    && body_sensitivity == RequestBodySensitivity::Sensitive)
69                    .then_some(operation_id!("robot_get_traffic"))
70            }
71        }
72    }
73}