1use super::ldap::{LdapBoundToken, LdapSession};
2use crate::idm::account::Account;
3use crate::idm::event::LdapApplicationAuthEvent;
4use crate::idm::server::{IdmServerAuthTransaction, IdmServerTransaction};
5use crate::prelude::*;
6use concread::cowcell::*;
7use hashbrown::HashMap;
8use kanidm_proto::internal::OperationError;
9use std::sync::Arc;
10use uuid::Uuid;
11
12#[derive(Clone)]
13pub(crate) struct Application {
14 pub uuid: Uuid,
15 pub name: String,
16 pub linked_group: Uuid,
17}
18
19impl Application {
20 #[cfg(test)]
21 pub(crate) fn try_from_entry_ro(
22 value: &Entry<EntrySealed, EntryCommitted>,
23 _qs: &mut QueryServerReadTransaction,
24 ) -> Result<Self, OperationError> {
25 if !value.attribute_equality(Attribute::Class, &EntryClass::Application.to_partialvalue()) {
26 return Err(OperationError::MissingClass(ENTRYCLASS_APPLICATION.into()));
27 }
28
29 let uuid = value.get_uuid();
30
31 let name = value
32 .get_ava_single_iname(Attribute::Name)
33 .map(|s| s.to_string())
34 .ok_or_else(|| OperationError::MissingAttribute(Attribute::Name))?;
35
36 let linked_group = value
37 .get_ava_single_refer(Attribute::LinkedGroup)
38 .ok_or_else(|| OperationError::MissingAttribute(Attribute::LinkedGroup))?;
39
40 Ok(Application {
41 name,
42 uuid,
43 linked_group,
44 })
45 }
46}
47
48#[derive(Clone)]
49struct LdapApplicationsInner {
50 set: HashMap<String, Application>,
51}
52
53pub struct LdapApplications {
54 inner: CowCell<LdapApplicationsInner>,
55}
56
57pub struct LdapApplicationsReadTransaction {
58 inner: CowCellReadTxn<LdapApplicationsInner>,
59}
60
61pub struct LdapApplicationsWriteTransaction<'a> {
62 inner: CowCellWriteTxn<'a, LdapApplicationsInner>,
63}
64
65impl LdapApplicationsWriteTransaction<'_> {
66 pub fn reload(&mut self, value: Vec<Arc<EntrySealedCommitted>>) -> Result<(), OperationError> {
67 let app_set: Result<HashMap<_, _>, _> = value
68 .into_iter()
69 .map(|ent| {
70 if !ent.attribute_equality(Attribute::Class, &EntryClass::Application.into()) {
71 error!("Missing class application");
72 return Err(OperationError::InvalidEntryState);
73 }
74
75 let uuid = ent.get_uuid();
76 let name = ent
77 .get_ava_single_iname(Attribute::Name)
78 .map(str::to_string)
79 .ok_or(OperationError::InvalidValueState)?;
80
81 let linked_group = ent
82 .get_ava_single_refer(Attribute::LinkedGroup)
83 .ok_or(OperationError::InvalidValueState)?;
84
85 let app = Application {
86 uuid,
87 name: name.clone(),
88 linked_group,
89 };
90
91 Ok((name, app))
92 })
93 .collect();
94
95 let new_inner = LdapApplicationsInner { set: app_set? };
96 self.inner.replace(new_inner);
97
98 Ok(())
99 }
100
101 pub fn commit(self) {
102 self.inner.commit();
103 }
104}
105
106impl LdapApplications {
107 pub fn read(&self) -> LdapApplicationsReadTransaction {
108 LdapApplicationsReadTransaction {
109 inner: self.inner.read(),
110 }
111 }
112
113 pub fn write(&self) -> LdapApplicationsWriteTransaction {
114 LdapApplicationsWriteTransaction {
115 inner: self.inner.write(),
116 }
117 }
118}
119
120impl TryFrom<Vec<Arc<EntrySealedCommitted>>> for LdapApplications {
121 type Error = OperationError;
122
123 fn try_from(value: Vec<Arc<EntrySealedCommitted>>) -> Result<Self, Self::Error> {
124 let apps = LdapApplications {
125 inner: CowCell::new(LdapApplicationsInner {
126 set: HashMap::new(),
127 }),
128 };
129
130 let mut apps_wr = apps.write();
131 apps_wr.reload(value)?;
132 apps_wr.commit();
133 Ok(apps)
134 }
135}
136
137impl IdmServerAuthTransaction<'_> {
138 pub async fn application_auth_ldap(
139 &mut self,
140 lae: &LdapApplicationAuthEvent,
141 ct: Duration,
142 ) -> Result<Option<LdapBoundToken>, OperationError> {
143 let usr_entry = self.get_qs_txn().internal_search_uuid(lae.target)?;
144
145 let account: Account =
146 Account::try_from_entry_ro(&usr_entry, &mut self.qs_read).map_err(|e| {
147 error!("Failed to search account {:?}", e);
148 e
149 })?;
150
151 if account.is_anonymous() {
152 return Err(OperationError::InvalidUuid);
153 }
154
155 if !account.is_within_valid_time(ct) {
156 security_info!("Account has expired or is not yet valid, not allowing to proceed");
157 return Err(OperationError::SessionExpired);
158 }
159
160 let application = self
161 .applications
162 .inner
163 .set
164 .get(&lae.application)
165 .ok_or_else(|| {
166 info!("Application {:?} not found", lae.application);
167 OperationError::NoMatchingEntries
168 })?;
169
170 let is_memberof = usr_entry
172 .get_ava_refer(Attribute::MemberOf)
173 .map(|member_of_set| member_of_set.contains(&application.linked_group))
174 .unwrap_or_default();
175
176 if !is_memberof {
177 debug!(
178 "User {:?} not member of application {}:{:?} linked group {:?}",
179 account.uuid, application.name, application.uuid, application.linked_group,
180 );
181 return Ok(None);
182 }
183
184 match account.verify_application_password(application, lae.cleartext.as_str())? {
185 Some(_) => {
186 let session_id = Uuid::new_v4();
187 security_info!(
188 "Starting session {} for {} {} with application {}:{:?}",
189 session_id,
190 account.spn,
191 account.uuid,
192 application.name,
193 application.uuid,
194 );
195
196 Ok(Some(LdapBoundToken {
197 spn: account.spn,
198 session_id,
199 effective_session: LdapSession::UnixBind(account.uuid),
200 }))
201 }
202 None => {
203 security_info!("Account does not have a configured application password.");
204 Ok(None)
205 }
206 }
207 }
208}
209
210#[derive(Debug)]
211pub struct GenerateApplicationPasswordEvent {
212 pub ident: Identity,
213 pub target: Uuid,
214 pub application: Uuid,
215 pub label: String,
216}
217
218impl GenerateApplicationPasswordEvent {
219 pub fn from_parts(
220 ident: Identity,
221 target: Uuid,
222 application: Uuid,
223 label: String,
224 ) -> Result<Self, OperationError> {
225 Ok(GenerateApplicationPasswordEvent {
226 ident,
227 target,
228 application,
229 label,
230 })
231 }
232
233 pub fn new_internal(target: Uuid, application: Uuid, label: String) -> Self {
234 GenerateApplicationPasswordEvent {
235 ident: Identity::from_internal(),
236 target,
237 application,
238 label,
239 }
240 }
241}
242
243#[cfg(test)]
244mod tests {
245 use crate::event::CreateEvent;
246 use crate::idm::account::Account;
247 use crate::idm::application::Application;
248 use crate::idm::application::GenerateApplicationPasswordEvent;
249 use crate::idm::server::IdmServerTransaction;
250 use crate::idm::serviceaccount::{DestroyApiTokenEvent, GenerateApiTokenEvent};
251 use crate::prelude::*;
252 use compact_jwt::{dangernoverify::JwsDangerReleaseWithoutVerify, JwsVerifier};
253 use kanidm_proto::internal::ApiToken as ProtoApiToken;
254 use std::time::Duration;
255
256 const TEST_CURRENT_TIME: u64 = 6000;
257
258 #[idm_test]
261 async fn test_idm_application_excludes(idms: &IdmServer, _idms_delayed: &mut IdmServerDelayed) {
262 let ct = Duration::from_secs(TEST_CURRENT_TIME);
263 let mut idms_prox_write = idms.proxy_write(ct).await.unwrap();
264
265 let test_grp_name = "testgroup1";
267 let test_grp_uuid = Uuid::new_v4();
268 let e1 = entry_init!(
269 (Attribute::Class, EntryClass::Object.to_value()),
270 (Attribute::Class, EntryClass::Group.to_value()),
271 (Attribute::Name, Value::new_iname(test_grp_name)),
272 (Attribute::Uuid, Value::Uuid(test_grp_uuid))
273 );
274 let test_entry_uuid = Uuid::new_v4();
275 let e2 = entry_init!(
276 (Attribute::Class, EntryClass::Object.to_value()),
277 (Attribute::Class, EntryClass::Account.to_value()),
278 (Attribute::Class, EntryClass::ServiceAccount.to_value()),
279 (Attribute::Class, EntryClass::Application.to_value()),
280 (Attribute::Class, EntryClass::Person.to_value()),
281 (Attribute::Name, Value::new_iname("test_app_name")),
282 (Attribute::Uuid, Value::Uuid(test_entry_uuid)),
283 (Attribute::Description, Value::new_utf8s("test_app_desc")),
284 (
285 Attribute::DisplayName,
286 Value::new_utf8s("test_app_dispname")
287 ),
288 (Attribute::LinkedGroup, Value::Refer(test_grp_uuid))
289 );
290 let ce = CreateEvent::new_internal(vec![e1, e2]);
291 let cr = idms_prox_write.qs_write.create(&ce);
292 assert!(cr.is_err());
293
294 let test_grp_name = "testgroup1";
296 let test_grp_uuid = Uuid::new_v4();
297 let e1 = entry_init!(
298 (Attribute::Class, EntryClass::Object.to_value()),
299 (Attribute::Class, EntryClass::Group.to_value()),
300 (Attribute::Name, Value::new_iname(test_grp_name)),
301 (Attribute::Uuid, Value::Uuid(test_grp_uuid))
302 );
303 let test_entry_uuid = Uuid::new_v4();
304 let e2 = entry_init!(
305 (Attribute::Class, EntryClass::Object.to_value()),
306 (Attribute::Class, EntryClass::Account.to_value()),
307 (Attribute::Class, EntryClass::Application.to_value()),
308 (Attribute::Class, EntryClass::Person.to_value()),
309 (Attribute::Name, Value::new_iname("test_app_name")),
310 (Attribute::Uuid, Value::Uuid(test_entry_uuid)),
311 (Attribute::Description, Value::new_utf8s("test_app_desc")),
312 (
313 Attribute::DisplayName,
314 Value::new_utf8s("test_app_dispname")
315 ),
316 (Attribute::LinkedGroup, Value::Refer(test_grp_uuid))
317 );
318 let ce = CreateEvent::new_internal(vec![e1, e2]);
319 let cr = idms_prox_write.qs_write.create(&ce);
320 assert!(cr.is_err());
321
322 let test_grp_name = "testgroup1";
324 let test_grp_uuid = Uuid::new_v4();
325 let e1 = entry_init!(
326 (Attribute::Class, EntryClass::Object.to_value()),
327 (Attribute::Class, EntryClass::Group.to_value()),
328 (Attribute::Name, Value::new_iname(test_grp_name)),
329 (Attribute::Uuid, Value::Uuid(test_grp_uuid))
330 );
331 let test_entry_uuid = Uuid::new_v4();
332 let e2 = entry_init!(
333 (Attribute::Class, EntryClass::Object.to_value()),
334 (Attribute::Class, EntryClass::Account.to_value()),
335 (Attribute::Class, EntryClass::Application.to_value()),
336 (Attribute::Name, Value::new_iname("test_app_name")),
337 (Attribute::Uuid, Value::Uuid(test_entry_uuid)),
338 (Attribute::Description, Value::new_utf8s("test_app_desc")),
339 (Attribute::LinkedGroup, Value::Refer(test_grp_uuid))
340 );
341 let ce = CreateEvent::new_internal(vec![e1, e2]);
342 let cr = idms_prox_write.qs_write.create(&ce);
343 assert!(cr.is_err());
344
345 let test_grp_name = "testgroup1";
347 let test_grp_uuid = Uuid::new_v4();
348 let e1 = entry_init!(
349 (Attribute::Class, EntryClass::Object.to_value()),
350 (Attribute::Class, EntryClass::Group.to_value()),
351 (Attribute::Name, Value::new_iname(test_grp_name)),
352 (Attribute::Uuid, Value::Uuid(test_grp_uuid))
353 );
354 let test_entry_uuid = Uuid::new_v4();
355 let e2 = entry_init!(
356 (Attribute::Class, EntryClass::Object.to_value()),
357 (Attribute::Class, EntryClass::Application.to_value()),
358 (Attribute::Name, Value::new_iname("test_app_name")),
359 (Attribute::Uuid, Value::Uuid(test_entry_uuid)),
360 (Attribute::Description, Value::new_utf8s("test_app_desc")),
361 (Attribute::LinkedGroup, Value::Refer(test_grp_uuid))
362 );
363 let ce = CreateEvent::new_internal(vec![e1, e2]);
364 let cr = idms_prox_write.qs_write.create(&ce);
365 assert!(cr.is_err());
366
367 let test_grp_name = "testgroup1";
369 let test_grp_uuid = Uuid::new_v4();
370 let e1 = entry_init!(
371 (Attribute::Class, EntryClass::Object.to_value()),
372 (Attribute::Class, EntryClass::Group.to_value()),
373 (Attribute::Name, Value::new_iname(test_grp_name)),
374 (Attribute::Uuid, Value::Uuid(test_grp_uuid))
375 );
376 let test_entry_uuid = Uuid::new_v4();
377 let e2 = entry_init!(
378 (Attribute::Class, EntryClass::Object.to_value()),
379 (Attribute::Class, EntryClass::Application.to_value()),
380 (Attribute::Class, EntryClass::ServiceAccount.to_value()),
381 (Attribute::Name, Value::new_iname("test_app_name")),
382 (Attribute::Uuid, Value::Uuid(test_entry_uuid)),
383 (Attribute::Description, Value::new_utf8s("test_app_desc")),
384 (Attribute::LinkedGroup, Value::Refer(test_grp_uuid))
385 );
386 let ce = CreateEvent::new_internal(vec![e1, e2]);
387 let cr = idms_prox_write.qs_write.create(&ce);
388 assert!(cr.is_ok());
389 }
390
391 #[idm_test]
393 async fn test_idm_application_no_linked_group(
394 idms: &IdmServer,
395 _idms_delayed: &mut IdmServerDelayed,
396 ) {
397 let ct = Duration::from_secs(TEST_CURRENT_TIME);
398 let mut idms_prox_write = idms.proxy_write(ct).await.unwrap();
399
400 let test_entry_uuid = Uuid::new_v4();
401
402 let e1 = entry_init!(
403 (Attribute::Class, EntryClass::Object.to_value()),
404 (Attribute::Class, EntryClass::Account.to_value()),
405 (Attribute::Class, EntryClass::ServiceAccount.to_value()),
406 (Attribute::Class, EntryClass::Application.to_value()),
407 (Attribute::Name, Value::new_iname("test_app_name")),
408 (Attribute::Uuid, Value::Uuid(test_entry_uuid)),
409 (Attribute::Description, Value::new_utf8s("test_app_desc")),
410 (
411 Attribute::DisplayName,
412 Value::new_utf8s("test_app_dispname")
413 )
414 );
415
416 let ce = CreateEvent::new_internal(vec![e1]);
417 let cr = idms_prox_write.qs_write.create(&ce);
418 assert!(cr.is_err());
419 }
420
421 #[idm_test]
423 async fn test_idm_application_linked_group(
424 idms: &IdmServer,
425 _idms_delayed: &mut IdmServerDelayed,
426 ) {
427 let test_entry_name = "test_app_name";
428 let test_entry_uuid = Uuid::new_v4();
429 let test_grp_name = "testgroup1";
430 let test_grp_uuid = Uuid::new_v4();
431
432 {
433 let mut idms_prox_write = idms.proxy_write(duration_from_epoch_now()).await.unwrap();
434
435 let e1 = entry_init!(
436 (Attribute::Class, EntryClass::Object.to_value()),
437 (Attribute::Class, EntryClass::Group.to_value()),
438 (Attribute::Name, Value::new_iname(test_grp_name)),
439 (Attribute::Uuid, Value::Uuid(test_grp_uuid))
440 );
441
442 let e2 = entry_init!(
443 (Attribute::Class, EntryClass::Object.to_value()),
444 (Attribute::Class, EntryClass::Account.to_value()),
445 (Attribute::Class, EntryClass::ServiceAccount.to_value()),
446 (Attribute::Class, EntryClass::Application.to_value()),
447 (Attribute::Name, Value::new_iname(test_entry_name)),
448 (Attribute::Uuid, Value::Uuid(test_entry_uuid)),
449 (Attribute::Description, Value::new_utf8s("test_app_desc")),
450 (
451 Attribute::DisplayName,
452 Value::new_utf8s("test_app_dispname")
453 ),
454 (Attribute::LinkedGroup, Value::Refer(test_grp_uuid))
455 );
456
457 let ce = CreateEvent::new_internal(vec![e1, e2]);
458 let cr = idms_prox_write.qs_write.create(&ce);
459 assert!(cr.is_ok());
460
461 let cr = idms_prox_write.qs_write.commit();
462 assert!(cr.is_ok());
463 }
464
465 {
466 let mut idms_prox_read = idms.proxy_read().await.unwrap();
467 let app = idms_prox_read
468 .qs_read
469 .internal_search_uuid(test_entry_uuid)
470 .and_then(|entry| {
471 Application::try_from_entry_ro(&entry, &mut idms_prox_read.qs_read)
472 })
473 .map_err(|e| {
474 trace!("Error: {:?}", e);
475 e
476 });
477 assert!(app.is_ok());
478
479 let app = app.unwrap();
480 assert_eq!(app.name, "test_app_name");
481 assert_eq!(app.uuid, test_entry_uuid);
482 assert_eq!(app.linked_group, test_grp_uuid);
483 }
484
485 {
488 let de = DeleteEvent::new_internal_invalid(filter!(f_eq(
489 Attribute::Uuid,
490 PartialValue::Uuid(test_grp_uuid)
491 )));
492 let mut idms_proxy_write = idms.proxy_write(duration_from_epoch_now()).await.unwrap();
493 assert!(idms_proxy_write.qs_write.delete(&de).is_err());
494 }
495
496 {
497 let de = DeleteEvent::new_internal_invalid(filter!(f_eq(
498 Attribute::Uuid,
499 PartialValue::Uuid(test_entry_uuid)
500 )));
501 let mut idms_proxy_write = idms.proxy_write(duration_from_epoch_now()).await.unwrap();
502 assert!(idms_proxy_write.qs_write.delete(&de).is_ok());
503 assert!(idms_proxy_write.qs_write.commit().is_ok());
504 }
505
506 {
507 let de = DeleteEvent::new_internal_invalid(filter!(f_eq(
508 Attribute::Uuid,
509 PartialValue::Uuid(test_grp_uuid)
510 )));
511 let mut idms_proxy_write = idms.proxy_write(duration_from_epoch_now()).await.unwrap();
512 assert!(idms_proxy_write.qs_write.delete(&de).is_ok());
513 assert!(idms_proxy_write.qs_write.commit().is_ok());
514 }
515 }
516
517 #[idm_test]
518 async fn test_idm_application_delete(idms: &IdmServer, _idms_delayed: &mut IdmServerDelayed) {
519 let test_usr_name = "testuser1";
520 let test_usr_uuid = Uuid::new_v4();
521 let test_app_name = "testapp1";
522 let test_app_uuid = Uuid::new_v4();
523 let test_grp_name = "testgroup1";
524 let test_grp_uuid = Uuid::new_v4();
525
526 {
527 let ct = duration_from_epoch_now();
528 let mut idms_prox_write = idms.proxy_write(ct).await.unwrap();
529
530 let e1 = entry_init!(
531 (Attribute::Class, EntryClass::Object.to_value()),
532 (Attribute::Class, EntryClass::Account.to_value()),
533 (Attribute::Class, EntryClass::Person.to_value()),
534 (Attribute::Name, Value::new_iname(test_usr_name)),
535 (Attribute::Uuid, Value::Uuid(test_usr_uuid)),
536 (Attribute::Description, Value::new_utf8s(test_usr_name)),
537 (Attribute::DisplayName, Value::new_utf8s(test_usr_name))
538 );
539
540 let e2 = entry_init!(
541 (Attribute::Class, EntryClass::Object.to_value()),
542 (Attribute::Class, EntryClass::Group.to_value()),
543 (Attribute::Name, Value::new_iname(test_grp_name)),
544 (Attribute::Uuid, Value::Uuid(test_grp_uuid)),
545 (Attribute::Member, Value::Refer(test_usr_uuid))
546 );
547
548 let e3 = entry_init!(
549 (Attribute::Class, EntryClass::Object.to_value()),
550 (Attribute::Class, EntryClass::ServiceAccount.to_value()),
551 (Attribute::Class, EntryClass::Application.to_value()),
552 (Attribute::Name, Value::new_iname(test_app_name)),
553 (Attribute::Uuid, Value::Uuid(test_app_uuid)),
554 (Attribute::LinkedGroup, Value::Refer(test_grp_uuid))
555 );
556
557 let ce = CreateEvent::new_internal(vec![e1, e2, e3]);
558 let cr = idms_prox_write.qs_write.create(&ce);
559 assert!(cr.is_ok());
560
561 let ev = GenerateApplicationPasswordEvent {
562 ident: Identity::from_internal(),
563 target: test_usr_uuid,
564 application: test_app_uuid,
565 label: "label".to_string(),
566 };
567 idms_prox_write
568 .generate_application_password(&ev)
569 .expect("Failed to create application password");
570
571 let cr = idms_prox_write.qs_write.commit();
572 assert!(cr.is_ok());
573 }
574
575 {
576 let mut idms_prox_read = idms.proxy_read().await.unwrap();
577 let account = idms_prox_read
578 .qs_read
579 .internal_search_uuid(test_usr_uuid)
580 .and_then(|entry| Account::try_from_entry_ro(&entry, &mut idms_prox_read.qs_read))
581 .map_err(|e| {
582 trace!("Error: {:?}", e);
583 e
584 })
585 .expect("Failed to search for account");
586
587 assert!(account.apps_pwds.values().count() > 0);
588 }
589
590 {
592 let de = DeleteEvent::new_internal_invalid(filter!(f_eq(
593 Attribute::Uuid,
594 PartialValue::Uuid(test_app_uuid)
595 )));
596 let mut idms_proxy_write = idms.proxy_write(duration_from_epoch_now()).await.unwrap();
597 assert!(idms_proxy_write.qs_write.delete(&de).is_ok());
598 assert!(idms_proxy_write.qs_write.commit().is_ok());
599 }
600
601 {
602 let mut idms_prox_read = idms.proxy_read().await.unwrap();
603 assert!(idms_prox_read
604 .qs_read
605 .internal_search_uuid(test_app_uuid)
606 .is_err());
607 }
608
609 {
610 let mut idms_prox_read = idms.proxy_read().await.unwrap();
611 let account = idms_prox_read
612 .qs_read
613 .internal_search_uuid(test_usr_uuid)
614 .and_then(|entry| Account::try_from_entry_ro(&entry, &mut idms_prox_read.qs_read))
615 .map_err(|e| {
616 trace!("Error: {:?}", e);
617 e
618 })
619 .expect("Failed to search for account");
620
621 assert_eq!(account.apps_pwds.values().count(), 0);
622 }
623 }
624
625 #[idm_test]
627 async fn test_idm_application_api_token(
628 idms: &IdmServer,
629 _idms_delayed: &mut IdmServerDelayed,
630 ) {
631 let ct = Duration::from_secs(TEST_CURRENT_TIME);
632 let past_grc = Duration::from_secs(TEST_CURRENT_TIME + 1) + AUTH_TOKEN_GRACE_WINDOW;
633 let exp = Duration::from_secs(TEST_CURRENT_TIME + 6000);
634 let post_exp = Duration::from_secs(TEST_CURRENT_TIME + 6010);
635 let mut idms_prox_write = idms.proxy_write(ct).await.unwrap();
636
637 let test_entry_uuid = Uuid::new_v4();
638 let test_group_uuid = Uuid::new_v4();
639
640 let e1 = entry_init!(
641 (Attribute::Class, EntryClass::Object.to_value()),
642 (Attribute::Class, EntryClass::Group.to_value()),
643 (Attribute::Name, Value::new_iname("test_group")),
644 (Attribute::Uuid, Value::Uuid(test_group_uuid))
645 );
646
647 let e2 = entry_init!(
648 (Attribute::Class, EntryClass::Object.to_value()),
649 (Attribute::Class, EntryClass::ServiceAccount.to_value()),
650 (Attribute::Class, EntryClass::Application.to_value()),
651 (Attribute::Name, Value::new_iname("test_app_name")),
652 (Attribute::Uuid, Value::Uuid(test_entry_uuid)),
653 (Attribute::Description, Value::new_utf8s("test_app_desc")),
654 (Attribute::LinkedGroup, Value::Refer(test_group_uuid))
655 );
656
657 let ce = CreateEvent::new_internal(vec![e1, e2]);
658 let cr = idms_prox_write.qs_write.create(&ce);
659 assert!(cr.is_ok());
660
661 let gte = GenerateApiTokenEvent::new_internal(test_entry_uuid, "TestToken", Some(exp));
662
663 let api_token = idms_prox_write
664 .service_account_generate_api_token(>e, ct)
665 .expect("failed to generate new api token");
666
667 trace!(?api_token);
668
669 let jws_verifier = JwsDangerReleaseWithoutVerify::default();
671
672 let apitoken_inner = jws_verifier
673 .verify(&api_token)
674 .unwrap()
675 .from_json::<ProtoApiToken>()
676 .unwrap();
677
678 let ident = idms_prox_write
679 .validate_client_auth_info_to_ident(api_token.clone().into(), ct)
680 .expect("Unable to verify api token.");
681
682 assert_eq!(ident.get_uuid(), Some(test_entry_uuid));
683
684 assert!(
686 idms_prox_write
687 .validate_client_auth_info_to_ident(api_token.clone().into(), post_exp)
688 .expect_err("Should not succeed")
689 == OperationError::SessionExpired
690 );
691
692 let dte =
694 DestroyApiTokenEvent::new_internal(apitoken_inner.account_id, apitoken_inner.token_id);
695 assert!(idms_prox_write
696 .service_account_destroy_api_token(&dte)
697 .is_ok());
698
699 let ident = idms_prox_write
702 .validate_client_auth_info_to_ident(api_token.clone().into(), ct)
703 .expect("Unable to verify api token.");
704 assert_eq!(ident.get_uuid(), Some(test_entry_uuid));
705
706 assert!(
708 idms_prox_write
709 .validate_client_auth_info_to_ident(api_token.clone().into(), past_grc)
710 .expect_err("Should not succeed")
711 == OperationError::SessionExpired
712 );
713
714 assert!(idms_prox_write.commit().is_ok());
715 }
716}