Files: public and private objects share one key layout, so no bucket policy or CDN can serve only public files
- Dominant language
- C#
- Stars
- 6
- Forks
- 7
- Avg merge
- 4h 42m
- Merged PRs (30d)
- 307
Description
Public and private files share one key layout in object storage, so nothing in front of the bucket can serve only the public ones.
## Where
- `BarakoCMS.Files/Features/Upload/Endpoint.cs` builds every key as `{Guid.NewGuid():N}{ext}`, whether `isPublic` is true or false.
- `BarakoCMS.Files.S3/S3FileStorage.cs` sets `S3CannedACL.PublicRead` on a public upload only when `UsePublicReadAcl` is true, and returns a public URL only when `PublicBaseUrl` is set.
- `GET /api/public/files/{id}` refuses a private file, then redirects to the public URL if there is one, and otherwise reads the object and streams it.
## Why it matters
Visibility lives in the database row and, optionally, in a per-object ACL. Most S3-compatible stores ignore object ACLs (Cloudflare R2, SeaweedFS, Garage), and AWS buckets created today have ACLs disabled by default. The remaining ways to give public files a direct URL all act on the whole bucket or a key prefix:
- a bucket policy granting `s3:GetObject`
- a CDN such as CloudFront with origin access, or an R2 public bucket
With one flat layout, any of those exposes private files to anyone who has a key. Keys are random, so this is not guessable today, but a key reaching a log, a referrer or an export is enough, and "private" should not depend on that.
So the only safe setting right now is what #774 documents: `UsePublicReadAcl=false`, no `PublicBaseUrl`, and every public file streamed through the API, which gives up the CDN URL the module exists to provide.
## Proposal
Put visibility in the key: `public/{guid}{ext}` and `private/{guid}{ext}`. A bucket policy or CDN origin can then be scoped to `public/*` and nothing else.
- New uploads use the prefix. Existing objects keep their stored key, since `StorageKey` is already read from the row, so no migration is required for reads.
- Changing a file's visibility after upload does not exist today; if it is added, it must move the object between prefixes.
- Document the scoped policy for AWS S3 and the CloudFront origin setup in the Files.S3 README.
## Done when
- A test uploads one public and one private file and asserts their keys start with `public/` and `private/`.
- The README shows a bucket policy on `public/*` only, and says that without the prefix no bucket-wide public access is safe.
Contributor guide
Research direction
Start with BarakoCMS.Files/Features/Upload/Endpoint.cs and BarakoCMS.Files.S3/S3FileStorage.cs, then inspect the GET /api/public/files/{id} path and the Files.S3 README. Add coverage for public and private key prefixes, and document an AWS policy and CloudFront setup scoped to public/*; done means existing stored keys still read and the README explains why bucket-wide access is unsafe.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- aws, csharp
- Domain
- backend, cloud, documentation, security, testing
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 72/100