Osy#betaRun it on Osyrin: osyrin.com

Reference / Storage

File.IsOwnUpload and File.SaveUpload

File.IsOwnUpload(path) → bool · File.SaveUpload(fileName, bytes) → string

A private upload's path records WHO uploaded it — the signed-in account, or the browser's visit when nobody was signed in. `File.IsOwnUpload(path)` answers whether the caller made it, which a function keeping an upload as the caller's file checks first, so a path that leaks cannot be claimed by another session. `File.SaveUpload` stores bytes as a private upload of the caller, exactly as `Upload(privately: true)` does from a page.

preview2 examples compiled by CIstoragefilessecurityupload

Summary#

A private upload — Upload(privately: true) on a page, a paste or a drop — lands at a path the platform chooses under uploads/, and that path records who made it. The page then hands the path to one of your functions. Before that function keeps the file as the caller's own, it asks:

if (!File.IsOwnUpload(upload.Path)) { throw new ValidationException("Please upload the file again."); }

true means the caller uploaded it — the same signed-in person, or, signed out, the same browser. Anybody else holding the path — a log line, a shared screen, another tab signed in as somebody else — gets false.

Signature#

bool   File.IsOwnUpload(string path)                    // did the CALLER upload this private upload?
string File.SaveUpload(string fileName, byte[] bytes)   // store bytes as the caller's private upload; answers its path

Description#

Needs use Osysharp.Storage;.

  • Who "the caller" is. The signed-in account, or the visit the platform issued the browser when nobody is signed in. Both are tried, so somebody who pasted a screenshot signed out and then signed in is still its uploader. Neither is an argument: you cannot ask on somebody else's behalf.
  • What answers false. Any path the platform did not choose for a private upload (public/…, a path your own code wrote with File.WriteAllBytes), and any upload made by somebody else.
  • File.SaveUpload keeps the file name's extension and nothing else of it. It is refused when there is nobody to record — a background run with no caller writes bytes with File.WriteAllBytes instead. In a test it is how a test stands in for the page's upload: runas the person, save, and hand the path to the function under test.
  • Uploads are limited. One browser (or one signed-in person) may upload 30 files a minute and 200 MB an hour to an app, and one network address 300 files a minute. Past that the upload is refused with 429 and the sentence "Too many files have been uploaded from here in a short time. Wait … and try again.", and nothing is stored. These are the defaults; whoever runs the platform sets its own numbers under Platform:Uploads (UploadsPerCallerPerMinute, MegabytesPerCallerPerHour, UploadsPerAddressPerMinute).

Examples#

app Desk { use Osysharp.Storage; }

using Osysharp.Storage;

[AllowAnonymous]
public Guid Keep(UploadedFile upload) {
  if (!File.IsOwnUpload(upload.Path)) {
    throw new ValidationException("That file was uploaded somewhere else. Please upload it again.");
  }
  var file = File.Create(upload.FileName, upload.ContentType, File.ReadAllBytes(upload.Path));
  File.Delete(upload.Path);
  return file.Id;
}
[Test]
void An_upload_is_its_uploaders() {
  var path = "";
  runas (new Visitor { Id = "visit-a" }) { path = File.SaveUpload("shot.png", [137, 80, 78, 71]); }
  runas (new Visitor { Id = "visit-b" }) { Assert.False(File.IsOwnUpload(path)); }
  runas (new Visitor { Id = "visit-a" }) { Assert.True(File.IsOwnUpload(path)); }
}

See also#

Related

upload

`Upload(onUploaded: Ingest) { … }` is a file picker wearing whatever you put inside it. When someone chooses a file its…

Files (addressing something the app stores)

The platform stores a file two ways, and which one you hold decides how you address it. A PATH-keyed file lives at an…

File.SignedUrl

Mints a temporary, signed URL that lets an authorized browser fetch a PRIVATE app file — one stored outside `public/` —…