timegraph_identity/
identity.rs

1extern crate base64;
2extern crate hex;
3use crate::{bytes_decode, bytes_encode, Signer, SIGNING_CONTEXT};
4
5#[derive(Debug, Clone)]
6pub enum Identity {
7    User {
8        // identifies user without API key
9        // allows frontend behave as a user
10        // playground is used ANONYMOUS_USER as user_id
11        subgraph_id: u64,
12        user_id: u64, // user identifier
13        key: String,  // fronted API key
14        // user Wallet pubkey
15        expiration: u64,
16        origins: String,
17        signature: String, // signed by frontend API key's Secret or user wallet
18    },
19    ApiKey {
20        subgraph_id: u64,
21        key: String, // API key's public value
22        expiration: u64,
23        origins: String,
24        signature: String, // signed by API key's Secret
25    },
26}
27
28// unsigned values are presented in the ssk string
29
30pub const DEFAULT_SUBGRAPH: u64 = 0;
31pub const ANONYMOUS_USER: u64 = 0; // Identity has system pubkey and signed by system
32                                   //   limited to anonymous namespace with User role
33pub const WALLET_USER: u64 = 1; // Identity has user pubkey and signed by user
34pub const FRONTEND_USER: u64 = 2; // Identity has system bub key and signed by system
35                                  //   can do specific operation like use management
36
37// signed values are for internal use
38pub const NO_SUBGRAPH: i64 = -1;
39pub const NO_USER: i64 = -1;
40
41// any other value of the Identity::User.user_id means
42//      it's a system SSK behaves as user with the specified ObjectId
43
44impl Identity {
45    pub fn from_ssk(ssk: &str) -> anyhow::Result<Identity> {
46        let elms: Vec<&str> = ssk.split(";").collect();
47        if elms.len() != 6 {
48            anyhow::bail!("invalid count of elements is the session key")
49        }
50        let identity = if elms[4].len() < 1 {
51            Identity::ApiKey {
52                subgraph_id: elms[0].parse()?,
53                key: elms[1].to_string(),
54                expiration: elms[2].parse()?,
55                origins: elms[3].to_string(),
56                signature: elms[5].to_string(),
57            }
58        } else {
59            Identity::User {
60                subgraph_id: elms[0].parse()?,
61                user_id: elms[4].parse()?,
62                key: elms[1].to_string(),
63                expiration: elms[2].parse()?,
64                origins: elms[3].to_string(),
65                signature: elms[5].to_string(),
66            }
67        };
68        identity.verify()?;
69        Ok(identity)
70    }
71
72    pub fn to_ssk(&self) -> anyhow::Result<String> {
73        let (ssk_str, sign) = self.stringify_as_ssk()?;
74        Ok(ssk_str + sign)
75    }
76
77    pub fn stringify_as_ssk(&self) -> anyhow::Result<(String, &str)> {
78        Ok(match self {
79            Identity::User {
80                subgraph_id,
81                user_id,
82                key,
83                expiration,
84                origins,
85                signature,
86            } => (
87                format!("{subgraph_id};{key};{expiration};{origins};{user_id};"),
88                signature,
89            ),
90            Identity::ApiKey {
91                subgraph_id,
92                key,
93                expiration,
94                origins,
95                signature,
96            } => (
97                format!("{subgraph_id};{key};{expiration};{origins};;"),
98                signature,
99            ),
100        })
101    }
102
103    pub fn sign(&mut self, secret: &str) -> anyhow::Result<()> {
104        let sk = bytes_decode(secret)?;
105        let signer = Signer::from_bytes(&sk)?;
106        self.sign_with(&signer)
107    }
108
109    pub fn sign_with(&mut self, signer: &Signer) -> anyhow::Result<()> {
110        let (ssk, _) = self.stringify_as_ssk()?;
111        let msg = ssk.as_bytes();
112        let s = bytes_encode(&signer.sign_msg(msg, SIGNING_CONTEXT)?.to_bytes())?;
113        match self {
114            Identity::User {
115                ref mut signature, ..
116            } => *signature = s,
117            Identity::ApiKey {
118                ref mut signature, ..
119            } => *signature = s,
120        }
121        Ok(())
122    }
123
124    pub fn verify(&self) -> anyhow::Result<()> {
125        let verifier = Signer::verifier_from_bytes(&match self {
126            Identity::User { key, .. } => bytes_decode(key)?,
127            Identity::ApiKey { key, .. } => bytes_decode(key)?,
128        })?;
129        let (ssk, signature) = self.stringify_as_ssk()?;
130        let signature = Signer::signature_from_bytes(&bytes_decode(signature)?)?;
131        verifier
132            .verify_msg(&ssk.as_bytes(), SIGNING_CONTEXT, &signature)
133            .or_else(|_| {
134                let bytes_start = b"<Bytes>";
135                let bytes_end = b"</Bytes>";
136                let wrapped_bytes =
137                    [&bytes_start[..], &ssk.as_bytes()[..], &bytes_end[..]].concat();
138                verifier.verify_msg(&wrapped_bytes, SIGNING_CONTEXT, &signature)
139            })
140    }
141
142    pub fn origin(&self) -> &str {
143        match self {
144            Identity::User { origins, .. } | Identity::ApiKey { origins, .. } => &origins,
145        }
146    }
147}