kanidm_client/
person.rs

1use std::collections::BTreeMap;
2
3use kanidm_proto::constants::*;
4use kanidm_proto::internal::{CredentialStatus, IdentifyUserRequest, IdentifyUserResponse};
5use kanidm_proto::v1::{AccountUnixExtend, Entry, SingleStringRequest, UatStatus};
6use uuid::Uuid;
7
8use crate::{ClientError, KanidmClient};
9
10impl KanidmClient {
11    pub async fn idm_person_account_list(&self) -> Result<Vec<Entry>, ClientError> {
12        self.perform_get_request("/v1/person").await
13    }
14
15    pub async fn idm_person_account_get(&self, id: &str) -> Result<Option<Entry>, ClientError> {
16        self.perform_get_request(format!("/v1/person/{id}").as_str())
17            .await
18    }
19
20    pub async fn idm_person_search(&self, id: &str) -> Result<Vec<Entry>, ClientError> {
21        self.perform_get_request(format!("/v1/person/_search/{id}").as_str())
22            .await
23    }
24
25    pub async fn idm_person_account_create(
26        &self,
27        name: &str,
28        displayname: &str,
29    ) -> Result<(), ClientError> {
30        let mut new_acct = Entry {
31            attrs: BTreeMap::new(),
32        };
33        new_acct
34            .attrs
35            .insert(ATTR_NAME.to_string(), vec![name.to_string()]);
36        new_acct
37            .attrs
38            .insert(ATTR_DISPLAYNAME.to_string(), vec![displayname.to_string()]);
39        self.perform_post_request("/v1/person", new_acct).await
40    }
41
42    pub async fn idm_person_account_update(
43        &self,
44        id: &str,
45        newname: Option<&str>,
46        displayname: Option<&str>,
47        legalname: Option<&str>,
48        mail: Option<&[String]>,
49    ) -> Result<(), ClientError> {
50        let mut update_entry = Entry {
51            attrs: BTreeMap::new(),
52        };
53
54        if let Some(newname) = newname {
55            update_entry
56                .attrs
57                .insert(ATTR_NAME.to_string(), vec![newname.to_string()]);
58        }
59        if let Some(newdisplayname) = displayname {
60            update_entry.attrs.insert(
61                ATTR_DISPLAYNAME.to_string(),
62                vec![newdisplayname.to_string()],
63            );
64        }
65        if let Some(newlegalname) = legalname {
66            // An empty string means we should purge the attribute, which
67            // is done by inserting an empty vec as the entry value
68            let val = if newlegalname.is_empty() {
69                vec![]
70            } else {
71                vec![newlegalname.to_string()]
72            };
73            update_entry.attrs.insert(ATTR_LEGALNAME.to_string(), val);
74        }
75        if let Some(mail) = mail {
76            update_entry
77                .attrs
78                .insert(ATTR_MAIL.to_string(), mail.to_vec());
79        }
80
81        self.perform_patch_request(format!("/v1/person/{id}").as_str(), update_entry)
82            .await
83    }
84
85    pub async fn idm_person_account_delete(&self, id: &str) -> Result<(), ClientError> {
86        self.perform_delete_request(format!("/v1/person/{id}").as_str())
87            .await
88    }
89
90    pub async fn idm_person_account_add_attr(
91        &self,
92        id: &str,
93        attr: &str,
94        values: &[&str],
95    ) -> Result<(), ClientError> {
96        let msg: Vec<_> = values.iter().map(|v| (*v).to_string()).collect();
97        self.perform_post_request(format!("/v1/person/{id}/_attr/{attr}").as_str(), msg)
98            .await
99    }
100
101    pub async fn idm_person_account_set_attr(
102        &self,
103        id: &str,
104        attr: &str,
105        values: &[&str],
106    ) -> Result<(), ClientError> {
107        let m: Vec<_> = values.iter().map(|v| (*v).to_string()).collect();
108        self.perform_put_request(format!("/v1/person/{id}/_attr/{attr}").as_str(), m)
109            .await
110    }
111
112    pub async fn idm_person_account_get_attr(
113        &self,
114        id: &str,
115        attr: &str,
116    ) -> Result<Option<Vec<String>>, ClientError> {
117        self.perform_get_request(format!("/v1/person/{id}/_attr/{attr}").as_str())
118            .await
119    }
120
121    pub async fn idm_person_account_purge_attr(
122        &self,
123        id: &str,
124        attr: &str,
125    ) -> Result<(), ClientError> {
126        self.perform_delete_request(format!("/v1/person/{id}/_attr/{attr}").as_str())
127            .await
128    }
129
130    pub async fn idm_person_account_get_credential_status(
131        &self,
132        id: &str,
133    ) -> Result<CredentialStatus, ClientError> {
134        let res: Result<CredentialStatus, ClientError> = self
135            .perform_get_request(format!("/v1/person/{id}/_credential/_status").as_str())
136            .await;
137        res.and_then(|cs| {
138            if cs.creds.is_empty() {
139                Err(ClientError::EmptyResponse)
140            } else {
141                Ok(cs)
142            }
143        })
144    }
145
146    // This helper calls through the credential update session wrappers to
147    pub async fn idm_person_account_primary_credential_set_password(
148        &self,
149        id: &str,
150        pw: &str,
151    ) -> Result<(), ClientError> {
152        let (session_tok, status) = self.idm_account_credential_update_begin(id).await?;
153        trace!(?status);
154
155        let status = self
156            .idm_account_credential_update_set_password(&session_tok, pw)
157            .await?;
158        trace!(?status);
159
160        self.idm_account_credential_update_commit(&session_tok)
161            .await
162    }
163
164    pub async fn idm_person_account_post_ssh_pubkey(
165        &self,
166        id: &str,
167        tag: &str,
168        pubkey: &str,
169    ) -> Result<(), ClientError> {
170        let sk = (tag.to_string(), pubkey.to_string());
171        self.perform_post_request(format!("/v1/person/{id}/_ssh_pubkeys").as_str(), sk)
172            .await
173    }
174
175    pub async fn idm_person_account_delete_ssh_pubkey(
176        &self,
177        id: &str,
178        tag: &str,
179    ) -> Result<(), ClientError> {
180        self.perform_delete_request(format!("/v1/person/{id}/_ssh_pubkeys/{tag}").as_str())
181            .await
182    }
183
184    pub async fn idm_person_account_unix_extend(
185        &self,
186        id: &str,
187        gidnumber: Option<u32>,
188        shell: Option<&str>,
189    ) -> Result<(), ClientError> {
190        let ux = AccountUnixExtend {
191            shell: shell.map(str::to_string),
192            gidnumber,
193        };
194        self.perform_post_request(format!("/v1/person/{id}/_unix").as_str(), ux)
195            .await
196    }
197
198    pub async fn idm_person_account_unix_cred_put(
199        &self,
200        id: &str,
201        cred: &str,
202    ) -> Result<(), ClientError> {
203        let req = SingleStringRequest {
204            value: cred.to_string(),
205        };
206        self.perform_put_request(
207            ["/v1/person/", id, "/_unix/_credential"].concat().as_str(),
208            req,
209        )
210        .await
211    }
212
213    pub async fn idm_person_account_unix_cred_delete(&self, id: &str) -> Result<(), ClientError> {
214        self.perform_delete_request(["/v1/person/", id, "/_unix/_credential"].concat().as_str())
215            .await
216    }
217
218    pub async fn idm_person_identify_user(
219        &self,
220        id: &str,
221        request: IdentifyUserRequest,
222    ) -> Result<IdentifyUserResponse, ClientError> {
223        self.perform_post_request(
224            ["/v1/person/", id, "/_identify_user"].concat().as_str(),
225            request,
226        )
227        .await
228    }
229
230    pub async fn idm_account_radius_credential_get(
231        &self,
232        id: &str,
233    ) -> Result<Option<String>, ClientError> {
234        self.perform_get_request(format!("/v1/person/{id}/_radius").as_str())
235            .await
236    }
237
238    pub async fn idm_account_radius_credential_regenerate(
239        &self,
240        id: &str,
241    ) -> Result<String, ClientError> {
242        self.perform_post_request(format!("/v1/person/{id}/_radius").as_str(), ())
243            .await
244    }
245
246    pub async fn idm_account_radius_credential_delete(&self, id: &str) -> Result<(), ClientError> {
247        self.perform_delete_request(format!("/v1/person/{id}/_radius").as_str())
248            .await
249    }
250
251    pub async fn idm_account_list_user_auth_token(
252        &self,
253        id: &str,
254    ) -> Result<Vec<UatStatus>, ClientError> {
255        self.perform_get_request(format!("/v1/account/{id}/_user_auth_token").as_str())
256            .await
257    }
258
259    pub async fn idm_account_destroy_user_auth_token(
260        &self,
261        id: &str,
262        token_id: Uuid,
263    ) -> Result<(), ClientError> {
264        self.perform_delete_request(
265            format!(
266                "/v1/account/{}/_user_auth_token/{}",
267                id,
268                &token_id.to_string()
269            )
270            .as_str(),
271        )
272        .await
273    }
274
275    pub async fn idm_person_certificate_list(&self, id: &str) -> Result<Vec<Entry>, ClientError> {
276        self.perform_get_request(format!("/v1/person/{id}/_certificate").as_str())
277            .await
278    }
279
280    pub async fn idm_person_certificate_create(
281        &self,
282        id: &str,
283        pem_data: &str,
284    ) -> Result<(), ClientError> {
285        let mut new_cert = Entry {
286            attrs: BTreeMap::new(),
287        };
288        new_cert
289            .attrs
290            .insert(ATTR_CERTIFICATE.to_string(), vec![pem_data.to_string()]);
291        self.perform_post_request(format!("/v1/person/{id}/_certificate").as_str(), new_cert)
292            .await
293    }
294}