Skip to content

Commit 1b857d4

Browse files
committed
fix(storage): replace retired azure-storage-blob with maintained azure-blob
The azure-storage-blob / azure-storage-common gems were retired by Microsoft on 13 Sep 2024 and are unmaintained. They also forced a manual `cgi ~> 0.5.1` pin under Ruby 4.0 (azure-storage-common needs CGI.parse, which Ruby 4.0's stripped stdlib omits and Rails 8 no longer pulls in transitively). Swap to testdouble/azure-blob, a maintained drop-in Active Storage adapter: - Gemfile: azure-storage-blob -> azure-blob; drop the now-redundant cgi pin (azure-blob declares cgi as a dependency, so the full library resolves again). - config/storage.yml: microsoft service AzureStorage -> AzureBlob. - Rewrite the signing regression guard to exercise the AzureBlob Active Storage service (offline URL signing for download + direct upload), replacing the old azure-storage-common internal-class assertions. Same Azure account / container / keys, so no data migration or infra change. Closes #7094
1 parent 8e893b1 commit 1b857d4

4 files changed

Lines changed: 48 additions & 53 deletions

File tree

Gemfile

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,11 @@ gem "rails", "~> 8.0"
88
gem "after_party" # Post-deployment tasks
99
gem "amazing_print" # Easier console reading
1010
gem "authtrail" # Track Devise login activity
11-
gem "azure-storage-blob", require: false
11+
gem "azure-blob", require: false # Active Storage adapter for Azure (maintained replacement for retired azure-storage-blob)
1212
gem "blueprinter" # JSON serialization
1313
gem "bugsnag" # Error tracking in production
1414
gem "caxlsx", "~> 4.5" # Excel spreadsheets - TODO can we remove this version restriction?
1515
gem "caxlsx_rails", "~> 0.7.1" # Excel spreadsheets - TODO can we remove this version restriction?
16-
# Ruby 4.0 ships a cgi stdlib with only escape/unescape; azure-storage-common needs the
17-
# full library's CGI.parse to sign blob URLs. Rails 8 no longer pulls cgi in transitively,
18-
# so without this the stripped stdlib wins and every Active Storage read/write 500s.
19-
# Guarded by spec/lib/azure_storage_signing_spec.rb. Remove when azure-storage-blob is dropped.
20-
gem "cgi", "~> 0.5.1"
2116
gem "cssbundling-rails", "~> 1.4" # CSS compilation
2217
gem "delayed_job_active_record" # Background job processing
2318
gem "devise" # Authentication

Gemfile.lock

Lines changed: 4 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -92,14 +92,9 @@ GEM
9292
dumb_delegator
9393
axe-core-rspec (4.12.0)
9494
axe-core-api (= 4.12.0)
95-
azure-storage-blob (2.0.3)
96-
azure-storage-common (~> 2.0)
97-
nokogiri (~> 1, >= 1.10.8)
98-
azure-storage-common (2.0.4)
99-
faraday (~> 1.0)
100-
faraday_middleware (~> 1.0, >= 1.0.0.rc1)
101-
net-http-persistent (~> 4.0)
102-
nokogiri (~> 1, >= 1.10.8)
95+
azure-blob (0.8.0)
96+
cgi
97+
rexml
10398
base64 (0.3.0)
10499
bcrypt (3.1.22)
105100
benchmark (0.5.0)
@@ -237,8 +232,6 @@ GEM
237232
faraday-patron (1.0.0)
238233
faraday-rack (1.0.0)
239234
faraday-retry (1.0.4)
240-
faraday_middleware (1.2.1)
241-
faraday (~> 1.0)
242235
ffi (1.17.4)
243236
ffi (1.17.4-arm64-darwin)
244237
ffi (1.17.4-x86_64-darwin)
@@ -342,15 +335,12 @@ GEM
342335
benchmark
343336
logger
344337
mini_mime (1.1.5)
345-
mini_portile2 (2.8.9)
346338
minitest (6.0.6)
347339
drb (~> 2.0)
348340
prism (~> 1.5)
349341
multi_xml (0.8.1)
350342
bigdecimal (>= 3.1, < 5)
351343
multipart-post (2.4.1)
352-
net-http-persistent (4.0.8)
353-
connection_pool (>= 2.2.4, < 4)
354344
net-imap (0.6.4.1)
355345
date
356346
net-protocol
@@ -361,9 +351,6 @@ GEM
361351
net-smtp (0.5.1)
362352
net-protocol
363353
nio4r (2.7.5)
364-
nokogiri (1.19.4)
365-
mini_portile2 (~> 2.8.2)
366-
racc (~> 1.4)
367354
nokogiri (1.19.4-arm64-darwin)
368355
racc (~> 1.4)
369356
nokogiri (1.19.4-x86_64-darwin)
@@ -709,7 +696,6 @@ GEM
709696

710697
PLATFORMS
711698
arm64-darwin
712-
ruby
713699
x86_64-darwin
714700
x86_64-linux
715701

@@ -719,7 +705,7 @@ DEPENDENCIES
719705
annotaterb
720706
authtrail
721707
axe-core-rspec
722-
azure-storage-blob
708+
azure-blob
723709
blueprinter
724710
brakeman
725711
bugsnag
@@ -729,7 +715,6 @@ DEPENDENCIES
729715
capybara-screenshot
730716
caxlsx (~> 4.5)
731717
caxlsx_rails (~> 0.7.1)
732-
cgi (~> 0.5.1)
733718
cssbundling-rails (~> 1.4)
734719
database_cleaner-active_record
735720
delayed_job_active_record

config/storage.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ local:
2323

2424
# Use rails credentials:edit to set the Azure Storage secret (as azure_storage:storage_access_key)
2525
microsoft:
26-
service: AzureStorage
26+
service: AzureBlob
2727
storage_account_name: <%= ENV['STORAGE_ACCOUNT_NAME'] %>
2828
storage_access_key: <%= ENV['STORAGE_ACCESS_KEY'] %>
2929
container: <%= ENV['STORAGE_CONTAINER'] %>
Lines changed: 42 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,54 @@
1-
require "rails_helper"
2-
require "azure/storage/blob"
1+
# frozen_string_literal: true
32

4-
# Production stores every attachment on Azure (config.active_storage.service = :microsoft),
5-
# and azure-storage-common signs both uploads and download URLs with CGI.parse. Ruby 4.0's
6-
# stdlib cgi ships only escape/unescape, so the Gemfile has to pin the real cgi gem back in
7-
# — Rails 8 stopped pulling it in transitively. When that pin goes missing, every Active
8-
# Storage read and write 500s in production while CI stays green, because the test
9-
# environment uses Disk storage and never touches this code.
3+
require "rails_helper"
4+
require "active_storage/service/azure_blob_service"
5+
6+
# Production stores every attachment on Azure (config.active_storage.service = :microsoft,
7+
# service: AzureBlob). The azure-blob gem signs both uploads and download URLs with CGI.parse.
8+
# Ruby 4.0's stdlib cgi ships only escape/unescape, so a full cgi gem must resolve — azure-blob
9+
# declares it as a dependency, which pulls it back in (Rails 8 stopped doing so transitively).
10+
# When that goes missing, every Active Storage read and write 500s in production while CI stays
11+
# green, because the test environment uses Disk storage and never touches this code.
1012
#
11-
# See https://github.com/rubyforgood/casa/issues/7093
13+
# This is the regression guard for the retired azure-storage-blob → azure-blob migration.
14+
# See https://github.com/rubyforgood/casa/issues/7094 (and the earlier #7093).
1215
RSpec.describe "Azure Storage request signing" do
13-
let(:account_name) { "casaaccount" }
14-
let(:access_key) { Base64.strict_encode64("not-a-real-key") }
16+
let(:service) do
17+
ActiveStorage::Service::AzureBlobService.new(
18+
storage_account_name: "casaaccount",
19+
storage_access_key: Base64.strict_encode64("not-a-real-key"),
20+
container: "casa"
21+
)
22+
end
1523

1624
it "has the full CGI library, not Ruby 4.0's escape-only stdlib" do
1725
expect(CGI).to respond_to(:parse)
1826
end
1927

20-
# Exercised by CaseCourtReportsController#save_report when it attaches the .docx.
21-
it "signs an upload request" do
22-
signer = Azure::Storage::Common::Core::Auth::SharedKey.new(account_name, access_key)
23-
uri = URI("https://#{account_name}.blob.core.windows.net/casa/report.docx?comp=block&blockid=abc")
24-
25-
signature = signer.sign(:put, uri, {"Content-Type" => "application/vnd.openxmlformats-officedocument.wordprocessingml.document"})
26-
27-
expect(signature).to start_with("#{account_name}:")
28+
# Exercised by active_storage/blobs/redirect#show when a user downloads a court report.
29+
it "signs a private download URL locally, without hitting Azure" do
30+
url = service.url(
31+
"report.docx",
32+
expires_in: 5.minutes,
33+
filename: ActiveStorage::Filename.new("report.docx"),
34+
disposition: :attachment,
35+
content_type: "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
36+
)
37+
38+
expect(url).to start_with("https://casaaccount.blob.core.windows.net/casa/report.docx")
39+
expect(url).to include("sig=")
2840
end
2941

30-
# Exercised by active_storage/blobs/redirect#show when a user downloads the report.
31-
it "signs a download URL" do
32-
generator = Azure::Storage::Common::Core::Auth::SharedAccessSignature.new(account_name, access_key)
33-
uri = URI("https://#{account_name}.blob.core.windows.net/casa/report.docx")
34-
35-
signed_uri = generator.signed_uri(uri, false, service: "b", permissions: "r", expiry: "2050-01-01T00:00:00Z")
36-
37-
expect(signed_uri.query).to include("sig=")
42+
# Exercised whenever an attachment is uploaded (e.g. CaseCourtReportsController#save_report).
43+
it "signs a direct-upload URL locally, without hitting Azure" do
44+
url = service.url_for_direct_upload(
45+
"report.docx",
46+
expires_in: 5.minutes,
47+
content_type: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
48+
content_length: 1024,
49+
checksum: "md5-checksum"
50+
)
51+
52+
expect(url).to include("sig=")
3853
end
3954
end

0 commit comments

Comments
 (0)