railiance-platform/scripts/task_execution_mediator.py

219 lines
13 KiB
Python
Raw Normal View History

"""Owner mediator seam for the existing strict Secrets handoff and Approval GET.
Only the disposable NativeFixtureAdapter is callable here. No production backend,
caller-supplied authorizer/transport, approval boolean or portable receipt exists.
The strict Secrets module is an explicit external dependency: absence refuses
before credential-bearing requests or native effects, never a permissive fallback.
"""
from __future__ import annotations
from dataclasses import dataclass
from datetime import datetime,timezone
import importlib
import json
import os
import stat
from pathlib import Path
from urllib.parse import urlsplit
from task_execution_cleanup import run_cleanup
from task_execution_journal import Binding,Cleanup,Journal,Refused
from task_execution_plan import Compiler,encoded,content_hash
from task_execution_native_fixture import FixtureAdmission,NativeFixtureAdapter,_absolute_budget
@dataclass(frozen=True)
class ConfirmationConfig:
"""Owner-installed independent read credential/route, never request fields."""
origin: str
token_file: Path
timeout_seconds: float = 3
_HANDOFF_FIELDS={'execution_contract','approval_id','request_digest','decision_id',
'native_binding','approval_binding_digest','submitted_request_digest',
'decision_not_before','decision_expires_at','approval_expires_at'}
def _strict_dependency():
try:
module=importlib.import_module('secrets_engine.task_execution_handoff')
for name in ['TaskExecutionAuthorization','TaskExecutionConsumption',
'authorize_task_execution','consume_task_execution']:
if not hasattr(module,name):raise ImportError()
return module
except ImportError:
raise Refused('strict Secrets task execution handoff dependency unavailable') from None
def _trusted_v2(cfg):
return (getattr(cfg,'authorization_policy_package',None)=='secrets-engine.task-execution.service-pilot'
and getattr(cfg,'authorization_policy_version',None)=='v2')
def _read_source_bundle(descriptor):
# Paths/hash expectations come from independently installed owner_descriptor,
# never a requester-selected file lookup. No bytes or raw OS errors escape.
for item in descriptor['source_bundle'].values():
path=Path(item['path']);fd=None
try:
if not path.is_absolute() or path.resolve()!=path:raise ValueError()
fd=os.open(path,os.O_RDONLY|os.O_NOFOLLOW|os.O_NONBLOCK)
before=os.fstat(fd)
if not stat.S_ISREG(before.st_mode) or before.st_nlink!=1 or before.st_mode&0o022 or not 0<before.st_size<=1048576:raise ValueError()
with os.fdopen(fd,'rb',closefd=False) as stream:raw=stream.read(1048577)
after=os.fstat(fd)
identity=lambda value:(value.st_dev,value.st_ino,value.st_size,value.st_mtime_ns,value.st_ctime_ns,value.st_mode,value.st_nlink)
if identity(before)!=identity(after) or len(raw)!=before.st_size or 'sha256:'+content_hash(raw)!=item['sha256']:raise ValueError()
except Exception:raise Refused('exact owner execution source bundle refused') from None
finally:
if fd is not None:os.close(fd)
def _source_bundle(descriptor):
with _absolute_budget(2):_read_source_bundle(descriptor)
def _fresh(cfg,handoff,descriptor):
if _trusted_v2(cfg):_source_bundle(descriptor)
from secrets_engine.application_time import read_window,validity_bounds
lower,upper=validity_bounds(window=read_window(cfg))
try:
parse=lambda value:datetime.fromisoformat(value.replace('Z','+00:00'))
deadline=parse(descriptor['operation_deadline'])
approval=parse(handoff['approval_expires_at']);decision=parse(handoff['decision_expires_at'])
start=parse(handoff['decision_not_before']) if handoff['decision_not_before'] else None
if any(value.tzinfo is None for value in [deadline,approval,decision]) or (start is not None and start.tzinfo is None):raise ValueError()
if upper>=min(deadline,approval,decision) or deadline>min(approval,decision) or (start is not None and lower<start):raise ValueError()
except (KeyError,TypeError,ValueError,AttributeError):
raise Refused('original task authority deadline refused') from None
def _intent(journal,binding,kind,fields):
digest=content_hash(encoded({'kind':kind,**fields}))
ordinal=journal.next_call_ordinal(binding)
journal.before_call(binding,ordinal,digest)
return ordinal,digest
def _unknown(journal,binding):
state=journal.observe(binding)['state']
if state not in {'unknown','failed','cleanup-confirmed'}:
journal.transition(binding,state,'unknown')
def run_native_fixture_task(*,compiler:Compiler,journal:Journal,plan_request:dict,
owner_descriptor:bytes,secrets_cfg,entry,
confirmation:ConfirmationConfig,
native_admission:FixtureAdmission):
"""Authorize inside owner boundary, reserve, consume once, independently GET.
Strict authorization is executed here through the actual Secrets-owned module;
an inbound TaskExecutionAuthorization or arbitrary callable is never accepted.
This function returns closed metadata, not tokens or raw approval/decision data.
Positive execution requires the module's real signed PDP path; the retained v1
Access policy stays DENY-only; actual v2 successor source is separately tested
through its authenticated caller/fresh-source path without runtime admission. Fixture policy/identity admission is independent
synthetic setup and cannot become production acceptance.
"""
strict=_strict_dependency()
from approval_engine.execution_confirmation import ExecutionConfirmationClient
from secrets_engine.execution_contract import ExecutionContract
if type(confirmation) is not ConfirmationConfig or type(native_admission) is not FixtureAdmission:
raise Refused('independent owner provider configuration required')
try:
origin=urlsplit(confirmation.origin)
if origin.scheme not in {'http','https'} or origin.hostname!='127.0.0.1' or not origin.port or origin.username or origin.password or origin.path or origin.query or origin.fragment:
raise ValueError()
if confirmation.origin!=str(getattr(secrets_cfg,'approval_url','')).rstrip('/'):
raise ValueError()
descriptor=ExecutionContract.parse(json.loads(owner_descriptor)).as_dict()
if encoded(descriptor)!=owner_descriptor or native_admission.descriptor!=owner_descriptor:
raise ValueError()
except Exception:
raise Refused('exact owner descriptor or provider route refused') from None
if _trusted_v2(secrets_cfg):_source_bundle(descriptor)
# An independently pinned owner catalogue must match every engine field. Its
# approval IDs are source references, never runtime authority.
proposal=compiler.proposal()
target={key:getattr(entry,key) for key in proposal['catalog_target']}
if encoded(target)!=encoded(proposal['catalog_target']) or entry.id!=proposal['catalog_id'] or entry.stage!='prod':
raise Refused('owner catalogue does not match strict task request')
snapshot={path:compiler.read(Path(path)) for path in compiler.pins}
owner_entry=compiler._catalogue_entry(snapshot)
# Only the served approval pointer is added after issue. Purpose or other
# source approval semantics cannot be substituted through an inbound entry.
without_reference=lambda value:{key:item for key,item in value.items() if key!='authorization_id'}
if encoded(without_reference(entry.approval))!=encoded(without_reference(owner_entry.approval)):
raise Refused('owner approval metadata differs from source')
client=ExecutionConfirmationClient(confirmation.origin,confirmation.token_file,
timeout_seconds=confirmation.timeout_seconds)
with journal.locked():
authorization=strict.authorize_task_execution(secrets_cfg,entry,
execution_contract=ExecutionContract.parse(descriptor))
if type(authorization) is not strict.TaskExecutionAuthorization:
raise Refused('strict task authorization missing')
handoff=authorization.as_owner_handoff()
if type(handoff) is not dict or set(handoff)!=_HANDOFF_FIELDS or encoded(handoff['execution_contract'])!=owner_descriptor:
raise Refused('strict task handoff does not match owner descriptor')
_fresh(secrets_cfg,handoff,descriptor)
request=dict(plan_request,request_digest=handoff['request_digest'])
plan=compiler.bind_execution(compiler.compile(request),descriptor,owner_descriptor)
binding=Binding(handoff['approval_id'],handoff['request_digest'],descriptor['operation_id'],plan.content_digest)
# Constructor validates fixed native origin/fixture process/grant sources
# but performs no native HTTP request or credential acquisition.
native=NativeFixtureAdapter(compiler,plan,journal,binding,native_admission)
journal.reserve(binding,native.cleanup)
fields={key:handoff[key] for key in ['approval_id','request_digest','decision_id']}
try:
ordinal,digest=_intent(journal,binding,'strict-secrets-task-consume',fields)
consumed=strict.consume_task_execution(secrets_cfg,authorization)
if type(consumed) is not strict.TaskExecutionConsumption:
raise Refused('strict task consumption missing')
receipt={key:getattr(consumed,key) for key in ['approval_id','request_digest','decision_id','consumed_at','idempotent']}
if any(receipt[key]!=value for key,value in fields.items()) or receipt['idempotent'] is not False:
raise Refused('historical or mismatched task consumption refused')
stamp=datetime.fromisoformat(receipt['consumed_at'].replace('Z','+00:00'))
if stamp.tzinfo is None:raise Refused('consumption timestamp missing')
journal.after_call(binding,ordinal,digest,content_hash(encoded(receipt)))
_fresh(secrets_cfg,handoff,descriptor)
ordinal,digest=_intent(journal,binding,'independent-approval-consumption-read',fields)
confirmed=client.confirm_consumption(binding.approval_id,binding.request_digest,
handoff['decision_id'],expected_binding=handoff['native_binding'],
expected_pdp_digest=handoff['approval_binding_digest'])
if confirmed!={key:receipt[key] for key in ['approval_id','request_digest','decision_id','consumed_at']}:
raise Refused('independent consumption correspondence refused')
journal.after_call(binding,ordinal,digest,content_hash(encoded(confirmed)))
_fresh(secrets_cfg,handoff,descriptor)
journal.transition(binding,'reserved','consume-confirmed',content_hash(encoded(confirmed)))
except Exception:
_unknown(journal,binding)
raise Refused('task consumption or independent confirmation unconfirmed; no native execution') from None
result={'operation_id':binding.operation_id,'request_digest':binding.request_digest,
'decision_id':handoff['decision_id'],'native_apply':'unverified',
'native_calls_completed':0,'cleanup_confirmed':False}
try:
journal.transition(binding,'consume-confirmed','authenticating')
# Fixture identity is independently seeded synthetic setup, not CCR38
# provenance proof or a production service login.
journal.transition(binding,'authenticating','grant-issuing')
_fresh(secrets_cfg,handoff,descriptor)
native.materialize_grant()
journal.transition(binding,'grant-issued','applying')
for ordinal in range(6):
_fresh(secrets_cfg,handoff,descriptor)
native.metadata_call(ordinal)
result['native_calls_completed']+=1
result['readback_evidence_digest']=native.verify_metadata()
result['native_apply']='verified'
except Exception:
_unknown(journal,binding)
result['native_apply']='unknown'
finally:
result['cleanup_confirmed']=run_cleanup(journal,binding,native)['confirmed']
if result['native_apply']=='verified' and result['cleanup_confirmed']:
journal.transition(binding,'verified','cleanup-confirmed',result['readback_evidence_digest'])
# Readback and cleanup are independent outcomes; no recovery authority
# or native compare-and-swap is implied by either fixture result.
result['business_state']=journal.observe(binding)['state']
return result