diff --git a/app/controllers/invitations_controller.rb b/app/controllers/invitations_controller.rb index 86fb68046d6..0b10c6cb20c 100644 --- a/app/controllers/invitations_controller.rb +++ b/app/controllers/invitations_controller.rb @@ -23,21 +23,18 @@ def create end def accept - @invitation = Invitation.pending.find_by!(token: params[:id]) - redirect_to new_registration_path(invitation: @invitation.token) + @invitation = Invitation.find_by!(token: params[:id]) + + if @invitation.pending? + redirect_to new_registration_path(invitation: @invitation.token) + else + raise ActiveRecord::RecordNotFound + end end private def invitation_params - base_params = params.require(:invitation).permit(:email) - - if params[:invitation][:role].in?(%w[admin member]) - base_params[:role] = params[:invitation][:role] - else - base_params[:role] = "member" - end - - base_params + params.require(:invitation).permit(:email, :role) end end diff --git a/app/models/invitation.rb b/app/models/invitation.rb index 90828b0f3a2..6bb97e861f6 100644 --- a/app/models/invitation.rb +++ b/app/models/invitation.rb @@ -13,6 +13,10 @@ class Invitation < ApplicationRecord scope :accepted, -> { where.not(accepted_at: nil) } scope :most_recent_for_email, ->(email) { where(email: email).order(accepted_at: :desc).first } + def pending? + accepted_at.nil? && expires_at > Time.current + end + private def generate_token diff --git a/test/controllers/invitations_controller_test.rb b/test/controllers/invitations_controller_test.rb new file mode 100644 index 00000000000..28d446ea3d0 --- /dev/null +++ b/test/controllers/invitations_controller_test.rb @@ -0,0 +1,89 @@ +require "test_helper" + +class InvitationsControllerTest < ActionDispatch::IntegrationTest + setup do + sign_in @user = users(:family_admin) + @invitation = invitations(:one) + end + + test "should get new" do + get new_invitation_url + assert_response :success + end + + test "should create invitation for member" do + assert_difference("Invitation.count") do + assert_enqueued_with(job: ActionMailer::MailDeliveryJob) do + post invitations_url, params: { + invitation: { + email: "new@example.com", + role: "member" + } + } + end + end + + invitation = Invitation.order(created_at: :desc).first + assert_equal "member", invitation.role + assert_equal @user, invitation.inviter + assert_equal "new@example.com", invitation.email + assert_redirected_to settings_profile_path + assert_equal I18n.t("invitations.create.success"), flash[:notice] + end + + test "non-admin cannot create admin invitation" do + sign_in users(:family_member) + + assert_difference("Invitation.count") do + post invitations_url, params: { + invitation: { + email: "new@example.com", + role: "admin" + } + } + end + + invitation = Invitation.last + assert_equal "member", invitation.role # Role should be downgraded to member + end + + test "admin can create admin invitation" do + assert_difference("Invitation.count") do + post invitations_url, params: { + invitation: { + email: "new@example.com", + role: "admin" + } + } + end + + invitation = Invitation.last + assert_equal "admin", invitation.role + assert_equal @user.family, invitation.family + assert_equal @user, invitation.inviter + end + + test "should handle invalid invitation creation" do + assert_no_difference("Invitation.count") do + post invitations_url, params: { + invitation: { + email: "", + role: "member" + } + } + end + + assert_redirected_to settings_profile_path + assert_equal I18n.t("invitations.create.failure"), flash[:alert] + end + + test "should accept invitation and redirect to registration" do + get accept_invitation_url(@invitation.token) + assert_redirected_to new_registration_path(invitation: @invitation.token) + end + + test "should not accept invalid invitation token" do + get accept_invitation_url("invalid-token") + assert_response :not_found + end +end diff --git a/test/fixtures/invitations.yml b/test/fixtures/invitations.yml new file mode 100644 index 00000000000..12ae9a052e8 --- /dev/null +++ b/test/fixtures/invitations.yml @@ -0,0 +1,19 @@ +one: + email: "test@example.com" + token: "valid-token-123" + role: "member" + inviter: family_admin + family: dylan_family + created_at: <%= Time.current %> + updated_at: <%= Time.current %> + expires_at: <%= 3.days.from_now %> + +two: + email: "another@example.com" + token: "valid-token-456" + role: "admin" + inviter: family_admin + family: dylan_family + created_at: <%= Time.current %> + updated_at: <%= Time.current %> + expires_at: <%= 3.days.from_now %>