feat(properties): prepare property commands before execution
Build and Push Docker Container / build-and-push (push) Successful in 4m28s
Build and Push Docker Container / build-and-push (push) Successful in 4m28s
- Create missing note files before running property:set. - Default date-only datetime values to midnight. - Keep property names out of path authorization checks. - Split policy, vault, daily note, and command target helpers. - Split n8n node args, description, output, and API response helpers. - Split API tests by auth, command, daily note, and property behavior. - Bump API and n8n node versions.
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
import app.auth as authmod
|
||||
from conftest import client, jwt, use_jwt_auth
|
||||
|
||||
def test_command_requires_auth():
|
||||
response = client.post("/commands", json={"args": ["--version"]})
|
||||
|
||||
assert response.status_code == 401
|
||||
assert response.json()["detail"] == "missing Authorization header; expected 'Bearer <jwt>'"
|
||||
|
||||
def test_invalid_token_reports_reason(monkeypatch):
|
||||
monkeypatch.setattr("app.auth.JWT_SECRET", "secret")
|
||||
|
||||
response = client.post(
|
||||
"/commands",
|
||||
headers={"Authorization": "Bearer not-a-jwt"},
|
||||
json={"args": ["--version"]},
|
||||
)
|
||||
|
||||
assert response.status_code == 401
|
||||
assert response.json()["detail"].startswith("invalid token:")
|
||||
|
||||
def test_jwt_claims_become_policy():
|
||||
token = jwt({"sub": "file", "vaults": ["work"], "paths": ["Inbox/"], "commands": ["read"]})
|
||||
claims = authmod.decode_hs256_jwt(token, b"test-jwt-secret")
|
||||
policy = authmod.policy_from_jwt_claims(token, claims)
|
||||
|
||||
assert policy.token == token
|
||||
assert policy.name == "file"
|
||||
assert policy.vaults == ("work",)
|
||||
assert policy.paths == ("Inbox/",)
|
||||
assert policy.commands == ("read",)
|
||||
|
||||
def test_token_policy_restricts_command(monkeypatch):
|
||||
token = use_jwt_auth(monkeypatch, {"sub": "limited", "commands": ["read"], "vaults": ["*"], "paths": ["*"]})
|
||||
|
||||
response = client.post(
|
||||
"/commands",
|
||||
headers={"Authorization": f"Bearer {token}"},
|
||||
json={"args": ["create", "path=Inbox/Nope.md", "content=Nope"]},
|
||||
)
|
||||
|
||||
assert response.status_code == 403
|
||||
assert response.json()["detail"].startswith("token is not allowed to run command: create")
|
||||
|
||||
def test_token_policy_restricts_vault(monkeypatch):
|
||||
token = use_jwt_auth(monkeypatch, {"sub": "work", "commands": ["*"], "vaults": ["work"], "paths": ["*"]})
|
||||
|
||||
response = client.post(
|
||||
"/commands",
|
||||
headers={"Authorization": f"Bearer {token}"},
|
||||
json={"args": ["vault=personal", "read", "path=Inbox/Test.md"]},
|
||||
)
|
||||
|
||||
assert response.status_code == 403
|
||||
assert response.json()["detail"].startswith("token is not allowed to access vault: personal")
|
||||
|
||||
def test_token_policy_restricts_path(monkeypatch):
|
||||
token = use_jwt_auth(monkeypatch, {"sub": "inbox", "commands": ["read"], "vaults": ["*"], "paths": ["Inbox/"]})
|
||||
|
||||
response = client.post(
|
||||
"/commands",
|
||||
headers={"Authorization": f"Bearer {token}"},
|
||||
json={"args": ["read", "path=Private/Secret.md"]},
|
||||
)
|
||||
|
||||
assert response.status_code == 403
|
||||
assert response.json()["detail"].startswith("token is not allowed to access path: Private/Secret.md")
|
||||
|
||||
def test_token_policy_allows_allowed_path(monkeypatch):
|
||||
token = use_jwt_auth(monkeypatch, {"sub": "inbox", "commands": ["read"], "vaults": ["*"], "paths": ["Inbox/"]})
|
||||
|
||||
response = client.post(
|
||||
"/commands",
|
||||
headers={"Authorization": f"Bearer {token}"},
|
||||
json={"args": ["-c", "print('ok')"]},
|
||||
)
|
||||
|
||||
assert response.status_code == 403
|
||||
|
||||
response = client.post(
|
||||
"/commands",
|
||||
headers={"Authorization": f"Bearer {token}"},
|
||||
json={"args": ["read", "path=Inbox/Allowed.md"]},
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response.json()["command"] == ["python", "read", "path=Inbox/Allowed.md"]
|
||||
Reference in New Issue
Block a user