refs #10078
[arvados.git] / apps / workbench / app / controllers / collections_controller.rb
index cadef383dd265e2eafb4d6e868509f886c4e64d4..20b227c3c7277d491c74b96c0f5de7bc415c0f4c 100644 (file)
@@ -1,6 +1,5 @@
 require "arvados/keep"
 require "uri"
-require "cgi"
 
 class CollectionsController < ApplicationController
   include ActionController::Live
@@ -137,7 +136,10 @@ class CollectionsController < ApplicationController
       return
     end
 
-    if Rails.configuration.keep_web_url
+    # If we are configured to use a keep-web server, just redirect to
+    # the appropriate URL.
+    if Rails.configuration.keep_web_url or
+        Rails.configuration.keep_web_download_url
       opts = {}
       if usable_token == params[:reader_token]
         opts[:path_token] = usable_token
@@ -148,10 +150,13 @@ class CollectionsController < ApplicationController
         # to read the collection.
         opts[:query_token] = usable_token
       end
-      opts[:disposition] = params[:disposition]
+      opts[:disposition] = params[:disposition] if params[:disposition]
       return redirect_to keep_web_url(params[:uuid], params[:file], opts)
     end
 
+    # No keep-web server available. Get the file data with arv-get,
+    # and serve it through Rails.
+
     file_name = params[:file].andand.sub(/^(\.\/|\/|)/, './')
     if file_name.nil? or not coll.manifest.has_file?(file_name)
       return render_not_found
@@ -248,13 +253,6 @@ class CollectionsController < ApplicationController
         @permissions = Link.limit(RELATION_LIMIT).order("modified_at DESC")
           .where(head_uuid: @object.uuid, link_class: 'permission',
                  name: 'can_read').results
-        @logs = Log.limit(RELATION_LIMIT).order("created_at DESC")
-          .select(%w(uuid event_type object_uuid event_at summary))
-          .where(object_uuid: @object.uuid).results
-        @is_persistent = Link.limit(1)
-          .where(head_uuid: @object.uuid, tail_uuid: current_user.uuid,
-                 link_class: 'resources', name: 'wants')
-          .results.any?
         @search_sharing = search_scopes
 
         if params["tab_pane"] == "Used_by"
@@ -325,24 +323,55 @@ class CollectionsController < ApplicationController
   end
 
   def keep_web_url(uuid_or_pdh, file, opts)
-    fmt = {uuid_or_pdh: uuid_or_pdh.sub('+', '-')}
-    uri = URI.parse(Rails.configuration.keep_web_url % fmt)
+    munged_id = uuid_or_pdh.sub('+', '-')
+    fmt = {uuid_or_pdh: munged_id}
+
+    tmpl = Rails.configuration.keep_web_url
+    if Rails.configuration.keep_web_download_url and
+        (!tmpl or opts[:disposition] == 'attachment')
+      # Prefer the attachment-only-host when we want an attachment
+      # (and when there is no preview link configured)
+      tmpl = Rails.configuration.keep_web_download_url
+    elsif not Rails.configuration.trust_all_content
+      check_uri = URI.parse(tmpl % fmt)
+      if opts[:query_token] and
+          not check_uri.host.start_with?(munged_id + "--") and
+          not check_uri.host.start_with?(munged_id + ".")
+        # We're about to pass a token in the query string, but
+        # keep-web can't accept that safely at a single-origin URL
+        # template (unless it's -attachment-only-host).
+        tmpl = Rails.configuration.keep_web_download_url
+        if not tmpl
+          raise ArgumentError, "Download precluded by site configuration"
+        end
+        logger.warn("Using download link, even though inline content " \
+                    "was requested: #{check_uri.to_s}")
+      end
+    end
+
+    if tmpl == Rails.configuration.keep_web_download_url
+      # This takes us to keep-web's -attachment-only-host so there is
+      # no need to add ?disposition=attachment.
+      opts.delete :disposition
+    end
+
+    uri = URI.parse(tmpl % fmt)
     uri.path += '/' unless uri.path.end_with? '/'
     if opts[:path_token]
       uri.path += 't=' + opts[:path_token] + '/'
     end
     uri.path += '_/'
-    uri.path += CGI::escape(file)
+    uri.path += URI.escape(file)
 
-    query_params = []
+    query = Hash[URI.decode_www_form(uri.query || '')]
     { query_token: 'api_token',
       disposition: 'disposition' }.each do |opt, param|
-      if opts[opt]
-        query_params << param + '=' + CGI::escape(opts[opt])
+      if opts.include? opt
+        query[param] = opts[opt]
       end
     end
-    unless query_params.empty?
-      uri.query = query_params.join '&'
+    unless query.empty?
+      uri.query = URI.encode_www_form(query)
     end
 
     uri.to_s