Class: CamaleonCmsAwsUploader
- Inherits:
-
CamaleonCmsUploader
- Object
- CamaleonCmsUploader
- CamaleonCmsAwsUploader
- Defined in:
- app/uploaders/camaleon_cms_aws_uploader.rb
Constant Summary
Constants inherited from CamaleonCmsUploader
CamaleonCmsUploader::PRIVATE_DIRECTORY, CamaleonCmsUploader::SVG_EXT_PATTERN
Instance Attribute Summary
Attributes inherited from CamaleonCmsUploader
Instance Method Summary collapse
-
#add_file(uploaded_io_or_file_path, key, args = {}) ⇒ Object
add a file object or file path into AWS server :key => (String) key of the file ot save in AWS :args => (HASH) false, is_thumb: false, where: - same_name: false => avoid to overwrite an existent file with same key and search for an available key - is_thumb: true => if this file is a thumbnail of an uploaded file.
-
#add_folder(key) ⇒ Object
Add a new folder to AWS with :key.
- #after_initialize ⇒ Object
-
#browser_files ⇒ Object
recover all files from AWS and parse it to save into DB as cache.
-
#bucket ⇒ Object
Initialize a bucket with AWS configurations return: (AWS Bucket object).
-
#delete_file(key) ⇒ Object
Delete the
:keyfile in AWS. -
#delete_folder(key) ⇒ Object
Delete a folder in AWS with :key.
- #fetch_file(file_name) ⇒ Object
-
#file_parse(s3_file) ⇒ Object
parse an AWS file into custom file_object.
-
#objects(prefix = '/', sort = 'created_at') ⇒ Object
load media files from a specific folder path.
-
#repair_private_acls! ⇒ Object
Security (audit 2026-08-11 M5 follow-up): uploads stored before the private-ACL fix kept a world-readable ACL under the private prefix.
- #resource_parameters ⇒ Object
- #setup_private_folder ⇒ Object
Methods inherited from CamaleonCmsUploader
#cache_item, #cama_prepare_browser_page, #clear_cache, delete_block, #disable_private_mode!, #enable_private_mode!, #file_exists?, #get_file, get_file_format, get_file_format_extensions, #get_media_collection, #initialize, #reload, #search, #search_new_key, #valid_folder_path?, validate_file_format, #version_path
Constructor Details
This class inherits a constructor from CamaleonCmsUploader
Instance Method Details
#add_file(uploaded_io_or_file_path, key, args = {}) ⇒ Object
add a file object or file path into AWS server :key => (String) key of the file ot save in AWS :args => (HASH) false, is_thumb: false, where:
- same_name: false => avoid to overwrite an existent file with same key and search for an available key
- is_thumb: true => if this file is a thumbnail of an uploaded file
86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 |
# File 'app/uploaders/camaleon_cms_aws_uploader.rb', line 86 def add_file(uploaded_io_or_file_path, key, args = {}) return { error: 'Invalid file path' } unless valid_folder_path?(key) args = { same_name: false, is_thumb: false }.merge(args) key = "#{@aws_settings['inner_folder']}/#{key}" if @aws_settings['inner_folder'].present? && !args[:is_thumb] key = key.cama_fix_media_key key = search_new_key(key) unless args[:same_name] if @instance # private hook to upload files by different way, add file data into result_data _args = { result_data: nil, file: uploaded_io_or_file_path, key: key, args: args, klass: self } @instance.hooks_run('uploader_aws_before_upload', _args) return _args[:result_data] if _args[:result_data].present? end s3_file = bucket.object(key.slice(1..-1)) # Security (audit 2026-08-11 M5): a private-mode upload must be owner-only. Storing it 'public-read' # (like a public file) left it world-readable at a guessable s3://bucket/private/<name> URL, # bypassing the download_private_file gate. That gate fetches via the authenticated S3 API, so a # 'private' ACL does not affect legitimate serving. acl = is_private_uploader? ? 'private' : 'public-read' s3_file.upload_file( uploaded_io_or_file_path.is_a?(String) ? uploaded_io_or_file_path : uploaded_io_or_file_path.path, @aws_settings[:aws_file_upload_settings].call({ acl: acl }) ) res = cache_item(file_parse(s3_file)) unless args[:is_thumb] res end |
#add_folder(key) ⇒ Object
Add a new folder to AWS with :key
115 116 117 118 119 120 121 122 123 |
# File 'app/uploaders/camaleon_cms_aws_uploader.rb', line 115 def add_folder(key) return { error: 'Invalid folder path' } unless valid_folder_path?(key) key = "#{@aws_settings['inner_folder']}/#{key}" if @aws_settings['inner_folder'].present? key = key.cama_fix_media_key s3_file = bucket.object(key.slice(1..-1) << '/') s3_file.put(body: nil) cache_item(file_parse(s3_file)) end |
#after_initialize ⇒ Object
2 3 4 5 6 7 8 9 10 11 |
# File 'app/uploaders/camaleon_cms_aws_uploader.rb', line 2 def after_initialize @cloudfront = @aws_settings[:cloud_front] || @current_site.get_option('filesystem_s3_cloudfront') @aws_region = @aws_settings[:region] || @current_site.get_option('filesystem_region', 'us-west-2') @aws_akey = @aws_settings[:access_key] || @current_site.get_option('filesystem_s3_access_key') @aws_asecret = @aws_settings[:secret_key] || @current_site.get_option('filesystem_s3_secret_key') @aws_bucket = @aws_settings[:bucket] || @current_site.get_option('filesystem_s3_bucket_name') @aws_endpoint = @aws_settings[:endpoint] || @current_site.get_option('filesystem_s3_endpoint') @aws_settings[:aws_file_upload_settings] ||= ->(settings) { settings } @aws_settings[:aws_file_read_settings] ||= ->(data, _s3_file) { data } end |
#browser_files ⇒ Object
recover all files from AWS and parse it to save into DB as cache
22 23 24 25 26 27 28 |
# File 'app/uploaders/camaleon_cms_aws_uploader.rb', line 22 def browser_files bucket.objects({ prefix: @aws_settings['inner_folder'].presence || nil }).each do |file| next if File.dirname(file.key).split('/').pop == 'thumb' cache_item(file_parse(file)) end end |
#bucket ⇒ Object
Initialize a bucket with AWS configurations return: (AWS Bucket object)
169 170 171 172 173 174 175 |
# File 'app/uploaders/camaleon_cms_aws_uploader.rb', line 169 def bucket @bucket ||= lambda { Aws.config.update({ region: @aws_region, credentials: Aws::Credentials.new(@aws_akey, @aws_asecret) }) s3 = Aws::S3::Resource.new(resource_parameters) s3.bucket(@aws_bucket) }.call end |
#delete_file(key) ⇒ Object
Delete the :key file in AWS
136 137 138 139 140 141 142 143 144 145 146 147 148 |
# File 'app/uploaders/camaleon_cms_aws_uploader.rb', line 136 def delete_file(key) return { error: 'Invalid file path' } unless valid_folder_path?(key) key = "#{@aws_settings['inner_folder']}/#{key}" if @aws_settings['inner_folder'].present? key = key.cama_fix_media_key begin bucket.object(key.slice(1..-1)).delete rescue StandardError '' end @instance.hooks_run('after_delete', key) get_media_collection.by_key(key).take.destroy end |
#delete_folder(key) ⇒ Object
Delete a folder in AWS with :key
126 127 128 129 130 131 132 133 |
# File 'app/uploaders/camaleon_cms_aws_uploader.rb', line 126 def delete_folder(key) return { error: 'Invalid folder path' } unless valid_folder_path?(key) key = "#{@aws_settings['inner_folder']}/#{key}" if @aws_settings['inner_folder'].present? key = key.cama_fix_media_key bucket.objects(prefix: key.slice(1..-1) << '/').delete get_media_collection.by_key(key).take.destroy end |
#fetch_file(file_name) ⇒ Object
39 40 41 42 43 44 45 46 47 |
# File 'app/uploaders/camaleon_cms_aws_uploader.rb', line 39 def fetch_file(file_name) return { error: 'Invalid file path' } unless valid_folder_path?(file_name) return file_name if file_exists?(file_name) return file_name if bucket.object(file_name).download_file(file_name) && file_exists?(file_name) { error: 'File not found' } end |
#file_parse(s3_file) ⇒ Object
parse an AWS file into custom file_object
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
# File 'app/uploaders/camaleon_cms_aws_uploader.rb', line 50 def file_parse(s3_file) key = s3_file.is_a?(String) ? s3_file : s3_file.key key = key.cama_fix_media_key is_dir = s3_file.is_a?(String) || File.extname(key) == '' url = if is_private_uploader? is_dir ? '' : File.basename(key) elsif is_dir '' else (@cloudfront.present? ? File.join(@cloudfront, key) : s3_file.public_url) end res = { 'name' => File.basename(key), 'folder_path' => File.dirname(key), 'url' => url, 'is_folder' => is_dir, 'file_size' => is_dir ? 0 : s3_file.size.round(2), 'thumb' => '', 'file_type' => is_dir ? '' : self.class.get_file_format(key), 'created_at' => is_dir ? '' : s3_file.last_modified, 'dimension' => '' }.with_indifferent_access if res['file_type'] == 'image' && File.extname(res['name']).downcase != '.gif' res['thumb'] = version_path(res['url']).sub(SVG_EXT_PATTERN, '.jpg') end res['key'] = File.join(res['folder_path'], res['name']) @aws_settings[:aws_file_read_settings].call(res, s3_file) end |
#objects(prefix = '/', sort = 'created_at') ⇒ Object
load media files from a specific folder path
31 32 33 34 35 36 37 |
# File 'app/uploaders/camaleon_cms_aws_uploader.rb', line 31 def objects(prefix = '/', sort = 'created_at') if @aws_settings['inner_folder'].present? prefix = "#{@aws_settings['inner_folder']}/#{prefix}".gsub('//', '/') prefix = prefix[0..-2] if prefix.end_with?('/') end super(prefix, sort) end |
#repair_private_acls! ⇒ Object
Security (audit 2026-08-11 M5 follow-up): uploads stored before the private-ACL fix kept a world-readable ACL under the private prefix. Sweep them back to owner-only and return the count. The prefix mirrors setup_private_folder -- <inner_folder>/private/ -- normalized exactly like add_file keys (leading slash added by cama_fix_media_key, then stripped for S3), with a trailing slash so a sibling folder such as private_x is not swept.
155 156 157 158 159 160 161 162 163 164 165 |
# File 'app/uploaders/camaleon_cms_aws_uploader.rb', line 155 def repair_private_acls! inner_folder = @aws_settings['inner_folder'].to_s inner_folder = "#{inner_folder}/#{PRIVATE_DIRECTORY}" unless is_private_uploader? prefix = "#{inner_folder.cama_fix_media_key.slice(1..-1)}/" repaired = 0 bucket.objects(prefix: prefix).each do |object| object.acl.put(acl: 'private') repaired += 1 end repaired end |
#resource_parameters ⇒ Object
177 178 179 180 181 |
# File 'app/uploaders/camaleon_cms_aws_uploader.rb', line 177 def resource_parameters return {} if @aws_endpoint.blank? { endpoint: @aws_endpoint } end |
#setup_private_folder ⇒ Object
13 14 15 16 17 18 19 |
# File 'app/uploaders/camaleon_cms_aws_uploader.rb', line 13 def setup_private_folder return unless is_private_uploader? add_folder(PRIVATE_DIRECTORY) @aws_settings['inner_folder'] = "#{@aws_settings['inner_folder']}/#{PRIVATE_DIRECTORY}" end |