22
33class UploadsController < ApplicationController
44 rescue_from ActionController ::MissingFile , with : :raise_not_found_exception
5- rescue_from SecurityError , with : :raise_not_found_exception
6- rescue_from CanCan ::AccessDenied , with : :raise_not_found_exception
5+ rescue_from SecurityError , with : :log_and_raise_not_found_exception
6+ rescue_from CanCan ::AccessDenied , with : :log_and_raise_not_found_exception
77
88 MODELS_OVERRIDES = {
99 "operator_document_file" => "document_file" ,
@@ -15,6 +15,7 @@ def download
1515 parse_upload_path
1616 ensure_valid_db_record
1717 track_download if trackable_request?
18+ check_authorization! if needs_authorization?
1819 send_file @sanitized_filepath , disposition : :inline
1920 end
2021
@@ -79,6 +80,29 @@ def ensure_valid_filename
7980 end
8081 end
8182
83+ def cookie_download_users
84+ cookies
85+ . select { |name , _v | name . ends_with? ( "download_user" ) }
86+ . map do |name , download_token |
87+ payload = Rails . application . message_verifier ( "download_token" ) . verify ( download_token )
88+ User . find_by ( id : payload [ "user_id" ] )
89+ rescue ActiveSupport ::MessageVerifier ::InvalidSignature
90+ nil
91+ end . compact
92+ end
93+
94+ def check_authorization!
95+ raise SecurityError unless download_users . any? { it . can? ( :download_protected , @record ) }
96+ end
97+
98+ def download_users
99+ [ current_user , *cookie_download_users ] . compact
100+ end
101+
102+ def needs_authorization?
103+ @uploader . protected?
104+ end
105+
82106 def allowed_models
83107 uploads_root = ApplicationUploader . new . root . join ( "uploads" )
84108 Dir . entries ( uploads_root )
@@ -151,7 +175,14 @@ def allowed_directory
151175 Rails . env . test? ? File . join ( Rails . root , "tmp" , "uploads" ) : File . join ( Rails . root , "uploads" )
152176 end
153177
178+ def log_and_raise_not_found_exception
179+ msg = "Unauthorized file download attempt: user_id=#{ current_user &.id } , path=#{ @sanitized_filepath } "
180+ Rails . logger . warn ( msg )
181+ Sentry . capture_message ( msg ) if ENV [ "SENTRY_LOG_UNAUTHORIZED_DOWNLOADS" ] == "true"
182+ raise_not_found_exception
183+ end
184+
154185 def raise_not_found_exception
155- raise ActionController ::RoutingError , "Not Found "
186+ raise ActionController ::RoutingError , "Not found or your download session has expired (try clicking on the link again) "
156187 end
157188end
0 commit comments