mirror of
https://github.com/discourse/discourse.git
synced 2026-08-05 11:52:32 +08:00
## Summary Prevent moderators from modifying group SMTP configuration by restricting these parameters to administrators. Previously, moderators with group management permissions could overwrite or clear sensitive mail settings, including credentials and server details, via the group update endpoint. ## Source - Patch Triage: https://patch.discourse.org/patch-triage/1150 Co-authored-by: discourse-patch-triage <272280883+discourse-patch-triage[bot]@users.noreply.github.com>
3591 lines
114 KiB
Ruby
Vendored
3591 lines
114 KiB
Ruby
Vendored
# frozen_string_literal: true
|
|
|
|
RSpec.describe GroupsController do
|
|
fab!(:user)
|
|
fab!(:user2, :user)
|
|
fab!(:other_user, :user)
|
|
let(:group) { Fabricate(:group, users: [user]) }
|
|
let(:moderator_group_id) { Group::AUTO_GROUPS[:moderators] }
|
|
fab!(:admin)
|
|
fab!(:moderator)
|
|
|
|
describe "#index" do
|
|
let(:staff_group) do
|
|
Fabricate(:group, name: "staff_group", visibility_level: Group.visibility_levels[:staff])
|
|
end
|
|
|
|
it "ensures that groups can be paginated" do
|
|
50.times { Fabricate(:group) }
|
|
|
|
get "/groups.json"
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
body = response.parsed_body
|
|
|
|
expect(body["groups"].size).to eq(36)
|
|
expect(body["total_rows_groups"]).to eq(50)
|
|
expect(body["load_more_groups"]).to eq("/groups?page=1")
|
|
|
|
get "/groups.json", params: { page: 1 }
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
body = response.parsed_body
|
|
|
|
expect(body["groups"].size).to eq(14)
|
|
expect(body["total_rows_groups"]).to eq(50)
|
|
expect(body["load_more_groups"]).to eq("/groups?page=2")
|
|
end
|
|
|
|
it "only accepts valid page numbers" do
|
|
get "/groups.json", params: { page: -1 }
|
|
expect(response.status).to eq(400)
|
|
|
|
get "/groups.json", params: { page: 0 }
|
|
expect(response.status).to eq(200)
|
|
|
|
get "/groups.json", params: { page: 1 }
|
|
expect(response.status).to eq(200)
|
|
end
|
|
|
|
context "when group directory is disabled" do
|
|
before { SiteSetting.enable_group_directory = false }
|
|
|
|
it "should deny access for an anon" do
|
|
get "/groups.json"
|
|
expect(response.status).to eq(403)
|
|
end
|
|
|
|
it "should deny access for a normal user" do
|
|
sign_in(user)
|
|
get "/groups.json"
|
|
|
|
expect(response.status).to eq(403)
|
|
end
|
|
|
|
it "should allow access for an admin" do
|
|
sign_in(admin)
|
|
get "/groups.json"
|
|
|
|
expect(response.status).to eq(200)
|
|
end
|
|
|
|
it "should allow access for a moderator" do
|
|
sign_in(moderator)
|
|
get "/groups.json"
|
|
|
|
expect(response.status).to eq(200)
|
|
end
|
|
end
|
|
|
|
context "with searchable" do
|
|
it "should return the searched groups" do
|
|
testing_group = Fabricate(:group, name: "testing")
|
|
|
|
get "/groups.json", params: { filter: "test" }
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
body = response.parsed_body
|
|
|
|
expect(body["groups"].first["id"]).to eq(testing_group.id)
|
|
expect(body["load_more_groups"]).to eq("/groups?filter=test&page=1")
|
|
end
|
|
end
|
|
|
|
context "with sortable" do
|
|
before do
|
|
group
|
|
sign_in(user)
|
|
end
|
|
|
|
fab!(:group_with_2_users) do
|
|
Fabricate(:group, name: "other_group", users: [user, other_user])
|
|
end
|
|
|
|
context "with default (descending) order" do
|
|
it "sorts by name" do
|
|
get "/groups.json", params: { order: "name" }
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
body = response.parsed_body
|
|
|
|
expect(body["groups"].map { |g| g["id"] }).to eq(
|
|
[group_with_2_users.id, group.id, moderator_group_id],
|
|
)
|
|
|
|
expect(body["load_more_groups"]).to eq("/groups?order=name&page=1")
|
|
end
|
|
|
|
it "sorts by user_count" do
|
|
get "/groups.json", params: { order: "user_count" }
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
body = response.parsed_body
|
|
|
|
expect(body["groups"].map { |g| g["id"] }).to eq(
|
|
[group_with_2_users.id, moderator_group_id, group.id],
|
|
)
|
|
|
|
expect(body["load_more_groups"]).to eq("/groups?order=user_count&page=1")
|
|
end
|
|
|
|
it "does not sort by hidden member counts" do
|
|
hidden_members_group =
|
|
Fabricate(
|
|
:group,
|
|
name: "zzz_hidden_group",
|
|
members_visibility_level: Group.visibility_levels[:owners],
|
|
users: [Fabricate(:user), Fabricate(:user), Fabricate(:user)],
|
|
)
|
|
|
|
get "/groups.json", params: { order: "user_count" }
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
body = response.parsed_body
|
|
group_ids = body["groups"].map { |group| group["id"] }
|
|
|
|
expect(group_ids.index(hidden_members_group.id)).to be >
|
|
group_ids.index(group_with_2_users.id)
|
|
expect(body["load_more_groups"]).to eq("/groups?page=1")
|
|
end
|
|
end
|
|
|
|
context "with ascending order" do
|
|
it "sorts by name" do
|
|
get "/groups.json", params: { order: "name", asc: true }
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
body = response.parsed_body
|
|
|
|
expect(body["groups"].map { |g| g["id"] }).to eq(
|
|
[moderator_group_id, group.id, group_with_2_users.id],
|
|
)
|
|
|
|
expect(body["load_more_groups"]).to eq("/groups?asc=true&order=name&page=1")
|
|
end
|
|
|
|
it "sorts by user_count" do
|
|
get "/groups.json", params: { order: "user_count", asc: "true" }
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
body = response.parsed_body
|
|
|
|
expect(body["groups"].map { |g| g["id"] }).to eq(
|
|
[moderator_group_id, group.id, group_with_2_users.id],
|
|
)
|
|
|
|
expect(body["load_more_groups"]).to eq("/groups?asc=true&order=user_count&page=1")
|
|
end
|
|
end
|
|
end
|
|
|
|
it "should return the right response" do
|
|
group
|
|
staff_group
|
|
|
|
get "/groups.json"
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
body = response.parsed_body
|
|
|
|
group_ids = body["groups"].map { |g| g["id"] }
|
|
|
|
expect(group_ids).to contain_exactly(group.id)
|
|
|
|
expect(body["load_more_groups"]).to eq("/groups?page=1")
|
|
expect(body["total_rows_groups"]).to eq(1)
|
|
expect(body["extras"]["type_filters"].map(&:to_sym)).to eq(
|
|
described_class::TYPE_FILTERS.keys - %i[my owner automatic non_automatic],
|
|
)
|
|
end
|
|
|
|
it "does not expose member counts for groups with hidden members" do
|
|
hidden_members_group =
|
|
Fabricate(
|
|
:group,
|
|
members_visibility_level: Group.visibility_levels[:owners],
|
|
users: [Fabricate(:user)],
|
|
)
|
|
|
|
get "/groups.json"
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
group_json =
|
|
response.parsed_body["groups"].find { |group| group["id"] == hidden_members_group.id }
|
|
|
|
expect(group_json).to be_present
|
|
expect(group_json["can_see_members"]).to eq(false)
|
|
expect(group_json).not_to have_key("user_count")
|
|
end
|
|
|
|
context "when viewing groups of another user" do
|
|
describe "when an invalid username is given" do
|
|
it "should return the right response" do
|
|
group
|
|
get "/groups.json", params: { username: "asdasd" }
|
|
|
|
expect(response.status).to eq(404)
|
|
end
|
|
end
|
|
|
|
it "should return the right response" do
|
|
u = Fabricate(:user)
|
|
m = Fabricate(:user)
|
|
o = Fabricate(:user)
|
|
|
|
levels = Group.visibility_levels.values
|
|
|
|
levels
|
|
.product(levels)
|
|
.each do |group_level, members_level|
|
|
g =
|
|
Fabricate(
|
|
:group,
|
|
name: "#{group_level}_#{members_level}",
|
|
visibility_level: group_level,
|
|
members_visibility_level: members_level,
|
|
users: [u],
|
|
)
|
|
|
|
if group_level == Group.visibility_levels[:members] ||
|
|
members_level == Group.visibility_levels[:members]
|
|
g.add(m)
|
|
end
|
|
if group_level == Group.visibility_levels[:owners] ||
|
|
members_level == Group.visibility_levels[:owners]
|
|
g.add_owner(o)
|
|
end
|
|
end
|
|
|
|
# anonymous user
|
|
get "/groups.json", params: { username: u.username }
|
|
|
|
expect(response.status).to eq(200)
|
|
group_names = response.parsed_body["groups"].map { |g| g["name"] }
|
|
expect(group_names).to contain_exactly("0_0")
|
|
|
|
# logged in user
|
|
sign_in(user)
|
|
get "/groups.json", params: { username: u.username }
|
|
|
|
expect(response.status).to eq(200)
|
|
group_names = response.parsed_body["groups"].map { |g| g["name"] }
|
|
expect(group_names).to contain_exactly("0_0", "0_1", "1_0", "1_1")
|
|
|
|
# member of the group
|
|
sign_in(m)
|
|
get "/groups.json", params: { username: u.username }
|
|
|
|
expect(response.status).to eq(200)
|
|
group_names = response.parsed_body["groups"].map { |g| g["name"] }
|
|
expect(group_names).to contain_exactly(
|
|
"0_0",
|
|
"0_1",
|
|
"0_2",
|
|
"1_0",
|
|
"1_1",
|
|
"1_2",
|
|
"2_0",
|
|
"2_1",
|
|
"2_2",
|
|
)
|
|
|
|
# owner
|
|
sign_in(o)
|
|
get "/groups.json", params: { username: u.username }
|
|
|
|
expect(response.status).to eq(200)
|
|
group_names = response.parsed_body["groups"].map { |g| g["name"] }
|
|
expect(group_names).to contain_exactly(
|
|
"0_0",
|
|
"0_1",
|
|
"0_4",
|
|
"1_0",
|
|
"1_1",
|
|
"1_4",
|
|
"2_4",
|
|
"3_4",
|
|
"4_0",
|
|
"4_1",
|
|
"4_2",
|
|
"4_3",
|
|
"4_4",
|
|
)
|
|
|
|
# moderator
|
|
sign_in(moderator)
|
|
get "/groups.json", params: { username: u.username }
|
|
|
|
expect(response.status).to eq(200)
|
|
group_names = response.parsed_body["groups"].map { |g| g["name"] }
|
|
expect(group_names).to contain_exactly(
|
|
"0_0",
|
|
"0_1",
|
|
"0_2",
|
|
"0_3",
|
|
"1_0",
|
|
"1_1",
|
|
"1_2",
|
|
"1_3",
|
|
"2_0",
|
|
"2_1",
|
|
"2_2",
|
|
"2_3",
|
|
"3_0",
|
|
"3_1",
|
|
"3_2",
|
|
"3_3",
|
|
)
|
|
|
|
# admin
|
|
sign_in(admin)
|
|
get "/groups.json", params: { username: u.username }
|
|
|
|
expect(response.status).to eq(200)
|
|
group_names = response.parsed_body["groups"].map { |g| g["name"] }
|
|
all_group_names = levels.product(levels).map { |a, b| "#{a}_#{b}" }
|
|
expect(group_names).to contain_exactly(*all_group_names)
|
|
end
|
|
end
|
|
|
|
context "when viewing as an admin" do
|
|
before do
|
|
sign_in(admin)
|
|
group.add(admin)
|
|
group.add_owner(admin)
|
|
end
|
|
|
|
it "should return the right response" do
|
|
staff_group
|
|
get "/groups.json"
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
body = response.parsed_body
|
|
|
|
group_ids = body["groups"].map { |g| g["id"] }
|
|
group_body = body["groups"].find { |g| g["id"] == group.id }
|
|
|
|
expect(group_body["is_group_user"]).to eq(true)
|
|
expect(group_body["is_group_owner"]).to eq(true)
|
|
expect(group_ids).to include(group.id, staff_group.id)
|
|
expect(body["load_more_groups"]).to eq("/groups?page=1")
|
|
expect(body["total_rows_groups"]).to eq(10)
|
|
|
|
expect(body["extras"]["type_filters"].map(&:to_sym)).to eq(
|
|
described_class::TYPE_FILTERS.keys - [:non_automatic],
|
|
)
|
|
end
|
|
|
|
context "when filterable by type" do
|
|
def expect_type_to_return_right_groups(type, expected_group_ids)
|
|
get "/groups.json", params: { type: type }
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
body = response.parsed_body
|
|
group_ids = body["groups"].map { |g| g["id"] }
|
|
|
|
expect(body["total_rows_groups"]).to eq(expected_group_ids.count)
|
|
expect(group_ids).to contain_exactly(*expected_group_ids)
|
|
end
|
|
|
|
describe "my groups" do
|
|
it "should return the groups admin is a member of" do
|
|
expect_type_to_return_right_groups("my", admin.group_users.map(&:group_id))
|
|
end
|
|
end
|
|
|
|
describe "owner groups" do
|
|
it "should return the groups admin is a owner of" do
|
|
group2 = Fabricate(:group)
|
|
_group3 = Fabricate(:group)
|
|
group2.add_owner(admin)
|
|
|
|
expect_type_to_return_right_groups(
|
|
"owner",
|
|
admin.group_users.where(owner: true).map(&:group_id),
|
|
)
|
|
end
|
|
end
|
|
|
|
describe "automatic groups" do
|
|
it "should return the right response" do
|
|
expect_type_to_return_right_groups(
|
|
"automatic",
|
|
Group::AUTO_GROUP_IDS.keys -
|
|
[
|
|
Group::AUTO_GROUPS[:everyone],
|
|
Group::AUTO_GROUPS[:anonymous_users],
|
|
Group::AUTO_GROUPS[:logged_in_users],
|
|
],
|
|
)
|
|
end
|
|
end
|
|
|
|
describe "non automatic groups" do
|
|
it "should return the right response" do
|
|
group2 = Fabricate(:group)
|
|
expect_type_to_return_right_groups("non_automatic", [group.id, group2.id])
|
|
end
|
|
end
|
|
|
|
describe "public groups" do
|
|
it "should return the right response" do
|
|
group2 = Fabricate(:group, public_admission: true)
|
|
|
|
expect_type_to_return_right_groups("public", [group2.id])
|
|
end
|
|
end
|
|
|
|
describe "close groups" do
|
|
it "should return the right response" do
|
|
group2 = Fabricate(:group, public_admission: false)
|
|
_group3 = Fabricate(:group, public_admission: true)
|
|
|
|
expect_type_to_return_right_groups("close", [group.id, group2.id])
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "groups_index_query modifier" do
|
|
fab!(:user)
|
|
fab!(:cool_group) { Fabricate(:group, name: "cool-group") }
|
|
fab!(:boring_group) { Fabricate(:group, name: "boring-group") }
|
|
|
|
it "allows changing the query" do
|
|
get "/groups.json"
|
|
expect(response.status).to eq(200)
|
|
expect(response.parsed_body["groups"].map { |g| g["id"] }).to include(
|
|
cool_group.id,
|
|
boring_group.id,
|
|
)
|
|
|
|
get "/groups.json", params: { filter: "cool" }
|
|
expect(response.status).to eq(200)
|
|
expect(response.parsed_body["groups"].map { |g| g["id"] }).to include(cool_group.id)
|
|
expect(response.parsed_body["groups"].map { |g| g["id"] }).not_to include(boring_group.id)
|
|
|
|
Plugin::Instance
|
|
.new
|
|
.register_modifier(:groups_index_query) do |query|
|
|
query.where("groups.name LIKE 'cool%'")
|
|
end
|
|
|
|
get "/groups.json"
|
|
expect(response.status).to eq(200)
|
|
expect(response.parsed_body["groups"].map { |g| g["id"] }).to include(cool_group.id)
|
|
expect(response.parsed_body["groups"].map { |g| g["id"] }).not_to include(boring_group.id)
|
|
|
|
get "/groups.json", params: { filter: "boring" }
|
|
expect(response.status).to eq(200)
|
|
expect(response.parsed_body["groups"].map { |g| g["id"] }).not_to include(
|
|
cool_group.id,
|
|
boring_group.id,
|
|
)
|
|
ensure
|
|
DiscoursePluginRegistry.clear_modifiers!
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "#show" do
|
|
shared_examples "group show behavior" do |path_prefix, param|
|
|
it "ensures the group can be seen" do
|
|
sign_in(user)
|
|
group.update!(visibility_level: Group.visibility_levels[:owners])
|
|
|
|
get "#{path_prefix}/#{group.public_send(param)}.json"
|
|
|
|
expect(response.status).to eq(404)
|
|
end
|
|
|
|
it "returns the right response" do
|
|
sign_in(user)
|
|
mod_group = Group.find(moderator_group_id)
|
|
|
|
get "#{path_prefix}/#{group.public_send(param)}.json"
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
body = response.parsed_body
|
|
|
|
expect(body["group"]["id"]).to eq(group.id)
|
|
expect(body["extras"]["visible_group_names"]).to eq([mod_group.name, group.name])
|
|
expect(response.headers["X-Robots-Tag"]).to eq("noindex")
|
|
end
|
|
|
|
context "as an admin" do
|
|
it "returns the right response" do
|
|
sign_in(admin)
|
|
|
|
get "#{path_prefix}/#{group.public_send(param)}.json"
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
body = response.parsed_body
|
|
|
|
expect(body["group"]["id"]).to eq(group.id)
|
|
|
|
groups = Group::AUTO_GROUPS.keys
|
|
groups.delete(:everyone)
|
|
groups.delete(:anonymous_users)
|
|
groups.delete(:logged_in_users)
|
|
groups.push(group.name)
|
|
|
|
expect(body["extras"]["visible_group_names"]).to contain_exactly(*groups.map(&:to_s))
|
|
end
|
|
end
|
|
end
|
|
|
|
it "should respond to HTML" do
|
|
group.update!(bio_raw: "testing **group** bio")
|
|
|
|
get "/groups/#{group.name}.html"
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
expect(response.body).to have_tag "title", text: "#{group.name} - #{SiteSetting.title}"
|
|
expect(response.body).to have_tag(:meta, with: { property: "og:title", content: group.name })
|
|
|
|
expect(response.body).to have_tag(
|
|
:meta,
|
|
with: {
|
|
property: "og:description",
|
|
content: "testing group bio",
|
|
},
|
|
)
|
|
end
|
|
|
|
it "renders a single-escaped, tag-free meta description from the bio" do
|
|
group.update!(bio_raw: "Tom & Jerry [blog](https://evil.example) win")
|
|
|
|
get "/groups/#{group.name}.html"
|
|
|
|
expect(response.body).to have_tag(
|
|
:meta,
|
|
with: {
|
|
name: "description",
|
|
content: "Tom & Jerry blog win",
|
|
},
|
|
)
|
|
expect(response.body).to have_tag(
|
|
:meta,
|
|
with: {
|
|
property: "og:description",
|
|
content: "Tom & Jerry blog win",
|
|
},
|
|
)
|
|
end
|
|
|
|
describe "when accessing by name" do
|
|
include_examples "group show behavior", "/groups", :name
|
|
|
|
describe "when viewing activity filters" do
|
|
it "should return the right response" do
|
|
get "/groups/#{group.name}/activity/posts.json"
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
body = response.parsed_body["group"]
|
|
|
|
expect(body["id"]).to eq(group.id)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "when accessing by id" do
|
|
include_examples "group show behavior", "/groups/by-id", :id
|
|
end
|
|
|
|
context "as a moderator with moderators_manage_groups enabled" do
|
|
before { SiteSetting.moderators_manage_groups = true }
|
|
|
|
it "includes automatic_membership_email_domains in the response" do
|
|
group.update!(automatic_membership_email_domains: "test.org")
|
|
sign_in(moderator)
|
|
|
|
get "/groups/#{group.name}.json"
|
|
|
|
expect(response.status).to eq(200)
|
|
expect(response.parsed_body["group"]["automatic_membership_email_domains"]).to eq(
|
|
"test.org",
|
|
)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "#mentions" do
|
|
it "ensures mentions are enabled" do
|
|
SiteSetting.enable_mentions = false
|
|
|
|
sign_in(user)
|
|
get "/groups/#{group.name}/mentions.json"
|
|
|
|
expect(response.status).to eq(404)
|
|
end
|
|
|
|
it "ensures the group can be seen" do
|
|
sign_in(user)
|
|
group.update!(visibility_level: Group.visibility_levels[:owners])
|
|
|
|
get "/groups/#{group.name}/mentions.json"
|
|
|
|
expect(response.status).to eq(404)
|
|
end
|
|
|
|
it "ensures the group members can be seen" do
|
|
sign_in(user)
|
|
group.update!(members_visibility_level: Group.visibility_levels[:owners])
|
|
|
|
get "/groups/#{group.name}/mentions.json"
|
|
|
|
expect(response.status).to eq(403)
|
|
end
|
|
|
|
it "returns the right response" do
|
|
post = Fabricate(:post)
|
|
GroupMention.create!(post: post, group: group)
|
|
|
|
sign_in(user)
|
|
get "/groups/#{group.name}/mentions.json"
|
|
|
|
expect(response.status).to eq(200)
|
|
expect(response.parsed_body["posts"].first["id"]).to eq(post.id)
|
|
end
|
|
|
|
it "omits hidden mentions from other users", :aggregate_failures do
|
|
visible_post = Fabricate(:post, user: user, raw: "visible group mention")
|
|
hidden_post = Fabricate(:post, user: user, raw: "private hidden group mention", hidden: true)
|
|
GroupMention.create!(post: visible_post, group: group)
|
|
GroupMention.create!(post: hidden_post, group: group)
|
|
|
|
sign_in(user2)
|
|
get "/groups/#{group.name}/mentions.json"
|
|
|
|
post_ids = response.parsed_body["posts"].map { |post| post["id"] }
|
|
|
|
expect(response.status).to eq(200)
|
|
expect(post_ids).to contain_exactly(visible_post.id)
|
|
expect(response.body).not_to include(hidden_post.raw)
|
|
end
|
|
|
|
it "returns hidden mentions to the author", :aggregate_failures do
|
|
hidden_post =
|
|
Fabricate(:post, user: user, raw: "author visible hidden group mention", hidden: true)
|
|
GroupMention.create!(post: hidden_post, group: group)
|
|
|
|
sign_in(user)
|
|
get "/groups/#{group.name}/mentions.json"
|
|
|
|
expect(response.status).to eq(200)
|
|
expect(response.parsed_body["posts"].map { |post| post["id"] }).to contain_exactly(
|
|
hidden_post.id,
|
|
)
|
|
expect(response.body).to include(hidden_post.raw)
|
|
end
|
|
|
|
it "supports pagination using before (date)" do
|
|
post = Fabricate(:post)
|
|
GroupMention.create!(post: post, group: group)
|
|
|
|
sign_in(user)
|
|
get "/groups/#{group.name}/mentions.json"
|
|
|
|
expect(response.status).to eq(200)
|
|
expect(response.parsed_body["posts"].first["id"]).to eq(post.id)
|
|
|
|
get "/groups/#{group.name}/mentions.json", params: { before: post.created_at }
|
|
|
|
expect(response.status).to eq(200)
|
|
expect(response.parsed_body["posts"]).to be_empty
|
|
end
|
|
|
|
it "supports pagination using before_post_id" do
|
|
post = Fabricate(:post)
|
|
GroupMention.create!(post: post, group: group)
|
|
|
|
sign_in(user)
|
|
get "/groups/#{group.name}/mentions.json"
|
|
|
|
expect(response.status).to eq(200)
|
|
expect(response.parsed_body["posts"].first["id"]).to eq(post.id)
|
|
|
|
get "/groups/#{group.name}/mentions.json", params: { before_post_id: post.id }
|
|
|
|
expect(response.status).to eq(200)
|
|
expect(response.parsed_body["posts"]).to be_empty
|
|
end
|
|
|
|
it "excludes posts from unlisted topics" do
|
|
visible_post = Fabricate(:post)
|
|
GroupMention.create!(post: visible_post, group: group)
|
|
|
|
unlisted_post = Fabricate(:post)
|
|
unlisted_post.topic.update!(visible: false)
|
|
GroupMention.create!(post: unlisted_post, group: group)
|
|
|
|
sign_in(user)
|
|
get "/groups/#{group.name}/mentions.json"
|
|
|
|
expect(response.status).to eq(200)
|
|
expect(response.parsed_body["posts"].map { |p| p["id"] }).to contain_exactly(visible_post.id)
|
|
end
|
|
end
|
|
|
|
describe "shared draft visibility" do
|
|
it "omits shared draft posts from posts and mentions endpoints", :aggregate_failures do
|
|
SiteSetting.shared_drafts_allowed_groups = Group::AUTO_GROUPS[:staff]
|
|
shared_drafts_category = Fabricate(:category)
|
|
SiteSetting.shared_drafts_category = shared_drafts_category.id
|
|
shared_draft_topic =
|
|
Fabricate(
|
|
:topic,
|
|
title: "Secret Shared Draft Group Topic",
|
|
category: shared_drafts_category,
|
|
)
|
|
shared_draft_post =
|
|
Fabricate(
|
|
:post,
|
|
user: user,
|
|
topic: shared_draft_topic,
|
|
raw: "secret shared draft group post",
|
|
)
|
|
Fabricate(:shared_draft, topic: shared_draft_topic, category: Fabricate(:category))
|
|
GroupMention.create!(post: shared_draft_post, group: group)
|
|
|
|
sign_in(user2)
|
|
["/groups/#{group.name}/posts.json", "/groups/#{group.name}/mentions.json"].each do |path|
|
|
get path
|
|
|
|
expect(response.status).to eq(200)
|
|
expect(response.parsed_body["posts"]).to be_empty
|
|
expect(response.body).not_to include(shared_draft_topic.title)
|
|
expect(response.body).not_to include(shared_draft_post.raw)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "#posts" do
|
|
it "ensures the group can be seen" do
|
|
sign_in(user)
|
|
group.update!(visibility_level: Group.visibility_levels[:owners])
|
|
|
|
get "/groups/#{group.name}/posts.json"
|
|
|
|
expect(response.status).to eq(404)
|
|
end
|
|
|
|
it "ensures the group members can be seen" do
|
|
sign_in(user)
|
|
group.update!(members_visibility_level: Group.visibility_levels[:owners])
|
|
|
|
get "/groups/#{group.name}/posts.json"
|
|
|
|
expect(response.status).to eq(403)
|
|
end
|
|
|
|
it "calls `posts_for` and responds with JSON" do
|
|
sign_in(user)
|
|
post = Fabricate(:post, user: user)
|
|
get "/groups/#{group.name}/posts.json"
|
|
|
|
expect(response.status).to eq(200)
|
|
expect(response.parsed_body["posts"].first["id"]).to eq(post.id)
|
|
end
|
|
|
|
it "omits hidden posts from other users", :aggregate_failures do
|
|
visible_post = Fabricate(:post, user: user, raw: "visible group post")
|
|
hidden_post = Fabricate(:post, user: user, raw: "private hidden group post", hidden: true)
|
|
|
|
sign_in(user2)
|
|
get "/groups/#{group.name}/posts.json"
|
|
|
|
post_ids = response.parsed_body["posts"].map { |post| post["id"] }
|
|
|
|
expect(response.status).to eq(200)
|
|
expect(post_ids).to contain_exactly(visible_post.id)
|
|
expect(response.body).not_to include(hidden_post.raw)
|
|
end
|
|
|
|
it "returns hidden posts to staff", :aggregate_failures do
|
|
hidden_post =
|
|
Fabricate(:post, user: user, raw: "staff visible hidden group post", hidden: true)
|
|
|
|
sign_in(moderator)
|
|
get "/groups/#{group.name}/posts.json"
|
|
|
|
expect(response.status).to eq(200)
|
|
expect(response.parsed_body["posts"].map { |post| post["id"] }).to contain_exactly(
|
|
hidden_post.id,
|
|
)
|
|
expect(response.body).to include(hidden_post.raw)
|
|
end
|
|
|
|
it "returns hidden posts to category moderators", :aggregate_failures do
|
|
SiteSetting.enable_category_group_moderation = true
|
|
moderated_category = Fabricate(:category)
|
|
moderation_group = Fabricate(:group)
|
|
category_moderator = Fabricate(:user)
|
|
moderation_group.add(category_moderator)
|
|
Fabricate(:category_moderation_group, category: moderated_category, group: moderation_group)
|
|
hidden_post =
|
|
Fabricate(
|
|
:post,
|
|
user: user,
|
|
topic: Fabricate(:topic, category: moderated_category),
|
|
raw: "category moderator visible hidden group post",
|
|
hidden: true,
|
|
)
|
|
|
|
sign_in(category_moderator)
|
|
get "/groups/#{group.name}/posts.json"
|
|
|
|
expect(response.status).to eq(200)
|
|
expect(response.parsed_body["posts"].map { |post| post["id"] }).to contain_exactly(
|
|
hidden_post.id,
|
|
)
|
|
expect(response.body).to include(hidden_post.raw)
|
|
end
|
|
|
|
it "does not include names when names are disabled" do
|
|
SiteSetting.enable_names = false
|
|
user.update!(name: "Hidden Full Name")
|
|
sign_in(user)
|
|
Fabricate(:post, user: user)
|
|
|
|
get "/groups/#{group.name}/posts.json"
|
|
|
|
expect(response.status).to eq(200)
|
|
post_response = response.parsed_body["posts"].first
|
|
expect(post_response["username"]).to eq(user.username)
|
|
expect(post_response).not_to have_key("name")
|
|
end
|
|
|
|
it "returns moderator actions" do
|
|
sign_in(user)
|
|
post = Fabricate(:post, user: user, post_type: Post.types[:moderator_action])
|
|
get "/groups/#{group.name}/posts.json"
|
|
|
|
expect(response.status).to eq(200)
|
|
expect(response.parsed_body["posts"].first["id"]).to eq(post.id)
|
|
end
|
|
|
|
it "supports pagination using before (date)" do
|
|
post = Fabricate(:post, user: user)
|
|
|
|
sign_in(user)
|
|
get "/groups/#{group.name}/posts.json"
|
|
|
|
expect(response.status).to eq(200)
|
|
expect(response.parsed_body["posts"].first["id"]).to eq(post.id)
|
|
|
|
get "/groups/#{group.name}/posts.json", params: { before: post.created_at }
|
|
|
|
expect(response.status).to eq(200)
|
|
expect(response.parsed_body["posts"]).to be_empty
|
|
end
|
|
|
|
it "supports pagination using before_post_id" do
|
|
post = Fabricate(:post, user: user)
|
|
|
|
sign_in(user)
|
|
get "/groups/#{group.name}/posts.json"
|
|
|
|
expect(response.status).to eq(200)
|
|
expect(response.parsed_body["posts"].first["id"]).to eq(post.id)
|
|
|
|
get "/groups/#{group.name}/posts.json", params: { before_post_id: post.id }
|
|
|
|
expect(response.status).to eq(200)
|
|
expect(response.parsed_body["posts"]).to be_empty
|
|
end
|
|
end
|
|
|
|
describe "#members" do
|
|
it "returns correct error code with invalid params" do
|
|
sign_in(user)
|
|
|
|
get "/groups/#{group.name}/members.json?limit=-1"
|
|
expect(response.status).to eq(400)
|
|
|
|
get "/groups/#{group.name}/members.json?offset=-1"
|
|
expect(response.status).to eq(400)
|
|
|
|
get "/groups/trust_level_0/members.json?limit=2000"
|
|
expect(response.status).to eq(400)
|
|
end
|
|
|
|
it "ensures the group can be seen" do
|
|
sign_in(user)
|
|
group.update!(visibility_level: Group.visibility_levels[:owners])
|
|
|
|
get "/groups/#{group.name}/members.json"
|
|
|
|
expect(response.status).to eq(404)
|
|
end
|
|
|
|
it "ensures the group members can be seen" do
|
|
group.update!(members_visibility_level: Group.visibility_levels[:logged_on_users])
|
|
|
|
get "/groups/#{group.name}/members.json", params: { limit: 1 }
|
|
|
|
expect(response.status).to eq(403)
|
|
end
|
|
|
|
it "hides activity timestamps for hidden profiles" do
|
|
SiteSetting.allow_users_to_hide_profile = true
|
|
|
|
hidden_user = Fabricate(:user, last_seen_at: 1.hour.ago, last_posted_at: 2.hours.ago)
|
|
hidden_user.user_option.update!(hide_profile: true)
|
|
visible_user = Fabricate(:user, last_seen_at: 3.hours.ago, last_posted_at: 4.hours.ago)
|
|
viewer = Fabricate(:user, trust_level: TrustLevel[2])
|
|
group.add(hidden_user)
|
|
group.add(visible_user)
|
|
|
|
sign_in(viewer)
|
|
get "/groups/#{group.name}/members.json"
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
members = response.parsed_body["members"]
|
|
hidden_member = members.find { |member| member["id"] == hidden_user.id }
|
|
visible_member = members.find { |member| member["id"] == visible_user.id }
|
|
|
|
expect(hidden_member).to be_present
|
|
expect(visible_member).to be_present
|
|
expect(hidden_member).not_to include("last_seen_at", "last_posted_at")
|
|
expect(visible_member).to include("last_seen_at", "last_posted_at")
|
|
end
|
|
|
|
it "ensures that membership can be paginated" do
|
|
freeze_time
|
|
|
|
first_user = Fabricate(:user)
|
|
group.add(first_user)
|
|
|
|
freeze_time 1.day.from_now
|
|
|
|
4.times { group.add(Fabricate(:user)) }
|
|
usernames = group.reload.users.map { |m| m.username }.sort
|
|
|
|
get "/groups/#{group.name}/members.json", params: { limit: 3, asc: true }
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
members = response.parsed_body["members"]
|
|
|
|
expect(members.map { |m| m["username"] }).to eq(usernames[0..2])
|
|
|
|
get "/groups/#{group.name}/members.json", params: { limit: 3, offset: 3, asc: true }
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
members = response.parsed_body["members"]
|
|
|
|
expect(members.map { |m| m["username"] }).to eq(usernames[3..5])
|
|
|
|
get "/groups/#{group.name}/members.json", params: { order: "added_at" }
|
|
members = response.parsed_body["members"]
|
|
|
|
first_group_user = group.group_users.find_by(user_id: first_user.id)
|
|
expect(Time.zone.parse(members.last["added_at"])).to be_within_one_second_of(
|
|
first_group_user.created_at,
|
|
)
|
|
end
|
|
|
|
it "can sort items" do
|
|
sign_in(user)
|
|
group.update!(visibility_level: Group.visibility_levels[:logged_on_users])
|
|
other_user = Fabricate(:user)
|
|
group.add_owner(other_user)
|
|
|
|
get "/groups/#{group.name}/members.json"
|
|
|
|
expect(response.parsed_body["members"].map { |u| u["id"] }).to eq([other_user.id, user.id])
|
|
expect(response.parsed_body["owners"].map { |u| u["id"] }).to eq([other_user.id])
|
|
|
|
get "/groups/#{group.name}/members.json?order=added_at&asc=true"
|
|
|
|
expect(response.parsed_body["members"].map { |u| u["id"] }).to eq([user.id, other_user.id])
|
|
expect(response.parsed_body["owners"].map { |u| u["id"] }).to eq([other_user.id])
|
|
end
|
|
|
|
context "when include_custom_fields is true" do
|
|
fab!(:user_field)
|
|
let(:user_field_name) { "user_field_#{user_field.id}" }
|
|
let!(:custom_user_field) do
|
|
UserCustomField.create!(user_id: user.id, name: user_field_name, value: "A custom field")
|
|
end
|
|
|
|
before do
|
|
sign_in(user)
|
|
SiteSetting.public_user_custom_fields = user_field_name
|
|
end
|
|
|
|
it "shows the custom fields" do
|
|
get "/groups/#{group.name}/members.json", params: { include_custom_fields: true }
|
|
|
|
expect(response.status).to eq(200)
|
|
response_custom_fields = response.parsed_body["members"].first["custom_fields"]
|
|
expect(response_custom_fields[user_field_name]).to eq("A custom field")
|
|
end
|
|
|
|
it "allows sorting by custom fields" do
|
|
group.add(user2)
|
|
UserCustomField.create!(user_id: user2.id, name: user_field_name, value: "C custom field")
|
|
group.add(other_user)
|
|
UserCustomField.create!(
|
|
user_id: other_user.id,
|
|
name: user_field_name,
|
|
value: "B custom field",
|
|
)
|
|
|
|
get "/groups/#{group.name}/members.json",
|
|
params: {
|
|
include_custom_fields: true,
|
|
order: "custom_field",
|
|
order_field: user_field_name,
|
|
asc: true,
|
|
}
|
|
|
|
expect(response.status).to eq(200)
|
|
expect(response.parsed_body["members"].pluck("id")).to eq(
|
|
[user.id, other_user.id, user2.id],
|
|
)
|
|
|
|
get "/groups/#{group.name}/members.json",
|
|
params: {
|
|
include_custom_fields: true,
|
|
order: "custom_field",
|
|
order_field: user_field_name,
|
|
}
|
|
|
|
expect(response.status).to eq(200)
|
|
expect(response.parsed_body["members"].pluck("id")).to eq(
|
|
[user2.id, other_user.id, user.id],
|
|
)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "#posts_feed" do
|
|
it "renders RSS" do
|
|
get "/groups/#{group.name}/posts.rss"
|
|
|
|
expect(response.status).to eq(200)
|
|
expect(response.media_type).to eq("application/rss+xml")
|
|
end
|
|
end
|
|
|
|
describe "#mentions_feed" do
|
|
it "renders RSS" do
|
|
get "/groups/#{group.name}/mentions.rss"
|
|
|
|
expect(response.status).to eq(200)
|
|
expect(response.media_type).to eq("application/rss+xml")
|
|
end
|
|
|
|
it "fails when disabled" do
|
|
SiteSetting.enable_mentions = false
|
|
|
|
get "/groups/#{group.name}/mentions.rss"
|
|
|
|
expect(response.status).to eq(404)
|
|
end
|
|
end
|
|
|
|
describe "#mentionable and #messageable" do
|
|
it "returns not found for hidden and missing groups", :aggregate_failures do
|
|
user.change_trust_level!(1)
|
|
hidden_group =
|
|
Fabricate(
|
|
:group,
|
|
name: "hidden_support",
|
|
mentionable_level: Group::ALIAS_LEVELS[:everyone],
|
|
messageable_level: Group::ALIAS_LEVELS[:everyone],
|
|
visibility_level: Group.visibility_levels[:staff],
|
|
)
|
|
missing_group_name = "missing_support"
|
|
|
|
sign_in(user)
|
|
|
|
get "/g/#{hidden_group.name}/mentionable.json"
|
|
expect(response.status).to eq(404)
|
|
expect(response.parsed_body["error_type"]).to eq("not_found")
|
|
|
|
get "/g/#{missing_group_name}/mentionable.json"
|
|
expect(response.status).to eq(404)
|
|
expect(response.parsed_body["error_type"]).to eq("not_found")
|
|
|
|
get "/g/#{hidden_group.name}/messageable.json"
|
|
expect(response.status).to eq(404)
|
|
expect(response.parsed_body["error_type"]).to eq("not_found")
|
|
|
|
get "/g/#{missing_group_name}/messageable.json"
|
|
expect(response.status).to eq(404)
|
|
expect(response.parsed_body["error_type"]).to eq("not_found")
|
|
end
|
|
end
|
|
|
|
describe "#mentionable" do
|
|
it "should return the right response" do
|
|
sign_in(user)
|
|
|
|
group.update!(
|
|
mentionable_level: Group::ALIAS_LEVELS[:owners_mods_and_admins],
|
|
visibility_level: Group.visibility_levels[:logged_on_users],
|
|
)
|
|
|
|
get "/groups/#{group.name}/mentionable.json"
|
|
expect(response.status).to eq(200)
|
|
|
|
body = response.parsed_body
|
|
expect(body["mentionable"]).to eq(false)
|
|
|
|
group.update!(
|
|
mentionable_level: Group::ALIAS_LEVELS[:everyone],
|
|
visibility_level: Group.visibility_levels[:logged_on_users],
|
|
)
|
|
|
|
get "/groups/#{group.name}/mentionable.json"
|
|
expect(response.status).to eq(200)
|
|
|
|
body = response.parsed_body
|
|
expect(body["mentionable"]).to eq(true)
|
|
|
|
group.update!(
|
|
mentionable_level: Group::ALIAS_LEVELS[:nobody],
|
|
visibility_level: Group.visibility_levels[:public],
|
|
)
|
|
|
|
get "/groups/#{group.name}/mentionable.json"
|
|
expect(response.status).to eq(200)
|
|
|
|
body = response.parsed_body
|
|
expect(body["mentionable"]).to eq(true)
|
|
end
|
|
end
|
|
|
|
describe "#messageable" do
|
|
it "should return the right response" do
|
|
user.change_trust_level!(1)
|
|
sign_in(user)
|
|
|
|
get "/groups/#{group.name}/messageable.json"
|
|
expect(response.status).to eq(200)
|
|
|
|
body = response.parsed_body
|
|
expect(body["messageable"]).to eq(false)
|
|
|
|
group.update!(
|
|
messageable_level: Group::ALIAS_LEVELS[:everyone],
|
|
visibility_level: Group.visibility_levels[:logged_on_users],
|
|
)
|
|
|
|
get "/groups/#{group.name}/messageable.json"
|
|
expect(response.status).to eq(200)
|
|
|
|
body = response.parsed_body
|
|
expect(body["messageable"]).to eq(true)
|
|
|
|
SiteSetting.personal_message_enabled_groups = Group::AUTO_GROUPS[:staff]
|
|
|
|
get "/groups/#{group.name}/messageable.json"
|
|
expect(response.status).to eq(200)
|
|
|
|
body = response.parsed_body
|
|
expect(body["messageable"]).to eq(true)
|
|
|
|
group.update!(messageable_level: Group::ALIAS_LEVELS[:only_admins])
|
|
|
|
get "/groups/#{group.name}/messageable.json"
|
|
expect(response.status).to eq(200)
|
|
|
|
body = response.parsed_body
|
|
expect(body["messageable"]).to eq(false)
|
|
end
|
|
end
|
|
|
|
describe "#update" do
|
|
let!(:group) do
|
|
Fabricate(:group, name: "test", users: [user], public_admission: false, public_exit: false)
|
|
end
|
|
let(:category) { Fabricate(:category) }
|
|
let(:tag) { Fabricate(:tag) }
|
|
|
|
context "with custom_fields" do
|
|
before do
|
|
user.update!(admin: true)
|
|
sign_in(user)
|
|
plugin = Plugin::Instance.new
|
|
plugin.register_editable_group_custom_field :test
|
|
@group = Fabricate(:group)
|
|
end
|
|
|
|
after { DiscoursePluginRegistry.reset! }
|
|
|
|
it "only updates allowed user fields" do
|
|
put "/groups/#{@group.id}.json",
|
|
params: {
|
|
group: {
|
|
custom_fields: {
|
|
test: :hello1,
|
|
test2: :hello2,
|
|
},
|
|
},
|
|
}
|
|
|
|
@group.reload
|
|
|
|
expect(response.status).to eq(200)
|
|
expect(@group.custom_fields["test"]).to eq("hello1")
|
|
expect(@group.custom_fields["test2"]).to be_blank
|
|
end
|
|
|
|
it "is secure when there are no registered editable fields" do
|
|
DiscoursePluginRegistry.reset!
|
|
put "/groups/#{@group.id}.json",
|
|
params: {
|
|
group: {
|
|
custom_fields: {
|
|
test: :hello1,
|
|
test2: :hello2,
|
|
},
|
|
},
|
|
}
|
|
|
|
@group.reload
|
|
|
|
expect(response.status).to eq(200)
|
|
expect(@group.custom_fields["test"]).to be_blank
|
|
expect(@group.custom_fields["test2"]).to be_blank
|
|
end
|
|
end
|
|
|
|
context "when user is group owner" do
|
|
before do
|
|
group.add_owner(user)
|
|
sign_in(user)
|
|
end
|
|
|
|
it "should be able update the group" do
|
|
group.update!(
|
|
allow_membership_requests: false,
|
|
visibility_level: 2,
|
|
mentionable_level: 2,
|
|
messageable_level: 2,
|
|
default_notification_level: 0,
|
|
grant_trust_level: 0,
|
|
)
|
|
|
|
expect do
|
|
put "/groups/#{group.id}.json",
|
|
params: {
|
|
group: {
|
|
mentionable_level: 1,
|
|
messageable_level: 1,
|
|
visibility_level: 1,
|
|
automatic_membership_email_domains: "test.org",
|
|
title: "haha",
|
|
primary_group: true,
|
|
grant_trust_level: 1,
|
|
incoming_email: "test@mail.org",
|
|
flair_bg_color: "FFF",
|
|
flair_color: "BBB",
|
|
flair_icon: "fa-circle-half-stroke",
|
|
bio_raw: "testing",
|
|
full_name: "awesome team",
|
|
public_admission: true,
|
|
public_exit: true,
|
|
allow_membership_requests: true,
|
|
membership_request_template: "testing",
|
|
default_notification_level: 1,
|
|
name: "testing",
|
|
tracking_category_ids: [category.id],
|
|
tracking_tags: [tag.name],
|
|
},
|
|
update_existing_users: false,
|
|
}
|
|
end.to change { GroupHistory.count }.by(12)
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
group.reload
|
|
|
|
expect(group.flair_bg_color).to eq("FFF")
|
|
expect(group.flair_color).to eq("BBB")
|
|
expect(group.flair_url).to eq("fa-circle-half-stroke")
|
|
expect(group.bio_raw).to eq("testing")
|
|
expect(group.full_name).to eq("awesome team")
|
|
expect(group.public_admission).to eq(true)
|
|
expect(group.public_exit).to eq(true)
|
|
expect(group.allow_membership_requests).to eq(true)
|
|
expect(group.membership_request_template).to eq("testing")
|
|
expect(group.name).to eq("test")
|
|
expect(group.visibility_level).to eq(2)
|
|
expect(group.mentionable_level).to eq(1)
|
|
expect(group.messageable_level).to eq(1)
|
|
expect(group.default_notification_level).to eq(1)
|
|
expect(group.automatic_membership_email_domains).to eq(nil)
|
|
expect(group.title).to eq(nil)
|
|
expect(group.primary_group).to eq(false)
|
|
expect(group.incoming_email).to eq(nil)
|
|
expect(group.grant_trust_level).to eq(0)
|
|
expect(group.group_category_notification_defaults.first&.category).to eq(category)
|
|
expect(group.group_tag_notification_defaults.first&.tag).to eq(tag)
|
|
end
|
|
|
|
it "should not clear group title when owner updates other settings" do
|
|
group.update!(title: "Original Title")
|
|
|
|
put "/groups/#{group.id}.json",
|
|
params: {
|
|
group: {
|
|
flair_bg_color: "FFF",
|
|
flair_color: "BBB",
|
|
flair_icon: "fa-circle-half-stroke",
|
|
},
|
|
}
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
group.reload
|
|
expect(group.title).to eq("Original Title")
|
|
expect(group.flair_bg_color).to eq("FFF")
|
|
end
|
|
|
|
it "should not allow group owner to modify the group title" do
|
|
group.update!(title: "Original Title")
|
|
|
|
put "/groups/#{group.id}.json",
|
|
params: {
|
|
group: {
|
|
title: "Hacked Title",
|
|
flair_bg_color: "FFF",
|
|
},
|
|
}
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
group.reload
|
|
expect(group.title).to eq("Original Title")
|
|
expect(group.flair_bg_color).to eq("FFF")
|
|
end
|
|
|
|
it "should not clear automatic_membership_email_domains when moderator owner updates group" do
|
|
SiteSetting.moderators_manage_groups = false
|
|
user.update!(moderator: true)
|
|
group.update!(automatic_membership_email_domains: "test.org")
|
|
|
|
put "/groups/#{group.id}.json",
|
|
params: {
|
|
group: {
|
|
bio_raw: "updated bio",
|
|
automatic_membership_email_domains: "",
|
|
},
|
|
}
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
group.reload
|
|
expect(group.automatic_membership_email_domains).to eq("test.org")
|
|
expect(group.bio_raw).to eq("updated bio")
|
|
end
|
|
|
|
it "should not be allowed to update automatic groups" do
|
|
group = Group.find(Group::AUTO_GROUPS[:admins])
|
|
|
|
put "/groups/#{group.id}.json", params: { group: { messageable_level: 1 } }
|
|
|
|
expect(response.status).to eq(403)
|
|
end
|
|
end
|
|
|
|
context "when user is group admin" do
|
|
before { sign_in(admin) }
|
|
|
|
it "should be able to update the group" do
|
|
group.update!(visibility_level: 2, members_visibility_level: 2, grant_trust_level: 0)
|
|
|
|
put "/groups/#{group.id}.json",
|
|
params: {
|
|
group: {
|
|
flair_color: "BBB",
|
|
name: "testing",
|
|
incoming_email: "test@mail.org",
|
|
primary_group: true,
|
|
automatic_membership_email_domains: "test.org",
|
|
title: "Admin Title",
|
|
grant_trust_level: 2,
|
|
visibility_level: 1,
|
|
members_visibility_level: 3,
|
|
tracking_category_ids: [category.id],
|
|
tracking_tags: [tag.name],
|
|
},
|
|
update_existing_users: false,
|
|
}
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
group.reload
|
|
expect(group.flair_color).to eq("BBB")
|
|
expect(group.name).to eq("testing")
|
|
expect(group.incoming_email).to eq("test@mail.org")
|
|
expect(group.primary_group).to eq(true)
|
|
expect(group.title).to eq("Admin Title")
|
|
expect(group.visibility_level).to eq(1)
|
|
expect(group.members_visibility_level).to eq(3)
|
|
expect(group.automatic_membership_email_domains).to eq("test.org")
|
|
expect(group.grant_trust_level).to eq(2)
|
|
expect(group.group_category_notification_defaults.first&.category).to eq(category)
|
|
expect(group.group_tag_notification_defaults.first&.tag).to eq(tag)
|
|
|
|
expect(Jobs::AutomaticGroupMembership.jobs.first["args"].first["group_id"]).to eq(group.id)
|
|
end
|
|
|
|
it "they should be able to update an automatic group" do
|
|
group = Group.find(Group::AUTO_GROUPS[:admins])
|
|
|
|
group.update!(
|
|
visibility_level: 2,
|
|
mentionable_level: 2,
|
|
messageable_level: 2,
|
|
default_notification_level: 2,
|
|
members_visibility_level: 2,
|
|
)
|
|
|
|
put "/groups/#{group.id}.json",
|
|
params: {
|
|
group: {
|
|
flair_bg_color: "FFF",
|
|
flair_color: "BBB",
|
|
flair_icon: "fa-circle-half-stroke",
|
|
name: "testing",
|
|
visibility_level: 1,
|
|
mentionable_level: 1,
|
|
messageable_level: 1,
|
|
default_notification_level: 1,
|
|
members_visibility_level: 1,
|
|
tracking_category_ids: [category.id],
|
|
tracking_tags: [tag.name],
|
|
},
|
|
update_existing_users: false,
|
|
}
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
group.reload
|
|
expect(group.flair_bg_color).to eq("FFF")
|
|
expect(group.flair_color).to eq("BBB")
|
|
expect(group.flair_icon).to eq("fa-circle-half-stroke")
|
|
expect(group.flair_url).to eq("fa-circle-half-stroke")
|
|
expect(group.name).to eq("admins")
|
|
expect(group.visibility_level).to eq(1)
|
|
expect(group.mentionable_level).to eq(1)
|
|
expect(group.messageable_level).to eq(1)
|
|
expect(group.default_notification_level).to eq(1)
|
|
expect(group.members_visibility_level).to eq(1)
|
|
expect(group.group_category_notification_defaults.first&.category).to eq(category)
|
|
expect(group.group_tag_notification_defaults.first&.tag).to eq(tag)
|
|
end
|
|
|
|
it "triggers a extensibility event" do
|
|
event =
|
|
DiscourseEvent
|
|
.track_events do
|
|
put "/groups/#{group.id}.json", params: { group: { flair_color: "BBB" } }
|
|
end
|
|
.last
|
|
|
|
expect(event[:event_name]).to eq(:group_updated)
|
|
expect(event[:params].first).to eq(group)
|
|
end
|
|
|
|
context "with user default notifications" do
|
|
it "should update default notification preference for existing users" do
|
|
group.update!(default_notification_level: NotificationLevels.all[:watching])
|
|
user1 = Fabricate(:user)
|
|
group.add(user1)
|
|
group.add(user2)
|
|
group_user1 = user1.group_users.first
|
|
group_user2 = user2.group_users.first
|
|
|
|
put "/groups/#{group.id}.json",
|
|
params: {
|
|
group: {
|
|
default_notification_level: NotificationLevels.all[:tracking],
|
|
},
|
|
}
|
|
|
|
expect(response.status).to eq(422)
|
|
expect(response.parsed_body["user_count"]).to eq(group.group_users.count)
|
|
expect(response.parsed_body["errors"].first).to eq(
|
|
I18n.t("groups.errors.update_existing_users_required", count: group.group_users.count),
|
|
)
|
|
expect(group_user1.reload.notification_level).to eq(NotificationLevels.all[:watching])
|
|
expect(group_user2.reload.notification_level).to eq(NotificationLevels.all[:watching])
|
|
|
|
group_user1.update!(notification_level: NotificationLevels.all[:regular])
|
|
|
|
put "/groups/#{group.id}.json",
|
|
params: {
|
|
group: {
|
|
default_notification_level: NotificationLevels.all[:tracking],
|
|
},
|
|
}
|
|
|
|
expect(response.status).to eq(422)
|
|
expect(response.parsed_body["user_count"]).to eq(group.group_users.count - 1)
|
|
expect(group_user1.reload.notification_level).to eq(NotificationLevels.all[:regular])
|
|
expect(group_user2.reload.notification_level).to eq(NotificationLevels.all[:watching])
|
|
|
|
put "/groups/#{group.id}.json",
|
|
params: {
|
|
group: {
|
|
default_notification_level: NotificationLevels.all[:tracking],
|
|
},
|
|
update_existing_users: true,
|
|
}
|
|
|
|
expect(response.status).to eq(200)
|
|
expect(response.parsed_body["success"]).to eq("OK")
|
|
expect(group_user1.reload.notification_level).to eq(NotificationLevels.all[:regular])
|
|
expect(group_user2.reload.notification_level).to eq(NotificationLevels.all[:tracking])
|
|
|
|
put "/groups/#{group.id}.json",
|
|
params: {
|
|
group: {
|
|
default_notification_level: NotificationLevels.all[:regular],
|
|
},
|
|
update_existing_users: false,
|
|
}
|
|
|
|
expect(response.status).to eq(200)
|
|
expect(response.parsed_body["success"]).to eq("OK")
|
|
expect(group_user1.reload.notification_level).to eq(NotificationLevels.all[:regular])
|
|
expect(group_user2.reload.notification_level).to eq(NotificationLevels.all[:tracking])
|
|
end
|
|
|
|
it "should update category & tag notification preferences for existing users" do
|
|
user1 = Fabricate(:user)
|
|
CategoryUser.create!(user: user1, category: category, notification_level: 4)
|
|
TagUser.create!(user: user1, tag: tag, notification_level: 4)
|
|
TagUser.create!(user: user2, tag: tag, notification_level: 4)
|
|
group.add(user1)
|
|
group.add(user2)
|
|
|
|
put "/groups/#{group.id}.json",
|
|
params: {
|
|
group: {
|
|
flair_color: "BBB",
|
|
name: "testing",
|
|
incoming_email: "test@mail.org",
|
|
primary_group: true,
|
|
automatic_membership_email_domains: "test.org",
|
|
grant_trust_level: 2,
|
|
visibility_level: 1,
|
|
members_visibility_level: 3,
|
|
tracking_category_ids: [category.id],
|
|
tracking_tags: [tag.name],
|
|
},
|
|
}
|
|
|
|
expect(response.status).to eq(422)
|
|
expect(response.parsed_body["user_count"]).to eq(group.group_users.count - 1)
|
|
|
|
put "/groups/#{group.id}.json",
|
|
params: {
|
|
group: {
|
|
flair_color: "BBB",
|
|
name: "testing",
|
|
incoming_email: "test@mail.org",
|
|
primary_group: true,
|
|
automatic_membership_email_domains: "test.org",
|
|
grant_trust_level: 2,
|
|
visibility_level: 1,
|
|
members_visibility_level: 3,
|
|
tracking_category_ids: [category.id],
|
|
tracking_tags: [tag.name],
|
|
},
|
|
update_existing_users: true,
|
|
}
|
|
|
|
expect(response.status).to eq(200)
|
|
expect(response.parsed_body["success"]).to eq("OK")
|
|
|
|
put "/groups/#{group.id}.json",
|
|
params: {
|
|
group: {
|
|
flair_color: "BBB",
|
|
name: "testing",
|
|
incoming_email: "test@mail.org",
|
|
primary_group: true,
|
|
automatic_membership_email_domains: "test.org",
|
|
grant_trust_level: 2,
|
|
visibility_level: 1,
|
|
members_visibility_level: 3,
|
|
watching_category_ids: [category.id],
|
|
tracking_tags: [tag.name],
|
|
},
|
|
update_existing_users: true,
|
|
}
|
|
|
|
expect(response.status).to eq(200)
|
|
expect(response.parsed_body["success"]).to eq("OK")
|
|
expect(
|
|
CategoryUser.exists?(user: user2, category: category, notification_level: 3),
|
|
).to be_truthy
|
|
end
|
|
end
|
|
end
|
|
|
|
context "when user is a site moderator" do
|
|
before do
|
|
SiteSetting.moderators_manage_groups = true
|
|
sign_in(moderator)
|
|
end
|
|
|
|
it "should not be able to update the group if the SiteSetting is false" do
|
|
SiteSetting.moderators_manage_groups = false
|
|
|
|
put "/groups/#{group.id}.json", params: { group: { name: "testing" } }
|
|
|
|
expect(response.status).to eq(403)
|
|
end
|
|
|
|
it "should not be able to update a group it cannot see" do
|
|
group.update!(visibility_level: Group.visibility_levels[:owners])
|
|
|
|
put "/groups/#{group.id}.json", params: { group: { name: "testing" } }
|
|
|
|
expect(response.status).to eq(403)
|
|
end
|
|
|
|
it "should be able to update the group" do
|
|
put "/groups/#{group.id}.json",
|
|
params: {
|
|
group: {
|
|
flair_color: "BBB",
|
|
name: "testing",
|
|
incoming_email: "test@mail.org",
|
|
primary_group: true,
|
|
automatic_membership_email_domains: "test.org",
|
|
grant_trust_level: 2,
|
|
visibility_level: 1,
|
|
members_visibility_level: 3,
|
|
tracking_category_ids: [category.id],
|
|
tracking_tags: [tag.name],
|
|
},
|
|
update_existing_users: false,
|
|
}
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
group.reload
|
|
expect(group.flair_color).to eq("BBB")
|
|
expect(group.name).to eq("testing")
|
|
expect(group.incoming_email).to eq("test@mail.org")
|
|
expect(group.primary_group).to eq(true)
|
|
expect(group.visibility_level).to eq(1)
|
|
expect(group.members_visibility_level).to eq(3)
|
|
expect(group.automatic_membership_email_domains).to eq("test.org")
|
|
expect(group.grant_trust_level).to eq(2)
|
|
expect(group.group_category_notification_defaults.first&.category).to eq(category)
|
|
expect(group.group_tag_notification_defaults.first&.tag).to eq(tag)
|
|
|
|
expect(Jobs::AutomaticGroupMembership.jobs.first["args"].first["group_id"]).to eq(group.id)
|
|
end
|
|
|
|
it "keeps SMTP email settings unchanged for moderators", :aggregate_failures do
|
|
SiteSetting.enable_smtp = true
|
|
group.update!(
|
|
allow_unknown_sender_topic_replies: false,
|
|
email_from_alias: "group-alias@example.com",
|
|
email_password: "secret_smtp_pass",
|
|
email_username: "group@example.com",
|
|
smtp_enabled: true,
|
|
smtp_port: 587,
|
|
smtp_server: "smtp.example.com",
|
|
smtp_ssl_mode: Group.smtp_ssl_modes[:starttls],
|
|
)
|
|
|
|
put "/groups/#{group.id}.json",
|
|
params: {
|
|
group: {
|
|
allow_unknown_sender_topic_replies: true,
|
|
email_from_alias: "evil-alias@example.com",
|
|
email_password: "attacker_controlled_pass",
|
|
email_username: "attacker@example.com",
|
|
flair_color: "BBB",
|
|
smtp_enabled: false,
|
|
smtp_port: 25,
|
|
smtp_server: "evil.attacker.example.com",
|
|
smtp_ssl_mode: Group.smtp_ssl_modes[:none],
|
|
},
|
|
update_existing_users: false,
|
|
}
|
|
|
|
expect(response.status).to eq(200)
|
|
expect(response.parsed_body["success"]).to eq("OK")
|
|
|
|
group.reload
|
|
expect(group.flair_color).to eq("BBB")
|
|
expect(group.smtp_server).to eq("smtp.example.com")
|
|
expect(group.smtp_port).to eq(587)
|
|
expect(group.smtp_ssl_mode).to eq(Group.smtp_ssl_modes[:starttls])
|
|
expect(group.smtp_enabled).to eq(true)
|
|
expect(group.email_username).to eq("group@example.com")
|
|
expect(group.email_password).to eq("secret_smtp_pass")
|
|
expect(group.email_from_alias).to eq("group-alias@example.com")
|
|
expect(group.allow_unknown_sender_topic_replies).to eq(false)
|
|
end
|
|
|
|
it "should be able to update an automatic group" do
|
|
group = Group.find(Group::AUTO_GROUPS[:trust_level_4])
|
|
|
|
group.update!(mentionable_level: 2, messageable_level: 2, default_notification_level: 2)
|
|
|
|
put "/groups/#{group.id}.json",
|
|
params: {
|
|
group: {
|
|
flair_bg_color: "FFF",
|
|
flair_color: "BBB",
|
|
flair_icon: "fa-circle-half-stroke",
|
|
mentionable_level: 1,
|
|
messageable_level: 1,
|
|
default_notification_level: 1,
|
|
},
|
|
}
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
group.reload
|
|
expect(group.flair_bg_color).to eq("FFF")
|
|
expect(group.flair_color).to eq("BBB")
|
|
expect(group.flair_icon).to eq("fa-circle-half-stroke")
|
|
expect(group.flair_url).to eq("fa-circle-half-stroke")
|
|
expect(group.name).to eq("trust_level_4")
|
|
expect(group.mentionable_level).to eq(1)
|
|
expect(group.messageable_level).to eq(1)
|
|
expect(group.default_notification_level).to eq(1)
|
|
end
|
|
|
|
it "triggers a extensibility event" do
|
|
event =
|
|
DiscourseEvent
|
|
.track_events do
|
|
put "/groups/#{group.id}.json", params: { group: { flair_color: "BBB" } }
|
|
end
|
|
.last
|
|
|
|
expect(event[:event_name]).to eq(:group_updated)
|
|
expect(event[:params].first).to eq(group)
|
|
end
|
|
end
|
|
|
|
context "when user is not a group owner or admin" do
|
|
it "should not be able to update the group" do
|
|
sign_in(user)
|
|
|
|
put "/groups/#{group.id}.json", params: { group: { name: "testing" } }
|
|
|
|
expect(response.status).to eq(403)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "#members" do
|
|
let(:user1) do
|
|
Fabricate(:user, last_seen_at: Time.zone.now, last_posted_at: 1.day.ago, email: "b@test.org")
|
|
end
|
|
|
|
let(:user2) do
|
|
Fabricate(:user, last_seen_at: 1.day.ago, last_posted_at: Time.zone.now, email: "a@test.org")
|
|
end
|
|
|
|
fab!(:user3) { Fabricate(:user, last_seen_at: nil, last_posted_at: nil, email: "c@test.org") }
|
|
|
|
fab!(:bot)
|
|
let(:group) { Fabricate(:group, users: [user1, user2, user3, bot]) }
|
|
|
|
it "should allow members to be sorted by" do
|
|
get "/groups/#{group.name}/members.json", params: { order: "last_seen_at" }
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
members = response.parsed_body["members"]
|
|
|
|
expect(members.map { |m| m["id"] }).to eq([user1.id, user2.id, user3.id])
|
|
|
|
get "/groups/#{group.name}/members.json", params: { order: "last_seen_at", asc: true }
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
members = response.parsed_body["members"]
|
|
|
|
expect(members.map { |m| m["id"] }).to eq([user2.id, user1.id, user3.id])
|
|
|
|
get "/groups/#{group.name}/members.json", params: { order: "last_posted_at" }
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
members = response.parsed_body["members"]
|
|
|
|
expect(members.map { |m| m["id"] }).to eq([user2.id, user1.id, user3.id])
|
|
end
|
|
|
|
it "should not allow members to be sorted by columns that are not allowed" do
|
|
get "/groups/#{group.name}/members.json", params: { order: "email" }
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
members = response.parsed_body["members"]
|
|
|
|
expect(members.map { |m| m["id"] }).to contain_exactly(user1.id, user2.id, user3.id)
|
|
end
|
|
|
|
it "can show group requests" do
|
|
sign_in(admin)
|
|
|
|
user4 = Fabricate(:user)
|
|
request4 = Fabricate(:group_request, user: user4, group: group)
|
|
|
|
get "/groups/#{group.name}/members.json", params: { requesters: true }
|
|
|
|
members = response.parsed_body["members"]
|
|
expect(members.length).to eq(1)
|
|
expect(members.first["username"]).to eq(user4.username)
|
|
expect(members.first["reason"]).to eq(request4.reason)
|
|
end
|
|
|
|
describe "filterable" do
|
|
describe "as a normal user" do
|
|
it "should not allow members to be filterable by email" do
|
|
email = "uniquetest@discourse.org"
|
|
user1.update!(email: email)
|
|
|
|
get "/groups/#{group.name}/members.json", params: { filter: email }
|
|
|
|
expect(response.status).to eq(200)
|
|
members = response.parsed_body["members"]
|
|
expect(members).to eq([])
|
|
end
|
|
end
|
|
|
|
describe "as an admin" do
|
|
before { sign_in(admin) }
|
|
|
|
it "should allow members to be filterable by username" do
|
|
email = "uniquetest@discourse.org"
|
|
user1.update!(email: email)
|
|
|
|
{
|
|
email.upcase => [user1.id],
|
|
"QUEtes" => [user1.id],
|
|
"#{user1.email},#{user2.email}" => [user1.id, user2.id],
|
|
}.each do |filter, ids|
|
|
get "/groups/#{group.name}/members.json", params: { filter: filter }
|
|
|
|
expect(response.status).to eq(200)
|
|
members = response.parsed_body["members"]
|
|
expect(members.map { |m| m["id"] }).to contain_exactly(*ids)
|
|
end
|
|
end
|
|
|
|
it "should allow members to be filterable by email" do
|
|
username = "uniquetest"
|
|
user1.update!(username: username)
|
|
|
|
[username.upcase, "QUEtes"].each do |filter|
|
|
get "/groups/#{group.name}/members.json", params: { filter: filter }
|
|
|
|
expect(response.status).to eq(200)
|
|
members = response.parsed_body["members"]
|
|
expect(members.map { |m| m["id"] }).to contain_exactly(user1.id)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "#edit" do
|
|
fab!(:group)
|
|
|
|
context "when user is not signed in" do
|
|
it "should be forbidden" do
|
|
put "/groups/#{group.id}/members.json", params: { usernames: "bob" }
|
|
expect(response).to be_forbidden
|
|
|
|
delete "/groups/#{group.id}/members.json", params: { username: "bob" }
|
|
expect(response).to be_forbidden
|
|
end
|
|
|
|
context "with public group" do
|
|
it "should be forbidden" do
|
|
group.update!(public_admission: true, public_exit: true)
|
|
|
|
put "/groups/#{group.id}/members.json", params: { usernames: "bob" }
|
|
expect(response.status).to eq(403)
|
|
|
|
delete "/groups/#{group.id}/members.json", params: { username: "bob" }
|
|
expect(response.status).to eq(403)
|
|
end
|
|
end
|
|
end
|
|
|
|
context "when user is not an owner of the group" do
|
|
before { sign_in(user) }
|
|
|
|
it "refuses membership changes to unauthorized users" do
|
|
put "/groups/#{group.id}/members.json", params: { usernames: "bob" }
|
|
expect(response).to be_forbidden
|
|
|
|
delete "/groups/#{group.id}/members.json", params: { username: "bob" }
|
|
expect(response).to be_forbidden
|
|
end
|
|
end
|
|
|
|
context "when user is an admin" do
|
|
fab!(:group) { Fabricate(:group, users: [admin], automatic: true) }
|
|
|
|
before { sign_in(admin) }
|
|
|
|
it "cannot add members to automatic groups" do
|
|
put "/groups/#{group.id}/members.json", params: { usernames: "bob" }
|
|
expect(response).to be_forbidden
|
|
|
|
delete "/groups/#{group.id}/members.json", params: { username: "bob" }
|
|
expect(response).to be_forbidden
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "membership edits" do
|
|
describe "#add_members" do
|
|
before { sign_in(admin) }
|
|
|
|
it "can make incremental adds" do
|
|
expect do
|
|
put "/groups/#{group.id}/members.json", params: { usernames: user2.username }
|
|
end.to change { group.users.count }.by(1)
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
group_history = GroupHistory.last
|
|
|
|
expect(group_history.action).to eq(GroupHistory.actions[:add_user_to_group])
|
|
expect(group_history.acting_user).to eq(admin)
|
|
expect(group_history.target_user).to eq(user2)
|
|
end
|
|
|
|
it "cannot add members to automatic groups" do
|
|
group.update!(automatic: true)
|
|
|
|
put "/groups/#{group.id}/members.json", params: { usernames: "l77t" }
|
|
expect(response.status).to eq(403)
|
|
end
|
|
|
|
it "does not notify users when the param is not present" do
|
|
expect {
|
|
put "/groups/#{group.id}/members.json", params: { usernames: user2.username }
|
|
}.not_to change { Topic.where(archetype: "private_message").count }
|
|
|
|
expect(response.status).to eq(200)
|
|
end
|
|
|
|
it "notifies users when the param is present" do
|
|
Jobs.run_immediately!
|
|
|
|
expect {
|
|
put "/groups/#{group.id}/members.json",
|
|
params: {
|
|
usernames: user2.username,
|
|
notify_users: true,
|
|
}
|
|
}.to change { Topic.where(archetype: "private_message").count }.by(1)
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
expect(Topic.last.topic_users.map(&:user_id)).to include(
|
|
Discourse::SYSTEM_USER_ID,
|
|
user2.id,
|
|
)
|
|
end
|
|
|
|
it "does not add users without sufficient permission" do
|
|
group.add_owner(user)
|
|
sign_in(user)
|
|
|
|
put "/groups/#{group.id}/members.json", params: { usernames: other_user.username }
|
|
expect(response.status).to eq(200)
|
|
end
|
|
|
|
it "returns a clear error when group owner without invite permission submits emails" do
|
|
group.add_owner(user)
|
|
sign_in(user)
|
|
|
|
put "/groups/#{group.id}/members.json", params: { emails: "test@example.com" }
|
|
expect(response.status).to eq(422)
|
|
expect(response.parsed_body["errors"].first).to include("Only usernames")
|
|
end
|
|
|
|
it "rejects emails even when valid usernames are also submitted by owner without invite permission" do
|
|
group.add_owner(user)
|
|
sign_in(user)
|
|
|
|
expect {
|
|
put "/groups/#{group.id}/members.json",
|
|
params: {
|
|
usernames: other_user.username,
|
|
emails: "nonexistent@example.com",
|
|
}
|
|
}.not_to change { group.users.count }
|
|
|
|
expect(response.status).to eq(422)
|
|
expect(response.parsed_body["errors"].first).to include("Only usernames")
|
|
end
|
|
|
|
context "when is able to add several members to a group" do
|
|
fab!(:user1, :user)
|
|
fab!(:user2) { Fabricate(:user, username: "UsEr2") }
|
|
|
|
it "adds by username" do
|
|
expect do
|
|
put "/groups/#{group.id}/members.json",
|
|
params: {
|
|
usernames: [user1.username, user2.username.upcase].join(","),
|
|
}
|
|
end.to change { group.users.count }.by(2)
|
|
|
|
expect(response.status).to eq(200)
|
|
end
|
|
|
|
it "adds by id" do
|
|
expect do
|
|
put "/groups/#{group.id}/members.json",
|
|
params: {
|
|
user_ids: [user1.id, user2.id].join(","),
|
|
}
|
|
end.to change { group.users.count }.by(2)
|
|
|
|
expect(response.status).to eq(200)
|
|
end
|
|
|
|
it "adds by email" do
|
|
expect do
|
|
put "/groups/#{group.id}/members.json",
|
|
params: {
|
|
user_emails: [user1.email, user2.email].join(","),
|
|
}
|
|
end.to change { group.users.count }.by(2)
|
|
|
|
expect(response.status).to eq(200)
|
|
end
|
|
|
|
it "adds missing users even if some exists" do
|
|
user2.update!(username: "alice")
|
|
user3 = Fabricate(:user, username: "bob")
|
|
[user2, user3].each { |user| group.add(user) }
|
|
|
|
expect do
|
|
put "/groups/#{group.id}/members.json",
|
|
params: {
|
|
user_emails: [user1.email, user2.email, user3.email].join(","),
|
|
}
|
|
end.to change { group.users.count }.by(1)
|
|
|
|
expect(response.status).to eq(200)
|
|
end
|
|
|
|
it "sends invites to new users and ignores existing users" do
|
|
user1.update!(username: "john")
|
|
user2.update!(username: "alice")
|
|
[user1, user2].each { |user| group.add(user) }
|
|
emails = %w[something@gmail.com anotherone@yahoo.com]
|
|
put "/groups/#{group.id}/members.json",
|
|
params: {
|
|
user_emails: [user1.email, user2.email].join(","),
|
|
emails: emails.join(","),
|
|
}
|
|
|
|
expect(response.status).to eq(200)
|
|
expect(response.parsed_body["emails"]).to eq(emails)
|
|
|
|
emails.each do |email|
|
|
invite = Invite.find_by(email: email)
|
|
expect(invite.groups).to eq([group])
|
|
end
|
|
end
|
|
|
|
it "sends invite emails when notify_users is true" do
|
|
expect_enqueued_with(job: :invite_email) do
|
|
put "/groups/#{group.id}/members.json",
|
|
params: {
|
|
emails: "something@gmail.com",
|
|
notify_users: true,
|
|
}
|
|
expect(response.status).to eq(200)
|
|
end
|
|
end
|
|
|
|
it "does not send invite emails when notify_users is false" do
|
|
expect_not_enqueued_with(job: :invite_email) do
|
|
put "/groups/#{group.id}/members.json",
|
|
params: {
|
|
emails: "something@gmail.com",
|
|
notify_users: false,
|
|
}
|
|
expect(response.status).to eq(200)
|
|
end
|
|
end
|
|
|
|
it "sends invite emails when neither notify_users nor skip_email is provided" do
|
|
expect_enqueued_with(job: :invite_email) do
|
|
put "/groups/#{group.id}/members.json", params: { emails: "something@gmail.com" }
|
|
expect(response.status).to eq(200)
|
|
end
|
|
end
|
|
|
|
it "does not send invite emails when skip_email is true" do
|
|
expect_not_enqueued_with(job: :invite_email) do
|
|
put "/groups/#{group.id}/members.json",
|
|
params: {
|
|
emails: "something@gmail.com",
|
|
skip_email: true,
|
|
}
|
|
expect(response.status).to eq(200)
|
|
end
|
|
end
|
|
|
|
it "displays warning when all members already exists" do
|
|
user1.update!(username: "john")
|
|
user2.update!(username: "alice")
|
|
user3 = Fabricate(:user, username: "bob")
|
|
[user1, user2, user3].each { |user| group.add(user) }
|
|
|
|
expect do
|
|
put "/groups/#{group.id}/members.json",
|
|
params: {
|
|
user_emails: [user1.email, user2.email, user3.email].join(","),
|
|
}
|
|
end.not_to change { group.users.count }
|
|
|
|
expect(response.status).to eq(422)
|
|
|
|
expect(response.parsed_body["errors"]).to include(
|
|
I18n.t("groups.errors.member_already_exist", username: "alice, bob, john", count: 3),
|
|
)
|
|
end
|
|
|
|
it "display error when try to add to many users at once" do
|
|
stub_const(GroupsController, "ADD_MEMBERS_LIMIT", 1) do
|
|
expect do
|
|
put "/groups/#{group.id}/members.json",
|
|
params: {
|
|
user_emails: [user1.email, user2.email].join(","),
|
|
}
|
|
end.not_to change { group.reload.users.count }
|
|
|
|
expect(response.status).to eq(422)
|
|
|
|
expect(response.parsed_body["errors"]).to include(
|
|
I18n.t("groups.errors.adding_too_many_users", count: 1),
|
|
)
|
|
end
|
|
end
|
|
end
|
|
|
|
it "returns 422 if member already exists" do
|
|
put "/groups/#{group.id}/members.json", params: { usernames: user.username }
|
|
|
|
expect(response.status).to eq(422)
|
|
|
|
expect(response.parsed_body["errors"]).to include(
|
|
I18n.t("groups.errors.member_already_exist", username: user.username, count: 1),
|
|
)
|
|
end
|
|
|
|
it "returns 400 if member is not found" do
|
|
[
|
|
{ usernames: "some thing" },
|
|
{ user_ids: "-5,-6" },
|
|
{ user_emails: "some@test.org" },
|
|
].each do |params|
|
|
put "/groups/#{group.id}/members.json", params: params
|
|
|
|
expect(response.status).to eq(400)
|
|
|
|
body = response.parsed_body
|
|
|
|
expect(body["error_type"]).to eq("invalid_parameters")
|
|
end
|
|
end
|
|
|
|
it "return a 400 if no user or emails are present" do
|
|
[
|
|
{ usernames: "nouserwiththisusername", emails: "" },
|
|
{ usernames: "", emails: "" },
|
|
].each do |params|
|
|
put "/groups/#{group.id}/members.json", params: params
|
|
expect(response.status).to eq(400)
|
|
body = response.parsed_body
|
|
|
|
expect(body["error_type"]).to eq("invalid_parameters")
|
|
end
|
|
end
|
|
|
|
it "will send invites to each email with group_id set" do
|
|
emails = %w[something@gmail.com anotherone@yahoo.com]
|
|
put "/groups/#{group.id}/members.json", params: { emails: emails.join(",") }
|
|
|
|
expect(response.status).to eq(200)
|
|
body = response.parsed_body
|
|
|
|
expect(body["emails"]).to eq(emails)
|
|
|
|
emails.each do |email|
|
|
invite = Invite.find_by(email: email)
|
|
expect(invite.groups).to eq([group])
|
|
end
|
|
end
|
|
|
|
it "adds known users by email when DiscourseConnect is enabled" do
|
|
SiteSetting.discourse_connect_url = "https://www.example.com/sso"
|
|
SiteSetting.discourse_connect_secret = "x" * 10
|
|
SiteSetting.enable_discourse_connect = true
|
|
|
|
expect do
|
|
put "/groups/#{group.id}/members.json", params: { emails: other_user.email }
|
|
end.to change { group.users.count }.by(1)
|
|
|
|
expect(response.status).to eq(200)
|
|
end
|
|
|
|
it "will find users by email, and invite the correct user" do
|
|
new_user = Fabricate(:user)
|
|
expect(new_user.group_ids.include?(group.id)).to eq(false)
|
|
|
|
put "/groups/#{group.id}/members.json", params: { emails: new_user.email }
|
|
|
|
expect(new_user.reload.group_ids.include?(group.id)).to eq(true)
|
|
end
|
|
|
|
it "will invite the user if their username and email are both invited" do
|
|
new_user = Fabricate(:user)
|
|
put "/groups/#{group.id}/members.json",
|
|
params: {
|
|
usernames: new_user.username,
|
|
emails: new_user.email,
|
|
}
|
|
expect(response.status).to eq(200)
|
|
expect(new_user.reload.group_ids.include?(group.id)).to eq(true)
|
|
end
|
|
|
|
context "with public group" do
|
|
before { group.update!(public_admission: true, public_exit: true) }
|
|
|
|
context "when admin" do
|
|
it "can make incremental adds" do
|
|
expect do
|
|
put "/groups/#{group.id}/members.json", params: { usernames: other_user.username }
|
|
end.to change { group.users.count }.by(1)
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
group_history = GroupHistory.last
|
|
|
|
expect(group_history.action).to eq(GroupHistory.actions[:add_user_to_group])
|
|
expect(group_history.acting_user).to eq(admin)
|
|
expect(group_history.target_user).to eq(other_user)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "#add_owners" do
|
|
context "when logged in as an admin" do
|
|
before { sign_in(admin) }
|
|
|
|
it "should work" do
|
|
put "/groups/#{group.id}/owners.json",
|
|
params: {
|
|
usernames: [user.username, admin.username].join(","),
|
|
}
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
response_body = response.parsed_body
|
|
|
|
expect(response_body["usernames"]).to contain_exactly(user.username, admin.username)
|
|
|
|
expect(group.group_users.where(owner: true).map(&:user)).to contain_exactly(user, admin)
|
|
end
|
|
|
|
it "returns not-found error when there is no group" do
|
|
group.destroy!
|
|
|
|
put "/groups/#{group.id}/owners.json", params: { usernames: user.username }
|
|
|
|
expect(response.status).to eq(404)
|
|
end
|
|
|
|
it "does not allow adding owners to an automatic group" do
|
|
group.update!(automatic: true)
|
|
|
|
expect do
|
|
put "/groups/#{group.id}/owners.json", params: { usernames: user.username }
|
|
end.to_not change { group.group_users.count }
|
|
|
|
expect(response.status).to eq(422)
|
|
expect(response.parsed_body["errors"]).to eq(
|
|
[I18n.t("groups.errors.can_not_modify_automatic")],
|
|
)
|
|
end
|
|
|
|
it "does not notify users when the param is not present" do
|
|
put "/groups/#{group.id}/owners.json", params: { usernames: user.username }
|
|
expect(response.status).to eq(200)
|
|
|
|
topic =
|
|
Topic.find_by(
|
|
title:
|
|
I18n.t(
|
|
"system_messages.user_added_to_group_as_owner.subject_template",
|
|
group_name: group.name,
|
|
),
|
|
archetype: "private_message",
|
|
)
|
|
expect(topic.nil?).to eq(true)
|
|
end
|
|
|
|
it "notifies users when the param is present" do
|
|
put "/groups/#{group.id}/owners.json",
|
|
params: {
|
|
usernames: user.username,
|
|
notify_users: true,
|
|
}
|
|
expect(response.status).to eq(200)
|
|
|
|
topic =
|
|
Topic.find_by(
|
|
title:
|
|
I18n.t(
|
|
"system_messages.user_added_to_group_as_owner.subject_template",
|
|
group_name: group.name,
|
|
),
|
|
archetype: "private_message",
|
|
)
|
|
expect(topic.nil?).to eq(false)
|
|
expect(topic.topic_users.map(&:user_id)).to include(-1, user.id)
|
|
end
|
|
end
|
|
|
|
context "when logged in as a moderator" do
|
|
before { sign_in(moderator) }
|
|
|
|
context "with moderators_manage_groups enabled" do
|
|
before { SiteSetting.moderators_manage_groups = true }
|
|
|
|
it "adds owners" do
|
|
put "/groups/#{group.id}/owners.json",
|
|
params: {
|
|
usernames: [user.username, admin.username, moderator.username].join(","),
|
|
}
|
|
|
|
response_body = response.parsed_body
|
|
|
|
expect(response.status).to eq(200)
|
|
expect(response_body["usernames"]).to contain_exactly(
|
|
user.username,
|
|
admin.username,
|
|
moderator.username,
|
|
)
|
|
expect(group.group_users.where(owner: true).map(&:user)).to contain_exactly(
|
|
user,
|
|
admin,
|
|
moderator,
|
|
)
|
|
end
|
|
end
|
|
|
|
context "with moderators_manage_groups disabled" do
|
|
before { SiteSetting.moderators_manage_groups = false }
|
|
|
|
it "prevents adding of owners with a 403 response" do
|
|
put "/groups/#{group.id}/owners.json",
|
|
params: {
|
|
usernames: [user.username, admin.username, moderator.username].join(","),
|
|
}
|
|
|
|
expect(response.status).to eq(403)
|
|
expect(response.parsed_body["errors"]).to include(I18n.t("invalid_access"))
|
|
expect(group.group_users.where(owner: true).map(&:user)).to be_empty
|
|
end
|
|
end
|
|
end
|
|
|
|
context "when logged in as a non-owner" do
|
|
before { sign_in(user) }
|
|
|
|
it "prevents adding of owners with a 403 response" do
|
|
put "/groups/#{group.id}/owners.json",
|
|
params: {
|
|
usernames: [user.username, admin.username].join(","),
|
|
}
|
|
|
|
expect(response.status).to eq(403)
|
|
expect(response.parsed_body["errors"]).to include(I18n.t("invalid_access"))
|
|
expect(group.group_users.where(owner: true).map(&:user)).to be_empty
|
|
end
|
|
end
|
|
|
|
context "when logged in as an owner" do
|
|
before { sign_in(user) }
|
|
|
|
it "allows adding new owners" do
|
|
group.add_owner(user)
|
|
|
|
put "/groups/#{group.id}/owners.json",
|
|
params: {
|
|
usernames: [user.username, admin.username].join(","),
|
|
}
|
|
|
|
expect(response.status).to eq(200)
|
|
expect(response.parsed_body["usernames"]).to contain_exactly(
|
|
user.username,
|
|
admin.username,
|
|
)
|
|
expect(group.group_users.where(owner: true).map(&:user)).to contain_exactly(user, admin)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "#join" do
|
|
let(:public_group) { Fabricate(:public_group) }
|
|
|
|
it "should allow a user to join a public group" do
|
|
sign_in(user)
|
|
|
|
expect do put "/groups/#{public_group.id}/join.json" end.to change {
|
|
public_group.users.count
|
|
}.by(1)
|
|
|
|
expect(response.status).to eq(204)
|
|
end
|
|
|
|
it "should not allow a user to join a public group they cannot see" do
|
|
hidden_group =
|
|
Fabricate(
|
|
:public_group,
|
|
visibility_level: Group.visibility_levels[:owners],
|
|
grant_trust_level: TrustLevel[4],
|
|
)
|
|
hidden_group.add_owner(admin)
|
|
user.update!(trust_level: TrustLevel[0])
|
|
sign_in(user)
|
|
|
|
expect(user.guardian.can_see_group?(hidden_group)).to eq(false)
|
|
|
|
put "/groups/#{hidden_group.id}/join.json"
|
|
|
|
expect(response.status).to eq(404)
|
|
expect(response.parsed_body["error_type"]).to eq("not_found")
|
|
expect(GroupUser.exists?(group_id: hidden_group.id, user_id: user.id)).to eq(false)
|
|
expect(user.reload.trust_level).to eq(TrustLevel[0])
|
|
end
|
|
|
|
it "should not allow a user to join a nonpublic group" do
|
|
sign_in(user)
|
|
|
|
expect do put "/groups/#{group.id}/join.json" end.not_to change { group.users.count }
|
|
|
|
expect(response).to be_forbidden
|
|
end
|
|
|
|
it "should not allow an anonymous user to call the join method" do
|
|
expect do put "/groups/#{group.id}/join.json" end.not_to change { group.users.count }
|
|
|
|
expect(response).to be_forbidden
|
|
end
|
|
|
|
it "the join method is idempotent" do
|
|
sign_in(user)
|
|
|
|
expect do put "/groups/#{public_group.id}/join.json" end.to change {
|
|
public_group.users.count
|
|
}.by(1)
|
|
expect(response.status).to eq(204)
|
|
|
|
expect do put "/groups/#{public_group.id}/join.json" end.not_to change {
|
|
public_group.users.count
|
|
}
|
|
expect(response.status).to eq(204)
|
|
|
|
expect do put "/groups/#{public_group.id}/join.json" end.not_to change {
|
|
public_group.users.count
|
|
}
|
|
expect(response.status).to eq(204)
|
|
end
|
|
end
|
|
|
|
describe "#remove_member" do
|
|
before { sign_in(admin) }
|
|
|
|
it "cannot remove members from automatic groups" do
|
|
group.update!(automatic: true)
|
|
|
|
delete "/groups/#{group.id}/members.json", params: { user_id: 42 }
|
|
expect(response.status).to eq(403)
|
|
end
|
|
|
|
it "raises an error if user to be removed is not found" do
|
|
delete "/groups/#{group.id}/members.json", params: { user_id: -10 }
|
|
expect(response.status).to eq(400)
|
|
end
|
|
|
|
it "returns skipped_usernames response body when removing a valid user but is not a member of that group" do
|
|
delete "/groups/#{group.id}/members.json", params: { user_id: Discourse::SYSTEM_USER_ID }
|
|
|
|
response_body = response.parsed_body
|
|
expect(response.status).to eq(200)
|
|
expect(response_body["usernames"]).to eq([])
|
|
expect(response_body["skipped_usernames"].first).to eq("system")
|
|
end
|
|
|
|
context "when is able to remove a member" do
|
|
it "removes by id" do
|
|
expect do
|
|
delete "/groups/#{group.id}/members.json", params: { user_id: user.id }
|
|
end.to change { group.users.count }.by(-1)
|
|
|
|
expect(response.status).to eq(200)
|
|
end
|
|
|
|
it "removes by id with integer in json" do
|
|
expect do
|
|
headers = { CONTENT_TYPE: "application/json" }
|
|
delete "/groups/#{group.id}/members.json",
|
|
params: "{\"user_id\":#{user.id}}",
|
|
headers: headers
|
|
end.to change { group.users.count }.by(-1)
|
|
|
|
expect(response.status).to eq(200)
|
|
end
|
|
|
|
it "removes by username" do
|
|
expect do
|
|
delete "/groups/#{group.id}/members.json", params: { username: user.username }
|
|
end.to change { group.users.count }.by(-1)
|
|
|
|
expect(response.status).to eq(200)
|
|
end
|
|
|
|
it "removes user.primary_group_id when user is removed from group" do
|
|
user.update!(primary_group_id: group.id)
|
|
|
|
delete "/groups/#{group.id}/members.json", params: { user_id: user.id }
|
|
|
|
expect(user.reload.primary_group_id).to eq(nil)
|
|
end
|
|
|
|
it "removes by user_email" do
|
|
expect do
|
|
delete "/groups/#{group.id}/members.json", params: { user_email: user.email }
|
|
end.to change { group.users.count }.by(-1)
|
|
|
|
expect(response.status).to eq(200)
|
|
end
|
|
|
|
context "with public group" do
|
|
let(:group) { Fabricate(:public_group, users: [other_user]) }
|
|
|
|
context "when admin" do
|
|
it "removes by username" do
|
|
expect do
|
|
delete "/groups/#{group.id}/members.json", params: { username: other_user.username }
|
|
end.to change { group.users.count }.by(-1)
|
|
|
|
expect(response.status).to eq(200)
|
|
end
|
|
end
|
|
|
|
it "should not allow a underprivileged user to leave a group for another user" do
|
|
sign_in(user)
|
|
|
|
delete "/groups/#{group.id}/members.json", params: { username: other_user.username }
|
|
|
|
expect(response).to be_forbidden
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "#remove_members" do
|
|
context "when is able to remove several members from a group" do
|
|
fab!(:user1, :user)
|
|
fab!(:user2) { Fabricate(:user, username: "UsEr2") }
|
|
let(:group1) { Fabricate(:group, users: [user1, user2]) }
|
|
|
|
it "removes by username" do
|
|
expect do
|
|
delete "/groups/#{group1.id}/members.json",
|
|
params: {
|
|
usernames: [user1.username, user2.username.upcase].join(","),
|
|
}
|
|
end.to change { group1.users.count }.by(-2)
|
|
expect(response.status).to eq(200)
|
|
end
|
|
|
|
it "removes by id" do
|
|
expect do
|
|
delete "/groups/#{group1.id}/members.json",
|
|
params: {
|
|
user_ids: [user1.id, user2.id].join(","),
|
|
}
|
|
end.to change { group1.users.count }.by(-2)
|
|
|
|
expect(response.status).to eq(200)
|
|
end
|
|
|
|
it "removes by id with integer in json" do
|
|
expect do
|
|
headers = { CONTENT_TYPE: "application/json" }
|
|
delete "/groups/#{group1.id}/members.json",
|
|
params: "{\"user_ids\":#{user1.id}}",
|
|
headers: headers
|
|
end.to change { group1.users.count }.by(-1)
|
|
|
|
expect(response.status).to eq(200)
|
|
end
|
|
|
|
it "removes by email" do
|
|
expect do
|
|
delete "/groups/#{group1.id}/members.json",
|
|
params: {
|
|
user_emails: [user1.email, user2.email].join(","),
|
|
}
|
|
end.to change { group1.users.count }.by(-2)
|
|
|
|
expect(response.status).to eq(200)
|
|
end
|
|
|
|
it "only removes users in that group" do
|
|
delete "/groups/#{group1.id}/members.json",
|
|
params: {
|
|
usernames: [user.username, user2.username].join(","),
|
|
}
|
|
|
|
response_body = response.parsed_body
|
|
expect(response.status).to eq(200)
|
|
expect(response_body["usernames"].first).to eq(user2.username)
|
|
expect(response_body["skipped_usernames"].first).to eq(user.username)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "#leave" do
|
|
let(:group_with_public_exit) { Fabricate(:group, public_exit: true, users: [user]) }
|
|
|
|
it "should allow a user to leave a group with public exit" do
|
|
sign_in(user)
|
|
|
|
expect do delete "/groups/#{group_with_public_exit.id}/leave.json" end.to change {
|
|
group_with_public_exit.users.count
|
|
}.by(-1)
|
|
|
|
expect(response.status).to eq(204)
|
|
end
|
|
|
|
it "should not allow a user to leave a group without public exit" do
|
|
sign_in(user)
|
|
|
|
expect do delete "/groups/#{group.id}/leave.json" end.not_to change { group.users.count }
|
|
|
|
expect(response).to be_forbidden
|
|
end
|
|
|
|
it "should not allow an anonymous user to call the leave method" do
|
|
expect do delete "/groups/#{group_with_public_exit.id}/leave.json" end.not_to change {
|
|
group_with_public_exit.users.count
|
|
}
|
|
|
|
expect(response).to be_forbidden
|
|
end
|
|
|
|
it "the leave method is idempotent" do
|
|
sign_in(user)
|
|
|
|
expect do delete "/groups/#{group_with_public_exit.id}/leave.json" end.to change {
|
|
group_with_public_exit.users.count
|
|
}.by(-1)
|
|
expect(response.status).to eq(204)
|
|
|
|
expect do delete "/groups/#{group_with_public_exit.id}/leave.json" end.not_to change {
|
|
group_with_public_exit.users.count
|
|
}
|
|
expect(response.status).to eq(204)
|
|
|
|
expect do delete "/groups/#{group_with_public_exit.id}/leave.json" end.not_to change {
|
|
group_with_public_exit.users.count
|
|
}
|
|
expect(response.status).to eq(204)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "#handle_membership_request" do
|
|
before do
|
|
group.add_owner(user)
|
|
sign_in(user)
|
|
end
|
|
|
|
it "sends a reply to the request membership topic when accepted" do
|
|
GroupRequest.create!(group: group, user: other_user)
|
|
|
|
# send the initial request PM
|
|
PostCreator.new(
|
|
other_user,
|
|
title: I18n.t("groups.request_membership_pm.title", group_name: group.name),
|
|
raw: "*British accent* Please, sir, may I have some group?",
|
|
archetype: Archetype.private_message,
|
|
target_usernames: user.username,
|
|
skip_validations: true,
|
|
).create!
|
|
|
|
topic = Topic.last
|
|
|
|
expect {
|
|
put "/groups/#{group.id}/handle_membership_request.json",
|
|
params: {
|
|
user_id: other_user.id,
|
|
accept: true,
|
|
}
|
|
}.to_not change { Topic.count }
|
|
|
|
expect(topic.archetype).to eq(Archetype.private_message)
|
|
expect(Topic.first.title).to eq(
|
|
I18n.t("groups.request_membership_pm.title", group_name: group.name),
|
|
)
|
|
|
|
post = Post.last
|
|
expect(post.topic_id).to eq(Topic.last.id)
|
|
expect(topic.posts.count).to eq(2)
|
|
expect(post.raw).to eq(
|
|
I18n.t("groups.request_accepted_pm.body", group_name: group.name).strip,
|
|
)
|
|
end
|
|
|
|
it "sends accepted membership request reply even if request is in another language" do
|
|
SiteSetting.allow_user_locale = true
|
|
other_user.update!(locale: "fr")
|
|
|
|
GroupRequest.create!(group: group, user: other_user)
|
|
|
|
# send the initial request PM
|
|
PostCreator.new(
|
|
other_user,
|
|
title: I18n.t("groups.request_membership_pm.title", group_name: group.name, locale: "fr"),
|
|
raw: "*French accent* Please let me in!",
|
|
archetype: Archetype.private_message,
|
|
target_usernames: user.username,
|
|
skip_validations: true,
|
|
).create!
|
|
|
|
topic = Topic.last
|
|
|
|
expect {
|
|
put "/groups/#{group.id}/handle_membership_request.json",
|
|
params: {
|
|
user_id: other_user.id,
|
|
accept: true,
|
|
}
|
|
}.to_not change { Topic.count }
|
|
|
|
expect(topic.archetype).to eq(Archetype.private_message)
|
|
expect(Topic.first.title).to eq(
|
|
I18n.t("groups.request_membership_pm.title", group_name: group.name, locale: "fr"),
|
|
)
|
|
|
|
post = Post.last
|
|
expect(post.topic_id).to eq(Topic.last.id)
|
|
expect(topic.posts.count).to eq(2)
|
|
expect(post.raw).to eq(
|
|
I18n.t("groups.request_accepted_pm.body", group_name: group.name, locale: "fr").strip,
|
|
)
|
|
end
|
|
|
|
it "works even though the user has no locale" do
|
|
other_user.update!(locale: "")
|
|
|
|
GroupRequest.create!(group: group, user: other_user)
|
|
|
|
# send the initial request PM
|
|
PostCreator.new(
|
|
other_user,
|
|
title: I18n.t("groups.request_membership_pm.title", group_name: group.name),
|
|
raw: "*Alien accent* Can I join?!",
|
|
archetype: Archetype.private_message,
|
|
target_usernames: user.username,
|
|
skip_validations: true,
|
|
).create!
|
|
|
|
topic = Topic.last
|
|
|
|
expect {
|
|
put "/groups/#{group.id}/handle_membership_request.json",
|
|
params: {
|
|
user_id: other_user.id,
|
|
accept: true,
|
|
}
|
|
}.to_not change { Topic.count }
|
|
|
|
expect(topic.posts.count).to eq(2)
|
|
end
|
|
end
|
|
|
|
describe "#histories" do
|
|
context "when user is not signed in" do
|
|
it "should raise the right error" do
|
|
get "/groups/#{group.name}/logs.json"
|
|
expect(response.status).to eq(403)
|
|
end
|
|
end
|
|
|
|
context "when user is not a group owner" do
|
|
before { sign_in(user) }
|
|
|
|
it "should be forbidden" do
|
|
get "/groups/#{group.name}/logs.json"
|
|
|
|
expect(response).to be_forbidden
|
|
end
|
|
end
|
|
|
|
describe "when user is a group owner" do
|
|
before do
|
|
group.add_owner(user)
|
|
sign_in(user)
|
|
end
|
|
|
|
describe "when viewing a public group" do
|
|
before do
|
|
group.update!(public_admission: true, public_exit: true)
|
|
|
|
GroupActionLogger.new(user, group).log_change_group_settings
|
|
end
|
|
|
|
it "should allow group owner to view history" do
|
|
get "/groups/#{group.name}/logs.json"
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
result = response.parsed_body["logs"].find { |entry| entry["subject"] == "public_exit" }
|
|
|
|
expect(result["action"]).to eq(GroupHistory.actions[1].to_s)
|
|
expect(result["subject"]).to eq("public_exit")
|
|
expect(result["prev_value"]).to eq("f")
|
|
expect(result["new_value"]).to eq("t")
|
|
end
|
|
end
|
|
|
|
it "does not expose email setting values in history logs" do
|
|
group.update!(
|
|
email_password: "secret_smtp_pass",
|
|
email_username: "group@example.com",
|
|
smtp_server: "smtp.example.com",
|
|
smtp_port: 587,
|
|
smtp_ssl_mode: "starttls",
|
|
)
|
|
GroupActionLogger.new(admin, group).log_change_group_settings
|
|
|
|
get "/groups/#{group.name}/logs.json"
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
logs = response.parsed_body["logs"]
|
|
redacted = I18n.t("staff_action_logs.redacted")
|
|
|
|
%w[email_password email_username smtp_server smtp_port smtp_ssl_mode].each do |subject|
|
|
entry = logs.find { |log| log["subject"] == subject }
|
|
expect(entry["new_value"]).to eq(redacted)
|
|
end
|
|
end
|
|
|
|
it "should not be allowed to view history of an automatic group" do
|
|
group = Group.find_by(id: Group::AUTO_GROUPS[:admins])
|
|
|
|
get "/groups/#{group.name}/logs.json"
|
|
|
|
expect(response.status).to eq(403)
|
|
end
|
|
end
|
|
|
|
context "when user is an admin" do
|
|
before { sign_in(admin) }
|
|
|
|
it "should be able to view history" do
|
|
GroupActionLogger.new(admin, group).log_remove_user_from_group(user)
|
|
|
|
get "/groups/#{group.name}/logs.json"
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
result = response.parsed_body["logs"].first
|
|
|
|
expect(result["action"]).to eq(GroupHistory.actions[3].to_s)
|
|
end
|
|
|
|
it "should be able to view history of automatic groups" do
|
|
group = Group.find_by(id: Group::AUTO_GROUPS[:admins])
|
|
|
|
get "/groups/#{group.name}/logs.json"
|
|
|
|
expect(response.status).to eq(200)
|
|
end
|
|
|
|
it "should be able to filter through the history" do
|
|
GroupActionLogger.new(admin, group).log_add_user_to_group(user)
|
|
GroupActionLogger.new(admin, group).log_remove_user_from_group(user)
|
|
|
|
get "/groups/#{group.name}/logs.json",
|
|
params: {
|
|
filters: {
|
|
"action" => "add_user_to_group",
|
|
},
|
|
}
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
logs = response.parsed_body["logs"]
|
|
|
|
expect(logs.count).to eq(1)
|
|
expect(logs.first["action"]).to eq(GroupHistory.actions[2].to_s)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "#request_membership" do
|
|
fab!(:new_user, :user)
|
|
|
|
before { group.update_column(:allow_membership_requests, true) }
|
|
|
|
it "requires the user to log in" do
|
|
post "/groups/#{group.name}/request_membership.json"
|
|
expect(response.status).to eq(403)
|
|
end
|
|
|
|
it "rejects the request when allow_membership_requests is false" do
|
|
group.update!(allow_membership_requests: false)
|
|
sign_in(new_user)
|
|
|
|
post "/groups/#{group.name}/request_membership.json", params: { reason: "Please add me" }
|
|
|
|
expect(response.status).to eq(403)
|
|
expect(GroupRequest.where(group: group, user: new_user).exists?).to eq(false)
|
|
end
|
|
|
|
it "rejects the request when the user is already a group member" do
|
|
sign_in(user)
|
|
|
|
post "/groups/#{group.name}/request_membership.json", params: { reason: "Please add me" }
|
|
|
|
expect(response.status).to eq(403)
|
|
expect(GroupRequest.where(group: group, user: user).exists?).to eq(false)
|
|
end
|
|
|
|
it "requires a reason" do
|
|
sign_in(new_user)
|
|
|
|
post "/groups/#{group.name}/request_membership.json"
|
|
expect(response.status).to eq(400)
|
|
end
|
|
|
|
it "checks for duplicates" do
|
|
sign_in(new_user)
|
|
|
|
post "/groups/#{group.name}/request_membership.json", params: { reason: "Please add me in" }
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
post "/groups/#{group.name}/request_membership.json", params: { reason: "Please add me in" }
|
|
|
|
expect(response.status).to eq(409)
|
|
end
|
|
|
|
it "limits the character count of the reason" do
|
|
sign_in(new_user)
|
|
|
|
post "/groups/#{group.name}/request_membership.json",
|
|
params: {
|
|
reason: "x" * (GroupRequest::REASON_CHARACTER_LIMIT + 1),
|
|
}
|
|
|
|
expect(response.status).to eq(422)
|
|
expect(response.parsed_body["errors"]).to contain_exactly(
|
|
"Reason is too long (maximum is 5000 characters)",
|
|
)
|
|
end
|
|
|
|
it "should create the right PM" do
|
|
owner1 = Fabricate(:user, last_seen_at: Time.zone.now)
|
|
owner2 = Fabricate(:user, last_seen_at: 1.day.ago)
|
|
[owner1, owner2].each { |owner| group.add_owner(owner) }
|
|
|
|
sign_in(new_user)
|
|
|
|
post "/groups/#{group.name}/request_membership.json", params: { reason: "Please add me in" }
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
post = Post.last
|
|
topic = post.topic
|
|
body = response.parsed_body
|
|
|
|
expect(body["relative_url"]).to eq(topic.relative_url)
|
|
expect(post.topic.custom_fields["requested_group_id"].to_i).to eq(group.id)
|
|
expect(post.user).to eq(new_user)
|
|
|
|
expect(topic.title).to eq(
|
|
I18n.t("groups.request_membership_pm.title", group_name: group.name),
|
|
)
|
|
|
|
expect(post.raw).to start_with("Please add me in")
|
|
expect(topic.archetype).to eq(Archetype.private_message)
|
|
expect(topic.allowed_users).to contain_exactly(new_user, owner1, owner2)
|
|
expect(topic.allowed_groups).to eq([])
|
|
end
|
|
end
|
|
|
|
describe "#search " do
|
|
fab!(:hidden_group) do
|
|
Fabricate(:group, visibility_level: Group.visibility_levels[:owners], name: "KingOfTheNorth")
|
|
end
|
|
|
|
before do
|
|
group.update!(
|
|
name: "GOT",
|
|
full_name: "Daenerys Targaryen",
|
|
visibility_level: Group.visibility_levels[:logged_on_users],
|
|
)
|
|
|
|
hidden_group
|
|
end
|
|
|
|
context "as an anon user" do
|
|
it "returns the right response" do
|
|
get "/groups/search.json"
|
|
expect(response.status).to eq(403)
|
|
end
|
|
end
|
|
|
|
context "as a normal user" do
|
|
it "returns the right response" do
|
|
sign_in(user)
|
|
|
|
get "/groups/search.json"
|
|
|
|
expect(response.status).to eq(200)
|
|
groups = response.parsed_body
|
|
|
|
expected_ids = Group::AUTO_GROUPS.map { |name, id| id }
|
|
expected_ids.delete(Group::AUTO_GROUPS[:everyone])
|
|
expected_ids.delete(Group::AUTO_GROUPS[:logged_in_users])
|
|
expected_ids.delete(Group::AUTO_GROUPS[:anonymous_users])
|
|
expected_ids << group.id
|
|
|
|
expect(groups.map { |group| group["id"] }).to contain_exactly(*expected_ids)
|
|
|
|
%w[GO nerys].each do |term|
|
|
get "/groups/search.json?term=#{term}"
|
|
|
|
expect(response.status).to eq(200)
|
|
groups = response.parsed_body
|
|
|
|
expect(groups.length).to eq(1)
|
|
expect(groups.first["id"]).to eq(group.id)
|
|
end
|
|
|
|
get "/groups/search.json?term=KingOfTheNorth"
|
|
|
|
expect(response.status).to eq(200)
|
|
groups = response.parsed_body
|
|
|
|
expect(groups).to eq([])
|
|
end
|
|
end
|
|
|
|
context "as a group owner" do
|
|
before { hidden_group.add_owner(user) }
|
|
|
|
it "returns the right response" do
|
|
sign_in(user)
|
|
|
|
get "/groups/search.json?term=north"
|
|
|
|
expect(response.status).to eq(200)
|
|
groups = response.parsed_body
|
|
|
|
expect(groups.length).to eq(1)
|
|
expect(groups.first["id"]).to eq(hidden_group.id)
|
|
end
|
|
end
|
|
|
|
context "as an admin" do
|
|
it "returns the right response" do
|
|
sign_in(admin)
|
|
|
|
get "/groups/search.json?ignore_automatic=true"
|
|
|
|
expect(response.status).to eq(200)
|
|
groups = response.parsed_body
|
|
|
|
expect(groups.length).to eq(2)
|
|
|
|
expect(groups.map { |group| group["id"] }).to contain_exactly(group.id, hidden_group.id)
|
|
|
|
get "/groups/search.json"
|
|
|
|
expect(response.status).to eq(200)
|
|
groups = response.parsed_body
|
|
|
|
automatic_ids = Group::AUTO_GROUPS.map { |name, id| id }
|
|
|
|
expect(groups.map { |group| group["id"] }).to contain_exactly(
|
|
group.id,
|
|
hidden_group.id,
|
|
*(
|
|
automatic_ids -
|
|
[
|
|
Group::AUTO_GROUPS[:everyone],
|
|
Group::AUTO_GROUPS[:anonymous_users],
|
|
Group::AUTO_GROUPS[:logged_in_users],
|
|
]
|
|
),
|
|
)
|
|
|
|
SiteSetting.granular_anonymous_and_logged_in_groups_permissions = false
|
|
get "/groups/search.json?include_everyone=true"
|
|
|
|
expect(response.status).to eq(200)
|
|
groups = response.parsed_body
|
|
|
|
automatic_ids = Group::AUTO_GROUPS.map { |name, id| id }
|
|
|
|
expect(groups.map { |group| group["id"] }).to contain_exactly(
|
|
group.id,
|
|
hidden_group.id,
|
|
*(
|
|
automatic_ids -
|
|
[Group::AUTO_GROUPS[:anonymous_users], Group::AUTO_GROUPS[:logged_in_users]]
|
|
),
|
|
)
|
|
|
|
get "/groups/search.json?include_pseudogroups=true"
|
|
|
|
expect(response.status).to eq(200)
|
|
groups = response.parsed_body
|
|
|
|
expect(groups.map { |group| group["id"] }).to contain_exactly(
|
|
group.id,
|
|
hidden_group.id,
|
|
*automatic_ids,
|
|
)
|
|
end
|
|
end
|
|
|
|
describe "groups_search_query modifier" do
|
|
fab!(:user)
|
|
fab!(:cool_group) { Fabricate(:group, name: "cool-group") }
|
|
fab!(:boring_group) { Fabricate(:group, name: "boring-group") }
|
|
|
|
before { sign_in(user) }
|
|
|
|
it "allows changing the query" do
|
|
get "/groups/search.json", params: { term: "cool" }
|
|
expect(response.status).to eq(200)
|
|
expect(response.parsed_body.map { |g| g["id"] }).to include(cool_group.id)
|
|
expect(response.parsed_body.map { |g| g["id"] }).not_to include(boring_group.id)
|
|
|
|
Plugin::Instance
|
|
.new
|
|
.register_modifier(:groups_search_query) do |query|
|
|
query.where("groups.name LIKE 'boring%'")
|
|
end
|
|
|
|
get "/groups/search.json", params: { term: "cool" }
|
|
expect(response.status).to eq(200)
|
|
expect(response.parsed_body.map { |g| g["id"] }).not_to include(
|
|
cool_group.id,
|
|
boring_group.id,
|
|
)
|
|
ensure
|
|
DiscoursePluginRegistry.clear_modifiers!
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "#new" do
|
|
describe "for an anon user" do
|
|
it "should return 404" do
|
|
get "/groups/custom/new"
|
|
|
|
expect(response.status).to eq(404)
|
|
end
|
|
end
|
|
|
|
describe "for a normal user" do
|
|
before { sign_in(user) }
|
|
|
|
it "should return 404" do
|
|
get "/groups/custom/new"
|
|
|
|
expect(response.status).to eq(404)
|
|
end
|
|
end
|
|
|
|
describe "for an admin user" do
|
|
before { sign_in(admin) }
|
|
|
|
it "should return 200" do
|
|
get "/groups/custom/new"
|
|
|
|
expect(response.status).to eq(200)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "#check_name" do
|
|
describe "for an anon user" do
|
|
it "should return the right response" do
|
|
get "/groups/check-name.json", params: { group_name: "test" }
|
|
expect(response.status).to eq(403)
|
|
end
|
|
end
|
|
|
|
it "should return the right response" do
|
|
sign_in(Fabricate(:user))
|
|
SiteSetting.reserved_usernames = "test|donkey"
|
|
get "/groups/check-name.json", params: { group_name: "test" }
|
|
|
|
expect(response.status).to eq(200)
|
|
expect(response.parsed_body["available"]).to eq(true)
|
|
end
|
|
end
|
|
|
|
describe "#permissions" do
|
|
before { sign_in(other_user) }
|
|
|
|
it "ensures the group can be seen" do
|
|
group.update!(visibility_level: Group.visibility_levels[:owners])
|
|
|
|
get "/groups/#{group.name}/permissions.json"
|
|
|
|
expect(response.status).to eq(404)
|
|
end
|
|
|
|
describe "with varying category permissions" do
|
|
fab!(:category)
|
|
|
|
before do
|
|
category.set_permissions("#{group.name}": :full)
|
|
category.save!
|
|
end
|
|
|
|
it "does not return categories the user cannot see" do
|
|
get "/groups/#{group.name}/permissions.json"
|
|
|
|
expect(response.status).to eq(200)
|
|
expect(response.parsed_body).to eq([])
|
|
end
|
|
|
|
it "returns categories the user can see" do
|
|
group.add(other_user)
|
|
|
|
get "/groups/#{group.name}/permissions.json"
|
|
|
|
expect(response.status).to eq(200)
|
|
expect(response.parsed_body.count).to eq(1)
|
|
expect(response.parsed_body.first["category"]["id"]).to eq(category.id)
|
|
end
|
|
end
|
|
|
|
it "returns categories alphabetically" do
|
|
sign_in(user)
|
|
|
|
["Three", "New Cat", "Abc", "Hello"].each do |name|
|
|
category = Fabricate(:category, name: name)
|
|
category.set_permissions("#{group.name}": :full)
|
|
category.save!
|
|
end
|
|
|
|
get "/groups/#{group.name}/permissions.json"
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
expect(response.parsed_body.map { |permission| permission["category"]["name"] }).to eq(
|
|
["Abc", "Hello", "New Cat", "Three"],
|
|
)
|
|
end
|
|
end
|
|
|
|
describe "#test_email_settings" do
|
|
let(:params) do
|
|
{
|
|
protocol: protocol,
|
|
ssl_mode: ssl_mode,
|
|
ssl: ssl,
|
|
port: port,
|
|
host: host,
|
|
username: username,
|
|
password: password,
|
|
}
|
|
end
|
|
|
|
before { sign_in(admin) }
|
|
|
|
context "when validating smtp" do
|
|
let(:protocol) { "smtp" }
|
|
let(:username) { "test@gmail.com" }
|
|
let(:password) { "password" }
|
|
let(:domain) { nil }
|
|
let(:ssl_mode) { Group.smtp_ssl_modes[:starttls] }
|
|
let(:ssl) { nil }
|
|
let(:host) { "smtp.somemailsite.com" }
|
|
let(:port) { 587 }
|
|
|
|
context "when an error is raised" do
|
|
before do
|
|
EmailSettingsValidator.expects(:validate_smtp).raises(
|
|
Net::SMTPAuthenticationError,
|
|
"Invalid credentials",
|
|
)
|
|
end
|
|
it "uses the friendly error message functionality to return the message to the user" do
|
|
post "/groups/#{group.id}/test_email_settings.json", params: params
|
|
expect(response.status).to eq(422)
|
|
expect(response.parsed_body["errors"]).to include(
|
|
I18n.t("email_settings.smtp_authentication_error", message: "Invalid credentials"),
|
|
)
|
|
end
|
|
end
|
|
end
|
|
|
|
context "when user is a non-staff group owner" do
|
|
let(:protocol) { "smtp" }
|
|
let(:username) { "test@gmail.com" }
|
|
let(:password) { "password" }
|
|
let(:ssl_mode) { Group.smtp_ssl_modes[:starttls] }
|
|
let(:ssl) { nil }
|
|
let(:host) { "smtp.somemailsite.com" }
|
|
let(:port) { 587 }
|
|
|
|
before do
|
|
sign_in(user)
|
|
group.group_users.where(user: user).last.update(owner: true)
|
|
end
|
|
|
|
it "does not allow testing email settings" do
|
|
post "/groups/#{group.id}/test_email_settings.json", params: params
|
|
expect(response.status).to eq(403)
|
|
end
|
|
end
|
|
|
|
context "when the host resolves to a private IP address" do
|
|
let(:protocol) { "smtp" }
|
|
let(:username) { "test@gmail.com" }
|
|
let(:password) { "password" }
|
|
let(:ssl_mode) { Group.smtp_ssl_modes[:starttls] }
|
|
let(:ssl) { nil }
|
|
let(:host) { "127.0.0.1" }
|
|
let(:port) { 587 }
|
|
|
|
before do
|
|
FinalDestination::SSRFDetector.stubs(:lookup_and_filter_ips).raises(
|
|
FinalDestination::SSRFDetector::DisallowedIpError,
|
|
)
|
|
end
|
|
|
|
it "rejects the request" do
|
|
post "/groups/#{group.id}/test_email_settings.json", params: params
|
|
expect(response.status).to eq(400)
|
|
expect(response.parsed_body["errors"].first).to include(
|
|
I18n.t("email_settings.invalid_host"),
|
|
)
|
|
end
|
|
end
|
|
|
|
context "when the host cannot be resolved" do
|
|
let(:protocol) { "smtp" }
|
|
let(:username) { "test@gmail.com" }
|
|
let(:password) { "password" }
|
|
let(:ssl_mode) { Group.smtp_ssl_modes[:starttls] }
|
|
let(:ssl) { nil }
|
|
let(:host) { "nonexistent.internal.host" }
|
|
let(:port) { 587 }
|
|
|
|
before do
|
|
FinalDestination::SSRFDetector.stubs(:lookup_and_filter_ips).raises(
|
|
FinalDestination::SSRFDetector::LookupFailedError,
|
|
)
|
|
end
|
|
|
|
it "rejects the request" do
|
|
post "/groups/#{group.id}/test_email_settings.json", params: params
|
|
expect(response.status).to eq(400)
|
|
expect(response.parsed_body["errors"].first).to include(
|
|
I18n.t("email_settings.host_resolve_failed"),
|
|
)
|
|
end
|
|
end
|
|
|
|
describe "global param validation and rate limit" do
|
|
let(:protocol) { "smtp" }
|
|
let(:host) { "smtp.gmail.com" }
|
|
let(:port) { 587 }
|
|
let(:username) { "test@gmail.com" }
|
|
let(:password) { "password" }
|
|
let(:ssl) { true }
|
|
let(:ssl_mode) { nil }
|
|
|
|
context "when the protocol is not accepted" do
|
|
let(:protocol) { "sigma" }
|
|
it "raises an invalid params error" do
|
|
post "/groups/#{group.id}/test_email_settings.json", params: params
|
|
expect(response.status).to eq(400)
|
|
expect(response.parsed_body["errors"].first).to match(/Valid protocol to test is smtp/)
|
|
end
|
|
end
|
|
|
|
context "when user is a regular user without staff access" do
|
|
before { sign_in(user) }
|
|
it "errors if the user does not have access to the group" do
|
|
post "/groups/#{group.id}/test_email_settings.json", params: params
|
|
|
|
expect(response.status).to eq(403)
|
|
end
|
|
end
|
|
|
|
context "when rate limited" do
|
|
it "rate limits per user" do
|
|
RateLimiter.enable
|
|
RateLimiter.any_instance.stubs(:rate_unlimited?).returns(false)
|
|
|
|
5.times { post "/groups/#{group.id}/test_email_settings.json", params: params }
|
|
post "/groups/#{group.id}/test_email_settings.json", params: params
|
|
expect(response.status).to eq(429)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "#set_notifications" do
|
|
fab!(:target_user, :user)
|
|
fab!(:non_member_user, :user)
|
|
fab!(:group)
|
|
|
|
context "when target user is in group" do
|
|
before do
|
|
group.add(moderator)
|
|
group.add(target_user)
|
|
end
|
|
|
|
it "allows a staff member to change notification level for a user who is a member of the group" do
|
|
sign_in(moderator)
|
|
|
|
group_user = GroupUser.find_by(group_id: group.id, user_id: target_user.id)
|
|
|
|
post "/groups/#{group.name}/notifications.json",
|
|
params: {
|
|
notification_level: NotificationLevels.all[:muted],
|
|
user_id: target_user.id,
|
|
}
|
|
|
|
expect(response.status).to eq(200)
|
|
expect(group_user.reload.notification_level).to eq(NotificationLevels.all[:muted])
|
|
end
|
|
|
|
it "does not allow a staff member to change notification level for a user who is not a member of the group" do
|
|
sign_in(moderator)
|
|
|
|
post "/groups/#{group.name}/notifications.json",
|
|
params: {
|
|
notification_level: NotificationLevels.all[:muted],
|
|
user_id: non_member_user.id,
|
|
}
|
|
|
|
expect(response.status).to eq(400)
|
|
end
|
|
|
|
it "does not allow a regular user to change another user's notification level" do
|
|
sign_in(target_user)
|
|
|
|
mod_group_user = GroupUser.find_by(group_id: group.id, user_id: moderator.id)
|
|
original_level = mod_group_user.notification_level
|
|
|
|
post "/groups/#{group.name}/notifications.json",
|
|
params: {
|
|
notification_level: NotificationLevels.all[:muted],
|
|
user_id: moderator.id,
|
|
}
|
|
|
|
expect(response.status).to eq(200)
|
|
expect(mod_group_user.reload.notification_level).to eq(original_level)
|
|
end
|
|
end
|
|
|
|
context "when target user is not in group" do
|
|
before { group.add(moderator) }
|
|
|
|
it "does not allow a staff member to change notification level for a user who is not a member of the group" do
|
|
sign_in(moderator)
|
|
|
|
post "/groups/#{group.name}/notifications.json",
|
|
params: {
|
|
notification_level: NotificationLevels.all[:muted],
|
|
user_id: target_user.id,
|
|
}
|
|
|
|
expect(response.status).to eq(400)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "requires_login for state-changing actions" do
|
|
fab!(:group)
|
|
|
|
it "returns not_logged_in error for anonymous add_members request" do
|
|
put "/groups/#{group.id}/members.json", params: { usernames: "bob" }
|
|
expect(response.status).to eq(403)
|
|
expect(response.parsed_body["error_type"]).to eq("not_logged_in")
|
|
end
|
|
|
|
it "returns not_logged_in error for anonymous add_owners request" do
|
|
put "/groups/#{group.id}/owners.json", params: { usernames: "bob" }
|
|
expect(response.status).to eq(403)
|
|
expect(response.parsed_body["error_type"]).to eq("not_logged_in")
|
|
end
|
|
|
|
it "returns not_logged_in error for anonymous remove_member request" do
|
|
delete "/groups/#{group.id}/members.json", params: { username: "bob" }
|
|
expect(response.status).to eq(403)
|
|
expect(response.parsed_body["error_type"]).to eq("not_logged_in")
|
|
end
|
|
|
|
it "returns not_logged_in error for anonymous handle_membership_request" do
|
|
put "/groups/#{group.id}/handle_membership_request.json", params: { user_id: 1 }
|
|
expect(response.status).to eq(403)
|
|
expect(response.parsed_body["error_type"]).to eq("not_logged_in")
|
|
end
|
|
end
|
|
end
|