From 73d43bc1d00c6af20fa9b8f26d95347447805765 Mon Sep 17 00:00:00 2001 From: "Sure Admin (bot)" Date: Fri, 14 Aug 2026 03:59:02 +0200 Subject: [PATCH] Fix SSO JIT new family creator role (#3024) * Fix SSO JIT new family creator role * Preserve super admin SSO creator defaults * Update new family creator role test --- app/controllers/api/v1/auth_controller.rb | 3 + app/controllers/oidc_accounts_controller.rb | 5 +- app/models/user.rb | 4 +- .../api/v1/auth_controller_test.rb | 60 +++++++++++++++++++ .../oidc_accounts_controller_test.rb | 30 ++++++++++ test/models/user_test.rb | 8 ++- 6 files changed, 104 insertions(+), 6 deletions(-) diff --git a/app/controllers/api/v1/auth_controller.rb b/app/controllers/api/v1/auth_controller.rb index 6332f65f3..0b79d6873 100644 --- a/app/controllers/api/v1/auth_controller.rb +++ b/app/controllers/api/v1/auth_controller.rb @@ -209,6 +209,9 @@ module Api else user.family = Family.new + # New family creators must be able to administer their own family. + # Lower provider defaults are promoted to admin by role_for_new_family_creator, + # while intentional super_admin defaults remain supported. provider_config = Rails.configuration.x.auth.sso_providers&.find { |p| p[:name] == cached[:provider] } provider_default_role = provider_config&.dig(:settings, :default_role) user.role = User.role_for_new_family_creator(fallback_role: provider_default_role || :admin) diff --git a/app/controllers/oidc_accounts_controller.rb b/app/controllers/oidc_accounts_controller.rb index 6e26e5e3a..3d26432fe 100644 --- a/app/controllers/oidc_accounts_controller.rb +++ b/app/controllers/oidc_accounts_controller.rb @@ -129,8 +129,9 @@ class OidcAccountsController < ApplicationController # Create new family for this user @user.family = Family.new - # Use provider-configured default role, or fall back to admin for family creators - # First user of an instance always becomes super_admin regardless of provider config + # New family creators must be able to administer their own family. + # Lower provider defaults are promoted to admin by role_for_new_family_creator, + # while intentional super_admin defaults remain supported. provider_config = Rails.configuration.x.auth.sso_providers&.find { |p| p[:name] == @pending_auth["provider"] } provider_default_role = provider_config&.dig(:settings, :default_role) @user.role = User.role_for_new_family_creator(fallback_role: provider_default_role || :admin) diff --git a/app/models/user.rb b/app/models/user.rb index e068a3a4c..257a77e20 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -74,8 +74,10 @@ class User < ApplicationRecord # Returns the appropriate role for a new user creating a family. # The very first user of an instance becomes super_admin; subsequent users - # get the specified fallback role (typically :admin for family creators). + # get the specified admin-capable fallback role. def self.role_for_new_family_creator(fallback_role: :admin) + fallback_role = fallback_role.to_s.in?(%w[admin super_admin]) ? fallback_role : :admin + User.exists? ? fallback_role : :super_admin end diff --git a/test/controllers/api/v1/auth_controller_test.rb b/test/controllers/api/v1/auth_controller_test.rb index a091c4eee..4c39adfed 100644 --- a/test/controllers/api/v1/auth_controller_test.rb +++ b/test/controllers/api/v1/auth_controller_test.rb @@ -705,6 +705,66 @@ class Api::V1::AuthControllerTest < ActionDispatch::IntegrationTest assert_nil Rails.cache.read("mobile_sso_link:#{linking_code}") end + test "sso_create_account makes new family creator admin even when provider default role is member" do + Rails.configuration.x.auth.stubs(:sso_providers).returns([ + { name: "google_oauth2", settings: { default_role: "member" } } + ]) + linking_code = SecureRandom.urlsafe_base64(32) + Rails.cache.write("mobile_sso_link:#{linking_code}", { + provider: "google_oauth2", + uid: "google-uid-member-default", + email: "member-default@example.com", + first_name: "Member", + last_name: "Default", + name: "Member Default", + device_info: @device_info.stringify_keys, + allow_account_creation: true + }, expires_in: 10.minutes) + + assert_difference([ "User.count", "OidcIdentity.count", "Family.count" ], 1) do + post "/api/v1/auth/sso_create_account", params: { + linking_code: linking_code, + first_name: "Member", + last_name: "Default" + } + end + + assert_response :success + new_user = User.find_by!(email: "member-default@example.com") + assert_equal "admin", new_user.role + assert new_user.admin? + end + + test "sso_create_account preserves super admin provider default for new family creator" do + Rails.configuration.x.auth.stubs(:sso_providers).returns([ + { name: "google_oauth2", settings: { default_role: "super_admin" } } + ]) + linking_code = SecureRandom.urlsafe_base64(32) + Rails.cache.write("mobile_sso_link:#{linking_code}", { + provider: "google_oauth2", + uid: "google-uid-super-admin-default", + email: "super-admin-default@example.com", + first_name: "Super", + last_name: "Default", + name: "Super Default", + device_info: @device_info.stringify_keys, + allow_account_creation: true + }, expires_in: 10.minutes) + + assert_difference([ "User.count", "OidcIdentity.count", "Family.count" ], 1) do + post "/api/v1/auth/sso_create_account", params: { + linking_code: linking_code, + first_name: "Super", + last_name: "Default" + } + end + + assert_response :success + new_user = User.find_by!(email: "super-admin-default@example.com") + assert_equal "super_admin", new_user.role + assert new_user.admin? + end + test "should reject SSO create account when not allowed" do linking_code = SecureRandom.urlsafe_base64(32) Rails.cache.write("mobile_sso_link:#{linking_code}", { diff --git a/test/controllers/oidc_accounts_controller_test.rb b/test/controllers/oidc_accounts_controller_test.rb index 9663bc62e..94873521f 100644 --- a/test/controllers/oidc_accounts_controller_test.rb +++ b/test/controllers/oidc_accounts_controller_test.rb @@ -187,6 +187,36 @@ class OidcAccountsControllerTest < ActionController::TestCase assert_equal new_user_auth["uid"], oidc_identity.uid end + test "create_user makes new family creator admin even when provider default role is member" do + session[:pending_oidc_auth] = new_user_auth + Rails.configuration.x.auth.stubs(:sso_providers).returns([ + { name: new_user_auth["provider"], settings: { default_role: "member" } } + ]) + + assert_difference [ "User.count", "OidcIdentity.count", "Family.count" ], 1 do + post :create_user + end + + new_user = User.find_by!(email: new_user_auth["email"]) + assert_equal "admin", new_user.role + assert new_user.admin? + end + + test "create_user preserves super admin provider default for new family creator" do + session[:pending_oidc_auth] = new_user_auth + Rails.configuration.x.auth.stubs(:sso_providers).returns([ + { name: new_user_auth["provider"], settings: { default_role: "super_admin" } } + ]) + + assert_difference [ "User.count", "OidcIdentity.count", "Family.count" ], 1 do + post :create_user + end + + new_user = User.find_by!(email: new_user_auth["email"]) + assert_equal "super_admin", new_user.role + assert new_user.admin? + end + test "create_user uses form params for name when provided" do session[:pending_oidc_auth] = new_user_auth diff --git a/test/models/user_test.rb b/test/models/user_test.rb index 2ab21ffec..b2bbfb796 100644 --- a/test/models/user_test.rb +++ b/test/models/user_test.rb @@ -690,13 +690,15 @@ class UserTest < ActiveSupport::TestCase assert_equal :super_admin, User.role_for_new_family_creator end - test "role_for_new_family_creator returns fallback role when users exist" do + test "role_for_new_family_creator returns admin-capable fallback role when users exist" do # Users exist from fixtures assert User.exists? assert_equal :admin, User.role_for_new_family_creator - assert_equal :member, User.role_for_new_family_creator(fallback_role: :member) - assert_equal "custom_role", User.role_for_new_family_creator(fallback_role: "custom_role") + assert_equal :admin, User.role_for_new_family_creator(fallback_role: :member) + assert_equal :admin, User.role_for_new_family_creator(fallback_role: :guest) + assert_equal :admin, User.role_for_new_family_creator(fallback_role: "custom_role") + assert_equal "super_admin", User.role_for_new_family_creator(fallback_role: "super_admin") end # Preview features preference tests