✨ PUT-1412 File sharing backend api - #3553
Draft
jfcastro92 wants to merge 21 commits into
Draft
Conversation
…-flatten-driver-permissions-to-hardcoded-values
…t the linked grant row before the flat view
A share is two writes that belong together: the permission grant, which authorizes access, and a share row, which makes it listable and ties it to an fsentry so it dies with the file. Nothing else grants fs:* to a user. Authorization reuses canManagePermission — an owner satisfies it through the is-owner implicator, a delegate through an explicit manage:fs:<uid> grant. An owner may clear any issuer's share of their node; anyone else only the ones they issued, or their own access. Self-revoke skips the manage gate but still requires `see`, so it cannot be used to probe for files. The per-day limit counts shares created rather than live rows, so revoking and re-sharing cannot recycle a slot, and changing an existing share's mode is not new reach and does not spend budget. Tunable via share_daily_limit.
POST /share, POST /share/revoke, GET /share/shared-with-me, GET /share/shares. The controller was registered but entirely commented out. Recipients × items fan out concurrently — every pair is a distinct (holder, entry) key, so none of them contend — bounded by runWithConcurrencyLimitSettled, which returns results index-aligned with the input for the per-pair outcome list. Responses carry usernames only, never internal ids, and the 404-not-403 rule is preserved so a failed call cannot confirm a file the caller could not otherwise see. Notifications are fired off the response path; a share must not fail over its own notification. Per-request caps on recipients and items bound one call's fan-out; the daily limit bounds the total.
…ontainment on shared paths
signFile defaults to a ~317k-year TTL and verifySignature checks only uid, expires and signature — never the ACL. A recipient who ever signed a shared file therefore held a permanent, revocation-proof URL to its bytes: revoking the share did nothing to it. signEntry now takes the acting user and drops to NON_OWNER_SIGNATURE_TTL_SECONDS (1 hour) when the signer is not the entry's owner. Owners keep the permanent default, so no existing client changes behavior. The signature-authenticated directory listing bounds its children unconditionally: that route has no session actor, and a signature proves possession rather than ownership, so a recipient holding a short-lived directory signature could otherwise mint permanent URLs for every child. A bounded window is not revocation — the durable fix is a per-entry signature epoch folded into the HMAC and bumped on any permission change.
listUserPermissionIssuers and its store method listUserPermissionIssuerIds existed to synthesize the filesystem root from the home directories of everyone who had granted the caller a permission. That listing is gone — it advertised folders readdir then refused to open — and the share index answers "who shared with me" directly, so nothing wants them back. One removed test only asserted that the call returned an array; the other covered readLinkedUserUserPerms round-tripping and is kept, rewritten without the issuer lookup.
share(), unshare(), listShared() and getShares() on puter.fs, following the existing FS operation shape: positional and options-object forms through defineOperation, JSDoc overloads as the published signature, relative paths resolved against the app's root directory. A bare recipient string is read as an email when it contains @ and as a username otherwise. Sharing an item with someone who already has it replaces their access rather than stacking a second grant, so raising read to write is one more call. Adds a sharing suite to the API runner, which passes unchanged on node, browser and workerd. Documents all four methods with runnable examples, and corrects the FS overview callout that told readers one user cannot read another's files — true before this, not after.
A sidebar entry listing everything other users have shared with you, backed by puter.fs.listShared(). The path is the sentinel `puter://shared` rather than /<user>/Shared: this is a query, not a directory, and a path-shaped value could collide with a folder someone actually creates. refresh_item_container and update_window_path both branch on it to skip the stat there is no fsentry for, and the listing swaps readdir for listShared. Entries render at their real paths under their owners' directories — the item container already preferred an explicit fsentry.path over joining onto the container, so nothing else had to change. Each carries who shared it and at what level, which the context menu reads next.
A sharing dialog shaped like its neighbours — options object, HTML-string template, jQuery wiring, delegating to UIWindow() — with a recipient field, a read/edit/share dropdown, and the current access list with revoke buttons. Reached from a new "Share…" context menu entry, which is hidden on items shared *with* you: re-sharing needs manage, so the dialog would only surface an error. Those items get "Remove from Shared" in place of Delete. Delete moves an item to *your* trash, which for someone else's file means moving their data out of their tree — FSService refuses it, and the user saw a bare 403. Removing your own access is what the action was reaching for, so that is what it now does.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.