diff --git a/.env b/.env index cbd4d820..8b0c0910 100644 --- a/.env +++ b/.env @@ -1,3 +1,5 @@ +AUTHENTICATION_REQUIRED= + AWS_REGION=us-east-2 ENVELOPE_DOWNLOADS_BUCKET=envelope-downloads diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 92333794..9dc9065e 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -3,12 +3,9 @@ name: Build and push on: push: - branches: ["eks-infrastructure","staging","main","production"] + branches: ["eks-infrastructure","staging","main","master","production","sandbox"] workflow_dispatch: - inputs: - environment: - description: 'Build & Push' permissions: id-token: write @@ -31,8 +28,21 @@ jobs: image: ${{ steps.img.outputs.image }} steps: - - name: Checkout code + - name: Checkout code (with submodules) uses: actions/checkout@v4 + with: + fetch-depth: 0 + submodules: recursive + + - name: Verify submodules present + run: | + git submodule status + if [ ! -d vendor/grape-middleware-logger ]; then + echo "Submodule vendor/grape-middleware-logger is missing" >&2 + exit 1 + fi + ls -la vendor/grape-middleware-logger | sed -n '1,50p' + - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v4 @@ -55,7 +65,14 @@ jobs: TAG="$DATE_TAG.$BUILD_NUM" echo "tag=$TAG" >> "$GITHUB_OUTPUT" + - name: Compute ref tag (branch name) + id: ref + run: | + REF_TAG=$(echo "${GITHUB_REF_NAME}" | tr '[:upper:]' '[:lower:]' | sed -E 's#[^a-z0-9._-]+#-#g') + echo "ref_tag=$REF_TAG" >> "$GITHUB_OUTPUT" + - name: Build Docker image (multi-stage) + id: build uses: docker/build-push-action@v5 with: context: . @@ -64,7 +81,7 @@ jobs: push: true tags: | ${{ steps.login-ecr.outputs.registry }}/${{ env.ECR_REPOSITORY }}:${{ steps.tag.outputs.tag }} - ${{ steps.login-ecr.outputs.registry }}/${{ env.ECR_REPOSITORY }}:staging + ${{ steps.login-ecr.outputs.registry }}/${{ env.ECR_REPOSITORY }}:${{ steps.ref.outputs.ref_tag }} cache-from: type=gha cache-to: type=gha,mode=max @@ -72,3 +89,58 @@ jobs: id: img run: | echo "image=${{ steps.login-ecr.outputs.registry }}/${{ env.ECR_REPOSITORY }}:${{ steps.tag.outputs.tag }}" >> "$GITHUB_OUTPUT" + + - name: Notify Slack (build result) + if: always() + env: + SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} + REPO: ${{ github.repository }} + RUN_URL: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }} + BRANCH: ${{ github.ref_name }} + IMAGE_DATE: ${{ steps.tag.outputs.tag }} + IMAGE_BRANCH: ${{ steps.ref.outputs.ref_tag }} + DIGEST: ${{ steps.build.outputs.digest }} + run: | + if [ -z "${SLACK_WEBHOOK_URL}" ]; then + echo "SLACK_WEBHOOK_URL not set; skipping notification"; + exit 0; + fi + STATUS="${{ job.status }}" + EMOJI=✅; [ "$STATUS" = "failure" ] && EMOJI=❌ + payload=$(jq -n \ + --arg repo "$REPO" \ + --arg branch "$BRANCH" \ + --arg tag_date "$IMAGE_DATE" \ + --arg tag_branch "$IMAGE_BRANCH" \ + --arg digest "${DIGEST:-}" \ + --arg run "$RUN_URL" \ + --arg status "$STATUS" \ + --arg emoji "$EMOJI" \ + '{ + text: ($emoji + " Build " + $status + " for " + $repo + " (" + $branch + ")"), + blocks: [ + { + "type": "header", + "text": { + "type": "plain_text", + "text": $emoji + " Build " + $status + " for " + $branch, + "emoji": true + } + }, + { + "type": "section", + "fields": [ + {"type":"mrkdwn", "text": "*Repository:*\n" + $repo}, + {"type":"mrkdwn", "text": "*Branch:*\n" + $branch}, + {"type":"mrkdwn", "text": "*Tag (date.build):*\n" + $tag_date}, + {"type":"mrkdwn", "text": "*Tag (branch):*\n" + $tag_branch}, + {"type":"mrkdwn", "text": "*Digest:*\n" + ($digest // "N/A")} + ] + }, + { + "type":"section", + "text":{"type":"mrkdwn","text":"<" + $run + "|View run>"} + } + ] + }') + curl -sS -X POST -H 'Content-type: application/json' --data "$payload" "$SLACK_WEBHOOK_URL" || true diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..25b7e7e4 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "vendor/grape-middleware-logger"] + path = vendor/grape-middleware-logger + url = https://github.com/soverin/grape-middleware-logger.git diff --git a/.rubocop.yml b/.rubocop.yml index 4ee00698..d7aed5ab 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -13,6 +13,7 @@ AllCops: Exclude: - "bin/**/*" - "db/migrate/**/*" + - "vendor/**/*" Bundler/OrderedGems: Enabled: false diff --git a/.ruby-version b/.ruby-version index 1cf82530..2aa51319 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -3.4.6 +3.4.7 diff --git a/Dockerfile b/Dockerfile index 67f955af..09a2b357 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,7 +2,7 @@ FROM registry.access.redhat.com/ubi10/ubi-minimal:10.0-1758185635 AS builder ARG PLAT=x86_64 -ARG RUBY_VERSION=3.4.6 +ARG RUBY_VERSION=3.4.7 ENV APP_PATH=/app/ ENV LANGUAGE=en_US:en ENV LANG=C.UTF-8 @@ -70,6 +70,22 @@ RUN set -eux; \ COPY Gemfile Gemfile.lock .ruby-version $APP_PATH +# Ensure path-based gems from submodules are available to Bundler +# Copy the grape-middleware-logger submodule before bundle install +COPY vendor/grape-middleware-logger $APP_PATH/vendor/grape-middleware-logger + +# Some gemspecs use `git ls-files`; submodule `.git` files reference parent repo +# which is not present in the image. Reinitialize as a standalone git repo. +RUN set -eux; \ + if [ -d "$APP_PATH/vendor/grape-middleware-logger" ]; then \ + cd "$APP_PATH/vendor/grape-middleware-logger"; \ + # If .git is a file (submodule link), remove it and init a new repo + if [ -e .git ] && [ ! -d .git ]; then rm -f .git; fi; \ + git init -q; \ + git add -A || true; \ + git -c user.email=builder@example -c user.name=builder commit -q -m "vendored submodule snapshot" || true; \ + fi + RUN mkdir -p ./vendor && \ mkdir -p ./vendor/cache COPY local_packages/grape-middleware-logger-2.4.0.gem ./vendor/cache/ @@ -107,7 +123,10 @@ RUN mkdir -p /runtime/usr/local /runtime/etc /runtime/usr/bin /runtime/usr/lib64 # Ruby runtime from /usr/local mkdir -p /runtime/usr/local/bin /runtime/usr/local/lib && \ cp -a /usr/local/bin/ruby /runtime/usr/local/bin/ && \ - cp -a /usr/local/bin/gem /usr/local/bin/rake /usr/local/bin/bundle /usr/local/bin/bundler /runtime/usr/local/bin/ 2>/dev/null || true && \ + cp -a /usr/local/bin/gem /runtime/usr/local/bin/ 2>/dev/null && \ + cp -a /usr/local/bin/rake /runtime/usr/local/bin/ 2>/dev/null && \ + cp -a /usr/local/bin/bundle /runtime/usr/local/bin/ 2>/dev/null && \ + cp -a /usr/local/bin/bundler /runtime/usr/local/bin/ 2>/dev/null && \ cp -a /usr/local/lib/ruby /runtime/usr/local/lib/ && \ cp -a /etc/pki /runtime/etc/ && \ cp -a /etc/ssl /runtime/etc/ || true && \ @@ -120,10 +139,10 @@ RUN mkdir -p /runtime/usr/local /runtime/etc /runtime/usr/bin /runtime/usr/lib64 cp -a /usr/bin/openssl /runtime/usr/bin/ && \ # Copy PostgreSQL client binaries, dereferencing symlinks if present for b in \ - /usr/bin/psql /usr/bin/pg_dump /usr/bin/pg_restore \ - /usr/pgsql-17/bin/psql /usr/pgsql-17/bin/pg_dump /usr/pgsql-17/bin/pg_restore; do \ - [ -f "$b" ] || continue; \ - cp -aL "$b" /runtime/usr/bin/ 2>/dev/null || true; \ + /usr/bin/psql /usr/bin/pg_dump /usr/bin/pg_restore \ + /usr/pgsql-17/bin/psql /usr/pgsql-17/bin/pg_dump /usr/pgsql-17/bin/pg_restore; do \ + [ -f "$b" ] || continue; \ + cp -aL "$b" /runtime/usr/bin/ 2>/dev/null || true; \ done && \ mkdir -p /runtime/usr/lib64/ossl-modules && \ cp -a /usr/lib64/ossl-modules/* /runtime/usr/lib64/ossl-modules/ 2>/dev/null || true @@ -135,12 +154,12 @@ COPY openssl.cnf /runtime/etc/pki/tls/openssl.cnf # Auto-collect shared library dependencies for Ruby, native gems, and psql RUN set -eux; \ mkdir -p /runtime/usr/lib64; \ - targets="/usr/local/bin/ruby /usr/bin/psql /usr/bin/pg_dump /usr/bin/pg_restore"; \ + targets="/usr/local/bin/ruby /usr/bin/psql /usr/bin/pg_dump /usr/bin/pg_restore /usr/bin/git"; \ if [ -d "$APP_PATH/vendor/bundle" ]; then \ sofiles=$(find "$APP_PATH/vendor/bundle" -type f -name "*.so" || true); \ targets="$targets $sofiles"; \ fi; \ - for t in $targets; do \ + for t in "$targets"; do \ [ -f "$t" ] || continue; \ ldd "$t" | awk '/=> \/|\//{print $3}' | sed -e 's/(0x[0-9a-fA-F]\+)//g' | grep -E '^/' || true; \ done | sort -u | while read -r lib; do \ @@ -186,6 +205,9 @@ RUN set -eux; \ cp -a /usr/lib64/libgdbm.so.* /runtime/usr/lib64/ 2>/dev/null || true; \ # App cp -a $APP_PATH /runtime/app; \ + # Git client for gems that call `git` at runtime + if [ -x /usr/bin/git ]; then cp -a /usr/bin/git /runtime/usr/bin/git; fi; \ + if [ -d /usr/libexec/git-core ]; then mkdir -p /runtime/usr/libexec && cp -a /usr/libexec/git-core /runtime/usr/libexec/git-core; fi; \ # Timezone data for TZInfo mkdir -p /runtime/usr/share && cp -a /usr/share/zoneinfo /runtime/usr/share/zoneinfo; \ chmod +x /tmp/docker-entrypoint.sh; cp /tmp/docker-entrypoint.sh /runtime/usr/bin/docker-entrypoint.sh @@ -194,7 +216,7 @@ RUN set -eux; \ FROM registry.access.redhat.com/ubi10/ubi-micro:10.0-1754556444 ENV APP_PATH=/app/ -ARG RUBY_VERSION=3.4.6 +ARG RUBY_VERSION=3.4.7 ENV PATH="/usr/local/bin:$PATH" ENV LD_LIBRARY_PATH="/usr/lib64:/lib64:/usr/local/lib" ENV OPENSSL_MODULES="/usr/lib64/ossl-modules" diff --git a/Gemfile b/Gemfile index 47766bc8..8780b795 100644 --- a/Gemfile +++ b/Gemfile @@ -9,7 +9,7 @@ gem 'fiddle', '~> 1.1' gem 'grape', '= 2.2.0' gem 'grape-entity', '~> 1.0' gem 'grape-kaminari', '~> 0.4' -gem 'grape-middleware-logger', '~> 2.4.0' +gem 'grape-middleware-logger', path: 'vendor/grape-middleware-logger' gem 'hashie', '~> 5.0' gem 'hashie-forbidden_attributes', '~> 0.1' gem 'jsonpath', '~> 1.1' @@ -22,7 +22,7 @@ gem 'pundit', '~> 2.5' gem 'rack-contrib', '~> 2.5' gem 'rack-cors', '~> 2.0' gem 'rake', '~> 13.2' -gem 'rdoc', '~> 6.13' +gem 'rdoc', '~> 6.15.0' gem 'rubyzip', '~> 2.4', require: 'zip' gem 'swagger-blocks', '~> 3.0.0' @@ -63,7 +63,7 @@ gem 'pg_search', '~> 2.3' gem 'dotenv', '~> 3.1', groups: %i[development test] # Background processing -gem 'activejob', '= 8.0.2', require: 'active_job' +gem 'activejob', '= 8.0.2.1', require: 'active_job' gem 'sidekiq', '= 7.3.8' gem 'sidekiq-failures', '~> 1.0' @@ -79,6 +79,10 @@ gem 'reline', '~> 0.6' # For lokilogger gem 'http' +# Vulnerability fixes +gem 'rack', '~> 2.2.20' +gem 'rexml', '>= 3.4.4' + # Development tools group :development do gem 'grape-raketasks' diff --git a/Gemfile.lock b/Gemfile.lock index f9215249..d49c94cd 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,24 +1,30 @@ +PATH + remote: vendor/grape-middleware-logger + specs: + grape-middleware-logger (1.12.0) + grape (>= 0.17) + GEM remote: https://rubygems.org/ specs: - actionview (8.0.2) - activesupport (= 8.0.2) + actionview (8.0.2.1) + activesupport (= 8.0.2.1) builder (~> 3.1) erubi (~> 1.11) rails-dom-testing (~> 2.2) rails-html-sanitizer (~> 1.6) - activejob (8.0.2) - activesupport (= 8.0.2) + activejob (8.0.2.1) + activesupport (= 8.0.2.1) globalid (>= 0.3.6) - activemodel (8.0.2) - activesupport (= 8.0.2) - activerecord (8.0.2) - activemodel (= 8.0.2) - activesupport (= 8.0.2) + activemodel (8.0.2.1) + activesupport (= 8.0.2.1) + activerecord (8.0.2.1) + activemodel (= 8.0.2.1) + activesupport (= 8.0.2.1) timeout (>= 0.4.0) activerecord-import (2.2.0) activerecord (>= 4.2) - activesupport (8.0.2) + activesupport (8.0.2.1) base64 benchmark (>= 0.3) bigdecimal @@ -47,7 +53,7 @@ GEM ast (2.4.3) attribute_normalizer (1.2.0) aws-eventstream (1.4.0) - aws-partitions (1.1168.0) + aws-partitions (1.1174.0) aws-sdk-core (3.233.0) aws-eventstream (~> 1, >= 1.3.0) aws-partitions (~> 1, >= 1.992.0) @@ -56,10 +62,10 @@ GEM bigdecimal jmespath (~> 1, >= 1.6.1) logger - aws-sdk-kms (1.113.0) + aws-sdk-kms (1.114.0) aws-sdk-core (~> 3, >= 3.231.0) aws-sigv4 (~> 1.5) - aws-sdk-s3 (1.199.1) + aws-sdk-s3 (1.200.0) aws-sdk-core (~> 3, >= 3.231.0) aws-sdk-kms (~> 1) aws-sigv4 (~> 1.5) @@ -71,7 +77,7 @@ GEM thread_safe (~> 0.3, >= 0.3.1) base64 (0.3.0) benchmark (0.4.1) - bigdecimal (3.2.3) + bigdecimal (3.3.1) builder (3.3.0) byebug (12.0.0) childprocess (5.1.0) @@ -128,7 +134,7 @@ GEM dry-logic (~> 1.4) zeitwerk (~> 2.6) encryptor (3.0.0) - erb (5.0.2) + erb (5.1.1) erubi (1.13.1) factory_bot (6.5.5) activesupport (>= 6.1.0) @@ -153,8 +159,6 @@ GEM grape-kaminari (0.4.5) grape (>= 1.6.1) kaminari-grape - grape-middleware-logger (2.4.0) - grape (>= 0.17) grape-raketasks (0.0.3) activesupport grape @@ -182,7 +186,7 @@ GEM rdoc (>= 4.0.0) reline (>= 0.4.2) jmespath (1.6.2) - json (2.15.0) + json (2.15.1) json-schema (5.2.2) addressable (~> 2.8) bigdecimal (~> 3.1) @@ -226,7 +230,7 @@ GEM mime-types-data (~> 3.2025, >= 3.2025.0507) mime-types-data (3.2025.0924) mini_portile2 (2.8.9) - minitest (5.25.5) + minitest (5.26.0) mize (0.6.1) multi_json (1.17.0) mustermann (3.0.4) @@ -234,7 +238,7 @@ GEM mustermann-grape (1.1.0) mustermann (>= 1.0.0) netrc (0.11.0) - newrelic_rpm (9.21.0) + newrelic_rpm (9.22.0) nio4r (2.7.4) nokogiri (1.18.10) mini_portile2 (~> 2.8.2) @@ -255,10 +259,10 @@ GEM pg_search (2.3.7) activerecord (>= 6.1) activesupport (>= 6.1) - pp (0.6.2) + pp (0.6.3) prettyprint prettyprint (0.2.0) - prism (1.5.1) + prism (1.6.0) pry (0.15.2) coderay (~> 1.1) method_source (~> 1.0) @@ -271,7 +275,7 @@ GEM pundit (2.5.2) activesupport (>= 3.0.0) racc (1.8.1) - rack (2.2.18) + rack (2.2.20) rack-contrib (2.5.0) rack (< 4) rack-cors (2.0.2) @@ -289,9 +293,10 @@ GEM rake (13.3.0) rb-readline (0.5.5) rbtree3 (0.7.1) - rdoc (6.14.2) + rdoc (6.15.0) erb psych (>= 4.0.0) + tsort redis (4.8.1) redis-client (0.26.1) connection_pool @@ -315,7 +320,7 @@ GEM rspec-expectations (3.13.5) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.13.0) - rspec-mocks (3.13.5) + rspec-mocks (3.13.6) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.13.0) rspec-support (3.13.6) @@ -377,16 +382,17 @@ GEM thor (1.4.0) thread_safe (0.3.6) timeout (0.4.3) - tins (1.44.1) + tins (1.45.0) bigdecimal mize (~> 0.6) sync + tsort (0.2.0) tzinfo (2.0.6) concurrent-ruby (~> 1.0) unicode-display_width (3.2.0) unicode-emoji (~> 4.1) unicode-emoji (4.1.0) - uri (1.0.3) + uri (1.0.4) uuid (2.3.9) macaddr (~> 1.0) vcr (6.3.1) @@ -408,7 +414,7 @@ PLATFORMS ruby DEPENDENCIES - activejob (= 8.0.2) + activejob (= 8.0.2.1) activerecord-import (~> 2.1) activerecord-jdbcpostgresql-adapter airborne (~> 0.3) @@ -434,7 +440,7 @@ DEPENDENCIES grape (= 2.2.0) grape-entity (~> 1.0) grape-kaminari (~> 0.4) - grape-middleware-logger (~> 2.4.0) + grape-middleware-logger! grape-raketasks hashie (~> 5.0) hashie-forbidden_attributes (~> 0.1) @@ -456,14 +462,16 @@ DEPENDENCIES pry (~> 0.15) puma (~> 6.6) pundit (~> 2.5) + rack (~> 2.2.20) rack-contrib (~> 2.5) rack-cors (~> 2.0) rake (~> 13.2) rb-readline (~> 0.5) - rdoc (~> 6.13) + rdoc (~> 6.15.0) redis (~> 4.8) reline (~> 0.6) rest-client (~> 2.1) + rexml (>= 3.4.4) rspec (~> 3.13) rubocop (~> 1.75) rubocop-factory_bot (~> 2.27) diff --git a/app/api/base.rb b/app/api/base.rb index f2b45f05..c05bb52c 100644 --- a/app/api/base.rb +++ b/app/api/base.rb @@ -1,9 +1,12 @@ require 'v1/base' require 'v2/base' +require_relative '../../lib/json_request_logger' module API # Main base class that defines all API versions class Base < Grape::API + helpers CommunityHelpers + insert_after Grape::Middleware::Formatter, Grape::Middleware::Logger, { logger: MR.logger, filter: Class.new do @@ -14,6 +17,13 @@ def filter(opts) end.new } + # Emit a single JSON log line per request + use JsonRequestLogger + + before do + authenticate! unless request.path == '/swagger.json' + end + mount API::V1::Base mount API::V2::Base end diff --git a/app/api/helpers/shared_helpers.rb b/app/api/helpers/shared_helpers.rb index ef5c3bb7..f85a326e 100644 --- a/app/api/helpers/shared_helpers.rb +++ b/app/api/helpers/shared_helpers.rb @@ -110,6 +110,11 @@ def test_response end def authenticate! + auth_required = ActiveRecord::Type::Boolean.new.deserialize( + ENV.fetch('AUTHENTICATION_REQUIRED', nil) + ) + + return if !auth_required && request.request_method == 'GET' return if current_user json_error!(['Invalid token'], nil, 401) diff --git a/docker-compose.yml b/docker-compose.yml index b67e42fd..767b52d5 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,4 +1,3 @@ -version: "3" services: db: image: postgres:16.10-alpine3.22 diff --git a/lib/json_request_logger.rb b/lib/json_request_logger.rb new file mode 100644 index 00000000..25e4b1e1 --- /dev/null +++ b/lib/json_request_logger.rb @@ -0,0 +1,69 @@ +require 'json' +require 'rack/request' + +# Rack middleware that emits a single JSON log line per request +class JsonRequestLogger + REDACT_KEYS = %w[password passwd secret token api_key authorization auth jwt bearer].freeze + + def initialize(app) + @app = app + end + + def call(env) + start = Process.clock_gettime(Process::CLOCK_MONOTONIC) + status = nil + headers = nil + body = nil + begin + status, headers, body = @app.call(env) + status + ensure + duration_ms = ((Process.clock_gettime(Process::CLOCK_MONOTONIC) - start) * 1000.0).round(2) + req = Rack::Request.new(env) + path = req.fullpath rescue env['PATH_INFO'] + params = safe_params(req) + labels = { + method: req.request_method, + path: path, + status: status || 500, + duration_ms: duration_ms, + ip: req.ip, + ua: req.user_agent, + request_id: env['HTTP_X_REQUEST_ID'] || env['action_dispatch.request_id'] || env['REQUEST_ID'] + } + # Prefer structured logging through MR helper if available + if defined?(MR) && MR.respond_to?(:log_with_labels) + MR.log_with_labels(:info, 'request', labels) + else + line = { message: 'request' }.merge(labels) + (defined?(MR) && MR.logger || Logger.new($stdout)).info(line.to_json) + end + end + [status, headers, body] + end + + private + + def safe_params(req) + params = req.params rescue {} + redact(params) + end + + def redact(obj) + case obj + when Hash + obj.transform_keys(&:to_s).each_with_object({}) do |(k, v), h| + if REDACT_KEYS.any? { |rk| k.downcase.include?(rk) } + h[k] = '[FILTERED]' + else + h[k] = redact(v) + end + end + when Array + obj.map { |v| redact(v) } + else + obj + end + end +end + diff --git a/lib/swagger_docs/sections/envelopes.rb b/lib/swagger_docs/sections/envelopes.rb index 32ca3e5e..bf5baf57 100644 --- a/lib/swagger_docs/sections/envelopes.rb +++ b/lib/swagger_docs/sections/envelopes.rb @@ -14,7 +14,7 @@ module Envelopes # rubocop:todo Metrics/ModuleLength, Style/Documentation key :produces, ['application/json'] key :tags, ['Envelopes'] - security + # security parameter community_name parameter metadata_only @@ -37,7 +37,7 @@ module Envelopes # rubocop:todo Metrics/ModuleLength, Style/Documentation key :produces, ['application/json'] key :tags, ['Envelopes'] - security + # security parameter community_name parameter published_by(required: true) @@ -96,7 +96,7 @@ module Envelopes # rubocop:todo Metrics/ModuleLength, Style/Documentation key :produces, ['application/json'] key :tags, ['Envelopes'] - security + # security parameter community_name parameter name: :after, @@ -137,7 +137,7 @@ module Envelopes # rubocop:todo Metrics/ModuleLength, Style/Documentation key :produces, ['application/json'] key :tags, ['Envelopes'] - security + # security parameter community_name @@ -155,7 +155,7 @@ module Envelopes # rubocop:todo Metrics/ModuleLength, Style/Documentation key :produces, ['application/json'] key :tags, ['Envelopes'] - security + # security parameter community_name parameter envelope_id @@ -175,7 +175,7 @@ module Envelopes # rubocop:todo Metrics/ModuleLength, Style/Documentation key :produces, ['application/json'] key :tags, ['Envelopes'] - security + # security parameter community_name parameter envelope_id @@ -195,7 +195,7 @@ module Envelopes # rubocop:todo Metrics/ModuleLength, Style/Documentation key :produces, ['application/json'] key :tags, ['Envelopes'] - security + # security parameter community_name parameter envelope_id diff --git a/lib/swagger_docs/sections/general.rb b/lib/swagger_docs/sections/general.rb index 97b97427..fed0a6a6 100644 --- a/lib/swagger_docs/sections/general.rb +++ b/lib/swagger_docs/sections/general.rb @@ -12,7 +12,7 @@ module General # rubocop:todo Style/Documentation key :produces, ['application/json'] key :tags, ['General'] - security + # security response 200 do key :description, 'API root' @@ -28,7 +28,7 @@ module General # rubocop:todo Style/Documentation key :produces, ['application/json'] key :tags, ['General'] - security + # security response 200 do key :description, 'General info about this API node' @@ -44,7 +44,7 @@ module General # rubocop:todo Style/Documentation key :produces, ['text/html'] key :tags, ['General'] - security + # security response 200, description: 'shows the README rendered in HTML' end @@ -57,7 +57,7 @@ module General # rubocop:todo Style/Documentation key :produces, ['application/json'] key :tags, ['General'] - security + # security response 200 do key :description, 'Retrieve a new ctid' diff --git a/lib/swagger_docs/sections/graphs.rb b/lib/swagger_docs/sections/graphs.rb index 6381f421..67b86055 100644 --- a/lib/swagger_docs/sections/graphs.rb +++ b/lib/swagger_docs/sections/graphs.rb @@ -12,7 +12,7 @@ module Graphs # rubocop:todo Style/Documentation key :produces, ['application/json'] key :tags, ['Graphs'] - security + # security parameter community_name @@ -58,7 +58,7 @@ module Graphs # rubocop:todo Style/Documentation key :produces, ['application/json'] key :tags, ['Graphs'] - security + # security parameter community_name parameter resource_id diff --git a/lib/swagger_docs/sections/resources.rb b/lib/swagger_docs/sections/resources.rb index be4298c3..670d39c2 100644 --- a/lib/swagger_docs/sections/resources.rb +++ b/lib/swagger_docs/sections/resources.rb @@ -145,7 +145,7 @@ module Resources # rubocop:todo Metrics/ModuleLength, Style/Documentation key :consumes, ['application/json'] key :tags, ['Resources'] - security + # security parameter community_name @@ -199,7 +199,7 @@ module Resources # rubocop:todo Metrics/ModuleLength, Style/Documentation key :produces, ['application/json'] key :tags, ['Resources'] - security + # security parameter community_name parameter resource_id diff --git a/lib/swagger_docs/sections/search.rb b/lib/swagger_docs/sections/search.rb index 7cfd7276..1afe371d 100644 --- a/lib/swagger_docs/sections/search.rb +++ b/lib/swagger_docs/sections/search.rb @@ -2,8 +2,7 @@ module MetadataRegistry class SwaggerDocs module Sections # rubocop:todo Style/Documentation - # rubocop:todo Metrics/ModuleLength - module Search # rubocop:todo Style/Documentation, Metrics/ModuleLength + module Search # rubocop:todo Style/Documentation # rubocop:enable Style/Documentation extend ActiveSupport::Concern @@ -87,7 +86,7 @@ module Search # rubocop:todo Style/Documentation, Metrics/ModuleLength key :produces, ['application/json'] key :tags, ['Search'] - security + # security parameter community_name parameters_for_search @@ -109,7 +108,7 @@ module Search # rubocop:todo Style/Documentation, Metrics/ModuleLength key :produces, ['application/json'] key :tags, ['Search'] - security + # security parameter community_name parameter resource_type(_in: :path) @@ -126,7 +125,6 @@ module Search # rubocop:todo Style/Documentation, Metrics/ModuleLength end end end - # rubocop:enable Metrics/ModuleLength end end end diff --git a/s3_bucket_purge.sh b/s3_bucket_purge.sh new file mode 100755 index 00000000..62ae2ced --- /dev/null +++ b/s3_bucket_purge.sh @@ -0,0 +1,68 @@ +#!/usr/bin/env bash +set -euo pipefail + +BUCKET="${1:-}" +REGION="${AWS_REGION:-us-east-1}" + +if [[ -z "${BUCKET}" ]]; then + echo "Usage: $0 " >&2 + exit 1 +fi + +echo "Target bucket: s3://${BUCKET} (region: ${REGION})" +echo "Verifying bucket exists..." +if ! aws s3api head-bucket --bucket "${BUCKET}" >/dev/null 2>&1; then + echo "Bucket not found or inaccessible: ${BUCKET}" >&2 + exit 1 +fi + +echo +echo "Listing current objects (first 50):" +aws s3 ls "s3://${BUCKET}" --recursive | head -n 50 || true +echo +echo "Object summary:" +aws s3 ls "s3://${BUCKET}" --recursive --human-readable --summarize | tail -n 3 || true + +echo +echo "Counting versions and delete markers (if versioning enabled)..." +VERSIONS_COUNT=$(aws s3api list-object-versions --bucket "${BUCKET}" --output text --query 'length(Versions)' 2>/dev/null || echo 0) +DELETEM_COUNT=$(aws s3api list-object-versions --bucket "${BUCKET}" --output text --query 'length(DeleteMarkers)' 2>/dev/null || echo 0) +echo "Versions: ${VERSIONS_COUNT}" +echo "Delete markers: ${DELETEM_COUNT}" + +echo +read -r -p "Purge ALL objects, versions, and delete markers? Type 'delete' to confirm: " CONFIRM +if [[ "${CONFIRM}" != "delete" ]]; then + echo "Aborted." + exit 1 +fi + +echo +echo "Deleting all object versions (if any)..." +aws s3api list-object-versions --bucket "${BUCKET}" --output text --query 'Versions[].[Key,VersionId]' \ +| while read -r KEY VERSION_ID; do + [[ -z "${KEY:-}" || -z "${VERSION_ID:-}" ]] && continue + aws s3api delete-object --bucket "${BUCKET}" --key "${KEY}" --version-id "${VERSION_ID}" >/dev/null || true + done + +echo "Deleting all delete markers (if any)..." +aws s3api list-object-versions --bucket "${BUCKET}" --output text --query 'DeleteMarkers[].[Key,VersionId]' \ +| while read -r KEY VERSION_ID; do + [[ -z "${KEY:-}" || -z "${VERSION_ID:-}" ]] && continue + aws s3api delete-object --bucket "${BUCKET}" --key "${KEY}" --version-id "${VERSION_ID}" >/dev/null || true + done + +echo "Deleting any remaining (unversioned/current) objects..." +aws s3 rm "s3://${BUCKET}" --recursive >/dev/null || true + +echo +read -r -p "Remove the S3 bucket itself? Type 'remove' to confirm: " CONFIRM_BUCKET +if [[ "${CONFIRM_BUCKET}" != "remove" ]]; then + echo "Bucket deletion skipped." + exit 0 +fi + +echo "Deleting bucket..." +aws s3api delete-bucket --bucket "${BUCKET}" --region "${REGION}" +echo "Bucket deleted: ${BUCKET}" + diff --git a/spec/jobs/download_envelopes_job_spec.rb b/spec/jobs/download_envelopes_job_spec.rb index d5c2250c..726c4bd1 100644 --- a/spec/jobs/download_envelopes_job_spec.rb +++ b/spec/jobs/download_envelopes_job_spec.rb @@ -22,7 +22,7 @@ .with(envelope_download:) .and_raise(error) - described_class.new.perform(envelope_download.id) + expect { described_class.new.perform(envelope_download.id) }.to raise_error(error) end end end diff --git a/terraform/environments/eks/k8s-manifests-staging/app-configmap.yaml b/terraform/environments/eks/k8s-manifests-staging/app-configmap.yaml index 73aff2aa..54db3476 100644 --- a/terraform/environments/eks/k8s-manifests-staging/app-configmap.yaml +++ b/terraform/environments/eks/k8s-manifests-staging/app-configmap.yaml @@ -15,5 +15,6 @@ data: IAM_CLIENT_ID: RegistryAPI IAM_URL: https://test-ce-kc-002.credentialengine.org/realms/CE-Test IAM_CLIENT: TestStagingRegistryAPI - AIRBRAKE_PROJECT_ID: '270205' - SIDEKIQ_CONCURRENCY: '10' \ No newline at end of file + AIRBRAKE_PROJECT_ID: "270205" + SIDEKIQ_CONCURRENCY: "10" + AUTHENTICATION_REQUIRED: "false" diff --git a/vendor/grape-middleware-logger b/vendor/grape-middleware-logger new file mode 160000 index 00000000..646dbfec --- /dev/null +++ b/vendor/grape-middleware-logger @@ -0,0 +1 @@ +Subproject commit 646dbfec4abbfa8605efa932195f3473f2f412ea