Course outline

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.

About 4 min readContent reviewed
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.

python A fictional action and relationship matrix
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")
Expected output
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.

python Check an allowlisted workshop view
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")
Expected output
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.

Official references