diff --git a/apps/dashboard/app/controllers/concerns/pathable.rb b/apps/dashboard/app/controllers/concerns/pathable.rb new file mode 100644 index 0000000000..57947dceab --- /dev/null +++ b/apps/dashboard/app/controllers/concerns/pathable.rb @@ -0,0 +1,47 @@ +module Pathable + extend ActiveSupport::Concern + + def normalized_path(path) + Pathname.new("/#{path.to_s.chomp('/').delete_prefix('/')}") + end + + def parse_path(path = nil, filesystem = nil) + normal_path = normalized_path(path || resolved_path) + filesystem ||= resolved_fs + if filesystem == 'fs' + @path = PosixFile.new(normal_path) + @filesystem = 'fs' + elsif ::Configuration.remote_files_enabled? && filesystem != 'fs' + @path = RemoteFile.new(normal_path, filesystem) + @filesystem = filesystem + else + @path = PosixFile.new(normal_path) + @filesystem = filesystem + raise StandardError, I18n.t('dashboard.files_remote_disabled') + end + end + + def validate_path! + if posix_file? + AllowlistPolicy.default.validate!(@path) + elsif @path.remote_type.nil? + raise StandardError, "Remote #{@path.remote} does not exist" + elsif ::Configuration.allowlist_paths.present? && (@path.remote_type == 'local' || @path.remote_type == 'alias') + # local and alias remotes would allow bypassing the AllowListPolicy + # TODO: Attempt to evaluate the path of them and validate? + raise StandardError, "Remotes of type #{@path.remote_type} are not allowed due to ALLOWLIST_PATH" + end + end + + def posix_file? + @path.is_a?(PosixFile) + end + + def resolved_path + raise NoMethodError, "Must implement resolved_path in #{self.class.to_s} to use Pathable concern" + end + + def resolved_fs + raise NoMethodError, "Must implement resolved_fs in #{self.class.to_s} to use Pathable concern" + end +end diff --git a/apps/dashboard/app/controllers/files_controller.rb b/apps/dashboard/app/controllers/files_controller.rb index 72dbd662e5..502a9ed40c 100644 --- a/apps/dashboard/app/controllers/files_controller.rb +++ b/apps/dashboard/app/controllers/files_controller.rb @@ -3,6 +3,7 @@ # The controller for all the files pages /dashboard/files class FilesController < ApplicationController include ActionController::Live + include Pathable before_action :strip_sendfile_headers, only: [:fs] @@ -39,8 +40,8 @@ def fs end end - # FIXME: below is a large block that should be moved to a model - # if moved to a model the exceptions can be handled there and + # FIXME: below is a large block that should be moved to a concern (Zipable, perhaps?) + # if moved to a concern the exceptions can be handled there and # then this code will be simpler to read # and we can avoid rescuing in a block so we can reintroduce # the block braces which is the Rails convention with the respond_to formats. @@ -192,41 +193,16 @@ def strip_sendfile_headers request.headers['HTTP_X_ACCEL_MAPPING'] = nil end - def normalized_path(path = params[:filepath]) - Pathname.new("/#{path.to_s.chomp('/').delete_prefix('/')}") + # Required for use with Pathable concern (app/controllers/concerns/pathable.rb) + def resolved_path + params[:filepath] end - - def parse_path(path = params[:filepath], filesystem = params[:fs]) - normal_path = normalized_path(path) - if filesystem == 'fs' - @path = PosixFile.new(normal_path) - @filesystem = 'fs' - elsif ::Configuration.remote_files_enabled? && filesystem != 'fs' - @path = RemoteFile.new(normal_path, filesystem) - @filesystem = filesystem - else - @path = PosixFile.new(normal_path) - @filesystem = filesystem - raise StandardError, I18n.t('dashboard.files_remote_disabled') - end - end - - def validate_path! - if posix_file? - AllowlistPolicy.default.validate!(@path) - elsif @path.remote_type.nil? - raise StandardError, "Remote #{@path.remote} does not exist" - elsif ::Configuration.allowlist_paths.present? && (@path.remote_type == 'local' || @path.remote_type == 'alias') - # local and alias remotes would allow bypassing the AllowListPolicy - # TODO: Attempt to evaluate the path of them and validate? - raise StandardError, "Remotes of type #{@path.remote_type} are not allowed due to ALLOWLIST_PATH" - end + + # Required for use with Pathable concern (app/controllers/concerns/pathable.rb) + def resolved_fs + params[:fs] end - - def posix_file? - @path.is_a?(PosixFile) - end - + def download? params[:download] end diff --git a/apps/dashboard/app/controllers/projects_controller.rb b/apps/dashboard/app/controllers/projects_controller.rb index cb77117e73..958186913a 100644 --- a/apps/dashboard/app/controllers/projects_controller.rb +++ b/apps/dashboard/app/controllers/projects_controller.rb @@ -2,10 +2,17 @@ # The controller for project pages /dashboard/projects. class ProjectsController < ApplicationController + include Pathable + # GET /projects/:id def show project_id = show_project_params[:id] @project = Project.find(project_id) + + parse_path + validate_path! + @files = @path.ls + if @project.nil? respond_to do |format| message = I18n.t('dashboard.jobs_project_not_found', project_id: project_id) @@ -27,6 +34,19 @@ def show end end end + + # GET /projects/:project_id/files/*filepath + def files + @project = Project.find(files_params[:project_id]) + parse_path(files_params[:filepath]) + validate_path! + Rails.logger.debug("\n\n\n==============================================================") + Rails.logger.debug("ProjectsController#files: request: #{request.methods.sort}") + Rails.logger.debug("==============================================================\n\n\n") + @files = @path.ls + + render(partial: 'projects/directory', locals: { project_id: @project.id, path: @path, files: @files }) + end # GET /projects def index @@ -162,6 +182,16 @@ def stop_job private + # Required for use with Pathable concern (app/controllers/concerns/pathable.rb) + def resolved_path + @project&.directory.to_s + end + + # Required for use with Pathable concern (app/controllers/concerns/pathable.rb) + def resolved_fs + 'fs' + end + def templates Project.templates.map do |project| label = project.title @@ -179,6 +209,10 @@ def project_params .permit(:name, :directory, :description, :icon, :id, :template) end + def files_params + params.permit(:project_id, :filepath) + end + def show_project_params params.permit(:id) end diff --git a/apps/dashboard/app/javascript/application.js b/apps/dashboard/app/javascript/application.js index b8639950eb..3b3fc068e6 100644 --- a/apps/dashboard/app/javascript/application.js +++ b/apps/dashboard/app/javascript/application.js @@ -20,6 +20,7 @@ import 'datatables.net'; import 'datatables.net-bs4/js/dataTables.bootstrap4'; import 'datatables.net-select/js/dataTables.select'; import 'datatables.net-plugins/api/processing().mjs'; +import "@hotwired/turbo-rails" import Rails from '@rails/ujs'; diff --git a/apps/dashboard/app/views/layouts/application.html.erb b/apps/dashboard/app/views/layouts/application.html.erb index 531b92fe86..613e185fb8 100644 --- a/apps/dashboard/app/views/layouts/application.html.erb +++ b/apps/dashboard/app/views/layouts/application.html.erb @@ -1,3 +1,4 @@ +<%= return "turbo_rails/frame" if turbo_frame_request? %>
diff --git a/apps/dashboard/app/views/projects/_directory.html.erb b/apps/dashboard/app/views/projects/_directory.html.erb new file mode 100644 index 0000000000..6e719b1aef --- /dev/null +++ b/apps/dashboard/app/views/projects/_directory.html.erb @@ -0,0 +1,21 @@ +<%= turbo_frame_tag "project_files" do %> ++ + | +Type | +Name | +Actions | +Size | +Modified at | +Owner | +Mode | +
---|