diff --git a/.ruby-version b/.ruby-version index 7921bd0..1454f6e 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -3.4.8 +4.0.1 diff --git a/Dockerfile b/Dockerfile index 7905b21..9a13ca4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,7 +8,7 @@ # For a containerized dev environment, see Dev Containers: https://guides.rubyonrails.org/getting_started_with_devcontainer.html # Make sure RUBY_VERSION matches the Ruby version in .ruby-version -ARG RUBY_VERSION=3.4.8 +ARG RUBY_VERSION=4.0.1 FROM docker.io/library/ruby:$RUBY_VERSION-slim AS base LABEL org.opencontainers.image.source=https://github.com/dkam/clinch diff --git a/Gemfile.lock b/Gemfile.lock index 2e3090c..ea4ce78 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -446,6 +446,7 @@ PLATFORMS arm-linux-gnu arm-linux-musl arm64-darwin-24 + arm64-darwin-25 x86_64-linux x86_64-linux-gnu x86_64-linux-musl diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 9078e54..eb41935 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -28,12 +28,10 @@ class ApplicationController < ActionController::Base uri = URI.parse(url) return url unless uri.query - # Parse query string into hash - params = CGI.parse(uri.query) + params = Rack::Utils.parse_query(uri.query) params.delete(param_name) - # Rebuild query string (empty string if no params left) - uri.query = params.any? ? URI.encode_www_form(params) : nil + uri.query = params.any? ? Rack::Utils.build_query(params) : nil uri.to_s rescue URI::InvalidURIError url diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index 44df291..76f2ea7 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -20,8 +20,8 @@ class SessionsController < ApplicationController begin uri = URI.parse(session[:return_to_after_authenticating]) if uri.query.present? - query_params = CGI.parse(uri.query) - @login_hint = query_params["login_hint"]&.first + query_params = Rack::Utils.parse_query(uri.query) + @login_hint = query_params["login_hint"] end rescue URI::InvalidURIError # Ignore parsing errors diff --git a/config/initializers/version.rb b/config/initializers/version.rb index ee1a2a7..4426632 100644 --- a/config/initializers/version.rb +++ b/config/initializers/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module Clinch - VERSION = "0.8.8" + VERSION = "0.9.0" end diff --git a/db/schema.rb b/db/schema.rb index 3e65766..7fcc033 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[8.1].define(version: 2026_01_05_000809) do +ActiveRecord::Schema[8.1].define(version: 2026_03_05_000001) do create_table "active_storage_attachments", force: :cascade do |t| t.bigint "blob_id", null: false t.datetime "created_at", null: false @@ -39,6 +39,24 @@ ActiveRecord::Schema[8.1].define(version: 2026_01_05_000809) do t.index ["blob_id", "variation_digest"], name: "index_active_storage_variant_records_uniqueness", unique: true end + create_table "api_keys", force: :cascade do |t| + t.integer "application_id", null: false + t.datetime "created_at", null: false + t.datetime "expires_at" + t.datetime "last_used_at" + t.string "name", null: false + t.datetime "revoked_at" + t.string "token_hmac", null: false + t.datetime "updated_at", null: false + t.integer "user_id", null: false + t.index ["application_id"], name: "index_api_keys_on_application_id" + t.index ["expires_at"], name: "index_api_keys_on_expires_at" + t.index ["revoked_at"], name: "index_api_keys_on_revoked_at" + t.index ["token_hmac"], name: "index_api_keys_on_token_hmac", unique: true + t.index ["user_id", "application_id"], name: "index_api_keys_on_user_id_and_application_id" + t.index ["user_id"], name: "index_api_keys_on_user_id" + end + create_table "application_groups", force: :cascade do |t| t.integer "application_id", null: false t.datetime "created_at", null: false @@ -249,6 +267,8 @@ ActiveRecord::Schema[8.1].define(version: 2026_01_05_000809) do add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id" add_foreign_key "active_storage_variant_records", "active_storage_blobs", column: "blob_id" + add_foreign_key "api_keys", "applications" + add_foreign_key "api_keys", "users" add_foreign_key "application_groups", "applications" add_foreign_key "application_groups", "groups" add_foreign_key "application_user_claims", "applications", on_delete: :cascade diff --git a/test/controllers/oidc_prompt_login_test.rb b/test/controllers/oidc_prompt_login_test.rb index 11b3ab6..a394318 100644 --- a/test/controllers/oidc_prompt_login_test.rb +++ b/test/controllers/oidc_prompt_login_test.rb @@ -46,7 +46,7 @@ class OidcPromptLoginTest < ActionDispatch::IntegrationTest assert_response :redirect first_redirect_url = response.location - first_code = CGI.parse(URI(first_redirect_url).query)["code"].first + first_code = Rack::Utils.parse_query(URI(first_redirect_url).query)["code"] # Exchange for tokens and extract auth_time post "/oauth/token", params: { @@ -90,7 +90,7 @@ class OidcPromptLoginTest < ActionDispatch::IntegrationTest # Should receive authorization code assert_response :redirect second_redirect_url = response.location - second_code = CGI.parse(URI(second_redirect_url).query)["code"].first + second_code = Rack::Utils.parse_query(URI(second_redirect_url).query)["code"] assert second_code.present?, "Should receive authorization code after re-authentication" @@ -134,11 +134,11 @@ class OidcPromptLoginTest < ActionDispatch::IntegrationTest # Parse the redirect URL uri = URI.parse(redirect_url) - query_params = uri.query ? CGI.parse(uri.query) : {} + query_params = uri.query ? Rack::Utils.parse_query(uri.query) : {} - assert_equal "login_required", query_params["error"]&.first, + assert_equal "login_required", query_params["error"], "Should return login_required error for prompt=none when not authenticated" - assert_equal "test-state", query_params["state"]&.first, + assert_equal "test-state", query_params["state"], "Should return state parameter" end @@ -165,7 +165,7 @@ class OidcPromptLoginTest < ActionDispatch::IntegrationTest assert_response :redirect first_redirect_url = response.location - first_code = CGI.parse(URI(first_redirect_url).query)["code"].first + first_code = Rack::Utils.parse_query(URI(first_redirect_url).query)["code"] # Exchange for tokens and extract auth_time from ID token post "/oauth/token", params: { @@ -209,7 +209,7 @@ class OidcPromptLoginTest < ActionDispatch::IntegrationTest # Should receive authorization code redirect assert_response :redirect second_redirect_url = response.location - second_code = CGI.parse(URI(second_redirect_url).query)["code"].first + second_code = Rack::Utils.parse_query(URI(second_redirect_url).query)["code"] assert second_code.present?, "Should receive authorization code after re-authentication"