1use std::collections::BTreeMap;
2
3use kanidm_proto::constants::{ATTR_DISPLAYNAME, ATTR_ENTRY_MANAGED_BY, ATTR_MAIL, ATTR_NAME};
4use kanidm_proto::internal::{ApiToken, CredentialStatus};
5use kanidm_proto::v1::{AccountUnixExtend, ApiTokenGenerate, Entry};
6use time::OffsetDateTime;
7use uuid::Uuid;
8
9use crate::{ClientError, KanidmClient};
10
11impl KanidmClient {
12 pub async fn idm_service_account_list(&self) -> Result<Vec<Entry>, ClientError> {
13 self.perform_get_request("/v1/service_account").await
14 }
15
16 pub async fn idm_service_account_get(&self, id: &str) -> Result<Option<Entry>, ClientError> {
17 self.perform_get_request(format!("/v1/service_account/{}", id).as_str())
18 .await
19 }
20
21 pub async fn idm_service_account_create(
23 &self,
24 name: &str,
25 displayname: &str,
26 entry_managed_by: &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 new_acct.attrs.insert(
38 ATTR_ENTRY_MANAGED_BY.to_string(),
39 vec![entry_managed_by.to_string()],
40 );
41
42 self.perform_post_request("/v1/service_account", new_acct)
43 .await
44 }
45
46 pub async fn idm_service_account_delete(&self, id: &str) -> Result<(), ClientError> {
47 self.perform_delete_request(["/v1/service_account/", id].concat().as_str())
48 .await
49 }
50
51 pub async fn idm_service_account_update(
52 &self,
53 id: &str,
54 newname: Option<&str>,
55 displayname: Option<&str>,
56 entry_managed_by: Option<&str>,
57 mail: Option<&[String]>,
58 ) -> Result<(), ClientError> {
59 let mut update_entry = Entry {
60 attrs: BTreeMap::new(),
61 };
62
63 if let Some(newname) = newname {
64 update_entry
65 .attrs
66 .insert(ATTR_NAME.to_string(), vec![newname.to_string()]);
67 }
68
69 if let Some(newdisplayname) = displayname {
70 update_entry.attrs.insert(
71 ATTR_DISPLAYNAME.to_string(),
72 vec![newdisplayname.to_string()],
73 );
74 }
75
76 if let Some(entry_managed_by) = entry_managed_by {
77 update_entry.attrs.insert(
78 ATTR_ENTRY_MANAGED_BY.to_string(),
79 vec![entry_managed_by.to_string()],
80 );
81 }
82
83 if let Some(mail) = mail {
84 update_entry
85 .attrs
86 .insert(ATTR_MAIL.to_string(), mail.to_vec());
87 }
88
89 self.perform_patch_request(format!("/v1/service_account/{}", id).as_str(), update_entry)
90 .await
91 }
92
93 pub async fn idm_service_account_add_attr(
94 &self,
95 id: &str,
96 attr: &str,
97 values: &[&str],
98 ) -> Result<(), ClientError> {
99 let msg: Vec<_> = values.iter().map(|v| (*v).to_string()).collect();
100 self.perform_post_request(
101 format!("/v1/service_account/{}/_attr/{}", id, attr).as_str(),
102 msg,
103 )
104 .await
105 }
106
107 pub async fn idm_service_account_set_attr(
108 &self,
109 id: &str,
110 attr: &str,
111 values: &[&str],
112 ) -> Result<(), ClientError> {
113 let m: Vec<_> = values.iter().map(|v| (*v).to_string()).collect();
114 self.perform_put_request(
115 format!("/v1/service_account/{}/_attr/{}", id, attr).as_str(),
116 m,
117 )
118 .await
119 }
120
121 pub async fn idm_service_account_get_attr(
122 &self,
123 id: &str,
124 attr: &str,
125 ) -> Result<Option<Vec<String>>, ClientError> {
126 self.perform_get_request(format!("/v1/service_account/{}/_attr/{}", id, attr).as_str())
127 .await
128 }
129
130 pub async fn idm_service_account_purge_attr(
131 &self,
132 id: &str,
133 attr: &str,
134 ) -> Result<(), ClientError> {
135 self.perform_delete_request(format!("/v1/service_account/{}/_attr/{}", id, attr).as_str())
136 .await
137 }
138
139 pub async fn idm_service_account_post_ssh_pubkey(
140 &self,
141 id: &str,
142 tag: &str,
143 pubkey: &str,
144 ) -> Result<(), ClientError> {
145 let sk = (tag.to_string(), pubkey.to_string());
146 self.perform_post_request(
147 format!("/v1/service_account/{}/_ssh_pubkeys", id).as_str(),
148 sk,
149 )
150 .await
151 }
152
153 pub async fn idm_service_account_delete_ssh_pubkey(
154 &self,
155 id: &str,
156 tag: &str,
157 ) -> Result<(), ClientError> {
158 self.perform_delete_request(
159 format!("/v1/service_account/{}/_ssh_pubkeys/{}", id, tag).as_str(),
160 )
161 .await
162 }
163
164 pub async fn idm_service_account_unix_extend(
165 &self,
166 id: &str,
168 gidnumber: Option<u32>,
170 shell: Option<&str>,
172 ) -> Result<(), ClientError> {
173 let ux = AccountUnixExtend {
174 shell: shell.map(str::to_string),
175 gidnumber,
176 };
177 self.perform_post_request(format!("/v1/service_account/{}/_unix", id).as_str(), ux)
178 .await
179 }
180
181 pub async fn idm_service_account_into_person(&self, id: &str) -> Result<(), ClientError> {
183 self.perform_post_request(
184 format!("/v1/service_account/{}/_into_person", id).as_str(),
185 (),
186 )
187 .await
188 }
189
190 pub async fn idm_service_account_get_credential_status(
191 &self,
192 id: &str,
193 ) -> Result<CredentialStatus, ClientError> {
194 let res: Result<CredentialStatus, ClientError> = self
195 .perform_get_request(format!("/v1/service_account/{}/_credential/_status", id).as_str())
196 .await;
197 res.and_then(|cs| {
198 if cs.creds.is_empty() {
199 Err(ClientError::EmptyResponse)
200 } else {
201 Ok(cs)
202 }
203 })
204 }
205
206 pub async fn idm_service_account_generate_password(
207 &self,
208 id: &str,
209 ) -> Result<String, ClientError> {
210 let res: Result<String, ClientError> = self
211 .perform_get_request(
212 format!("/v1/service_account/{}/_credential/_generate", id).as_str(),
213 )
214 .await;
215 res.and_then(|pw| {
216 if pw.is_empty() {
217 Err(ClientError::EmptyResponse)
218 } else {
219 Ok(pw)
220 }
221 })
222 }
223
224 pub async fn idm_service_account_list_api_token(
225 &self,
226 id: &str,
227 ) -> Result<Vec<ApiToken>, ClientError> {
228 self.perform_get_request(format!("/v1/service_account/{}/_api_token", id).as_str())
230 .await
231 }
232
233 pub async fn idm_service_account_generate_api_token(
234 &self,
235 id: &str,
236 label: &str,
237 expiry: Option<OffsetDateTime>,
238 read_write: bool,
239 ) -> Result<String, ClientError> {
240 let new_token = ApiTokenGenerate {
241 label: label.to_string(),
242 expiry,
243 read_write,
244 };
245 self.perform_post_request(
246 format!("/v1/service_account/{}/_api_token", id).as_str(),
247 new_token,
248 )
249 .await
250 }
251
252 pub async fn idm_service_account_destroy_api_token(
253 &self,
254 id: &str,
255 token_id: Uuid,
256 ) -> Result<(), ClientError> {
257 self.perform_delete_request(
258 format!(
259 "/v1/service_account/{}/_api_token/{}",
260 id,
261 &token_id.to_string()
262 )
263 .as_str(),
264 )
265 .await
266 }
267}