Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions scripts/check_installed_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,10 +181,13 @@ async def restore_drill(settings, folder, client, packet, source_dsn):
expected_settings['recovery_quarantine'] = current_settings['recovery_quarantine']
original['tables']['settings'] = sorted(canonical(row) for row in expected_settings.items())
assert current == original, 'Restore changed other table values or sequence state'
restored_settings = load_settings(folder / 'restore-etc')
marker = restored_settings.recovery_marker
assert marker.is_file(), 'Restore did not install its configured quarantine marker'
for marker_present in (True, False):
if not marker_present:
(folder / 'restore-etc/recovery-drill.json').unlink()
recovered = Application(load_settings(folder / 'restore-etc'))
marker.unlink()
recovered = Application(restored_settings)
try:
await recovered.load()
replay = await recovered.executor.execute(packet)
Expand Down
19 changes: 8 additions & 11 deletions src/msg/plugins/discovery.py
Original file line number Diff line number Diff line change
Expand Up @@ -570,8 +570,15 @@ def install(app):
op, finish = registration(app, 'discovery', ('identity', 'content'))
fields = {'type': 'array', 'items': STRING, 'maxItems': 30, 'uniqueItems': True}

@op(
'discovery.read_query',
obj({'home_summary': {'const': True}}, ('home_summary',)),
effect='read',
version=4,
)
async def home(ctx, request, tx):
"""Live counts and recent resources, filtered by current read access."""
require(ctx.principal.subject is None, 'public_home_summary_only')
timezone = ZoneInfo('Asia/Taipei')
today = ctx.now.astimezone(timezone).date()
posts = posts_today = users = 0
Expand Down Expand Up @@ -805,16 +812,6 @@ async def read_segment(ctx, request, tx):

async def list_items(ctx, request, tx, *, arguments=None, budget=None):
a = dict(request.arguments if arguments is None else arguments)
if 'home_summary' in a:
require(
request.operation == 'discovery.read_query'
and request.contract_version == 1
and a == {'home_summary': True},
'invalid_home_summary',
)
require(ctx.principal.subject is None, 'public_home_summary_only')
return await home(ctx, request, tx)

stable = request.operation == 'discovery.read_query'
internal_page = getattr(request, 'internal_page_state', None)
if internal_page is not None:
Expand Down Expand Up @@ -957,7 +954,7 @@ async def list_items(ctx, request, tx, *, arguments=None, budget=None):

op('discovery.list', obj(listing), effect='read')(list_items)
op('discovery.search', obj(listing, ('query',)), effect='read')(list_items)
op('discovery.read_query', obj({**listing, 'home_summary': BOOLEAN}), effect='read')(list_items)
op('discovery.read_query', obj(listing), effect='read')(list_items)

nested_schema = obj(
{
Expand Down
8 changes: 7 additions & 1 deletion src/msg/transports/http_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -1764,10 +1764,15 @@ async def dispatch(request: Request):
if header:
packet = path_packet(header, 'j', limits.max_request_bytes)
require(packet.operation == spec.name, 'operation_mismatch')
require(packet.contract_version == spec.version, 'representation_mismatch')
require(dict(packet.arguments) == args, 'representation_mismatch')
else:
packet = request_for(
spec.name, args, service.settings.service_url, source='manual'
spec.name,
args,
service.settings.service_url,
source='manual',
contract_version=spec.version,
)
result = await service.executor.execute(packet, entry='network')
value = result_wire(result)
Expand All @@ -1791,6 +1796,7 @@ async def dispatch(request: Request):
{'home_summary': True},
service.settings.service_url,
source='manual',
contract_version=4,
)
result = await service.executor.execute(packet, entry='network')
if result.error and result.error.code != 'query_cost_exceeded':
Expand Down
44 changes: 39 additions & 5 deletions tests/test_branding.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
import pytest
from test_service import NOW, call, register

from msg.core.codec import canonical, wire
from msg.core.codec import b64, canonical, wire
from msg.core.requests import request_for
from msg.transports.dictionary import build_dictionary
from msg.transports.http import create_app


Expand Down Expand Up @@ -217,11 +219,43 @@ async def test_home_summary_uses_existing_read_authority_and_remains_public(inst
app, _ = installed
assert all(operation.name != 'discovery.home' for operation in app.registry.operations())
key, uid, cert = await register(app, 'home-summary-reader')
anonymous = await call(app, 'discovery.read_query', {'home_summary': True})
anonymous = await call(app, 'discovery.read_query', {'home_summary': True}, contract_version=4)
assert anonymous.status == 'ok' and 'posts' in anonymous.data
authenticated = await call(
app, 'discovery.read_query', {'home_summary': True}, key=key, subject=uid, certs=(cert,)
app,
'discovery.read_query',
{'home_summary': True},
key=key,
subject=uid,
certs=(cert,),
contract_version=4,
)
assert authenticated.error.code == 'public_home_summary_only'
mixed = await call(app, 'discovery.read_query', {'home_summary': True, 'parent': '/main'})
assert mixed.error.code == 'invalid_home_summary'
mixed = await call(
app, 'discovery.read_query', {'home_summary': True, 'parent': '/main'}, contract_version=4
)
assert mixed.error.code == 'schema_validation'
for args in ({}, {'home_summary': False}):
invalid = await call(app, 'discovery.read_query', args, contract_version=4)
assert invalid.error.code == 'schema_validation'
legacy = await call(app, 'discovery.read_query', {'home_summary': True})
assert legacy.error.code == 'schema_validation'
dictionary = build_dictionary(app.registry)
direct_path = next(
row['example']
for row in dictionary.document['operations']
if row['name'] == 'discovery.read_query' and row['version'] == 4
)
wrong_version = request_for(
'discovery.read_query', {'home_summary': True}, app.settings.service_url
)
async with httpx.AsyncClient(
transport=httpx.ASGITransport(app=create_app(app)), base_url=app.settings.service_url
) as http:
direct = await http.get(direct_path)
assert direct.status_code == 200 and 'posts' in direct.json()['data']
mismatched = await http.get(
direct_path, headers={'X-Msg-Request': b64(canonical(wrong_version))}
)
assert mismatched.status_code == 400
assert mismatched.json()['error']['code'] == 'representation_mismatch'
5 changes: 3 additions & 2 deletions tests/test_nginx_secret_logs.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,9 +157,10 @@ def recovery_drill_active(self):
async def execute(self, packet, *, entry):
# The homepage is the only read exercised by this boundary fixture.
# Any attempted business execution still fails immediately.
assert packet.operation == 'discovery.home'
assert packet.operation == 'discovery.read_query'
assert packet.contract_version == 4
assert entry == 'network'
assert not packet.arguments
assert packet.arguments == {'home_summary': True}
return SimpleNamespace(
error=None,
data={
Expand Down
5 changes: 3 additions & 2 deletions tests/test_request_target_safety.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,10 @@ def recovery_drill_active(self):
async def execute(self, packet, *, entry):
# The homepage is the only read exercised by this boundary fixture.
# Any attempted business execution still fails immediately.
assert packet.operation == 'discovery.home'
assert packet.operation == 'discovery.read_query'
assert packet.contract_version == 4
assert entry == 'network'
assert not packet.arguments
assert packet.arguments == {'home_summary': True}
return SimpleNamespace(
error=None,
data={
Expand Down
Loading