Skip to main content

kanidm_client/
person.rs

1use crate::{ClientError, KanidmClient};
2use kanidm_proto::constants::*;
3use kanidm_proto::internal::{CredentialStatus, IdentifyUserRequest, IdentifyUserResponse};
4use kanidm_proto::v1::{AccountUnixExtend, Entry, SingleStringRequest, UatStatus};
5use std::collections::BTreeMap;
6use uuid::Uuid;
7
8impl KanidmClient {
9    pub async fn idm_person_account_list(&self) -> Result<Vec<Entry>, ClientError> {
10        self.perform_get_request("/v1/person").await
11    }
12
13    pub async fn idm_person_account_get(&self, id: &str) -> Result<Option<Entry>, ClientError> {
14        self.perform_get_request(format!("/v1/person/{id}").as_str())
15            .await
16    }
17
18    pub async fn idm_person_search(&self, id: &str) -> Result<Vec<Entry>, ClientError> {
19        self.perform_get_request(format!("/v1/person/_search/{id}").as_str())
20            .await
21    }
22
23    pub async fn idm_person_account_create(
24        &self,
25        name: &str,
26        displayname: &str,
27    ) -> Result<(), ClientError> {
28        let mut new_acct = Entry {
29            attrs: BTreeMap::new(),
30        };
31        new_acct
32            .attrs
33            .insert(ATTR_NAME.to_string(), vec![name.to_string()]);
34        new_acct
35            .attrs
36            .insert(ATTR_DISPLAYNAME.to_string(), vec![displayname.to_string()]);
37        self.perform_post_request("/v1/person", new_acct).await
38    }
39
40    pub async fn idm_person_account_update(
41        &self,
42        id: &str,
43        newname: Option<&str>,
44        displayname: Option<&str>,
45        legalname: Option<&str>,
46        mail: Option<&[String]>,
47    ) -> Result<(), ClientError> {
48        let mut update_entry = Entry {
49            attrs: BTreeMap::new(),
50        };
51
52        if let Some(newname) = newname {
53            update_entry
54                .attrs
55                .insert(ATTR_NAME.to_string(), vec![newname.to_string()]);
56        }
57        if let Some(newdisplayname) = displayname {
58            update_entry.attrs.insert(
59                ATTR_DISPLAYNAME.to_string(),
60                vec![newdisplayname.to_string()],
61            );
62        }
63        if let Some(newlegalname) = legalname {
64            // An empty string means we should purge the attribute, which
65            // is done by inserting an empty vec as the entry value
66            let val = if newlegalname.is_empty() {
67                vec![]
68            } else {
69                vec![newlegalname.to_string()]
70            };
71            update_entry.attrs.insert(ATTR_LEGALNAME.to_string(), val);
72        }
73        if let Some(mail) = mail {
74            update_entry
75                .attrs
76                .insert(ATTR_MAIL.to_string(), mail.to_vec());
77        }
78
79        self.perform_patch_request(format!("/v1/person/{id}").as_str(), update_entry)
80            .await
81    }
82
83    pub async fn idm_person_account_delete(&self, id: &str) -> Result<(), ClientError> {
84        self.perform_delete_request(format!("/v1/person/{id}").as_str())
85            .await
86    }
87
88    pub async fn idm_person_account_add_attr(
89        &self,
90        id: &str,
91        attr: &str,
92        values: &[&str],
93    ) -> Result<(), ClientError> {
94        let msg: Vec<_> = values.iter().map(|v| (*v).to_string()).collect();
95        self.perform_post_request(format!("/v1/person/{id}/_attr/{attr}").as_str(), msg)
96            .await
97    }
98
99    pub async fn idm_person_account_set_attr(
100        &self,
101        id: &str,
102        attr: &str,
103        values: &[&str],
104    ) -> Result<(), ClientError> {
105        let m: Vec<_> = values.iter().map(|v| (*v).to_string()).collect();
106        self.perform_put_request(format!("/v1/person/{id}/_attr/{attr}").as_str(), m)
107            .await
108    }
109
110    pub async fn idm_person_account_get_attr(
111        &self,
112        id: &str,
113        attr: &str,
114    ) -> Result<Option<Vec<String>>, ClientError> {
115        self.perform_get_request(format!("/v1/person/{id}/_attr/{attr}").as_str())
116            .await
117    }
118
119    pub async fn idm_person_account_purge_attr(
120        &self,
121        id: &str,
122        attr: &str,
123    ) -> Result<(), ClientError> {
124        self.perform_delete_request(format!("/v1/person/{id}/_attr/{attr}").as_str())
125            .await
126    }
127
128    pub async fn idm_person_account_get_credential_status(
129        &self,
130        id: &str,
131    ) -> Result<CredentialStatus, ClientError> {
132        let res: Result<CredentialStatus, ClientError> = self
133            .perform_get_request(format!("/v1/person/{id}/_credential/_status").as_str())
134            .await;
135        res.and_then(|cs| {
136            if cs.creds.is_empty() {
137                Err(ClientError::EmptyResponse)
138            } else {
139                Ok(cs)
140            }
141        })
142    }
143
144    // This helper calls through the credential update session wrappers to
145    pub async fn idm_person_account_primary_credential_set_password(
146        &self,
147        id: &str,
148        pw: &str,
149    ) -> Result<(), ClientError> {
150        let (session_tok, status) = self.idm_account_credential_update_begin(id).await?;
151        trace!(?status);
152
153        let status = self
154            .idm_account_credential_update_set_password(&session_tok, pw)
155            .await?;
156        trace!(?status);
157
158        self.idm_account_credential_update_commit(&session_tok)
159            .await
160    }
161
162    pub async fn idm_person_account_post_ssh_pubkey(
163        &self,
164        id: &str,
165        tag: &str,
166        pubkey: &str,
167    ) -> Result<(), ClientError> {
168        let tag = urlencoding::encode(tag);
169        let sk = (tag, pubkey.to_string());
170        self.perform_post_request(format!("/v1/person/{id}/_ssh_pubkeys").as_str(), sk)
171            .await
172    }
173
174    pub async fn idm_person_account_delete_ssh_pubkey(
175        &self,
176        id: &str,
177        tag: &str,
178    ) -> Result<(), ClientError> {
179        let tag = urlencoding::encode(tag);
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}