Skip to main content

snap_control/pg_wap/crpc/
model.rs

1// Copyright 2026 Anapaya Systems
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//   http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Models of the WAP control plane API, and the trait that serves them.
16//!
17//! There is one model per message of the `anapaya.wap.v1.WapControl` service. A model is the
18//! parsed and validated form of its message: domains are [`CustomerDomain`]s, segments are
19//! [`SignedPathSegment`]s and timestamps are [`SystemTime`]s, so a
20//! [`ControlServiceAPIHandler`] never sees an unvalidated string or an untyped number.
21
22use std::{collections::BTreeMap, net::IpAddr, time::SystemTime};
23
24use sciparse::segment::SignedPathSegment;
25
26use crate::pg_wap::sni::CustomerDomain;
27
28/// A request to authorize a client for a set of targets.
29///
30/// The model of `anapaya.wap.v1.AuthorizeTargetsRequest`.
31#[derive(Debug)]
32pub struct AuthorizeTargetsRequest {
33    /// The targets to authorize, with the private segments granted towards each of them.
34    ///
35    /// The key is the customer domain of the target, i.e. the part of the SNI that follows the
36    /// WAP ID and the namespace. A client cannot know the WAP ID it has to use before it has the
37    /// response to this request, and grants are held per customer domain, see
38    /// [`GrantManager::authorize`](crate::pg_wap::grants::GrantManager::authorize).
39    pub targets: BTreeMap<CustomerDomain, AuthSegments>,
40}
41
42/// The private segments granted towards one target.
43///
44/// The model of `anapaya.wap.v1.AuthSegments`. The segments a client grants here are the ones
45/// the WAP cannot look up itself, typically because they come from a Hidden Segment Directory.
46///
47/// The three kinds are kept apart because that is how a client obtains them. The grants they turn
48/// into only distinguish core from non-core segments, see
49/// [`GrantedSegmentId`](crate::pg_wap::grants::GrantedSegmentId).
50#[derive(Debug, Default)]
51pub struct AuthSegments {
52    /// The up segments granted for the target.
53    pub up_segments: Vec<SignedPathSegment>,
54    /// The down segments granted for the target.
55    pub down_segments: Vec<SignedPathSegment>,
56    /// The core segments granted for the target.
57    pub core_segments: Vec<SignedPathSegment>,
58}
59
60impl AuthSegments {
61    /// The number of segments granted here, over all three kinds.
62    pub fn count(&self) -> usize {
63        self.up_segments.len() + self.down_segments.len() + self.core_segments.len()
64    }
65}
66
67/// The grant handed out in response to an [`AuthorizeTargetsRequest`].
68///
69/// The model of `anapaya.wap.v1.AuthorizeTargetsResponse`.
70#[derive(Debug)]
71pub struct AuthorizeTargetsResponse {
72    /// The IP address the grant was handed out for, reflected back to the client.
73    pub client_ip: IpAddr,
74    /// The ID of the WAP that handled the request.
75    ///
76    /// The grant is only valid on this WAP, so the client has to address it in the SNI of the
77    /// data plane connections that follow, see [`WapSNI`](crate::pg_wap::sni::WapSNI).
78    pub wap_id: String,
79    /// The port the data plane of that WAP listens on.
80    pub data_plane_port: u16,
81    /// When the grant expires, i.e. by when the client has to have authorized again.
82    pub expiry_time: SystemTime,
83}
84
85/// The failures a [`ControlServiceAPIHandler`] reports.
86#[derive(Debug, thiserror::Error)]
87pub enum AuthorizeTargetsError {
88    /// The client has to send a different request.
89    #[error("{0}")]
90    InvalidRequest(String),
91    /// The client is over a grant limit.
92    #[error("{0}")]
93    LimitReached(String),
94    /// Internal error. The endpoint logs it and returns a fixed message.
95    #[error(transparent)]
96    Internal(#[from] anyhow::Error),
97}
98
99/// Serves the requests of the `anapaya.wap.v1.WapControl` service.
100///
101/// Implemented by the WAP control plane and served by
102/// [`nest_crpc_api`](super::api::nest_crpc_api).
103pub trait ControlServiceAPIHandler: Send + Sync {
104    /// Authorizes `client_ip` for the targets and segments of `request`, as of `now`.
105    ///
106    /// `client_ip` is the address the request arrived from, not one the request itself names, so a
107    /// client cannot ask for a grant on somebody else's address.
108    ///
109    /// Grants are additive and are never shortened, so a client refreshes its authorization by
110    /// calling this again before the returned expiry, see
111    /// [`GrantManager::authorize`](crate::pg_wap::grants::GrantManager::authorize).
112    ///
113    /// Returned error carries an HTTP status code and a human-readable message.
114    fn authorize_targets(
115        &self,
116        client_ip: IpAddr,
117        request: AuthorizeTargetsRequest,
118        now: SystemTime,
119    ) -> Result<AuthorizeTargetsResponse, AuthorizeTargetsError>;
120}