1use std::collections::{BTreeMap, HashMap};
2use std::hash::Hash;
3use std::time::{SystemTime, UNIX_EPOCH};
4
5use serde::{Deserialize, Serialize};
6use uuid::Uuid;
7use wascap::jwt;
8
9pub(crate) const POLICY_TYPE_VERSION: &str = "v1";
12
13#[async_trait::async_trait]
15pub trait PolicyManager: Send + Sync {
16 async fn evaluate_start_component(
18 &self,
19 _component_id: &str,
20 _image_ref: &str,
21 _max_instances: u32,
22 _annotations: &BTreeMap<String, String>,
23 _claims: Option<&jwt::Claims<jwt::Component>>,
24 ) -> anyhow::Result<Response> {
25 Ok(Response {
26 request_id: Uuid::new_v4().to_string(),
27 permitted: true,
28 message: None,
29 })
30 }
31
32 async fn evaluate_start_provider(
34 &self,
35 _provider_id: &str,
36 _provider_ref: &str,
37 _annotations: &BTreeMap<String, String>,
38 _claims: Option<&jwt::Claims<jwt::CapabilityProvider>>,
39 ) -> anyhow::Result<Response> {
40 Ok(Response {
41 request_id: Uuid::new_v4().to_string(),
42 permitted: true,
43 message: None,
44 })
45 }
46
47 async fn evaluate_perform_invocation(
49 &self,
50 _component_id: &str,
51 _image_ref: &str,
52 _annotations: &BTreeMap<String, String>,
53 _claims: Option<&jwt::Claims<jwt::Component>>,
54 _interface: String,
55 _function: String,
56 ) -> anyhow::Result<Response> {
57 Ok(Response {
58 request_id: Uuid::new_v4().to_string(),
59 permitted: true,
60 message: None,
61 })
62 }
63}
64
65#[derive(Default)]
68pub struct DefaultPolicyManager;
69impl super::PolicyManager for DefaultPolicyManager {}
70
71#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Hash)]
72pub struct PolicyClaims {
74 #[serde(rename = "publicKey")]
76 pub public_key: String,
77 pub issuer: String,
79 #[serde(rename = "issuedAt")]
81 pub issued_at: String,
82 #[serde(rename = "expiresAt")]
84 pub expires_at: Option<u64>,
85 pub expired: bool,
87}
88
89#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Hash)]
90pub struct ComponentInformation {
92 #[serde(rename = "componentId")]
94 pub component_id: String,
95 #[serde(rename = "imageRef")]
97 pub image_ref: String,
98 #[serde(rename = "maxInstances")]
100 pub max_instances: u32,
101 pub annotations: BTreeMap<String, String>,
103 pub claims: Option<PolicyClaims>,
105}
106
107#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Hash)]
108pub struct ProviderInformation {
110 #[serde(rename = "providerId")]
112 pub provider_id: String,
113 #[serde(rename = "imageRef")]
115 pub image_ref: String,
116 pub annotations: BTreeMap<String, String>,
118 pub claims: Option<PolicyClaims>,
120}
121
122#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Hash)]
123pub struct PerformInvocationRequest {
125 pub interface: String,
127 pub function: String,
129 pub target: ComponentInformation,
131}
132
133#[derive(Clone, Debug, Serialize)]
135pub struct HostInfo {
136 #[serde(rename = "publicKey")]
138 pub public_key: String,
139 #[serde(rename = "lattice")]
141 pub lattice: String,
142 pub labels: HashMap<String, String>,
144}
145
146#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize, Hash)]
148pub enum RequestKind {
149 #[serde(rename = "performInvocation")]
151 PerformInvocation,
152 #[serde(rename = "startComponent")]
154 StartComponent,
155 #[serde(rename = "startProvider")]
157 StartProvider,
158 #[serde(rename = "unknown")]
160 Unknown,
161}
162
163#[derive(Clone, Debug, Eq, PartialEq, Serialize, Hash)]
164#[serde(untagged)]
165pub enum RequestBody {
167 PerformInvocation(PerformInvocationRequest),
169 StartComponent(ComponentInformation),
171 StartProvider(ProviderInformation),
173 Unknown,
175}
176
177impl From<&RequestBody> for RequestKey {
178 fn from(val: &RequestBody) -> RequestKey {
179 match val {
180 RequestBody::StartComponent(ref req) => RequestKey {
181 kind: RequestKind::StartComponent,
182 cache_key: format!("{}_{}", req.component_id, req.image_ref),
183 },
184 RequestBody::StartProvider(ref req) => RequestKey {
185 kind: RequestKind::StartProvider,
186 cache_key: format!("{}_{}", req.provider_id, req.image_ref),
187 },
188 RequestBody::PerformInvocation(ref req) => RequestKey {
189 kind: RequestKind::PerformInvocation,
190 cache_key: format!(
191 "{}_{}_{}_{}",
192 req.target.component_id, req.target.image_ref, req.interface, req.function
193 ),
194 },
195 RequestBody::Unknown => RequestKey {
196 kind: RequestKind::Unknown,
197 cache_key: String::new(),
198 },
199 }
200 }
201}
202
203#[derive(Serialize)]
205pub(crate) struct Request {
206 #[serde(rename = "requestId")]
208 #[allow(clippy::struct_field_names)]
209 pub(crate) request_id: String,
210 pub(crate) kind: RequestKind,
212 pub(crate) version: String,
214 pub(crate) request: RequestBody,
216 pub(crate) host: HostInfo,
218}
219
220#[derive(Clone, Debug, Hash, Eq, PartialEq)]
221pub(crate) struct RequestKey {
222 kind: RequestKind,
224 cache_key: String,
229}
230
231#[derive(Clone, Debug, Deserialize)]
233pub struct Response {
234 #[serde(rename = "requestId")]
236 pub request_id: String,
237 pub permitted: bool,
239 #[serde(skip_serializing_if = "Option::is_none")]
241 pub message: Option<String>,
242}
243
244fn is_expired(expires: u64) -> bool {
245 SystemTime::now()
246 .duration_since(UNIX_EPOCH)
247 .expect("time went backwards") .as_secs()
249 > expires
250}
251
252impl From<&jwt::Claims<jwt::Component>> for PolicyClaims {
253 fn from(claims: &jwt::Claims<jwt::Component>) -> Self {
254 PolicyClaims {
255 public_key: claims.subject.to_string(),
256 issuer: claims.issuer.to_string(),
257 issued_at: claims.issued_at.to_string(),
258 expires_at: claims.expires,
259 expired: claims.expires.is_some_and(is_expired),
260 }
261 }
262}
263
264impl From<&jwt::Claims<jwt::CapabilityProvider>> for PolicyClaims {
265 fn from(claims: &jwt::Claims<jwt::CapabilityProvider>) -> Self {
266 PolicyClaims {
267 public_key: claims.subject.to_string(),
268 issuer: claims.issuer.to_string(),
269 issued_at: claims.issued_at.to_string(),
270 expires_at: claims.expires,
271 expired: claims.expires.is_some_and(is_expired),
272 }
273 }
274}