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 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 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}