You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 743ac88
Browse filesBrowse the repository at this point in the historyBrowse files
feat(spec): `alipay-v3` joins the `signed` connector-instance auth schemes, with response verification (ADR-0143 D2.3, #26710)
7
+
8
+
Clause-②: yes (widening)
9
+
Metadata-protocol: added
10
+
11
+
<!-- adr-0087: not-required (no-migration-prescription) One member is added to the closed signing-scheme list and one shape to the `signed` union. The signer registry's third parameter changes from the key string to an object carrying the key and an optional vendor public key; that function is internal to the automation service and in no release yet (it ships with the pending `26710-signed-connector-auth-aliyun` changeset). Nothing an author wrote before is refused or re-shaped, so there is nothing for a migration to rewrite. -->
12
+
13
+
A declarative connector instance can now declare Alipay's OpenAPI v3 signature as its signing scheme. Its responses are verified too. It joins the Alibaba Cloud and Tencent Cloud schemes.
- The `signed` union gains its closed shape: `{ type: 'signed', scheme: 'alipay-v3', keyId, credentialRef, vendorPublicKeyRef }`.
18
+
-`keyId` is the app id.
19
+
-`credentialRef` names the application private key.
20
+
-`vendorPublicKeyRef` names the Alipay public key that every response is verified with. It is required.
21
+
- The same publish refusals apply as for the other schemes: inline key spellings by name, an authored `signature`, and `signed` on `provider: 'mcp'`. Also refused:
22
+
-`appPrivateKey`, by name;
23
+
-`alipayPublicKey`, which is answered with `vendorPublicKeyRef`.
24
+
25
+
`appId` is answered with `keyId`. An unknown scheme's refusal now lists all three schemes.
26
+
-**`@objectstack/service-automation`**
27
+
- Every request is signed `ALIPAY-SHA256withRSA` with a fresh nonce and timestamp.
28
+
- Every response below status 400 must carry a valid Alipay signature, an empty body included. A signed error response is verified too. A response that fails either check fails the dispatch, and its body is never returned.
29
+
- The vendor public key resolves through the same chain as `credentialRef`. An instance whose public key is missing is degraded with that reference named. The credential door lists both references against the instance.
30
+
- A key that is not an RSA key fails boot, naming which reference, never the value.
fix(objectql,rest,metadata-core)!: a datasource definition's stored row is served only to datasource administrators, whichever exit reads it (#27884)
8
+
9
+
Clause-②: no (narrowing for readers below datasource administration)
10
+
Metadata-protocol: none
11
+
12
+
<!-- adr-0087: not-required (no-migration-prescription) No key of any schema is added, removed, renamed or re-shaped, and no metadata an author writes changes meaning: a stored datasource definition is written, stored and served to its administrators exactly as before. What narrows is which readers a stored row of a capability-gated type is served to. There is nothing for a migration to rewrite and no tombstone; a reader who needs the definition is granted the capability. -->
13
+
14
+
**BREAKING** read-set narrowing for callers below datasource administration. The metadata read door already served a `datasource` or `external_catalog` definition only to holders of `manage_platform_settings`, the capability the datasource family's own door requires. The stored row each definition lives in now admits the same readers, so one document admits one set of callers at every exit.
15
+
16
+
-**One row.** The type-to-capability table moves to `@objectstack/metadata-core` as `STORED_METADATA_TYPE_READ_CAPABILITIES`, with `storedMetadataTypesWithheldFrom(caller)` and `isCapabilityGatedStoredMetadataType(type)`. `@objectstack/rest` keeps exporting `META_TYPE_READ_CAPABILITIES`, now that same frozen object, and the metadata read door answers exactly as before.
17
+
-**Now.** A caller-context read of a stored metadata row of a listed type, by a caller without the type's capability, is served no row: a list or count leaves it out, and a read of its id answers as a read of a row that does not exist. Holders of the capability, platform administrators among them, are served everything as before. System reads are unchanged.
18
+
-**Events.** A record-change event of such a row carries no metadata body to any subscriber; the row's other columns are unchanged.
19
+
-**Who sees it.** A reader below datasource administration is no longer served stored datasource or federation-catalog rows.
20
+
21
+
**The remedy.** Grant `manage_platform_settings` to a caller that needs a datasource definition, or read it through the datasource admin door (`GET /api/v1/datasources/:name`) as such a caller.
fix(runtime)!: the connector connection door's begin and revoke legs refuse a delegated or agent caller by name; connecting and revoking an organization's connector is a human administrator's act
6
+
7
+
Clause-②: no (narrowing) — `POST` and `DELETE /api/v1/automation/connectors/:name/connection` refuse a delegated or agent caller by name: one admitted by an API key or an OAuth access token, or carrying an agent envelope (`principalKind: 'agent'`, `onBehalfOf`). Nothing is widened.
8
+
Metadata-protocol: none
9
+
10
+
<!-- adr-0087: not-required (no-migration-prescription) No key of any schema is added, removed, renamed or re-shaped, and no metadata an author writes changes meaning: one HTTP door narrows which callers may take two of its steps. The refusal is the door's existing 403 PERMISSION_DENIED with a sentence naming the caller's kind, so there is nothing for a migration to rewrite and no tombstone. -->
11
+
12
+
**BREAKING** caller narrowing at the connector connection door (`@objectstack/runtime`, ADR-0143 D5).
13
+
14
+
-**The rule.** The begin leg (connect, reconnect) and the revoke leg refuse a delegated or agent caller, whatever it holds, with `403 PERMISSION_DENIED` and a message that names its kind (an agent acting on a person's behalf, an OAuth access token, an API key) and the rule. D5 binds a connection to the session that began it, so connecting and revoking is taken by a person signed in.
15
+
-**Unchanged.** Who holds the capability (platform administrators, the organization-administrator sets, explicit grants), the system context's bypass, the read leg (`GET …/:name/connection`) and the OAuth callback.
16
+
-**Who sees it.** Any caller on these two steps that is not a person signed in.
17
+
18
+
**The remedy.** Take the step from a signed-in session (the Console, or a session token), as the administrator who holds the capability.
keyId: '2021000000000000', // the app id: public, stays in metadata
649
+
credentialRef: 'ALIPAY_APP_PRIVATE_KEY', // the application private key (RSA)
650
+
vendorPublicKeyRef: 'ALIPAY_PUBLIC_KEY', // the Alipay public key responses are verified with
651
+
}
638
652
```
639
653
654
+
Both of an `alipay-v3` instance's references are listed against it by the
655
+
credential door, and setting or replacing either one re-materializes it. The
656
+
keys are accepted as Alipay's console issues them (the bare base64 body) or as
657
+
PEM.
658
+
640
659
What the platform does with it:
641
660
642
661
-**Every request is signed, each attempt fresh.** A retry is signed again with
@@ -648,7 +667,8 @@ What the platform does with it:
648
667
(`external.connector` is refused with `422`): a driver cannot sign per
649
668
request.
650
669
-**Schemes that sign their responses are verified** before a result is
651
-
returned; a response that does not verify fails the dispatch.
670
+
returned; a response that does not verify fails the dispatch, and its body
671
+
never reaches the flow.
652
672
-**`aliyun-acs3-hmac-sha256`** signs `host`, `content-type` and every
653
673
`x-acs-*` header, and sets `x-acs-date`, `x-acs-signature-nonce`,
654
674
`x-acs-content-sha256` and `Authorization`. The API's own action and version
@@ -659,9 +679,16 @@ What the platform does with it:
659
679
the first label of the endpoint host (`cvm.tencentcloudapi.com`, or a
660
680
regional `cvm.ap-guangzhou.tencentcloudapi.com`, is `cvm`), so point
661
681
`baseUrl` at the product's own endpoint. `X-TC-Action`, `X-TC-Version` and
662
-
`X-TC-Region` are yours to send and are not signed. A request without a body
663
-
is sent with `Content-Type: application/x-www-form-urlencoded`, the type it
664
-
is signed with.
682
+
`X-TC-Region` are yours to send and are not signed. A request without a
683
+
content type is sent with `Content-Type: application/x-www-form-urlencoded`,
684
+
the type it is signed with.
685
+
-**`alipay-v3`** signs the method, the path and query as sent and the body,
686
+
with a fresh nonce and millisecond timestamp, and sets `Authorization:
687
+
ALIPAY-SHA256withRSA app_id=…,nonce=…,timestamp=…,sign=…`. Every response
688
+
below status 400 must carry a valid `alipay-signature` (an empty body too),
689
+
checked against the key `vendorPublicKeyRef` names; an error response is
690
+
verified when it is signed, and an unsigned one fails as the error it is.
691
+
Public-key mode only: Alipay's certificate mode is not supported.
665
692
666
693
What the publish door refuses:
667
694
@@ -670,9 +697,11 @@ What the publish door refuses:
670
697
signer and its test vectors, so a scheme the server cannot run is never
671
698
accepted.
672
699
-**An inline key**: `secret`, `privateKey`, `secretKey` or `accessKeySecret`
673
-
is refused by name, pointing at `credentialRef`. The vendors' words for the
674
-
key id (`accessKeyId`, `secretId`) are answered with `keyId`. An authored `signature` is
675
-
refused too: it is computed for every request.
700
+
is refused by name, pointing at `credentialRef` (on `alipay-v3`,
701
+
`appPrivateKey` too, and `alipayPublicKey` points at `vendorPublicKeyRef`).
702
+
The vendors' words for the key id (`accessKeyId`, `secretId`, `appId`) are
703
+
answered with `keyId`. An authored `signature` is refused too: it is computed
704
+
for every request.
676
705
-**`provider: 'mcp'`**, at `auth.type`: an MCP server is not signed per
677
706
request. Declare `provider: 'rest'` or `provider: 'openapi'`.
678
707
@@ -736,7 +765,9 @@ retry):
736
765
| An `authorization_code` instance nobody connected, or whose grant the vendor refused |**Not fatal**: the instance registers **degraded**, "not connected" or "reconnect required" — connect it (`POST …/connectors/:name/connection`), and it materializes without a restart |
737
766
| The token endpoint is unreachable or answers `5xx` / `429`|**Not fatal**: the dispatch fails `CONNECTOR_UPSTREAM_UNAVAILABLE` and is retried on the existing backoff; the connection stays connected |
738
767
|`auth` is `signed` on a provider that does not say it applies the request signer |**Hard boot error** naming the instance, the scheme and the provider: its calls would go out unsigned — declare it through `rest` or `openapi`|
739
-
|`credentialRef` resolves to nothing |**Not fatal**: the instance registers **degraded**, naming the reference — set it (`PUT …/credential-refs/:ref`), and it materializes without a restart |
768
+
|`credentialRef` (or a `signed` instance's `vendorPublicKeyRef`) resolves to nothing |**Not fatal**: the instance registers **degraded**, naming the reference — set it (`PUT …/credential-refs/:ref`), and it materializes without a restart |
769
+
| A `signed` reference resolves to something that is not a key of the scheme's kind (an `alipay-v3` key that is not RSA) |**Hard boot error** naming the instance and which reference cannot sign or verify, never the value |
770
+
| A response to an `alipay-v3` call is unsigned (below status 400) or its signature does not verify | The dispatch **fails** naming the instance and the status; the response's body is never returned |
740
771
| The upstream refuses the credential (`401` / `403`) |**Not fatal**: the instance registers **degraded**. A stored credential is marked `rejected` and waits until it is set again (or a restart); an environment or vault credential is retried on the backoff |
741
772
| Instance `name` collides with a plugin-registered connector |**Hard boot error** — there is no silent precedence between the two worlds |
742
773
| stdio transport without host opt-in |**Hard boot error** with the `declarativeStdio` fix inline — a security rejection is never retried |
Copy file name to clipboardExpand all lines: content/docs/permissions/system-context.mdx
+2Lines changed: 2 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -122,6 +122,7 @@ bugs — it is the protection or the side effect that silently does not happen.
122
122
| 18 | REST anonymous-deny seam satisfied | rest | Get: `enforceAuth` passes with no `userId`. Not reachable from the wire — `isSystem` is never set on an inbound request |`enforceAuth`|
123
123
| 18b | Settings namespace read scope not applied | service-settings | Get: a read of `sys_setting`, `sys_setting_audit` or `sys_platform_setting` across every namespace, whatever each namespace's `readPermission` names — the settings service's own reads of its stores take this path. Lose: the per-namespace read gate the generic data API applies to every other caller, the same rule the settings routes enforce |`settingsReadDoorMiddleware`|
124
124
| 18c |**Credential-column filter / sort refusal not applied** — a `where`, `orderBy`, per-aggregation `filter` or `expand` entry naming a `password` / `secret` column is evaluated | objectql | Get: the server's own match on a stored credential value, a lookup that never returns it. Every other caller, administrators included, is refused `400 INVALID_FIELD` for anything but a has-a-value check (ADR-0100). Lose: nothing a wire caller reaches — `isSystem` is never set on an inbound request |`rejectCredentialEvaluation`|
125
+
| 18d |**Stored-metadata type read gate not applied** — a read of `sys_metadata` / `sys_metadata_history` is not narrowed by the capability its row's type requires | objectql | Get: every row of the family on `find` / `findOne` / `count` / `aggregate` — the metadata loader, the `/meta` doors' own store reads, boot (a read with no execution context is not narrowed either). Lose: the narrowing that serves a stored row of a capability-gated type (`datasource`, `external_catalog`) only to holders of the capability the `/meta` type gate names for it |`storedMetadataTypeReadFilter`|
125
126
126
127
### 2. Write pipeline and data integrity
127
128
@@ -543,6 +544,7 @@ and CI refuses one that differs from what the tree produces.
0 commit comments