2019-04-30 08:27:42 +08:00
|
|
|
|
# frozen_string_literal: true
|
|
|
|
|
|
2022-07-28 10:27:38 +08:00
|
|
|
|
RSpec.describe InvitesController do
|
2023-11-10 06:47:59 +08:00
|
|
|
|
fab!(:admin)
|
2024-01-29 17:52:02 +08:00
|
|
|
|
fab!(:user) { Fabricate(:user, trust_level: TrustLevel[2]) }
|
2020-06-09 23:19:32 +08:00
|
|
|
|
|
2022-07-27 18:21:10 +08:00
|
|
|
|
describe "#show" do
|
2023-11-10 06:47:59 +08:00
|
|
|
|
fab!(:invite)
|
2018-05-08 22:42:58 +08:00
|
|
|
|
|
2021-03-26 00:26:22 +08:00
|
|
|
|
it "shows the accept invite page" do
|
2020-10-24 23:51:01 +08:00
|
|
|
|
get "/invites/#{invite.invite_key}"
|
|
|
|
|
expect(response.status).to eq(200)
|
2023-11-14 03:26:43 +08:00
|
|
|
|
expect(response.body).to have_tag(
|
|
|
|
|
:script,
|
|
|
|
|
with: {
|
|
|
|
|
"data-discourse-entrypoint" => "discourse",
|
|
|
|
|
},
|
|
|
|
|
)
|
2021-03-26 00:26:22 +08:00
|
|
|
|
expect(response.body).not_to include(invite.email)
|
|
|
|
|
expect(response.body).to_not include(
|
|
|
|
|
I18n.t(
|
|
|
|
|
"invite.not_found_template",
|
|
|
|
|
site_name: SiteSetting.title,
|
|
|
|
|
base_url: Discourse.base_url,
|
2023-01-09 19:18:21 +08:00
|
|
|
|
),
|
2021-03-26 00:26:22 +08:00
|
|
|
|
)
|
2021-11-30 20:59:37 +08:00
|
|
|
|
|
|
|
|
|
expect(response.body).to have_tag("div#data-preloaded") do |element|
|
|
|
|
|
json = JSON.parse(element.current_scope.attribute("data-preloaded").value)
|
|
|
|
|
invite_info = JSON.parse(json["invite_info"])
|
|
|
|
|
expect(invite_info["username"]).to eq("")
|
|
|
|
|
expect(invite_info["email"]).to eq("i*****g@a***********e.ooo")
|
|
|
|
|
end
|
2020-10-24 23:51:01 +08:00
|
|
|
|
end
|
|
|
|
|
|
2022-03-21 22:28:52 +08:00
|
|
|
|
context "when email data is present in authentication data" do
|
|
|
|
|
let(:store) { ActionDispatch::Session::CookieStore.new({}) }
|
|
|
|
|
let(:session_stub) do
|
|
|
|
|
ActionDispatch::Request::Session.create(store, ActionDispatch::TestRequest.create, {})
|
2023-01-09 19:18:21 +08:00
|
|
|
|
end
|
2022-03-21 22:28:52 +08:00
|
|
|
|
|
|
|
|
|
before do
|
|
|
|
|
session_stub[:authentication] = { email: invite.email }
|
|
|
|
|
ActionDispatch::Request.any_instance.stubs(:session).returns(session_stub)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "shows unobfuscated email" do
|
|
|
|
|
get "/invites/#{invite.invite_key}"
|
|
|
|
|
expect(response.status).to eq(200)
|
2023-11-27 14:04:13 +08:00
|
|
|
|
expect(response.body).to_not have_tag(:body, with: { class: "no-ember" })
|
2022-03-21 22:28:52 +08:00
|
|
|
|
expect(response.body).to include(invite.email)
|
|
|
|
|
expect(response.body).not_to include("i*****g@a***********e.ooo")
|
|
|
|
|
end
|
2020-09-02 18:24:49 +08:00
|
|
|
|
end
|
|
|
|
|
|
2021-03-29 19:03:19 +08:00
|
|
|
|
it "shows default user fields" do
|
|
|
|
|
user_field = Fabricate(:user_field)
|
|
|
|
|
staged_user = Fabricate(:user, staged: true, email: invite.email)
|
|
|
|
|
staged_user.set_user_field(user_field.id, "some value")
|
|
|
|
|
staged_user.save_custom_fields
|
|
|
|
|
|
|
|
|
|
get "/invites/#{invite.invite_key}"
|
|
|
|
|
expect(response.body).to have_tag("div#data-preloaded") do |element|
|
|
|
|
|
json = JSON.parse(element.current_scope.attribute("data-preloaded").value)
|
|
|
|
|
invite_info = JSON.parse(json["invite_info"])
|
2021-07-12 05:57:38 +08:00
|
|
|
|
expect(invite_info["username"]).to eq(staged_user.username)
|
2021-03-29 19:03:19 +08:00
|
|
|
|
expect(invite_info["user_fields"][user_field.id.to_s]).to eq("some value")
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2021-05-26 16:47:44 +08:00
|
|
|
|
it "includes token validity boolean" do
|
|
|
|
|
get "/invites/#{invite.invite_key}"
|
|
|
|
|
expect(response.body).to have_tag("div#data-preloaded") do |element|
|
|
|
|
|
json = JSON.parse(element.current_scope.attribute("data-preloaded").value)
|
|
|
|
|
invite_info = JSON.parse(json["invite_info"])
|
|
|
|
|
expect(invite_info["email_verified_by_link"]).to eq(false)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
get "/invites/#{invite.invite_key}?t=#{invite.email_token}"
|
|
|
|
|
expect(response.body).to have_tag("div#data-preloaded") do |element|
|
|
|
|
|
json = JSON.parse(element.current_scope.attribute("data-preloaded").value)
|
|
|
|
|
invite_info = JSON.parse(json["invite_info"])
|
|
|
|
|
expect(invite_info["email_verified_by_link"]).to eq(true)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2022-06-21 11:56:50 +08:00
|
|
|
|
describe "logged in user viewing an invite" do
|
2023-11-10 06:47:59 +08:00
|
|
|
|
fab!(:group)
|
2021-07-08 00:42:42 +08:00
|
|
|
|
|
2022-06-21 11:56:50 +08:00
|
|
|
|
before { sign_in(user) }
|
2021-07-08 00:42:42 +08:00
|
|
|
|
|
2022-11-02 00:33:32 +08:00
|
|
|
|
it "shows the accept invite page when user's email matches the invite email" do
|
2022-06-21 11:56:50 +08:00
|
|
|
|
invite.update_columns(email: user.email)
|
2021-07-08 00:42:42 +08:00
|
|
|
|
|
2022-11-02 00:33:32 +08:00
|
|
|
|
get "/invites/#{invite.invite_key}"
|
|
|
|
|
expect(response.status).to eq(200)
|
2023-11-27 14:04:13 +08:00
|
|
|
|
expect(response.body).to_not have_tag(:body, with: { class: "no-ember" })
|
2022-11-02 00:33:32 +08:00
|
|
|
|
expect(response.body).not_to include(
|
|
|
|
|
I18n.t(
|
|
|
|
|
"invite.not_found_template",
|
|
|
|
|
site_name: SiteSetting.title,
|
|
|
|
|
base_url: Discourse.base_url,
|
2023-01-09 19:18:21 +08:00
|
|
|
|
),
|
2022-11-02 00:33:32 +08:00
|
|
|
|
)
|
2023-01-09 19:18:21 +08:00
|
|
|
|
|
2022-11-02 00:33:32 +08:00
|
|
|
|
expect(response.body).to have_tag("div#data-preloaded") do |element|
|
|
|
|
|
json = JSON.parse(element.current_scope.attribute("data-preloaded").value)
|
|
|
|
|
invite_info = JSON.parse(json["invite_info"])
|
|
|
|
|
expect(invite_info["username"]).to eq(user.username)
|
|
|
|
|
expect(invite_info["email"]).to eq(user.email)
|
|
|
|
|
expect(invite_info["existing_user_id"]).to eq(user.id)
|
|
|
|
|
expect(invite_info["existing_user_can_redeem"]).to eq(true)
|
|
|
|
|
end
|
2022-06-21 11:56:50 +08:00
|
|
|
|
end
|
2021-07-08 00:42:42 +08:00
|
|
|
|
|
2022-11-02 00:33:32 +08:00
|
|
|
|
it "shows the accept invite page when user's email domain matches the domain an invite link is restricted to" do
|
2022-06-21 11:56:50 +08:00
|
|
|
|
invite.update!(email: nil, domain: "discourse.org")
|
|
|
|
|
user.update!(email: "someguy@discourse.org")
|
2022-02-09 23:22:30 +08:00
|
|
|
|
|
2022-11-02 00:33:32 +08:00
|
|
|
|
get "/invites/#{invite.invite_key}"
|
|
|
|
|
expect(response.status).to eq(200)
|
2023-11-27 14:04:13 +08:00
|
|
|
|
expect(response.body).to_not have_tag(:body, with: { class: "no-ember" })
|
2022-11-02 00:33:32 +08:00
|
|
|
|
expect(response.body).not_to include(
|
|
|
|
|
I18n.t(
|
|
|
|
|
"invite.not_found_template",
|
|
|
|
|
site_name: SiteSetting.title,
|
|
|
|
|
base_url: Discourse.base_url,
|
2023-01-09 19:18:21 +08:00
|
|
|
|
),
|
2022-11-02 00:33:32 +08:00
|
|
|
|
)
|
2023-01-09 19:18:21 +08:00
|
|
|
|
|
2022-11-02 00:33:32 +08:00
|
|
|
|
expect(response.body).to have_tag("div#data-preloaded") do |element|
|
|
|
|
|
json = JSON.parse(element.current_scope.attribute("data-preloaded").value)
|
|
|
|
|
invite_info = JSON.parse(json["invite_info"])
|
|
|
|
|
expect(invite_info["username"]).to eq(user.username)
|
|
|
|
|
expect(invite_info["email"]).to eq(user.email)
|
|
|
|
|
expect(invite_info["existing_user_id"]).to eq(user.id)
|
|
|
|
|
expect(invite_info["existing_user_can_redeem"]).to eq(true)
|
|
|
|
|
end
|
2022-06-21 11:56:50 +08:00
|
|
|
|
end
|
2020-09-02 18:24:49 +08:00
|
|
|
|
|
2022-11-02 00:33:32 +08:00
|
|
|
|
it "does not allow the user to accept the invite when their email domain does not match the domain of the invite" do
|
2022-06-21 11:56:50 +08:00
|
|
|
|
user.update!(email: "someguy@discourse.com")
|
|
|
|
|
invite.update!(email: nil, domain: "discourse.org")
|
2022-02-15 23:35:58 +08:00
|
|
|
|
|
2022-11-02 00:33:32 +08:00
|
|
|
|
get "/invites/#{invite.invite_key}"
|
|
|
|
|
expect(response.status).to eq(200)
|
2022-06-21 11:56:50 +08:00
|
|
|
|
|
2022-11-02 00:33:32 +08:00
|
|
|
|
expect(response.body).to have_tag("div#data-preloaded") do |element|
|
|
|
|
|
json = JSON.parse(element.current_scope.attribute("data-preloaded").value)
|
|
|
|
|
invite_info = JSON.parse(json["invite_info"])
|
|
|
|
|
expect(invite_info["existing_user_can_redeem"]).to eq(false)
|
2022-11-17 13:51:58 +08:00
|
|
|
|
expect(invite_info["existing_user_can_redeem_error"]).to eq(
|
|
|
|
|
I18n.t("invite.existing_user_cannot_redeem"),
|
|
|
|
|
)
|
2022-11-02 00:33:32 +08:00
|
|
|
|
end
|
2022-06-21 11:56:50 +08:00
|
|
|
|
end
|
|
|
|
|
|
2022-11-02 00:33:32 +08:00
|
|
|
|
it "does not allow the user to accept the invite when their email does not match the invite" do
|
2022-06-21 11:56:50 +08:00
|
|
|
|
invite.update_columns(email: "notuseremail@discourse.org")
|
|
|
|
|
|
2022-11-02 00:33:32 +08:00
|
|
|
|
get "/invites/#{invite.invite_key}"
|
|
|
|
|
expect(response.status).to eq(200)
|
2022-06-21 11:56:50 +08:00
|
|
|
|
|
2022-11-02 00:33:32 +08:00
|
|
|
|
expect(response.body).to have_tag("div#data-preloaded") do |element|
|
|
|
|
|
json = JSON.parse(element.current_scope.attribute("data-preloaded").value)
|
|
|
|
|
invite_info = JSON.parse(json["invite_info"])
|
|
|
|
|
expect(invite_info["existing_user_can_redeem"]).to eq(false)
|
|
|
|
|
end
|
2022-06-21 11:56:50 +08:00
|
|
|
|
end
|
2022-11-17 13:51:58 +08:00
|
|
|
|
|
|
|
|
|
it "does not allow the user to accept the invite when a multi-use invite link has already been redeemed by the user" do
|
|
|
|
|
invite.update!(email: nil, max_redemptions_allowed: 10)
|
|
|
|
|
expect(invite.redeem(redeeming_user: user)).not_to eq(nil)
|
|
|
|
|
|
|
|
|
|
get "/invites/#{invite.invite_key}"
|
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
|
|
|
|
|
|
expect(response.body).to have_tag("div#data-preloaded") do |element|
|
|
|
|
|
json = JSON.parse(element.current_scope.attribute("data-preloaded").value)
|
|
|
|
|
invite_info = JSON.parse(json["invite_info"])
|
|
|
|
|
expect(invite_info["existing_user_id"]).to eq(user.id)
|
|
|
|
|
expect(invite_info["existing_user_can_redeem"]).to eq(false)
|
|
|
|
|
expect(invite_info["existing_user_can_redeem_error"]).to eq(
|
|
|
|
|
I18n.t("invite.existing_user_already_redemeed"),
|
|
|
|
|
)
|
|
|
|
|
end
|
|
|
|
|
end
|
2022-11-25 09:57:04 +08:00
|
|
|
|
|
|
|
|
|
it "allows the user to accept the invite when its an invite link that they have not redeemed" do
|
|
|
|
|
invite.update!(email: nil, max_redemptions_allowed: 10)
|
|
|
|
|
|
|
|
|
|
get "/invites/#{invite.invite_key}"
|
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
|
|
|
|
|
|
expect(response.body).to have_tag("div#data-preloaded") do |element|
|
|
|
|
|
json = JSON.parse(element.current_scope.attribute("data-preloaded").value)
|
|
|
|
|
invite_info = JSON.parse(json["invite_info"])
|
|
|
|
|
expect(invite_info["existing_user_id"]).to eq(user.id)
|
|
|
|
|
expect(invite_info["existing_user_can_redeem"]).to eq(true)
|
|
|
|
|
end
|
|
|
|
|
end
|
2022-02-15 23:35:58 +08:00
|
|
|
|
end
|
|
|
|
|
|
2021-03-26 00:26:22 +08:00
|
|
|
|
it "fails if invite does not exist" do
|
|
|
|
|
get "/invites/missing"
|
|
|
|
|
expect(response.status).to eq(200)
|
2023-11-27 14:04:13 +08:00
|
|
|
|
|
|
|
|
|
expect(response.body).to have_tag(:body, with: { class: "no-ember" })
|
2021-03-26 00:26:22 +08:00
|
|
|
|
expect(response.body).to include(I18n.t("invite.not_found", base_url: Discourse.base_url))
|
2018-05-08 22:42:58 +08:00
|
|
|
|
end
|
|
|
|
|
|
2021-03-26 00:26:22 +08:00
|
|
|
|
it "fails if invite expired" do
|
|
|
|
|
invite.update(expires_at: 1.day.ago)
|
2018-05-08 22:42:58 +08:00
|
|
|
|
|
2021-03-26 00:26:22 +08:00
|
|
|
|
get "/invites/#{invite.invite_key}"
|
2018-06-07 16:11:09 +08:00
|
|
|
|
expect(response.status).to eq(200)
|
2023-11-27 14:04:13 +08:00
|
|
|
|
|
|
|
|
|
expect(response.body).to have_tag(:body, with: { class: "no-ember" })
|
2021-03-26 00:26:22 +08:00
|
|
|
|
expect(response.body).to include(I18n.t("invite.expired", base_url: Discourse.base_url))
|
2018-05-08 22:42:58 +08:00
|
|
|
|
end
|
|
|
|
|
|
2021-03-26 00:26:22 +08:00
|
|
|
|
it "stores the invite key in the secure session if invite exists" do
|
2021-03-19 08:20:10 +08:00
|
|
|
|
get "/invites/#{invite.invite_key}"
|
|
|
|
|
expect(response.status).to eq(200)
|
2021-03-26 00:26:22 +08:00
|
|
|
|
invite_key = read_secure_session["invite-key"]
|
2021-03-19 08:20:10 +08:00
|
|
|
|
expect(invite_key).to eq(invite.invite_key)
|
|
|
|
|
end
|
|
|
|
|
|
2021-03-26 00:26:22 +08:00
|
|
|
|
it "returns error if invite has already been redeemed" do
|
2021-04-02 16:11:07 +08:00
|
|
|
|
expect(invite.redeem).not_to eq(nil)
|
2018-05-08 22:42:58 +08:00
|
|
|
|
|
2021-03-26 00:26:22 +08:00
|
|
|
|
get "/invites/#{invite.invite_key}"
|
2018-06-07 16:11:09 +08:00
|
|
|
|
expect(response.status).to eq(200)
|
2023-11-27 14:04:13 +08:00
|
|
|
|
|
|
|
|
|
expect(response.body).to have_tag(:body, with: { class: "no-ember" })
|
2021-03-26 00:26:22 +08:00
|
|
|
|
expect(response.body).to include(
|
|
|
|
|
I18n.t(
|
|
|
|
|
"invite.not_found_template",
|
|
|
|
|
site_name: SiteSetting.title,
|
|
|
|
|
base_url: Discourse.base_url,
|
2023-01-09 19:18:21 +08:00
|
|
|
|
),
|
2021-03-26 00:26:22 +08:00
|
|
|
|
)
|
2021-04-02 16:11:07 +08:00
|
|
|
|
|
|
|
|
|
invite.update!(email: nil) # convert to email invite
|
|
|
|
|
|
|
|
|
|
get "/invites/#{invite.invite_key}"
|
|
|
|
|
expect(response.status).to eq(200)
|
2023-11-27 14:04:13 +08:00
|
|
|
|
|
|
|
|
|
expect(response.body).to have_tag(:body, with: { class: "no-ember" })
|
2021-04-02 16:11:07 +08:00
|
|
|
|
expect(response.body).to include(
|
|
|
|
|
I18n.t(
|
|
|
|
|
"invite.not_found_template_link",
|
|
|
|
|
site_name: SiteSetting.title,
|
|
|
|
|
base_url: Discourse.base_url,
|
2023-01-09 19:18:21 +08:00
|
|
|
|
),
|
2021-04-02 16:11:07 +08:00
|
|
|
|
)
|
2018-05-08 22:42:58 +08:00
|
|
|
|
end
|
|
|
|
|
end
|
2018-06-01 13:06:08 +08:00
|
|
|
|
|
2022-07-27 18:21:10 +08:00
|
|
|
|
describe "#create" do
|
2021-03-26 00:26:22 +08:00
|
|
|
|
it "requires to be logged in" do
|
|
|
|
|
post "/invites.json", params: { email: "test@example.com" }
|
2018-06-01 13:06:08 +08:00
|
|
|
|
expect(response.status).to eq(403)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context "while logged in" do
|
2021-03-26 00:26:22 +08:00
|
|
|
|
before { sign_in(user) }
|
2018-06-01 13:06:08 +08:00
|
|
|
|
|
2021-03-26 00:26:22 +08:00
|
|
|
|
it "fails if you cannot invite to the forum" do
|
|
|
|
|
sign_in(Fabricate(:user))
|
2018-06-01 13:06:08 +08:00
|
|
|
|
|
2021-03-26 00:26:22 +08:00
|
|
|
|
post "/invites.json", params: { email: "test@example.com" }
|
|
|
|
|
expect(response).to be_forbidden
|
2018-06-01 13:06:08 +08:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2022-07-28 00:14:14 +08:00
|
|
|
|
context "with invite to topic" do
|
2023-11-10 06:47:59 +08:00
|
|
|
|
fab!(:topic)
|
2018-06-01 13:06:08 +08:00
|
|
|
|
|
2021-03-26 00:26:22 +08:00
|
|
|
|
it "works" do
|
|
|
|
|
sign_in(user)
|
2018-06-01 13:06:08 +08:00
|
|
|
|
|
2022-09-13 01:16:53 +08:00
|
|
|
|
post "/invites.json",
|
|
|
|
|
params: {
|
|
|
|
|
email: "test@example.com",
|
|
|
|
|
topic_id: topic.id,
|
|
|
|
|
invite_to_topic: true,
|
|
|
|
|
}
|
2021-03-26 00:26:22 +08:00
|
|
|
|
expect(response.status).to eq(200)
|
2022-09-13 01:16:53 +08:00
|
|
|
|
expect(Jobs::InviteEmail.jobs.first["args"].first["invite_to_topic"]).to be_truthy
|
2018-06-01 13:06:08 +08:00
|
|
|
|
end
|
|
|
|
|
|
2021-03-26 00:26:22 +08:00
|
|
|
|
it "fails when topic_id is invalid" do
|
|
|
|
|
sign_in(user)
|
|
|
|
|
|
|
|
|
|
post "/invites.json", params: { email: "test@example.com", topic_id: -9999 }
|
|
|
|
|
expect(response.status).to eq(400)
|
2018-06-01 13:06:08 +08:00
|
|
|
|
end
|
2022-02-17 00:35:02 +08:00
|
|
|
|
|
2022-07-28 00:14:14 +08:00
|
|
|
|
context "when topic is private" do
|
2023-11-10 06:47:59 +08:00
|
|
|
|
fab!(:group)
|
2022-02-17 00:35:02 +08:00
|
|
|
|
|
2023-12-06 20:16:10 +08:00
|
|
|
|
fab!(:secured_category) do
|
2022-02-17 00:35:02 +08:00
|
|
|
|
category = Fabricate(:category)
|
|
|
|
|
category.permissions = { group.name => :full }
|
|
|
|
|
category.save!
|
|
|
|
|
category
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
fab!(:topic) { Fabricate(:topic, category: secured_category) }
|
|
|
|
|
|
|
|
|
|
it "does not work and returns a list of required groups" do
|
|
|
|
|
sign_in(admin)
|
|
|
|
|
|
|
|
|
|
post "/invites.json", params: { email: "test@example.com", topic_id: topic.id }
|
|
|
|
|
expect(response.status).to eq(422)
|
|
|
|
|
expect(response.parsed_body["errors"]).to contain_exactly(
|
|
|
|
|
I18n.t("invite.requires_groups", groups: group.name),
|
|
|
|
|
)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "does not work if user cannot edit groups" do
|
|
|
|
|
group.add(user)
|
|
|
|
|
sign_in(user)
|
|
|
|
|
|
|
|
|
|
post "/invites.json", params: { email: "test@example.com", topic_id: topic.id }
|
|
|
|
|
expect(response.status).to eq(403)
|
|
|
|
|
end
|
|
|
|
|
end
|
2021-03-26 00:26:22 +08:00
|
|
|
|
end
|
2018-06-01 13:06:08 +08:00
|
|
|
|
|
2022-07-28 00:14:14 +08:00
|
|
|
|
context "with invite to group" do
|
2023-11-10 06:47:59 +08:00
|
|
|
|
fab!(:group)
|
2021-03-26 00:26:22 +08:00
|
|
|
|
|
|
|
|
|
it "works for admins" do
|
2020-06-09 23:19:32 +08:00
|
|
|
|
sign_in(admin)
|
2021-03-26 00:26:22 +08:00
|
|
|
|
|
|
|
|
|
post "/invites.json", params: { email: "test@example.com", group_ids: [group.id] }
|
2018-06-07 16:11:09 +08:00
|
|
|
|
expect(response.status).to eq(200)
|
2021-03-26 00:26:22 +08:00
|
|
|
|
expect(Invite.find_by(email: "test@example.com").invited_groups.count).to eq(1)
|
2018-06-01 13:06:08 +08:00
|
|
|
|
end
|
|
|
|
|
|
2021-03-26 00:26:22 +08:00
|
|
|
|
it "works for group owners" do
|
|
|
|
|
sign_in(user)
|
2018-06-01 13:06:08 +08:00
|
|
|
|
group.add_owner(user)
|
|
|
|
|
|
2021-03-26 00:26:22 +08:00
|
|
|
|
post "/invites.json", params: { email: "test@example.com", group_ids: [group.id] }
|
2018-06-07 16:11:09 +08:00
|
|
|
|
expect(response.status).to eq(200)
|
2021-03-26 00:26:22 +08:00
|
|
|
|
expect(Invite.find_by(email: "test@example.com").invited_groups.count).to eq(1)
|
2018-06-01 13:06:08 +08:00
|
|
|
|
end
|
|
|
|
|
|
2021-03-26 00:26:22 +08:00
|
|
|
|
it "works with multiple groups" do
|
2020-06-09 23:19:32 +08:00
|
|
|
|
sign_in(admin)
|
2021-03-26 00:26:22 +08:00
|
|
|
|
group2 = Fabricate(:group)
|
2018-06-01 13:06:08 +08:00
|
|
|
|
|
2021-03-26 00:26:22 +08:00
|
|
|
|
post "/invites.json",
|
|
|
|
|
params: {
|
|
|
|
|
email: "test@example.com",
|
|
|
|
|
group_names: "#{group.name},#{group2.name}",
|
|
|
|
|
}
|
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
|
expect(Invite.find_by(email: "test@example.com").invited_groups.count).to eq(2)
|
2020-06-09 23:19:32 +08:00
|
|
|
|
end
|
2018-06-01 13:06:08 +08:00
|
|
|
|
|
2021-03-26 00:26:22 +08:00
|
|
|
|
it "fails for group members" do
|
|
|
|
|
sign_in(user)
|
|
|
|
|
group.add(user)
|
2018-06-01 13:06:08 +08:00
|
|
|
|
|
2021-03-26 00:26:22 +08:00
|
|
|
|
post "/invites.json", params: { email: "test@example.com", group_ids: [group.id] }
|
|
|
|
|
expect(response.status).to eq(403)
|
|
|
|
|
end
|
2018-06-01 13:06:08 +08:00
|
|
|
|
|
2021-03-26 00:26:22 +08:00
|
|
|
|
it "fails for other users" do
|
|
|
|
|
sign_in(user)
|
2018-06-01 13:06:08 +08:00
|
|
|
|
|
2021-03-26 00:26:22 +08:00
|
|
|
|
post "/invites.json", params: { email: "test@example.com", group_ids: [group.id] }
|
|
|
|
|
expect(response.status).to eq(403)
|
|
|
|
|
end
|
2020-06-10 09:29:28 +08:00
|
|
|
|
|
2021-03-26 00:26:22 +08:00
|
|
|
|
it "fails to invite new user to a group-private topic" do
|
|
|
|
|
sign_in(user)
|
|
|
|
|
private_category = Fabricate(:private_category, group: group)
|
|
|
|
|
group_private_topic = Fabricate(:topic, category: private_category)
|
2020-06-10 09:29:28 +08:00
|
|
|
|
|
2021-03-26 00:26:22 +08:00
|
|
|
|
post "/invites.json",
|
|
|
|
|
params: {
|
|
|
|
|
email: "test@example.com",
|
|
|
|
|
topic_id: group_private_topic.id,
|
|
|
|
|
}
|
|
|
|
|
expect(response.status).to eq(403)
|
|
|
|
|
end
|
|
|
|
|
end
|
2020-06-09 23:19:32 +08:00
|
|
|
|
|
2022-07-28 00:14:14 +08:00
|
|
|
|
context "with email invite" do
|
2022-05-10 23:45:43 +08:00
|
|
|
|
subject(:create_invite) { post "/invites.json", params: params }
|
|
|
|
|
|
|
|
|
|
let(:params) { { email: email } }
|
|
|
|
|
let(:email) { "test@example.com" }
|
|
|
|
|
|
2021-03-26 00:26:22 +08:00
|
|
|
|
before { sign_in(user) }
|
2020-06-09 23:19:32 +08:00
|
|
|
|
|
2022-05-10 23:45:43 +08:00
|
|
|
|
context "when doing successive calls" do
|
|
|
|
|
let(:invite) { Invite.last }
|
2020-06-09 23:19:32 +08:00
|
|
|
|
|
2022-05-10 23:45:43 +08:00
|
|
|
|
it "creates invite once and updates it after" do
|
|
|
|
|
create_invite
|
|
|
|
|
expect(response).to have_http_status :ok
|
|
|
|
|
expect(Jobs::InviteEmail.jobs.size).to eq(1)
|
2021-05-12 18:06:39 +08:00
|
|
|
|
|
2022-05-10 23:45:43 +08:00
|
|
|
|
create_invite
|
|
|
|
|
expect(response).to have_http_status :ok
|
|
|
|
|
expect(response.parsed_body["id"]).to eq(invite.id)
|
|
|
|
|
end
|
2021-03-26 00:26:22 +08:00
|
|
|
|
end
|
2020-06-09 23:19:32 +08:00
|
|
|
|
|
2022-05-10 23:45:43 +08:00
|
|
|
|
context 'when "skip_email" parameter is provided' do
|
|
|
|
|
before { params[:skip_email] = true }
|
2020-06-09 23:19:32 +08:00
|
|
|
|
|
2022-05-10 23:45:43 +08:00
|
|
|
|
it "accepts the parameter" do
|
|
|
|
|
create_invite
|
|
|
|
|
expect(response).to have_http_status :ok
|
|
|
|
|
expect(Jobs::InviteEmail.jobs.size).to eq(0)
|
|
|
|
|
end
|
2021-03-26 00:26:22 +08:00
|
|
|
|
end
|
2020-06-09 23:19:32 +08:00
|
|
|
|
|
2022-05-10 23:45:43 +08:00
|
|
|
|
context "when validations fail" do
|
|
|
|
|
let(:email) { "test@mailinator.com" }
|
2020-06-09 23:19:32 +08:00
|
|
|
|
|
2022-05-10 23:45:43 +08:00
|
|
|
|
it "fails" do
|
|
|
|
|
create_invite
|
|
|
|
|
expect(response).to have_http_status :unprocessable_entity
|
|
|
|
|
expect(response.parsed_body["errors"]).to be_present
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2024-02-20 05:34:40 +08:00
|
|
|
|
context "when email address is too long" do
|
|
|
|
|
let(:email) { "a" * 495 + "@example.com" }
|
|
|
|
|
|
|
|
|
|
it "fails" do
|
|
|
|
|
create_invite
|
|
|
|
|
expect(response).to have_http_status :unprocessable_entity
|
|
|
|
|
expect(response.parsed_body["errors"]).to be_present
|
|
|
|
|
error_message = response.parsed_body["errors"].first
|
|
|
|
|
expect(error_message).to eq("Email is too long (maximum is 500 characters)")
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2022-05-10 23:45:43 +08:00
|
|
|
|
context "when providing an email belonging to an existing user" do
|
|
|
|
|
let(:email) { user.email }
|
|
|
|
|
|
|
|
|
|
before { SiteSetting.hide_email_address_taken = hide_email_address_taken }
|
|
|
|
|
|
|
|
|
|
context 'when "hide_email_address_taken" setting is disabled' do
|
|
|
|
|
let(:hide_email_address_taken) { false }
|
|
|
|
|
|
|
|
|
|
it "returns an error" do
|
|
|
|
|
create_invite
|
|
|
|
|
expect(response).to have_http_status :unprocessable_entity
|
|
|
|
|
expect(body).to match(/no need to invite/)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context 'when "hide_email_address_taken" setting is enabled' do
|
|
|
|
|
let(:hide_email_address_taken) { true }
|
|
|
|
|
|
|
|
|
|
it "doesn’t inform the user" do
|
|
|
|
|
create_invite
|
2023-03-08 23:38:58 +08:00
|
|
|
|
expect(response).to have_http_status :unprocessable_entity
|
|
|
|
|
expect(body).to match(/There was a problem with your request./)
|
2022-05-10 23:45:43 +08:00
|
|
|
|
end
|
|
|
|
|
end
|
2020-06-09 23:19:32 +08:00
|
|
|
|
end
|
|
|
|
|
end
|
2018-06-01 13:06:08 +08:00
|
|
|
|
|
2024-02-20 05:34:40 +08:00
|
|
|
|
context "with domain invite" do
|
|
|
|
|
it "works" do
|
|
|
|
|
sign_in(admin)
|
|
|
|
|
|
|
|
|
|
post "/invites.json", params: { domain: "example.com" }
|
|
|
|
|
expect(response).to have_http_status :ok
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "fails when domain is invalid" do
|
|
|
|
|
sign_in(admin)
|
|
|
|
|
|
|
|
|
|
post "/invites.json", params: { domain: "example" }
|
|
|
|
|
|
|
|
|
|
expect(response).to have_http_status :unprocessable_entity
|
|
|
|
|
|
|
|
|
|
error_message = response.parsed_body["errors"].first
|
|
|
|
|
expect(error_message).to eq(I18n.t("invite.domain_not_allowed_admin"))
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "fails when domain is too long" do
|
|
|
|
|
sign_in(admin)
|
|
|
|
|
|
|
|
|
|
post "/invites.json", params: { domain: "a" * 500 + ".ca" }
|
|
|
|
|
expect(response).to have_http_status :unprocessable_entity
|
|
|
|
|
|
|
|
|
|
error_message = response.parsed_body["errors"].first
|
|
|
|
|
expect(error_message).to eq("Domain is too long (maximum is 500 characters)")
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "fails when custom message is too long" do
|
|
|
|
|
sign_in(admin)
|
|
|
|
|
|
|
|
|
|
post "/invites.json", params: { custom_message: "b" * 1001, domain: "example.com" }
|
|
|
|
|
expect(response).to have_http_status :unprocessable_entity
|
|
|
|
|
|
|
|
|
|
error_message = response.parsed_body["errors"].first
|
|
|
|
|
expect(error_message).to eq("Custom message is too long (maximum is 1000 characters)")
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2022-07-28 00:14:14 +08:00
|
|
|
|
context "with link invite" do
|
2021-03-26 00:26:22 +08:00
|
|
|
|
it "works" do
|
|
|
|
|
sign_in(admin)
|
|
|
|
|
|
|
|
|
|
post "/invites.json"
|
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
|
expect(Invite.last.email).to eq(nil)
|
|
|
|
|
expect(Invite.last.invited_by).to eq(admin)
|
|
|
|
|
expect(Invite.last.max_redemptions_allowed).to eq(1)
|
2018-06-01 13:06:08 +08:00
|
|
|
|
end
|
|
|
|
|
|
2021-03-26 00:26:22 +08:00
|
|
|
|
it "fails if over invite_link_max_redemptions_limit" do
|
|
|
|
|
sign_in(admin)
|
2018-06-01 13:06:08 +08:00
|
|
|
|
|
2021-03-26 00:26:22 +08:00
|
|
|
|
post "/invites.json",
|
|
|
|
|
params: {
|
|
|
|
|
max_redemptions_allowed: SiteSetting.invite_link_max_redemptions_limit - 1,
|
|
|
|
|
}
|
|
|
|
|
expect(response.status).to eq(200)
|
2020-06-09 23:19:32 +08:00
|
|
|
|
|
2021-03-26 00:26:22 +08:00
|
|
|
|
post "/invites.json",
|
|
|
|
|
params: {
|
|
|
|
|
max_redemptions_allowed: SiteSetting.invite_link_max_redemptions_limit + 1,
|
|
|
|
|
}
|
|
|
|
|
expect(response.status).to eq(422)
|
|
|
|
|
end
|
2018-06-01 13:06:08 +08:00
|
|
|
|
|
2021-03-26 00:26:22 +08:00
|
|
|
|
it "fails if over invite_link_max_redemptions_limit_users" do
|
|
|
|
|
sign_in(user)
|
2018-06-01 13:06:08 +08:00
|
|
|
|
|
2021-03-26 00:26:22 +08:00
|
|
|
|
post "/invites.json",
|
|
|
|
|
params: {
|
|
|
|
|
max_redemptions_allowed: SiteSetting.invite_link_max_redemptions_limit_users - 1,
|
|
|
|
|
}
|
|
|
|
|
expect(response.status).to eq(200)
|
2018-06-01 13:06:08 +08:00
|
|
|
|
|
2021-03-26 00:26:22 +08:00
|
|
|
|
post "/invites.json",
|
|
|
|
|
params: {
|
|
|
|
|
max_redemptions_allowed: SiteSetting.invite_link_max_redemptions_limit_users + 1,
|
|
|
|
|
}
|
|
|
|
|
expect(response.status).to eq(422)
|
2018-06-01 13:06:08 +08:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2023-12-28 23:16:04 +08:00
|
|
|
|
describe "#create-multiple" do
|
|
|
|
|
it "fails if you are not admin" do
|
|
|
|
|
sign_in(Fabricate(:user))
|
|
|
|
|
post "/invites/create-multiple.json",
|
|
|
|
|
params: {
|
|
|
|
|
email: %w[test@example.com test1@example.com bademail],
|
|
|
|
|
}
|
|
|
|
|
expect(response.status).to eq(403)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "creates multiple invites for multiple emails" do
|
|
|
|
|
sign_in(admin)
|
|
|
|
|
post "/invites/create-multiple.json",
|
|
|
|
|
params: {
|
|
|
|
|
email: %w[test@example.com test1@example.com bademail],
|
|
|
|
|
}
|
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
|
json = JSON(response.body)
|
|
|
|
|
expect(json["failed_invitations"].length).to eq(1)
|
|
|
|
|
expect(json["successful_invitations"].length).to eq(2)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "creates many invite codes with one request" do #change to
|
|
|
|
|
sign_in(admin)
|
|
|
|
|
num_emails = 5 # increase manually for load testing
|
|
|
|
|
post "/invites/create-multiple.json",
|
|
|
|
|
params: {
|
|
|
|
|
email: 1.upto(num_emails).map { |i| "test#{i}@example.com" },
|
|
|
|
|
#email: %w[test+1@example.com test1@example.com]
|
|
|
|
|
}
|
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
|
json = JSON(response.body)
|
|
|
|
|
expect(json["failed_invitations"].length).to eq(0)
|
|
|
|
|
expect(json["successful_invitations"].length).to eq(num_emails)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context "with invite to topic" do
|
|
|
|
|
fab!(:topic)
|
|
|
|
|
|
|
|
|
|
it "works" do
|
|
|
|
|
sign_in(admin)
|
|
|
|
|
|
|
|
|
|
post "/invites/create-multiple.json",
|
|
|
|
|
params: {
|
|
|
|
|
email: ["test@example.com"],
|
|
|
|
|
topic_id: topic.id,
|
|
|
|
|
invite_to_topic: true,
|
|
|
|
|
}
|
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
|
expect(Jobs::InviteEmail.jobs.first["args"].first["invite_to_topic"]).to be_truthy
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "fails when topic_id is invalid" do
|
|
|
|
|
sign_in(admin)
|
|
|
|
|
|
|
|
|
|
post "/invites/create-multiple.json",
|
|
|
|
|
params: {
|
|
|
|
|
email: ["test@example.com"],
|
|
|
|
|
topic_id: -9999,
|
|
|
|
|
}
|
|
|
|
|
expect(response.status).to eq(400)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context "with invite to group" do
|
|
|
|
|
fab!(:group)
|
|
|
|
|
|
|
|
|
|
it "works for admins" do
|
|
|
|
|
sign_in(admin)
|
|
|
|
|
|
|
|
|
|
post "/invites/create-multiple.json",
|
|
|
|
|
params: {
|
|
|
|
|
email: ["test@example.com"],
|
|
|
|
|
group_ids: [group.id],
|
|
|
|
|
}
|
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
|
expect(Invite.find_by(email: "test@example.com").invited_groups.count).to eq(1)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "works with multiple groups" do
|
|
|
|
|
sign_in(admin)
|
|
|
|
|
group2 = Fabricate(:group)
|
|
|
|
|
|
|
|
|
|
post "/invites/create-multiple.json",
|
|
|
|
|
params: {
|
|
|
|
|
email: ["test@example.com"],
|
|
|
|
|
group_names: "#{group.name},#{group2.name}",
|
|
|
|
|
}
|
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
|
expect(Invite.find_by(email: "test@example.com").invited_groups.count).to eq(2)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context "with email invite" do
|
|
|
|
|
subject(:create_multiple_invites) { post "/invites/create-multiple.json", params: params }
|
|
|
|
|
|
|
|
|
|
let(:params) { { email: [email] } }
|
|
|
|
|
let(:email) { "test@example.com" }
|
|
|
|
|
|
|
|
|
|
before { sign_in(admin) }
|
|
|
|
|
|
|
|
|
|
context "when doing successive calls" do
|
|
|
|
|
let(:invite) { Invite.last }
|
|
|
|
|
|
|
|
|
|
it "creates invite once and updates it after" do
|
|
|
|
|
create_multiple_invites
|
|
|
|
|
expect(response).to have_http_status :ok
|
|
|
|
|
expect(Jobs::InviteEmail.jobs.size).to eq(1)
|
|
|
|
|
|
|
|
|
|
create_multiple_invites
|
|
|
|
|
expect(response).to have_http_status :ok
|
|
|
|
|
expect(response.parsed_body["successful_invitations"][0]["invite"]["id"]).to eq(invite.id)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context 'when "skip_email" parameter is provided' do
|
|
|
|
|
before { params[:skip_email] = true }
|
|
|
|
|
|
|
|
|
|
it "accepts the parameter" do
|
|
|
|
|
create_multiple_invites
|
|
|
|
|
expect(response).to have_http_status :ok
|
|
|
|
|
expect(Jobs::InviteEmail.jobs.size).to eq(0)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "fails if asked to generate too many invites at once" do
|
|
|
|
|
SiteSetting.max_api_invites = 3
|
|
|
|
|
sign_in(admin)
|
|
|
|
|
post "/invites/create-multiple.json",
|
|
|
|
|
params: {
|
|
|
|
|
email: %w[
|
|
|
|
|
mail1@mailinator.com
|
|
|
|
|
mail2@mailinator.com
|
|
|
|
|
mail3@mailinator.com
|
|
|
|
|
mail4@mailinator.com
|
|
|
|
|
],
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
expect(response.status).to eq(422)
|
|
|
|
|
expect(response.parsed_body["errors"][0]).to eq(
|
|
|
|
|
I18n.t("invite.max_invite_emails_limit_exceeded", max: SiteSetting.max_api_invites),
|
|
|
|
|
)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2022-07-27 18:21:10 +08:00
|
|
|
|
describe "#retrieve" do
|
2021-04-06 23:01:07 +08:00
|
|
|
|
it "requires to be logged in" do
|
|
|
|
|
get "/invites/retrieve.json", params: { email: "test@example.com" }
|
|
|
|
|
expect(response.status).to eq(403)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context "while logged in" do
|
|
|
|
|
before { sign_in(user) }
|
|
|
|
|
|
|
|
|
|
fab!(:invite) { Fabricate(:invite, invited_by: user, email: "test@example.com") }
|
|
|
|
|
|
|
|
|
|
it "raises an error when the email is missing" do
|
|
|
|
|
get "/invites/retrieve.json"
|
|
|
|
|
expect(response.status).to eq(400)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "raises an error when the email cannot be found" do
|
|
|
|
|
get "/invites/retrieve.json", params: { email: "test2@example.com" }
|
|
|
|
|
expect(response.status).to eq(400)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "can retrieve the invite" do
|
|
|
|
|
get "/invites/retrieve.json", params: { email: "test@example.com" }
|
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2022-07-27 18:21:10 +08:00
|
|
|
|
describe "#update" do
|
2021-03-03 17:45:29 +08:00
|
|
|
|
fab!(:invite) { Fabricate(:invite, invited_by: admin, email: "test@example.com") }
|
|
|
|
|
|
2021-03-26 00:26:22 +08:00
|
|
|
|
it "requires to be logged in" do
|
|
|
|
|
put "/invites/#{invite.id}", params: { email: "test2@example.com" }
|
|
|
|
|
expect(response.status).to eq(400)
|
2021-03-06 19:29:35 +08:00
|
|
|
|
end
|
|
|
|
|
|
2021-03-26 00:26:22 +08:00
|
|
|
|
context "while logged in" do
|
|
|
|
|
before { sign_in(admin) }
|
2021-03-03 17:45:29 +08:00
|
|
|
|
|
2021-03-26 00:26:22 +08:00
|
|
|
|
it "resends invite email if updating email address" do
|
|
|
|
|
put "/invites/#{invite.id}", params: { email: "test2@example.com" }
|
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
|
expect(Jobs::InviteEmail.jobs.size).to eq(1)
|
|
|
|
|
end
|
2021-03-03 17:45:29 +08:00
|
|
|
|
|
2021-03-26 00:26:22 +08:00
|
|
|
|
it "does not resend invite email if skip_email if updating email address" do
|
|
|
|
|
put "/invites/#{invite.id}", params: { email: "test2@example.com", skip_email: true }
|
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
|
expect(Jobs::InviteEmail.jobs.size).to eq(0)
|
|
|
|
|
end
|
2021-03-03 17:45:29 +08:00
|
|
|
|
|
2021-03-26 00:26:22 +08:00
|
|
|
|
it "does not resend invite email when updating other fields" do
|
|
|
|
|
put "/invites/#{invite.id}", params: { custom_message: "new message" }
|
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
|
expect(invite.reload.custom_message).to eq("new message")
|
|
|
|
|
expect(Jobs::InviteEmail.jobs.size).to eq(0)
|
|
|
|
|
end
|
2021-03-03 17:45:29 +08:00
|
|
|
|
|
2021-05-12 18:06:39 +08:00
|
|
|
|
it "cannot create duplicated invites" do
|
|
|
|
|
Fabricate(:invite, invited_by: admin, email: "test2@example.com")
|
|
|
|
|
|
|
|
|
|
put "/invites/#{invite.id}.json", params: { email: "test2@example.com" }
|
|
|
|
|
expect(response.status).to eq(409)
|
|
|
|
|
end
|
2022-05-10 23:45:43 +08:00
|
|
|
|
|
2023-06-16 10:44:35 +08:00
|
|
|
|
describe "rate limiting" do
|
|
|
|
|
before { RateLimiter.enable }
|
|
|
|
|
|
|
|
|
|
use_redis_snapshotting
|
|
|
|
|
|
|
|
|
|
it "can send invite email" do
|
|
|
|
|
sign_in(user)
|
|
|
|
|
|
|
|
|
|
invite = Fabricate(:invite, invited_by: user, email: "test@example.com")
|
|
|
|
|
|
|
|
|
|
expect { put "/invites/#{invite.id}", params: { send_email: true } }.to change {
|
|
|
|
|
RateLimiter.new(user, "resend-invite-per-hour", 10, 1.hour).remaining
|
|
|
|
|
}.by(-1)
|
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
|
expect(Jobs::InviteEmail.jobs.size).to eq(1)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2022-05-10 23:45:43 +08:00
|
|
|
|
context "when providing an email belonging to an existing user" do
|
|
|
|
|
subject(:update_invite) { put "/invites/#{invite.id}.json", params: { email: admin.email } }
|
|
|
|
|
|
|
|
|
|
before { SiteSetting.hide_email_address_taken = hide_email_address_taken }
|
|
|
|
|
|
|
|
|
|
context "when 'hide_email_address_taken' setting is disabled" do
|
|
|
|
|
let(:hide_email_address_taken) { false }
|
|
|
|
|
|
|
|
|
|
it "returns an error" do
|
|
|
|
|
update_invite
|
|
|
|
|
expect(response).to have_http_status :unprocessable_entity
|
|
|
|
|
expect(body).to match(/no need to invite/)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context "when 'hide_email_address_taken' setting is enabled" do
|
|
|
|
|
let(:hide_email_address_taken) { true }
|
|
|
|
|
|
|
|
|
|
it "doesn't inform the user" do
|
|
|
|
|
update_invite
|
2023-03-08 23:38:58 +08:00
|
|
|
|
expect(response).to have_http_status :unprocessable_entity
|
|
|
|
|
expect(body).to match(/There was a problem with your request./)
|
2022-05-10 23:45:43 +08:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
2021-03-03 17:45:29 +08:00
|
|
|
|
end
|
2021-03-26 00:26:22 +08:00
|
|
|
|
end
|
2021-03-06 19:29:35 +08:00
|
|
|
|
|
2022-07-27 18:21:10 +08:00
|
|
|
|
describe "#destroy" do
|
2021-03-26 00:26:22 +08:00
|
|
|
|
it "requires to be logged in" do
|
|
|
|
|
delete "/invites.json", params: { email: "test@example.com" }
|
|
|
|
|
expect(response.status).to eq(403)
|
|
|
|
|
end
|
2021-03-06 19:29:35 +08:00
|
|
|
|
|
2021-03-26 00:26:22 +08:00
|
|
|
|
context "while logged in" do
|
|
|
|
|
fab!(:invite) { Fabricate(:invite, invited_by: user) }
|
2021-03-06 19:29:35 +08:00
|
|
|
|
|
2021-03-26 00:26:22 +08:00
|
|
|
|
before { sign_in(user) }
|
2021-03-06 19:29:35 +08:00
|
|
|
|
|
2021-03-26 00:26:22 +08:00
|
|
|
|
it "raises an error when id is missing" do
|
|
|
|
|
delete "/invites.json"
|
|
|
|
|
expect(response.status).to eq(400)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "raises an error when invite does not exist" do
|
|
|
|
|
delete "/invites.json", params: { id: 848 }
|
|
|
|
|
expect(response.status).to eq(400)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "raises an error when invite is not created by user" do
|
|
|
|
|
another_invite = Fabricate(:invite, email: "test2@example.com")
|
|
|
|
|
|
|
|
|
|
delete "/invites.json", params: { id: another_invite.id }
|
|
|
|
|
expect(response.status).to eq(400)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "destroys the invite" do
|
|
|
|
|
delete "/invites.json", params: { id: invite.id }
|
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
|
expect(invite.reload.trashed?).to be_truthy
|
|
|
|
|
end
|
2021-03-06 19:29:35 +08:00
|
|
|
|
end
|
2021-03-03 17:45:29 +08:00
|
|
|
|
end
|
|
|
|
|
|
2022-07-27 18:21:10 +08:00
|
|
|
|
describe "#perform_accept_invitation" do
|
2021-03-26 00:26:22 +08:00
|
|
|
|
context "with an invalid invite" do
|
|
|
|
|
it "redirects to the root" do
|
|
|
|
|
put "/invites/show/doesntexist.json"
|
2021-03-03 17:45:29 +08:00
|
|
|
|
expect(response.status).to eq(404)
|
2021-03-26 00:26:22 +08:00
|
|
|
|
expect(response.parsed_body["message"]).to eq(I18n.t("invite.not_found_json"))
|
2019-02-06 21:49:00 +08:00
|
|
|
|
expect(session[:current_user_id]).to be_blank
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2018-06-01 13:06:08 +08:00
|
|
|
|
context "with a deleted invite" do
|
2023-11-10 06:47:59 +08:00
|
|
|
|
fab!(:invite)
|
2018-12-05 23:43:07 +08:00
|
|
|
|
|
2021-03-26 00:26:22 +08:00
|
|
|
|
before { invite.trash! }
|
2018-06-01 13:06:08 +08:00
|
|
|
|
|
2021-03-26 00:26:22 +08:00
|
|
|
|
it "redirects to the root" do
|
2018-06-01 13:06:08 +08:00
|
|
|
|
put "/invites/show/#{invite.invite_key}.json"
|
2021-03-03 17:45:29 +08:00
|
|
|
|
expect(response.status).to eq(404)
|
2021-03-26 00:26:22 +08:00
|
|
|
|
expect(response.parsed_body["message"]).to eq(I18n.t("invite.not_found_json"))
|
2018-06-01 13:06:08 +08:00
|
|
|
|
expect(session[:current_user_id]).to be_blank
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2020-06-09 23:19:32 +08:00
|
|
|
|
context "with an expired invite" do
|
2021-03-26 00:26:22 +08:00
|
|
|
|
fab!(:invite) { Fabricate(:invite, expires_at: 1.day.ago) }
|
2020-06-09 23:19:32 +08:00
|
|
|
|
|
2021-03-26 00:26:22 +08:00
|
|
|
|
it "response is not successful" do
|
|
|
|
|
put "/invites/show/#{invite.invite_key}.json"
|
2021-03-03 17:45:29 +08:00
|
|
|
|
expect(response.status).to eq(404)
|
2021-03-26 00:26:22 +08:00
|
|
|
|
expect(response.parsed_body["message"]).to eq(I18n.t("invite.not_found_json"))
|
2020-06-09 23:19:32 +08:00
|
|
|
|
expect(session[:current_user_id]).to be_blank
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2021-03-26 00:26:22 +08:00
|
|
|
|
context "with an email invite" do
|
|
|
|
|
let(:topic) { Fabricate(:topic) }
|
|
|
|
|
let(:invite) { Invite.generate(topic.user, email: "iceking@adventuretime.ooo", topic: topic) }
|
2018-06-01 13:06:08 +08:00
|
|
|
|
|
|
|
|
|
it "redeems the invite" do
|
|
|
|
|
put "/invites/show/#{invite.invite_key}.json"
|
2021-03-26 00:26:22 +08:00
|
|
|
|
expect(invite.reload.redeemed?).to be_truthy
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "logs in the user" do
|
|
|
|
|
events =
|
|
|
|
|
DiscourseEvent.track_events do
|
2021-04-14 17:15:56 +08:00
|
|
|
|
put "/invites/show/#{invite.invite_key}.json",
|
|
|
|
|
params: {
|
|
|
|
|
email_token: invite.email_token,
|
|
|
|
|
}
|
2021-03-26 00:26:22 +08:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
expect(events.map { |event| event[:event_name] }).to include(
|
|
|
|
|
:user_logged_in,
|
|
|
|
|
:user_first_logged_in,
|
|
|
|
|
)
|
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
|
expect(session[:current_user_id]).to eq(invite.invited_users.first.user_id)
|
|
|
|
|
expect(invite.reload.redeemed?).to be_truthy
|
|
|
|
|
user = User.find(invite.invited_users.first.user_id)
|
|
|
|
|
expect(user.ip_address).to be_present
|
|
|
|
|
expect(user.registration_ip_address).to be_present
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "redirects to the first topic the user was invited to" do
|
2021-04-14 17:15:56 +08:00
|
|
|
|
put "/invites/show/#{invite.invite_key}.json", params: { email_token: invite.email_token }
|
2021-03-26 00:26:22 +08:00
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
|
expect(response.parsed_body["redirect_to"]).to eq(topic.relative_url)
|
2021-08-26 15:43:56 +08:00
|
|
|
|
expect(
|
|
|
|
|
Notification.where(
|
|
|
|
|
notification_type: Notification.types[:invited_to_topic],
|
|
|
|
|
topic: topic,
|
|
|
|
|
).count,
|
|
|
|
|
).to eq(1)
|
2021-03-26 00:26:22 +08:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "sets the timezone of the user in user_options" do
|
|
|
|
|
put "/invites/show/#{invite.invite_key}.json", params: { timezone: "Australia/Melbourne" }
|
|
|
|
|
expect(response.status).to eq(200)
|
2018-06-01 13:06:08 +08:00
|
|
|
|
invite.reload
|
2021-03-26 00:26:22 +08:00
|
|
|
|
user = User.find(invite.invited_users.first.user_id)
|
|
|
|
|
expect(user.user_option.timezone).to eq("Australia/Melbourne")
|
2018-06-01 13:06:08 +08:00
|
|
|
|
end
|
|
|
|
|
|
2021-03-26 00:26:22 +08:00
|
|
|
|
it "does not log in the user if there are validation errors" do
|
|
|
|
|
put "/invites/show/#{invite.invite_key}.json", params: { password: "password" }
|
2022-10-26 20:04:55 +08:00
|
|
|
|
|
2021-03-26 00:26:22 +08:00
|
|
|
|
expect(response.status).to eq(412)
|
2022-10-26 20:04:55 +08:00
|
|
|
|
expect(session[:current_user_id]).to eq(nil)
|
2021-03-26 00:26:22 +08:00
|
|
|
|
end
|
|
|
|
|
|
2022-01-13 04:24:54 +08:00
|
|
|
|
it "does not log in the user if they were not approved" do
|
|
|
|
|
SiteSetting.must_approve_users = true
|
|
|
|
|
|
|
|
|
|
put "/invites/show/#{invite.invite_key}.json",
|
|
|
|
|
params: {
|
|
|
|
|
password: SecureRandom.hex,
|
|
|
|
|
email_token: invite.email_token,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
expect(session[:current_user_id]).to eq(nil)
|
|
|
|
|
expect(response.parsed_body["message"]).to eq(I18n.t("activation.approval_required"))
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "does not log in the user if they were not activated" do
|
|
|
|
|
put "/invites/show/#{invite.invite_key}.json", params: { password: SecureRandom.hex }
|
|
|
|
|
|
|
|
|
|
expect(session[:current_user_id]).to eq(nil)
|
|
|
|
|
expect(response.parsed_body["message"]).to eq(I18n.t("invite.confirm_email"))
|
|
|
|
|
end
|
|
|
|
|
|
2021-03-26 00:26:22 +08:00
|
|
|
|
it "fails when local login is disabled and no external auth is configured" do
|
2021-03-02 15:13:04 +08:00
|
|
|
|
SiteSetting.enable_local_logins = false
|
|
|
|
|
|
|
|
|
|
put "/invites/show/#{invite.invite_key}.json"
|
|
|
|
|
expect(response.status).to eq(404)
|
|
|
|
|
end
|
|
|
|
|
|
2021-03-26 00:26:22 +08:00
|
|
|
|
context "with OmniAuth provider" do
|
|
|
|
|
fab!(:authenticated_email) { "test@example.com" }
|
2021-03-02 15:13:04 +08:00
|
|
|
|
|
|
|
|
|
before do
|
|
|
|
|
OmniAuth.config.test_mode = true
|
|
|
|
|
|
|
|
|
|
OmniAuth.config.mock_auth[:google_oauth2] = OmniAuth::AuthHash.new(
|
|
|
|
|
provider: "google_oauth2",
|
|
|
|
|
uid: "12345",
|
|
|
|
|
info: OmniAuth::AuthHash::InfoHash.new(email: authenticated_email, name: "First Last"),
|
|
|
|
|
extra: {
|
|
|
|
|
raw_info:
|
|
|
|
|
OmniAuth::AuthHash.new(
|
|
|
|
|
email_verified: true,
|
|
|
|
|
email: authenticated_email,
|
2021-03-26 00:26:22 +08:00
|
|
|
|
family_name: "Last",
|
|
|
|
|
given_name: "First",
|
|
|
|
|
gender: "male",
|
|
|
|
|
name: "First Last",
|
2021-03-02 15:13:04 +08:00
|
|
|
|
),
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
|
2021-03-26 00:26:22 +08:00
|
|
|
|
Rails.application.env_config["omniauth.auth"] = OmniAuth.config.mock_auth[:google_oauth2]
|
2021-03-02 15:13:04 +08:00
|
|
|
|
SiteSetting.enable_google_oauth2_logins = true
|
|
|
|
|
|
2021-03-26 00:26:22 +08:00
|
|
|
|
get "/auth/google_oauth2/callback.json"
|
2021-03-02 15:13:04 +08:00
|
|
|
|
expect(response.status).to eq(302)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
after do
|
2021-03-26 00:26:22 +08:00
|
|
|
|
Rails.application.env_config["omniauth.auth"] = OmniAuth.config.mock_auth[
|
|
|
|
|
:google_oauth2
|
|
|
|
|
] = nil
|
2021-03-02 15:13:04 +08:00
|
|
|
|
OmniAuth.config.test_mode = false
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "should associate the invited user with authenticator records" do
|
|
|
|
|
SiteSetting.auth_overrides_name = true
|
2021-03-26 00:26:22 +08:00
|
|
|
|
invite.update!(email: authenticated_email)
|
2021-03-02 15:13:04 +08:00
|
|
|
|
|
2021-03-26 00:26:22 +08:00
|
|
|
|
expect {
|
|
|
|
|
put "/invites/show/#{invite.invite_key}.json", params: { name: "somename" }
|
|
|
|
|
}.to change { User.with_email(authenticated_email).exists? }.to(true)
|
|
|
|
|
expect(response.status).to eq(200)
|
2021-03-02 15:13:04 +08:00
|
|
|
|
|
|
|
|
|
user = User.find_by_email(authenticated_email)
|
|
|
|
|
expect(user.name).to eq("First Last")
|
2021-03-26 00:26:22 +08:00
|
|
|
|
expect(user.user_associated_accounts.first.provider_name).to eq("google_oauth2")
|
2021-03-02 15:13:04 +08:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "returns the right response even if local logins has been disabled" do
|
|
|
|
|
SiteSetting.enable_local_logins = false
|
|
|
|
|
invite.update!(email: authenticated_email)
|
|
|
|
|
|
|
|
|
|
put "/invites/show/#{invite.invite_key}.json"
|
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "returns the right response if authenticated email does not match invite email" do
|
|
|
|
|
put "/invites/show/#{invite.invite_key}.json"
|
|
|
|
|
expect(response.status).to eq(412)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2022-07-27 18:21:10 +08:00
|
|
|
|
describe ".post_process_invite" do
|
2021-03-26 00:26:22 +08:00
|
|
|
|
it "sends a welcome message if set" do
|
|
|
|
|
SiteSetting.send_welcome_message = true
|
|
|
|
|
user.send_welcome_message = true
|
|
|
|
|
put "/invites/show/#{invite.invite_key}.json"
|
|
|
|
|
expect(response.status).to eq(200)
|
2018-06-01 13:06:08 +08:00
|
|
|
|
|
2021-03-26 00:26:22 +08:00
|
|
|
|
expect(Jobs::SendSystemMessage.jobs.size).to eq(1)
|
|
|
|
|
end
|
2018-06-01 13:06:08 +08:00
|
|
|
|
|
2021-03-26 00:26:22 +08:00
|
|
|
|
it "refreshes automatic groups if staff" do
|
|
|
|
|
topic.user.grant_admin!
|
|
|
|
|
invite.update!(moderator: true)
|
2019-11-25 08:49:27 +08:00
|
|
|
|
|
2021-03-26 00:26:22 +08:00
|
|
|
|
put "/invites/show/#{invite.invite_key}.json"
|
|
|
|
|
expect(response.status).to eq(200)
|
2018-06-01 13:06:08 +08:00
|
|
|
|
|
2021-03-26 00:26:22 +08:00
|
|
|
|
expect(invite.invited_users.first.user.groups.pluck(:name)).to contain_exactly(
|
|
|
|
|
"moderators",
|
|
|
|
|
"staff",
|
|
|
|
|
)
|
2018-06-01 13:06:08 +08:00
|
|
|
|
end
|
|
|
|
|
|
2021-03-26 00:26:22 +08:00
|
|
|
|
context "without password" do
|
|
|
|
|
it "sends password reset email" do
|
2018-06-01 13:06:08 +08:00
|
|
|
|
put "/invites/show/#{invite.invite_key}.json"
|
2018-06-07 16:11:09 +08:00
|
|
|
|
expect(response.status).to eq(200)
|
2018-12-12 01:04:07 +08:00
|
|
|
|
|
2021-03-26 00:26:22 +08:00
|
|
|
|
expect(Jobs::InvitePasswordInstructionsEmail.jobs.size).to eq(1)
|
|
|
|
|
expect(Jobs::CriticalUserEmail.jobs.size).to eq(0)
|
2018-06-01 13:06:08 +08:00
|
|
|
|
end
|
2021-03-26 00:26:22 +08:00
|
|
|
|
end
|
2018-06-01 13:06:08 +08:00
|
|
|
|
|
2021-03-26 00:26:22 +08:00
|
|
|
|
context "with password" do
|
2022-07-28 00:14:14 +08:00
|
|
|
|
context "when user was invited via email" do
|
2021-03-26 00:26:22 +08:00
|
|
|
|
before { invite.update_column(:emailed_status, Invite.emailed_status_types[:pending]) }
|
2019-05-10 20:49:12 +08:00
|
|
|
|
|
2021-03-26 00:26:22 +08:00
|
|
|
|
it "does not send an activation email and activates the user" do
|
|
|
|
|
expect do
|
2021-04-14 17:15:56 +08:00
|
|
|
|
put "/invites/show/#{invite.invite_key}.json",
|
|
|
|
|
params: {
|
|
|
|
|
password: "verystrongpassword",
|
|
|
|
|
email_token: invite.email_token,
|
|
|
|
|
}
|
2021-03-26 00:26:22 +08:00
|
|
|
|
end.to change { UserAuthToken.count }.by(1)
|
2019-05-10 20:49:12 +08:00
|
|
|
|
|
2018-12-11 06:24:02 +08:00
|
|
|
|
expect(response.status).to eq(200)
|
2018-12-12 01:04:07 +08:00
|
|
|
|
|
2021-03-26 00:26:22 +08:00
|
|
|
|
expect(Jobs::InvitePasswordInstructionsEmail.jobs.size).to eq(0)
|
2018-12-11 06:24:02 +08:00
|
|
|
|
expect(Jobs::CriticalUserEmail.jobs.size).to eq(0)
|
|
|
|
|
|
2021-03-26 00:26:22 +08:00
|
|
|
|
invited_user = User.find_by_email(invite.email)
|
|
|
|
|
expect(invited_user.active).to eq(true)
|
|
|
|
|
expect(invited_user.email_confirmed?).to eq(true)
|
2018-12-11 06:24:02 +08:00
|
|
|
|
end
|
2021-04-14 17:15:56 +08:00
|
|
|
|
|
|
|
|
|
it "does not activate user if email token is missing" do
|
|
|
|
|
expect do
|
|
|
|
|
put "/invites/show/#{invite.invite_key}.json",
|
|
|
|
|
params: {
|
|
|
|
|
password: "verystrongpassword",
|
|
|
|
|
}
|
2022-07-19 22:03:03 +08:00
|
|
|
|
end.not_to change { UserAuthToken.count }
|
2021-04-14 17:15:56 +08:00
|
|
|
|
|
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
|
|
|
|
|
|
expect(Jobs::InvitePasswordInstructionsEmail.jobs.size).to eq(0)
|
|
|
|
|
expect(Jobs::CriticalUserEmail.jobs.size).to eq(1)
|
|
|
|
|
|
|
|
|
|
invited_user = User.find_by_email(invite.email)
|
|
|
|
|
expect(invited_user.active).to eq(false)
|
|
|
|
|
expect(invited_user.email_confirmed?).to eq(false)
|
|
|
|
|
end
|
2021-03-26 00:26:22 +08:00
|
|
|
|
end
|
2018-12-11 06:24:02 +08:00
|
|
|
|
|
2022-07-28 00:14:14 +08:00
|
|
|
|
context "when user was invited via link" do
|
2021-03-26 00:26:22 +08:00
|
|
|
|
before do
|
|
|
|
|
invite.update_column(:emailed_status, Invite.emailed_status_types[:not_required])
|
2023-01-09 19:18:21 +08:00
|
|
|
|
end
|
2018-12-11 06:24:02 +08:00
|
|
|
|
|
2021-03-26 00:26:22 +08:00
|
|
|
|
it "sends an activation email and does not activate the user" do
|
|
|
|
|
expect do
|
|
|
|
|
put "/invites/show/#{invite.invite_key}.json",
|
|
|
|
|
params: {
|
|
|
|
|
password: "verystrongpassword",
|
|
|
|
|
}
|
|
|
|
|
end.not_to change { UserAuthToken.count }
|
2019-01-08 15:10:20 +08:00
|
|
|
|
|
2021-03-26 00:26:22 +08:00
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
|
expect(response.parsed_body["message"]).to eq(I18n.t("invite.confirm_email"))
|
2018-12-11 06:24:02 +08:00
|
|
|
|
|
2021-03-26 00:26:22 +08:00
|
|
|
|
invited_user = User.find_by_email(invite.email)
|
|
|
|
|
expect(invited_user.active).to eq(false)
|
|
|
|
|
expect(invited_user.email_confirmed?).to eq(false)
|
2018-12-12 01:04:07 +08:00
|
|
|
|
|
2021-03-26 00:26:22 +08:00
|
|
|
|
expect(Jobs::InvitePasswordInstructionsEmail.jobs.size).to eq(0)
|
|
|
|
|
expect(Jobs::CriticalUserEmail.jobs.size).to eq(1)
|
2018-12-12 01:04:07 +08:00
|
|
|
|
|
2021-12-13 13:29:47 +08:00
|
|
|
|
tokens = EmailToken.where(user_id: invited_user.id, confirmed: false, expired: false)
|
2021-03-26 00:26:22 +08:00
|
|
|
|
expect(tokens.size).to eq(1)
|
2018-12-12 01:04:07 +08:00
|
|
|
|
|
2021-03-26 00:26:22 +08:00
|
|
|
|
job_args = Jobs::CriticalUserEmail.jobs.first["args"].first
|
|
|
|
|
expect(job_args["type"]).to eq("signup")
|
|
|
|
|
expect(job_args["user_id"]).to eq(invited_user.id)
|
2021-12-13 13:29:47 +08:00
|
|
|
|
expect(EmailToken.hash_token(job_args["email_token"])).to eq(tokens.first.token_hash)
|
2018-12-11 06:24:02 +08:00
|
|
|
|
end
|
2018-06-01 13:06:08 +08:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2021-12-08 23:06:57 +08:00
|
|
|
|
context "with a domain invite" do
|
|
|
|
|
fab!(:invite) do
|
|
|
|
|
Fabricate(
|
|
|
|
|
:invite,
|
|
|
|
|
email: nil,
|
|
|
|
|
emailed_status: Invite.emailed_status_types[:not_required],
|
|
|
|
|
domain: "example.com",
|
|
|
|
|
)
|
2023-01-09 19:18:21 +08:00
|
|
|
|
end
|
2021-12-08 23:06:57 +08:00
|
|
|
|
|
|
|
|
|
it "creates an user if email matches domain" do
|
|
|
|
|
expect {
|
|
|
|
|
put "/invites/show/#{invite.invite_key}.json",
|
|
|
|
|
params: {
|
|
|
|
|
email: "test@example.com",
|
|
|
|
|
password: "verystrongpassword",
|
|
|
|
|
}
|
|
|
|
|
}.to change { User.count }
|
|
|
|
|
|
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
|
expect(response.parsed_body["message"]).to eq(I18n.t("invite.confirm_email"))
|
|
|
|
|
expect(invite.reload.redemption_count).to eq(1)
|
|
|
|
|
|
|
|
|
|
invited_user = User.find_by_email("test@example.com")
|
|
|
|
|
expect(invited_user).to be_present
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "does not create an user if email does not match domain" do
|
|
|
|
|
expect {
|
|
|
|
|
put "/invites/show/#{invite.invite_key}.json",
|
|
|
|
|
params: {
|
|
|
|
|
email: "test@example2.com",
|
|
|
|
|
password: "verystrongpassword",
|
|
|
|
|
}
|
|
|
|
|
}.not_to change { User.count }
|
|
|
|
|
|
|
|
|
|
expect(response.status).to eq(412)
|
|
|
|
|
expect(response.parsed_body["message"]).to eq(I18n.t("invite.domain_not_allowed"))
|
|
|
|
|
expect(invite.reload.redemption_count).to eq(0)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2021-03-26 00:26:22 +08:00
|
|
|
|
context "with an invite link" do
|
|
|
|
|
fab!(:invite) do
|
|
|
|
|
Fabricate(:invite, email: nil, emailed_status: Invite.emailed_status_types[:not_required])
|
2023-01-09 19:18:21 +08:00
|
|
|
|
end
|
2020-06-09 23:19:32 +08:00
|
|
|
|
|
2023-07-28 19:53:48 +08:00
|
|
|
|
it "does not create multiple users for a single use invite" do
|
|
|
|
|
user_count = User.count
|
|
|
|
|
|
|
|
|
|
2
|
|
|
|
|
.times
|
|
|
|
|
.map do
|
|
|
|
|
Thread.new do
|
|
|
|
|
put "/invites/show/#{invite.invite_key}.json",
|
|
|
|
|
params: {
|
|
|
|
|
email: "test@example.com",
|
|
|
|
|
password: "verystrongpassword",
|
|
|
|
|
}
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
.each(&:join)
|
|
|
|
|
|
|
|
|
|
expect(invite.reload.max_redemptions_allowed).to eq(1)
|
|
|
|
|
expect(invite.reload.redemption_count).to eq(1)
|
|
|
|
|
expect(User.count).to eq(user_count + 1)
|
|
|
|
|
end
|
|
|
|
|
|
2021-03-26 00:26:22 +08:00
|
|
|
|
it "sends an activation email and does not activate the user" do
|
2022-11-02 00:33:32 +08:00
|
|
|
|
expect {
|
|
|
|
|
put "/invites/show/#{invite.invite_key}.json",
|
|
|
|
|
params: {
|
|
|
|
|
email: "test@example.com",
|
|
|
|
|
password: "verystrongpassword",
|
|
|
|
|
}
|
|
|
|
|
}.not_to change { UserAuthToken.count }
|
2020-06-09 23:19:32 +08:00
|
|
|
|
|
|
|
|
|
expect(response.status).to eq(200)
|
2021-03-26 00:26:22 +08:00
|
|
|
|
expect(response.parsed_body["message"]).to eq(I18n.t("invite.confirm_email"))
|
|
|
|
|
expect(invite.reload.redemption_count).to eq(1)
|
2020-06-09 23:19:32 +08:00
|
|
|
|
|
2021-03-26 00:26:22 +08:00
|
|
|
|
invited_user = User.find_by_email("test@example.com")
|
2020-06-09 23:19:32 +08:00
|
|
|
|
expect(invited_user.active).to eq(false)
|
|
|
|
|
expect(invited_user.email_confirmed?).to eq(false)
|
|
|
|
|
|
|
|
|
|
expect(Jobs::InvitePasswordInstructionsEmail.jobs.size).to eq(0)
|
|
|
|
|
expect(Jobs::CriticalUserEmail.jobs.size).to eq(1)
|
|
|
|
|
|
2021-12-13 13:29:47 +08:00
|
|
|
|
tokens = EmailToken.where(user_id: invited_user.id, confirmed: false, expired: false)
|
2020-06-09 23:19:32 +08:00
|
|
|
|
expect(tokens.size).to eq(1)
|
|
|
|
|
|
2021-03-26 00:26:22 +08:00
|
|
|
|
job_args = Jobs::CriticalUserEmail.jobs.first["args"].first
|
|
|
|
|
expect(job_args["type"]).to eq("signup")
|
|
|
|
|
expect(job_args["user_id"]).to eq(invited_user.id)
|
2021-12-13 13:29:47 +08:00
|
|
|
|
expect(EmailToken.hash_token(job_args["email_token"])).to eq(tokens.first.token_hash)
|
2020-06-09 23:19:32 +08:00
|
|
|
|
end
|
2022-11-02 00:33:32 +08:00
|
|
|
|
|
|
|
|
|
it "does not automatically log in the user if their email matches an existing user's and shows an error" do
|
|
|
|
|
Fabricate(:user, email: "test@example.com")
|
|
|
|
|
put "/invites/show/#{invite.invite_key}.json",
|
|
|
|
|
params: {
|
|
|
|
|
email: "test@example.com",
|
|
|
|
|
password: "verystrongpassword",
|
|
|
|
|
}
|
|
|
|
|
expect(session[:current_user_id]).to be_blank
|
|
|
|
|
expect(response.status).to eq(412)
|
|
|
|
|
expect(response.parsed_body["message"]).to include("Primary email has already been taken")
|
|
|
|
|
expect(invite.reload.redemption_count).to eq(0)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "does not automatically log in the user if their email matches an existing admin's and shows an error" do
|
|
|
|
|
Fabricate(:admin, email: "test@example.com")
|
|
|
|
|
put "/invites/show/#{invite.invite_key}.json",
|
|
|
|
|
params: {
|
|
|
|
|
email: "test@example.com",
|
|
|
|
|
password: "verystrongpassword",
|
|
|
|
|
}
|
|
|
|
|
expect(session[:current_user_id]).to be_blank
|
|
|
|
|
expect(response.status).to eq(412)
|
|
|
|
|
expect(response.parsed_body["message"]).to include("Primary email has already been taken")
|
|
|
|
|
expect(invite.reload.redemption_count).to eq(0)
|
|
|
|
|
end
|
2020-06-09 23:19:32 +08:00
|
|
|
|
end
|
|
|
|
|
|
2022-07-28 00:14:14 +08:00
|
|
|
|
context "when new registrations are disabled" do
|
2023-11-10 06:47:59 +08:00
|
|
|
|
fab!(:topic)
|
2021-03-26 00:26:22 +08:00
|
|
|
|
fab!(:invite) { Invite.generate(topic.user, email: "test@example.com", topic: topic) }
|
2018-12-05 23:43:07 +08:00
|
|
|
|
|
2018-06-01 13:06:08 +08:00
|
|
|
|
before { SiteSetting.allow_new_registrations = false }
|
|
|
|
|
|
2021-03-26 00:26:22 +08:00
|
|
|
|
it "does not redeem the invite" do
|
2018-06-01 13:06:08 +08:00
|
|
|
|
put "/invites/show/#{invite.invite_key}.json"
|
|
|
|
|
expect(response.status).to eq(200)
|
2021-03-26 00:26:22 +08:00
|
|
|
|
expect(invite.reload.invited_users).to be_blank
|
2018-06-01 13:06:08 +08:00
|
|
|
|
expect(invite.redeemed?).to be_falsey
|
2021-03-26 00:26:22 +08:00
|
|
|
|
expect(response.body).to include(I18n.t("login.new_registrations_disabled"))
|
2018-06-01 13:06:08 +08:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2022-07-28 00:14:14 +08:00
|
|
|
|
context "when user is already logged in" do
|
2022-11-02 00:33:32 +08:00
|
|
|
|
before { sign_in(user) }
|
|
|
|
|
|
2022-11-14 10:02:06 +08:00
|
|
|
|
context "for an email invite" do
|
|
|
|
|
fab!(:invite) { Fabricate(:invite, email: "test@example.com") }
|
|
|
|
|
fab!(:user) { Fabricate(:user, email: "test@example.com") }
|
2023-11-10 06:47:59 +08:00
|
|
|
|
fab!(:group)
|
2022-11-02 00:33:32 +08:00
|
|
|
|
|
2022-11-14 10:02:06 +08:00
|
|
|
|
it "redeems the invitation and creates the invite accepted notification" do
|
|
|
|
|
put "/invites/show/#{invite.invite_key}.json", params: { id: invite.invite_key }
|
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
|
expect(response.parsed_body["message"]).to eq(I18n.t("invite.existing_user_success"))
|
|
|
|
|
invite.reload
|
|
|
|
|
expect(invite.invited_users.first.user).to eq(user)
|
|
|
|
|
expect(invite.redeemed?).to be_truthy
|
|
|
|
|
expect(
|
|
|
|
|
Notification.exists?(
|
|
|
|
|
user: invite.invited_by,
|
|
|
|
|
notification_type: Notification.types[:invitee_accepted],
|
|
|
|
|
),
|
|
|
|
|
).to eq(true)
|
|
|
|
|
end
|
2022-11-02 00:33:32 +08:00
|
|
|
|
|
2022-11-14 10:02:06 +08:00
|
|
|
|
it "redirects to the first topic the user was invited to and creates the topic notification" do
|
|
|
|
|
topic = Fabricate(:topic)
|
|
|
|
|
TopicInvite.create!(invite: invite, topic: topic)
|
|
|
|
|
put "/invites/show/#{invite.invite_key}.json", params: { id: invite.invite_key }
|
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
|
expect(response.parsed_body["redirect_to"]).to eq(topic.relative_url)
|
|
|
|
|
expect(
|
|
|
|
|
Notification.where(
|
|
|
|
|
notification_type: Notification.types[:invited_to_topic],
|
|
|
|
|
topic: topic,
|
|
|
|
|
).count,
|
|
|
|
|
).to eq(1)
|
|
|
|
|
end
|
2022-11-02 00:33:32 +08:00
|
|
|
|
|
2022-11-14 10:02:06 +08:00
|
|
|
|
it "adds the user to the private topic" do
|
|
|
|
|
topic = Fabricate(:private_message_topic)
|
|
|
|
|
TopicInvite.create!(invite: invite, topic: topic)
|
|
|
|
|
put "/invites/show/#{invite.invite_key}.json", params: { id: invite.invite_key }
|
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
|
expect(response.parsed_body["redirect_to"]).to eq(topic.relative_url)
|
|
|
|
|
expect(TopicAllowedUser.exists?(user: user, topic: topic)).to eq(true)
|
|
|
|
|
end
|
2022-11-02 00:33:32 +08:00
|
|
|
|
|
2022-11-14 10:02:06 +08:00
|
|
|
|
it "adds the user to the groups specified on the invite and allows them to access the secure topic" do
|
|
|
|
|
group.add_owner(invite.invited_by)
|
|
|
|
|
secured_category = Fabricate(:category)
|
|
|
|
|
secured_category.permissions = { group.name => :full }
|
|
|
|
|
secured_category.save!
|
|
|
|
|
|
|
|
|
|
topic = Fabricate(:topic, category: secured_category)
|
|
|
|
|
TopicInvite.create!(invite: invite, topic: topic)
|
|
|
|
|
InvitedGroup.create!(invite: invite, group: group)
|
2022-11-02 00:33:32 +08:00
|
|
|
|
|
|
|
|
|
put "/invites/show/#{invite.invite_key}.json", params: { id: invite.invite_key }
|
2022-11-14 10:02:06 +08:00
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
|
expect(response.parsed_body["message"]).to eq(I18n.t("invite.existing_user_success"))
|
|
|
|
|
expect(response.parsed_body["redirect_to"]).to eq(topic.relative_url)
|
|
|
|
|
invite.reload
|
|
|
|
|
expect(invite.redeemed?).to be_truthy
|
|
|
|
|
expect(user.reload.groups).to include(group)
|
|
|
|
|
expect(
|
|
|
|
|
Notification.where(
|
|
|
|
|
notification_type: Notification.types[:invited_to_topic],
|
|
|
|
|
topic: topic,
|
|
|
|
|
).count,
|
|
|
|
|
).to eq(1)
|
|
|
|
|
end
|
2022-11-02 00:33:32 +08:00
|
|
|
|
|
2022-11-14 10:02:06 +08:00
|
|
|
|
it "does not try to log in the user automatically" do
|
|
|
|
|
expect do
|
|
|
|
|
put "/invites/show/#{invite.invite_key}.json", params: { id: invite.invite_key }
|
|
|
|
|
end.not_to change { UserAuthToken.count }
|
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
|
expect(response.parsed_body["message"]).to eq(I18n.t("invite.existing_user_success"))
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "errors if the user's email doesn't match the invite email" do
|
|
|
|
|
user.update!(email: "blah@test.com")
|
|
|
|
|
put "/invites/show/#{invite.invite_key}.json", params: { id: invite.invite_key }
|
|
|
|
|
expect(response.status).to eq(412)
|
|
|
|
|
expect(response.parsed_body["message"]).to eq(I18n.t("invite.not_matching_email"))
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "errors if the user's email domain doesn't match the invite domain" do
|
|
|
|
|
user.update!(email: "blah@test.com")
|
|
|
|
|
invite.update!(email: nil, domain: "example.com")
|
|
|
|
|
put "/invites/show/#{invite.invite_key}.json", params: { id: invite.invite_key }
|
|
|
|
|
expect(response.status).to eq(412)
|
|
|
|
|
expect(response.parsed_body["message"]).to eq(I18n.t("invite.domain_not_allowed"))
|
|
|
|
|
end
|
2022-11-02 00:33:32 +08:00
|
|
|
|
end
|
|
|
|
|
|
2022-11-14 10:02:06 +08:00
|
|
|
|
context "for an invite link" do
|
|
|
|
|
fab!(:invite) { Fabricate(:invite, email: nil) }
|
|
|
|
|
fab!(:user) { Fabricate(:user, email: "test@example.com") }
|
2023-11-10 06:47:59 +08:00
|
|
|
|
fab!(:group)
|
2022-11-14 10:02:06 +08:00
|
|
|
|
|
|
|
|
|
it "redeems the invitation and creates the invite accepted notification" do
|
|
|
|
|
put "/invites/show/#{invite.invite_key}.json", params: { id: invite.invite_key }
|
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
|
expect(response.parsed_body["message"]).to eq(I18n.t("invite.existing_user_success"))
|
|
|
|
|
invite.reload
|
|
|
|
|
expect(invite.invited_users.first.user).to eq(user)
|
|
|
|
|
expect(invite.redeemed?).to be_truthy
|
|
|
|
|
expect(
|
|
|
|
|
Notification.exists?(
|
|
|
|
|
user: invite.invited_by,
|
|
|
|
|
notification_type: Notification.types[:invitee_accepted],
|
|
|
|
|
),
|
|
|
|
|
).to eq(true)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "redirects to the first topic the user was invited to and creates the topic notification" do
|
|
|
|
|
topic = Fabricate(:topic)
|
|
|
|
|
TopicInvite.create!(invite: invite, topic: topic)
|
|
|
|
|
put "/invites/show/#{invite.invite_key}.json", params: { id: invite.invite_key }
|
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
|
expect(response.parsed_body["redirect_to"]).to eq(topic.relative_url)
|
|
|
|
|
expect(
|
|
|
|
|
Notification.where(
|
|
|
|
|
notification_type: Notification.types[:invited_to_topic],
|
|
|
|
|
topic: topic,
|
|
|
|
|
).count,
|
|
|
|
|
).to eq(1)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "adds the user to the groups specified on the invite and allows them to access the secure topic" do
|
|
|
|
|
group.add_owner(invite.invited_by)
|
|
|
|
|
secured_category = Fabricate(:category)
|
|
|
|
|
secured_category.permissions = { group.name => :full }
|
|
|
|
|
secured_category.save!
|
|
|
|
|
|
|
|
|
|
topic = Fabricate(:topic, category: secured_category)
|
|
|
|
|
TopicInvite.create!(invite: invite, topic: topic)
|
|
|
|
|
InvitedGroup.create!(invite: invite, group: group)
|
|
|
|
|
|
|
|
|
|
put "/invites/show/#{invite.invite_key}.json", params: { id: invite.invite_key }
|
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
|
expect(response.parsed_body["message"]).to eq(I18n.t("invite.existing_user_success"))
|
|
|
|
|
expect(response.parsed_body["redirect_to"]).to eq(topic.relative_url)
|
|
|
|
|
invite.reload
|
|
|
|
|
expect(invite.redeemed?).to be_truthy
|
|
|
|
|
expect(user.reload.groups).to include(group)
|
|
|
|
|
expect(
|
|
|
|
|
Notification.where(
|
|
|
|
|
notification_type: Notification.types[:invited_to_topic],
|
|
|
|
|
topic: topic,
|
|
|
|
|
).count,
|
|
|
|
|
).to eq(1)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "does not try to log in the user automatically" do
|
|
|
|
|
expect do
|
|
|
|
|
put "/invites/show/#{invite.invite_key}.json", params: { id: invite.invite_key }
|
|
|
|
|
end.not_to change { UserAuthToken.count }
|
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
|
expect(response.parsed_body["message"]).to eq(I18n.t("invite.existing_user_success"))
|
|
|
|
|
end
|
2018-06-01 13:06:08 +08:00
|
|
|
|
end
|
|
|
|
|
end
|
2021-06-30 17:00:47 +08:00
|
|
|
|
|
2022-07-28 00:14:14 +08:00
|
|
|
|
context "with topic invites" do
|
2021-06-30 17:00:47 +08:00
|
|
|
|
fab!(:invite) { Fabricate(:invite, email: "test@example.com") }
|
|
|
|
|
|
|
|
|
|
fab!(:secured_category) do
|
|
|
|
|
secured_category = Fabricate(:category)
|
|
|
|
|
secured_category.permissions = { staff: :full }
|
|
|
|
|
secured_category.save!
|
|
|
|
|
secured_category
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "redirects user to topic if activated" do
|
|
|
|
|
topic = Fabricate(:topic)
|
|
|
|
|
TopicInvite.create!(invite: invite, topic: topic)
|
|
|
|
|
|
|
|
|
|
put "/invites/show/#{invite.invite_key}.json", params: { email_token: invite.email_token }
|
|
|
|
|
expect(response.parsed_body["redirect_to"]).to eq(topic.relative_url)
|
2021-08-26 15:43:56 +08:00
|
|
|
|
expect(
|
|
|
|
|
Notification.where(
|
|
|
|
|
notification_type: Notification.types[:invited_to_topic],
|
|
|
|
|
topic: topic,
|
|
|
|
|
).count,
|
|
|
|
|
).to eq(1)
|
2021-06-30 17:00:47 +08:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "sets destination_url cookie if user is not activated" do
|
|
|
|
|
topic = Fabricate(:topic)
|
|
|
|
|
TopicInvite.create!(invite: invite, topic: topic)
|
|
|
|
|
|
|
|
|
|
put "/invites/show/#{invite.invite_key}.json"
|
|
|
|
|
expect(cookies["destination_url"]).to eq(topic.relative_url)
|
2021-08-26 15:43:56 +08:00
|
|
|
|
expect(
|
|
|
|
|
Notification.where(
|
|
|
|
|
notification_type: Notification.types[:invited_to_topic],
|
|
|
|
|
topic: topic,
|
|
|
|
|
).count,
|
|
|
|
|
).to eq(1)
|
2021-06-30 17:00:47 +08:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "does not redirect user if they cannot see topic" do
|
2021-08-26 15:43:56 +08:00
|
|
|
|
topic = Fabricate(:topic, category: secured_category)
|
|
|
|
|
TopicInvite.create!(invite: invite, topic: topic)
|
2021-06-30 17:00:47 +08:00
|
|
|
|
|
|
|
|
|
put "/invites/show/#{invite.invite_key}.json", params: { email_token: invite.email_token }
|
|
|
|
|
expect(response.parsed_body["redirect_to"]).to eq("/")
|
2021-08-26 15:43:56 +08:00
|
|
|
|
expect(
|
|
|
|
|
Notification.where(
|
|
|
|
|
notification_type: Notification.types[:invited_to_topic],
|
|
|
|
|
topic: topic,
|
|
|
|
|
).count,
|
|
|
|
|
).to eq(0)
|
2021-06-30 17:00:47 +08:00
|
|
|
|
end
|
|
|
|
|
end
|
2021-07-12 05:57:38 +08:00
|
|
|
|
|
2022-07-28 00:14:14 +08:00
|
|
|
|
context "with staged user" do
|
2023-11-10 06:47:59 +08:00
|
|
|
|
fab!(:invite)
|
2021-07-12 05:57:38 +08:00
|
|
|
|
fab!(:staged_user) { Fabricate(:user, staged: true, email: invite.email) }
|
|
|
|
|
|
|
|
|
|
it "can keep the old username" do
|
|
|
|
|
old_username = staged_user.username
|
|
|
|
|
|
|
|
|
|
put "/invites/show/#{invite.invite_key}.json",
|
|
|
|
|
params: {
|
|
|
|
|
username: staged_user.username,
|
|
|
|
|
password: "Password123456",
|
|
|
|
|
email_token: invite.email_token,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
|
expect(invite.reload.redeemed?).to be_truthy
|
|
|
|
|
user = invite.invited_users.first.user
|
|
|
|
|
expect(user.username).to eq(old_username)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "can change the username" do
|
|
|
|
|
put "/invites/show/#{invite.invite_key}.json",
|
|
|
|
|
params: {
|
|
|
|
|
username: "new_username",
|
|
|
|
|
password: "Password123456",
|
|
|
|
|
email_token: invite.email_token,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
|
expect(invite.reload.redeemed?).to be_truthy
|
|
|
|
|
user = invite.invited_users.first.user
|
|
|
|
|
expect(user.username).to eq("new_username")
|
|
|
|
|
end
|
|
|
|
|
end
|
2018-06-01 13:06:08 +08:00
|
|
|
|
end
|
|
|
|
|
|
2022-07-27 18:21:10 +08:00
|
|
|
|
describe "#destroy_all_expired" do
|
2021-03-03 17:45:29 +08:00
|
|
|
|
it "removes all expired invites sent by a user" do
|
|
|
|
|
SiteSetting.invite_expiry_days = 1
|
|
|
|
|
|
|
|
|
|
user = Fabricate(:admin)
|
|
|
|
|
invite_1 = Fabricate(:invite, invited_by: user)
|
|
|
|
|
invite_2 = Fabricate(:invite, invited_by: user)
|
|
|
|
|
expired_invite = Fabricate(:invite, invited_by: user)
|
|
|
|
|
expired_invite.update!(expires_at: 2.days.ago)
|
|
|
|
|
|
|
|
|
|
sign_in(user)
|
2021-03-26 00:26:22 +08:00
|
|
|
|
post "/invites/destroy-all-expired"
|
2021-03-03 17:45:29 +08:00
|
|
|
|
|
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
|
expect(invite_1.reload.deleted_at).to eq(nil)
|
|
|
|
|
expect(invite_2.reload.deleted_at).to eq(nil)
|
|
|
|
|
expect(expired_invite.reload.deleted_at).to be_present
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2022-07-27 18:21:10 +08:00
|
|
|
|
describe "#resend_invite" do
|
2021-03-26 00:26:22 +08:00
|
|
|
|
it "requires to be logged in" do
|
|
|
|
|
post "/invites/reinvite.json", params: { email: "first_name@example.com" }
|
2018-06-01 13:06:08 +08:00
|
|
|
|
expect(response.status).to eq(403)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context "while logged in" do
|
2021-03-26 00:26:22 +08:00
|
|
|
|
fab!(:user) { sign_in(Fabricate(:user)) }
|
|
|
|
|
fab!(:invite) { Fabricate(:invite, invited_by: user) }
|
2019-05-07 11:12:20 +08:00
|
|
|
|
fab!(:another_invite) { Fabricate(:invite, email: "last_name@example.com") }
|
2018-06-01 13:06:08 +08:00
|
|
|
|
|
|
|
|
|
it "raises an error when the email is missing" do
|
2021-03-26 00:26:22 +08:00
|
|
|
|
post "/invites/reinvite.json"
|
2018-06-01 13:06:08 +08:00
|
|
|
|
expect(response.status).to eq(400)
|
|
|
|
|
end
|
|
|
|
|
|
2021-03-26 00:26:22 +08:00
|
|
|
|
it "raises an error when the email cannot be found" do
|
|
|
|
|
post "/invites/reinvite.json", params: { email: "first_name@example.com" }
|
2018-06-01 13:06:08 +08:00
|
|
|
|
expect(response.status).to eq(400)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "raises an error when the invite is not yours" do
|
2021-03-26 00:26:22 +08:00
|
|
|
|
post "/invites/reinvite.json", params: { email: another_invite.email }
|
2018-06-01 13:06:08 +08:00
|
|
|
|
expect(response.status).to eq(400)
|
|
|
|
|
end
|
|
|
|
|
|
2021-03-26 00:26:22 +08:00
|
|
|
|
it "resends the invite" do
|
|
|
|
|
post "/invites/reinvite.json", params: { email: invite.email }
|
2018-06-07 16:11:09 +08:00
|
|
|
|
expect(response.status).to eq(200)
|
2018-06-01 13:06:08 +08:00
|
|
|
|
expect(Jobs::InviteEmail.jobs.size).to eq(1)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2022-07-27 18:21:10 +08:00
|
|
|
|
describe "#resend_all_invites" do
|
2022-06-28 03:50:20 +08:00
|
|
|
|
let(:admin) { Fabricate(:admin) }
|
|
|
|
|
|
2023-06-16 10:44:35 +08:00
|
|
|
|
before do
|
|
|
|
|
SiteSetting.invite_expiry_days = 30
|
|
|
|
|
RateLimiter.enable
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
use_redis_snapshotting
|
2021-03-03 17:45:29 +08:00
|
|
|
|
|
2022-06-28 03:50:20 +08:00
|
|
|
|
it "resends all non-redeemed invites by a user" do
|
2021-06-17 15:45:53 +08:00
|
|
|
|
freeze_time
|
|
|
|
|
|
2022-02-04 03:07:40 +08:00
|
|
|
|
new_invite = Fabricate(:invite, invited_by: admin)
|
|
|
|
|
expired_invite = Fabricate(:invite, invited_by: admin)
|
2021-03-03 17:45:29 +08:00
|
|
|
|
expired_invite.update!(expires_at: 2.days.ago)
|
2022-02-04 03:07:40 +08:00
|
|
|
|
redeemed_invite = Fabricate(:invite, invited_by: admin)
|
2021-03-03 17:45:29 +08:00
|
|
|
|
Fabricate(:invited_user, invite: redeemed_invite, user: Fabricate(:user))
|
|
|
|
|
redeemed_invite.update!(expires_at: 5.days.ago)
|
|
|
|
|
|
2022-02-04 03:07:40 +08:00
|
|
|
|
sign_in(admin)
|
2021-03-26 00:26:22 +08:00
|
|
|
|
post "/invites/reinvite-all"
|
2021-03-03 17:45:29 +08:00
|
|
|
|
|
|
|
|
|
expect(response.status).to eq(200)
|
2021-06-17 15:45:53 +08:00
|
|
|
|
expect(new_invite.reload.expires_at).to eq_time(30.days.from_now)
|
|
|
|
|
expect(expired_invite.reload.expires_at).to eq_time(2.days.ago)
|
|
|
|
|
expect(redeemed_invite.reload.expires_at).to eq_time(5.days.ago)
|
2021-03-03 17:45:29 +08:00
|
|
|
|
end
|
2022-06-28 03:50:20 +08:00
|
|
|
|
|
|
|
|
|
it "errors if admins try to exceed limit of one bulk invite per day" do
|
|
|
|
|
sign_in(admin)
|
2022-07-12 00:58:23 +08:00
|
|
|
|
start = Time.now
|
2022-06-28 03:50:20 +08:00
|
|
|
|
|
2022-07-12 00:58:23 +08:00
|
|
|
|
freeze_time(start)
|
2022-06-28 03:50:20 +08:00
|
|
|
|
post "/invites/reinvite-all"
|
2022-07-12 00:58:23 +08:00
|
|
|
|
expect(response.parsed_body["errors"]).to_not be_present
|
2022-06-28 03:50:20 +08:00
|
|
|
|
|
2022-07-12 00:58:23 +08:00
|
|
|
|
freeze_time(start + 10.minutes)
|
2022-06-28 03:50:20 +08:00
|
|
|
|
post "/invites/reinvite-all"
|
|
|
|
|
expect(response.parsed_body["errors"][0]).to eq(I18n.t("rate_limiter.slow_down"))
|
|
|
|
|
end
|
2021-03-03 17:45:29 +08:00
|
|
|
|
end
|
|
|
|
|
|
2022-07-27 18:21:10 +08:00
|
|
|
|
describe "#upload_csv" do
|
2021-03-26 00:26:22 +08:00
|
|
|
|
it "requires to be logged in" do
|
|
|
|
|
post "/invites/upload_csv.json"
|
2018-06-01 13:06:08 +08:00
|
|
|
|
expect(response.status).to eq(403)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context "while logged in" do
|
|
|
|
|
let(:csv_file) { File.new("#{Rails.root}/spec/fixtures/csv/discourse.csv") }
|
2021-03-26 00:26:22 +08:00
|
|
|
|
let(:file) { Rack::Test::UploadedFile.new(File.open(csv_file)) }
|
2018-06-01 13:06:08 +08:00
|
|
|
|
|
2021-03-29 19:03:19 +08:00
|
|
|
|
let(:csv_file_with_headers) do
|
|
|
|
|
File.new("#{Rails.root}/spec/fixtures/csv/discourse_headers.csv")
|
2023-01-09 19:18:21 +08:00
|
|
|
|
end
|
2021-03-29 19:03:19 +08:00
|
|
|
|
let(:file_with_headers) { Rack::Test::UploadedFile.new(File.open(csv_file_with_headers)) }
|
2021-12-06 09:08:21 +08:00
|
|
|
|
let(:csv_file_with_locales) do
|
|
|
|
|
File.new("#{Rails.root}/spec/fixtures/csv/invites_with_locales.csv")
|
2023-01-09 19:18:21 +08:00
|
|
|
|
end
|
2021-12-06 09:08:21 +08:00
|
|
|
|
let(:file_with_locales) { Rack::Test::UploadedFile.new(File.open(csv_file_with_locales)) }
|
2021-03-29 19:03:19 +08:00
|
|
|
|
|
2021-03-26 00:26:22 +08:00
|
|
|
|
it "fails if you cannot bulk invite to the forum" do
|
2018-06-01 13:06:08 +08:00
|
|
|
|
sign_in(Fabricate(:user))
|
2021-03-26 00:26:22 +08:00
|
|
|
|
post "/invites/upload_csv.json", params: { file: file, name: "discourse.csv" }
|
2018-06-01 13:06:08 +08:00
|
|
|
|
expect(response.status).to eq(403)
|
|
|
|
|
end
|
|
|
|
|
|
2021-03-26 00:26:22 +08:00
|
|
|
|
it "allows admin to bulk invite" do
|
2020-06-09 23:19:32 +08:00
|
|
|
|
sign_in(admin)
|
2021-03-26 00:26:22 +08:00
|
|
|
|
post "/invites/upload_csv.json", params: { file: file, name: "discourse.csv" }
|
2018-06-07 16:11:09 +08:00
|
|
|
|
expect(response.status).to eq(200)
|
2018-06-01 13:06:08 +08:00
|
|
|
|
expect(Jobs::BulkInvite.jobs.size).to eq(1)
|
|
|
|
|
end
|
2019-06-12 17:05:21 +08:00
|
|
|
|
|
2021-11-10 01:43:23 +08:00
|
|
|
|
it "allows admin to bulk invite when DiscourseConnect enabled" do
|
|
|
|
|
SiteSetting.discourse_connect_url = "https://example.com"
|
|
|
|
|
SiteSetting.enable_discourse_connect = true
|
|
|
|
|
|
|
|
|
|
sign_in(admin)
|
|
|
|
|
post "/invites/upload_csv.json", params: { file: file, name: "discourse.csv" }
|
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
|
expect(Jobs::BulkInvite.jobs.size).to eq(1)
|
|
|
|
|
end
|
|
|
|
|
|
2021-03-26 00:26:22 +08:00
|
|
|
|
it "sends limited invites at a time" do
|
2019-06-12 17:05:21 +08:00
|
|
|
|
SiteSetting.max_bulk_invites = 3
|
2020-06-09 23:19:32 +08:00
|
|
|
|
sign_in(admin)
|
2021-03-26 00:26:22 +08:00
|
|
|
|
post "/invites/upload_csv.json", params: { file: file, name: "discourse.csv" }
|
2019-06-12 17:05:21 +08:00
|
|
|
|
|
|
|
|
|
expect(response.status).to eq(422)
|
|
|
|
|
expect(Jobs::BulkInvite.jobs.size).to eq(1)
|
2021-03-26 00:26:22 +08:00
|
|
|
|
expect(response.parsed_body["errors"][0]).to eq(
|
|
|
|
|
I18n.t("bulk_invite.max_rows", max_bulk_invites: SiteSetting.max_bulk_invites),
|
|
|
|
|
)
|
2019-06-12 17:05:21 +08:00
|
|
|
|
end
|
2021-03-29 19:03:19 +08:00
|
|
|
|
|
|
|
|
|
it "can import user fields" do
|
|
|
|
|
Jobs.run_immediately!
|
|
|
|
|
user_field = Fabricate(:user_field, name: "location")
|
|
|
|
|
Fabricate(:group, name: "discourse")
|
|
|
|
|
Fabricate(:group, name: "ubuntu")
|
|
|
|
|
|
|
|
|
|
sign_in(admin)
|
|
|
|
|
|
|
|
|
|
post "/invites/upload_csv.json",
|
|
|
|
|
params: {
|
|
|
|
|
file: file_with_headers,
|
|
|
|
|
name: "discourse_headers.csv",
|
|
|
|
|
}
|
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
|
|
|
|
|
|
user = User.where(staged: true).find_by_email("test@example.com")
|
|
|
|
|
expect(user.user_fields[user_field.id.to_s]).to eq("usa")
|
|
|
|
|
|
|
|
|
|
user2 = User.where(staged: true).find_by_email("test2@example.com")
|
|
|
|
|
expect(user2.user_fields[user_field.id.to_s]).to eq("europe")
|
|
|
|
|
end
|
2021-12-06 09:08:21 +08:00
|
|
|
|
|
|
|
|
|
it "can pre-set user locales" do
|
|
|
|
|
Jobs.run_immediately!
|
|
|
|
|
sign_in(admin)
|
|
|
|
|
|
|
|
|
|
post "/invites/upload_csv.json",
|
|
|
|
|
params: {
|
|
|
|
|
file: file_with_locales,
|
|
|
|
|
name: "discourse_headers.csv",
|
|
|
|
|
}
|
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
|
|
|
|
|
|
user = User.where(staged: true).find_by_email("test@example.com")
|
|
|
|
|
expect(user.locale).to eq("de")
|
|
|
|
|
|
|
|
|
|
user2 = User.where(staged: true).find_by_email("test2@example.com")
|
|
|
|
|
expect(user2.locale).to eq("pl")
|
|
|
|
|
end
|
2018-06-01 13:06:08 +08:00
|
|
|
|
end
|
|
|
|
|
end
|
2018-05-08 22:42:58 +08:00
|
|
|
|
end
|