The resume validator was always constructed with ..ResumeValidator::default(),
so expected_etag and expected_last_modified were permanently None and the
ETag/Last-Modified branches of validate_resume_response were unreachable.
The checks were written and tested, but nothing fed them: a provider that
replaced a file between an interruption and the resume returned a
well-formed 206 at the requested offset, and those bytes were appended to a
partial belonging to the previous resource. The result is a silently
corrupt recording that reports success.
RecordingMetadata now persists the validators captured from the first
response, so they survive a restart. Only a fresh transfer may set them; a
resume keeps the ones its partial was written against.
Weak ETags are discarded rather than stored. RFC 9110 forbids a weak
validator in a range request because it promises only semantic equivalence,
so two responses can share a weak tag and still differ byte for byte. A
response that downgrades a previously strong tag to a weak one now fails
validation instead of passing it by absence.
filter_request_header also rejected only host and connection, so a
configured Range or If-Range became a client default header and collided
with the offset a resumable transfer asks for. The transport-owned set now
covers the range and conditional headers plus Content-Length and
Transfer-Encoding, matched case-insensitively.
A single write permission could not express the policy the DVR needs: a
user who may request a recording is not necessarily one who may cancel
someone else's, and neither implies the right to delete a file. Replace it
with recording.create, recording.manage and recording.delete, and map each
action onto the one it actually needs:
- create private/shared -> recording.create
- edit, cancel, manage rules -> recording.manage
- delete, system retention sweep -> recording.delete
The split renumbers every permission bit above recording.read, so
CURRENT_PERMISSION_SCHEMA_VERSION is bumped to 4 and tokens issued earlier
fail closed at the validator instead of having their bits reinterpreted.
The removed recording.write name now decodes to nothing, so a groups file
that still lists it loses the permission rather than silently gaining one
of the three replacements.
Permission bit values are frozen in a test: they are the wire format, and
reordering the enum would reinterpret every issued token.