1use anyhow::Context as _;
2use aranya_crypto::EncryptionPublicKey;
3use aranya_daemon_api::{self as api, CS};
4use aranya_id::custom_id;
5use aranya_policy_text::Text;
6use aranya_util::Addr;
7use buggy::BugExt as _;
8use tracing::instrument;
9
10#[cfg(feature = "preview")]
11use crate::client::{Permission, RoleManagementPermission};
12use crate::{
13 client::{
14 create_ctx, Client, Device, DeviceId, Devices, Label, LabelId, Labels, PublicKeyBundle,
15 Role, RoleId, Roles,
16 },
17 config::SyncPeerConfig,
18 error::{self, aranya_error, IpcError, Result},
19 util::{ApiConv as _, ApiId},
20};
21
22custom_id! {
23 pub struct TeamId;
25}
26impl ApiId<api::TeamId> for TeamId {}
27
28#[derive(Debug)]
30pub struct Team<'a> {
31 pub(super) client: &'a Client,
32 pub(super) id: api::TeamId,
33}
34
35impl Team<'_> {
36 pub fn team_id(&self) -> TeamId {
38 TeamId::from_api(self.id)
39 }
40
41 #[instrument(skip(self))]
43 pub async fn close_team(&self) -> Result<()> {
44 self.client
45 .daemon
46 .close_team(create_ctx(), self.id)
47 .await
48 .map_err(IpcError::new)?
49 .map_err(aranya_error)
50 }
51}
52
53impl Team<'_> {
54 #[instrument(skip(self))]
58 pub async fn encrypt_psk_seed_for_peer(&self, peer_enc_pk: &[u8]) -> Result<Vec<u8>> {
59 let peer_enc_pk: EncryptionPublicKey<CS> = postcard::from_bytes(peer_enc_pk)
60 .context("bad peer_enc_pk")
61 .map_err(error::other)?;
62 let wrapped = self
63 .client
64 .daemon
65 .encrypt_psk_seed_for_peer(create_ctx(), self.id, peer_enc_pk)
66 .await
67 .map_err(IpcError::new)?
68 .map_err(aranya_error)?;
69 let wrapped = postcard::to_allocvec(&wrapped).assume("can serialize")?;
70 Ok(wrapped)
71 }
72
73 #[instrument(skip(self))]
75 pub async fn add_sync_peer(&self, addr: Addr, config: SyncPeerConfig) -> Result<()> {
76 self.client
77 .daemon
78 .add_sync_peer(create_ctx(), addr, self.id, config.into())
79 .await
80 .map_err(IpcError::new)?
81 .map_err(aranya_error)
82 }
83
84 #[instrument(skip(self))]
89 pub async fn sync_now(&self, addr: Addr, cfg: Option<SyncPeerConfig>) -> Result<()> {
90 self.client
91 .daemon
92 .sync_now(create_ctx(), addr, self.id, cfg.map(Into::into))
93 .await
94 .map_err(IpcError::new)?
95 .map_err(aranya_error)
96 }
97
98 #[instrument(skip(self))]
100 pub async fn remove_sync_peer(&self, addr: Addr) -> Result<()> {
101 self.client
102 .daemon
103 .remove_sync_peer(create_ctx(), addr, self.id)
104 .await
105 .map_err(IpcError::new)?
106 .map_err(aranya_error)
107 }
108}
109
110impl Team<'_> {
111 #[instrument(skip(self))]
113 pub async fn add_device(
114 &self,
115 keys: PublicKeyBundle,
116 initial_role: Option<RoleId>,
117 ) -> Result<()> {
118 self.client
119 .daemon
120 .add_device_to_team(
121 create_ctx(),
122 self.id,
123 keys.into_api(),
124 initial_role.map(RoleId::into_api),
125 )
126 .await
127 .map_err(IpcError::new)?
128 .map_err(aranya_error)
129 }
130
131 pub fn device(&self, id: DeviceId) -> Device<'_> {
133 Device {
134 client: self.client,
135 team_id: self.id,
136 id: id.into_api(),
137 }
138 }
139
140 #[instrument(skip(self))]
142 pub async fn devices(&self) -> Result<Devices> {
143 let data = self
144 .client
145 .daemon
146 .devices_on_team(create_ctx(), self.id)
147 .await
148 .map_err(IpcError::new)?
149 .map_err(aranya_error)?
150 .into_vec()
154 .into_iter()
155 .map(DeviceId::from_api)
156 .collect();
157 Ok(Devices { data })
158 }
159
160 #[cfg(feature = "preview")]
172 #[cfg_attr(docsrs, doc(cfg(feature = "preview")))]
173 pub async fn sync_hello_subscribe(
174 &self,
175 peer: Addr,
176 config: crate::config::HelloSubscriptionConfig,
177 ) -> Result<()> {
178 self.client
181 .daemon
182 .sync_hello_subscribe(
183 create_ctx(),
184 peer,
185 self.id,
186 config.graph_change_debounce(),
187 config.expiration(),
188 config.periodic_interval(),
189 )
190 .await
191 .map_err(IpcError::new)?
192 .map_err(aranya_error)
193 }
194
195 #[cfg(feature = "preview")]
199 #[cfg_attr(docsrs, doc(cfg(feature = "preview")))]
200 pub async fn sync_hello_unsubscribe(&self, peer: Addr) -> Result<()> {
201 self.client
202 .daemon
203 .sync_hello_unsubscribe(create_ctx(), peer, self.id)
204 .await
205 .map_err(IpcError::new)?
206 .map_err(aranya_error)
207 }
208}
209
210impl Team<'_> {
211 #[instrument(skip(self))]
218 pub async fn setup_default_roles(&self, owning_role: RoleId) -> Result<Roles> {
219 let roles = self
220 .client
221 .daemon
222 .setup_default_roles(create_ctx(), self.id, owning_role.into_api())
223 .await
224 .map_err(IpcError::new)?
225 .map_err(aranya_error)?
226 .into_vec()
230 .into_iter()
231 .map(Role::from_api)
232 .collect();
233 Ok(Roles { roles })
234 }
235
236 #[cfg(feature = "preview")]
242 #[cfg_attr(docsrs, doc(cfg(feature = "preview")))]
243 #[instrument(skip(self))]
244 pub async fn create_role(&self, role_name: Text, owning_role: RoleId) -> Result<Role> {
245 let role = self
246 .client
247 .daemon
248 .create_role(create_ctx(), self.id, role_name, owning_role.into_api())
249 .await
250 .map_err(IpcError::new)?
251 .map_err(aranya_error)?;
252 Ok(Role::from_api(role))
253 }
254
255 #[cfg(feature = "preview")]
260 #[cfg_attr(docsrs, doc(cfg(feature = "preview")))]
261 #[instrument(skip(self))]
262 pub async fn delete_role(&self, role_id: RoleId) -> Result<()> {
263 self.client
264 .daemon
265 .delete_role(create_ctx(), self.id, role_id.into_api())
266 .await
267 .map_err(IpcError::new)?
268 .map_err(aranya_error)?;
269 Ok(())
270 }
271
272 #[cfg(feature = "preview")]
274 #[cfg_attr(docsrs, doc(cfg(feature = "preview")))]
275 #[instrument(skip(self))]
276 pub async fn add_perm_to_role(&self, role_id: RoleId, perm: Permission) -> Result<()> {
277 self.client
278 .daemon
279 .add_perm_to_role(create_ctx(), self.id, role_id.into_api(), perm)
280 .await
281 .map_err(IpcError::new)?
282 .map_err(aranya_error)?;
283 Ok(())
284 }
285
286 #[cfg(feature = "preview")]
288 #[cfg_attr(docsrs, doc(cfg(feature = "preview")))]
289 #[instrument(skip(self))]
290 pub async fn remove_perm_from_role(&self, role_id: RoleId, perm: Permission) -> Result<()> {
291 self.client
292 .daemon
293 .remove_perm_from_role(create_ctx(), self.id, role_id.into_api(), perm)
294 .await
295 .map_err(IpcError::new)?
296 .map_err(aranya_error)?;
297 Ok(())
298 }
299
300 #[cfg(feature = "preview")]
302 #[cfg_attr(docsrs, doc(cfg(feature = "preview")))]
303 #[instrument(skip(self))]
304 pub async fn add_role_owner(&self, role: RoleId, owning_role: RoleId) -> Result<()> {
305 self.client
306 .daemon
307 .add_role_owner(
308 create_ctx(),
309 self.id,
310 role.into_api(),
311 owning_role.into_api(),
312 )
313 .await
314 .map_err(IpcError::new)?
315 .map_err(aranya_error)
316 }
317
318 #[cfg(feature = "preview")]
320 #[cfg_attr(docsrs, doc(cfg(feature = "preview")))]
321 #[instrument(skip(self))]
322 pub async fn remove_role_owner(&self, role: RoleId, owning_role: RoleId) -> Result<()> {
323 self.client
324 .daemon
325 .remove_role_owner(
326 create_ctx(),
327 self.id,
328 role.into_api(),
329 owning_role.into_api(),
330 )
331 .await
332 .map_err(IpcError::new)?
333 .map_err(aranya_error)
334 }
335
336 #[instrument(skip(self))]
338 pub async fn role_owners(&self, role: RoleId) -> Result<Roles> {
339 let roles = self
340 .client
341 .daemon
342 .role_owners(create_ctx(), self.id, role.into_api())
343 .await
344 .map_err(IpcError::new)?
345 .map_err(aranya_error)?
346 .into_vec()
350 .into_iter()
351 .map(Role::from_api)
352 .collect();
353 Ok(Roles { roles })
354 }
355
356 #[cfg(feature = "preview")]
358 #[cfg_attr(docsrs, doc(cfg(feature = "preview")))]
359 #[instrument(skip(self))]
360 pub async fn assign_role_management_permission(
361 &self,
362 role: RoleId,
363 managing_role: RoleId,
364 perm: RoleManagementPermission,
365 ) -> Result<()> {
366 self.client
367 .daemon
368 .assign_role_management_perm(
369 create_ctx(),
370 self.id,
371 role.into_api(),
372 managing_role.into_api(),
373 perm,
374 )
375 .await
376 .map_err(IpcError::new)?
377 .map_err(aranya_error)
378 }
379
380 #[cfg(feature = "preview")]
383 #[cfg_attr(docsrs, doc(cfg(feature = "preview")))]
384 #[instrument(skip(self))]
385 pub async fn revoke_role_management_permission(
386 &self,
387 role: RoleId,
388 managing_role: RoleId,
389 perm: RoleManagementPermission,
390 ) -> Result<()> {
391 self.client
392 .daemon
393 .revoke_role_management_perm(
394 create_ctx(),
395 self.id,
396 role.into_api(),
397 managing_role.into_api(),
398 perm,
399 )
400 .await
401 .map_err(IpcError::new)?
402 .map_err(aranya_error)
403 }
404
405 #[instrument(skip(self))]
407 pub async fn roles(&self) -> Result<Roles> {
408 let roles = self
409 .client
410 .daemon
411 .team_roles(create_ctx(), self.id)
412 .await
413 .map_err(IpcError::new)?
414 .map_err(aranya_error)?
415 .into_vec()
419 .into_iter()
420 .map(Role::from_api)
421 .collect();
422 Ok(Roles { roles })
423 }
424}
425
426impl Team<'_> {
427 #[instrument(skip(self))]
429 pub async fn create_label(
430 &self,
431 label_name: Text,
432 managing_role_id: RoleId,
433 ) -> Result<LabelId> {
434 self.client
435 .daemon
436 .create_label(
437 create_ctx(),
438 self.id,
439 label_name,
440 managing_role_id.into_api(),
441 )
442 .await
443 .map_err(IpcError::new)?
444 .map_err(aranya_error)
445 .map(LabelId::from_api)
446 }
447
448 #[instrument(skip(self))]
450 pub async fn delete_label(&self, label_id: LabelId) -> Result<()> {
451 self.client
452 .daemon
453 .delete_label(create_ctx(), self.id, label_id.into_api())
454 .await
455 .map_err(IpcError::new)?
456 .map_err(aranya_error)
457 }
458
459 #[instrument(skip(self))]
461 pub async fn add_label_managing_role(
462 &self,
463 label_id: LabelId,
464 managing_role_id: RoleId,
465 ) -> Result<()> {
466 self.client
467 .daemon
468 .add_label_managing_role(
469 create_ctx(),
470 self.id,
471 label_id.into_api(),
472 managing_role_id.into_api(),
473 )
474 .await
475 .map_err(IpcError::new)?
476 .map_err(aranya_error)
477 }
478
479 #[instrument(skip(self))]
481 pub async fn label(&self, label_id: LabelId) -> Result<Option<Label>> {
482 let label = self
483 .client
484 .daemon
485 .label(create_ctx(), self.id, label_id.into_api())
486 .await
487 .map_err(IpcError::new)?
488 .map_err(aranya_error)?
489 .map(Label::from_api);
490 Ok(label)
491 }
492
493 #[instrument(skip(self))]
495 pub async fn labels(&self) -> Result<Labels> {
496 let labels = self
497 .client
498 .daemon
499 .labels(create_ctx(), self.id)
500 .await
501 .map_err(IpcError::new)?
502 .map_err(aranya_error)?
503 .into_iter()
504 .map(Label::from_api)
505 .collect();
506 Ok(Labels { labels })
507 }
508}