Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a19023995e | ||
|
|
878588d364 |
@@ -308,6 +308,24 @@ paths:
|
|||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
$ref: ../schemas/control-v1.schema.json#/$defs/ConnectionManifest
|
$ref: ../schemas/control-v1.schema.json#/$defs/ConnectionManifest
|
||||||
|
'202':
|
||||||
|
description: >-
|
||||||
|
Confirmed over-cap reconnect creates or returns a durable `session.display_relaunch` termination operation.
|
||||||
|
Before initial operation creation and on every replay, the authenticated principal and active client device/key MUST
|
||||||
|
match the broker session. Under the session and unique Stop lock, the Server MUST create at most one termination
|
||||||
|
operation total per broker session. A same-owner/device lost-response retry MUST return that same operation.
|
||||||
|
If a user Stop wins first, reconnect MUST return a stable non-202 result. If display relaunch wins first, a later user
|
||||||
|
Stop MUST converge on that same existing `StopOperation` without creating a second operation or issuing a second Terminate.
|
||||||
|
The operation reuses the existing one-way non-reissued Stop work/ack path. This response is not a manifest, does not
|
||||||
|
assert termination completion, and does not authorize a replacement session before `applied`.
|
||||||
|
After `applied`, the client submits a fresh `SessionRequest` with a new idempotency key.
|
||||||
|
Failed or `termination_unconfirmed` outcomes never auto-relaunch. Client local Stop or teardown MUST invalidate
|
||||||
|
relaunch generation so a later `applied` state cannot cause a fresh launch.
|
||||||
|
Maximum JSON body: 16384 bytes.
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: ../schemas/control-v1.schema.json#/$defs/StopOperation
|
||||||
'400': {$ref: '#/components/responses/InvalidRequest'}
|
'400': {$ref: '#/components/responses/InvalidRequest'}
|
||||||
'401': {$ref: '#/components/responses/Unauthorized'}
|
'401': {$ref: '#/components/responses/Unauthorized'}
|
||||||
'404': {$ref: '#/components/responses/NotFound'}
|
'404': {$ref: '#/components/responses/NotFound'}
|
||||||
|
|||||||
@@ -319,6 +319,30 @@ def main() -> int:
|
|||||||
for operation_id in ("logoutSession", "requestBrokerSession", "allocateBrokerSession", "reconnectBrokerSession", "cancelBrokerSession"):
|
for operation_id in ("logoutSession", "requestBrokerSession", "allocateBrokerSession", "reconnectBrokerSession", "cancelBrokerSession"):
|
||||||
operation = openapi.split(f" operationId: {operation_id}\n", 1)[1].split(" responses:\n", 1)[0]
|
operation = openapi.split(f" operationId: {operation_id}\n", 1)[1].split(" responses:\n", 1)[0]
|
||||||
assert " browserCsrfHeader: []\n - nativeBearer: []\n" in operation, f"{operation_id}: native bearer must remain a separate OR requirement"
|
assert " browserCsrfHeader: []\n - nativeBearer: []\n" in operation, f"{operation_id}: native bearer must remain a separate OR requirement"
|
||||||
|
reconnect_endpoint = openapi.split(" operationId: reconnectBrokerSession\n", 1)[1].split("\n /api/", 1)[0]
|
||||||
|
assert " '202':\n" in reconnect_endpoint
|
||||||
|
relaunch_response = reconnect_endpoint.split(" '202':\n", 1)[1].split(" '400':", 1)[0]
|
||||||
|
relaunch_schema = relaunch_response.split(" content:\n", 1)[1]
|
||||||
|
assert relaunch_schema == (
|
||||||
|
" application/json:\n"
|
||||||
|
" schema:\n"
|
||||||
|
" $ref: ../schemas/control-v1.schema.json#/$defs/StopOperation\n"
|
||||||
|
), "reconnect 202 must contain only the exact StopOperation schema reference"
|
||||||
|
assert "ConnectionManifest" not in relaunch_response
|
||||||
|
assert "anyOf:" not in relaunch_response and "oneOf:" not in relaunch_response
|
||||||
|
relaunch_text = " ".join(relaunch_response.split())
|
||||||
|
assert "durable `session.display_relaunch` termination operation" in relaunch_text
|
||||||
|
assert "Before initial operation creation and on every replay, the authenticated principal and active client device/key MUST match the broker session" in relaunch_text
|
||||||
|
assert "MUST create at most one termination operation total per broker session" in relaunch_text
|
||||||
|
assert "A same-owner/device lost-response retry MUST return that same operation" in relaunch_text
|
||||||
|
assert "If a user Stop wins first, reconnect MUST return a stable non-202 result" in relaunch_text
|
||||||
|
assert "a later user Stop MUST converge on that same existing `StopOperation` without creating a second operation or issuing a second Terminate" in relaunch_text
|
||||||
|
assert "not a manifest" in relaunch_text
|
||||||
|
assert "does not assert termination completion" in relaunch_text
|
||||||
|
assert "does not authorize a replacement session before `applied`" in relaunch_text
|
||||||
|
assert "fresh `SessionRequest` with a new idempotency key" in relaunch_text
|
||||||
|
assert "Failed or `termination_unconfirmed` outcomes never auto-relaunch" in relaunch_text
|
||||||
|
assert "Client local Stop or teardown MUST invalidate relaunch generation so a later `applied` state cannot cause a fresh launch" in relaunch_text
|
||||||
for operation_id in ("loginBrowserSession", "rotateNativeCredential", "issueNativeTunnelCredential"):
|
for operation_id in ("loginBrowserSession", "rotateNativeCredential", "issueNativeTunnelCredential"):
|
||||||
operation = openapi.split(f" operationId: {operation_id}\n", 1)[1].split(" responses:\n", 1)[0]
|
operation = openapi.split(f" operationId: {operation_id}\n", 1)[1].split(" responses:\n", 1)[0]
|
||||||
assert "browserCsrf" not in operation, f"{operation_id}: excluded operation gained browser CSRF"
|
assert "browserCsrf" not in operation, f"{operation_id}: excluded operation gained browser CSRF"
|
||||||
|
|||||||
Reference in New Issue
Block a user