Repository navigation
Conversation
… (RED) Reproduction from the N1b design: a policy owner holding only kv/get and kv/metadata on the content registers a Policy/v3 whose source network is named after itself and was never created. Today registration is accepted.
…RED) Foreign, missing, inactive, chain-mismatched and self-named networks, every ceiling network, the legacy source form, and the Share/D3 did:pkh shapes over the real /delegate, /encryption/networks and registration routes. Adds the refusal counter the tests read.
… at registration
Every encryption network a new Policy/v3 names (the content source's and each
encryption ceiling entry) must parse, belong to the owner of the content
source's space (principal match: EIP-55 case-insensitive, chain id exact) and
be active on this node; otherwise registration returns
403 content-source-network-invalid. New registrations with a legacy
tinycloud:// kvResource are refused the same way. Refusals are counted in
tinycloud_policy_source_network_refused_total{reason}.
The Policy/v2 flow fixtures now create the content owner's network they
register against.
…istence lookup content_owner_networks (parse, owner, legacy form) is a pure function so stored registrations can be classified with the same rule; source_network_refusal adds the existence and state lookup for new registrations, after every network has passed the owner check.
|
For whichever of #358 and this PR merges second: the fixture change for #358's harness
diff --git a/tinycloud-node-server/src/policy_v3/owner_authority_tests.rs b/tinycloud-node-server/src/policy_v3/owner_authority_tests.rs
index 9911c5b..648dbb7 100644
--- a/tinycloud-node-server/src/policy_v3/owner_authority_tests.rs
+++ b/tinycloud-node-server/src/policy_v3/owner_authority_tests.rs
@@ -483,6 +483,18 @@ impl Harness {
);
let network = format!("urn:tinycloud:encryption:{}:mainnet", space_owner.did());
let client = Self::boot_app(&db, &vector, &issuer, &boot).await?;
+ // Registration requires the source network to exist and be the
+ // content owner's (TC-1423 N1b).
+ client
+ .rocket()
+ .state::<tinycloud_core::encryption_network::EncryptionService>()
+ .expect("encryption service")
+ .create_one_of_one_network(tinycloud_core::encryption_network::CreateNetworkRequest {
+ name: "mainnet".into(),
+ owner_did: space_owner.did().to_string(),
+ threshold: tinycloud_core::encryption_network::Threshold::one_of_one(),
+ })
+ .await?;
let harness = Self {
client,
db,Follow-up for the N1 builder (#358, head 09d7e33): review item m3. It isn't part of this PR. The goal is to classify Policy/v3 rows registered before N1b with the same predicate that N1b applies at registration (parse, owner and legacy form, but not existence). A stored policy whose networks fail it becomes Change in
|
|
Review of B1 (major): a second The shape:
The network check passes, because K's network is compared with K's space. The owner check passes too. Observed at this head, with the existing
This is the same outcome as the original reproduction at 989078c. Suggested fix: in for capability in policy.get("capabilityCeiling").and_then(Value::as_array).into_iter().flatten()
.filter(|c| c.get("kind").and_then(Value::as_str) == Some("kv"))
{
let value = capability.get("resource").and_then(Value::as_str).unwrap_or_default();
if value.starts_with("tinycloud://") { return Err(SourceNetworkRefusal::LegacyResource); }
let space = value.parse::<ResourceId>().map_err(|_| SourceNetworkRefusal::Unparsable)?;
if !did_principal_matches(space.space().did().as_str(), content_owner.as_str()) {
return Err(SourceNetworkRefusal::ForeignOwner);
}
}With this added:
Released clients send exactly one kv entry, equal to Please add regression tests: the registration case in Verified as described:
Details: TC-1423 (Linear). |
From the #363 review (B1): a kv-only key moves the content source to its own space on a network it created, and adds a second kv ceiling entry on the space owner's content. Registration is accepted at 1bfa6cf. Regression tests in the Policy/v2 flow and the registration harness, plus controls: the key sharing its own space, several kv entries all in the owner's spaces, and legacy or unparsable kv entries.
… owner Review B1 on #363: the network check bound the networks to the owner of contentSource.kvResource only, so a kv-only key could move the source to its own space on its own network and add the space owner's content as a second kv entry. content_owner_networks now also requires every kv ceiling entry to be non-legacy, to parse, and to name a space of the content source's owner (refusal reason foreign_space); a ceiling entry of any other kind fails it. The predicate stays pure, so N1's classifier reuses it unchanged.
|
Review B1 addressed at
Updated m3 note (supersedes the one above): Follow-up for the N1 builder (#358, head 09d7e33): review item m3. It isn't part of this PR. The goal is to classify Policy/v3 rows registered before N1b with the same predicate that N1b applies at registration (parse, network owner, every kv ceiling entry in a space of the content owner, and legacy form, but not existence). A stored policy whose networks fail it becomes Change in
|
|
APPROVE: see TC-1423 |
|
APPROVE: see TC-1423 (reviewed exact head 86fc01b) |
Check the owner and existence of policy source networks at registration (TC-1423).
Change
POST /policy/v3/policieschecks every encryption network a new policy names:contentSource.encryptionNetworkand eachencryptioncapability-ceiling entry. Each one must:urn:tinycloud:encryption:<ownerDid>:<name>);ResourceId::space().did()ofcontentSource.kvResource, compared withdid_principal_matches, so EIP-55 letter case is ignored and the chain id must match;active. The lookup uses theencryption_networkrow key and the sameactiverule as decrypt.Every
kvceiling entry must also name a space of that same owner, so that every resource a policy grants belongs to one owner. A ceiling carries onlykvandencryption(decrypt) entries:registration_projectionsrefuses any other kind with 400, and the check fails closed on one too.New registrations with a legacy
tinycloud://kvResourceorkvceiling entry are refused too. No client emits that form, and it names no space owner to compare with.Every refusal returns
403 content-source-network-invalidand writes nothing. It is counted intinycloud_policy_source_network_refused_total{reason}, wherereasonis one ofunparsable,legacy_resource,foreign_owner,foreign_space,missingorinactive. There are no per-owner labels.content_owner_networks), which record owner authority for policy registrations #358 uses to classify stored rows.Compatibility (code-read: js-sdk
origin/master129c793, shareorigin/mainc6488e7)urn:tinycloud:encryption:did:pkh:eip155:1:<address lowercased>:default(src/share/openkey-session.ts:72-75). It registers the node's descriptor spelling of that network (composer.ts:378; sdk-servicesEncryptionService.ts:150-163). The space id is the same did:pkh, checksummed. Both spellings match the space owner here.decrypt+network.createon that URN (openkey-session.ts:97).signInensures the network before any publish (node-sdkTinyCloudNode.ts:2461,:3404-3433,:5971-5995).tc shareregistersgetEncryptionNetworkIdForSpace(ownerSpaceId)=urn:tinycloud:encryption:<space owner DID>:default(cli/src/share/adapters.ts:599-619;TinyCloudNode.ts:4901-4921).encryptToNetworkresolves the network first, so a missing network fails before registration.kvceiling entry, equal tocontentSource.kvResource, and one encryption entry, equal to the source network (share-sdk/src/addressed-publish.ts:333-336; the CLI and Share both publish through it). TheirkvResourceis the ordinarytinycloud:<did>:<space>/kv/<path>form; neither repo emitstinycloud://.@tinycloud/node-sdk3.3.0-beta.7 and@tinycloud/share-sdk1.1.1, usingpublishAddressedSharewith Share's manifest. The results are under Verification.Tests
policy_v3::tests::policy_source_network_must_belong_to_the_content_owner: a policy owner with kv get + metadata only registers over a network named after itself that does not exist. Refused.policy_v3::source_network_testsuses the real/delegate,/encryption/networksand registration routes:pending,rotatingandrevokednetworks (andactivepasses);tinycloud://source;:defaultURN in the CACAO and the policy, and the network created through the route with the SDK's checksummed invocation. Passes;:shareon a did:pkh owner (passes);eip155:137vs:1);postgres_source_network_check, which runs the same lookups on PostgreSQL whenTINYCLOUD_TEST_POSTGRES_URLis set.policy_v3::tests::policy_kv_entries_must_belong_to_the_content_owner: the source is in the key's own space on its own network, plus a kv entry on another owner's content. Refused with 403.kv_entry_outside_the_content_owner_is_refused: the key creates its network through the route with no proof.own_space_on_own_network_registers,kv_entries_all_owned_by_the_content_owner_register(a second space of the same owner),legacy_kv_entry_is_refused,unparsable_or_unknown_ceiling_entry_fails_the_predicate.:mainnetnetwork they register against. Their test wrappers box the flow future, which had reached the default test-thread stack.Verification (local only; no Docker, no prod)
Test results.
cargo test -p tinycloud-node --lib policy_v3::runs 55 passed, 0 failed (with PostgreSQL).source_network_testsand the flow tests pass on SQLite and PostgreSQL. Run against a tree without the check, the registration tests that expect403get200(five cases), or are refused only later aspolicy-owner-not-authorized(the legacy and unparsable cases); the Share and:sharecases pass either way.Mutation check. Comparing against the space-id string (
resource.space().to_string()) rather than the space DID fails 9 tests, including the Share shape and the three flow tests.Lint and format.
cargo clippy --workspace --all-targets -- -D warningsis clean, andcargo fmt --all -- --checkpasses.cargo test --workspace --no-fail-faston SQLite (CIunset): 1249 passed, 3 failed. The three failures are timing tests intinycloud-coreunder host load average ~50 (PoolTimedOut,database is locked,Elapsed). All three pass when re-run alone.Same, against native PostgreSQL 14 (
TINYCLOUD_TEST_POSTGRES_URL): 1251 passed, 1 failed. The failure ispostgres_space_filter_survives_a_collation_that_ignores_the_sentinel, because PG 14 has no collationrules(CI uses PG 16). It is unrelated to this change.Released SDK (
@tinycloud/node-sdk3.3.0-beta.7,@tinycloud/share-sdk1.1.1,publishAddressedSharewith Share's manifest), against a local node::defaultURNcontent-source-network-invalidpolicy-owner-not-authorizedcontent-source-network-invalidThe publish then stops at the envelope's https-origin check, because the local node is http. Registration is the step under test.
Merge with record owner authority for policy registrations #358. Two additive conflicts: the test-module list and the CHANGELOG. record owner authority for policy registrations #358's
owner_authority_testsregister on a:mainnetnetwork they do not create, so record owner authority for policy registrations #358 carries a fixture change (create the network afterboot_app); the patch is in the comment below.policy_v3::tests pass on the merge, on SQLite and PostgreSQL.Related change
#358 classifies stored registrations with
content_owner_networks; the exact change is in the PR comment below.Details: TC-1423