manta_server/service/
template.rs

1//! BOS session template queries and BOS session creation with access validation.
2
3use manta_backend_dispatcher::error::Error;
4use manta_backend_dispatcher::interfaces::bos::{
5  ClusterSessionTrait, ClusterTemplateTrait,
6};
7use manta_backend_dispatcher::interfaces::hsm::group::GroupTrait;
8use manta_backend_dispatcher::types::bos::session::BosSession;
9use manta_backend_dispatcher::types::bos::session::Operation;
10use manta_backend_dispatcher::types::bos::session_template::BosSessionTemplate;
11
12use crate::server::common::app_context::InfraContext;
13use crate::server::common::authorization::{
14  get_groups_names_available, validate_target_hsm_members,
15};
16use crate::server::common::node_ops::validate_xname_format;
17pub use manta_shared::shared::params::template::{
18  ApplyTemplateParams, GetTemplateParams,
19};
20
21/// Fetch and filter BOS session templates from the backend.
22pub async fn get_templates(
23  infra: &InfraContext<'_>,
24  token: &str,
25  params: &GetTemplateParams,
26) -> Result<Vec<BosSessionTemplate>, Error> {
27  let target_hsm_group_vec = get_groups_names_available(
28    infra.backend,
29    token,
30    params.hsm_group.as_deref(),
31    params.settings_hsm_group_name.as_deref(),
32  )
33  .await?;
34
35  let hsm_member_vec = infra
36    .backend
37    .get_member_vec_from_group_name_vec(token, &target_hsm_group_vec)
38    .await?;
39
40  let limit_ref = params.limit.as_ref();
41
42  tracing::info!(
43    "Get BOS sessiontemplates for HSM groups: {:?}",
44    target_hsm_group_vec
45  );
46
47  let mut bos_sessiontemplate_vec = infra
48    .backend
49    .get_and_filter_templates(
50      token,
51      infra.shasta_base_url,
52      infra.shasta_root_cert,
53      &target_hsm_group_vec,
54      &hsm_member_vec,
55      params.name.as_deref(),
56      limit_ref,
57    )
58    .await?;
59
60  bos_sessiontemplate_vec.sort_by(|a, b| a.name.cmp(&b.name));
61
62  Ok(bos_sessiontemplate_vec)
63}
64
65/// Validate template access, resolve limit targets, and build
66/// a BOS session ready for creation.
67///
68/// Returns `(bos_session, resolved_limit_vec)`.
69pub async fn validate_and_prepare_template_session(
70  infra: &InfraContext<'_>,
71  token: &str,
72  params: &ApplyTemplateParams,
73) -> Result<(BosSession, Vec<String>), Error> {
74  let backend = infra.backend;
75
76  // Fetch BOS sessiontemplate
77  let bos_sessiontemplate_vec = backend
78    .get_and_filter_templates(
79      token,
80      infra.shasta_base_url,
81      infra.shasta_root_cert,
82      &[],
83      &[],
84      Some(&params.bos_sessiontemplate_name),
85      None,
86    )
87    .await?;
88
89  let bos_sessiontemplate = if bos_sessiontemplate_vec.is_empty() {
90    return Err(Error::NotFound(format!(
91      "No BOS sessiontemplate '{}' found",
92      params.bos_sessiontemplate_name
93    )));
94  } else {
95    bos_sessiontemplate_vec.first().ok_or_else(|| {
96      Error::NotFound("BOS sessiontemplate list unexpectedly empty".to_string())
97    })?
98  };
99
100  // Validate user has access to the BOS sessiontemplate targets
101  tracing::info!(
102    "Validate user has access to HSM group in BOS sessiontemplate"
103  );
104  let target_hsm_vec = bos_sessiontemplate.get_target_hsm();
105  let target_xname_vec: Vec<String> = if !target_hsm_vec.is_empty() {
106    backend
107      .get_member_vec_from_group_name_vec(token, &target_hsm_vec)
108      .await
109      .unwrap_or_default()
110  } else {
111    bos_sessiontemplate.get_target_xname()
112  };
113
114  validate_target_hsm_members(backend, token, &target_xname_vec).await?;
115
116  // Validate user has access to xnames in `limit` argument
117  tracing::info!("Validate user has access to xnames in BOS sessiontemplate");
118  let limit_vec: Vec<String> =
119    params.limit.split(',').map(str::to_string).collect();
120
121  let mut xnames_to_validate_access_vec = Vec::new();
122
123  for limit_value in &limit_vec {
124    tracing::info!("Check if limit value '{}', is an xname", limit_value);
125    if validate_xname_format(limit_value) {
126      tracing::info!("limit value '{}' is an xname", limit_value);
127      xnames_to_validate_access_vec.push(limit_value.to_string());
128    } else {
129      let hsm_members_vec_rslt = backend
130        .get_member_vec_from_group_name_vec(
131          token,
132          std::slice::from_ref(limit_value),
133        )
134        .await;
135
136      if let Ok(mut hsm_members_vec) = hsm_members_vec_rslt {
137        tracing::info!(
138          "Check if limit value '{}', is an HSM group name",
139          limit_value
140        );
141        xnames_to_validate_access_vec.append(&mut hsm_members_vec);
142      } else {
143        return Err(Error::BadRequest(format!(
144          "Value '{limit_value}' in 'limit' argument does not match \
145           an xname or a HSM group name."
146        )));
147      }
148    }
149  }
150
151  tracing::info!("Validate list of xnames translated from 'limit argument'");
152  validate_target_hsm_members(backend, token, &xnames_to_validate_access_vec)
153    .await?;
154
155  tracing::info!("Access to '{}' granted. Continue.", params.limit);
156
157  // Build BOS session
158  let bos_session = BosSession {
159    name: params.bos_session_name.clone(),
160    tenant: None,
161    operation: Some(
162      Operation::from_str(&params.bos_session_operation).map_err(|_| {
163        Error::BadRequest(format!(
164          "Invalid BOS session operation '{}'",
165          params.bos_session_operation
166        ))
167      })?,
168    ),
169    template_name: params.bos_sessiontemplate_name.clone(),
170    limit: Some(limit_vec.join(",")),
171    stage: Some(false),
172    components: None,
173    include_disabled: Some(params.include_disabled),
174    status: None,
175  };
176
177  Ok((bos_session, limit_vec))
178}
179
180/// Create a BOS session via the backend.
181pub async fn create_bos_session(
182  infra: &InfraContext<'_>,
183  token: &str,
184  bos_session: BosSession,
185) -> Result<BosSession, Error> {
186  infra
187    .backend
188    .post_template_session(
189      token,
190      infra.shasta_base_url,
191      infra.shasta_root_cert,
192      bos_session,
193    )
194    .await
195}