Skip to content

Modernize to Rails 8 / Ruby 3.2, add tests, security fixes, and turnkey Docker#36

Open
kltm wants to merge 7 commits into
ncbo:masterfrom
berkeleybop:feature/rails8-modernization
Open

Modernize to Rails 8 / Ruby 3.2, add tests, security fixes, and turnkey Docker#36
kltm wants to merge 7 commits into
ncbo:masterfrom
berkeleybop:feature/rails8-modernization

Conversation

@kltm

@kltm kltm commented Jun 2, 2026

Copy link
Copy Markdown

Status: WIP / draft — for evaluation, not yet ready to merge.

This modernizes the License Server end-to-end and packages it so it can be stood
up with a single command for evaluation. It is an in-place upgrade, so the
diff stays reviewable.

What's in this PR (4 commits)

  1. Rails 5.1.7 → 8.0.3, Ruby 2.7.8 → 3.2.9ontologies_api_client → its
    GitHub tag v2.9.0; puma instead of Passenger; the Sprockets pipeline is
    retained (sprockets-rails); uglifierterser; turbolinks/coffee dropped.
  2. A Minitest test harness — the app previously had no working tests. Covers
    the license-key crypto contract, the License date / latest_licenses
    helpers, login + access control (anon / owner / admin), and the
    approve → key → email flow. The two BioPortal API calls are stubbed.
  3. Security fixes — CSRF re-enabled; explicit strong params replace
    params.permit! (this also closes a privilege escalation: a non-admin could
    POST approval_status=approved and have a license key issued); the
    user-scoped SQL is parameterized.
  4. Turnkey Dockerdocker compose up brings up app + MySQL + memcached,
    auto-migrates/seeds, and serves puma on :3000. Config is ENV-driven; no
    secrets are baked into the image.

The license-key format is unchanged and verified byte-stable across the
OpenSSL 1.1 → 3 jump, so appliances in the field keep validating keys minted by
the upgraded server. (Issue #34's signature rework is intentionally out of scope.)

Try it

BP_REST_URL=<your BioPortal REST API> API_KEY=<your API key> docker compose up --build
# then open http://localhost:3000 and log in with BioPortal credentials

Run the test suite (the production image is slim, so this re-adds the test gems):

docker compose run --rm -e RAILS_ENV=test app \
  bash -lc 'BUNDLE_WITHOUT= bundle install && BUNDLE_WITHOUT= bin/rails test'
# => 13 runs, 50 assertions, 0 failures

Still open (why this is a draft)

  • Runtime secrets for a real deploy: SECRET_KEY_BASE, the production RSA signing
    key (mount + PRIVATE_KEY_FILE), SMTP. The container generates ephemeral dev
    versions, with warnings, when these are absent.
  • Reconciling the existing Capistrano deploy with the Docker path, and how the
    whenever expiry-notification cron should run in a container.
  • A few deferred tests and a Dalli protocol deprecation warning.

Feedback on the overall approach is very welcome.

— Posted by Claude Code agent on behalf of @kltm.

kltm and others added 4 commits June 2, 2026 14:08
Upgrade rails 5.1.7 -> 8.0.3 and ruby 2.7.8 -> 3.2.9; move ontologies_api_client to its GitHub tag v2.9.0 (the modern, activesupport-8 line) and drop the faraday-v1 / dalli_store-era workarounds. Replace turbolinks/coffee/uglifier (uglifier -> terser; Sprockets retained via sprockets-rails), add config/storage.yml, use the positional enum syntax, set load_defaults 8.0, and regenerate schema.rb in the Rails 8 dump format. The license-key format is unchanged (verified byte-stable across OpenSSL 1.1 -> 3).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The app shipped without working tests. Add characterization + integration tests covering the license-key crypto contract, the License date / latest_licenses helpers, login + access control (anon/owner/admin), and the approve -> key -> email flow; the two BioPortal API calls are stubbed at the boundary. Fix the placeholder fixtures; load the per-env config and use an in-memory cache store in test so flash/session persist across redirects. (minitest is pinned to ~> 5.25 in the Gemfile -- Rails 8 is incompatible with minitest 6.)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Remove the blanket skip_before_action :verify_authenticity_token so CSRF is enforced again. Replace params[:license].permit! with explicit strong params; this also closes a privilege escalation where a non-admin could submit approval_status=approved and have a license key generated. Parameterize the user-scoped query in init_max_ids via Active Record rather than string-interpolated SQL.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Dockerfile (Ruby 3.2.9, puma, asset precompile) plus docker-compose.yml wiring app + mysql + memcached, an ENV-driven config template, and an entrypoint that renders config, prepares the database, and launches puma. 'docker compose up --build' brings up a working instance (auto migrate/seed). No secrets are baked into the image; .dockerignore excludes local config, keys, and tmp/.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@kltm

kltm commented Jun 2, 2026

Copy link
Copy Markdown
Author

@jvendetti I was wondering if I was on the right path here (developing on Ubuntu 24.04). I was wondering if you could take a quick look at this for me; running would look like:

BP_REST_URL="XYZ" API_KEY="ABC" docker compose up --build

@jvendetti

jvendetti commented Jun 3, 2026

Copy link
Copy Markdown
Member

Hi @kltm - sure, took a quick look and this seems like the right path. I was able to run the compose command you listed to bring up the license server and login with my BP account:

Screenshot 2026-06-02 at 5 51 40 PM

@kltm

kltm commented Jun 3, 2026

Copy link
Copy Markdown
Author

Great--thank you for taking a look at it. I'm going to poke at it a little more, but I think there may be enough there to me usable. I'll try and figure out a better way to test.

kltm and others added 2 commits June 15, 2026 14:12
GitHub Actions (.github/workflows/test.yml) runs the Minitest suite on push/PR against a MySQL service. Add a 'test' service to docker-compose (built with the test gem group, under the 'test' profile) so the suite runs with 'docker compose run --rm test'. Make the Dockerfile BUNDLE_WITHOUT an ARG so that image can include the test group.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
BioPortal accounts can have a nil firstName/lastName; LicensesController#new called nil.strip, raising a 500 on the Create License form. Coerce with to_s before stripping. Includes a regression test -- the first real bug caught by the new test suite / CI.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@kltm

kltm commented Jun 15, 2026

Copy link
Copy Markdown
Author

A note to clarify deployment, since this PR adds a Docker path alongside the existing Capistrano setup. Choosing between them is an NCBO decision — this PR deliberately doesn't settle it.

  • Existing Capistrano + Passenger deploy (config/deploy.rb, .github/workflows/deploy.yml) is left unchanged. The app code is upgraded to Rails 8 (which Passenger supports), but this PR has not exercised the Capistrano/Passenger path end-to-end — only the Docker path is verified here.
  • New Docker path (docker compose up) runs the app on puma and is the one validated in this PR.

Two things to be aware of if you adopt Docker for production:

  1. Runtime secrets/keys — provide SECRET_KEY_BASE, the real RSA signing key (mount it and set PRIVATE_KEY_FILE), and SMTP settings. Without these the container generates ephemeral dev values (with warnings) — fine for evaluation, not for production licensing.
  2. The nightly expiry-reminder cron — the whenever-generated crontab (rake batch:send_licence_to_expire_notifications) is installed by Capistrano on the app host. A plain container has no cron, so under Docker this job must be wired up separately (e.g. a small scheduler/sidecar invoking the rake task). It is not included here.

Happy to implement whichever direction you prefer.

— Posted by Claude Code agent on behalf of @kltm.

CLAUDE.md documents the app, its architecture, configuration, the Docker run/test workflow, and gotchas for future changes. docs/plans/ holds dated design notes and the record of the Rails 8 / Ruby 3.2 modernization (repo + issue survey, a behavioral characterization, approach/findings, and the upgrade plan).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@kltm kltm marked this pull request as ready for review June 15, 2026 22:15
@kltm

kltm commented Jun 15, 2026

Copy link
Copy Markdown
Author

@jvendetti I believe that this should cover the main target (update/refresh and dockerization), as well as #20 in order to test the tests added with the refresh.

@kltm kltm changed the title [WIP] Modernize to Rails 8 / Ruby 3.2, add tests, security fixes, and turnkey Docker Modernize to Rails 8 / Ruby 3.2, add tests, security fixes, and turnkey Docker Jun 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants