Line data Source code
1 : //! Structs representing the JSON formats used in the compute_ctl's HTTP API.
2 : use std::str::FromStr;
3 :
4 : use serde::{Deserialize, Serialize};
5 :
6 : use crate::privilege::Privilege;
7 : use crate::responses::ComputeCtlConfig;
8 : use crate::spec::{ComputeSpec, ExtVersion, PgIdent};
9 :
10 : /// The value to place in the [`ComputeClaims::audience`] claim.
11 : pub static COMPUTE_AUDIENCE: &str = "compute";
12 :
13 : /// Available scopes for a compute's JWT.
14 0 : #[derive(Copy, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
15 : #[serde(rename_all = "snake_case")]
16 : pub enum ComputeClaimsScope {
17 : /// An admin-scoped token allows access to all of `compute_ctl`'s authorized
18 : /// facilities.
19 : #[serde(rename = "compute_ctl:admin")]
20 : Admin,
21 : }
22 :
23 : impl FromStr for ComputeClaimsScope {
24 : type Err = anyhow::Error;
25 :
26 1 : fn from_str(s: &str) -> Result<Self, Self::Err> {
27 1 : match s {
28 1 : "compute_ctl:admin" => Ok(ComputeClaimsScope::Admin),
29 0 : _ => Err(anyhow::anyhow!("invalid compute claims scope \"{s}\"")),
30 : }
31 1 : }
32 : }
33 :
34 : /// When making requests to the `compute_ctl` external HTTP server, the client
35 : /// must specify a set of claims in `Authorization` header JWTs such that
36 : /// `compute_ctl` can authorize the request.
37 0 : #[derive(Clone, Debug, Deserialize, Serialize)]
38 : #[serde(rename = "snake_case")]
39 : pub struct ComputeClaims {
40 : /// The compute ID that will validate the token. The only case in which this
41 : /// can be [`None`] is if [`Self::scope`] is
42 : /// [`ComputeClaimsScope::Admin`].
43 : pub compute_id: Option<String>,
44 :
45 : /// The scope of what the token authorizes.
46 : pub scope: Option<ComputeClaimsScope>,
47 :
48 : /// The recipient the token is intended for.
49 : ///
50 : /// See [RFC 7519](https://www.rfc-editor.org/rfc/rfc7519#section-4.1.3) for
51 : /// more information.
52 : ///
53 : /// TODO: Remove the [`Option`] wrapper when control plane learns to send
54 : /// the claim.
55 : #[serde(rename = "aud")]
56 : pub audience: Option<Vec<String>>,
57 : }
58 :
59 : /// Request of the /configure API
60 : ///
61 : /// We now pass only `spec` in the configuration request, but later we can
62 : /// extend it and something like `restart: bool` or something else. So put
63 : /// `spec` into a struct initially to be more flexible in the future.
64 0 : #[derive(Debug, Deserialize, Serialize)]
65 : pub struct ConfigurationRequest {
66 : pub spec: ComputeSpec,
67 : pub compute_ctl_config: ComputeCtlConfig,
68 : }
69 :
70 0 : #[derive(Deserialize, Debug)]
71 : pub struct ExtensionInstallRequest {
72 : pub extension: PgIdent,
73 : pub database: PgIdent,
74 : pub version: ExtVersion,
75 : }
76 :
77 0 : #[derive(Deserialize, Debug)]
78 : pub struct SetRoleGrantsRequest {
79 : pub database: PgIdent,
80 : pub schema: PgIdent,
81 : pub privileges: Vec<Privilege>,
82 : pub role: PgIdent,
83 : }
84 :
85 : #[cfg(test)]
86 : mod test {
87 : use std::str::FromStr;
88 :
89 : use crate::requests::ComputeClaimsScope;
90 :
91 : /// Confirm that whether we parse the scope by string or through serde, the
92 : /// same values parse to the same enum variant.
93 : #[test]
94 1 : fn compute_request_scopes() {
95 : const ADMIN_SCOPE: &str = "compute_ctl:admin";
96 :
97 1 : let from_serde: ComputeClaimsScope =
98 1 : serde_json::from_str(&format!("\"{ADMIN_SCOPE}\"")).unwrap();
99 1 : let from_str = ComputeClaimsScope::from_str(ADMIN_SCOPE).unwrap();
100 :
101 1 : assert_eq!(from_serde, from_str);
102 1 : }
103 : }
|