Secure Software Development / Intermediate: implementation boundaries and verification / Lesson 10
Design trusted identity and permission interfaces
Keep identity establishment, resource scope, action policy, and response projection distinct.
On this page
Lesson goals
- Review which boundary supplies trusted subject facts.
- Test action and field permissions with small invented relationship matrices.
Establish subject context before trusting its attributes
A request may contain organizerId, tenant, and role fields, but those values do not establish who the caller is or what authority they hold. Use the application’s reviewed identity integration to obtain trusted subject context. Keep that integration separate from the policy function so tests can state exactly which boundary they exercise. A local matrix with invented subject dictionaries is a policy test, not token verification. Session lifecycle, authentication strength, and revocation depend on the chosen credential model and require their own authorized evidence rather than an improvised classroom credential implementation.
Express permission as a relationship and action
An organizer may view a workshop but lack permission to export attendee contacts or cancel another person’s registration. Test actions independently instead of assuming a broad role grants every operation. The first example requires an active subject, matching tenant, assignment, and the specific export action. Each denied case changes one decisive attribute. The predicate is intentionally fictional and small. A real design must also ensure that resource attributes come from trusted storage and that every route and background entry point invokes the appropriate policy before disclosing or changing protected data.
workshop = {"id":"w1", "tenant":"community-a", "organizers":{"o1"}}
def allowed(subject, item, action):
return subject["active"] is True and subject["tenant"] == item["tenant"] and subject["id"] in item["organizers"] and action == "export-contacts"
owner = {"id":"o1", "tenant":"community-a", "active":True}
assert allowed(owner, workshop, "export-contacts") is True
assert allowed({**owner, "tenant":"community-b"}, workshop, "export-contacts") is False
assert allowed({**owner, "active":False}, workshop, "export-contacts") is False
assert allowed(owner, workshop, "cancel-registration") is False
print("four action-policy cases checked")
four action-policy cases checked
The complete program evaluates trusted classroom dictionaries. It does not validate credentials or query assignments. Four cases check only this predicate; middleware coverage, storage scope, and lifecycle enforcement remain unverified.
Project only the authorized representation
Permission to view a workshop does not necessarily include attendee contacts or internal moderation notes. A response builder should expose the fields promised for that view, not serialize the entire stored object and hope callers ignore extra data. The second program creates a public projection with an allowlist and checks excluded fields. The fixture contains public classroom markers only. This projection does not perform resource authorization, so it must be used after the appropriate decision. Field filtering and resource scoping complement each other and cannot substitute for one another.
stored = {"id":"w1", "title":"Paper Gardens", "contactMarker":"lab-contact-only", "moderationNote":"lab-internal-only"}
public = {name: stored[name] for name in ("id", "title")}
assert set(public) == {"id", "title"}
assert "contactMarker" not in public
assert "moderationNote" not in public
print("public workshop fields checked")
public workshop fields checked
The complete local program verifies one public representation and uses no real contact data. It cannot establish every serializer path or prove that unauthorized callers are refused. Those are separate checks at the application boundary.
Revisit authority when relationships change
Assignments, roles, and tenant membership can change after an initial request. A background export or stored result needs a documented rule for whose authority applies and when it is rechecked. Caching can make a correct predicate operate on stale facts, so name the freshness assumption and supported revocation behavior. Do not promise instant effect unless the actual system supports and verifies it. Include reassignment and removed-subject cases in the approved integration plan. Local booleans establish neither cache invalidation nor session termination across distributed components.
Review enforcement paths and denied effects
For every sensitive operation, trace subject establishment, scoped resource loading, policy evaluation, mutation or projection, and response. Verify denial leaves entries, exports, jobs, and notifications unchanged where required. A list view should test exclusion as well as inclusion, because returning the correct workshop alongside another tenant’s workshop is still a failure. Code review can reveal a missing call, while an authorized component test checks the actual wiring. Keep the evidence sources distinct and avoid describing a handful of local relationship cases as comprehensive security verification.
Common errors and fixes
A client role field grants export access
- Why it happens
- The application trusts authority claims from the request.
- How to fix it
- Obtain subject attributes from the reviewed identity boundary.
- Prevent it next time
- Separate client input and trusted context in interfaces and fixtures.
Public view includes moderation notes
- Why it happens
- Whole-object serialization exceeds the view contract.
- How to fix it
- Build the documented field projection after authorization.
- Prevent it next time
- Test explicit absence of restricted properties.
A removed organizer keeps downloading stored exports
- Why it happens
- Result access uses stale or request-time-only authority.
- How to fix it
- Implement and verify the approved retrieval and freshness policy.
- Prevent it next time
- Include relationship changes in lifecycle cases.
Check your understanding
1. The local policy matrix passes. What must be checked before claiming the real export is protected?
Show worked solution
Verify trusted identity integration, scoped resource loading, actual route and job invocation, permitted field projection, and denial effects in the authorized environment. Record cache and lifecycle assumptions. The local predicate result remains useful but cannot establish those assembled boundaries.
2. A list contains the owner’s workshop and another tenant’s workshop. Why can an inclusion test pass incorrectly?
Show worked solution
It asks only whether one expected item is present. Require exact authorized membership, multiplicity, and relevant count, then check forbidden identifiers are absent. Use mixed-tenant synthetic fixtures and keep pagination semantics consistent with authorization scope.
Key takeaways
- Trusted identity and permission evaluation are separate boundaries.
- Action, resource, field, and lifecycle scope all matter.
- Policy models need actual enforcement evidence before deployment claims.