Consolidate Persimmons shared backend and tests
This commit is contained in:
1 parent
f84c8cae09
commit
e42269682a
1 file changed
+88
@@ -0,0 +1,88 @@
|
||||
import base64
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import tempfile
|
||||
import unittest
|
||||
from unittest.mock import patch
|
||||
import httpx
|
||||
from persimmons import server
|
||||
from tests.persimmons_gitea_fixture import GiteaFixture
|
||||
|
||||
class HostTests(unittest.IsolatedAsyncioTestCase):
|
||||
async def asyncSetUp(self):
|
||||
self.tmp = tempfile.TemporaryDirectory()
|
||||
self.data = Path(self.tmp.name)
|
||||
self.patch = patch.object(server, 'DATA', self.data)
|
||||
self.patch.start()
|
||||
self.gitea = GiteaFixture()
|
||||
self.backend = patch.object(server.gitea, 'call', side_effect=self.gitea)
|
||||
self.backend.start()
|
||||
self.env = patch.dict(os.environ, PERSIMMONS_HUMAN_VAULT_DB=str(self.data / 'vault.sqlite'))
|
||||
self.env.start()
|
||||
self.client = httpx.AsyncClient(transport=httpx.ASGITransport(app=server.app), base_url=server.ORIGIN)
|
||||
|
||||
async def asyncTearDown(self):
|
||||
await self.client.aclose()
|
||||
self.env.stop()
|
||||
self.patch.stop()
|
||||
self.backend.stop()
|
||||
self.tmp.cleanup()
|
||||
|
||||
async def test_home_health_assets_and_no_runtime_tea_calls(self):
|
||||
response = await self.client.get('/')
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertIn('put plainly', response.text)
|
||||
for path in ['/healthz', '/assets/app.js', '/assets/version.mjs', '/assets/version.json', '/assets/extension.zip', '/assets/update.html', '/assets/install.html', '/assets/update-extension.py', '/signin']:
|
||||
self.assertEqual((await self.client.get(path)).status_code, 200)
|
||||
self.assertNotIn('https://teahub.xyz/api', (await self.client.get('/assets/app.js')).text)
|
||||
self.assertEqual((await self.client.get('/mcp')).status_code, 404)
|
||||
self.assertEqual((await self.client.get('/', headers={'Host':'evil.invalid'})).status_code, 400)
|
||||
|
||||
async def test_session_vault_boundary_persistence_logout(self):
|
||||
body = {'name':'taste-fixture', 'password':'disposable synthetic password'}
|
||||
auth = '/api/auth/register'
|
||||
self.assertEqual((await self.client.post(auth, json=body)).status_code, 403)
|
||||
self.assertEqual((await self.client.post(auth, json=body, headers={'Origin':'https://evil.invalid'})).status_code, 403)
|
||||
response = await self.client.post(auth, json=body, headers={'Origin':server.ORIGIN})
|
||||
self.assertEqual(response.status_code, 200)
|
||||
for flag in ['HttpOnly', 'Secure', 'SameSite=none', 'Path=/']:
|
||||
self.assertIn(flag, response.headers['set-cookie'])
|
||||
identity = (await self.client.get('/api/session')).json()
|
||||
owner = identity['owner']
|
||||
self.assertEqual(identity['name'], body['name'])
|
||||
url = '/api/human-vault/items'
|
||||
headers = {'Origin':server.human_vault.EXTENSION_ORIGIN, 'X-Vault-Owner':owner}
|
||||
self.assertEqual((await self.client.get(url)).status_code, 403)
|
||||
self.assertEqual((await self.client.post(url+'/read')).status_code, 403)
|
||||
self.assertEqual((await self.client.post(url+'/read', headers=headers)).json()['items'], [])
|
||||
self.assertEqual((await self.client.post(url+'/read', content='unexpected', headers=headers)).status_code, 400)
|
||||
self.assertEqual((await self.client.options(url, headers={**headers, 'Access-Control-Request-Method':'POST', 'Access-Control-Request-Headers':'content-type,x-vault-owner'})).headers['access-control-allow-origin'], headers['Origin'])
|
||||
# Validation accepts opaque ciphertext; accepted vault suite uses Web Crypto.
|
||||
item = {'v':1,'id':'a'*32,'origin':'https://example.com','iv':base64.b64encode(b'i'*12).decode(),'ciphertext':base64.b64encode(b'c'*24).decode()}
|
||||
self.assertEqual((await self.client.post(url, json=item, headers=headers)).status_code, 201)
|
||||
self.assertEqual((await self.client.post(url+'/'+item['id']+'/read', headers=headers)).json()['item'], item)
|
||||
self.assertEqual((await self.client.post(url+'/'+item['id']+'/read', headers={**headers,'X-Vault-Owner':'other'})).status_code, 409)
|
||||
self.assertEqual(server.human_vault.Store(self.data / 'vault.sqlite').operate(owner,'GET',item['id'])[0]['item'], item)
|
||||
self.assertEqual((await self.client.get(url+'/'+item['id'], headers={**headers,'X-Vault-Owner':'other'})).status_code, 409)
|
||||
cookie = self.client.cookies.get(server.COOKIE)
|
||||
self.assertEqual((await self.client.post('/api/logout', headers={'Origin':server.ORIGIN})).status_code, 200)
|
||||
self.assertEqual((await self.client.get(url, headers={**headers,'Cookie':server.COOKIE+'='+cookie})).status_code, 401)
|
||||
self.assertEqual((await self.client.post('/api/auth/login',json=body,headers={'Origin':server.ORIGIN})).status_code, 200)
|
||||
self.assertEqual((await self.client.get(url,headers=headers)).json()['items'][0]['id'],item['id'])
|
||||
self.assertEqual((await self.client.delete('/api/vault-index/'+item['id'])).status_code, 403)
|
||||
self.assertEqual((await self.client.delete('/api/vault-index/'+item['id'], headers={
|
||||
'Origin': server.ORIGIN, 'X-Requested-With': 'Persimmons'})).status_code, 200)
|
||||
self.assertEqual((await self.client.get(url,headers=headers)).json()['items'], [])
|
||||
self.assertNotIn(body['password'].encode(), (self.data/'sessions.sqlite').read_bytes())
|
||||
self.assertNotIn(cookie.encode(), (self.data/'sessions.sqlite').read_bytes())
|
||||
|
||||
async def test_different_accounts_and_untrusted_preflight(self):
|
||||
for name in ['fixture-one','fixture-two']:
|
||||
self.assertEqual((await self.client.post('/api/auth/register',json={'name':name,'password':'disposable synthetic password'},headers={'Origin':server.ORIGIN})).status_code,200)
|
||||
owner=(await self.client.get('/api/session')).json()['owner']
|
||||
if name=='fixture-one': first=owner
|
||||
else: self.assertNotEqual(first,owner)
|
||||
response=await self.client.options('/api/human-vault/items',headers={'Origin':'chrome-extension://'+'a'*32,'Access-Control-Request-Method':'GET'})
|
||||
self.assertEqual(response.status_code,403)
|
||||
self.assertNotIn('access-control-allow-origin',response.headers)
|
||||
Reference in new issue
Block a user