From 5c92cf8050617ca1f8117f830e7b194ba9972570 Mon Sep 17 00:00:00 2001 From: croxyz Date: Wed, 22 Jul 2026 14:23:40 +0300 Subject: [PATCH 1/2] fix: protect demo account settings --- .../users/registrations_controller.rb | 16 ++++ app/models/user.rb | 6 ++ config/routes.rb | 4 +- spec/requests/users/registrations_spec.rb | 86 +++++++++++++++++++ 4 files changed, 111 insertions(+), 1 deletion(-) create mode 100644 app/controllers/users/registrations_controller.rb create mode 100644 spec/requests/users/registrations_spec.rb diff --git a/app/controllers/users/registrations_controller.rb b/app/controllers/users/registrations_controller.rb new file mode 100644 index 0000000..9ae8107 --- /dev/null +++ b/app/controllers/users/registrations_controller.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +module Users + class RegistrationsController < Devise::RegistrationsController + before_action :protect_demo_account!, only: %i[update destroy] + + private + + def protect_demo_account! + return unless current_user&.demo_account? + + redirect_to authenticated_root_path, + alert: "Demo account settings are protected." + end + end +end diff --git a/app/models/user.rb b/app/models/user.rb index 4ce0a9e..9ecf6ac 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1,6 +1,12 @@ # frozen_string_literal: true class User < ApplicationRecord + DEMO_EMAIL = "demo@example.com" + + def demo_account? + email == DEMO_EMAIL + end + devise :database_authenticatable, :registerable, :recoverable, :rememberable, :validatable diff --git a/config/routes.rb b/config/routes.rb index 7f4d388..ccabe30 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,7 +1,9 @@ # frozen_string_literal: true Rails.application.routes.draw do - devise_for :users + devise_for :users, controllers: { + registrations: "users/registrations" + } authenticated :user do root "dashboard#index", as: :authenticated_root diff --git a/spec/requests/users/registrations_spec.rb b/spec/requests/users/registrations_spec.rb new file mode 100644 index 0000000..4d4fbc6 --- /dev/null +++ b/spec/requests/users/registrations_spec.rb @@ -0,0 +1,86 @@ +# frozen_string_literal: true + +require "rails_helper" + +RSpec.describe "User registrations" do + describe "PATCH /users" do + context "when the current user is the demo user" do + let(:user) { create(:user, email: User::DEMO_EMAIL) } + + before { sign_in user } + + it "does not update the account" do + patch user_registration_path, params: { + user: { + email: "changed@example.com", + current_password: user.password + } + } + + expect(user.reload.email).to eq(User::DEMO_EMAIL) + end + + it "redirects with an alert" do + patch user_registration_path, params: { + user: { + email: "changed@example.com", + current_password: user.password + } + } + + expect(response).to redirect_to(authenticated_root_path) + expect(flash[:alert]).to eq("Demo account settings are protected.") + end + end + + context "when the current user is not the demo user" do + let(:user) { create(:user) } + + before { sign_in user } + + it "allows the account to be updated" do + patch user_registration_path, params: { + user: { + email: "changed@example.com", + current_password: user.password + } + } + + expect(user.reload.email).to eq("changed@example.com") + end + end + end + + describe "DELETE /users" do + context "when the current user is the demo user" do + let!(:user) { create(:user, email: User::DEMO_EMAIL) } + + before { sign_in user } + + it "does not delete the account" do + expect { + delete user_registration_path + }.not_to change(User, :count) + end + + it "redirects with an alert" do + delete user_registration_path + + expect(response).to redirect_to(authenticated_root_path) + expect(flash[:alert]).to eq("Demo account settings are protected.") + end + end + + context "when the current user is not the demo user" do + let!(:user) { create(:user) } + + before { sign_in user } + + it "deletes the account" do + expect { + delete user_registration_path + }.to change(User, :count).by(-1) + end + end + end +end From 6b1d18044df8df46e5096b4e19137c18e54943ef Mon Sep 17 00:00:00 2001 From: croxyz Date: Wed, 22 Jul 2026 14:41:36 +0300 Subject: [PATCH 2/2] chore: update vulnerable sanitizer dependencies and linter fixes --- Gemfile.lock | 6 +++--- .../users/registrations_controller.rb | 4 +++- config/locales/en.yml | 6 +++++- spec/requests/users/registrations_spec.rb | 16 ++++++++++------ 4 files changed, 21 insertions(+), 11 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 41e2abf..2012174 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -188,7 +188,7 @@ GEM language_server-protocol (3.17.0.5) lint_roller (1.1.0) logger (1.7.0) - loofah (2.25.1) + loofah (2.25.2) crass (~> 1.0.2) nokogiri (>= 1.12.0) mail (2.9.0) @@ -293,8 +293,8 @@ GEM activesupport (>= 5.0.0) minitest nokogiri (>= 1.6) - rails-html-sanitizer (1.7.0) - loofah (~> 2.25) + rails-html-sanitizer (1.7.1) + loofah (~> 2.25, >= 2.25.2) nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0) railties (8.1.3) actionpack (= 8.1.3) diff --git a/app/controllers/users/registrations_controller.rb b/app/controllers/users/registrations_controller.rb index 9ae8107..e41a4b0 100644 --- a/app/controllers/users/registrations_controller.rb +++ b/app/controllers/users/registrations_controller.rb @@ -2,7 +2,9 @@ module Users class RegistrationsController < Devise::RegistrationsController + # rubocop:disable Rails/LexicallyScopedActionFilter before_action :protect_demo_account!, only: %i[update destroy] + # rubocop:enable Rails/LexicallyScopedActionFilter private @@ -10,7 +12,7 @@ def protect_demo_account! return unless current_user&.demo_account? redirect_to authenticated_root_path, - alert: "Demo account settings are protected." + alert: t("users.registrations.demo_account_protected") end end end diff --git a/config/locales/en.yml b/config/locales/en.yml index 3a2948e..1d3d24f 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -52,4 +52,8 @@ en: success: "Project successfully deleted." index: new_project: "New Project" - title: "Projects" \ No newline at end of file + title: "Projects" + + users: + registrations: + demo_account_protected: "Demo account settings are protected." \ No newline at end of file diff --git a/spec/requests/users/registrations_spec.rb b/spec/requests/users/registrations_spec.rb index 4d4fbc6..cdfe2db 100644 --- a/spec/requests/users/registrations_spec.rb +++ b/spec/requests/users/registrations_spec.rb @@ -29,7 +29,9 @@ } expect(response).to redirect_to(authenticated_root_path) - expect(flash[:alert]).to eq("Demo account settings are protected.") + expect(flash[:alert]).to eq( + I18n.t("users.registrations.demo_account_protected") + ) end end @@ -58,16 +60,18 @@ before { sign_in user } it "does not delete the account" do - expect { + expect do delete user_registration_path - }.not_to change(User, :count) + end.not_to change(User, :count) end it "redirects with an alert" do delete user_registration_path expect(response).to redirect_to(authenticated_root_path) - expect(flash[:alert]).to eq("Demo account settings are protected.") + expect(flash[:alert]).to eq( + I18n.t("users.registrations.demo_account_protected") + ) end end @@ -77,9 +81,9 @@ before { sign_in user } it "deletes the account" do - expect { + expect do delete user_registration_path - }.to change(User, :count).by(-1) + end.to change(User, :count).by(-1) end end end