md-paperclip-azure 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 54530987473e6eb813e49b16d20ecbcf08471fd51feb99302ef5fc25249934dc
4
+ data.tar.gz: 80e829fa4ca574592900647253069ef304f761571567569b9d7da9fffa2663de
5
+ SHA512:
6
+ metadata.gz: b578b3bfd0bbaa873fc3d5565f3c1924c150f13dbc7fda78a0ff91c761fff81a326dd7a2f0a8ca1416e2c49489687003a02da88ab199b73c7e4779033ff75bc3
7
+ data.tar.gz: cb820a5db30cc4e11e70d327d4e3dce9c760b0f474cf363be8df399506056d081c846810dd9e141abe163b953c665dea4665098cd0622a819bfb284925d03d21
data/.autotest ADDED
@@ -0,0 +1,25 @@
1
+ # -*- ruby -*-
2
+
3
+ require "autotest/restart"
4
+
5
+ # Autotest.add_hook :initialize do |at|
6
+ # at.testlib = "minitest/unit"
7
+ #
8
+ # at.extra_files << "../some/external/dependency.rb"
9
+ #
10
+ # at.libs << ":../some/external"
11
+ #
12
+ # at.add_exception "vendor"
13
+ #
14
+ # at.add_mapping(/dependency.rb/) do |f, _|
15
+ # at.files_matching(/test_.*rb$/)
16
+ # end
17
+ #
18
+ # %w(TestA TestB).each do |klass|
19
+ # at.extra_class_map[klass] = "test/test_misc.rb"
20
+ # end
21
+ # end
22
+
23
+ # Autotest.add_hook :run_command do |at|
24
+ # system "rake build"
25
+ # end
data/History.txt ADDED
@@ -0,0 +1,6 @@
1
+ === 1.0.0 / 2017-07-24
2
+
3
+ * 1 major enhancement
4
+
5
+ * Birthday!
6
+
data/Manifest.txt ADDED
@@ -0,0 +1,9 @@
1
+ .autotest
2
+ History.txt
3
+ Manifest.txt
4
+ README.md
5
+ Rakefile
6
+ lib/paperclip/storage/azure/environment.rb
7
+ lib/paperclip/storage/azure.rb
8
+ lib/paperclip/azure.rb
9
+ lib/paperclip-azure.rb
data/README.md ADDED
@@ -0,0 +1,127 @@
1
+ = paperclip-azure
2
+
3
+ home :: https://github.com/mistydemeo/paperclip-azure
4
+ code :: https://github.com/mistydemeo/paperclip-azure
5
+ bugs :: https://github.com/mistydemeo/paperclip-azure/issues
6
+
7
+ == DESCRIPTION:
8
+
9
+ Paperclip-Azure is a [Paperclip](https://github.com/thoughtbot/paperclip) storage driver for storing files in a Microsoft Azure Blob. This is a friendly fork of the [original gem](https://github.com/supportify/paperclip-azure); it incorporates bugfixes that haven't yet been incldued in point releases of the upstream gem and some new features.
10
+
11
+ == FEATURES/PROBLEMS:
12
+
13
+ * FIX (list of features or problems)
14
+
15
+ == SYNOPSIS:
16
+
17
+ The Azure storage engine has been developed to work as similarly to S3 storage configuration as is possible. This gem can be configured in a Paperclip initializer or environment file as follows:
18
+
19
+ Paperclip::Attachment.default_options[:storage] = :azure
20
+ Paperclip::Attachment.default_options[:azure_credentials] = {
21
+ storage_account_name: ENV['AZURE_STORAGE_ACCOUNT'],
22
+ storage_access_key: ENV['AZURE_STORAGE_ACCESS_KEY'],
23
+ container: ENV['AZURE_CONTAINER_NAME']
24
+ }
25
+
26
+ Or, at the level of the model such as in the following example:
27
+
28
+ has_attached_file :download,
29
+ storage: :azure,
30
+ azure_credentials: {
31
+ storage_account_name: ENV['AZURE_STORAGE_ACCOUNT'],
32
+ storage_access_key: ENV['AZURE_STORAGE_ACCESS_KEY'],
33
+ container: ENV['AZURE_CONTAINER_NAME']
34
+ }
35
+
36
+ Additionally, you can also supply credentials using a path or a File that contains the +storage_access_key+ and +storage_account_name+ that Azure gives you. You can 'environment-space' this just like you do to your `database.yml` file, so different environments can use different accounts:
37
+
38
+ development:
39
+ storage_account_name: foo
40
+ storage_access_key: 123...
41
+ test:
42
+ storage_account_name: foo
43
+ storage_access_key: abc...
44
+ production:
45
+ storage_account_name: foo
46
+ storage_access_key: 456...
47
+
48
+ This is not required, however, and the file may simply look like this:
49
+
50
+ storage_account_name: foo
51
+ storage_access_key: 456...
52
+
53
+ In which case, those access keys will be used in all environments. You can also put your container name in this file, instead of adding it to the code directly. This is useful when you want the same account but a different container for development versus production.
54
+
55
+
56
+ === Private Blob Access
57
+
58
+ In the even that are using a Blob that has been configured for Private access, you will need to use the Shared Access Signature functionality of Azure. This functionality has been baked in to the `Attachment#expiring_url` method. Simply specify a time and a style and you will get a proper URL as follows:
59
+
60
+ object.attachment.expiring_url(30.minutes.since, :thumb)
61
+
62
+ For more information about Azure Shared Access Signatures, please refer to [here](http://azure.microsoft.com/en-us/documentation/articles/storage-dotnet-shared-access-signature-part-1/).
63
+
64
+ === Azure Environments
65
+
66
+ Microsoft offers specialized Azure implementations for special circumstances should the need arise. As of the most recent update of this gem, the AzureChinaCloud, AzureUSGovernment, and AzureGermanCloud environments all offer specific storage URL's that differ from those of the standard AzureCloud. These regions can be specified via the `:region` key of the `:azure_credentials` dictionary by using the symbols `:cn`, `:usgovt`, and `:de` respectively. When working with one of these environments, simply update your credentials to include the region as follows:
67
+
68
+ Paperclip::Attachment.default_options[:azure_credentials] = {
69
+ storage_account_name: ENV['AZURE_STORAGE_ACCOUNT'],
70
+ storage_access_key: ENV['AZURE_STORAGE_ACCESS_KEY'],
71
+ container: ENV['AZURE_CONTAINER_NAME'],
72
+ region: :de
73
+ }
74
+
75
+ Or, in the instance where the credentials are specified at the model level:
76
+
77
+ has_attached_file :download,
78
+ storage: :azure,
79
+ azure_credentials: {
80
+ storage_account_name: ENV['AZURE_STORAGE_ACCOUNT'],
81
+ storage_access_key: ENV['AZURE_STORAGE_ACCESS_KEY'],
82
+ container: ENV['AZURE_CONTAINER_NAME'],
83
+ region: :cn
84
+ }
85
+
86
+ == REQUIREMENTS:
87
+
88
+ * An Azure storage account.
89
+
90
+ == INSTALL:
91
+
92
+ Add this line to your application's Gemfile after the Paperclip gem:
93
+
94
+ gem 'md-paperclip-azure', '~> 2.0'
95
+
96
+ And then execute:
97
+
98
+ $ bundle install
99
+
100
+ == DEVELOPERS:
101
+
102
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
103
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
104
+ * Fork the project.
105
+ * After checking out the source, run:
106
+
107
+ $ rake newb
108
+
109
+ This task will install any missing dependencies, run the tests/specs, and generate the RDoc.
110
+ * Start a feature/bugfix branch.
111
+ * Commit and push until you are happy with your contribution.
112
+ * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
113
+ * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
114
+ * Submit a pull request for the finished product's integration.
115
+
116
+ == LICENSE:
117
+
118
+ (The MIT License)
119
+
120
+ Copyright (c) 2017 Supportify, Inc.
121
+ Copyright (c) 2023 Misty De Méo
122
+
123
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
124
+
125
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
126
+
127
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,30 @@
1
+ # -*- ruby -*-
2
+
3
+ require "rubygems"
4
+ require "hoe"
5
+
6
+ Hoe.plugin :bundler
7
+ Hoe.plugin :debug
8
+ Hoe.plugin :git
9
+ Hoe.plugin :gemspec
10
+ Hoe.plugin :rubygems
11
+
12
+ Hoe.spec "md-paperclip-azure" do
13
+ developer("Misty De Méo", "mistydemeo@gmail.com")
14
+ license "MIT" # this should match the license in the README
15
+
16
+ extra_deps << ['azure-storage-blob', '~> 2.0.1']
17
+ extra_deps << ['hashie', '~> 3.5']
18
+ extra_deps << ['addressable', '~> 2.5']
19
+
20
+ extra_dev_deps << ['paperclip', '>= 4.3.6']
21
+ extra_dev_deps << ['sqlite3', '~> 1.3.8']
22
+ extra_dev_deps << ['rspec', '~> 3.0']
23
+ extra_dev_deps << ['simplecov', '~> 0.14']
24
+ extra_dev_deps << ['activerecord', '>= 4.2.0']
25
+ extra_dev_deps << ['activerecord-import', '~> 0.19']
26
+ extra_dev_deps << ['activemodel', '>= 4.2.0']
27
+ extra_dev_deps << ['activesupport', '>= 4.2.0']
28
+ end
29
+
30
+ # vim: syntax=ruby
@@ -0,0 +1,5 @@
1
+ module Paperclip; end
2
+
3
+ class Paperclip::Azure
4
+ VERSION = "2.0.0"
5
+ end
@@ -0,0 +1,19 @@
1
+ module Paperclip
2
+ module Storage
3
+ module Azure
4
+ class Environment
5
+
6
+ ENVIRONMENT_SUFFIX = {
7
+ global: 'core.windows.net',
8
+ cn: 'core.chinacloudapi.cn',
9
+ de: "core.cloudapi.de",
10
+ usgovt: 'core.usgovcloudapi.net'
11
+ }
12
+
13
+ def self.url_for(account_name, region = nil)
14
+ "#{account_name}.blob.#{ENVIRONMENT_SUFFIX[region || :global]}"
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,314 @@
1
+ require 'paperclip/storage/azure/environment'
2
+
3
+ require 'azure/core'
4
+ require 'azure/storage/blob'
5
+ require 'azure/storage/common'
6
+
7
+ module Paperclip
8
+ module Storage
9
+ # Azure's container file hosting service is a scalable, easy place to store files for
10
+ # distribution. You can find out more about it at http://azure.microsoft.com/en-us/services/storage/
11
+ #
12
+ # To use Paperclip with Azure, include the +azure-storage-blob+ gem in your Gemfile:
13
+ # gem 'azure-storage-blob'
14
+ # There are a few Azure-specific options for has_attached_file:
15
+ # * +azure_credentials+: Takes a path, a File, a Hash or a Proc. The path (or File) must point
16
+ # to a YAML file containing the +storage_access_key+ and +storage_account_name+ that azure
17
+ # gives you. You can 'environment-space' this just like you do to your
18
+ # database.yml file, so different environments can use different accounts:
19
+ # development:
20
+ # storage_account_name: foo
21
+ # storage_access_key: 123...
22
+ # test:
23
+ # storage_account_name: foo
24
+ # storage_access_key: abc...
25
+ # production:
26
+ # storage_account_name: foo
27
+ # storage_access_key: 456...
28
+ # This is not required, however, and the file may simply look like this:
29
+ # storage_account_name: foo
30
+ # storage_access_key: 456...
31
+ # In which case, those access keys will be used in all environments. You can also
32
+ # put your container name in this file, instead of adding it to the code directly.
33
+ # This is useful when you want the same account but a different container for
34
+ # development versus production.
35
+ # When using a Proc it provides a single parameter which is the attachment itself. A
36
+ # method #instance is available on the attachment which will take you back to your
37
+ # code. eg.
38
+ # class User
39
+ # has_attached_file :download,
40
+ # :storage => :azure,
41
+ # :azure_credentials => Proc.new{|a| a.instance.azure_credentials }
42
+ #
43
+ # def azure_credentials
44
+ # { :container => "xxx", :storage_account_name => "xxx", :storage_access_key => "xxx" }
45
+ # end
46
+ # end
47
+ #
48
+ # * +container+: This is the name of the Azure container that will store your files. Remember
49
+ # that the container must be unique across the storage account. If the container does not exist
50
+ # Paperclip will attempt to create it. The container name will not be interpolated.
51
+ # You can define the container as a Proc if you want to determine it's name at runtime.
52
+ # Paperclip will call that Proc with attachment as the only argument.
53
+ # * +path+: This is the key under the container in which the file will be stored. The
54
+ # URL will be constructed from the container and the path. This is what you will want
55
+ # to interpolate. Keys should be unique, like filenames, and despite the fact that
56
+ # Azure (strictly speaking) does not support directories, you can still use a / to
57
+ # separate parts of your file name.
58
+ # * +region+: Depending on the region, different base urls are used. Supported values :global, :de
59
+
60
+ module Azure
61
+ def self.extended base
62
+ begin
63
+ require 'azure/storage/blob/blob_service'
64
+ rescue LoadError => e
65
+ e.message << " (You may need to install the azure-storage-blob gem)"
66
+ raise e
67
+ end unless defined?(::Azure::Core)
68
+
69
+ base.instance_eval do
70
+ @azure_options = @options[:azure_options] || {}
71
+
72
+ unless @options[:url].to_s.match(/\A:azure.*url\z/) || @options[:url] == ":asset_host".freeze
73
+ @options[:path] = path_option.gsub(/:url/, @options[:url]).sub(/\A:rails_root\/public\/system/, "".freeze)
74
+ @options[:url] = ":azure_path_url".freeze
75
+ end
76
+ @options[:url] = @options[:url].inspect if @options[:url].is_a?(Symbol)
77
+
78
+ @http_proxy = @options[:http_proxy] || nil
79
+ end
80
+
81
+ unless Paperclip::Interpolations.respond_to? :azure_alias_url
82
+ Paperclip.interpolates(:azure_alias_url) do |attachment, style|
83
+ protocol = attachment.azure_protocol(style, true)
84
+ host = attachment.azure_host_alias
85
+ path = attachment.path(style).
86
+ split("/")[attachment.azure_prefixes_in_alias..-1].
87
+ join("/").
88
+ sub(%r{\A/}, "")
89
+ "#{protocol}//#{host}/#{path}"
90
+ end
91
+ end
92
+ Paperclip.interpolates(:azure_path_url) do |attachment, style|
93
+ attachment.azure_uri(style)
94
+ end unless Paperclip::Interpolations.respond_to? :azure_path_url
95
+ Paperclip.interpolates(:asset_host) do |attachment, style|
96
+ "#{attachment.path(style).sub(%r{\A/}, "".freeze)}"
97
+ end unless Paperclip::Interpolations.respond_to? :asset_host
98
+ end
99
+
100
+ def expiring_url(time = 3600, style_name = default_style)
101
+ if path(style_name)
102
+ signer = ::Azure::Core::Auth::SharedAccessSignature.new(
103
+ azure_account_name,
104
+ azure_credentials[:storage_access_key]
105
+ )
106
+ obj_path = path(style_name).gsub(%r{\A/}, '')
107
+ "#{azure_uri}?#{signer.generate_token(container_name, obj_path, 'r', time)}"
108
+ else
109
+ url(style_name)
110
+ end
111
+ end
112
+
113
+ def azure_host_alias
114
+ @azure_host_alias = @options[:azure_host_alias]
115
+ @azure_host_alias = @azure_host_alias.call(self) if @azure_host_alias.respond_to?(:call)
116
+ @azure_host_alias
117
+ end
118
+
119
+ def azure_prefixes_in_alias
120
+ @azure_prefixes_in_alias ||= @options[:azure_prefixes_in_alias].to_i
121
+ end
122
+
123
+ def azure_protocol(style = default_style, with_colon = false)
124
+ protocol = @azure_options[:protocol]
125
+ protocol = protocol.call(style, self) if protocol.respond_to?(:call)
126
+
127
+ if with_colon && !protocol.empty?
128
+ "#{protocol}:"
129
+ else
130
+ protocol.to_s
131
+ end
132
+ end
133
+
134
+ def auto_connect_duration
135
+ @auto_connect_duration ||= @options[:auto_connect_duration] || azure_credentials[:auto_connect_duration] || 10
136
+ @auto_connect_duration
137
+ end
138
+
139
+ def azure_credentials
140
+ @azure_credentials ||= parse_credentials(@options[:azure_credentials])
141
+ end
142
+
143
+ def azure_account_name
144
+ account_name = @options[:azure_storage_account_name] || azure_credentials[:storage_account_name]
145
+ account_name = account_name.call(self) if account_name.is_a?(Proc)
146
+
147
+ account_name
148
+ end
149
+
150
+ def container_name
151
+ @container ||= @options[:container] || azure_credentials[:container]
152
+ @container = @container.call(self) if @container.respond_to?(:call)
153
+ @container or raise ArgumentError, "missing required :container option"
154
+ end
155
+
156
+ def azure_interface
157
+ @azure_interface ||= begin
158
+ config = {}
159
+
160
+ [:storage_account_name, :storage_access_key, :container].each do |opt|
161
+ config[opt] = azure_credentials[opt] if azure_credentials[opt]
162
+ end
163
+
164
+ obtain_azure_instance_for(config.merge(@azure_options))
165
+ end
166
+ end
167
+
168
+ def obtain_azure_instance_for(options)
169
+ instances = (Thread.current[:paperclip_azure_instances] ||= {})
170
+ return instances[options] if instance[options]
171
+
172
+ if options[:use_development_storage]
173
+ service = ::Azure::Storage::Blob::BlobService.create(use_development_storage: true)
174
+ else
175
+ service = ::Azure::Storage::Blob::BlobService.create(storage_account_name: options[:storage_account_name],
176
+ storage_access_key: options[:storage_access_key])
177
+ end
178
+ instances[options] = service
179
+ end
180
+
181
+ def azure_uri(style_name = default_style)
182
+ "https://#{azure_base_url}/#{container_name}/#{path(style_name).gsub(%r{\A/}, '')}"
183
+ end
184
+
185
+ def azure_base_url
186
+ Environment.url_for azure_account_name, azure_credentials[:region]
187
+ end
188
+
189
+ def azure_container
190
+ @azure_container ||= azure_interface.get_container_properties container_name
191
+ end
192
+
193
+ def azure_object(style_name = default_style)
194
+ azure_interface.get_blob_properties container_name, path(style_name).sub(%r{\A/},'')
195
+ end
196
+
197
+ def parse_credentials(creds)
198
+ creds = creds.respond_to?('call') ? creds.call(self) : creds
199
+ creds = find_credentials(creds).stringify_keys
200
+ env = Object.const_defined?(:Rails) ? Rails.env : nil
201
+ (creds[env] || creds).symbolize_keys
202
+ end
203
+
204
+ def exists?(style = default_style)
205
+ if original_filename
206
+ !azure_object(style).nil?
207
+ else
208
+ false
209
+ end
210
+ rescue ::Azure::Core::Http::HTTPError => e
211
+ raise unless e.status_code == 404
212
+
213
+ false
214
+ end
215
+
216
+ def create_container
217
+ azure_interface.create_container container_name
218
+ end
219
+
220
+ def flush_writes #:nodoc:
221
+ @queued_for_write.each do |style, file|
222
+ retries = 0
223
+ begin
224
+ log("saving #{path(style)}")
225
+
226
+ write_options = {
227
+ content_type: file.content_type,
228
+ }
229
+
230
+ if azure_container
231
+ save_blob container_name, path(style).sub(%r{\A/},''), file, write_options
232
+ end
233
+ rescue ::Azure::Core::Http::HTTPError => e
234
+ if e.status_code == 404
235
+ create_container
236
+ retry
237
+ else
238
+ raise
239
+ end
240
+ ensure
241
+ file.rewind
242
+ end
243
+ end
244
+
245
+ after_flush_writes # allows attachment to clean up temp files
246
+
247
+ @queued_for_write = {}
248
+ end
249
+
250
+ def save_blob(container_name, storage_path, file, write_options)
251
+
252
+ if file.size < 64.megabytes
253
+ azure_interface.create_block_blob container_name, storage_path, file.read, write_options
254
+ else
255
+ blocks = []; count = 0
256
+ while data = file.read(4.megabytes)
257
+ block_id = "block_#{(count += 1).to_s.rjust(5, '0')}"
258
+
259
+ azure_interface.put_blob_block container_name, storage_path, block_id, data
260
+
261
+ blocks << [block_id]
262
+ end
263
+
264
+ azure_interface.commit_blob_blocks container_name, storage_path, blocks
265
+ end
266
+ end
267
+
268
+ def flush_deletes #:nodoc:
269
+ @queued_for_delete.each do |path|
270
+ begin
271
+ log("deleting #{path}")
272
+
273
+ azure_interface.delete_blob container_name, path
274
+ rescue ::Azure::Core::Http::HTTPError => e
275
+ raise unless e.status_code == 404
276
+ end
277
+ end
278
+ @queued_for_delete = []
279
+ end
280
+
281
+ def copy_to_local_file(style, local_dest_path)
282
+ log("copying #{path(style)} to local file #{local_dest_path}")
283
+
284
+ blob, content = azure_interface.get_blob(container_name, path(style).sub(%r{\A/},''))
285
+
286
+ ::File.open(local_dest_path, 'wb') do |local_file|
287
+ local_file.write(content)
288
+ end
289
+ rescue ::Azure::Core::Http::HTTPError => e
290
+ raise unless e.status_code == 404
291
+
292
+ warn("#{e} - cannot copy #{path(style)} to local file #{local_dest_path}")
293
+ false
294
+ end
295
+
296
+ private
297
+
298
+ def find_credentials creds
299
+ case creds
300
+ when File
301
+ YAML::load(ERB.new(File.read(creds.path)).result)
302
+ when String, Pathname
303
+ YAML::load(ERB.new(File.read(creds)).result)
304
+ when Hash
305
+ creds
306
+ when NilClass
307
+ {}
308
+ else
309
+ raise ArgumentError, "Credentials given are not a path, file, proc, or hash."
310
+ end
311
+ end
312
+ end
313
+ end
314
+ end
@@ -0,0 +1,44 @@
1
+ require 'openssl'
2
+
3
+ require File.join(File.dirname(__FILE__), 'paperclip', 'storage', 'azure')
4
+
5
+ module Azure
6
+ module Core
7
+ module Auth
8
+ class SharedAccessSignature
9
+ attr_accessor :version
10
+ attr_accessor :account
11
+ attr_accessor :access_key
12
+ attr_accessor :resource_type
13
+
14
+ def initialize(account = ENV['AZURE_STORAGE_ACCOUNT'], access_key = ENV['AZURE_STORAGE_ACCESS_KEY'])
15
+ @version = '2020-02-10'
16
+ @account = account
17
+ @access_key = access_key
18
+ @resource_type = 'b'
19
+ end
20
+
21
+ def generate_token(container, key, permission='r', timeout=900, content_disposition='')
22
+ expiry = (Time.now.utc + timeout.seconds).utc.iso8601
23
+ resource_path = "/blob/#{@account}/#{container}/#{key[0] == '/' ? key[1..-1] : key}"
24
+ string_to_sign = "#{permission}\n\n#{expiry}\n#{resource_path}\n\n\n\n#{@version}\n#{@resource_type}\n\n\n#{content_disposition}\n\n\n"
25
+ "se=#{URI.encode_www_form_component(expiry)}" \
26
+ "&sp=#{permission}" \
27
+ "&sv=#{@version}" \
28
+ "&sr=#{@resource_type}" \
29
+ "&rscd=#{URI.encode_www_form_component(content_disposition)}" \
30
+ "&sig=#{URI.encode_www_form_component(sign_string(@access_key, string_to_sign))}"
31
+ end
32
+
33
+ private
34
+
35
+ def sign_string(key, string_to_sign)
36
+ digest = OpenSSL::Digest::SHA256.new
37
+ hmac = OpenSSL::HMAC.new(Base64.decode64(key), digest)
38
+ hmac << string_to_sign
39
+ Base64.encode64(hmac.digest).strip
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
metadata ADDED
@@ -0,0 +1,253 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: md-paperclip-azure
3
+ version: !ruby/object:Gem::Version
4
+ version: 2.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Misty De Méo
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2023-02-15 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: azure-storage-blob
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 2.0.1
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 2.0.1
27
+ - !ruby/object:Gem::Dependency
28
+ name: hashie
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '3.5'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '3.5'
41
+ - !ruby/object:Gem::Dependency
42
+ name: addressable
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '2.5'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '2.5'
55
+ - !ruby/object:Gem::Dependency
56
+ name: paperclip
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: 4.3.6
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: 4.3.6
69
+ - !ruby/object:Gem::Dependency
70
+ name: sqlite3
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 1.3.8
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 1.3.8
83
+ - !ruby/object:Gem::Dependency
84
+ name: rspec
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '3.0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '3.0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: simplecov
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '0.14'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '0.14'
111
+ - !ruby/object:Gem::Dependency
112
+ name: activerecord
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: 4.2.0
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: 4.2.0
125
+ - !ruby/object:Gem::Dependency
126
+ name: activerecord-import
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '0.19'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '0.19'
139
+ - !ruby/object:Gem::Dependency
140
+ name: activemodel
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: 4.2.0
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: 4.2.0
153
+ - !ruby/object:Gem::Dependency
154
+ name: activesupport
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: 4.2.0
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ version: 4.2.0
167
+ - !ruby/object:Gem::Dependency
168
+ name: rdoc
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - ">="
172
+ - !ruby/object:Gem::Version
173
+ version: '4.0'
174
+ - - "<"
175
+ - !ruby/object:Gem::Version
176
+ version: '7'
177
+ type: :development
178
+ prerelease: false
179
+ version_requirements: !ruby/object:Gem::Requirement
180
+ requirements:
181
+ - - ">="
182
+ - !ruby/object:Gem::Version
183
+ version: '4.0'
184
+ - - "<"
185
+ - !ruby/object:Gem::Version
186
+ version: '7'
187
+ - !ruby/object:Gem::Dependency
188
+ name: hoe
189
+ requirement: !ruby/object:Gem::Requirement
190
+ requirements:
191
+ - - "~>"
192
+ - !ruby/object:Gem::Version
193
+ version: '4.0'
194
+ type: :development
195
+ prerelease: false
196
+ version_requirements: !ruby/object:Gem::Requirement
197
+ requirements:
198
+ - - "~>"
199
+ - !ruby/object:Gem::Version
200
+ version: '4.0'
201
+ description: Paperclip-Azure is a [Paperclip](https://github.com/thoughtbot/paperclip)
202
+ storage driver for storing files in a Microsoft Azure Blob. This is a friendly fork
203
+ of the [original gem](https://github.com/supportify/paperclip-azure); it incorporates
204
+ bugfixes that haven't yet been incldued in point releases of the upstream gem and
205
+ some new features.
206
+ email:
207
+ - mistydemeo@gmail.com
208
+ executables: []
209
+ extensions: []
210
+ extra_rdoc_files:
211
+ - History.txt
212
+ - Manifest.txt
213
+ - README.md
214
+ files:
215
+ - ".autotest"
216
+ - History.txt
217
+ - Manifest.txt
218
+ - README.md
219
+ - Rakefile
220
+ - lib/paperclip-azure.rb
221
+ - lib/paperclip/azure.rb
222
+ - lib/paperclip/storage/azure.rb
223
+ - lib/paperclip/storage/azure/environment.rb
224
+ homepage: https://github.com/mistydemeo/paperclip-azure
225
+ licenses:
226
+ - MIT
227
+ metadata:
228
+ homepage_uri: https://github.com/mistydemeo/paperclip-azure
229
+ source_code_uri: https://github.com/mistydemeo/paperclip-azure
230
+ bug_tracker_uri: https://github.com/mistydemeo/paperclip-azure/issues
231
+ post_install_message:
232
+ rdoc_options:
233
+ - "--main"
234
+ - README.md
235
+ require_paths:
236
+ - lib
237
+ required_ruby_version: !ruby/object:Gem::Requirement
238
+ requirements:
239
+ - - ">="
240
+ - !ruby/object:Gem::Version
241
+ version: '0'
242
+ required_rubygems_version: !ruby/object:Gem::Requirement
243
+ requirements:
244
+ - - ">="
245
+ - !ruby/object:Gem::Version
246
+ version: '0'
247
+ requirements: []
248
+ rubygems_version: 3.4.6
249
+ signing_key:
250
+ specification_version: 4
251
+ summary: Paperclip-Azure is a [Paperclip](https://github.com/thoughtbot/paperclip)
252
+ storage driver for storing files in a Microsoft Azure Blob
253
+ test_files: []