diff --git a/CHANGELOG.md b/CHANGELOG.md index 606f62e59..649e02f31 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Environment variable `VERSION` to change the displayed version in the footer ([#3300], [#3302]) - System-wide default welcome message ([#3301]) +- Privacy setting to disable finding users by partial matches of their name or email address ([#2264], [#3316]) ### Fixed @@ -764,6 +765,7 @@ You can find the changelog for older versions there [here](https://github.com/TH [#2165]: https://github.com/THM-Health/PILOS/pull/2165 [#2222]: https://github.com/THM-Health/PILOS/pull/2222 [#2223]: https://github.com/THM-Health/PILOS/pull/2223 +[#2264]: https://github.com/THM-Health/PILOS/issues/2264 [#2265]: https://github.com/THM-Health/PILOS/issues/2265 [#2279]: https://github.com/THM-Health/PILOS/pull/2279 [#2281]: https://github.com/THM-Health/PILOS/pull/2281 @@ -873,6 +875,7 @@ You can find the changelog for older versions there [here](https://github.com/TH [#3302]: https://github.com/THM-Health/PILOS/pull/3302 [#3314]: https://github.com/THM-Health/PILOS/issues/3314 [#3315]: https://github.com/THM-Health/PILOS/pull/3315 +[#3316]: https://github.com/THM-Health/PILOS/pull/3316 [unreleased]: https://github.com/THM-Health/PILOS/compare/v4.17.0...develop [v3.0.0]: https://github.com/THM-Health/PILOS/releases/tag/v3.0.0 [v3.0.1]: https://github.com/THM-Health/PILOS/releases/tag/v3.0.1 diff --git a/app/Http/Controllers/api/v1/SettingsController.php b/app/Http/Controllers/api/v1/SettingsController.php index bff8927df..c33a375e2 100644 --- a/app/Http/Controllers/api/v1/SettingsController.php +++ b/app/Http/Controllers/api/v1/SettingsController.php @@ -189,6 +189,7 @@ public function update(UpdateSettingsRequest $request) $roomSettings->hide_owner_from_guests = $request->boolean('room_hide_owner_from_guests'); $userSettings->password_change_allowed = $request->boolean('user_password_change_allowed'); + $userSettings->search_by_name = $request->boolean('user_search_by_name'); $bannerSettings->enabled = $request->boolean('banner_enabled'); $bannerSettings->title = $request->input('banner_title'); diff --git a/app/Http/Controllers/api/v1/UserController.php b/app/Http/Controllers/api/v1/UserController.php index c25ad099f..5380fff57 100644 --- a/app/Http/Controllers/api/v1/UserController.php +++ b/app/Http/Controllers/api/v1/UserController.php @@ -19,6 +19,7 @@ use App\Services\AuthenticationService; use App\Services\EmailVerification\EmailVerificationService; use App\Settings\GeneralSettings; +use App\Settings\UserSettings; use Carbon\Carbon; use Exception; use Illuminate\Auth\Access\AuthorizationException; @@ -31,6 +32,7 @@ use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Password; use Illuminate\Support\Facades\Storage; +use Illuminate\Support\Str; class UserController extends Controller { @@ -41,18 +43,28 @@ public function __construct() } /** - * Search for users in the whole database, based on first name and last name + * Search for users in the whole database + * Depending on the search_by_name setting it will either search for partial matches of firstname, lastname and email + * or only for an exact email match * * @param UserSearchRequest $request query parameter with search query * @return AnonymousResourceCollection */ public function search(UserSearchRequest $request) { + $userSettings = app(UserSettings::class); + if (! $request->filled('query')) { abort(204, 'Too many results'); } - $query = User::withNameOrEmail($request->query('query')); + if ($userSettings->search_by_name) { + $query = User::withNameOrEmail($request->query('query')); + } else { + // Escape wildcard characters and escape backslashes to prevent escaping our added escape backslash + $email = Str::replace(['\\', '%', '_'], ['\\\\', '\%', '\_'], $request->query('query')); + $query = User::whereLike('email', $email); + } if ($query->count() > config('bigbluebutton.user_search_limit')) { abort(204, 'Too many results'); diff --git a/app/Http/Requests/UpdateSettingsRequest.php b/app/Http/Requests/UpdateSettingsRequest.php index 9ae7803d0..6091d9725 100644 --- a/app/Http/Requests/UpdateSettingsRequest.php +++ b/app/Http/Requests/UpdateSettingsRequest.php @@ -75,6 +75,7 @@ public function rules() 'room_hide_owner_from_guests' => ['required', 'boolean'], 'user_password_change_allowed' => ['required', 'boolean'], + 'user_search_by_name' => ['required', 'boolean'], 'recording_server_usage_enabled' => ['required', 'boolean'], 'recording_server_usage_retention_period' => ['required', 'numeric', Rule::enum(TimePeriod::class)], diff --git a/app/Http/Resources/ConfigResource.php b/app/Http/Resources/ConfigResource.php index dd529a9c1..c9236037c 100644 --- a/app/Http/Resources/ConfigResource.php +++ b/app/Http/Resources/ConfigResource.php @@ -69,6 +69,7 @@ public function toArray($request) ], 'user' => [ 'password_change_allowed' => $userSettings->password_change_allowed, + 'search_by_name' => $userSettings->search_by_name, ], 'bbb' => [ 'file_mimes' => config('bigbluebutton.allowed_file_mimes'), diff --git a/app/Http/Resources/SettingsResource.php b/app/Http/Resources/SettingsResource.php index 52f1b5880..5935cf606 100644 --- a/app/Http/Resources/SettingsResource.php +++ b/app/Http/Resources/SettingsResource.php @@ -70,6 +70,7 @@ public function toArray($request) 'room_file_terms_of_use' => $roomSettings->file_terms_of_use, 'room_hide_owner_from_guests' => $roomSettings->hide_owner_from_guests, 'user_password_change_allowed' => $userSettings->password_change_allowed, + 'user_search_by_name' => $userSettings->search_by_name, 'recording_server_usage_enabled' => $recordingSettings->server_usage_enabled, 'recording_server_usage_retention_period' => $recordingSettings->server_usage_retention_period, 'recording_meeting_usage_enabled' => $recordingSettings->meeting_usage_enabled, diff --git a/app/Settings/UserSettings.php b/app/Settings/UserSettings.php index 4a5c2313e..8ebca456e 100644 --- a/app/Settings/UserSettings.php +++ b/app/Settings/UserSettings.php @@ -10,6 +10,8 @@ class UserSettings extends Settings { public bool $password_change_allowed; + public bool $search_by_name; + public static function group(): string { return 'user'; diff --git a/database/settings/2026_07_15_130614_add_user_search_by_name.php b/database/settings/2026_07_15_130614_add_user_search_by_name.php new file mode 100644 index 000000000..d7be415db --- /dev/null +++ b/database/settings/2026_07_15_130614_add_user_search_by_name.php @@ -0,0 +1,18 @@ +migrator->add('user.search_by_name', true); + } + + public function down(): void + { + $this->migrator->delete('user.search_by_name'); + } +}; diff --git a/lang/en/admin.php b/lang/en/admin.php index 776656a68..adbadfb7c 100644 --- a/lang/en/admin.php +++ b/lang/en/admin.php @@ -426,6 +426,10 @@ ], 'two_weeks' => '2 Weeks (14 Days)', 'two_years' => '2 Years (730 Days)', + 'user_search_by_name' => [ + 'description' => 'Allows users to find others by partial matches of their name or email address. When disabled, users can only be found by entering their exact email address.', + 'title' => 'Allow searching users by name', + ], 'user_settings' => 'User settings', ], 'streaming' => [ diff --git a/lang/en/rooms.php b/lang/en/rooms.php index 238a8a87f..2da3f25fd 100644 --- a/lang/en/rooms.php +++ b/lang/en/rooms.php @@ -222,8 +222,11 @@ 'add' => [ 'add' => 'Add', 'no_options' => 'Enter the name or email address of a user.', + 'no_options_email_only' => 'Enter the email address of a user.', 'no_result' => 'Oops! No user was found for this query.', 'placeholder' => 'Name or email', + 'placeholder_email_only' => 'Email', + 'searching' => 'Searching...', 'select_role' => 'Please select a role', 'select_user' => 'Please select the user you would like to add', 'too_many_results' => 'Too many users were found. Please enter a more precise search term.', diff --git a/resources/js/components/RoomTabMembersAddSingleModal.vue b/resources/js/components/RoomTabMembersAddSingleModal.vue index 038740d52..ca0deb3ae 100644 --- a/resources/js/components/RoomTabMembersAddSingleModal.vue +++ b/resources/js/components/RoomTabMembersAddSingleModal.vue @@ -39,51 +39,13 @@
- - - - - - + />
@@ -144,7 +106,6 @@ diff --git a/resources/js/components/UserSearch.vue b/resources/js/components/UserSearch.vue new file mode 100644 index 000000000..fffdad158 --- /dev/null +++ b/resources/js/components/UserSearch.vue @@ -0,0 +1,153 @@ + + diff --git a/resources/js/views/AdminSettings.vue b/resources/js/views/AdminSettings.vue index 96cc6c588..ee779f2e9 100644 --- a/resources/js/views/AdminSettings.vue +++ b/resources/js/views/AdminSettings.vue @@ -1151,6 +1151,36 @@ /> + +
+ + {{ $t("admin.settings.user_search_by_name.title") }} + +
+
+ + +
+ {{ + $t("admin.settings.user_search_by_name.description") + }} + +
+
roomSettings->save(); $this->userSettings->password_change_allowed = true; + $this->userSettings->search_by_name = true; $this->userSettings->save(); $this->recordingSettings->server_usage_enabled = true; @@ -164,6 +166,7 @@ public function test_view_settings() 'room_hide_owner_from_guests' => false, 'user_password_change_allowed' => true, + 'user_search_by_name' => true, 'recording_server_usage_enabled' => true, 'recording_server_usage_retention_period' => 7, @@ -228,6 +231,7 @@ public function test_update_settings() 'room_hide_owner_from_guests' => true, 'user_password_change_allowed' => 1, + 'user_search_by_name' => 1, 'recording_server_usage_enabled' => 0, 'recording_server_usage_retention_period' => 7, @@ -292,6 +296,7 @@ public function test_update_settings() 'room_hide_owner_from_guests' => true, 'user_password_change_allowed' => 1, + 'user_search_by_name' => 1, 'recording_server_usage_enabled' => 0, 'recording_server_usage_retention_period' => 7, @@ -309,12 +314,14 @@ public function test_update_settings() $this->assertEquals('http://localhost', app(GeneralSettings::class)->legal_notice_url); $this->assertEquals('http://localhost', app(GeneralSettings::class)->privacy_policy_url); $this->assertEquals('http://localhost', app(GeneralSettings::class)->accessibility_statement_url); + $this->assertTrue(app(UserSettings::class)->search_by_name); $payload['general_help_url'] = ''; $payload['general_legal_notice_url'] = ''; $payload['general_privacy_policy_url'] = ''; $payload['general_accessibility_statement_url'] = ''; $payload['room_file_terms_of_use'] = ''; + $payload['user_search_by_name'] = 0; $payload['bbb_default_welcome_message'] = ''; $this->putJson(route('api.v1.settings.update'), $payload) @@ -325,6 +332,7 @@ public function test_update_settings() $this->assertNull(app(GeneralSettings::class)->privacy_policy_url); $this->assertNull(app(GeneralSettings::class)->accessibility_statement_url); $this->assertNull(app(RoomSettings::class)->file_terms_of_use); + $this->assertFalse(app(UserSettings::class)->search_by_name); $this->assertNull(app(BigBlueButtonSettings::class)->default_welcome_message); } @@ -373,6 +381,7 @@ public function test_update_with_valid_inputs_image_file() 'room_hide_owner_from_guests' => false, 'user_password_change_allowed' => 1, + 'user_search_by_name' => 1, 'recording_server_usage_enabled' => 0, 'recording_server_usage_retention_period' => 7, @@ -452,6 +461,7 @@ public function test_update_with_valid_inputs_image_file_and_url() 'room_hide_owner_from_guests' => false, 'user_password_change_allowed' => 1, + 'user_search_by_name' => 1, 'recording_server_usage_enabled' => 0, 'recording_server_usage_retention_period' => 7, @@ -535,6 +545,7 @@ public function test_update_with_invalid_inputs() 'room_hide_owner_from_guests' => 'notbool', 'user_password_change_allowed' => 'foo', + 'user_search_by_name' => 'foo', 'recording_server_usage_enabled' => 'foo', 'recording_server_usage_retention_period' => 'notnumber', @@ -589,6 +600,7 @@ public function test_update_with_invalid_inputs() 'room_hide_owner_from_guests', 'user_password_change_allowed', + 'user_search_by_name', 'recording_server_usage_enabled', 'recording_server_usage_retention_period', @@ -673,6 +685,7 @@ public function test_update_theme_custom_css() 'room_hide_owner_from_guests' => false, 'user_password_change_allowed' => 1, + 'user_search_by_name' => 1, 'recording_server_usage_enabled' => 0, 'recording_server_usage_retention_period' => 7, @@ -780,6 +793,7 @@ public function test_update_min_max() 'room_hide_owner_from_guests' => false, 'user_password_change_allowed' => 1, + 'user_search_by_name' => 1, 'recording_server_usage_enabled' => 0, 'recording_server_usage_retention_period' => 1, @@ -835,6 +849,7 @@ public function test_update_min_max() 'room_hide_owner_from_guests' => false, 'user_password_change_allowed' => 1, + 'user_search_by_name' => 1, 'recording_server_usage_enabled' => 0, 'recording_server_usage_retention_period' => 366, @@ -917,6 +932,7 @@ public function test_update_default_presentation() 'room_hide_owner_from_guests' => false, 'user_password_change_allowed' => 1, + 'user_search_by_name' => 1, 'recording_server_usage_enabled' => 0, 'recording_server_usage_retention_period' => 7, @@ -1029,6 +1045,7 @@ public function test_update_bbb_style() 'room_hide_owner_from_guests' => false, 'user_password_change_allowed' => 1, + 'user_search_by_name' => 1, 'recording_server_usage_enabled' => 0, 'recording_server_usage_retention_period' => 7, @@ -1126,6 +1143,7 @@ public function test_update_bbb_logo() 'room_hide_owner_from_guests' => false, 'user_password_change_allowed' => 1, + 'user_search_by_name' => 1, 'recording_server_usage_enabled' => 0, 'recording_server_usage_retention_period' => 7, @@ -1210,6 +1228,7 @@ public function test_update_bbb_dark_logo() 'room_hide_owner_from_guests' => false, 'user_password_change_allowed' => 1, + 'user_search_by_name' => 1, 'recording_server_usage_enabled' => 0, 'recording_server_usage_retention_period' => 7, @@ -1300,6 +1319,7 @@ public function test_virus_files() 'room_hide_owner_from_guests' => false, 'user_password_change_allowed' => 1, + 'user_search_by_name' => 1, 'recording_server_usage_enabled' => 0, 'recording_server_usage_retention_period' => 7, diff --git a/tests/Backend/Feature/api/v1/UserTest.php b/tests/Backend/Feature/api/v1/UserTest.php index 12e4ad94a..40c29d11d 100644 --- a/tests/Backend/Feature/api/v1/UserTest.php +++ b/tests/Backend/Feature/api/v1/UserTest.php @@ -14,6 +14,7 @@ use App\Notifications\PasswordReset; use App\Notifications\UserWelcome; use App\Notifications\VerifyEmail; +use App\Settings\UserSettings; use Carbon\Carbon; use Database\Seeders\RolesAndPermissionsSeeder; use Illuminate\Foundation\Testing\RefreshDatabase; @@ -198,6 +199,10 @@ public function test_search() $searchLimit = 5; config(['bigbluebutton.user_search_limit' => $searchLimit]); + $userSettings = app(UserSettings::class); + $userSettings->search_by_name = true; + $userSettings->save(); + $users = []; $users[] = User::factory()->create(['firstname' => 'Gregory', 'lastname' => 'Dumas', 'email' => 'gregory.dumas@example.com']); $users[] = User::factory()->create(['firstname' => 'Mable', 'lastname' => 'Torres', 'email' => 'mable.torres@example.com']); @@ -254,6 +259,69 @@ public function test_search() ->assertJsonCount(1, 'data'); } + public function test_search_by_name_disabled() + { + $searchLimit = 5; + config(['bigbluebutton.user_search_limit' => $searchLimit]); + + $userSettings = app(UserSettings::class); + $userSettings->search_by_name = false; + $userSettings->save(); + + $users = []; + $users[] = User::factory()->create(['firstname' => 'Gregory', 'lastname' => 'Dumas', 'email' => 'gregory.dumas@example.com']); + + // Check with exact lastname match (search for names should not be possible) + $this->actingAs($users[0])->getJson(route('api.v1.users.search').'?query=Dumas') + ->assertSuccessful() + ->assertJsonCount(0, 'data'); + + // Check with exact firstname match (search for names should not be possible) + $this->actingAs($users[0])->getJson(route('api.v1.users.search').'?query=Gregory') + ->assertSuccessful() + ->assertJsonCount(0, 'data'); + + // Check fragments of email (query must match exactly, fragments are disabled) + $this->actingAs($users[0])->getJson(route('api.v1.users.search').'?query=example.com') + ->assertSuccessful() + ->assertJsonCount(0, 'data'); + + // Check exact email (case-insensitive) + $this->actingAs($users[0])->getJson(route('api.v1.users.search').'?query=Gregory.Dumas@example.com') + ->assertSuccessful() + ->assertJsonPath('data.0.firstname', $users[0]->firstname) + ->assertJsonCount(1, 'data'); + + // Check wildcard characters are treated literally and do not perform pattern matching + $this->actingAs($users[0])->getJson(route('api.v1.users.search').'?query=gregory%') + ->assertSuccessful() + ->assertJsonCount(0, 'data'); + $this->actingAs($users[0])->getJson(route('api.v1.users.search').'?query=gregory_dumas@example.com') + ->assertSuccessful() + ->assertJsonCount(0, 'data'); + + // Check if adding a backslash cannot bypass the escaping + $this->actingAs($users[0])->getJson(route('api.v1.users.search').'?query=gregory\%') + ->assertSuccessful() + ->assertJsonCount(0, 'data'); + + // Add \ to email and test if it cannot bypass the escaping + $users[0]->email = 'gregory\.dumas@example.com'; + $users[0]->save(); + + $this->actingAs($users[0])->getJson(route('api.v1.users.search').'?query=gregory\%') + ->assertSuccessful() + ->assertJsonCount(0, 'data'); + + // Check if email with all chars can still be found + $users[0]->email = 'gregory\_%.dumas@example.com'; + $users[0]->save(); + + $this->actingAs($users[0])->getJson(route('api.v1.users.search').'?query=gregory\_%.dumas@example.com') + ->assertSuccessful() + ->assertJsonCount(1, 'data'); + } + public function test_create() { $user = User::factory()->create(); diff --git a/tests/Frontend/e2e/AdminSettingsEdit.cy.js b/tests/Frontend/e2e/AdminSettingsEdit.cy.js index 3c22a5f2d..ec3e689a1 100644 --- a/tests/Frontend/e2e/AdminSettingsEdit.cy.js +++ b/tests/Frontend/e2e/AdminSettingsEdit.cy.js @@ -2279,9 +2279,17 @@ describe("Admin settings with edit permission", function () { cy.get("#password-change-allowed").should("be.checked").click(); }); + cy.get('[data-test="user-search-by-name-field"]') + .should("be.visible") + .and("include.text", "admin.settings.user_search_by_name.title") + .within(() => { + cy.get("#user-search-by-name").should("be.checked").click(); + }); + // Save changes cy.fixture("settings.json").then((settings) => { settings.data.user_password_change_allowed = false; + settings.data.user_search_by_name = false; const saveChangesRequest = interceptIndefinitely( "POST", @@ -2313,6 +2321,7 @@ describe("Admin settings with edit permission", function () { ); expect(formData.get("user_password_change_allowed")).to.equal("0"); + expect(formData.get("user_search_by_name")).to.equal("0"); }); // Check that config is loaded @@ -2324,6 +2333,7 @@ describe("Admin settings with edit permission", function () { // Check that settings are shown correctly cy.get("#password-change-allowed").should("not.be.checked"); + cy.get("#user-search-by-name").should("not.be.checked"); }); it("change recording and statistics settings", function () { diff --git a/tests/Frontend/e2e/AdminSettingsView.cy.js b/tests/Frontend/e2e/AdminSettingsView.cy.js index 32274e4e2..a5525e57a 100644 --- a/tests/Frontend/e2e/AdminSettingsView.cy.js +++ b/tests/Frontend/e2e/AdminSettingsView.cy.js @@ -727,6 +727,48 @@ describe("Admin settings with edit permission", function () { .should("be.checked") .and("be.disabled"); }); + + cy.get('[data-test="user-search-by-name-field"]') + .should("be.visible") + .and("include.text", "admin.settings.user_search_by_name.title") + .and("include.text", "admin.settings.user_search_by_name.description") + .within(() => { + cy.get("#user-search-by-name").should("be.checked").and("be.disabled"); + }); + + // Reload with different settings + cy.fixture("settings.json").then((settings) => { + settings.data.user_password_change_allowed = false; + settings.data.user_search_by_name = false; + + cy.intercept("GET", "api/v1/settings", { + statusCode: 200, + body: settings, + }).as("settingsRequest"); + }); + + cy.reload(); + + cy.wait("@settingsRequest"); + + cy.get('[data-test="password-change-allowed-field"]') + .should("be.visible") + .and("include.text", "admin.settings.password_change_allowed") + .within(() => { + cy.get("#password-change-allowed") + .should("not.be.checked") + .and("be.disabled"); + }); + + cy.get('[data-test="user-search-by-name-field"]') + .should("be.visible") + .and("include.text", "admin.settings.user_search_by_name.title") + .and("include.text", "admin.settings.user_search_by_name.description") + .within(() => { + cy.get("#user-search-by-name") + .should("not.be.checked") + .and("be.disabled"); + }); }); it("check recording and statistics settings with only view permission", function () { diff --git a/tests/Frontend/e2e/RoomsViewMembersMemberActions.cy.js b/tests/Frontend/e2e/RoomsViewMembersMemberActions.cy.js index 0971e4743..31f3a75e9 100644 --- a/tests/Frontend/e2e/RoomsViewMembersMemberActions.cy.js +++ b/tests/Frontend/e2e/RoomsViewMembersMemberActions.cy.js @@ -94,22 +94,6 @@ describe("Rooms view members member actions", function () { cy.get('[data-test="select-user-dropdown"]').find("input").type("aura"); - cy.wait("@userSearchRequest").then((interception) => { - expect(interception.request.query).to.contain({ - query: "La", - }); - }); - cy.wait("@userSearchRequest").then((interception) => { - expect(interception.request.query).to.contain({ - query: "Lau", - }); - }); - cy.wait("@userSearchRequest").then((interception) => { - expect(interception.request.query).to.contain({ - query: "Laur", - }); - }); - cy.wait("@userSearchRequest").then((interception) => { expect(interception.request.query).to.contain({ query: "Laura", @@ -241,6 +225,45 @@ describe("Rooms view members member actions", function () { .should("include.text", "rooms.roles.moderator"); }); + it("add new member with search by name disabled", function () { + cy.fixture("config.json").then((config) => { + config.data.user.search_by_name = false; + + cy.intercept("GET", "api/v1/config", { + statusCode: 200, + body: config, + }); + }); + + cy.visit("/rooms/abc-def-123#tab=members"); + + cy.wait("@roomMembersRequest"); + + cy.get('[data-test="room-members-add-button"]').click(); + + // Click on add single user option + cy.get("#overlay_menu_0") + .should("have.text", "rooms.members.add_single_user") + .click(); + + cy.get('[data-test="room-members-add-single-dialog"]').should("be.visible"); + + // Check prompt + cy.get('[data-test="select-user-dropdown"]').should( + "include.text", + "rooms.members.modals.add.no_options_email_only", + ); + + // Check placeholder + cy.get('[data-test="select-user-dropdown"]') + .find("input") + .should( + "have.attr", + "placeholder", + "rooms.members.modals.add.placeholder_email_only", + ); + }); + it("add new member errors", function () { cy.visit("/rooms/abc-def-123#tab=members"); cy.wait("@roomRequest"); diff --git a/tests/Frontend/e2e/RoomsViewSettings.cy.js b/tests/Frontend/e2e/RoomsViewSettings.cy.js index 9cf9991a3..ed82c41ef 100644 --- a/tests/Frontend/e2e/RoomsViewSettings.cy.js +++ b/tests/Frontend/e2e/RoomsViewSettings.cy.js @@ -1926,7 +1926,11 @@ describe("Rooms view settings", function () { // Check placeholder and type in input cy.get('[data-test="new-owner-dropdown"]') .find("input") - .should("have.attr", "placeholder", "app.user_name") + .should( + "have.attr", + "placeholder", + "rooms.members.modals.add.placeholder", + ) .click(); cy.get('[data-test="new-owner-dropdown"]').find("input").type("L"); @@ -1980,22 +1984,6 @@ describe("Rooms view settings", function () { cy.get('[data-test="new-owner-dropdown"]').find("input").type("aura"); - cy.wait("@userSearchRequest").then((interception) => { - expect(interception.request.query).to.contain({ - query: "La", - }); - }); - cy.wait("@userSearchRequest").then((interception) => { - expect(interception.request.query).to.contain({ - query: "Lau", - }); - }); - cy.wait("@userSearchRequest").then((interception) => { - expect(interception.request.query).to.contain({ - query: "Laur", - }); - }); - cy.wait("@userSearchRequest").then((interception) => { expect(interception.request.query).to.contain({ query: "Laura", @@ -2199,6 +2187,49 @@ describe("Rooms view settings", function () { cy.get('[data-test="room-access-code-overlay"]').should("be.visible"); }); + it("transfer ownership with search by name disabled", function () { + cy.fixture("config.json").then((config) => { + config.data.user.search_by_name = false; + + cy.intercept("GET", "api/v1/config", { + statusCode: 200, + body: config, + }); + }); + + cy.visit("/rooms/abc-def-123#tab=settings"); + + cy.wait("@roomSettingsRequest"); + + cy.get("[data-test=room-transfer-ownership-dialog]").should("not.exist"); + cy.get('[data-test="room-transfer-ownership-button"]') + .should("have.text", "rooms.modals.transfer_ownership.title") + .click(); + + // Check that dialog is shown correctly + cy.get("[data-test=room-transfer-ownership-dialog]") + .should("be.visible") + .within(() => { + // Check autofocus + cy.get(".multiselect__content").should("be.visible"); + + // Check prompt + cy.get('[data-test="new-owner-dropdown"]').should( + "include.text", + "rooms.members.modals.add.no_options_email_only", + ); + + // Check placeholder + cy.get('[data-test="new-owner-dropdown"]') + .find("input") + .should( + "have.attr", + "placeholder", + "rooms.members.modals.add.placeholder_email_only", + ); + }); + }); + it("transfer ownership errors", function () { cy.visit("/rooms/abc-def-123#tab=settings"); diff --git a/tests/Frontend/fixtures/config.json b/tests/Frontend/fixtures/config.json index 74c9bf878..d3dfe047b 100644 --- a/tests/Frontend/fixtures/config.json +++ b/tests/Frontend/fixtures/config.json @@ -35,7 +35,8 @@ "room_name_limit": 50 }, "user": { - "password_change_allowed": true + "password_change_allowed": true, + "search_by_name": true }, "banner": {}, "recording": { diff --git a/tests/Frontend/fixtures/settings.json b/tests/Frontend/fixtures/settings.json index 798a5c52a..e18541d17 100644 --- a/tests/Frontend/fixtures/settings.json +++ b/tests/Frontend/fixtures/settings.json @@ -33,6 +33,7 @@ "room_file_terms_of_use": "Room file terms of use", "room_hide_owner_from_guests": false, "user_password_change_allowed": true, + "user_search_by_name": true, "recording_server_usage_enabled": true, "recording_server_usage_retention_period": -1, "recording_meeting_usage_enabled": true,