Private media / uploads

Is there an established method of scoping permissions to media files managed by the upload plugin?

My use case is to have a series of “attachments” associated with a content type, which would ideally be stored in an S3 bucket. Only users with permission to view the content type should have permission to access the “attached file”, which might be a PDF, image, or other arbitrary file.

I would like to leverage the upload plugin, but, to handle permissions, proxy S3 requests through the Strapi server.

Is there an existing plugin for this, or method of using the standard upload and media support, that would support something like this?

Any pointers would be very much appreciated!

2 Likes

I don’t believe we currently have the code to pass along permissions to the file over to AWS-S3 at the moment (nor do we have file based permissions on the local uploaded files).

I don’t really have any suggestions on this :thinking:

@DMehaffy Darn, that’s what I’m coming to myself, perusing through the Strapi source code…

One thought I’ve had is that files might be base64 encoded and stored in a string field in the database (or perhaps the db layer could have blob support added). It wouldn’t be efficient for heavy loads, but seems like it would work.

Thoughts on putting files in the Strapi-managed db? One concern I’ve had with that is that it would be preferable to not, by default, return such content attributes on normal GET requests - it would be nice to support downloading them with a separate API call. Not sure if that is possible…

1 Like

Hey i’m searching for a solution for the same problem. Currently i’m trying to rewrite the upload provider and creating a plugin, which handles ‘private uploads’, because i found nobody having the same issue except you. But now i’m failing using the strapi upload plugin inside of my plugin… I though that i could use some of the service functions, but there is much more to do. Next step would be either copying the same functionality of the upload plugin into my custom plugin or customize the strapi upload plugin directly.

If you found a clean solution please feel free to share the method.

I have a solution to this that is suitable to both S3 and DigitalOcean spaces (as well as any other provider that uses S3 style object storage).

Firstly, as has been established here, and in this post (Strapi.service outside api?), strapi does not use it’s own service functions when creating a new content. This is legitimately buckwild to me because of how opinionated the architecture is but there you go.

  1. Integrate S3/DO upload as normal. This is going to upload your files, as normal to you object storage service, and store them publicly.
  2. Webhook onto the creation of the content, ideally onto entry.create. Here you can create an S3 client instance, hop onto your S3 and change the previously uploaded public ACL to private.
  3. Update your service.find function (and similar) to intercept, and change the existing URL (which is private) to a SignedUrl (which can be made public for up to 7 days).

The major flaw in this approach is that by making the file private on creation you exclude the editor from accessing it. Technically the file’s hash and extension still compose to a valid object in the bucket, but you’d need to implement custom logic to essentially presign URLs every time the editor edits it.

Now in some cases this is fine. A CMS user will chuck in a file/video whatever, and only ever access it through the public CMS API which will, based on this logic, always deliver a fresh Pre-signed URL to the content, but if your CMS user frequently checks the content via the editor, then you’ll need to override the editors endpoints as well.

I haven’t done this because it’s not really in scope of what I’m doing.

…Of course a simple fix would be if Strapi used the services it makes us implement :wink:

It’s a much larger problem than that, I’ve already suggested something but we still have a lot of discussion to do before we change the logic.

The core issue is S3/whatever has no method to authenticate Strapi users so we have to proxy the files through the Koa server (and using the SDKs for each to fetch and not just upload/delete, leading to more complex upload providers).

The largest problem is that we are still using buffers and not streams which is why we have problems like: Multiple Image Uploads result in memory leak · Issue #6432 · strapi/strapi · GitHub

The upload plugin is due for a major refactoring in the future and this is one of the topics that I plan to raise with our Product and Engineering teams. (same for folders in the media lib)

1 Like

I mean from a fully functional perspective, yes. What I’m proposing here is a hack, where you modify the API such that the response content includes presigned URLs, that you sign using custom controller logic, whilst also applying private to the ACL of any files when new content is created. It only really affects the Strapi editor if a user of the CMS attempts to access media via the editor.

Again it’s not ideal, but from a front-end user perspective, it provides the desired authentication control

Any update on this wrt logic?

No, not at this time and I’m not sure if it’s something we can introduce without breaking changes.

Any update on this logic in V5?