1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
use std::env::VarError;
use std::fmt::{Debug, Formatter};

use crate::identity::{
    ClientSecretCredential, ConfidentialClientApplication, PublicClientApplication,
    ResourceOwnerPasswordCredential,
};

const AZURE_TENANT_ID: &str = "AZURE_TENANT_ID";
const AZURE_CLIENT_ID: &str = "AZURE_CLIENT_ID";
const AZURE_CLIENT_SECRET: &str = "AZURE_CLIENT_SECRET";
const AZURE_USERNAME: &str = "AZURE_USERNAME";
const AZURE_PASSWORD: &str = "AZURE_PASSWORD";

#[derive(Clone)]
pub struct EnvironmentCredential;

impl Debug for EnvironmentCredential {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("EnvironmentCredential").finish()
    }
}

impl EnvironmentCredential {
    pub fn resource_owner_password_credential(
    ) -> Result<PublicClientApplication<ResourceOwnerPasswordCredential>, VarError> {
        match EnvironmentCredential::try_username_password_compile_time_env() {
            Ok(credential) => Ok(credential),
            Err(_) => EnvironmentCredential::try_username_password_runtime_env(),
        }
    }

    pub fn client_secret_credential(
    ) -> Result<ConfidentialClientApplication<ClientSecretCredential>, VarError> {
        match EnvironmentCredential::try_azure_client_secret_compile_time_env() {
            Ok(credential) => Ok(credential),
            Err(_) => EnvironmentCredential::try_azure_client_secret_runtime_env(),
        }
    }

    fn try_azure_client_secret_compile_time_env(
    ) -> Result<ConfidentialClientApplication<ClientSecretCredential>, VarError> {
        let tenant_id = option_env!("AZURE_TENANT_ID");
        let azure_client_id = option_env!("AZURE_CLIENT_ID").ok_or(VarError::NotPresent)?;
        let azure_client_secret = option_env!("AZURE_CLIENT_SECRET").ok_or(VarError::NotPresent)?;
        EnvironmentCredential::client_secret_env(
            tenant_id.map(|s| s.to_owned()),
            azure_client_id.to_owned(),
            azure_client_secret.to_owned(),
        )
    }

    fn try_azure_client_secret_runtime_env(
    ) -> Result<ConfidentialClientApplication<ClientSecretCredential>, VarError> {
        let tenant_id = std::env::var(AZURE_TENANT_ID).ok();
        let azure_client_id = std::env::var(AZURE_CLIENT_ID)?;
        let azure_client_secret = std::env::var(AZURE_CLIENT_SECRET)?;
        EnvironmentCredential::client_secret_env(tenant_id, azure_client_id, azure_client_secret)
    }

    fn client_secret_env(
        tenant_id: Option<String>,
        azure_client_id: String,
        azure_client_secret: String,
    ) -> Result<ConfidentialClientApplication<ClientSecretCredential>, VarError> {
        match tenant_id {
            Some(tenant_id) => Ok(ConfidentialClientApplication::credential(
                ClientSecretCredential::new_with_tenant(
                    tenant_id,
                    azure_client_id,
                    azure_client_secret,
                ),
            )),
            None => Ok(ConfidentialClientApplication::credential(
                ClientSecretCredential::new(azure_client_id, azure_client_secret),
            )),
        }
    }

    fn try_username_password_compile_time_env(
    ) -> Result<PublicClientApplication<ResourceOwnerPasswordCredential>, VarError> {
        let tenant_id = option_env!("AZURE_TENANT_ID");
        let azure_client_id = option_env!("AZURE_CLIENT_ID").ok_or(VarError::NotPresent)?;
        let azure_username = option_env!("AZURE_USERNAME").ok_or(VarError::NotPresent)?;
        let azure_password = option_env!("AZURE_PASSWORD").ok_or(VarError::NotPresent)?;
        Ok(EnvironmentCredential::username_password_env(
            tenant_id.map(|s| s.to_owned()),
            azure_client_id.to_owned(),
            azure_username.to_owned(),
            azure_password.to_owned(),
        ))
    }

    fn try_username_password_runtime_env(
    ) -> Result<PublicClientApplication<ResourceOwnerPasswordCredential>, VarError> {
        let tenant_id = std::env::var(AZURE_TENANT_ID).ok();
        let azure_client_id = std::env::var(AZURE_CLIENT_ID)?;
        let azure_username = std::env::var(AZURE_USERNAME)?;
        let azure_password = std::env::var(AZURE_PASSWORD)?;
        Ok(EnvironmentCredential::username_password_env(
            tenant_id,
            azure_client_id,
            azure_username,
            azure_password,
        ))
    }

    fn username_password_env(
        tenant_id: Option<String>,
        azure_client_id: String,
        azure_username: String,
        azure_password: String,
    ) -> PublicClientApplication<ResourceOwnerPasswordCredential> {
        match tenant_id {
            Some(tenant_id) => {
                PublicClientApplication::new(ResourceOwnerPasswordCredential::new_with_tenant(
                    tenant_id,
                    azure_client_id,
                    azure_username,
                    azure_password,
                ))
            }
            None => PublicClientApplication::new(ResourceOwnerPasswordCredential::new(
                azure_client_id,
                azure_username,
                azure_password,
            )),
        }
    }
}