Whose Pump Is Botie Allowed to Inspect?
The tool name is allowed, but its arguments point at another customer’s asset. Build the service-side checks that connect the caller, operation and stored record.
14 sections · 4 questions · 1 coding challenge
The arrow keys move between sections too.
Permission Had to Follow the Object Being Accessed
BOTIE
An agent introduces a new way to propose that operation, but the service still has the same responsibility. In our invented workshop, the model proposes an asset operation while application-owned records establish the caller’s tenant and grants.
Sources for this history
A Valid Pump ID Belongs to the Wrong Customer
Botie now asks for an allowed tool: read_manual. The request contains a real asset ID, and the service can find a record for it. There is still a problem. The record belongs to a different customer.
We cannot settle that by asking Botie whether the caller seems trustworthy. The service needs the identity established by login and the permissions attached to that identity. Then it must compare those permissions with the particular object being requested.
The identity and policy records are invented server-side fixtures. This browser does not authenticate a real user.
Step 1 of 2
A familiar ID is not enough
The same short asset name may occur in two organisations. Keep its owner beside it.
| Caller | Stored object | Requested operation |
|---|---|---|
| North tenant; P7 grant; read grant | North tenant; P7 | read |
| North tenant; P7 grant; read grant | South tenant; P7 | read |
North tenant; P7 grant; read grant
- Stored object
- North tenant; P7
- Requested operation
- read
North tenant; P7 grant; read grant
- Stored object
- South tenant; P7
- Requested operation
- read
Step 2 of 2
The handler checks the object it actually loaded
Resolve the model-supplied ID inside the authorised tenant, then enforce the operation policy before returning data or making a change.
- 1
Authenticated session
Current caller and tenant - 2
Scoped lookup
Stored object identity and ownership - 3
Authorisation
Object and operation grants - 4
Handler
Only the authorised work
An allowed tool still needs an authorised object
Each branch establishes one permission condition over the object actually being accessed.
Does this caller have this permission on this asset?
Build separate tenant, asset and operation comparisons. Then change the stored asset’s tenant without changing its visible ID.
BOTIE
Access decision
allow this operation
Follow the operations
1. Server-authenticated principal
{ "tenant": "north", "assets": [ "P7" ], "operations": [ "read" ] }2. Stored asset record
{ "tenant": "north", "id": "P7" }3. Match the tenant
true
4. Check this asset grant
true
5. Requested operation
read
6. Check the operation grant
true
7. Require every access check
allow this operation
8. Result
allow this operation
The browser fixtures illustrate policy evaluation; they provide no authentication, database transaction, token verification or production permission service.
1. Separate identity from permission
Authentication establishes which account is making the request, under the assumptions of the login system. Authorisation decides what that account may do. A signed-in technician may read manuals for assigned assets and still be unable to create jobs or access another site.
The service should derive the principal from the verified session or token, not from a field Botie supplies. Here principal means the application’s representation of that caller. Our small dictionary stores a tenant and two grant lists. A real application may query a policy service or database instead.
A model-generated role such as administrator has no effect on the authenticated principal.
2. Check the object before returning its contents
Our service stores P7 under tenant north. A caller from north still needs a grant for P7. Checking the tenant alone would grant access to every asset in that organisation. Checking only the short ID would confuse two tenants that both use P7.
In the workshop, Match the tenant and Check this asset grant are separate components so their results remain visible. In a database, a scoped query can require both tenant and object ID before it returns the row. Keep a clear policy for objects shared across tenants rather than quietly removing the tenant condition.
The example’s policy denies cross-tenant access. Sharing would require an explicit additional rule.
3. Deny requests that have no matching rule
The operation list contains read. A request to export, delete or schedule has no matching grant and is denied. That is a default-deny policy: absence of permission does not become permission. Missing or malformed principal fields must also fail the check.
Use exact identifiers from a controlled operation vocabulary. Substring matches are dangerous here: read is contained in some longer names without meaning the same operation. An unchecked wildcard can expand a narrow role into authority over future capabilities.
This is a deterministic decision over server records. A second language model is unnecessary.
Look up the owner before considering an operation
A dictionary can use a tuple as a compound key. Include the tenant in the lookup instead of searching by the short ID alone.
records = {("north", "P7"): "North pump manual", ("south", "P7"): "South pump manual"}
caller_tenant = "north"
requested_id = "P7"
print(records.get((caller_tenant, requested_id)))
print(records.get((caller_tenant, "P8")))North pump manual
NoneThe absent row remains absent. This lookup illustrates tenant scoping; operation and assignment checks still belong beside it.
Check yourself
Questions on Parts 1 to 3
Question 1 of 2
Why must this policy deny the request?
Use the information below.
Authenticated principal: tenant north, assets [P7], operations [read]. Stored asset: tenant south, id P7. Request: read.4. Enforce the check where the action happens
Hiding an export button or removing a tool from a model prompt helps guide normal use. Neither prevents a caller from reaching an exposed service route another way. The service that reads or changes the asset must enforce the policy for each request.
For writes, keep the authorisation decision and the relevant state change together closely enough that an intervening ownership or permission change cannot silently invalidate the check. A database transaction, version comparison or policy revision check may be needed. The right mechanism depends on where those records live.
This is the reason to test the handler directly, without a model or the website interface in the path.
5. Give the tool less authority than the whole service
A maintenance assistant that only reads manuals should use a scoped read capability. It should not receive a general database administrator credential or an unrestricted command runner. Credentials belong in the service environment and should stay out of model-visible arguments and results.
The application must account for both the user’s authority and the service’s own authority. A powerful service account does not mean every user who can ask Botie a question may exercise all of its permissions. Check the caller before using that service account on their behalf.
Least privilege makes a mistaken request less consequential, while per-request authorisation decides whether that request may proceed.
6. Bind approval to what will actually be executed
An approved inspection for P7 does not authorise an inspection for P8. Preserve the action identity, arguments and revision that the reviewer saw, then compare them again at execution time. Recheck the reviewer’s current authority and the caller’s current access when your policy requires it.
Approval and authorisation answer different questions. A permitted caller may still need a human decision for a consequential write. A human saying yes does not give the application permission to cross a tenant boundary. Keep both checks in the execution path.
The earlier approval workshop supplied an exact-action match. This lesson adds the object-access boundary around that action.
7. Make access failures observable without revealing the record
A denied caller should not receive the protected manual inside an error message. Depending on the application, even revealing whether an asset exists may disclose information. A consistent external response can hide that distinction while a protected audit record retains the internal reason.
Test a permitted read, another tenant’s matching ID, an unassigned object in the same tenant, and a forbidden operation. Add a case where permissions change before execution. These cases catch different mistakes; one successful read says little about the deny paths.
The construction is correct only if it preserves the permitted case while closing the gaps exposed by the changed records.
Check yourself
Questions on Parts 1 to 7
Question 1 of 2
Which independent check fails?
Use the information below.
Principal: tenant north, assets [P7], operations [read]. Object: tenant north, id P7. Proposed operation: export.Try it with Botie
Build with Botie
Write and run Python