user_groups: Use can_join_group setting to check permisison.

This commit adds code to use can_join_group setting when
checking permission to join group in webapp.

Fixes part of #25938.
This commit is contained in:
Sahil Batra 2024-10-02 16:43:47 +05:30 committed by Tim Abbott
parent 6d0d1a0700
commit 701e391def
10 changed files with 166 additions and 7 deletions

View File

@ -215,6 +215,16 @@ export function can_manage_user_group(group_id: number): boolean {
); );
} }
export function can_join_user_group(group_id: number): boolean {
const group = user_groups.get_user_group_from_id(group_id);
if (user_has_permission_for_group_setting(group.can_join_group, "can_join_group", "group")) {
return true;
}
return can_manage_user_group(group_id);
}
export function user_can_create_user_groups(): boolean { export function user_can_create_user_groups(): boolean {
return user_has_permission_for_group_setting( return user_has_permission_for_group_setting(
realm.realm_can_create_groups, realm.realm_can_create_groups,

View File

@ -145,6 +145,7 @@ export const user_group_schema = z.object({
members: z.array(z.number()), members: z.array(z.number()),
is_system_group: z.boolean(), is_system_group: z.boolean(),
direct_subgroup_ids: z.array(z.number()), direct_subgroup_ids: z.array(z.number()),
can_join_group: group_setting_type_schema,
can_manage_group: group_setting_type_schema, can_manage_group: group_setting_type_schema,
can_mention_group: z.number(), can_mention_group: z.number(),
deactivated: z.boolean(), deactivated: z.boolean(),

View File

@ -16,6 +16,7 @@ export type UserGroupUpdateEvent = {
data: { data: {
name?: string; name?: string;
description?: string; description?: string;
can_join_group?: number;
can_manage_group?: number; can_manage_group?: number;
can_mention_group?: number; can_mention_group?: number;
deactivated?: boolean; deactivated?: boolean;

View File

@ -258,7 +258,17 @@ function update_group_membership_button(group_id) {
$group_settings_button.text($t({defaultMessage: "Join group"})); $group_settings_button.text($t({defaultMessage: "Join group"}));
} }
if (settings_data.can_manage_user_group(group_id)) { const can_join_group = settings_data.can_join_user_group(group_id);
const can_leave_group = settings_data.can_manage_user_group(group_id);
let can_update_membership = true;
if (!is_member && !can_join_group) {
can_update_membership = false;
} else if (is_member && !can_leave_group) {
can_update_membership = false;
}
if (can_update_membership) {
$group_settings_button.prop("disabled", false); $group_settings_button.prop("disabled", false);
$group_settings_button.css("pointer-events", ""); $group_settings_button.css("pointer-events", "");
const $group_settings_button_wrapper = $group_settings_button.closest( const $group_settings_button_wrapper = $group_settings_button.closest(
@ -298,7 +308,7 @@ export function handle_member_edit_event(group_id, user_ids) {
// is added to it. The whole list is redrawed to // is added to it. The whole list is redrawed to
// maintain the sorted order of groups. // maintain the sorted order of groups.
redraw_user_group_list(); redraw_user_group_list();
} else if (!settings_data.can_manage_user_group(group_id)) { } else if (!settings_data.can_join_user_group(group_id)) {
// We remove the group row immediately only if the // We remove the group row immediately only if the
// user cannot join the group again themselves. // user cannot join the group again themselves.
const group_row = row_for_group_id(group_id); const group_row = row_for_group_id(group_id);
@ -315,7 +325,8 @@ export function handle_member_edit_event(group_id, user_ids) {
const item = group; const item = group;
item.is_member = user_groups.is_user_in_group(group_id, people.my_current_user_id()); item.is_member = user_groups.is_user_in_group(group_id, people.my_current_user_id());
item.can_edit = settings_data.can_manage_user_group(item.id); item.can_join = settings_data.can_join_user_group(item.id);
item.can_leave = settings_data.can_manage_user_group(item.id);
const html = render_browse_user_groups_list_item(item); const html = render_browse_user_groups_list_item(item);
const $new_row = $(html); const $new_row = $(html);
@ -634,6 +645,9 @@ export function update_group(event) {
if (event.data.can_manage_group !== undefined) { if (event.data.can_manage_group !== undefined) {
update_group_management_ui(); update_group_management_ui();
} }
if (event.data.can_join_group !== undefined) {
update_group_membership_button(group.id);
}
} }
} }
@ -831,7 +845,8 @@ export function setup_page(callback) {
people.my_current_user_id(), people.my_current_user_id(),
item.id, item.id,
); );
item.can_edit = settings_data.can_manage_user_group(item.id); item.can_join = settings_data.can_join_user_group(item.id);
item.can_leave = settings_data.can_manage_user_group(item.id);
return render_browse_user_groups_list_item(item); return render_browse_user_groups_list_item(item);
}, },
filter: { filter: {

View File

@ -356,7 +356,7 @@ function remove_member({
}); });
} }
if (people.is_my_user_id(target_user_id) && !settings_data.can_manage_user_group(group_id)) { if (people.is_my_user_id(target_user_id) && !settings_data.can_join_user_group(group_id)) {
const html_body = render_leave_user_group_modal({ const html_body = render_leave_user_group_modal({
message: $t({ message: $t({
defaultMessage: "Once you leave this group, you will not be able to rejoin.", defaultMessage: "Once you leave this group, you will not be able to rejoin.",

View File

@ -55,6 +55,7 @@ export function add(user_group_raw: UserGroupRaw): UserGroup {
members: new Set(user_group_raw.members), members: new Set(user_group_raw.members),
is_system_group: user_group_raw.is_system_group, is_system_group: user_group_raw.is_system_group,
direct_subgroup_ids: new Set(user_group_raw.direct_subgroup_ids), direct_subgroup_ids: new Set(user_group_raw.direct_subgroup_ids),
can_join_group: user_group_raw.can_join_group,
can_manage_group: user_group_raw.can_manage_group, can_manage_group: user_group_raw.can_manage_group,
can_mention_group: user_group_raw.can_mention_group, can_mention_group: user_group_raw.can_mention_group,
deactivated: user_group_raw.deactivated, deactivated: user_group_raw.deactivated,
@ -112,6 +113,12 @@ export function update(event: UserGroupUpdateEvent): void {
user_group_name_dict.delete(group.name); user_group_name_dict.delete(group.name);
user_group_name_dict.set(group.name, group); user_group_name_dict.set(group.name, group);
} }
if (event.data.can_join_group !== undefined) {
group.can_join_group = event.data.can_join_group;
user_group_name_dict.delete(group.name);
user_group_name_dict.set(group.name, group);
}
} }
export function get_user_group_from_name(name: string): UserGroup | undefined { export function get_user_group_from_name(name: string): UserGroup | undefined {

View File

@ -1,6 +1,6 @@
<div class="group-row" data-group-id="{{id}}" data-group-name="{{name}}"> <div class="group-row" data-group-id="{{id}}" data-group-name="{{name}}">
{{#if is_member}} {{#if is_member}}
<div class="check checked join_leave_button tippy-zulip-tooltip {{#unless can_edit}}disabled{{/unless}}" data-tooltip-template-id="{{#if can_edit}}leave-{{name}}-group-tooltip-template{{else}}cannot-leave-{{name}}-group-tooltip-template{{/if}}"> <div class="check checked join_leave_button tippy-zulip-tooltip {{#unless can_leave}}disabled{{/unless}}" data-tooltip-template-id="{{#if can_leave}}leave-{{name}}-group-tooltip-template{{else}}cannot-leave-{{name}}-group-tooltip-template{{/if}}">
<template id="leave-{{name}}-group-tooltip-template"> <template id="leave-{{name}}-group-tooltip-template">
<span> <span>
{{#tr}} {{#tr}}
@ -23,7 +23,7 @@
<div class='join_leave_status'></div> <div class='join_leave_status'></div>
</div> </div>
{{else}} {{else}}
<div class="check join_leave_button {{#unless can_edit}}disabled{{/unless}} tippy-zulip-tooltip" data-tooltip-template-id="{{#if can_edit}}join-{{name}}-group-tooltip-template{{else}}cannot-join-{{name}}-group-tooltip-template{{/if}}"> <div class="check join_leave_button {{#unless can_join}}disabled{{/unless}} tippy-zulip-tooltip" data-tooltip-template-id="{{#if can_join}}join-{{name}}-group-tooltip-template{{else}}cannot-join-{{name}}-group-tooltip-template{{/if}}">
<template id="join-{{name}}-group-tooltip-template"> <template id="join-{{name}}-group-tooltip-template">
<span> <span>
{{#tr}} {{#tr}}

View File

@ -440,6 +440,7 @@ const hamletcharacters = user_group_item({
members: new Set([100, 104]), members: new Set([100, 104]),
is_system_group: false, is_system_group: false,
direct_subgroup_ids: new Set([]), direct_subgroup_ids: new Set([]),
can_join_group: 2,
can_manage_group: 2, can_manage_group: 2,
can_mention_group: 2, can_mention_group: 2,
deactivated: false, deactivated: false,
@ -454,6 +455,7 @@ const backend = user_group_item({
members: new Set([101]), members: new Set([101]),
is_system_group: false, is_system_group: false,
direct_subgroup_ids: new Set([1]), direct_subgroup_ids: new Set([1]),
can_join_group: 1,
can_manage_group: 1, can_manage_group: 1,
can_mention_group: 1, can_mention_group: 1,
deactivated: false, deactivated: false,
@ -468,6 +470,7 @@ const call_center = user_group_item({
members: new Set([102]), members: new Set([102]),
is_system_group: false, is_system_group: false,
direct_subgroup_ids: new Set([]), direct_subgroup_ids: new Set([]),
can_join_group: 2,
can_manage_group: 2, can_manage_group: 2,
can_mention_group: 2, can_mention_group: 2,
deactivated: false, deactivated: false,

View File

@ -442,6 +442,124 @@ run_test("can_manage_user_group", () => {
assert.ok(settings_data.can_manage_user_group(students.id)); assert.ok(settings_data.can_manage_user_group(students.id));
}); });
run_test("can_join_user_group", () => {
const admins = {
description: "Administrators",
name: "role:administrators",
id: 1,
members: new Set([1]),
is_system_group: true,
direct_subgroup_ids: new Set([]),
can_join_group: 4,
can_manage_group: 4,
can_mention_group: 1,
};
const moderators = {
description: "Moderators",
name: "role:moderators",
id: 2,
members: new Set([2]),
is_system_group: true,
direct_subgroup_ids: new Set([1]),
can_join_group: 4,
can_manage_group: 4,
can_mention_group: 1,
};
const members = {
description: "Members",
name: "role:members",
id: 3,
members: new Set([3, 4]),
is_system_group: true,
direct_subgroup_ids: new Set([1, 2]),
can_join_group: 4,
can_manage_group: 4,
can_mention_group: 4,
};
const nobody = {
description: "Nobody",
name: "role:nobody",
id: 4,
members: new Set([]),
is_system_group: true,
direct_subgroup_ids: new Set([]),
can_join_group: 4,
can_manage_group: 4,
can_mention_group: 2,
};
const students = {
description: "Students group",
name: "Students",
id: 5,
members: new Set([1, 2]),
is_system_group: false,
direct_subgroup_ids: new Set([4, 5]),
can_join_group: 1,
can_manage_group: {
direct_members: [4],
direct_subgroups: [],
},
can_mention_group: 3,
creator_id: 4,
};
user_groups.initialize({
realm_user_groups: [admins, moderators, members, nobody, students],
});
realm.realm_can_manage_all_groups = nobody.id;
page_params.is_spectator = true;
assert.ok(!settings_data.can_join_user_group(students.id));
page_params.is_spectator = false;
// admin user
current_user.user_id = 1;
assert.ok(settings_data.can_join_user_group(students.id));
// moderator user
current_user.user_id = 2;
assert.ok(!settings_data.can_join_user_group(students.id));
let event = {
group_id: students.id,
data: {
can_join_group: moderators.id,
},
};
user_groups.update(event);
assert.ok(settings_data.can_join_user_group(students.id));
current_user.user_id = 1;
assert.ok(settings_data.can_join_user_group(students.id));
// Some other user.
current_user.user_id = 5;
assert.ok(!settings_data.can_join_user_group(students.id));
event = {
group_id: students.id,
data: {
can_join_group: {
direct_members: [5],
direct_subgroups: [admins.id],
},
},
};
user_groups.update(event);
assert.ok(settings_data.can_join_user_group(students.id));
current_user.user_id = 2;
assert.ok(!settings_data.can_join_user_group(students.id));
// User can join the group if they can add anyone in the group which
// depends on can_manage_group and realm.can_manage_all_groups settings.
current_user.user_id = 4;
assert.ok(settings_data.can_join_user_group(students.id));
realm.realm_can_manage_all_groups = moderators.id;
current_user.user_id = 2;
assert.ok(settings_data.can_join_user_group(students.id));
});
run_test("type_id_to_string", () => { run_test("type_id_to_string", () => {
page_params.bot_types = [ page_params.bot_types = [
{ {

View File

@ -19,6 +19,7 @@ run_test("user_groups", () => {
members: new Set([1, 2]), members: new Set([1, 2]),
is_system_group: false, is_system_group: false,
direct_subgroup_ids: new Set([4, 5]), direct_subgroup_ids: new Set([4, 5]),
can_join_group: 1,
can_manage_group: 1, can_manage_group: 1,
can_mention_group: 2, can_mention_group: 2,
deactivated: false, deactivated: false,
@ -41,6 +42,7 @@ run_test("user_groups", () => {
members: new Set([3]), members: new Set([3]),
is_system_group: false, is_system_group: false,
direct_subgroup_ids: new Set([]), direct_subgroup_ids: new Set([]),
can_join_group: 1,
can_manage_group: 1, can_manage_group: 1,
can_mention_group: 2, can_mention_group: 2,
deactivated: false, deactivated: false,
@ -51,6 +53,7 @@ run_test("user_groups", () => {
members: new Set([1, 2, 3]), members: new Set([1, 2, 3]),
is_system_group: false, is_system_group: false,
direct_subgroup_ids: new Set([4, 5, 6]), direct_subgroup_ids: new Set([4, 5, 6]),
can_join_group: 1,
can_manage_group: 1, can_manage_group: 1,
can_mention_group: 1, can_mention_group: 1,
deactivated: false, deactivated: false,
@ -61,6 +64,7 @@ run_test("user_groups", () => {
members: new Set([1, 2, 3]), members: new Set([1, 2, 3]),
is_system_group: false, is_system_group: false,
direct_subgroup_ids: new Set([4, 5, 6]), direct_subgroup_ids: new Set([4, 5, 6]),
can_join_group: 1,
can_manage_group: 1, can_manage_group: 1,
can_mention_group: 1, can_mention_group: 1,
deactivated: true, deactivated: true,