duo_auth/
types.rs

1use serde::Deserialize;
2use serde_with::serde_as;
3use serde_with::NoneAsEmptyString;
4
5use super::request::Parameters;
6
7structstruck::strike! {
8    #[strikethrough[serde_as]]
9    #[strikethrough[derive(Debug, Deserialize)]]
10    #[serde(rename_all = "snake_case")]
11    #[serde(tag = "result")]
12    pub enum PreauthResponse {
13        Auth {
14            devices: Vec<pub struct Device {
15                pub capabilities: Option<Vec<pub enum DeviceCapability {
16                    #![derive(PartialEq, Eq, PartialOrd, Ord)]
17                    #![serde(rename_all = "snake_case")]
18
19                    Auto,
20                    Push,
21                    Sms,
22                    Phone,
23                    MobileOtp,
24                }>>,
25                pub device: String,
26                pub display_name: Option<String>,
27                #[serde_as(as = "NoneAsEmptyString")]
28                pub name: Option<String>,
29                #[serde_as(as = "NoneAsEmptyString")]
30                pub number: Option<String>,
31                pub sms_nextcode: Option<String>,
32                pub r#type: pub enum DeviceType {
33                    #![derive(PartialEq, Eq, PartialOrd, Ord)]
34                    #![serde(rename_all = "snake_case")]
35
36                    Phone,
37                    Token,
38                },
39
40            }>,
41        },
42        Enroll {
43            enroll_portal_url: String,
44        },
45        Allow,
46        Deny,
47    }
48}
49
50structstruck::strike! {
51    #[strikethrough[serde_as]]
52    #[strikethrough[derive(Deserialize, Debug)]]
53    pub struct AuthStatusResponse {
54        pub result: pub enum AuthResult {
55            #![serde(rename_all = "snake_case")]
56
57            Allow,
58            Deny,
59            Waiting,
60        },
61        pub status: pub enum AuthStatus {
62            #![serde(rename_all = "snake_case")]
63
64            Calling,
65            Answered,
66            Pushed,
67            PushFailed,
68            Timeout,
69            Fraud,
70            Allow,
71            Bypass,
72            Deny,
73            LockedOut,
74            Sent,
75        },
76        pub status_msg: String,
77        pub trusted_device_token: Option<String>,
78    }
79}
80
81impl AuthStatusResponse {
82    pub fn ready(&self) -> Option<bool> {
83        match self.result {
84            AuthResult::Allow => Some(true),
85            AuthResult::Deny => Some(false),
86            AuthResult::Waiting => None,
87        }
88    }
89}
90
91#[derive(Clone, Debug)]
92pub enum User {
93    UserId { id: String },
94    Username { username: String },
95}
96
97impl User {
98    pub(crate) fn apply(self, parameters: &mut Parameters) {
99        match self {
100            Self::UserId { id } => parameters.set("user_id", id),
101            Self::Username { username } => parameters.set("username", username),
102        };
103    }
104
105    pub fn user_id<S: Into<String>>(id: S) -> Self {
106        Self::UserId { id: id.into() }
107    }
108
109    pub fn username<S: Into<String>>(username: S) -> Self {
110        Self::Username {
111            username: username.into(),
112        }
113    }
114}
115
116#[derive(Clone, Debug)]
117pub struct PreauthRequest {
118    pub user: User,
119    pub ipaddr: Option<String>,
120    pub hostname: Option<String>,
121    pub trusted_device_token: Option<String>,
122}
123
124impl PreauthRequest {
125    pub fn new(user: User) -> Self {
126        Self {
127            user,
128            ipaddr: None,
129            hostname: None,
130            trusted_device_token: None,
131        }
132    }
133
134    pub(crate) fn apply(self, parameters: &mut Parameters) {
135        self.user.apply(parameters);
136        parameters.set_opt("ipaddr", self.ipaddr);
137        parameters.set_opt("hostname", self.hostname);
138        parameters.set_opt("trusted_device_token", self.trusted_device_token);
139    }
140}
141
142structstruck::strike! {
143    #[strikethrough[derive(Clone, Debug)]]
144    pub struct AuthRequest {
145        pub user: User,
146        pub factor: pub enum AuthRequestFactor {
147            Auto {
148                device: Option<String>,
149                r#type: Option<String>,
150                display_username: Option<String>,
151                push_info: Option<String>,
152            },
153            Push {
154                device: String,
155                r#type: Option<String>,
156                display_username: Option<String>,
157                push_info: Option<String>,
158            },
159            Passcode { passcode: String },
160            Phone { device: String },
161            Sms { device: String },
162        },
163        pub ipaddr: Option<String>,
164        pub hostname: Option<String>,
165    }
166}
167
168impl AuthRequest {
169    pub fn new(user: User, factor: AuthRequestFactor) -> Self {
170        Self {
171            user,
172            factor,
173            ipaddr: None,
174            hostname: None,
175        }
176    }
177
178    pub(crate) fn apply(self, parameters: &mut Parameters) {
179        self.user.apply(parameters);
180        self.factor.apply(parameters);
181        parameters.set_opt("ipaddr", self.ipaddr);
182        parameters.set_opt("hostname", self.hostname);
183    }
184}
185
186impl AuthRequestFactor {
187    pub fn auto() -> Self {
188        Self::Auto {
189            device: Some("auto".into()),
190            r#type: None,
191            display_username: None,
192            push_info: None,
193        }
194    }
195
196    pub(crate) fn apply(self, parameters: &mut Parameters) {
197        match self {
198            Self::Auto {
199                device,
200                r#type,
201                display_username,
202                push_info,
203            } => {
204                parameters.set("factor", "auto");
205                parameters.set_opt("device", device);
206                parameters.set_opt("type", r#type);
207                parameters.set_opt("display_username", display_username);
208                parameters.set_opt("push_info", push_info);
209            }
210            Self::Push {
211                device,
212                r#type,
213                display_username,
214                push_info,
215            } => {
216                parameters.set("factor", "push");
217                parameters.set("device", device);
218                parameters.set_opt("type", r#type);
219                parameters.set_opt("display_username", display_username);
220                parameters.set_opt("push_info", push_info);
221            }
222            Self::Passcode { passcode } => {
223                parameters.set("factor", "passcode");
224                parameters.set("passcode", passcode);
225            }
226            Self::Phone { device } => {
227                parameters.set("factor", "phone");
228                parameters.set("device", device);
229            }
230            Self::Sms { device } => {
231                parameters.set("factor", "sms");
232                parameters.set("device", device);
233            }
234        };
235    }
236}
237
238#[derive(Debug, Deserialize)]
239pub struct EnrollResponse {
240    pub activation_barcode: String,
241    pub activation_code: String,
242    pub expiration: u64,
243    pub user_id: String,
244    pub username: String,
245}
246
247#[derive(Debug, Deserialize)]
248#[serde(rename_all = "snake_case")]
249pub enum EnrollStatusResponse {
250    Success,
251    Invalid,
252    Waiting,
253}