Download_endpoint with authorization on nested route

Hello @janko

I have a problem but i’m not trully sure if it’s a bug or i’m using shrine in the wrong way.

I was trying to use the download_endpoint with authorization. This is my uploader

class FileUploader < Shrine
  plugin :instrumentation

  plugin :add_metadata
  plugin :download_endpoint, redirect: true, prefix: "file"
  plugin :pretty_location
end

Then i added the route on routes.rb

Rails.application.routes.draw do
   ...
   resources :catalogs do
     member do
      get "/file/*rest", to: "catalogs#file", as: file'
     end
   end
end

And the controller

class CatalogController < ApplicationController
  ...
  def file
    set_rack_response FileUploader.download_response(request.env)
  end
end

The thing is when trying to generate an url for this nested route

/catalog/123/file/eyJpZCI6ImNhbmRpZGF0ZS...(rest of hashed metadata)

It’s not working, as i’m getting an exception undefined method `post_match’ for nil:NilClass

I trace the error up to the DownloadEndpoint plugin. s i see the problem is that on the download_response method there is a “trick” to find the hashed metadata, but that trick checks that the path info strictly start with the prefix of the uploader. So if i have scoped/namespaces routes or nested route like in this case, it doesn’t work.

I know shrine is not rails based, but not sure how should i resolve this one, or if it’s something that should be fixed on shrine.

There is an additional situation i see, that i can’t keep the original filename if using the redirect: true option (pointing to the direct url instead of streaming the file), but i think that is expected and there is no way to use the original filename. Or a file can be uploaded having the original filename instead of the autogenerated id?

Thanks in advance

Hi @pldavid

Yes, the Shrine.download_response approach requires that the :prefix option matches the actual path prefix of the route. Strange that an explanatory exception wasn’t raised, I was trying to catch that case here.

Since you want a dynamic path and to redirect, you could implement this directly without the download_endpoint plugin:

class CatalogController < ApplicationController
  def file
    catalog = Catalog.find(params[:id])

    redirect_to catalog.file.url(
      # if you're using the S3 storage, you can set the download filename
      response_content_disposition: ContentDisposition.inline("myfilename.jpg")
    )
  end
end

I first got that explanatory exception, but then i update the route to match the validation shrine was doing, and i got the nil exception.

In the end i basically did what you suggest, was not sure if the strict route validation on the download_endpoint plugin was on purpose or was maybe a glitch. But didn’t know the trick on s3 for setting the filename, thanks for that!