A Supplier Login Must Not Unlock Every Equipment Quotation
An equipment portal checks that a user has the supplier role before returning a quotation. That sounds restrictive until a second supplier changes the quotation identifier in the URL and receives a competitor's document.
The missing decision is not whether the visitor is a supplier. It is whether that authenticated identity may perform this action on this specific quotation.
This is a hypothetical defensive design. Every identity and resource below is synthetic. It is not a description of Zenith Eclipse software or a reported security incident.
Define the document boundary first
A procurement workspace can contain a buyer's requirement, several supplier responses, technical review notes and a combined comparison. These need different audiences.
Zenith Eclipse's machinery and equipment sourcing process involves specifications, supplier offers and comparison. In a proposed portal supporting that kind of work, permission to see the shared equipment brief should not automatically grant access to competing commercial responses.
Define explicit actions: read brief, submit response, read own response, review comparison and download attachment. Avoid a single project-member flag that silently grants all of them.
OWASP's authorization guidance recommends denying by default and validating permission on every request. It also explains why object relationships matter beyond role membership. The policy below applies those principles to one narrow resource type.
Evaluate a resource-specific grant
Resolve principal identity from the authenticated session. Load resource ownership and grants from trusted server-side storage. Never accept these values from the request body.
def may_read_quote(principal, quote, grants):
if not principal["active"]:
return False
if principal["workspace"] != quote["workspace"]:
return False
key = (
principal["workspace"],
principal["id"],
quote["id"],
"read",
)
return key in grants
user = {"active": True, "workspace": "W1", "id": "supplier-A"}
quote = {"workspace": "W1", "id": "quote-A"}
grants = {("W1", "supplier-A", "quote-A", "read")}
assert may_read_quote(user, quote, grants)
assert not may_read_quote(
user, {"workspace": "W1", "id": "quote-B"}, grants
)
assert not may_read_quote(
user, {"workspace": "W2", "id": "quote-A"}, grants
)
assert not may_read_quote({**user, "active": False}, quote, grants)
This pure function is a policy example, not authentication middleware or a complete secure endpoint. The surrounding service must validate records, protect grant administration and enforce the result before returning any protected content.
Scope storage lookups by workspace and resource identity. A globally unique or hard-to-guess identifier is useful for addressing; it does not establish permission. Keep response behavior consistent enough that denied requests do not reveal useful details about another supplier's records.
Apply the same decision to derived content
Attachments, previews, search snippets and exports can leak the same information through different routes. Each needs authorization against the resource it represents.
A comparison workbook combines several offers. Do not authorize it using only the first quotation's grant. Define a separate comparison permission and generate only the fields the recipient is allowed to receive. Internal review notes may require their own field-level exclusion.
For background exports, retain the requesting principal and intended workspace. Recheck access when the job executes and before delivering the result. A successful check at enqueue time should not survive a revoked grant indefinitely.
Private object storage should remain private. If a deployment issues temporary download URLs, recognize that a previously issued URL may remain usable until expiry unless the serving mechanism supports revocation. Where immediate revocation is required, use an authorization-checking delivery path rather than promising it from a long-lived bearer link.
Make revocation a tested behavior
Decide how grant changes invalidate cached decisions. A cache keyed only by document ID can serve one supplier's permitted result to another. Include the security context and policy revision, or avoid caching sensitive authorization results until the invalidation contract is reliable.
Test two suppliers in the same workspace, identical-looking identifiers across workspaces, direct attachment access, a queued export after revocation and access to a comparison containing unauthorized offers. Assert that the response contains no protected bytes, not merely that the UI hides a button.
Audit grant changes and denied requests with bounded metadata; avoid copying quotation contents into logs. The practical goal is an explicit, explainable chain from authenticated identity to allowed action to exact resource. A supplier role is only one input to that decision.
About the publisher: Zenith Eclipse Co works across agricultural commodities, international logistics and machinery sourcing.
Comments
Join the discussionNo comments yet. Be the first to comment.