1use std::{collections::HashMap, fmt::Display};
2
3use serde::{Deserialize, Serialize};
4
5use crate::{IsQueryArgs, Model, Permission, Role, utils::null_to_default};
6
7#[cfg_attr(feature = "salvo", derive(salvo::prelude::ToSchema))]
8#[derive(Serialize, Deserialize, Debug)]
9pub struct Userinfo {
10 sub: String,
11 iss: String,
12 aud: String,
13 #[serde(rename = "preferred_username", skip_serializing_if = "Option::is_none")]
14 name: Option<String>,
15 #[serde(rename = "name", skip_serializing_if = "Option::is_none")]
16 display_name: Option<String>,
17 #[serde(skip_serializing_if = "Option::is_none")]
18 email: Option<String>,
19 #[serde(rename = "email_verified", skip_serializing_if = "Option::is_none")]
20 email_verified: Option<bool>,
21 #[serde(rename = "picture", skip_serializing_if = "Option::is_none")]
22 avatar: Option<String>,
23 #[serde(skip_serializing_if = "Option::is_none")]
24 address: Option<String>,
25 #[serde(skip_serializing_if = "Option::is_none")]
26 phone: Option<String>,
27 #[serde(skip_serializing_if = "Option::is_none")]
28 groups: Option<Vec<String>>,
29 #[serde(skip_serializing_if = "Option::is_none")]
30 roles: Option<Vec<String>>,
31 #[serde(skip_serializing_if = "Option::is_none")]
32 permissions: Option<Vec<String>>,
33}
34
35#[cfg_attr(feature = "salvo", derive(salvo::prelude::ToSchema))]
37#[derive(Debug, Clone, Serialize, Deserialize, Default)]
38#[serde(rename_all = "camelCase", default)]
39pub struct User {
40 pub owner: String,
41 pub name: String,
42 pub created_time: String,
43 pub updated_time: String,
44 pub deleted_time: String,
45 pub id: String,
46 pub external_id: String,
47 pub r#type: String,
48 pub password: String,
49 pub password_salt: String,
50 pub password_type: String,
51 pub display_name: String,
52 pub first_name: String,
53 pub last_name: String,
54 pub avatar: String,
55 pub avatar_type: String,
56 pub permanent_avatar: String,
57 pub email: String,
58 pub email_verified: bool,
59 pub phone: String,
60 pub country_code: String,
61 pub region: String,
62 pub location: String,
63 #[serde(deserialize_with = "null_to_default")]
64 pub address: Vec<String>,
65 pub affiliation: String,
66 pub title: String,
67 pub id_card_type: String,
68 pub id_card: String,
69 pub homepage: String,
70 pub bio: String,
71 pub tag: String,
72 pub language: String,
73 pub gender: String,
74 pub birthday: String,
75 pub education: String,
76 pub score: i32,
77 pub karma: i32,
78 pub ranking: i32,
79 pub balance: f64,
80 pub currency: String,
81 pub is_default_avatar: bool,
82 pub is_online: bool,
83 pub is_admin: bool,
84 pub is_forbidden: bool,
85 pub is_deleted: bool,
86 pub signup_application: String,
87 pub hash: String,
88 pub pre_hash: String,
89 pub access_key: String,
90 pub access_secret: String,
91 pub access_token: String,
92 pub created_ip: String,
93 pub last_signin_time: String,
94 pub last_signin_ip: String,
95 pub github: String,
96 pub google: String,
97 pub qq: String,
98 pub wechat: String,
99 pub facebook: String,
100 pub dingtalk: String,
101 pub weibo: String,
102 pub gitee: String,
103 pub linkedin: String,
104 pub wecom: String,
105 pub lark: String,
106 pub gitlab: String,
107 pub adfs: String,
108 pub baidu: String,
109 pub alipay: String,
110 pub casdoor: String,
111 pub infoflow: String,
112 pub apple: String,
113 pub azuread: String,
114 pub azureadb2c: String,
115 pub slack: String,
116 pub steam: String,
117 pub bilibili: String,
118 pub okta: String,
119 pub douyin: String,
120 pub line: String,
121 pub amazon: String,
122 pub auth0: String,
123 pub battlenet: String,
124 pub bitbucket: String,
125 pub r#box: String,
126 pub cloudfoundry: String,
127 pub dailymotion: String,
128 pub deezer: String,
129 pub digitalocean: String,
130 pub discord: String,
131 pub dropbox: String,
132 pub eveonline: String,
133 pub fitbit: String,
134 pub gitea: String,
135 pub heroku: String,
136 pub influxcloud: String,
137 pub instagram: String,
138 pub intercom: String,
139 pub kakao: String,
140 pub lastfm: String,
141 pub mailru: String,
142 pub meetup: String,
143 pub microsoftonline: String,
144 pub naver: String,
145 pub nextcloud: String,
146 pub onedrive: String,
147 pub oura: String,
148 pub patreon: String,
149 pub paypal: String,
150 pub salesforce: String,
151 pub shopify: String,
152 pub soundcloud: String,
153 pub spotify: String,
154 pub strava: String,
155 pub stripe: String,
156 pub tiktok: String,
157 pub tumblr: String,
158 pub twitch: String,
159 pub twitter: String,
160 pub typetalk: String,
161 pub uber: String,
162 pub vk: String,
163 pub wepay: String,
164 pub xero: String,
165 pub yahoo: String,
166 pub yammer: String,
167 pub yandex: String,
168 pub zoom: String,
169 pub metamask: String,
170 pub web3onboard: String,
171 pub custom: String,
172 #[serde(deserialize_with = "null_to_default")]
173 pub webauthn_credentials: Vec<String>,
174 pub preferred_mfa_type: String,
175 #[serde(deserialize_with = "null_to_default")]
176 pub recovery_codes: Vec<String>,
177 pub totp_secret: String,
178 pub mfa_phone_enabled: bool,
179 pub mfa_email_enabled: bool,
180 #[serde(deserialize_with = "null_to_default")]
181 pub multi_factor_auths: Vec<MfaProps>,
182 pub invitation: String,
183 pub invitation_code: String,
184 #[serde(deserialize_with = "null_to_default")]
185 pub face_ids: Vec<FaceId>,
186 pub ldap: String,
187 pub properties: HashMap<String, String>,
188 #[serde(deserialize_with = "null_to_default")]
189 pub roles: Vec<Role>,
190 #[serde(deserialize_with = "null_to_default")]
191 pub permissions: Vec<Permission>,
192 #[serde(deserialize_with = "null_to_default")]
193 pub groups: Vec<String>,
194 pub last_signin_wrong_time: String,
195 pub signin_wrong_times: i32,
196 #[serde(deserialize_with = "null_to_default")]
197 pub managed_accounts: Vec<ManagedAccount>,
198 #[serde(deserialize_with = "null_to_default")]
199 pub mfa_accounts: Vec<MfaAccount>,
200 pub need_update_password: bool,
201 pub ip_whitelist: String,
202}
203
204#[cfg_attr(feature = "salvo", derive(salvo::prelude::ToSchema))]
205#[derive(Debug, Clone, Serialize, Deserialize, Default)]
206#[serde(rename_all = "camelCase", default)]
207pub struct FaceId {
208 pub name: String,
209 pub face_id_data: Vec<f64>,
210}
211
212#[cfg_attr(feature = "salvo", derive(salvo::prelude::ToSchema))]
213#[derive(Debug, Clone, Serialize, Deserialize, Default)]
214#[serde(rename_all = "camelCase", default)]
215pub struct MfaProps {
216 pub enabled: bool,
217 pub is_preferred: bool,
218 pub mfa_type: String,
219 pub secret: Option<String>,
220 pub country_code: Option<String>,
221 pub url: Option<String>,
222 pub recovery_codes: Option<Vec<String>>,
223}
224
225#[cfg_attr(feature = "salvo", derive(salvo::prelude::ToSchema))]
226#[derive(Debug, Clone, Serialize, Deserialize, Default)]
227#[serde(rename_all = "camelCase", default)]
228pub struct MfaAccount {
229 pub account_name: String,
230 pub issuer: String,
231 pub secret_key: String,
232}
233
234impl Model for User {
235 fn ident() -> &'static str {
236 "user"
237 }
238 fn plural_ident() -> &'static str {
239 "users"
240 }
241 fn owner(&self) -> &str {
242 &self.owner
243 }
244 fn name(&self) -> &str {
245 &self.name
246 }
247 fn support_update_columns() -> bool {
248 true
249 }
250}
251
252#[cfg_attr(feature = "salvo", derive(salvo::prelude::ToSchema))]
253#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
254#[serde(rename_all = "camelCase", default)]
255pub struct UserGroup {
256 owner: String,
257 name: String,
258 created_time: String,
259 updated_time: String,
260
261 display_name: String,
262 manager: String,
263 contact_email: String,
264 type_: String,
265 parent_id: String,
266 is_top_group: bool,
267 users: Vec<String>,
268
269 title: Option<String>,
270 key: Option<String>,
271 children: Option<Vec<UserGroup>>,
272
273 is_enabled: bool,
274}
275
276impl Model for UserGroup {
277 fn ident() -> &'static str {
278 "group"
279 }
280 fn plural_ident() -> &'static str {
281 "groups"
282 }
283 fn owner(&self) -> &str {
284 &self.owner
285 }
286 fn name(&self) -> &str {
287 &self.name
288 }
289 fn support_update_columns() -> bool {
290 false
291 }
292}
293
294#[cfg_attr(feature = "salvo", derive(salvo::prelude::ToSchema))]
295#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
296#[serde(rename_all = "camelCase", default)]
297pub struct ManagedAccount {
298 pub application: String,
299 pub password: String,
300 pub signin_url: String,
301 pub username: String,
302}
303
304#[cfg_attr(feature = "salvo", derive(salvo::prelude::ToSchema))]
305#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
306#[serde(rename_all = "camelCase", default)]
307pub struct MultiFactorAuth {
308 pub country_code: String,
309 pub enabled: bool,
310 pub is_preferred: bool,
311 pub mfa_type: String,
312 #[serde(deserialize_with = "null_to_default")]
313 pub recovery_codes: Vec<String>,
314 pub secret: String,
315 pub url: String,
316}
317
318#[cfg_attr(feature = "salvo", derive(salvo::prelude::ToSchema))]
319#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
320#[serde(rename_all = "camelCase", default)]
321pub struct WebauthnCredential {}
322
323#[cfg_attr(feature = "salvo", derive(salvo::prelude::ToSchema))]
325#[derive(Debug, Default, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
326#[serde(rename_all = "camelCase")]
327pub enum QueryUserSet {
328 Offline,
330 Online,
332 #[default]
334 #[serde(other)]
335 All,
336}
337impl Display for QueryUserSet {
338 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
339 match self {
340 QueryUserSet::Offline => write!(f, "0"),
341 QueryUserSet::Online => write!(f, "1"),
342 QueryUserSet::All => write!(f, ""),
343 }
344 }
345}
346
347#[cfg_attr(feature = "salvo", derive(salvo::prelude::ToParameters, salvo::prelude::ToSchema))]
348#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq, Eq)]
349pub struct UserQueryArgs {
350 #[cfg_attr(feature = "salvo", salvo(parameter(parameter_in=Query,required=false)))]
351 #[serde(rename = "groupName", skip_serializing_if = "Option::is_none")]
352 pub group_name: Option<String>,
353 #[cfg_attr(feature = "salvo", salvo(parameter(parameter_in=Query,required=false)))]
354 #[serde(rename = "pageSize", skip_serializing_if = "Option::is_none")]
355 pub page_size: Option<i32>,
356 #[cfg_attr(feature = "salvo", salvo(parameter(parameter_in=Query,required=false)))]
358 #[serde(rename = "p", skip_serializing_if = "Option::is_none")]
359 pub page: Option<i32>,
360 #[cfg_attr(feature = "salvo", salvo(parameter(parameter_in=Query,required=false)))]
361 #[serde(rename = "field", skip_serializing_if = "Option::is_none")]
362 pub field: Option<String>,
363 #[cfg_attr(feature = "salvo", salvo(parameter(parameter_in=Query,required=false)))]
364 #[serde(rename = "value", skip_serializing_if = "Option::is_none")]
365 pub value: Option<String>,
366 #[cfg_attr(feature = "salvo", salvo(parameter(parameter_in=Query,required=false)))]
367 #[serde(rename = "sortField", skip_serializing_if = "Option::is_none")]
368 pub sort_field: Option<String>,
369 #[cfg_attr(feature = "salvo", salvo(parameter(parameter_in=Query,required=false)))]
370 #[serde(rename = "sortOrder", skip_serializing_if = "Option::is_none")]
371 pub sort_order: Option<String>,
372}
373impl IsQueryArgs for UserQueryArgs {}
374
375#[cfg_attr(feature = "salvo", derive(salvo::prelude::ToParameters, salvo::prelude::ToSchema))]
376#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq, Eq)]
377#[serde(rename_all = "camelCase")]
378pub struct GetUserArgs {
379 #[cfg_attr(feature = "salvo", salvo(parameter(parameter_in=Query,required=false)))]
380 #[serde(skip_serializing_if = "Option::is_none")]
381 pub user_id: Option<String>,
382 #[cfg_attr(feature = "salvo", salvo(parameter(parameter_in=Query,required=false)))]
383 #[serde(skip_serializing_if = "Option::is_none")]
384 pub name: Option<String>,
385 #[cfg_attr(feature = "salvo", salvo(parameter(parameter_in=Query,required=false)))]
386 #[serde(skip_serializing_if = "Option::is_none")]
387 pub email: Option<String>,
388 #[cfg_attr(feature = "salvo", salvo(parameter(parameter_in=Query,required=false)))]
389 #[serde(skip_serializing_if = "Option::is_none")]
390 pub phone: Option<String>,
391}
392
393#[cfg_attr(feature = "salvo", derive(salvo::prelude::ToParameters, salvo::prelude::ToSchema))]
394#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq, Eq)]
395pub struct UserGroupQueryArgs {
396 #[cfg_attr(feature = "salvo", salvo(parameter(parameter_in=Query,required=false)))]
397 #[serde(rename = "withTree", skip_serializing_if = "Option::is_none")]
398 pub with_tree: Option<String>,
399 #[cfg_attr(feature = "salvo", salvo(parameter(parameter_in=Query,required=false)))]
400 #[serde(rename = "pageSize", skip_serializing_if = "Option::is_none")]
401 pub page_size: Option<i32>,
402 #[cfg_attr(feature = "salvo", salvo(parameter(parameter_in=Query,required=false)))]
404 #[serde(rename = "p", skip_serializing_if = "Option::is_none")]
405 pub page: Option<i32>,
406 #[cfg_attr(feature = "salvo", salvo(parameter(parameter_in=Query,required=false)))]
407 #[serde(rename = "field", skip_serializing_if = "Option::is_none")]
408 pub field: Option<String>,
409 #[cfg_attr(feature = "salvo", salvo(parameter(parameter_in=Query,required=false)))]
410 #[serde(rename = "value", skip_serializing_if = "Option::is_none")]
411 pub value: Option<String>,
412 #[cfg_attr(feature = "salvo", salvo(parameter(parameter_in=Query,required=false)))]
413 #[serde(rename = "sortField", skip_serializing_if = "Option::is_none")]
414 pub sort_field: Option<String>,
415 #[cfg_attr(feature = "salvo", salvo(parameter(parameter_in=Query,required=false)))]
416 #[serde(rename = "sortOrder", skip_serializing_if = "Option::is_none")]
417 pub sort_order: Option<String>,
418}
419impl IsQueryArgs for UserGroupQueryArgs {}
420
421#[cfg_attr(feature = "salvo", derive(salvo::prelude::ToParameters, salvo::prelude::ToSchema))]
422#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq, Eq)]
423#[serde(rename_all = "camelCase")]
424pub struct SetPasswordArgs {
425 #[cfg_attr(feature = "salvo", salvo(parameter(parameter_in=Query)))]
426 pub user_name: String,
427 #[cfg_attr(feature = "salvo", salvo(parameter(parameter_in=Query)))]
428 pub new_password: String,
429 #[cfg_attr(feature = "salvo", salvo(parameter(parameter_in=Query,required=false)))]
430 #[serde(skip_serializing_if = "Option::is_none")]
431 pub old_password: Option<String>,
432 #[cfg_attr(feature = "salvo", salvo(parameter(parameter_in=Query,required=false)))]
433 #[serde(skip_serializing_if = "Option::is_none")]
434 pub user_owner: Option<String>,
435}
436
437#[cfg(test)]
438mod tests {
439 use super::*;
440 #[test]
441 fn test_user_query_args() {
442 let mut args = UserQueryArgs::default();
443 let query_part = serde_urlencoded::to_string(&args).unwrap();
444 assert_eq!("", query_part);
445
446 args.page = Some(0);
447 args.page_size = Some(1);
448 let query_part = serde_urlencoded::to_string(&args).unwrap();
449 assert_eq!("pageSize=1&p=0", query_part);
450 }
451 #[test]
452 fn test_user() {
453 let json_data = r#"
454 {
455 "owner": "example_owner",
456 "name": "example_name",
457 "createdTime": "2022-01-01T00:00:00Z",
458 "updatedTime": "2022-01-01T00:00:00Z",
459
460 "id": "example_id",
461 "type": "example_type",
462 "password": "example_password",
463 "passwordSalt": "example_salt",
464 "passwordType": "example_type",
465 "displayName": "Example User",
466 "firstName": "First",
467 "lastName": "Last",
468 "avatar": "example_avatar",
469 "avatarType": "example_type",
470 "permanentAvatar": "example_perm_avatar",
471 "email": "example@example.com",
472 "emailVerified": true,
473 "phone": "123456789",
474 "countryCode": "example_cc",
475 "region": "example_region",
476 "location": "Example Location",
477 "address": ["Example Address"],
478 "affiliation": "example_affiliation",
479 "title": "example_title",
480 "idCardType": "example_card_type",
481 "idCard": "example_card",
482 "homepage": "https://example.com",
483 "bio": "Example bio",
484 "tag": "example_tag",
485 "language": "en",
486 "gender": "M",
487 "birthday": "1990-01-01",
488 "education": "example_education",
489 "score": 100,
490 "karma": 10,
491 "ranking": 1,
492 "isDefaultAvatar": true,
493 "isOnline": true,
494 "isAdmin": true,
495 "isForbidden": false,
496 "isDeleted": false,
497 "signupApplication": "example_signup_app",
498 "hash": "example_hash",
499 "preHash": "example_pre_hash",
500
501 "github": "example_github",
502 "google": "example_google",
503 "qq": "example_qq",
504 "wechat": "example_wechat",
505 "facebook": "example_facebook",
506 "dingtalk": "example_dingtalk",
507 "weibo": "example_weibo",
508 "gitee": "example_gitee",
509 "linkedin": "example_linkedin",
510 "wecom": "example_wecom",
511 "lark": "example_lark",
512 "gitlab": "example_gitlab",
513 "ldap": "example_ldap",
514
515 "properties": {
516 "additionalProp1": "value1",
517 "additionalProp2": "value2",
518 "additionalProp3": "value3"
519 },
520 "groups": ["ExampleGroup"],
521 "lastSigninWrongTime": "2022-01-01T00:00:00Z",
522 "signinWrongTimes": 0
523 }
524 "#;
525
526 let casdoor_user: User = serde_json::from_str(json_data).expect("JSON parsing failed");
527 println!("{:?}", casdoor_user);
528 }
529}