diff --git a/.github/actions/setup-rubygems.org/action.yml b/.github/actions/setup-rubygems.org/action.yml index 0de437399f6..00969ffe0a7 100644 --- a/.github/actions/setup-rubygems.org/action.yml +++ b/.github/actions/setup-rubygems.org/action.yml @@ -14,7 +14,7 @@ runs: shell: bash run: | docker compose up -d --wait - - uses: ruby/setup-ruby@70da3bbf44ac06db1b0547ce2acc9380a5270d1e # v1.175.0 + - uses: ruby/setup-ruby@0cde4689ba33c09f1b890c1725572ad96751a3fc # v1.178.0 with: ruby-version: ${{ inputs.ruby-version }} bundler-cache: true diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index eac0ae9f772..6649a87632c 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -41,11 +41,11 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@cdcdbb579706841c47f7063dda365e292e5cad7a # v2.13.4 + uses: github/codeql-action/init@f079b8493333aace61c81488f8bd40919487bd9f # v3.25.7 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -58,7 +58,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@cdcdbb579706841c47f7063dda365e292e5cad7a # v2.13.4 + uses: github/codeql-action/autobuild@f079b8493333aace61c81488f8bd40919487bd9f # v3.25.7 # ℹ️ Command-line programs to run using the OS shell. # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun @@ -71,6 +71,6 @@ jobs: # ./location_of_script_within_repo/buildscript.sh - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@cdcdbb579706841c47f7063dda365e292e5cad7a # v2.13.4 + uses: github/codeql-action/analyze@f079b8493333aace61c81488f8bd40919487bd9f # v3.25.7 with: category: "/language:${{matrix.language}}" diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 5016d3bcb6d..ac1676db5be 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -14,10 +14,10 @@ jobs: name: Docker build (and optional push) runs-on: ubuntu-22.04 env: - RUBYGEMS_VERSION: 3.5.10 - RUBY_VERSION: 3.3.1 + RUBYGEMS_VERSION: 3.5.11 + RUBY_VERSION: 3.3.2 steps: - - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 - name: Set up Docker Buildx id: buildx uses: docker/setup-buildx-action@d70bba72b1f3fd22344832f00baa16ece964efeb # master diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 7813afcc38e..4b9f6a36260 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -12,8 +12,8 @@ jobs: name: Rubocop runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 - - uses: ruby/setup-ruby@cacc9f1c0b3f4eb8a16a6bb0ed10897b43b9de49 # v1.176.0 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + - uses: ruby/setup-ruby@0cde4689ba33c09f1b890c1725572ad96751a3fc # v1.178.0 with: bundler-cache: true - name: Rubocop @@ -22,8 +22,8 @@ jobs: name: Brakeman runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 - - uses: ruby/setup-ruby@cacc9f1c0b3f4eb8a16a6bb0ed10897b43b9de49 # v1.176.0 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + - uses: ruby/setup-ruby@0cde4689ba33c09f1b890c1725572ad96751a3fc # v1.178.0 with: bundler-cache: true - name: Brakeman @@ -32,8 +32,8 @@ jobs: name: Importmap Verify runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 - - uses: ruby/setup-ruby@cacc9f1c0b3f4eb8a16a6bb0ed10897b43b9de49 # v1.176.0 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + - uses: ruby/setup-ruby@0cde4689ba33c09f1b890c1725572ad96751a3fc # v1.178.0 with: bundler-cache: true - name: Importmap Verify @@ -50,8 +50,8 @@ jobs: steps: - name: login to Github Packages run: echo "${{ github.token }}" | docker login https://ghcr.io -u ${GITHUB_ACTOR} --password-stdin - - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 - - uses: ruby/setup-ruby@cacc9f1c0b3f4eb8a16a6bb0ed10897b43b9de49 # v1.176.0 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + - uses: ruby/setup-ruby@0cde4689ba33c09f1b890c1725572ad96751a3fc # v1.178.0 with: bundler-cache: true - name: krane render diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml index 936f783e1a1..7da88d79ed5 100644 --- a/.github/workflows/scorecards.yml +++ b/.github/workflows/scorecards.yml @@ -32,7 +32,7 @@ jobs: steps: - name: "Checkout code" - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v3.1.0 + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v3.1.0 with: persist-credentials: false @@ -67,6 +67,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@cdcdbb579706841c47f7063dda365e292e5cad7a # v2.13.4 + uses: github/codeql-action/upload-sarif@f079b8493333aace61c81488f8bd40919487bd9f # v3.25.7 with: sarif_file: results.sarif diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c19962b57b8..7bdd6304f7e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -26,10 +26,10 @@ jobs: matrix: rubygems: - name: locked - version: "3.5.10" + version: "3.5.11" - name: latest version: latest - ruby_version: ["3.3.1"] + ruby_version: ["3.3.2"] tests: - name: general command: test @@ -42,7 +42,7 @@ jobs: # Fail hard when Toxiproxy is not running to ensure all tests (even Toxiproxy optional ones) are passing REQUIRE_TOXIPROXY: true steps: - - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 - name: Setup rubygems.org uses: ./.github/actions/setup-rubygems.org @@ -64,6 +64,6 @@ jobs: - name: Upload coverage to Codecov if: matrix.rubygems.name == 'locked' && (success() || failure()) - uses: codecov/codecov-action@5ecb98a3c6b747ed38dc09f787459979aebb39be # v4.3.1 + uses: codecov/codecov-action@125fc84a9a348dbcf27191600683ec096ec9021c # v4.4.1 env: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} diff --git a/.ruby-version b/.ruby-version index bea438e9ade..47725433179 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -3.3.1 +3.3.2 diff --git a/Dockerfile b/Dockerfile index 26bc08ea152..29f9ce05760 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,8 +1,8 @@ # syntax = docker/dockerfile:1.4 # Make sure RUBY_VERSION matches the Ruby version in .ruby-version and Gemfile -ARG RUBY_VERSION=3.3.1 -ARG ALPINE_VERSION=3.18 +ARG RUBY_VERSION=3.3.2 +ARG ALPINE_VERSION=3.20 FROM ruby:$RUBY_VERSION-alpine${ALPINE_VERSION} as base # Install packages diff --git a/Gemfile b/Gemfile index 852330dbe8b..64979e314ed 100644 --- a/Gemfile +++ b/Gemfile @@ -5,17 +5,17 @@ ruby file: ".ruby-version" gem "rails", "~> 7.1.0", ">= 7.1.3.2" gem "rails-i18n", "~> 7.0" -gem "aws-sdk-s3", "~> 1.149" -gem "aws-sdk-sqs", "~> 1.73" +gem "aws-sdk-s3", "~> 1.151" +gem "aws-sdk-sqs", "~> 1.74" gem "bootsnap", "~> 1.18" gem "clearance", "~> 2.7" gem "dalli", "~> 3.2" gem "ddtrace", "~> 1.23", require: "ddtrace/auto_instrument" gem "dogstatsd-ruby", "~> 5.5" -gem "google-protobuf", "~> 4.26" +gem "google-protobuf", "~> 4.27" gem "faraday", "~> 2.9" gem "faraday-retry", "~> 2.2" -gem "good_job", "~> 3.28" +gem "good_job", "~> 3.29" gem "gravtastic", "~> 3.2" gem "high_voltage", "~> 3.1" gem "honeybadger", "~> 5.5.1" # see https://github.com/rubygems/rubygems.org/pull/4598 @@ -34,7 +34,7 @@ gem "rack", "~> 3.0" gem "rackup", "~> 2.1" gem "rack-utf8_sanitizer", "~> 1.8" gem "rbtrace", "~> 0.5.1" -gem "rdoc", "~> 6.6" +gem "rdoc", "~> 6.7" gem "roadie-rails", "~> 3.2" gem "ruby-magic", "~> 0.6" gem "shoryuken", "~> 6.2", require: false @@ -57,7 +57,7 @@ gem "strong_migrations", "~> 1.8" gem "phlex-rails", "~> 1.2" gem "discard", "~> 1.3" gem "user_agent_parser", "~> 2.17" -gem "pghero", "~> 3.4" +gem "pghero", "~> 3.5" # Admin dashboard gem "avo", "~> 2.51" @@ -116,21 +116,21 @@ group :development do gem "rails-erd", "~> 1.7" gem "listen", "~> 3.9" gem "letter_opener", "~> 1.10" - gem "letter_opener_web", "~> 2.0" + gem "letter_opener_web", "~> 3.0" gem "derailed_benchmarks", "~> 2.1" gem "memory_profiler", "~> 1.0" end group :test do - gem "minitest", "~> 5.22", require: false + gem "minitest", "~> 5.23", require: false gem "capybara", "~> 3.40" gem "launchy", "~> 3.0" gem "rack-test", "~> 2.1", require: "rack/test" gem "rails-controller-testing", "~> 1.0" - gem "mocha", "~> 2.2", require: false + gem "mocha", "~> 2.3", require: false gem "shoulda-context", "~> 3.0.0.rc1" gem "shoulda-matchers", "~> 6.2" - gem "selenium-webdriver", "~> 4.20" + gem "selenium-webdriver", "~> 4.21" gem "webmock", "~> 3.23" gem "simplecov", "~> 0.22", require: false gem "simplecov-cobertura", "~> 2.1", require: false diff --git a/Gemfile.lock b/Gemfile.lock index b729f9020a6..da13327b940 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -110,20 +110,20 @@ GEM zeitwerk (>= 2.6.2) awrence (1.2.1) aws-eventstream (1.3.0) - aws-partitions (1.925.0) - aws-sdk-core (3.194.2) + aws-partitions (1.930.0) + aws-sdk-core (3.196.1) aws-eventstream (~> 1, >= 1.3.0) aws-partitions (~> 1, >= 1.651.0) aws-sigv4 (~> 1.8) jmespath (~> 1, >= 1.6.1) - aws-sdk-kms (1.80.0) + aws-sdk-kms (1.81.0) aws-sdk-core (~> 3, >= 3.193.0) aws-sigv4 (~> 1.1) - aws-sdk-s3 (1.149.1) + aws-sdk-s3 (1.151.0) aws-sdk-core (~> 3, >= 3.194.0) aws-sdk-kms (~> 1) aws-sigv4 (~> 1.8) - aws-sdk-sqs (1.73.0) + aws-sdk-sqs (1.74.0) aws-sdk-core (~> 3, >= 3.193.0) aws-sigv4 (~> 1.1) aws-sigv4 (1.8.0) @@ -154,7 +154,7 @@ GEM regexp_parser (>= 1.5, < 3.0) xpath (~> 3.2) cbor (0.5.9.8) - chartkick (5.0.6) + chartkick (5.0.7) childprocess (5.0.0) choice (0.2.0) chunky_png (1.4.0) @@ -168,7 +168,7 @@ GEM railties (>= 5.0) coderay (1.1.3) compact_index (0.15.0) - concurrent-ruby (1.2.3) + concurrent-ruby (1.3.1) connection_pool (2.4.1) cose (1.3.0) cbor (~> 0.5.9) @@ -254,14 +254,15 @@ GEM ffi (~> 1.0) globalid (1.2.1) activesupport (>= 6.1) - good_job (3.28.2) + good_job (3.29.2) activejob (>= 6.0.0) activerecord (>= 6.0.0) concurrent-ruby (>= 1.0.2) fugit (>= 1.1) railties (>= 6.0.0) thor (>= 0.14.1) - google-protobuf (4.26.1) + google-protobuf (4.27.0) + bigdecimal rake (>= 13) gravtastic (3.2.6) groupdate (6.4.0) @@ -341,10 +342,10 @@ GEM http (>= 4.4.1, < 6.0.0) letter_opener (1.10.0) launchy (>= 2.2, < 4) - letter_opener_web (2.0.0) - actionmailer (>= 5.2) - letter_opener (~> 1.7) - railties (>= 5.2) + letter_opener_web (3.0.0) + actionmailer (>= 6.1) + letter_opener (~> 1.9) + railties (>= 6.1) rexml libdatadog (7.0.0.1.0) libddwaf (1.14.0.0.0) @@ -358,7 +359,7 @@ GEM loofah (2.22.0) crass (~> 1.0.2) nokogiri (>= 1.12.0) - lookbook (2.3.0) + lookbook (2.3.1) activemodel css_parser htmlbeautifier (~> 1.3) @@ -391,7 +392,7 @@ GEM mini_histogram (0.3.1) mini_mime (1.1.5) mini_portile2 (2.8.6) - minitest (5.22.3) + minitest (5.23.1) minitest-gcstats (1.3.1) minitest (~> 5.0) minitest-reporters (1.6.1) @@ -399,7 +400,7 @@ GEM builder minitest (>= 5.0) ruby-progressbar - mocha (2.2.0) + mocha (2.3.0) ruby2_keywords (>= 0.0.5) msgpack (1.7.2) multi_json (1.15.0) @@ -474,7 +475,7 @@ GEM pg (1.5.6) pg_query (5.1.0) google-protobuf (>= 3.22.3) - pghero (3.4.1) + pghero (3.5.0) activerecord (>= 6) phlex (1.10.2) phlex-rails (1.2.1) @@ -499,7 +500,7 @@ GEM activesupport (>= 3.0.0) pwned (2.3.0) raabro (1.4.0) - racc (1.7.3) + racc (1.8.0) rack (3.0.11) rack-attack (6.7.0) rack (>= 1.0, < 4) @@ -576,13 +577,14 @@ GEM ffi (>= 1.0.6) msgpack (>= 0.4.3) optimist (>= 3.0.0) - rdoc (6.6.3.1) + rdoc (6.7.0) psych (>= 4.0.0) redcarpet (3.6.0) regexp_parser (2.9.0) - reline (0.5.6) + reline (0.5.8) io-console (~> 0.5) - rexml (3.2.6) + rexml (3.2.8) + strscan (>= 3.0.9) roadie (5.2.1) css_parser (~> 1.4) nokogiri (~> 1.15) @@ -644,7 +646,7 @@ GEM searchkick (5.3.1) activemodel (>= 6.1) hashie - selenium-webdriver (4.20.1) + selenium-webdriver (4.21.1) base64 (~> 0.2) rexml (~> 3.2, >= 3.2.5) rubyzip (>= 1.2.2, < 3.0) @@ -684,6 +686,7 @@ GEM stringio (3.1.0) strong_migrations (1.8.0) activerecord (>= 5.2) + strscan (3.1.0) swd (2.0.3) activesupport (>= 3) attr_required (>= 0.0.5) @@ -738,7 +741,7 @@ GEM activesupport faraday (~> 2.0) faraday-follow_redirects - webmock (3.23.0) + webmock (3.23.1) addressable (>= 2.8.0) crack (>= 0.3.2) hashdiff (>= 0.4.0, < 2.0.0) @@ -752,7 +755,7 @@ GEM xpath (3.2.0) nokogiri (~> 1.8) yard (0.9.36) - zeitwerk (2.6.13) + zeitwerk (2.6.15) PLATFORMS ruby @@ -762,8 +765,8 @@ DEPENDENCIES amazing_print (~> 1.6) autoprefixer-rails (~> 10.4) avo (~> 2.51) - aws-sdk-s3 (~> 1.149) - aws-sdk-sqs (~> 1.73) + aws-sdk-s3 (~> 1.151) + aws-sdk-sqs (~> 1.74) bcrypt (~> 3.1) bootsnap (~> 1.18) brakeman (~> 6.1) @@ -784,8 +787,8 @@ DEPENDENCIES faraday (~> 2.9) faraday-retry (~> 2.2) faraday_middleware-aws-sigv4 (~> 1.0) - good_job (~> 3.28) - google-protobuf (~> 4.26) + good_job (~> 3.29) + google-protobuf (~> 4.27) gravtastic (~> 3.2) groupdate (~> 6.2) high_voltage (~> 3.1) @@ -796,16 +799,16 @@ DEPENDENCIES launchdarkly-server-sdk (~> 8.4) launchy (~> 3.0) letter_opener (~> 1.10) - letter_opener_web (~> 2.0) + letter_opener_web (~> 3.0) listen (~> 3.9) lookbook (~> 2.3) mail (~> 2.8) maintenance_tasks (~> 2.7) memory_profiler (~> 1.0) - minitest (~> 5.22) + minitest (~> 5.23) minitest-gcstats (~> 1.3) minitest-reporters (~> 1.6) - mocha (~> 2.2) + mocha (~> 2.3) observer (~> 0.1.2) octokit (~> 8.1) omniauth (~> 2.1) @@ -815,7 +818,7 @@ DEPENDENCIES opensearch-ruby (~> 3.3) pg (~> 1.5) pg_query (~> 5.1) - pghero (~> 3.4) + pghero (~> 3.5) phlex-rails (~> 1.2) pp (= 0.5.0) prosopite (~> 1.4) @@ -833,7 +836,7 @@ DEPENDENCIES rails-i18n (~> 7.0) rails_semantic_logger (~> 4.14) rbtrace (~> 0.5.1) - rdoc (~> 6.6) + rdoc (~> 6.7) roadie-rails (~> 3.2) rotp (~> 6.2) rqrcode (~> 2.1) @@ -845,7 +848,7 @@ DEPENDENCIES rubocop-rails (~> 2.18) ruby-magic (~> 0.6) searchkick (~> 5.3) - selenium-webdriver (~> 4.20) + selenium-webdriver (~> 4.21) shoryuken (~> 6.2) shoulda-context (~> 3.0.0.rc1) shoulda-matchers (~> 6.2) @@ -892,11 +895,11 @@ CHECKSUMS avo (2.51.0) sha256=0d5785cda01b5b0d2575e7419cda4dc7a5d7805068f160d48ecc7458ee74ec03 awrence (1.2.1) sha256=dd1d214c12a91f449d1ef81d7ee3babc2816944e450752e7522c65521872483e aws-eventstream (1.3.0) sha256=f1434cc03ab2248756eb02cfa45e900e59a061d7fbdc4a9fd82a5dd23d796d3f - aws-partitions (1.925.0) sha256=e41bce6567dd71532618fc6003f8aaedeacca29f18443690d714d73d5f4baaeb - aws-sdk-core (3.194.2) sha256=f925fb739cd093e5834910aed85aba5ac8d1b210f26c2cf51f0daf932cc77567 - aws-sdk-kms (1.80.0) sha256=55621357f04d43cf03fd4ca1d7113a52799f5ea482f2417557140d702736eae5 - aws-sdk-s3 (1.149.1) sha256=664e608190d42b486dc79b5dc65e7c2240923902a9833063327a9d831226a46a - aws-sdk-sqs (1.73.0) sha256=87dac3a9e06908ffed136ff7e038637cc5f34de7a99a7d64192e4cb7c66ccde0 + aws-partitions (1.930.0) sha256=6cfce1550e3586813dc2765bd5b95c59e7471739f417d9c66f742750dca3994b + aws-sdk-core (3.196.1) sha256=e36bfec78d841041acb4424a728e35fc2c324e4ee6f07e1c301bbdf4c69d8438 + aws-sdk-kms (1.81.0) sha256=b15dd1e840756a13f27d4f3d8308571f97a4902e0a21c753ea9be14138a4f496 + aws-sdk-s3 (1.151.0) sha256=9e40e64f3ea112b33fdbb0416b6b44247372b983f6a7a9c30fa9b5627a4f7008 + aws-sdk-sqs (1.74.0) sha256=cc4951b044803a7e6ad51d4e24f4c182dbca404afc6528183f207de4be1f1bda aws-sigv4 (1.8.0) sha256=84dd99768b91b93b63d1d8e53ee837cfd06ab402812772a7899a78f9f9117cbc base64 (0.2.0) sha256=0f25e9b21a02a0cc0cea8ef92b2041035d39350946e8789c562b2d1a3da01507 bcrypt (3.1.20) sha256=8410f8c7b3ed54a3c00cd2456bf13917d695117f033218e2483b2e40b0784099 @@ -912,14 +915,14 @@ CHECKSUMS byebug (11.1.3) sha256=2485944d2bb21283c593d562f9ae1019bf80002143cc3a255aaffd4e9cf4a35b capybara (3.40.0) sha256=42dba720578ea1ca65fd7a41d163dd368502c191804558f6e0f71b391054aeef cbor (0.5.9.8) sha256=9ee097fc58d9bc5e406d112cd2d4e112c7354ec16f8b6ff34e4732c1e44b4eb7 - chartkick (5.0.6) sha256=96c5984471d4c2017b28914bd1bcda7f9e8a3a9d1903059aaadc7a4044c87193 + chartkick (5.0.7) sha256=fe52cfd34a51ff0c42dabe26c59a827ffc5c74de56816eddcb74b7c639938893 childprocess (5.0.0) sha256=0746b7ab1d6c68156e64a3767631d7124121516192c0492929a7f0af7310d835 choice (0.2.0) sha256=a19617f7dfd4921b38a85d0616446620de685a113ec6d1ecc85bdb67bf38c974 chunky_png (1.4.0) sha256=89d5b31b55c0cf4da3cf89a2b4ebc3178d8abe8cbaf116a1dba95668502fdcfe clearance (2.7.1) sha256=6604beacb8abe4ba939da41491148d8ff965f4484bba946bb50a61be61683f0d coderay (1.1.3) sha256=dc530018a4684512f8f38143cd2a096c9f02a1fc2459edcfe534787a7fc77d4b compact_index (0.15.0) sha256=5c6c404afca8928a7d9f4dde9524f6e1610db17e675330803055db282da84a8b - concurrent-ruby (1.2.3) sha256=82fdd3f8a0816e28d513e637bb2b90a45d7b982bdf4f3a0511722d2e495801e2 + concurrent-ruby (1.3.1) sha256=c369f1d0875b42295fe0fabc321065f3cfeab8c32c526c01b6b05af1efc8b0ce connection_pool (2.4.1) sha256=0f40cf997091f1f04ff66da67eabd61a9fe0d4928b9a3645228532512fab62f4 cose (1.3.0) sha256=63247c66a5bc76e53926756574fe3724cc0a88707e358c90532ae2a320e98601 crack (1.0.0) sha256=c83aefdb428cdc7b66c7f287e488c796f055c0839e6e545fec2c7047743c4a49 @@ -958,8 +961,8 @@ CHECKSUMS fugit (1.11.0) sha256=addc9cd3031611921d1dbac094de3a645bc8858828639fd035c9cedd3b460bb9 get_process_mem (0.2.7) sha256=4afd3c3641dd6a817c09806c7d6d509d8a9984512ac38dea8b917426bbf77eba globalid (1.2.1) sha256=70bf76711871f843dbba72beb8613229a49429d1866828476f9c9d6ccc327ce9 - good_job (3.28.2) sha256=ec7d6580acf4b090b6d45c72618f3e32889c0078c7d8a5f50f2813dd68f673c4 - google-protobuf (4.26.1) sha256=52059a231bd6521728f5e278b64f1dea9d9ffecccbde7d53ef719721bb074493 + good_job (3.29.2) sha256=a9e3854a103cf0b64334b248fdfb813cbf07d62a96e65b07134d4c2f4d48b994 + google-protobuf (4.27.0) sha256=5e679347abc4721a3346913b8f69640a4ee13e0105d605b1da226b25346cd88d gravtastic (3.2.6) sha256=ef98abcecf7c402b61cff1ae7c50a2c6d97dd22bac21ea9b421ce05bc03d734f groupdate (6.4.0) sha256=65940645bf2a48f9b2d10ab7a1d19bdc78f3c89559d8fce39cea3448a15aec54 hashdiff (1.1.0) sha256=b5465f0e7375f1ee883f53a766ece4dbc764b7674a7c5ffd76e79b2f5f6fc9c9 @@ -993,13 +996,13 @@ CHECKSUMS launchy (3.0.1) sha256=b7fa60bda0197cf57614e271a250a8ca1f6a34ab889a3c73f67ec5d57c8a7f2c ld-eventsource (2.2.2) sha256=5ea087a6f06bbd8e325d2c1aaead50f37f13d025b952985739e9380a78a96beb letter_opener (1.10.0) sha256=2ff33f2e3b5c3c26d1959be54b395c086ca6d44826e8bf41a14ff96fdf1bdbb2 - letter_opener_web (2.0.0) sha256=33860ad41e1785d75456500e8ca8bba8ed71ee6eaf08a98d06bbab67c5577b6f + letter_opener_web (3.0.0) sha256=3f391efe0e8b9b24becfab5537dfb17a5cf5eb532038f947daab58cb4b749860 libdatadog (7.0.0.1.0) sha256=b4321485dd0f664ad43540cacb5ace0fedba064ad978f97510172c1ad6940316 libddwaf (1.14.0.0.0) sha256=b91ea9675f7d79d1cd10dd6513e3706760ac442cb8902164fbcef79b7082a8fd listen (3.9.0) sha256=db9e4424e0e5834480385197c139cb6b0ae0ef28cc13310cfd1ca78377d59c67 llhttp-ffi (0.5.0) sha256=496f40ad44bcbf99de02da1f26b1ad64e6593cd487b931508a86228e2a3af0fa loofah (2.22.0) sha256=10d76e070c86b12fec74b6a9515fd1940f4459198b991342d0a7897d86c372fe - lookbook (2.3.0) sha256=0d225070902451124cd37279112d773c05f3b396fc2d86f9d971da559fd4b6bd + lookbook (2.3.1) sha256=d6ba294f3bd2fe8c39c30530a12aa99d42d202a5d6bff3d3edcafd6d2978dc39 mail (2.8.1) sha256=ec3b9fadcf2b3755c78785cb17bc9a0ca9ee9857108a64b6f5cfc9c0b5bfc9ad maintenance_tasks (2.7.0) sha256=0857d732e6c3078a501a1552d0ffb6321c900dc821c15bf09b6457931d6ccbcb marcel (1.0.4) sha256=0d5649feb64b8f19f3d3468b96c680bae9746335d02194270287868a661516a4 @@ -1010,10 +1013,10 @@ CHECKSUMS mini_histogram (0.3.1) sha256=6a114b504e4618b0e076cc672996036870f7cc6f16b8e5c25c0c637726d2dd94 mini_mime (1.1.5) sha256=8681b7e2e4215f2a159f9400b5816d85e9d8c6c6b491e96a12797e798f8bccef mini_portile2 (2.8.6) sha256=4e2ab09b924906fd42c0b6eb72816db6a435d0404e9cbdcc5d722c133b493991 - minitest (5.22.3) sha256=ea84676290cb5e2b4f31f25751af6050aa90d3e43e4337141c3e3e839611981e + minitest (5.23.1) sha256=f1e8f8d6ffd96fb17339ce50768bcbbdbbadff5073cb9583d084403877a77abe minitest-gcstats (1.3.1) sha256=cb25490f93aac02e3a5ff307e560d41afcdcafa7952c1c32efdeb9886b1f4711 minitest-reporters (1.6.1) sha256=f8fe74e46ab40dada29402f55ca236368d0af65afc410db4219189b7a1c0fc38 - mocha (2.2.0) sha256=8ba0d52c035973f950a9a4fa29f6f1022138518dfb2c846d9db2597aa9912431 + mocha (2.3.0) sha256=f3af2eee619afe9b67a960a24fcdea3a05f548b528e6974458c89121a0204408 msgpack (1.7.2) sha256=59ab62fd8a4d0dfbde45009f87eb6f158ab2628a7c48886b0256f175166baaa8 multi_json (1.15.0) sha256=1fd04138b6e4a90017e8d1b804c039031399866ff3fbabb7822aea367c78615d multi_xml (0.7.1) sha256=4fce100c68af588ff91b8ba90a0bb3f0466f06c909f21a32f4962059140ba61b @@ -1042,7 +1045,7 @@ CHECKSUMS parser (3.3.0.5) sha256=7748313e505ca87045dc0465c776c802043f777581796eb79b1654c5d19d2687 pg (1.5.6) sha256=4bc3ad2438825eea68457373555e3fd4ea1a82027b8a6be98ef57c0d57292b1c pg_query (5.1.0) sha256=b7f7f47c864f08ccbed46a8244906fb6ee77ee344fd27250717963928c93145d - pghero (3.4.1) sha256=7f949828119ab17de22ebaef1854ab8c738106bdc31bee3ac0acd0462c0efa88 + pghero (3.5.0) sha256=7b459d383673e358017d0dd210c11b6a82bbfb340c73236ba0e50bb6c0351e6a phlex (1.10.2) sha256=49dca7df081258f937be5e4ee0a81b11743f2b4fea25ac7537912b9c9344b1e6 phlex-rails (1.2.1) sha256=1d80709c02114cda869951d22bfca189b5f208d1eb89f2e6fafbe3c0240a822f pp (0.5.0) sha256=f8f40bc2ba9e1ab351b9458151da3a89f46034f7f599a8e0a06abb9b9f4411dd @@ -1056,7 +1059,7 @@ CHECKSUMS pundit (2.3.2) sha256=7ca09a5801ebaedad1966f7eb0b1c52ecb8c94b3b6ab70122cb22856ac187fa3 pwned (2.3.0) sha256=63f5a9576f109203684e9dd053f815649fd5bc0a0348b7190568272641b22353 raabro (1.4.0) sha256=d4fa9ff5172391edb92b242eed8be802d1934b1464061ae5e70d80962c5da882 - racc (1.7.3) sha256=b785ab8a30ec43bce073c51dbbe791fd27000f68d1c996c95da98bf685316905 + racc (1.8.0) sha256=09349a65c37c4fe710a435f25c9f1652e39f29ad6b1fa08d4a8d30c0553d3a08 rack (3.0.11) sha256=a08473678160760d9085ebe14508a42add18cde4217107b4b1aa01c8f14ff98c rack-attack (6.7.0) sha256=3ca47e8f66cd33b2c96af53ea4754525cd928ed3fa8da10ee6dad0277791d77c rack-oauth2 (2.2.1) sha256=c73aa87c508043e2258f02b4fb110cacba9b37d2ccf884e22487d014a120d1a5 @@ -1078,11 +1081,11 @@ CHECKSUMS rb-fsevent (0.11.2) sha256=43900b972e7301d6570f64b850a5aa67833ee7d87b458ee92805d56b7318aefe rb-inotify (0.10.1) sha256=050062d4f31d307cca52c3f6a7f4b946df8de25fc4bd373e1a5142e41034a7ca rbtrace (0.5.1) sha256=e8cba64d462bfb8ba102d7be2ecaacc789247d52ac587d8003549d909cb9c5dc - rdoc (6.6.3.1) sha256=39f7b749229ab5ad9d21c81586151c1dd7a549fa8be4070ee09b524f9c656345 + rdoc (6.7.0) sha256=b17d5f0f57b0853d7b880d4360a32c7caf8dbb81f8503a36426df809e617f379 redcarpet (3.6.0) sha256=8ad1889c0355ff4c47174af14edd06d62f45a326da1da6e8a121d59bdcd2e9e9 regexp_parser (2.9.0) sha256=81a00ba141cec0d4b4bf58cb80cd9193e5180836d3fa6ef623f7886d3ba8bdd9 - reline (0.5.6) sha256=0cfa889a415bdaa7b2965f976af922c0a226a2959123911606c5e5099add573e - rexml (3.2.6) sha256=e0669a2d4e9f109951cb1fde723d8acd285425d81594a2ea929304af50282816 + reline (0.5.8) sha256=467faa77616677035786819d7d2e6cfa048be64542c10d8eda81dacd939aea02 + rexml (3.2.8) sha256=0908a86381d9f973824680df4e0a75422766272f03b1c0e49db7e79c23db1135 roadie (5.2.1) sha256=e4a4f61ce792bd91b228b6844b4bad6b160cdc1b8df86c81a8b983082a5001d6 roadie-rails (3.2.0) sha256=90a534857fcfe9fdbe4f9ebfdbc47e5d33462c4f36f478fc80ba6a7b6257433f rotp (6.3.0) sha256=75d40087e65ed0d8022c33055a6306c1c400d1c12261932533b5d6cbcd868854 @@ -1107,7 +1110,7 @@ CHECKSUMS sassc-embedded (1.70.1) sha256=a95172c9c6725dfc412c702a0e705fb8a5bcb3aac2a32586b18e5432987103d3 sawyer (0.9.2) sha256=fa3a72d62a4525517b18857ddb78926aab3424de0129be6772a8e2ba240e7aca searchkick (5.3.1) sha256=dc1181543f6a68354e380651f235fa7f3df6a09e4cd67fc284dc293fa9860f57 - selenium-webdriver (4.20.1) sha256=560ca00d45bed16d661089da674290ce81564949888daa1f8659fe77fd39a2ac + selenium-webdriver (4.21.1) sha256=c30b64014532fc5156c60797985f839f36adbe60ff4653e7112b008dc1c83263 semantic (1.6.1) sha256=3cdbb48f59198ebb782a3fdfb87b559e0822a311610db153bae22777a7d0c163 semantic_logger (4.15.0) sha256=ec4f56122b5d2e2117d148b86c69fb62c1194a2b01a271be04ba8678a85f81ff shoryuken (6.2.1) sha256=95ddc0a717624a54e799d25a0a05100cb5a0c3728a96211935c214faaf16b3b6 @@ -1124,6 +1127,7 @@ CHECKSUMS stimulus-rails (1.3.3) sha256=4d1f9ab1d64e605f4c9cdd4cc530a9538b510606d32d02249d106256845c562c stringio (3.1.0) sha256=c1f6263ae03a15025e51194ab19b06b15e06adcaaedb7f5f6c06ab60f5d67718 strong_migrations (1.8.0) sha256=18de155ebcddf44e60e74f9a6c0b4bfd2d1e576dfe1c67f4aafc4ec5b0442f5d + strscan (3.1.0) sha256=01b8a81d214fbf7b5308c6fb51b5972bbfc4a6aa1f166fd3618ba97e0fcd5555 swd (2.0.3) sha256=4cdbe2a4246c19f093fce22e967ec3ebdd4657d37673672e621bf0c7eb770655 tailwindcss-rails (2.6.0) sha256=1450c61d0853552017932231e37ee96539f70ac9c9ae9fcd1514915336d5365a terser (1.2.2) sha256=86ddfa0de7fa8f6c8fd34ad611596f787a77e21bed3db08b90e7c30942d20288 @@ -1145,7 +1149,7 @@ CHECKSUMS view_component (3.12.1) sha256=f2ce2ad2945389f4bbd4ff77465605e9019041e5c804d16d093791be2542b18b webauthn (3.1.0) sha256=e545fcf17d8a6b821161a37c1c4bc8c3d2ead0ff6ff3b098f57f417e731790b7 webfinger (2.1.3) sha256=567a52bde77fb38ca6b67e55db755f988766ec4651c1d24916a65aa70540695c - webmock (3.23.0) sha256=100787435c1f556129a238c11cc7cbee38cb9c2864709c6a0dcdcf822545f31f + webmock (3.23.1) sha256=0fa738c0767d1c4ec8cc57f6b21998f0c238c8a5b32450df1c847f2767140d95 webrick (1.8.1) sha256=19411ec6912911fd3df13559110127ea2badd0c035f7762873f58afc803e158f websocket (1.2.10) sha256=2cc1a4a79b6e63637b326b4273e46adcddf7871caa5dc5711f2ca4061a629fa8 websocket-driver (0.7.6) sha256=f69400be7bc197879726ad8e6f5869a61823147372fd8928836a53c2c741d0db @@ -1153,10 +1157,10 @@ CHECKSUMS xml-simple (1.1.9) sha256=d21131e519c86f1a5bc2b6d2d57d46e6998e47f18ed249b25cad86433dbd695d xpath (3.2.0) sha256=6dfda79d91bb3b949b947ecc5919f042ef2f399b904013eb3ef6d20dd3a4082e yard (0.9.36) sha256=5505736c1b00c926f71053a606ab75f02070c5960d0778b901fe9d8b0a470be4 - zeitwerk (2.6.13) sha256=2db861b021d020f48b5bf51613f355fcd041bf0a27ad8c748fa88fa974c5c7d4 + zeitwerk (2.6.15) sha256=b2e68622ba95680a357430c89e1777d6e6796d63c7c02e8790cc38f4c33822cf RUBY VERSION - ruby 3.3.1p55 + ruby 3.3.2p78 BUNDLED WITH - 2.5.10 + 2.5.11 diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index a9f18239187..6d8e75d7b18 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -30,10 +30,6 @@ def create end def webauthn_create - unless mfa_session_active? - login_failure(t("multifactor_auths.session_expired")) - return - end return login_failure(@webauthn_error) unless webauthn_credential_verified? record_mfa_login_duration(mfa_type: "webauthn") @@ -60,8 +56,6 @@ def otp_create record_mfa_login_duration(mfa_type: "otp") do_login(two_factor_label: "OTP", two_factor_method: "otp", authentication_method: "password") - elsif !mfa_session_active? - login_failure(t("multifactor_auths.session_expired")) else login_failure(t("multifactor_auths.incorrect_otp")) end @@ -136,7 +130,7 @@ def find_user end def find_mfa_user - @user = User.find_by(id: session[:mfa_user]) if session[:mfa_user] + @user = User.find_by(id: session[:mfa_user]) if mfa_session_active? && session[:mfa_user] return if @user delete_mfa_session login_failure t("multifactor_auths.session_expired") diff --git a/app/models/rubygem.rb b/app/models/rubygem.rb index 846a643dfa7..2c52d9f1588 100644 --- a/app/models/rubygem.rb +++ b/app/models/rubygem.rb @@ -130,9 +130,9 @@ def self.current_rubygems_release end def all_errors(version = nil) - [self, linkset, version].compact.map do |ar| + [self, linkset, version].compact.flat_map do |ar| ar.errors.full_messages - end.flatten.join(", ") + end.join(", ") end has_many :public_versions, -> { by_position.published }, class_name: "Version", inverse_of: :rubygem diff --git a/app/models/user.rb b/app/models/user.rb index 0fb39f8afc4..32ddbd61510 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -64,7 +64,8 @@ class User < ApplicationRecord has_many :oidc_pending_trusted_publishers, class_name: "OIDC::PendingTrustedPublisher", inverse_of: :user, dependent: :destroy has_many :oidc_rubygem_trusted_publishers, through: :rubygems, class_name: "OIDC::RubygemTrustedPublisher" - validates :email, length: { maximum: Gemcutter::MAX_FIELD_LENGTH }, format: { with: URI::MailTo::EMAIL_REGEXP }, presence: true + validates :email, length: { maximum: Gemcutter::MAX_FIELD_LENGTH }, format: { with: URI::MailTo::EMAIL_REGEXP }, presence: true, +uniqueness: { case_sensitive: false } validates :unconfirmed_email, length: { maximum: Gemcutter::MAX_FIELD_LENGTH }, format: { with: URI::MailTo::EMAIL_REGEXP }, allow_blank: true validates :handle, uniqueness: { case_sensitive: false }, allow_nil: true, if: :handle_changed? @@ -96,12 +97,16 @@ def self.authenticate(who, password) # to UTF-8. who = who.encode(Encoding::UTF_8) - user = find_by(email: who.downcase) || find_by(handle: who) + user = find_by_email(who) || find_by(handle: who) user if user&.authenticated?(password) rescue BCrypt::Errors::InvalidHash, Encoding::UndefinedConversionError nil end + def self.find_by_email(email) + find_by("lower(email) = lower(?)", email) + end + def self.find_by_slug!(slug) raise ActiveRecord::RecordNotFound if slug.blank? find_by(id: slug) || find_by!(handle: slug) @@ -114,7 +119,7 @@ def self.find_by_slug(slug) def self.find_by_name(name) return if name.blank? - find_by(email: name) || find_by(handle: name) + find_by_email(name) || find_by(handle: name) end def self.find_by_blocked(slug) @@ -135,7 +140,7 @@ def self.ownership_request_notifiable_owners end def self.normalize_email(email) - super + email.to_s.gsub(/\s+/, "") rescue ArgumentError => e Rails.error.report(e, handled: true) "" diff --git a/app/policies/api_key_policy.rb b/app/policies/api_key_policy.rb index b99a290268c..bea701cfbd8 100644 --- a/app/policies/api_key_policy.rb +++ b/app/policies/api_key_policy.rb @@ -5,11 +5,11 @@ def resolve end end - def avo_show? - Pundit.policy!(user, record.owner).avo_show? - end - has_association :api_key_rubygem_scope has_association :ownership has_association :oidc_id_token + + def avo_show? + Pundit.policy!(user, record.owner).avo_show? + end end diff --git a/app/policies/deletion_policy.rb b/app/policies/deletion_policy.rb index 76e4740bfd4..491ccd15578 100644 --- a/app/policies/deletion_policy.rb +++ b/app/policies/deletion_policy.rb @@ -5,6 +5,8 @@ def resolve end end + has_association :version + def avo_index? rubygems_org_admin? end @@ -12,6 +14,4 @@ def avo_index? def avo_show? rubygems_org_admin? end - - has_association :version end diff --git a/app/policies/events/rubygem_event_policy.rb b/app/policies/events/rubygem_event_policy.rb index b2845a90c39..0c1644d8efa 100644 --- a/app/policies/events/rubygem_event_policy.rb +++ b/app/policies/events/rubygem_event_policy.rb @@ -5,9 +5,9 @@ def resolve end end - def avo_index? = rubygems_org_admin? - def avo_show? = rubygems_org_admin? - has_association :rubygem has_association :ip_address + + def avo_index? = rubygems_org_admin? + def avo_show? = rubygems_org_admin? end diff --git a/app/policies/events/user_event_policy.rb b/app/policies/events/user_event_policy.rb index 255eefb8d63..bbe74fb1457 100644 --- a/app/policies/events/user_event_policy.rb +++ b/app/policies/events/user_event_policy.rb @@ -5,9 +5,9 @@ def resolve end end - def avo_index? = rubygems_org_admin? - def avo_show? = rubygems_org_admin? - has_association :user has_association :ip_address + + def avo_index? = rubygems_org_admin? + def avo_show? = rubygems_org_admin? end diff --git a/app/policies/geoip_info_policy.rb b/app/policies/geoip_info_policy.rb index 940a31263e6..42f0aa258dd 100644 --- a/app/policies/geoip_info_policy.rb +++ b/app/policies/geoip_info_policy.rb @@ -5,8 +5,8 @@ def resolve end end + has_association :ip_addresses + def avo_index? = rubygems_org_admin? def avo_show? = rubygems_org_admin? - - has_association :ip_addresses end diff --git a/app/policies/ip_address_policy.rb b/app/policies/ip_address_policy.rb index 26a9c0af85a..fda18b72cfa 100644 --- a/app/policies/ip_address_policy.rb +++ b/app/policies/ip_address_policy.rb @@ -5,9 +5,9 @@ def resolve end end - def avo_index? = rubygems_org_admin? - def avo_show? = rubygems_org_admin? - has_association :user_events has_association :rubygem_events + + def avo_index? = rubygems_org_admin? + def avo_show? = rubygems_org_admin? end diff --git a/app/policies/oidc/api_key_role_policy.rb b/app/policies/oidc/api_key_role_policy.rb index 9491a6f934b..a3f0ffba8da 100644 --- a/app/policies/oidc/api_key_role_policy.rb +++ b/app/policies/oidc/api_key_role_policy.rb @@ -5,12 +5,12 @@ def resolve end end + has_association :provider + has_association :id_tokens + def avo_index? = rubygems_org_admin? def avo_show? = rubygems_org_admin? def avo_create? = rubygems_org_admin? def avo_update? = rubygems_org_admin? def act_on? = rubygems_org_admin? - - has_association :provider - has_association :id_tokens end diff --git a/app/policies/oidc/id_token_policy.rb b/app/policies/oidc/id_token_policy.rb index fd94f93797a..f2c5b2553f9 100644 --- a/app/policies/oidc/id_token_policy.rb +++ b/app/policies/oidc/id_token_policy.rb @@ -5,10 +5,10 @@ def resolve end end - def avo_index? = rubygems_org_admin? - def avo_show? = rubygems_org_admin? - has_association :provider has_association :api_key_role has_association :api_key + + def avo_index? = rubygems_org_admin? + def avo_show? = rubygems_org_admin? end diff --git a/app/policies/oidc/pending_trusted_publisher_policy.rb b/app/policies/oidc/pending_trusted_publisher_policy.rb index 0c1b670ff8d..e7b0ee3b09a 100644 --- a/app/policies/oidc/pending_trusted_publisher_policy.rb +++ b/app/policies/oidc/pending_trusted_publisher_policy.rb @@ -5,9 +5,9 @@ def resolve end end - def avo_index? = rubygems_org_admin? - def avo_show? = rubygems_org_admin? - has_association :rubygem has_association :trusted_publisher + + def avo_index? = rubygems_org_admin? + def avo_show? = rubygems_org_admin? end diff --git a/app/policies/oidc/provider_policy.rb b/app/policies/oidc/provider_policy.rb index 200fd3a69e6..84ede4d5f06 100644 --- a/app/policies/oidc/provider_policy.rb +++ b/app/policies/oidc/provider_policy.rb @@ -5,11 +5,11 @@ def resolve end end + has_association :api_key_roles + def avo_index? = rubygems_org_admin? def avo_show? = rubygems_org_admin? def avo_create? = rubygems_org_admin? def avo_update? = rubygems_org_admin? def act_on? = rubygems_org_admin? - - has_association :api_key_roles end diff --git a/app/policies/oidc/rubygem_trusted_publisher_policy.rb b/app/policies/oidc/rubygem_trusted_publisher_policy.rb index 4aced2a2386..bb16fe9a8f3 100644 --- a/app/policies/oidc/rubygem_trusted_publisher_policy.rb +++ b/app/policies/oidc/rubygem_trusted_publisher_policy.rb @@ -5,9 +5,9 @@ def resolve end end - def avo_index? = rubygems_org_admin? - def avo_show? = rubygems_org_admin? - has_association :rubygem has_association :trusted_publisher + + def avo_index? = rubygems_org_admin? + def avo_show? = rubygems_org_admin? end diff --git a/app/policies/oidc/trusted_publisher/github_action_policy.rb b/app/policies/oidc/trusted_publisher/github_action_policy.rb index 155fbfb8bcf..66837ff5ad3 100644 --- a/app/policies/oidc/trusted_publisher/github_action_policy.rb +++ b/app/policies/oidc/trusted_publisher/github_action_policy.rb @@ -5,12 +5,12 @@ def resolve end end - def avo_index? = rubygems_org_admin? - def avo_show? = rubygems_org_admin? - has_association :trusted_publishers has_association :rubygem_trusted_publishers has_association :pending_trusted_publishers has_association :rubygems has_association :api_keys + + def avo_index? = rubygems_org_admin? + def avo_show? = rubygems_org_admin? end diff --git a/app/policies/ownership_policy.rb b/app/policies/ownership_policy.rb index 3227e5a8633..a13333c77ca 100644 --- a/app/policies/ownership_policy.rb +++ b/app/policies/ownership_policy.rb @@ -5,9 +5,9 @@ def resolve end end + has_association :api_key_rubygem_scopes + def avo_show? rubygems_org_admin? end - - has_association :api_key_rubygem_scopes end diff --git a/app/policies/rubygem_policy.rb b/app/policies/rubygem_policy.rb index 73f4fe46ecf..8c22536f1d7 100644 --- a/app/policies/rubygem_policy.rb +++ b/app/policies/rubygem_policy.rb @@ -9,18 +9,6 @@ def resolve end end - def avo_index? - rubygems_org_admin? - end - - def avo_show? - rubygems_org_admin? - end - - def act_on? - rubygems_org_admin? - end - has_association :versions has_association :latest_version has_association :ownerships @@ -34,6 +22,17 @@ def act_on? has_association :gem_download has_association :audits has_association :link_verifications - has_association :oidc_rubygem_trusted_publishers + + def avo_index? + rubygems_org_admin? + end + + def avo_show? + rubygems_org_admin? + end + + def act_on? + rubygems_org_admin? + end end diff --git a/app/policies/user_policy.rb b/app/policies/user_policy.rb index 5b2dba40866..daf84c1449d 100644 --- a/app/policies/user_policy.rb +++ b/app/policies/user_policy.rb @@ -6,18 +6,6 @@ def resolve end end - def avo_index? - rubygems_org_admin? - end - - def avo_show? - rubygems_org_admin? - end - - def act_on? - rubygems_org_admin? - end - has_association :ownerships has_association :rubygems has_association :subscriptions @@ -34,4 +22,16 @@ def act_on? has_association :webauthn_credentials has_association :webauthn_verification has_association :events + + def avo_index? + rubygems_org_admin? + end + + def avo_show? + rubygems_org_admin? + end + + def act_on? + rubygems_org_admin? + end end diff --git a/app/policies/version_policy.rb b/app/policies/version_policy.rb index fce0be8ec72..91da344956d 100644 --- a/app/policies/version_policy.rb +++ b/app/policies/version_policy.rb @@ -9,6 +9,10 @@ def resolve end end + has_association :dependencies + has_association :gem_download + has_association :deletion + def avo_index? rubygems_org_admin? end @@ -20,8 +24,4 @@ def avo_show? def act_on? rubygems_org_admin? end - - has_association :dependencies - has_association :gem_download - has_association :deletion end diff --git a/app/policies/web_hook_policy.rb b/app/policies/web_hook_policy.rb index c4f862f782c..7555fb6082d 100644 --- a/app/policies/web_hook_policy.rb +++ b/app/policies/web_hook_policy.rb @@ -5,6 +5,8 @@ def resolve end end + has_association :audits + def avo_index? rubygems_org_admin? end @@ -16,6 +18,4 @@ def avo_show? def act_on? rubygems_org_admin? end - - has_association :audits end diff --git a/app/policies/webauthn_credential_policy.rb b/app/policies/webauthn_credential_policy.rb index 88c5442f864..3fa6d688e27 100644 --- a/app/policies/webauthn_credential_policy.rb +++ b/app/policies/webauthn_credential_policy.rb @@ -5,9 +5,9 @@ def resolve end end + has_association :user + def avo_show? Pundit.policy!(user, record.user).avo_show? end - - has_association :user end diff --git a/app/policies/webauthn_verification_policy.rb b/app/policies/webauthn_verification_policy.rb index bb4b1d36554..1c2e3300ad1 100644 --- a/app/policies/webauthn_verification_policy.rb +++ b/app/policies/webauthn_verification_policy.rb @@ -5,9 +5,9 @@ def resolve end end + has_association :user + def avo_show? Pundit.policy!(user, record.user).avo_show? end - - has_association :user end diff --git a/app/tasks/maintenance/backfill_linkset_links_to_version_metadata_task.rb b/app/tasks/maintenance/backfill_linkset_links_to_version_metadata_task.rb new file mode 100644 index 00000000000..eddbb04aa3f --- /dev/null +++ b/app/tasks/maintenance/backfill_linkset_links_to_version_metadata_task.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +class Maintenance::BackfillLinksetLinksToVersionMetadataTask < MaintenanceTasks::Task + def collection + Version.includes(:rubygem, rubygem: [:linkset]) + end + + def process(version) + return unless (linkset = version.rubygem.linkset) + + if version.metadata_uri_set? + # only the homepage does not respect #metadata_uri_set? + backfill_links(version, linkset, Links::LINKS.slice("home")) + else + backfill_links(version, linkset, Links::LINKS) + end + end + + private + + def backfill_links(version, linkset, links) + # would need a transaction since we're updating multiple attributes and + # metadata_uri_set? needs to be updated atomically to keep the backfill idempotent, + # but there is only a single update being issued here + + changes = false + links.each do |short, long| + next if version.metadata[long].present? + + next unless (value = linkset[short.to_sym]) + + version.metadata[long] = value + changes = true + end + version.save! if changes + end +end diff --git a/config/database.yml.sample b/config/database.yml.sample index 9389ad3e71a..fbc8eb0dcf3 100644 --- a/config/database.yml.sample +++ b/config/database.yml.sample @@ -13,7 +13,6 @@ development: pool: 5 timeout: 5000 - test: primary: <<: *default @@ -24,14 +23,6 @@ test: pool: 5 timeout: 5000 -oidc-api-token: - primary: - <<: *default - database: rubygems_oidc_api_token - min_messages: error - pool: 30 - reconnect: true - staging: primary: <<: *default diff --git a/config/database.yml.ts-sample b/config/database.yml.ts-sample index ceffad6f109..004fc5beb40 100644 --- a/config/database.yml.ts-sample +++ b/config/database.yml.ts-sample @@ -45,6 +45,20 @@ test: pool: 5 timeout: 5000 +staging: + primary: + <<: *default + database: rubygems_staging + min_messages: error + pool: 30 + reconnect: true + downloads: + <<: *timescale + database: rubygems_tsdb_staging + min_messages: error + pool: 30 + reconnect: true + production: primary: <<: *default diff --git a/config/environments/development.rb b/config/environments/development.rb index 86cd49f07de..63cbd9de3ce 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -95,7 +95,7 @@ config.rails_semantic_logger.rendered = !enable_semantic_log_format unless enable_semantic_log_format require 'rails_development_log_formatter' - SemanticLogger.add_appender(io: $stdout, formatter: RailsDevelopmentLogFormatter.new) + config.semantic_logger.add_appender(io: $stdout, formatter: RailsDevelopmentLogFormatter.new) config.rails_semantic_logger.format = RailsDevelopmentLogFormatter.new end diff --git a/config/environments/production.rb b/config/environments/production.rb index 80b38130b49..8d5862924f6 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -61,11 +61,12 @@ # Include generic and useful information about system operation, but avoid logging too much # information to avoid inadvertent exposure of personally identifiable information (PII). + $stdout.sync = true config.log_level = :info config.rails_semantic_logger.format = :json config.rails_semantic_logger.semantic = true config.rails_semantic_logger.add_file_appender = false - SemanticLogger.add_appender(io: $stdout, formatter: :json) + config.semantic_logger.add_appender(io: $stdout, formatter: config.rails_semantic_logger.format) # Prepend all log lines with the following tags. # config.log_tags = [ :request_id ] diff --git a/config/initializers/rack_attack.rb b/config/initializers/rack_attack.rb index d3ac7dbbda3..0ef13c32ebd 100644 --- a/config/initializers/rack_attack.rb +++ b/config/initializers/rack_attack.rb @@ -208,11 +208,19 @@ def self.api_key_owner_id(req) end end - protected_confirmation_action = [{ controller: "email_confirmations", action: "create" }] + protected_confirmation_action = [ + { controller: "email_confirmations", action: "create" }, + { controller: "email_confirmations", action: "unconfirmed" } + ] throttle("email_confirmations/email", limit: REQUEST_LIMIT_PER_EMAIL, period: LIMIT_PERIOD) do |req| - if protected_route?(protected_confirmation_action, req.path, req.request_method) && req.params['email_confirmation'] - User.normalize_email(req.params['email_confirmation']['email']).presence + if protected_route?(protected_confirmation_action, req.path, req.request_method) + if req.params['email_confirmation'] + User.normalize_email(req.params['email_confirmation']['email']).presence + else + action_dispatch_req = ActionDispatch::Request.new(req.env) + User.find_by_remember_token(action_dispatch_req.cookie_jar.signed["remember_token"])&.email.presence + end end end diff --git a/config/locales/avo.zh-TW.yml b/config/locales/avo.zh-TW.yml new file mode 100644 index 00000000000..0183c2d0ff4 --- /dev/null +++ b/config/locales/avo.zh-TW.yml @@ -0,0 +1,120 @@ +--- +zh-TW: + avo: + action_ran_successfully: 操作已成功執行! + actions: 操作 + and_x_other_resources: 和 %{count} 個其他資源 + are_you_sure: 您確定嗎? + are_you_sure_detach_item: 您確定要中斷此 %{item} 的連結嗎? + are_you_sure_you_want_to_run_this_option: 您確定要執行此操作嗎? + attach: 連結 + attach_and_attach_another: 連結 & 連結其他 + attach_item: 連結 %{item} + attachment_class_attached: "已連結 %{attachment_class}。" + attachment_class_detached: "已中斷 %{attachment_class} 的連結。" + attachment_destroyed: 已刪除附件 + cancel: 取消 + choose_a_country: 選擇國家 + choose_an_option: 選擇選項 + choose_item: 選擇 %{item} + clear_value: 清除數值 + click_to_reveal_filters: 點擊以顯示篩選器 + confirm: 確認 + create_new_item: 建立新 %{item} + dashboard: 儀表板 + dashboards: 儀表板 + delete: 刪除 + delete_file: 刪除檔案 + delete_item: 刪除 %{item} + detach_item: 中斷 %{item} 的連結 + details: 詳細資料 + download: 下載 + download_file: 下載檔案 + download_item: 下載 %{item} + edit: 編輯 + edit_item: 編輯 %{item} + empty_dashboard_message: 將卡片加入儀表板 + failed: 失敗 + failed_to_find_attachment: 找不到附件 + failed_to_load: 載入失敗 + field_translations: + file: + one: 檔案 + other: 檔案 + zero: 檔案 + people: + one: peep + other: peeps + zero: peeps + filter_by: 篩選方式 + filters: 篩選器 + go_back: 返回 + grid_view: 網格檢視 + hide_content: 隱藏內容 + home: 首頁 + key_value_field: + add_row: 新增行 + delete_row: 刪除行 + key: 索引鍵 + value: 值 + list_is_empty: 列表是空的 + loading: 正在載入 + more: 更多 + new: 新 + next_page: 下一頁 + no_cards_present: 無卡片 + no_item_found: 找不到記錄 + no_options_available: 無可用選項 + no_related_item_found: 找不到相關記錄 + not_authorized: 您無權執行此操作。 + number_of_items: + one: 一個 %{item} + other: "%{count} %{item}" + zero: 無 %{item} + oops_nothing_found: 糟糕!完全找不到... + order: + higher: 將記錄上移 + lower: 將記錄下移 + reorder_record: 重新排列記錄 + to_bottom: 將記錄移至最下方 + to_top: 將記錄移至最上方 + per_page: 每頁 + prev_page: 上一頁 + remove_selection: 移除所選 + reset_filters: 重設篩選器 + resource_created: 已建立記錄 + resource_destroyed: 已刪除記錄 + resource_translations: + user: + one: 使用者 + other: 使用者 + zero: 使用者 + resource_updated: 已更新記錄 + resources: 資源 + run: 執行 + save: 儲存 + search: + cancel_button: 取消 + placeholder: 搜尋 + select_all: 全選 + select_all_matching: 選擇所有相符 + select_item: 選擇項目 + show_content: 顯示內容 + sign_out: 登出 + switch_to_view: 切換至 %{view_type} 檢視 + table_view: 表格檢視 + tools: 工具 + type_to_search: 輸入以搜尋。 + unauthorized: 未經授權 + undo: 復原 + view: 檢視 + view_item: 檢視 %{item} + was_successfully_created: 已成功建立 + was_successfully_updated: 已成功更新 + x_items_more: + one: 還有一個項目 + other: "還有 %{count} 個項目" + zero: 沒有項目了 + x_records_selected_from_a_total_of_x_html: 已從此頁面的 %{count} 項記錄中選取了 %{selected} 項 + x_records_selected_from_all_pages_html: 從所有頁面選取了 %{count} 項記錄 + you_missed_something_check_form: 您似乎漏掉了什麼。請檢查表單。 diff --git a/config/locales/zh-TW.yml b/config/locales/zh-TW.yml index c3ca9ab16e4..075807c45e2 100644 --- a/config/locales/zh-TW.yml +++ b/config/locales/zh-TW.yml @@ -1,38 +1,42 @@ --- zh-TW: - credentials_required: + credentials_required: 需要認證 copied: 已複製 copy_to_clipboard: 複製 edit: 編輯 verification_expired: feed_latest: RubyGems.org | 最新 Gems feed_subscribed: RubyGems.org | 訂閱 Gems - footer_about_html: RubyGems.org 是 Ruby 社群的 Gem 套件管理服務,讓你能立即地發佈及安裝你的 Gem 套件,並且利用 - API 查詢及操作可用 Gem 的詳細資訊。
現在就成為貢獻者,貢獻一己之力來改善本站。 - footer_sponsors_html: - footer_join_rt_html: + footer_about_html: RubyGems.org 是 Ruby 社群的 Gem 套件管理服務,讓您能立即地發佈安裝您的 Gem 套件,並且利用 API 查詢及操作可用 Gem 的詳細資訊。
現在就成為貢獻者,貢獻一己之力來改善本站。 + footer_sponsors_html: RubyGems.org 透過與廣大的 Ruby 社群合作而誕生。 Fastly + 提供頻寬和 CDN 支援, Ruby Central 支付設備費用,並為進行中的開發和營運工作提供資金。 + 進一步了解我們的贊助商和它們是如何合作的。 + footer_join_rt_html: 我們需要您的幫助來資助開發者為保證 RubyGems.org 能夠順利運行所耗費的時間。 立刻加入 + Ruby Central。 form_disable_with: 請稍候... - invalid_page: + invalid_page: 頁碼超出範圍。已重新導向至預設頁面。 locale_name: 正體中文 none: 無 not_found: 沒有找到 - api_key_forbidden: - please_sign_up: 無法存取,請先在 https://rubygems.org 上註冊帳號 - please_sign_in: - otp_incorrect: 你的 OTP 碼 不正確。請檢查後再試一次。 - otp_missing: 你已啟用多重要素驗證,但是沒有輸入 OTP 碼。請輸入後再試一次。 + api_key_forbidden: API 金鑰沒有存取權限 + please_sign_up: 存取遭拒。請先在 https://rubygems.org 上註冊帳號 + please_sign_in: 請登入以繼續。 + otp_incorrect: 您的 OTP 碼不正確。請檢查後再試一次。 + otp_missing: 您已啟用多重要素驗證,但是沒有輸入 OTP 碼。請輸入後再試一次。 sign_in: 登入 sign_up: 註冊 dependency_list: - multifactor_authentication: MFA 驗證 + multifactor_authentication: 多重要素驗證 subtitle: Ruby 社群 Gem 套件管理平台 - this_rubygem_could_not_be_found: 找不到這個 gem + this_rubygem_could_not_be_found: 找不到此 rubygem。 time_ago: "%{duration} 前" title: RubyGems.org update: 更新 - try_again: 請再試一次 + try_again: 發生錯誤。請再試一次。 advanced_search: 進階搜尋 - authenticate: + authenticate: 驗證 helpers: submit: create: @@ -49,28 +53,28 @@ zh-TW: funding: session: password: 密碼 - who: Email / 帳號 + who: 電子郵件地址或帳號 user: avatar: 頭像 - email: Email + email: 電子郵件地址 full_name: 全名 handle: 帳號 password: 密碼 api_key: - oidc_api_key_role: + oidc_api_key_role: OIDC API 金鑰角色 oidc/id_token: - jti: - api_key_role: + jti: JWT ID + api_key_role: API 金鑰角色 oidc/api_key_role: - api_key_permissions: + api_key_permissions: API 金鑰權限 oidc/trusted_publisher/github_action: repository_owner_id: oidc/pending_trusted_publisher: rubygem_name: errors: messages: - unpwn: - blocked: + unpwn: 曾出現在資料外洩事件中,不應再使用 + blocked: 網域 '%{domain}' 因濫發垃圾郵件而遭封鎖。請使用有效的個人電子郵件地址。 models: api_key: attributes: @@ -79,14 +83,14 @@ zh-TW: ownership: attributes: user_id: - already_confirmed: - already_invited: + already_confirmed: 已是此 Gem 的擁有者 + already_invited: 已獲邀加入此 Gem version: attributes: gem_full_name: - taken: + taken: "%{value} 已存在" full_name: - taken: + taken: "%{value} 已存在" oidc/rubygem_trusted_publisher: attributes: rubygem: @@ -104,14 +108,14 @@ zh-TW: activemodel: attributes: oidc/provider/configuration: - jwks_uri: - id_token_signing_alg_values_supported: + jwks_uri: JWKS URI + id_token_signing_alg_values_supported: IP 權杖簽署演算法 errors: models: oidc/api_key_permissions: attributes: valid_for: - inclusion: + inclusion: "%{value} 秒數應介於 5 分鐘 (300 秒) 和 1 天 (86,400 秒) 之間" gems: too_long: api_keys: @@ -123,71 +127,71 @@ zh-TW: enable_mfa: expiration: create: - success: + success: 已建立新 API 金鑰 invalid_gem: destroy: - success: + success: 已刪除 API 金鑰:%{name} index: - api_keys: - name: + api_keys: API 金鑰 + name: 名稱 scopes: - gem: + gem: Gem age: - last_access: - action: - delete: - confirm: - confirm_all: - new_key: + last_access: 最後存取於 + action: 操作 + delete: 刪除 + confirm: API 金鑰將無效。您確定嗎? + confirm_all: 所有 API 金鑰將無效。您確定嗎? + new_key: 新 API 金鑰 index_rubygems: - push_rubygem: - yank_rubygem: - add_owner: - remove_owner: - access_webhooks: - show_dashboard: + push_rubygem: 推送 rubygem + yank_rubygem: 移除 rubygem + add_owner: 新增擁有者 + remove_owner: 移除擁有者 + access_webhooks: 存取 Webhooks + show_dashboard: 顯示儀表板 configure_trusted_publishers: - reset: - save_key: - mfa: + reset: 重設 + save_key: 請注意,我們無法再次顯示您的 API 金鑰。新 API 金鑰: + mfa: MFA expiration: new: - new_api_key: + new_api_key: 新 API 金鑰 reset: - success: + success: 已刪除所有 API 金鑰 update: - success: + success: 已成功更新 API 金鑰 invalid_gem: edit: - edit_api_key: - invalid_key: - all_gems: + edit_api_key: 編輯 API 金鑰 + invalid_key: 無法編輯無效的 API 金鑰。請刪除此金鑰並重新建立。 + all_gems: 所有 Gems gem_ownership_removed: dashboards: show: creating_link_text: Gem 建立 gem_link_text: Gem 頁面 latest: 最近更新 - latest_title: 最新的 RSS Feed + latest_title: 最近更新 RSS 摘要 migrating_link_text: Gem 轉移 mine: 我的 Gems my_subscriptions: 我的訂閱 - no_owned_html: 你尚未發佈任何 Gem。可以閱讀 %{creating_link} 教學,或參考 %{migrating_link} 教學來將你的 + no_owned_html: 您尚未發佈任何 Gem。可以閱讀 %{creating_link} 教學,或參考 %{migrating_link} 教學來將您的 Gem 從 RubyForge 遷移過來。 - no_subscriptions_html: 你還沒有訂閱過 Gem,前往 %{gem_link} 來訂閱! + no_subscriptions_html: 您還沒有訂閱過 Gem,前往 %{gem_link} 來訂閱! title: 控制台 dependencies: show: - click_to_expand: + click_to_expand: 點擊箭頭圖示來展開。 email_confirmations: create: - promise_resend: + promise_resend: 如果該帳號存在,我們會將用於啟用帳號的確認連結傳送到您的電子郵件地址。 new: - submit: 重新發送 - title: 重新發送確認信 - will_email_notice: 我們將會通過 Email 發送帳號認證信連結給你。 + submit: 重新傳送 + title: 重新傳送確認信 + will_email_notice: 我們將會通過電子郵件傳送帳號認證信連結給您。 update: - confirmed_email: Email 驗證成功。 + confirmed_email: 電子郵件地址驗證成功。 token_failure: 請確認 URL 或再次提交 home: index: @@ -210,7 +214,7 @@ zh-TW: help: 說明 hosted_by: 托管 monitored_by: 監控 - optimized_by: 優化 + optimized_by: 最佳化 resolved_with: 解析 security: 安全 source_code: 原始碼 @@ -220,129 +224,137 @@ zh-TW: tested_by: 測試 tracking_by: 追蹤 uptime: 上線時間 - verified_by: + verified_by: 驗證 secured_by: - looking_for_maintainers: + looking_for_maintainers: 徵求維護者 header: - dashboard: 控制台 - settings: - edit_profile: + dashboard: 儀表板 + settings: 設定 + edit_profile: 編輯個人檔案 search_gem_html: 搜尋 Gems… sign_in: 登入 sign_out: 登出 sign_up: 註冊 - mfa_banner_html: + mfa_banner_html: "\U0001F389 我們現在支援安全裝置了!設定新的裝置來提升您的帳號安全。[了解詳情](部落格文章連結)!" mailer: - confirm_your_email: - confirmation_subject: - link_expiration_explanation_html: + confirm_your_email: 已寄送連結,請點擊連結來確認您的電子郵件地址。 + confirmation_subject: "%{host} 電子郵件地址確認" + link_expiration_explanation_html: 請注意,此連結將在 3 小時後過期。您可以前往確認信頁面要求傳送新連結。 email_confirmation: - title: - subtitle: - confirmation_link: - welcome_message: + title: 電子郵件地址確認 + subtitle: 就快完成了! + confirmation_link: 確認電子郵件地址 + welcome_message: 歡迎來到 RubyGems.org!點擊下方的連結來驗證您的電子郵件地址。 email_reset: - title: - subtitle: - visit_link_instructions: + title: 電子郵件地址重設 + subtitle: 嗨 %{handle}! + visit_link_instructions: 您已變更您在 %{host} 的電子郵件地址。請點擊下列網址來重新啟用您的帳號。 deletion_complete: - title: - subtitle: - subject: - body_html: + title: 刪除完成 + subtitle: 掰掰! + subject: 您在 rubygems.org 上的帳號已被刪除 + body_html: 您在 %{host} 的帳號刪除請求已處理完畢。您隨時可以透過 %{sign_up} 頁面建立新帳號。 deletion_failed: - title: - subtitle: - subject: - body_html: + title: 刪除失敗 + subtitle: 抱歉! + subject: 您在 rubygems.org 上的帳號刪除請求失敗 + body_html: 您曾在 rubygems.org 送出帳號刪除的請求。很遺憾,我們無法處理您的請求,請稍後再試。若無法解決,請 %{contact} + 我們。 notifiers_changed: - subject: - title: - subtitle: - 'on': - off_html: + subject: 您已更改 RubyGems.org 的電子郵件通知設定 + title: 電子郵件通知 + subtitle: 嗨 %{handle} + 'on': 開 + off_html: "" gem_pushed: - subject: - title: + subject: Gem %{gem} 已推送至 RubyGems.org + title: Gem 已推送 gem_yanked: - subject: - title: + subject: Gem %{gem} 已從 RubyGems.org 移除 + title: Gem 已移除 reset_api_key: - subject: - title: - subtitle: + subject: RubyGems.org 上的 API 金鑰遭重設 + title: API 金鑰重設 + subtitle: 嗨 %{handle} webauthn_credential_created: - subject: - title: - subtitle: + subject: "%{host} 上的安全裝置已新增" + title: 已新增安全裝置 + subtitle: 嗨 %{handle}! webauthn_credential_removed: - subject: - title: - subtitle: + subject: "%{host} 上的安全裝置已移除" + title: 安全裝置已移除 + subtitle: 嗨 %{handle}! totp_enabled: - subject: - title: - subtitle: + subject: "%{host} 上的驗證器應用程式已啟用" + title: 已啟用驗證器應用程式 + subtitle: 嗨 %{handle}! totp_disabled: - subject: - title: - subtitle: + subject: "%{host} 上的驗證器應用程式已停用" + title: 已停用驗證器應用程式 + subtitle: 嗨 %{handle}! email_reset_update: - subject: - title: + subject: 您已在 %{host} 請求電子郵件地址更新 + title: 已請求電子郵件地址更新 ownership_confirmation: - subject: - title: - subtitle: - body_text: - body_html: + subject: 請確認 RubyGems.org 上 %{gem} Gem 的所有權 + title: 所有權確認 + subtitle: 嗨 %{handle}! + body_text: "%{authorizer} 把您加入了 %{gem} Gem 的擁有者名單。請點擊下方連結來確認您的所有權。" + body_html: %{authorizer} 把您加入了 %{gem} + Gem 的擁有者名單。請點擊下方連結來確認您的所有權。 link_expiration_explanation_html: owner_added: - subject_self: - subject_others: + subject_self: 您加入了 %{gem} Gem 的擁有者名單 + subject_others: 使用者 %{owner_handle} 加入了 %{gem} Gem 的擁有者名單 title: - subtitle: + subtitle: 嗨 %{user_handle}! body_self_html: body_others_html: owner_removed: subject: title: - subtitle: + subtitle: 嗨 %{user_handle}! body_html: ownerhip_request_closed: - title: - subtitle: - body_html: + title: 所有權請求 + subtitle: 嗨 %{handle}! + body_html: 感謝您申請 %{gem} 的所有權。我們很遺憾地通知您,您的所有權請求已被 Gem 擁有者關閉。 ownerhip_request_approved: - body_html: + body_html: 恭喜!您對 %{gem} 的所有權請求已通過。您已加入 Gem 的擁有者名單。 new_ownership_requests: body_html: zero: one: - other: - button: - disable_notifications: - owners_page: + other: "%{gem}%{count} 項新所有權請求。請點擊下方按鈕來查看所有請求。" + button: 所有權請求 + disable_notifications: 若要停止接收這些訊息,請更新您的 + owners_page: 所有權 web_hook_deleted: - title: - subject: - subtitle: - body_text: - body_html: - global_text: - global_html: - gem_text: - gem_html: + title: Webhook 遭刪除 + subject: 您在 RubyGems.org 上的 Webhook 遭到刪除 + subtitle: 嗨 %{handle}! + body_text: 您傳送 POST 請求至 %{url} 的 Webhook 在 %{failures} 次失敗後遭到刪除。 + body_html: 您傳送 POST 請求至 %{url} + 的 Webhook 在 %{failures} 次失敗後遭到刪除。 + global_text: 之前這個 Webhook 會在任何 Gem 被推送的時候被呼叫。 + global_html: 之前這個 Webhook 會在任何 Gem 被推送的時候被呼叫。 + gem_text: 之前這個 Webhook 會在 %{gem} 被推送的時候被呼叫。 + gem_html: 之前這個 Webhook 會在 %{gem} + 被推送的時候被呼叫。 web_hook_disabled: - title: - subject: - subtitle: - body_text: + title: Webhook 遭停用 + subject: 您在 RubyGems.org 上的 Webhook 遭到停用 + subtitle: 嗨 %{handle}! + body_text: | + 您傳送 POST 請求至 %{url} 的 Webhook 因 %{disabled_reason} 被停用。 + 該 Webhook 最後成功於 %{last_success},之後便失敗了 %{failures_since_last_success} 次。 + 您可以執行 `%{delete_command}` 命令來刪除此 Webhook。 body_html: - global_text: - global_html: - gem_text: - gem_html: + global_text: 之前這個 Webhook 會在任何 Gem 被推送的時候被呼叫。 + global_html: 之前這個 Webhook 會在任何 Gem 被推送的時候被呼叫。 + gem_text: 之前這個 Webhook 會在 %{gem} 被推送的時候被呼叫。 + gem_html: 之前這個 Webhook 會在 %{gem} + 被推送的時候被呼叫。 gem_trusted_publisher_added: title: news: @@ -354,18 +366,18 @@ zh-TW: title: 熱門新發佈 pages: about: - contributors_amount: + contributors_amount: "%{count} 位 Ruby 愛好者" downloads_amount: checkout_code: - mit_licensed: + mit_licensed: MIT 授權 logo_header: - logo_details: + logo_details: 只要點選下載按鈕即可獲得三份 .PNG 和一份 .SVG 格式的 RubyGems Logo。 founding_html: 本專案由 %{founder} 於 2009 年 4 月創立,發展過程中有超過 %{contributors} 貢獻者以及 - %{downloads}。自 RubyGems 1.3.6 發佈以來,本站名稱由 Gemcutter 更名為 %{title},本站自此之後成為 Ruby - 社群的核心網站。 - support_html: 雖然 Gemcutter 並不是由一個特定的公司運作,但在發展過程中接受了許多來源的幫助。目前的設計、圖像以及網站的前端開發是由 + %{downloads} 次下載。自 RubyGems 1.3.6 發佈以來,本站名稱由 Gemcutter 更名為 %{title},本站自此之後成為 + Ruby 社群的核心網站。 + support_html: 雖然 RubyGems.org 並不是由一個特定的公司運作,但在發展過程中接受了許多來源的幫助。目前的設計、圖像以及網站的前端開發是由 %{dockyard} 提供。%{github} 也幫助我們能更容易地協作和分享原始碼。本站部署在 %{heroku} 上,其一流的服務,更有助於證明 - Gemcutter 是一個可以穩定、可行的解決方案。Our infrastructure is currently hosted on %{aws}. + RubyGems.org 是一個可以穩定、可行的解決方案。Our infrastructure is currently hosted on %{aws}. technical_html: 關於本站的技術資訊:100% Ruby。主站是一個 %{rails} 應用程式。Gems 架設在 %{s3} 上, served by %{fastly}, 使得 Gem 從發佈到提供下載的時間大幅縮短。詳細資訊可從 GitHub 上的 %{source_code} 查看(遵守 %{license} 協議)。 @@ -378,15 +390,15 @@ zh-TW: data: title: download: - title: + title: 下載 RubyGems faq: - title: + title: 常見問題 migrate: - title: + title: 轉移 Gems security: - title: + title: 安全性 sponsors: - title: + title: 贊助者 password_mailer: change_password: closing: @@ -401,184 +413,191 @@ zh-TW: new: submit: 更新密碼 title: 修改密碼 - will_email_notice: 系統將會寄一封包含重設密碼連結的電子郵件給你 + will_email_notice: 系統將會寄一封包含重設密碼連結的電子郵件給您 create: success: failure_on_missing_email: update: failure: multifactor_auths: - incorrect_otp: 你的 OTP 碼 不正確。 - session_expired: - require_totp_disabled: - require_mfa_enabled: 你的多重要素驗證已停用,請先啟用。 - require_totp_enabled: - require_webauthn_enabled: - setup_required_html: - setup_recommended: - strong_mfa_level_required_html: - strong_mfa_level_recommended: - setup_webauthn_html: + incorrect_otp: 您的 OTP 碼不正確。 + session_expired: 您的登入頁面工作階段已過期。 + require_totp_disabled: 您基於 OTP 的多重要素驗證已經啟用。您需要先移除才能重新設定。 + require_mfa_enabled: 您尚未啟用多重要素驗證,請先啟用。 + require_totp_enabled: 您未啟用驗證器應用程式。請先啟用。 + require_webauthn_enabled: 您未啟用任何安全裝置。您需要先將裝置綁定到您的帳號。 + setup_required_html: 您必須設定多重要素驗證以保護您的帳號和 Gems。請閱讀我們的部落格文章以了解詳情。 + setup_recommended: 為保護您的帳號和 Gems,我們建議您設定多重要素驗證。未來我們將強制要求所有帳號啟用 MFA。 + strong_mfa_level_required_html: 為保護您的帳號和 Gems,您必須將您的 MFA 等級設為 "使用者介面和 Gem 登入" + 或 "使用者介面和 API"。請閱讀我們的部落格文章以了解詳情。 + strong_mfa_level_recommended: 為保護您的帳號和 Gems,我們建議您將您的 MFA 等級設為 "使用者介面和 Gem 登入" + 或 "使用者介面和 API"。未來我們將強制要求所有帳號將設為上述 MFA 等級。 + setup_webauthn_html: "\U0001F389 我們現在支援安全裝置了!設定新的裝置來提升您的帳號安全。了解詳情!" new: title: 啟用多重要素驗證 - scan_prompt: 請用你的驗證裝置掃描 QR-code。如果你沒辦法掃描,手動輸入下面的資料。 + scan_prompt: 請用您的驗證裝置掃描 QR-code。如果您沒辦法掃描,手動輸入下面的資料。 otp_prompt: 輸入驗證裝置上的數字來繼續。 confirm: 我已把復原碼收在安全的地方。 enable: 啟用 - account: '帳號: %{account}' - key: '金鑰: %{key}' - time_based: '基於時間的: 是' + account: 帳號:%{account} + key: 金鑰:%{key} + time_based: 基於時間:是 create: - qrcode_expired: QR-code 和金鑰已過期。請再重新註冊裝置。 - success: 你已成功啟用多重要素驗證。 + qrcode_expired: QR-code 和金鑰已過期。請重新註冊裝置。 + success: 您已成功啟用基於 OTP 的多重要素驗證。 recovery: - copied: + copied: "[ 已複製 ]" continue: 繼續 title: 復原碼 - copy: + copy: "[ 複製 ]" saved: note_html: already_generated: destroy: - success: 你已成功停用多重要素驗證。 + success: 您已成功停用基於 OTP 的多重要素驗證。 update: - invalid_level: - success: 你已成功修改多重驗證等級。 + invalid_level: MFA 等級無效。 + success: 您已成功修改多重驗證等級。 prompt: webauthn_credential_note: sign_in_with_webauthn_credential: - otp_code: - otp_or_recovery: - recovery_code: + otp_code: OTP 碼 + otp_or_recovery: OTP 或復原碼 + recovery_code: 復原碼 recovery_code_html: - security_device: - verify_code: + security_device: 安全裝置 + verify_code: 驗證碼 notifiers: update: - success: + success: 您已成功更新您的電子郵件通知設定。 show: - info: - 'on': - 'off': - recommended: - title: - update: - owner_heading: - owner_request_heading: - push_heading: + info: 為協助偵測未經授權的 Gem 或所有權變更,我們將在您擁有的 Gem 推送新版本、遭移除,或新增擁有者時傳送電子郵件給您。在接收和閱讀這些郵件的同時,您也在保護 + Ruby 的生態圈。 + 'on': 開 + 'off': 關 + recommended: 建議 + title: 電子郵件通知 + update: 更新 + owner_heading: 所有權通知 + owner_request_heading: 所有權請求通知 + push_heading: 推送通知 webauthn_verifications: - expired_or_already_used: - no_port: + expired_or_already_used: 您所使用的連結中的權杖已過期或被使用。 + no_port: 未提供連接埠。請再試一次。 pending: prompt: - title: - authenticating_as: - authenticate: - no_webauthn_devices: + title: 透過安全裝置驗證 + authenticating_as: 驗證身分為 + authenticate: 驗證 + no_webauthn_devices: 您未啟用任何安全裝置 successful_verification: - title: - close_browser: + title: 成功! + close_browser: 請關閉此瀏覽器。 failed_verification: - title: - close_browser: + title: 錯誤 - 驗證失敗 + close_browser: 請關閉此瀏覽器並重試。 owners: confirm: confirmed_email: - token_expired: + token_expired: 確認權杖已過期。請從 Gem 頁面嘗試重新傳送權杖。 index: - add_owner: - name: - mfa: - status: - confirmed_at: - added_by: - action: + add_owner: 新增擁有者 + name: 擁有者 + mfa: MFA 狀態 + status: 狀態 + confirmed_at: 確認於 + added_by: 新增者 + action: 操作 email_field: - submit_button: - info: - confirmed: - pending: - confirm_remove: + submit_button: 新增擁有者 + info: 新增或移除擁有者 + confirmed: 已確認 + pending: 待確認 + confirm_remove: 您確定要將此使用者從擁有者名單中移除嗎? resend_confirmation: resent_notice: create: - success_notice: + success_notice: "%{handle} 已以未確認擁有者的身分加入。所有權存取將在使用者點擊傳送到他們的電子郵件地址的確認信後啟用。" destroy: removed_notice: - failed_notice: - mfa_required: + failed_notice: 無法移除 Gem 的唯一擁有者 + mfa_required: Gem 啟用了 MFA 要求,請設定您的帳號的 MFA。 settings: edit: - title: - webauthn_credentials: - no_webauthn_credentials: - webauthn_credential_note: - otp_code: + title: 編輯設定 + webauthn_credentials: 安全裝置 + no_webauthn_credentials: 您沒有任何安全裝置 + webauthn_credential_note: 安全裝置可以是任何遵循 FIDO2 標準的裝置,例如安全或生物特徵金鑰。 + otp_code: OTP 或復原碼 api_access: - confirm_reset: 確定要重設嗎?此動作無法還原 - credentials_html: 如果你需要從 command line 中執行 %{gem_commands_link},你需要先準備 %{gem_credentials_file} - 這個檔案,用以下的指令可以產生: - key_is_html: 你的 API key 是 %{key}。 - link_text: Gem 指令 - reset: 重設 API key + confirm_reset: 確定要重設嗎?此動作無法還原。 + credentials_html: 如果您需要從命令列中執行 %{gem_commands_link},您需要先準備 %{gem_credentials_file} + 這個檔案,可以使用以下的命令產生: + key_is_html: 您的 API key 是 %{key}。 + link_text: Gem 命令 + reset: 重設我的 API 金鑰 reset_all: title: API 存取 reset_password: title: 重設密碼 mfa: multifactor_auth: 多重要素驗證 - otp: - disabled_html: + otp: 驗證器應用程式 + disabled_html: 您尚未啟用基於 OTP 的多重要素驗證。請參閱 RubyGems + MFA 指南來了解關於 MFA 等級的資訊。 go_settings: 註冊新裝置 - level_html: - enabled_note: + level_html: 您已啟用多重要素驗證。請點擊 '更新' 來變更您的 MFA 等級。請參閱 RubyGems + MFA 指南來了解關於 MFA 等級的資訊。 + enabled_note: 您已啟用多重要素驗證。請輸入驗證器提供的 OTP 碼或復原碼來停用。 update: 更新 - disable: - enabled: - disabled: + disable: 停用 + enabled: 已啟用 + disabled: 已停用 level: title: 多重驗證等級 disabled: 停用 - ui_only: 只有使用者介面 - ui_and_api: 使用者介面與API - ui_and_gem_signin: + ui_only: 僅使用者介面 + ui_and_api: 使用者介面與 API (建議) + ui_and_gem_signin: 使用者介面和 Gem 登入 profiles: adoptions: no_ownership_calls: - no_ownership_requests: - title: - subtitle_html: + no_ownership_requests: 您尚未建立任何所有權請求。 + title: 認養 + subtitle_html: 請求新維護者或所有權 (了解詳情) edit: change_avatar: disabled_avatar_html: - email_awaiting_confirmation: 請驗證你的新 Email %{unconfirmed_email} + email_awaiting_confirmation: 請確認您的新電子郵件地址 %{unconfirmed_email} enter_password: 輸入密碼 - optional_full_name: + optional_full_name: 選填。將公開顯示 optional_twitter_username: X 帳號(可選) twitter_username: 帳號 title: 編輯個人檔案 delete: delete: 刪除 - delete_profile: 刪除個人資料 + delete_profile: 刪除個人檔案 warning: 警告 delete: - title: - confirm: - instructions: - list_only_owner_html: - list_multi_owner: + title: 刪除個人檔案 + confirm: 確認 + instructions: 我們很遺憾 + list_only_owner_html: 這些 Gems 將與您的個人檔案一起被移除。如果您想要在刪除個人檔案前新增擁有者,請使用 %{command_link} + 命令。 + list_multi_owner: 您將失去這些 Gem 的存取權,但其他擁有者將不受影響。 warning: 警告 rubygem: - owners_header: + owners_header: 擁有者 destroy: request_queued: update: - confirmation_mail_sent: - updated: - public_email: 在公開的個人頁面中展示 email - request_denied: + confirmation_mail_sent: 您將在幾分鐘內收到一封郵件。內含確認您新的電子郵件地址的指示。 + updated: 您的個人檔案已更新。 + public_email: 在公開個人檔案顯示電子郵件地址 + request_denied: 此請求遭拒。我們無法驗證您的密碼。 show: - title: 简介 %{username} + title: "%{username} 的個人檔案" security_events: title: description_html: @@ -587,8 +606,8 @@ zh-TW: downloads_for_this_version: 這個版本 required_ruby_version: Ruby 版本需求 required_rubygems_version: RubyGems 版本需求 - requires_mfa: - released_with_mfa: + requires_mfa: 新版本需要 MFA + released_with_mfa: 使用 MFA 發布的版本 links: badge: 徽章 bugs: Bug 追蹤 @@ -600,39 +619,39 @@ zh-TW: header: 相關連結 home: 首頁 mail: 郵件群組 - report_abuse: 舉報投訴 + report_abuse: 檢舉濫用 reverse_dependencies: 反向依賴 review_changes: rss: RSS subscribe: 訂閱 unsubscribe: 取消訂閱 wiki: Wiki - resend_ownership_confirmation: - ownership: + resend_ownership_confirmation: 重新傳送確認信 + ownership: 所有權 oidc: api_key_role: name: new: trusted_publishers: reserved: - reserved_namespace: + reserved_namespace: 此命名空間被 rubygems.org 保留。 dependencies: header: "%{title} 相依性套件" gem_members: authors_header: 作者 self_no_mfa_warning_html: - not_using_mfa_warning_show: - not_using_mfa_warning_hide: + not_using_mfa_warning_show: "* 某些擁有者未使用多重要素驗證 (MFA)。點擊此處以顯示完整名單。" + not_using_mfa_warning_hide: "* 下列擁有者未使用多重要素驗證 (MFA)。點擊此處以隱藏。" owners_header: 擁有者 - pushed_by: - using_mfa_info: - yanked_by: - sha_256_checksum: SHA 256 checksum - signature_period: - expired: + pushed_by: 推送者 + using_mfa_info: "* 擁有者皆使用多重要素驗證 (MFA)。" + yanked_by: 移除者 + sha_256_checksum: SHA 256 總和檢查碼 + signature_period: 簽名有效期 + expired: 已過期 version_navigation: - previous_version: - next_version: + previous_version: "← 上一版本" + next_version: 下一版本 → index: downloads: 下載 title: Gems @@ -640,15 +659,15 @@ zh-TW: bundler_header: Gemfile install: 安裝 licenses_header: - one: License - other: License + one: 授權 + other: 授權 no_licenses: 無 requirements_header: 必填 show_all_versions: 顯示所有版本(共 %{count}) versions_header: 版本列表 yanked_notice: 這個 Gem 版本已被移除,因此無法提供下載,也無法被其他的 Gem 相依。 show_yanked: - not_hosted_notice: 這個 Gem 目前沒有在 Gemcutter 上 + not_hosted_notice: 這個 Gem 目前沒有在 RubyGems.org 上 reserved_namespace_html: one: other: @@ -657,62 +676,64 @@ zh-TW: description_html: reverse_dependencies: index: - title: - subtitle: - no_reverse_dependencies: + title: "%{name} 的反向依賴" + subtitle: 下列 Gems 的最新版本需要 %{name} + no_reverse_dependencies: 此 Gem 沒有反向依賴。 search: - search_reverse_dependencies_html: + search_reverse_dependencies_html: 搜尋反向依賴 Gems… searches: advanced: name: 名稱 summary: 摘要 description: 描述 downloads: 下載數 - updated: 最後更新時間 - yanked: + updated: 更新於 + yanked: 移除於 show: subtitle: "%{query}" - month_update: - week_update: + month_update: 於最近一個月更新 (%{count}) + week_update: 於最近一週更新 (%{count}) filter: - yanked: - suggestion: + yanked: 遭移除 (%{count}) + suggestion: 您是不是在找 sessions: new: forgot_password: 忘記密碼? resend_confirmation: 沒收到確認信? verify: - title: - confirm: - notice: + title: 確認密碼 + confirm: 確認 + notice: 請確認您的密碼以繼續。 create: account_blocked: stats: index: - title: + title: 統計資料 all_time_most_downloaded: 歷史下載次數排行 total_downloads: 總下載次數 total_gems: Gems 總數 total_users: 總使用者數量 users: create: - email_sent: 已發送確認信到你的信箱地址。 + email_sent: 已傳送確認信到您的電子郵件地址。 new: have_account: 已經註冊過了? versions: index: - not_hosted_notice: 這個 Gem 目前沒有在 Gemcutter 上 + not_hosted_notice: 這個 Gem 目前沒有在 RubyGems.org 上 title: "%{name} 的所有版本" versions_since: other: 自從 %{since} 以來,有 %{count} 個版本 one: 自從 %{since} 以來,有 %{count} 個版本 - imported_gem_version_notice: + imported_gem_version_notice: 此版本的 Gem 於 %{import_date} 匯入 RubyGems.org。顯示日期由作者在 + gemspec 指定。 version: yanked: 已被移除 adoptions: index: - title: - subtitle_owner_html: + title: 認養 + subtitle_owner_html: 請求新的維護者加入 %{gem} (read + more) subtitle_user_html: ownership_calls: no_ownership_calls: @@ -722,54 +743,55 @@ zh-TW: create: success_notice: index: - title: - subtitle_html: - share_requirements: + title: 徵求維護者 + subtitle_html: RubyGems 正在尋找新的維護者加入團隊 (了解詳情) + share_requirements: 請說明您需要哪方面的協助 note_for_applicants: - created_by: + created_by: 建立者 details: - apply: - close: - markup_supported_html: + apply: 申請 + close: 關閉 + markup_supported_html: 支援 + Rdoc 標記語言 create_call: ownership_requests: create: - success_notice: + success_notice: 您的所有權請求已送出。 update: - approved_notice: + approved_notice: 所有權請求已核准。%{name} 已加入擁有者名單。 closed_notice: close: success_notice: - ownership_requests: + ownership_requests: 所有權請求 note_for_owners: - your_ownership_requests: - close_all: + your_ownership_requests: 您的所有權請求 + close_all: 全部關閉 approve: - gems_published: - created_at: - no_ownership_requests: - create_req: + gems_published: 已發布的 Gems + created_at: 建立於 + no_ownership_requests: 您的專案的加入請求將在此顯示。 + create_req: 建立所有權請求 signin_to_create_html: webauthn_credentials: callback: - success: + success: 您已成功註冊安全裝置。 recovery: - continue: - title: + continue: 繼續 + title: 您已成功新增安全裝置 notice_html: - copied: - copy: + copied: "[ 已複製 ]" + copy: "[ 複製 ]" saved: webauthn_credential: - confirm_delete: - delete_failed: - delete: - confirm: - saved: + confirm_delete: 已刪除認證 + delete_failed: 無法刪除認證 + delete: 刪除 + confirm: 您確定要刪除此認證嗎? + saved: 已成功建立安全裝置 form: - new_device: - nickname: - submit: + new_device: 註冊新安全裝置 + nickname: 暱稱 + submit: 註冊裝置 oidc: api_key_roles: index: diff --git a/config/puma.rb b/config/puma.rb index cd9ac87511b..cbc576cdb8f 100644 --- a/config/puma.rb +++ b/config/puma.rb @@ -46,6 +46,11 @@ sleep 1 end +on_worker_boot do + # Re-open appenders after forking the process. https://logger.rocketjob.io/forking.html + SemanticLogger.reopen +end + on_restart do Rails.configuration.launch_darkly_client&.close end diff --git a/config/routes.rb b/config/routes.rb index fcca4053e1d..b27fceb74b6 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -242,11 +242,6 @@ patch 'unconfirmed' end - # The resource was plural path /passwords/new, but now we are using singular path /password/new - # TODO: remove the following get/post routes a few days after this PR is deployed - get "passwords/new", to: "passwords#new" - post "passwords", to: "passwords#create" - resource :password, only: %i[new create edit update] do post 'otp_edit', to: 'passwords#otp_edit', as: :otp_edit post 'webauthn_edit', to: 'passwords#webauthn_edit', as: :webauthn_edit @@ -261,14 +256,7 @@ post 'webauthn_authenticate', to: 'sessions#webauthn_authenticate', as: :webauthn_authenticate end - resources :users, only: %i[new create] do - # TODO: remove the password resource a few days after this PR is deployed - # allowing time for existing password reset emails to be used - resource :password, only: %i[create edit update] do - post 'otp_edit', to: 'passwords#otp_edit', as: :otp_edit - post 'webauthn_edit', to: 'passwords#webauthn_edit', as: :webauthn_edit - end - end + resources :users, only: %i[new create] get '/sign_in' => 'sessions#new', as: 'sign_in' delete '/sign_out' => 'sessions#destroy', as: 'sign_out' diff --git a/db/migrate/20181128191130_add_index_to_lowercase_email.rb b/db/migrate/20181128191130_add_index_to_lowercase_email.rb new file mode 100644 index 00000000000..c43036cb05f --- /dev/null +++ b/db/migrate/20181128191130_add_index_to_lowercase_email.rb @@ -0,0 +1,9 @@ +class AddIndexToLowercaseEmail < ActiveRecord::Migration[5.2] + def up + add_index "users", "lower(email) varchar_pattern_ops", name: "index_users_on_lower_email" + end + + def down + remove_index "users", name: "index_users_on_lower_email" + end +end diff --git a/db/migrate/20240327004732_add_foreign_keys.rb b/db/migrate/20240327004732_add_foreign_keys.rb new file mode 100644 index 00000000000..ca681b43e94 --- /dev/null +++ b/db/migrate/20240327004732_add_foreign_keys.rb @@ -0,0 +1,14 @@ +class AddForeignKeys < ActiveRecord::Migration[7.1] + def change + add_foreign_key "api_key_rubygem_scopes", "api_keys", name: "api_key_rubygem_scopes_api_key_id_fk", validate: false + add_foreign_key "audits", "admin_github_users", name: "audits_admin_github_user_id_fk", validate: false + add_foreign_key "ownership_calls", "rubygems", name: "ownership_calls_rubygem_id_fk", validate: false + add_foreign_key "ownership_calls", "users", name: "ownership_calls_user_id_fk", validate: false + add_foreign_key "ownership_requests", "users", column: "approver_id", name: "ownership_requests_approver_id_fk", validate: false + add_foreign_key "ownership_requests", "ownership_calls", name: "ownership_requests_ownership_call_id_fk", validate: false + add_foreign_key "ownership_requests", "rubygems", name: "ownership_requests_rubygem_id_fk", validate: false + add_foreign_key "ownership_requests", "users", name: "ownership_requests_user_id_fk", validate: false + add_foreign_key "versions", "rubygems", name: "versions_rubygem_id_fk", validate: false + add_foreign_key "web_hooks", "users", name: "web_hooks_user_id_fk", validate: false + end +end diff --git a/db/migrate/20240327005038_validate_new_keys.rb b/db/migrate/20240327005038_validate_new_keys.rb new file mode 100644 index 00000000000..13d28610826 --- /dev/null +++ b/db/migrate/20240327005038_validate_new_keys.rb @@ -0,0 +1,14 @@ +class ValidateNewKeys < ActiveRecord::Migration[7.1] + def change + validate_foreign_key "api_key_rubygem_scopes", "api_keys", name: "api_key_rubygem_scopes_api_key_id_fk" + validate_foreign_key "audits", "admin_github_users", name: "audits_admin_github_user_id_fk" + validate_foreign_key "ownership_calls", "rubygems", name: "ownership_calls_rubygem_id_fk" + validate_foreign_key "ownership_calls", "users", name: "ownership_calls_user_id_fk" + validate_foreign_key "ownership_requests", "users", column: "approver_id", name: "ownership_requests_approver_id_fk" + validate_foreign_key "ownership_requests", "ownership_calls", name: "ownership_requests_ownership_call_id_fk" + validate_foreign_key "ownership_requests", "rubygems", name: "ownership_requests_rubygem_id_fk" + validate_foreign_key "ownership_requests", "users", name: "ownership_requests_user_id_fk" + validate_foreign_key "versions", "rubygems", name: "versions_rubygem_id_fk" + validate_foreign_key "web_hooks", "users", name: "web_hooks_user_id_fk" + end +end diff --git a/db/migrate/20240328003844_add_linkset_foreign_key.rb b/db/migrate/20240328003844_add_linkset_foreign_key.rb new file mode 100644 index 00000000000..9f98f960590 --- /dev/null +++ b/db/migrate/20240328003844_add_linkset_foreign_key.rb @@ -0,0 +1,5 @@ +class AddLinksetForeignKey < ActiveRecord::Migration[7.1] + def change + add_foreign_key "linksets", "rubygems", name: "linksets_rubygem_id_fk", validate: false + end +end diff --git a/db/migrate/20240328004017_validate_linkset_foreign_key.rb b/db/migrate/20240328004017_validate_linkset_foreign_key.rb new file mode 100644 index 00000000000..6d42471cde1 --- /dev/null +++ b/db/migrate/20240328004017_validate_linkset_foreign_key.rb @@ -0,0 +1,5 @@ +class ValidateLinksetForeignKey < ActiveRecord::Migration[7.1] + def change + validate_foreign_key "linksets", "rubygems", name: "linksets_rubygem_id_fk" + end +end diff --git a/db/migrate/20240507181615_remove_legacy_api_key_scope_columns_from_api_keys.rb b/db/migrate/20240507181615_remove_legacy_api_key_scope_columns_from_api_keys.rb new file mode 100644 index 00000000000..753fbfdb367 --- /dev/null +++ b/db/migrate/20240507181615_remove_legacy_api_key_scope_columns_from_api_keys.rb @@ -0,0 +1,12 @@ +class RemoveLegacyApiKeyScopeColumnsFromApiKeys < ActiveRecord::Migration[7.1] + def change + # The columns are ignored + safety_assured do + remove_columns :api_keys, + *%i[show_dashboard index_rubygems push_rubygem yank_rubygem add_owner remove_owner access_webhooks], + type: :boolean, + null: false, + default: false + end + end +end diff --git a/db/migrate/20240522185716_create_good_job_process_lock_ids.rb b/db/migrate/20240522185716_create_good_job_process_lock_ids.rb new file mode 100644 index 00000000000..f1b70a8f2e4 --- /dev/null +++ b/db/migrate/20240522185716_create_good_job_process_lock_ids.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +class CreateGoodJobProcessLockIds < ActiveRecord::Migration[7.1] + def change + reversible do |dir| + dir.up do + # Ensure this incremental update migration is idempotent + # with monolithic install migration. + return if connection.column_exists?(:good_jobs, :locked_by_id) + end + end + + add_column :good_jobs, :locked_by_id, :uuid + add_column :good_jobs, :locked_at, :datetime + add_column :good_job_executions, :process_id, :uuid + add_column :good_job_processes, :lock_type, :integer, limit: 2 + end +end diff --git a/db/migrate/20240522185717_create_good_job_process_lock_indexes.rb b/db/migrate/20240522185717_create_good_job_process_lock_indexes.rb new file mode 100644 index 00000000000..7b9b52a34ff --- /dev/null +++ b/db/migrate/20240522185717_create_good_job_process_lock_indexes.rb @@ -0,0 +1,42 @@ +# frozen_string_literal: true + +class CreateGoodJobProcessLockIndexes < ActiveRecord::Migration[7.1] + disable_ddl_transaction! + + def change + reversible do |dir| + dir.up do + unless connection.index_name_exists?(:good_jobs, :index_good_jobs_on_priority_scheduled_at_unfinished_unlocked) + add_index :good_jobs, %i[priority scheduled_at], + order: { priority: "ASC NULLS LAST", scheduled_at: :asc }, + where: "finished_at IS NULL AND locked_by_id IS NULL", + name: :index_good_jobs_on_priority_scheduled_at_unfinished_unlocked, + algorithm: :concurrently + end + + unless connection.index_name_exists?(:good_jobs, :index_good_jobs_on_locked_by_id) + add_index :good_jobs, :locked_by_id, + where: "locked_by_id IS NOT NULL", + name: :index_good_jobs_on_locked_by_id, + algorithm: :concurrently + end + + unless connection.index_name_exists?(:good_job_executions, :index_good_job_executions_on_process_id_and_created_at) + add_index :good_job_executions, %i[process_id created_at], + name: :index_good_job_executions_on_process_id_and_created_at, + algorithm: :concurrently + end + end + + dir.down do + remove_index(:good_jobs, name: :index_good_jobs_on_priority_scheduled_at_unfinished_unlocked) if connection.index_name_exists?(:good_jobs, +:index_good_jobs_on_priority_scheduled_at_unfinished_unlocked) + remove_index(:good_jobs, name: :index_good_jobs_on_locked_by_id) if connection.index_name_exists?(:good_jobs, +:index_good_jobs_on_locked_by_id) + remove_index(:good_job_executions, name: :index_good_job_executions_on_process_id_and_created_at) if connection.index_name_exists?( + :good_job_executions, :index_good_job_executions_on_process_id_and_created_at + ) + end + end + end +end diff --git a/db/schema.rb b/db/schema.rb index ca2bfc2aa54..e1bf4f13690 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.1].define(version: 2024_05_06_180817) do +ActiveRecord::Schema[7.1].define(version: 2024_05_22_185717) do # These are extensions that must be enabled in order to support this database enable_extension "hstore" enable_extension "pgcrypto" @@ -39,13 +39,6 @@ create_table "api_keys", force: :cascade do |t| t.string "name", null: false t.string "hashed_key", null: false - t.boolean "index_rubygems", default: false, null: false - t.boolean "push_rubygem", default: false, null: false - t.boolean "yank_rubygem", default: false, null: false - t.boolean "add_owner", default: false, null: false - t.boolean "remove_owner", default: false, null: false - t.boolean "access_webhooks", default: false, null: false - t.boolean "show_dashboard", default: false, null: false t.datetime "last_accessed_at", precision: nil t.datetime "created_at", precision: nil, null: false t.datetime "updated_at", precision: nil, null: false @@ -193,13 +186,16 @@ t.text "error" t.integer "error_event", limit: 2 t.text "error_backtrace", array: true + t.uuid "process_id" t.index ["active_job_id", "created_at"], name: "index_good_job_executions_on_active_job_id_and_created_at" + t.index ["process_id", "created_at"], name: "index_good_job_executions_on_process_id_and_created_at" end create_table "good_job_processes", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| t.datetime "created_at", null: false t.datetime "updated_at", null: false t.jsonb "state" + t.integer "lock_type", limit: 2 end create_table "good_job_settings", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| @@ -232,6 +228,8 @@ t.text "job_class" t.integer "error_event", limit: 2 t.text "labels", array: true + t.uuid "locked_by_id" + t.datetime "locked_at" t.index ["active_job_id", "created_at"], name: "index_good_jobs_on_active_job_id_and_created_at" t.index ["batch_callback_id"], name: "index_good_jobs_on_batch_callback_id", where: "(batch_callback_id IS NOT NULL)" t.index ["batch_id"], name: "index_good_jobs_on_batch_id", where: "(batch_id IS NOT NULL)" @@ -240,8 +238,10 @@ t.index ["cron_key", "cron_at"], name: "index_good_jobs_on_cron_key_and_cron_at_cond", unique: true, where: "(cron_key IS NOT NULL)" t.index ["finished_at"], name: "index_good_jobs_jobs_on_finished_at", where: "((retried_good_job_id IS NULL) AND (finished_at IS NOT NULL))" t.index ["labels"], name: "index_good_jobs_on_labels", where: "(labels IS NOT NULL)", using: :gin + t.index ["locked_by_id"], name: "index_good_jobs_on_locked_by_id", where: "(locked_by_id IS NOT NULL)" t.index ["priority", "created_at"], name: "index_good_job_jobs_for_candidate_lookup", where: "(finished_at IS NULL)" t.index ["priority", "created_at"], name: "index_good_jobs_jobs_on_priority_created_at_when_unfinished", order: { priority: "DESC NULLS LAST" }, where: "(finished_at IS NULL)" + t.index ["priority", "scheduled_at"], name: "index_good_jobs_on_priority_scheduled_at_unfinished_unlocked", where: "((finished_at IS NULL) AND (locked_by_id IS NULL))" t.index ["queue_name", "scheduled_at"], name: "index_good_jobs_on_queue_name_and_scheduled_at", where: "(finished_at IS NULL)" t.index ["scheduled_at"], name: "index_good_jobs_on_scheduled_at", where: "(finished_at IS NULL)" end @@ -483,6 +483,7 @@ t.string "mfa_hashed_recovery_codes", default: [], array: true t.boolean "public_email", default: false, null: false t.datetime "deleted_at" + t.index "lower((email)::text) varchar_pattern_ops", name: "index_users_on_lower_email" t.index ["email"], name: "index_users_on_email" t.index ["handle"], name: "index_users_on_handle" t.index ["id", "confirmation_token"], name: "index_users_on_id_and_confirmation_token" @@ -578,6 +579,8 @@ t.index ["user_id"], name: "index_webauthn_verifications_on_user_id", unique: true end + add_foreign_key "api_key_rubygem_scopes", "api_keys", name: "api_key_rubygem_scopes_api_key_id_fk" + add_foreign_key "audits", "admin_github_users", name: "audits_admin_github_user_id_fk" add_foreign_key "events_rubygem_events", "geoip_infos" add_foreign_key "events_rubygem_events", "ip_addresses" add_foreign_key "events_rubygem_events", "rubygems" @@ -585,14 +588,23 @@ add_foreign_key "events_user_events", "ip_addresses" add_foreign_key "events_user_events", "users" add_foreign_key "ip_addresses", "geoip_infos" + add_foreign_key "linksets", "rubygems", name: "linksets_rubygem_id_fk" add_foreign_key "oidc_api_key_roles", "oidc_providers" add_foreign_key "oidc_api_key_roles", "users" add_foreign_key "oidc_id_tokens", "api_keys" add_foreign_key "oidc_id_tokens", "oidc_api_key_roles" add_foreign_key "oidc_pending_trusted_publishers", "users" add_foreign_key "oidc_rubygem_trusted_publishers", "rubygems" + add_foreign_key "ownership_calls", "rubygems", name: "ownership_calls_rubygem_id_fk" + add_foreign_key "ownership_calls", "users", name: "ownership_calls_user_id_fk" + add_foreign_key "ownership_requests", "ownership_calls", name: "ownership_requests_ownership_call_id_fk" + add_foreign_key "ownership_requests", "rubygems", name: "ownership_requests_rubygem_id_fk" + add_foreign_key "ownership_requests", "users", column: "approver_id", name: "ownership_requests_approver_id_fk" + add_foreign_key "ownership_requests", "users", name: "ownership_requests_user_id_fk" add_foreign_key "ownerships", "users", on_delete: :cascade add_foreign_key "versions", "api_keys", column: "pusher_api_key_id" + add_foreign_key "versions", "rubygems", name: "versions_rubygem_id_fk" + add_foreign_key "web_hooks", "users", name: "web_hooks_user_id_fk" add_foreign_key "webauthn_credentials", "users" add_foreign_key "webauthn_verifications", "users" end diff --git a/lib/gem_cache_purger.rb b/lib/gem_cache_purger.rb index 9f2698f0677..af0f5e46440 100644 --- a/lib/gem_cache_purger.rb +++ b/lib/gem_cache_purger.rb @@ -6,7 +6,6 @@ def self.call(gem_name) FastlyPurgeJob.perform_later(path:, soft: true) end - Rails.cache.delete("deps/v1/#{gem_name}") FastlyPurgeJob.perform_later(path: "versions", soft: true) FastlyPurgeJob.perform_later(path: "gem/#{gem_name}", soft: true) FastlyPurgeJob.perform_later(key: "gem/#{gem_name}", soft: true) diff --git a/test/functional/sessions_controller_test.rb b/test/functional/sessions_controller_test.rb index 40897d0e79d..485f5085ec8 100644 --- a/test/functional/sessions_controller_test.rb +++ b/test/functional/sessions_controller_test.rb @@ -661,6 +661,8 @@ class SessionsControllerTest < ActionController::TestCase context "when providing credentials but the session expired" do setup do travel 30.minutes + @existing_webauthn = @controller.session[:webauthn_authentication] + post( :webauthn_create, params: { @@ -680,7 +682,6 @@ class SessionsControllerTest < ActionController::TestCase assert_nil @controller.session[:mfa_expires_at] assert_nil @controller.session[:mfa_login_started_at] assert_nil @controller.session[:mfa_user] - assert_nil @controller.session[:webauthn_authentication] end should "not sign in the user" do @@ -693,6 +694,8 @@ class SessionsControllerTest < ActionController::TestCase should "render sign in page" do assert_template "sessions/new" + refute_nil @controller.session[:webauthn_authentication] + refute_equal @existing_webauthn, @controller.session[:webauthn_authentication] end end end diff --git a/test/integration/rack_attack_test.rb b/test/integration/rack_attack_test.rb index 783d94cce80..446a95123e8 100644 --- a/test/integration/rack_attack_test.rb +++ b/test/integration/rack_attack_test.rb @@ -60,6 +60,17 @@ class RackAttackTest < ActionDispatch::IntegrationTest assert_response :success end + should "allow email confirmation resend via unconfirmed" do + stay_under_limit_for("clearance/ip/1") + stay_under_email_limit_for("email_confirmations/email") + + patch "/email_confirmations/unconfirmed", + headers: { REMOTE_ADDR: @ip_address } + follow_redirect! + + assert_response :success + end + context "owners requests" do setup do post session_path(session: { who: @user.handle, password: PasswordHelpers::SECURE_TEST_PASSWORD }) diff --git a/test/integration/sign_up_test.rb b/test/integration/sign_up_test.rb index 7db96a0faf2..a8b119bbe3e 100644 --- a/test/integration/sign_up_test.rb +++ b/test/integration/sign_up_test.rb @@ -14,6 +14,19 @@ class SignUpTest < SystemTest User.find_by(handle: "nick").events.where(tag: Events::UserEvent::CREATED).sole end + test "sign up stores original email casing" do + visit sign_up_path + + fill_in "Email", with: "Email@person.com" + fill_in "Username", with: "nick" + fill_in "Password", with: PasswordHelpers::SECURE_TEST_PASSWORD + click_button "Sign up" + + assert page.has_selector? "#flash_notice", text: "A confirmation mail has been sent to your email address." + + assert_equal "Email@person.com", User.last.email + end + test "sign up with no handle" do visit sign_up_path diff --git a/test/models/user_test.rb b/test/models/user_test.rb index 482c17b5dd6..ad5e82958c2 100644 --- a/test/models/user_test.rb +++ b/test/models/user_test.rb @@ -926,7 +926,7 @@ class UserTest < ActiveSupport::TestCase context ".normalize_email" do should "return the normalized email" do - assert_equal "user@example.com", User.normalize_email(:"UsEr@ example . COM") + assert_equal "UsEr@example.COM", User.normalize_email(:"UsEr@ example . COM") end should "return an empty string on invalid inputs" do diff --git a/test/tasks/maintenance/backfill_linkset_links_to_version_metadata_task_test.rb b/test/tasks/maintenance/backfill_linkset_links_to_version_metadata_task_test.rb new file mode 100644 index 00000000000..258ed6d2b38 --- /dev/null +++ b/test/tasks/maintenance/backfill_linkset_links_to_version_metadata_task_test.rb @@ -0,0 +1,91 @@ +# frozen_string_literal: true + +require "test_helper" + +class Maintenance::BackfillLinksetLinksToVersionMetadataTaskTest < ActiveSupport::TestCase + context "#collection" do + should "return all versions" do + assert_equal Version.count, Maintenance::BackfillLinksetLinksToVersionMetadataTask.collection.count + end + end + + context "#process" do + context "without a linkset" do + setup do + @version = create(:version) + @rubygem = @version.rubygem + @rubygem.update!(linkset: nil) + end + + should "not change version metadata" do + assert_no_changes "@version.reload.metadata" do + Maintenance::BackfillLinksetLinksToVersionMetadataTask.process(@version) + end + end + end + + context "with a linkset and version metadata uris" do + setup do + @version = create( + :version, + metadata: { + "source_code_uri" => "https://example.com/source", + "documentation_uri" => "https://example.com/docs", + "foo" => "bar" + } + ) + @rubygem = @version.rubygem + @rubygem.linkset.update!("home" => "https://example.com/home", + "wiki" => "https://example.com/wiki") + end + + should "only update the home uri" do + Maintenance::BackfillLinksetLinksToVersionMetadataTask.process(@version) + + assert_equal({ + "source_code_uri" => "https://example.com/source", + "documentation_uri" => "https://example.com/docs", + "foo" => "bar", + "homepage_uri" => "https://example.com/home" + }, @version.reload.metadata) + end + + should "not update the home uri when present in metadata" do + @version.metadata["homepage_uri"] = "https://example.com/home/custom" + @version.save! + + Maintenance::BackfillLinksetLinksToVersionMetadataTask.process(@version) + + assert_equal({ + "source_code_uri" => "https://example.com/source", + "documentation_uri" => "https://example.com/docs", + "foo" => "bar", + "homepage_uri" => "https://example.com/home/custom" + }, @version.reload.metadata) + end + end + + context "with a linkset and no version metadata uris" do + setup do + @version = create(:version, metadata: { "foo" => "bar" }) + @rubygem = @version.rubygem + @rubygem.linkset.update!("home" => "https://example.com/home", + "wiki" => "https://example.com/wiki") + end + + should "update the version metadata" do + Maintenance::BackfillLinksetLinksToVersionMetadataTask.process(@version) + + assert_equal({ + "wiki_uri" => "https://example.com/wiki", + "foo" => "bar", + "homepage_uri" => "https://example.com/home", + "bug_tracker_uri" => "http://example.com", + "source_code_uri" => "http://example.com", + "mailing_list_uri" => "http://example.com", + "documentation_uri" => "http://example.com" + }, @version.reload.metadata) + end + end + end +end diff --git a/test/unit/gem_cache_purger_test.rb b/test/unit/gem_cache_purger_test.rb index a0e9df57dcb..d73aa57f616 100644 --- a/test/unit/gem_cache_purger_test.rb +++ b/test/unit/gem_cache_purger_test.rb @@ -11,7 +11,6 @@ class GemCachePurgerTest < ActiveSupport::TestCase should "expire API memcached" do Rails.cache.expects(:delete).with("info/#{@gem_name}") Rails.cache.expects(:delete).with("names") - Rails.cache.expects(:delete).with("deps/v1/#{@gem_name}") GemCachePurger.call(@gem_name) end