iroh_services/
api_secret.rs1use std::{
2 collections::BTreeSet,
3 env::VarError,
4 fmt::{self, Display},
5 str::FromStr,
6};
7
8use anyhow::{Context, anyhow};
9use iroh::{EndpointAddr, EndpointId, SecretKey, TransportAddr};
10use iroh_tickets::{ParseError, Ticket};
11use serde::{Deserialize, Serialize};
12
13#[derive(Debug, Clone)]
16pub struct ApiSecret {
17 pub secret: SecretKey,
19 pub remote: EndpointAddr,
21}
22
23impl Display for ApiSecret {
24 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
25 write!(f, "{}", Ticket::serialize(self))
26 }
27}
28
29#[derive(Serialize, Deserialize)]
30struct Variant0EndpointAddr {
31 endpoint_id: EndpointId,
32 addrs: BTreeSet<TransportAddr>,
33}
34
35#[derive(Serialize, Deserialize)]
37enum TicketWireFormat {
38 Variant0(Variant0ServicesTicket),
39}
40
41#[derive(Serialize, Deserialize)]
42struct Variant0ServicesTicket {
43 secret: SecretKey,
44 addr: Variant0EndpointAddr,
45}
46
47impl Ticket for ApiSecret {
48 const KIND: &'static str = "services";
51
52 fn to_bytes(&self) -> Vec<u8> {
53 let data = TicketWireFormat::Variant0(Variant0ServicesTicket {
54 secret: self.secret.clone(),
55 addr: Variant0EndpointAddr {
56 endpoint_id: self.remote.id,
57 addrs: self.remote.addrs.clone(),
58 },
59 });
60 postcard::to_stdvec(&data).expect("postcard serialization failed")
61 }
62
63 fn from_bytes(bytes: &[u8]) -> Result<Self, ParseError> {
64 let res: TicketWireFormat = postcard::from_bytes(bytes)?;
65 let TicketWireFormat::Variant0(Variant0ServicesTicket { secret, addr }) = res;
66 Ok(Self {
67 secret,
68 remote: EndpointAddr {
69 id: addr.endpoint_id,
70 addrs: addr.addrs.clone(),
71 },
72 })
73 }
74}
75
76impl FromStr for ApiSecret {
77 type Err = ParseError;
78
79 fn from_str(s: &str) -> Result<Self, Self::Err> {
80 iroh_tickets::Ticket::deserialize(s)
81 }
82}
83
84impl ApiSecret {
85 pub fn new(secret: SecretKey, remote: impl Into<EndpointAddr>) -> Self {
87 Self {
88 secret,
89 remote: remote.into(),
90 }
91 }
92
93 pub fn from_env_var(env_var: &str) -> anyhow::Result<Self> {
95 match std::env::var(env_var) {
96 Ok(ticket_string) => Self::from_str(&ticket_string)
97 .context(format!("invalid api secret at env var {env_var}")),
98 Err(VarError::NotPresent) => Err(anyhow!("{env_var} environment variable is not set")),
99 Err(VarError::NotUnicode(e)) => Err(anyhow!(
100 "{env_var} environment variable is not valid unicode: {:?}",
101 e
102 )),
103 }
104 }
105
106 pub fn addr(&self) -> &EndpointAddr {
108 &self.remote
109 }
110}