2 Commits
Author SHA1 Message Date
Dan MilneandClaude Opus 4.8 126e26685d Bump version to 0.17.1
Build and publish image / prepare (push) Has been cancelled
CI / scan_ruby (push) Has been cancelled
CI / scan_js (push) Has been cancelled
CI / scan_container (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / test (push) Has been cancelled
CI / system-test (push) Has been cancelled
Build and publish image / build (amd64, linux/amd64, ubuntu-latest) (push) Has been cancelled
Build and publish image / build (arm64, linux/arm64, ubuntu-24.04-arm) (push) Has been cancelled
Build and publish image / merge (push) Has been cancelled
Patch release: fix 500 when deleting an application with device codes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LARKs4cVsGrvKCkKZhPcy6
2026-07-19 20:39:59 +10:00
Dan MilneandClaude Opus 4.8 45a1203647 Fix 500 when deleting an application with device codes
Deleting an application left its oidc_device_codes orphaned, tripping the
FK constraint and raising SQLite3::ConstraintException. Unlike the other
child tables, oidc_device_codes had neither a dependent: :destroy
association nor an on_delete: :cascade FK, so any app that had started a
device-authorization flow could not be deleted (regardless of groups/users,
since a pending device code has a null user_id).

Add the missing has_many :oidc_device_codes, dependent: :destroy and a
migration to cascade at the DB level, mirroring application_user_claims.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LARKs4cVsGrvKCkKZhPcy6
2026-07-19 20:39:33 +10:00
5 changed files with 28 additions and 3 deletions
+1
View File
@@ -36,6 +36,7 @@ class Application < ApplicationRecord
has_many :allowed_groups, through: :application_groups, source: :group
has_many :application_user_claims, dependent: :destroy
has_many :oidc_authorization_codes, dependent: :destroy
has_many :oidc_device_codes, dependent: :destroy
has_many :oidc_access_tokens, dependent: :destroy
has_many :oidc_refresh_tokens, dependent: :destroy
has_many :oidc_user_consents, dependent: :destroy
+1 -1
View File
@@ -1,5 +1,5 @@
# frozen_string_literal: true
module Clinch
VERSION = "0.17.0"
VERSION = "0.17.1"
end
@@ -0,0 +1,14 @@
class AddCascadeToOidcDeviceCodesApplicationFk < ActiveRecord::Migration[8.1]
# Deleting an application left its oidc_device_codes orphaned, tripping this
# FK and 500ing the destroy. Mirror application_user_claims: cascade at the DB
# level so the delete is safe even if the model-layer cascade is bypassed.
def up
remove_foreign_key :oidc_device_codes, :applications
add_foreign_key :oidc_device_codes, :applications, on_delete: :cascade
end
def down
remove_foreign_key :oidc_device_codes, :applications
add_foreign_key :oidc_device_codes, :applications
end
end
Generated
+2 -2
View File
@@ -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_07_19_000005) do
ActiveRecord::Schema[8.1].define(version: 2026_07_19_000006) do
create_table "active_storage_attachments", force: :cascade do |t|
t.bigint "blob_id", null: false
t.datetime "created_at", null: false
@@ -329,7 +329,7 @@ ActiveRecord::Schema[8.1].define(version: 2026_07_19_000005) do
add_foreign_key "oidc_access_tokens", "users"
add_foreign_key "oidc_authorization_codes", "applications"
add_foreign_key "oidc_authorization_codes", "users"
add_foreign_key "oidc_device_codes", "applications"
add_foreign_key "oidc_device_codes", "applications", on_delete: :cascade
add_foreign_key "oidc_device_codes", "users"
add_foreign_key "oidc_refresh_tokens", "applications"
add_foreign_key "oidc_refresh_tokens", "oidc_access_tokens"
+10
View File
@@ -81,4 +81,14 @@ class ApplicationTest < ActiveSupport::TestCase
assert app.valid?, app.errors.full_messages.to_sentence
end
test "destroying an application with a pending device code succeeds (regression for FK 500)" do
app = applications(:kavita_app)
device_code = app.oidc_device_codes.create!
assert_difference("OidcDeviceCode.count", -1) do
assert_nothing_raised { app.destroy }
end
refute OidcDeviceCode.exists?(device_code.id)
end
end