docs(idm): complete delegation lifecycle guidance
This commit is contained in:
@@ -35,6 +35,34 @@ authority must clear it, or whether only the authority can initiate and grant
|
||||
it. Recipient acceptance can be required or waived only by an explicit policy
|
||||
with recorded provenance.
|
||||
|
||||
## Delegation, Substitution, And Acting In Place
|
||||
|
||||
These modes are explicit assignment sources; ordinary group membership never
|
||||
creates them:
|
||||
|
||||
- A **delegated assignment** is a bounded substitution. For example, a registry
|
||||
lead may delegate the same delegable function to a deputy until Friday. The
|
||||
deputy acts as themself, and Access receives both the derived assignment ID
|
||||
and its source assignment ID.
|
||||
- An **acting-for assignment** is a bounded representation context. For example,
|
||||
an assistant may select an active acting context for the represented function
|
||||
holder. Access records the real account, the selected assignment, and the
|
||||
represented account on the session and in audit evidence. No acting-for
|
||||
authority is effective until that exact context is selected.
|
||||
- A **direct assignment** is the holder's own function fact and has no source
|
||||
assignment or represented account.
|
||||
|
||||
The Organizations function must permit the requested mode. The source and
|
||||
derived assignments must belong to the same tenant and function, and the
|
||||
source must be current and active. An actor also needs the assignment-write
|
||||
scope; where a governance profile is enabled, Policy must authorize the
|
||||
request/grant or an administrator must use the recorded emergency-override
|
||||
path. Validity windows make substitutions expire automatically. Deactivation
|
||||
revokes an assignment without deleting its provenance. IDM emits changed,
|
||||
revoked, and expired lifecycle events and writes the normal assignment audit
|
||||
record; effective-directory reads immediately exclude inactive, future,
|
||||
expired, or source-invalid derived assignments.
|
||||
|
||||
## Grant Profiles
|
||||
|
||||
The first policy profiles are:
|
||||
|
||||
@@ -475,7 +475,10 @@ manifest = ModuleManifest(
|
||||
"function and unit are owned by Organizations. Source distinguishes "
|
||||
"direct, delegated, acting-for, directory, governance, and system facts. "
|
||||
"Delegation and acting-for require a valid source assignment and the "
|
||||
"corresponding function permission. Subunit scope broadens the fact's "
|
||||
"corresponding Organizations function permission. A delegate acts as themself; "
|
||||
"acting-for additionally requires Access to select the exact representation "
|
||||
"context before it contributes authority. Source and derived assignments must "
|
||||
"remain current, active, tenant-local, and function-compatible. Subunit scope broadens the fact's "
|
||||
"organizational reach. Deactivation and expiry preserve provenance while "
|
||||
"removing the assignment from effective resolution. Governed request and "
|
||||
"grant decisions retain actor, policy, workflow revision, comments, and "
|
||||
|
||||
@@ -99,6 +99,26 @@ class AssignmentWorkflowTests(unittest.TestCase):
|
||||
),
|
||||
)
|
||||
|
||||
def test_delegated_assignment_rejects_function_that_forbids_delegation(self) -> None:
|
||||
base = assignment(id="source-1")
|
||||
item = assignment(
|
||||
id="assignment-2",
|
||||
identity_id="identity-2",
|
||||
account_id="account-2",
|
||||
source="delegated",
|
||||
delegated_from_assignment_id="source-1",
|
||||
)
|
||||
|
||||
self.assert_invalid(
|
||||
"This organization function does not allow delegation.",
|
||||
lambda: validate_assignment_source_rules( # type: ignore[arg-type]
|
||||
item,
|
||||
function=function(delegable=False),
|
||||
base=base,
|
||||
account_linked_to_identity=lambda _identity_id, _account_id: False,
|
||||
),
|
||||
)
|
||||
|
||||
def test_acting_for_assignment_accepts_identity_linked_account(self) -> None:
|
||||
base = assignment(id="source-1", identity_id="identity-1", account_id=None)
|
||||
item = assignment(
|
||||
@@ -136,6 +156,27 @@ class AssignmentWorkflowTests(unittest.TestCase):
|
||||
),
|
||||
)
|
||||
|
||||
def test_acting_for_assignment_rejects_function_that_forbids_representation(self) -> None:
|
||||
base = assignment(id="source-1")
|
||||
item = assignment(
|
||||
id="assignment-2",
|
||||
identity_id="identity-2",
|
||||
account_id="acting-account",
|
||||
source="acting_for",
|
||||
delegated_from_assignment_id="source-1",
|
||||
acting_for_account_id="account-1",
|
||||
)
|
||||
|
||||
self.assert_invalid(
|
||||
"This organization function does not allow acting in place.",
|
||||
lambda: validate_assignment_source_rules( # type: ignore[arg-type]
|
||||
item,
|
||||
function=function(act_in_place_allowed=False),
|
||||
base=base,
|
||||
account_linked_to_identity=lambda _identity_id, _account_id: False,
|
||||
),
|
||||
)
|
||||
|
||||
def test_update_plan_does_not_mutate_until_applied(self) -> None:
|
||||
item = assignment()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user