MCPcopy Create free account
hub / github.com/NodeDB-Lab/nodedb / role_inheritance_cycle_rejected

Function role_inheritance_cycle_rejected

nodedb/src/control/security/role.rs:574–625  ·  view source on GitHub ↗
()

Source from the content-addressed store, hash-verified

572 /// `RoleInheritanceCycle`.
573 #[test]
574 fn role_inheritance_cycle_rejected() {
575 let store = RoleStore::new();
576 store
577 .create_role("a", TenantId::new(1), None, None)
578 .unwrap();
579 store
580 .create_role("b", TenantId::new(1), Some("a"), None)
581 .unwrap();
582 store
583 .create_role("c", TenantId::new(1), Some("b"), None)
584 .unwrap();
585 // Now try to create a new role "d" with parent "c" that would make a→b→c→d and
586 // then try to make "a" a child of "c" (a cycle): create_role("a2", parent=c)
587 // won't cycle, but creating any role with parent chain looping back is what we test.
588 // Simulate by directly trying to insert a role whose parent chain leads back to itself.
589 // The simplest test: try to create role "loop" with parent "c" AND
590 // separately verify we can't create a role whose ancestor is itself.
591 // Direct cycle: create role "x" with parent "x" is caught by existence check first.
592 // Real test: A→B→C exists. Now drop C and re-create with parent A (not a cycle).
593 // The canonical test: A inherits B, B inherits C. Try to make C's parent = A.
594 // We can't mutate existing parents in this API, so test via prepare_role:
595 // a has no parent, b→a, c→b. Making a role "d" with parent "c" is fine.
596 // Cycle test: simulate A→B→C and try making A's chain go through C by
597 // adding role "cycle_root" with parent="c" where "cycle_root" is also
598 // somewhere above c — but since parents are immutable after creation,
599 // we use check_inheritance_chain directly.
600 //
601 // Real observable cycle via public API: create roles in reverse to force a
602 // cycle attempt at write time.
603 let store2 = RoleStore::new();
604 store2
605 .create_role("roleA", TenantId::new(1), None, None)
606 .unwrap();
607 store2
608 .create_role("roleB", TenantId::new(1), Some("roleA"), None)
609 .unwrap();
610 store2
611 .create_role("roleC", TenantId::new(1), Some("roleB"), None)
612 .unwrap();
613 // Now try to create "roleA_child" with parent "roleC" — no cycle.
614 store2
615 .create_role("roleA_child", TenantId::new(1), Some("roleC"), None)
616 .unwrap();
617 // Cycle: call check_inheritance_chain directly for roleA → roleC (roleA is ancestor).
618 let roles_guard = store2.roles.read().unwrap();
619 let err = check_inheritance_chain("roleA", "roleC", &roles_guard).unwrap_err();
620 drop(roles_guard);
621 assert!(
622 matches!(err, crate::Error::RoleInheritanceCycle { .. }),
623 "expected RoleInheritanceCycle, got: {err:?}"
624 );
625 }
626
627 /// `resolve_inheritance` must return at most `MAX_ROLE_INHERITANCE_DEPTH`
628 /// entries and never spin infinitely.

Callers

nothing calls this directly

Calls 3

check_inheritance_chainFunction · 0.85
create_roleMethod · 0.80
readMethod · 0.45

Tested by

no test coverage detected