Tool permissions by environment

Agent buildEasy15 min

Your task

An operations agent is being promoted from dev to prod, and each environment grants it a different set of tools. The grants live in a permission matrix, for example:

{
  "dev":  { "*": ["read", "write", "delete"] },
  "prod": { "*": ["read"], "tickets": ["read", "write"], "db": [] }
}

For each environment, a tool name maps to the list of actions the agent may take with that tool. The tool name "*" covers every tool that has no entry of its own.

Write check_calls(matrix, calls). Each call is [env, tool, action]. Return "allow" or "deny" for each call, in order, by these rules:

  1. An environment that is not in the matrix denies everything.
  2. If the environment has an entry for exactly this tool, that entry alone decides, even when "*" would grant more. An empty list therefore locks a tool down.
  3. Otherwise the environment's "*" entry decides. With no "*" entry either, the call is denied.
  4. The action is allowed only if it appears in the chosen list.
  5. Whatever the matrix says, the action "delete" in the environment "prod" is always denied. This rule sits outside the matrix on purpose, so no config change can grant it.

Environment, tool and action names are compared exactly, case included, and a name can be any string, even "constructor" or "toString".

Examples, with the matrix above:

  • ["dev", "db", "delete"] gives "allow": dev's "*" entry grants it.
  • ["prod", "tickets", "write"] gives "allow".
  • ["prod", "db", "read"] gives "deny": db's own empty entry beats "*".
  • ["staging", "db", "read"] gives "deny": there is no staging environment.

So check_calls(matrix, [["dev", "db", "delete"], ["prod", "tickets", "write"], ["prod", "db", "read"], ["staging", "db", "read"]]) returns ["allow", "allow", "deny", "deny"].

Examples

check_calls(matrix: dict, calls: list[list[str]]) -> list[str]
Example 1
Inputcheck_calls({"dev": {"*": ["read", "write", "delete"]}, "prod": {"*": ["read"], "tickets": ["read", "write"], "db": []}}, [["dev", "db", "delete"], ["prod", "tickets", "write"], ["prod", "db", "read"], ["staging", "db", "read"]])
Output["allow", "allow", "deny", "deny"]
Example 2
Inputcheck_calls({"dev": {"*": ["read", "write", "delete"]}, "prod": {"*": ["read"], "tickets": ["read", "write"], "db": []}}, [["prod", "search", "read"], ["prod", "search", "write"]])
Output["allow", "deny"]