posthog_cli/utils/
auth.rs1use super::homedir::{ensure_homedir_exists, posthog_home_dir};
2use anyhow::{Context, Error};
3use inquire::{validator::Validation, CustomUserError};
4use tracing::info;
5
6use serde::{Deserialize, Serialize};
7
8#[derive(Debug, Serialize, Deserialize)]
9pub struct Token {
10 pub token: String,
11 pub env_id: String,
12}
13
14pub trait CredentialProvider {
15 fn get_credentials(&self) -> Result<Token, Error>;
16 fn store_credentials(&self, token: Token) -> Result<(), Error>;
17 fn report_location(&self) -> String;
18}
19
20pub struct HomeDirProvider;
21
22impl CredentialProvider for HomeDirProvider {
23 fn get_credentials(&self) -> Result<Token, Error> {
24 let home = posthog_home_dir();
25 let file = home.join("credentials.json");
26 let token = std::fs::read_to_string(file.clone()).context(format!(
27 "While trying to read credentials from file {:?}",
28 file
29 ))?;
30 let token = serde_json::from_str(&token).context("While trying to parse token")?;
31 Ok(token)
32 }
33
34 fn store_credentials(&self, token: Token) -> Result<(), Error> {
35 let home = posthog_home_dir();
36 ensure_homedir_exists()?;
37 let file = home.join("credentials.json");
38 let token = serde_json::to_string(&token).context("While trying to serialize token")?;
39 std::fs::write(file.clone(), token).context(format!(
40 "While trying to write credentials to file {:?}",
41 file
42 ))?;
43 Ok(())
44 }
45
46 fn report_location(&self) -> String {
47 posthog_home_dir()
48 .join("credentials.json")
49 .to_string_lossy()
50 .to_string()
51 }
52}
53
54pub struct EnvVarProvider;
56
57impl CredentialProvider for EnvVarProvider {
58 fn get_credentials(&self) -> Result<Token, Error> {
59 let token = std::env::var("POSTHOG_CLI_TOKEN").context("While trying to read env var")?;
60 let env_id = std::env::var("POSTHOG_CLI_ENV_ID").context("While trying to read env var")?;
61 Ok(Token { token, env_id })
62 }
63
64 fn store_credentials(&self, _token: Token) -> Result<(), Error> {
65 Ok(())
66 }
67
68 fn report_location(&self) -> String {
69 unimplemented!("We should never try to save a credential to the env");
70 }
71}
72
73pub fn token_validator(token: &str) -> Result<Validation, CustomUserError> {
74 if token.is_empty() {
75 return Ok(Validation::Invalid("Token cannot be empty".into()));
76 };
77
78 if !token.starts_with("phx_") {
79 return Ok(Validation::Invalid(
80 "Token looks wrong, must start with 'phx_'".into(),
81 ));
82 }
83
84 Ok(Validation::Valid)
85}
86
87pub fn load_token() -> Result<Token, Error> {
88 let env = EnvVarProvider;
89 let env_err = match env.get_credentials() {
90 Ok(token) => {
91 info!("Using token from env var, for environment {}", token.env_id);
92 return Ok(token);
93 }
94 Err(e) => e,
95 };
96 let provider = HomeDirProvider;
97 let dir_err = match provider.get_credentials() {
98 Ok(token) => {
99 info!(
100 "Using token from: {}, for environment {}",
101 provider.report_location(),
102 token.env_id
103 );
104 return Ok(token);
105 }
106 Err(e) => e,
107 };
108
109 Err(
110 anyhow::anyhow!("Couldn't load credentials... Have you logged in recently?")
111 .context(env_err)
112 .context(dir_err),
113 )
114}