diff --git a/backend/src/app/rpc/commands/teams.clj b/backend/src/app/rpc/commands/teams.clj index 17f23fcf9c..728e97bef1 100644 --- a/backend/src/app/rpc/commands/teams.clj +++ b/backend/src/app/rpc/commands/teams.clj @@ -944,8 +944,10 @@ ::sm/params schema:delete-team-member ::db/transaction true} [{:keys [::db/conn ::mbus/msgbus] :as cfg} {:keys [::rpc/profile-id team-id member-id] :as params}] - (let [team (get-team conn :profile-id profile-id :team-id team-id) - perms (get-permissions conn profile-id team-id)] + (let [team (get-team conn :profile-id profile-id :team-id team-id) + perms (get-permissions conn profile-id team-id) + members (get-team-members conn team-id) + member (d/seek #(= member-id (:id %)) members)] (when-not (or (:is-owner perms) (:is-admin perms)) (ex/raise :type :validation @@ -955,6 +957,15 @@ (ex/raise :type :validation :code :cant-remove-yourself)) + (when-not member + (ex/raise :type :not-found + :code :member-does-not-exist)) + + (when (and (:is-owner member) + (not (:is-owner perms))) + (ex/raise :type :validation + :code :cant-remove-owner)) + (db/delete! conn :team-profile-rel {:profile-id member-id :team-id team-id}) diff --git a/backend/test/backend_tests/rpc_team_test.clj b/backend/test/backend_tests/rpc_team_test.clj index bf803e57c7..3d755d8231 100644 --- a/backend/test/backend_tests/rpc_team_test.clj +++ b/backend/test/backend_tests/rpc_team_test.clj @@ -1328,3 +1328,83 @@ out (th/command! data)] (t/is (th/success? out)) (t/is (= 1 (:call-count @mock))))))) + +(t/deftest admin-cannot-remove-team-owner + (let [owner (th/create-profile* 1 {:is-active true}) + admin (th/create-profile* 2 {:is-active true}) + team (th/create-team* 1 {:profile-id (:id owner)})] + + (th/create-team-role* {:team-id (:id team) + :profile-id (:id admin) + :role :admin}) + + (let [out (th/command! {::th/type :delete-team-member + ::rpc/profile-id (:id admin) + :team-id (:id team) + :member-id (:id owner)})] + (t/is (not (th/success? out))) + (t/is (th/ex-of-type? (:error out) :validation)) + (t/is (th/ex-of-code? (:error out) :cant-remove-owner))))) + +(t/deftest owner-can-remove-another-owner + (let [owner1 (th/create-profile* 1 {:is-active true}) + owner2 (th/create-profile* 2 {:is-active true}) + team (th/create-team* 1 {:profile-id (:id owner1)})] + + (th/create-team-role* {:team-id (:id team) + :profile-id (:id owner2) + :role :owner}) + + (let [out (th/command! {::th/type :delete-team-member + ::rpc/profile-id (:id owner1) + :team-id (:id team) + :member-id (:id owner2)})] + (t/is (th/success? out))))) + +(t/deftest owner-can-remove-admin + (let [owner (th/create-profile* 1 {:is-active true}) + admin (th/create-profile* 2 {:is-active true}) + team (th/create-team* 1 {:profile-id (:id owner)})] + + (th/create-team-role* {:team-id (:id team) + :profile-id (:id admin) + :role :admin}) + + (let [out (th/command! {::th/type :delete-team-member + ::rpc/profile-id (:id owner) + :team-id (:id team) + :member-id (:id admin)})] + (t/is (th/success? out))))) + +(t/deftest admin-can-remove-admin + (let [owner (th/create-profile* 1 {:is-active true}) + admin1 (th/create-profile* 2 {:is-active true}) + admin2 (th/create-profile* 3 {:is-active true}) + team (th/create-team* 1 {:profile-id (:id owner)})] + + (th/create-team-role* {:team-id (:id team) + :profile-id (:id admin1) + :role :admin}) + + (th/create-team-role* {:team-id (:id team) + :profile-id (:id admin2) + :role :admin}) + + (let [out (th/command! {::th/type :delete-team-member + ::rpc/profile-id (:id admin1) + :team-id (:id team) + :member-id (:id admin2)})] + (t/is (th/success? out))))) + +(t/deftest delete-nonexistent-member-returns-not-found + (let [owner (th/create-profile* 1 {:is-active true}) + team (th/create-team* 1 {:profile-id (:id owner)}) + fake-id (uuid/next)] + + (let [out (th/command! {::th/type :delete-team-member + ::rpc/profile-id (:id owner) + :team-id (:id team) + :member-id fake-id})] + (t/is (not (th/success? out))) + (t/is (th/ex-of-type? (:error out) :not-found)) + (t/is (th/ex-of-code? (:error out) :member-does-not-exist)))))