From 47363d96f12a8f6c0afef6957cec7b99b38b1fe7 Mon Sep 17 00:00:00 2001 From: Pablo Alba <pablo.alba@kaleidos.net> Date: Mon, 26 Sep 2022 23:56:58 +0200 Subject: [PATCH] :sparkles: Improve invitation token validation --- CHANGES.md | 1 + backend/src/app/db.clj | 46 ++- backend/src/app/rpc/commands/verify_token.clj | 95 +++--- backend/src/app/rpc/mutations/teams.clj | 32 +- backend/src/app/tokens.clj | 10 +- backend/test/app/services_teams_test.clj | 296 +++++++++++++----- 6 files changed, 325 insertions(+), 155 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 04630e28f..a094033dd 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -37,6 +37,7 @@ - Fix inconsistent message on deleting library when a library is linked from deleted files - Fix change multiple colors with SVG [Taiga #3889](https://tree.taiga.io/project/penpot/issue/3889) - Fix ungroup does not work for typographies [Taiga #4195](https://tree.taiga.io/project/penpot/issue/4195) +- Fix inviting to non existing users can fail [Taiga #4108](https://tree.taiga.io/project/penpot/issue/4108) ### :arrow_up: Deps updates ### :heart: Community contributions by (Thank you!) diff --git a/backend/src/app/db.clj b/backend/src/app/db.clj index f547cfa9a..28d8a3c50 100644 --- a/backend/src/app/db.clj +++ b/backend/src/app/db.clj @@ -5,6 +5,7 @@ ;; Copyright (c) KALEIDOS INC (ns app.db + (:refer-clojure :exclude [get]) (:require [app.common.data :as d] [app.common.exceptions :as ex] @@ -270,28 +271,55 @@ (sql/delete table params opts) (assoc opts :return-keys true)))) -(defn- is-deleted? +(defn is-row-deleted? [{:keys [deleted-at]}] (and (dt/instant? deleted-at) (< (inst-ms deleted-at) (inst-ms (dt/now))))) -(defn get-by-params +(defn get* + "Internal function for retrieve a single row from database that + matches a simple filters." ([ds table params] - (get-by-params ds table params nil)) - ([ds table params {:keys [check-not-found] :or {check-not-found true} :as opts}] - (let [res (exec-one! ds (sql/select table params opts))] - (when (and check-not-found (or (not res) (is-deleted? res))) + (get* ds table params nil)) + ([ds table params {:keys [check-deleted?] :or {check-deleted? true} :as opts}] + (let [rows (exec! ds (sql/select table params opts)) + rows (cond->> rows + check-deleted? + (remove is-row-deleted?))] + (first rows)))) + +(defn get + ([ds table params] + (get ds table params nil)) + ([ds table params {:keys [check-deleted?] :or {check-deleted? true} :as opts}] + (let [row (get* ds table params opts)] + (when (and (not row) check-deleted?) (ex/raise :type :not-found :table table :hint "database object not found")) - res))) + row))) + +(defn get-by-params + "DEPRECATED" + ([ds table params] + (get-by-params ds table params nil)) + ([ds table params {:keys [check-not-found] :or {check-not-found true} :as opts}] + (let [row (get* ds table params (assoc opts :check-deleted? check-not-found))] + (when (and (not row) check-not-found) + (ex/raise :type :not-found + :table table + :hint "database object not found")) + row))) (defn get-by-id ([ds table id] - (get-by-params ds table {:id id} nil)) + (get ds table {:id id} nil)) ([ds table id opts] - (get-by-params ds table {:id id} opts))) + (let [opts (cond-> opts + (contains? opts :check-not-found) + (assoc :check-deleted? (:check-not-found opts)))] + (get ds table {:id id} opts)))) (defn query ([ds table params] diff --git a/backend/src/app/rpc/commands/verify_token.clj b/backend/src/app/rpc/commands/verify_token.clj index 27f453464..03e5e1d00 100644 --- a/backend/src/app/rpc/commands/verify_token.clj +++ b/backend/src/app/rpc/commands/verify_token.clj @@ -80,16 +80,19 @@ ;; --- Team Invitation (defn- accept-invitation - [{:keys [conn] :as cfg} {:keys [member-id team-id role member-email] :as claims} invitation] - (let [member (profile/retrieve-profile conn member-id) - - ;; Update the role if there is an invitation + [{:keys [conn] :as cfg} {:keys [team-id role member-email] :as claims} invitation member] + (let [;; Update the role if there is an invitation role (or (some-> invitation :role keyword) role) params (merge {:team-id team-id - :profile-id member-id} + :profile-id (:id member)} (teams/role->params role))] + ;; Do not allow blocked users accept invitations. + (when (:is-blocked member) + (ex/raise :type :restriction + :code :profile-blocked)) + ;; Insert the invited member to the team (db/insert! conn :team-profile-rel params {:on-conflict-do-nothing true}) @@ -98,7 +101,7 @@ (when-not (:is-active member) (db/update! conn :profile {:is-active true} - {:id member-id})) + {:id (:id member)})) ;; Delete the invitation (db/delete! conn :team-invitation @@ -106,7 +109,6 @@ (assoc member :is-active true))) - (s/def ::spec.team-invitation/profile-id ::us/uuid) (s/def ::spec.team-invitation/role ::us/keyword) (s/def ::spec.team-invitation/team-id ::us/uuid) @@ -122,23 +124,28 @@ :opt-un [::spec.team-invitation/member-id])) (defmethod process-token :team-invitation - [{:keys [conn session] :as cfg} {:keys [profile-id token]} {:keys [member-id team-id member-email] :as claims}] + [{:keys [conn session] :as cfg} {:keys [profile-id token]} + {:keys [member-id team-id member-email] :as claims}] + (us/assert ::team-invitation-claims claims) - (let [invitation (db/get-by-params conn :team-invitation - {:team-id team-id :email-to member-email} - {:check-not-found false})] + (let [invitation (db/get* conn :team-invitation + {:team-id team-id :email-to member-email}) + profile (db/get* conn :profile + {:id profile-id} + {:columns [:id :email]})] (when (nil? invitation) (ex/raise :type :validation :code :invalid-token :hint "no invitation associated with the token")) - (cond - ;; This happens when token is filled with member-id and current - ;; user is already logged in with exactly invited account. - (and (uuid? profile-id) (uuid? member-id)) - (if (= member-id profile-id) - (let [profile (accept-invitation cfg claims invitation)] + (if (some? profile) + (if (or (= member-id profile-id) + (= member-email (:email profile))) + ;; if we have logged-in user and it matches the invitation we + ;; proceed with accepting the invitation and joining the + ;; current profile to the invited team. + (let [profile (accept-invitation cfg claims invitation profile)] (with-meta (assoc claims :state :created) {::audit/name "accept-team-invitation" @@ -146,40 +153,36 @@ (audit/profile->props profile) {:team-id (:team-id claims) :role (:role claims)}) - ::audit/profile-id member-id})) + ::audit/profile-id profile-id})) + (ex/raise :type :validation :code :invalid-token :hint "logged-in user does not matches the invitation")) - ;; This happens when an unlogged user, uses an invitation link. - (and (not profile-id) (uuid? member-id)) - (let [profile (accept-invitation cfg claims invitation)] - (with-meta - (assoc claims :state :created) - {:transform-response ((:create session) (:id profile)) - ::audit/name "accept-team-invitation" - ::audit/props (merge - (audit/profile->props profile) - {:team-id (:team-id claims) - :role (:role claims)}) - ::audit/profile-id member-id})) + ;; If we have not logged-in user, we try find the invited + ;; profile by member-id or member-email props of the invitation + ;; token; If profile is found, we accept the invitation and + ;; leave the user logged-in. + (if-let [member (db/get* conn :profile + (if member-id + {:id member-id} + {:email member-email}) + {:columns [:id :email]})] + (let [profile (accept-invitation cfg claims invitation member)] + (with-meta + (assoc claims :state :created) + {:transform-response ((:create session) (:id profile)) + ::audit/name "accept-team-invitation" + ::audit/props (merge + (audit/profile->props profile) + {:team-id (:team-id claims) + :role (:role claims)}) + ::audit/profile-id member-id})) - ;; This case means that invitation token does not match with - ;; registred user, so we need to indicate to frontend to redirect - ;; it to register page. - (and (not profile-id) (nil? member-id)) - {:invitation-token token - :iss :team-invitation - :redirect-to :auth-register - :state :pending} - - ;; In all other cases, just tell to fontend to redirect the user - ;; to the login page. - :else - {:invitation-token token - :iss :team-invitation - :redirect-to :auth-login - :state :pending}))) + {:invitation-token token + :iss :team-invitation + :redirect-to :auth-register + :state :pending})))) ;; --- Default diff --git a/backend/src/app/rpc/mutations/teams.clj b/backend/src/app/rpc/mutations/teams.clj index 393ebad8b..e7f6e88df 100644 --- a/backend/src/app/rpc/mutations/teams.clj +++ b/backend/src/app/rpc/mutations/teams.clj @@ -376,18 +376,17 @@ :code :profile-is-muted :hint "looks like the profile has reported repeatedly as spam or has permanent bounces")) - (doseq [email emails] - (create-team-invitation - (assoc cfg - :email email - :conn conn - :team team - :profile profile - :role role)) - ) - - (with-meta {} - {::audit/props {:invitations (count emails)}})))) + (let [invitations (->> emails + (map (fn [email] + (assoc cfg + :email email + :conn conn + :team team + :profile profile + :role role))) + (map create-team-invitation))] + (with-meta (vec invitations) + {::audit/props {:invitations (count invitations)}}))))) (def sql:upsert-team-invitation "insert into team_invitation(team_id, email_to, role, valid_until) @@ -449,10 +448,7 @@ (when-not (:is-active member) (db/update! conn :profile {:is-active true} - {:id (:id member)})) - - (assoc member :is-active true)) - + {:id (:id member)}))) (do (db/exec-one! conn [sql:upsert-team-invitation (:id team) (str/lower email) (name role) @@ -464,7 +460,9 @@ :invited-by (:fullname profile) :team (:name team) :token itoken - :extra-data ptoken}))))) + :extra-data ptoken}))) + + itoken)) ;; --- Mutation: Create Team & Invite Members diff --git a/backend/src/app/tokens.clj b/backend/src/app/tokens.clj index 8c253fe32..30ca32b3b 100644 --- a/backend/src/app/tokens.clj +++ b/backend/src/app/tokens.clj @@ -26,10 +26,14 @@ (t/encode))] (jwe/encrypt payload tokens-key {:alg :a256kw :enc :a256gcm}))) +(defn decode + [{:keys [tokens-key]} token] + (let [payload (jwe/decrypt token tokens-key {:alg :a256kw :enc :a256gcm})] + (t/decode payload))) + (defn verify - [{:keys [tokens-key]} {:keys [token] :as params}] - (let [payload (jwe/decrypt token tokens-key {:alg :a256kw :enc :a256gcm}) - claims (t/decode payload)] + [sprops {:keys [token] :as params}] + (let [claims (decode sprops token)] (when (and (dt/instant? (:exp claims)) (dt/is-before? (:exp claims) (dt/now))) (ex/raise :type :validation diff --git a/backend/test/app/services_teams_test.clj b/backend/test/app/services_teams_test.clj index 766d62aec..650fda6b6 100644 --- a/backend/test/app/services_teams_test.clj +++ b/backend/test/app/services_teams_test.clj @@ -11,6 +11,7 @@ [app.http :as http] [app.storage :as sto] [app.test-helpers :as th] + [app.tokens :as tokens] [app.util.time :as dt] [clojure.test :as t] [datoteka.core :as fs] @@ -19,7 +20,7 @@ (t/use-fixtures :once th/state-init) (t/use-fixtures :each th/database-reset) -(t/deftest test-invite-team-member +(t/deftest invite-team-member (with-mocks [mock {:target 'app.emails/send! :return nil}] (let [profile1 (th/create-profile* 1 {:is-active true}) profile2 (th/create-profile* 2 {:is-active true}) @@ -34,17 +35,16 @@ :profile-id (:id profile1)}] ;; invite external user without complaints - (let [data (assoc data :email "foo@bar.com") - out (th/mutation! data) - ;;retrieve the value from the database and check its content + (let [data (assoc data :email "foo@bar.com") + out (th/mutation! data) + ;; retrieve the value from the database and check its content invitation (db/exec-one! - th/*pool* - ["select count(*) as num from team_invitation where team_id = ? and email_to = ?" - (:team-id data) "foo@bar.com"])] + th/*pool* + ["select count(*) as num from team_invitation where team_id = ? and email_to = ?" + (:team-id data) "foo@bar.com"])] ;; (th/print-result! out) - - (t/is (= {} (:result out))) + (t/is (th/success? out)) (t/is (= 1 (:call-count (deref mock)))) (t/is (= 1 (:num invitation)))) @@ -52,7 +52,7 @@ (th/reset-mock! mock) (let [data (assoc data :email (:email profile2)) out (th/mutation! data)] - (t/is (= {} (:result out))) + (t/is (th/success? out)) (t/is (= 1 (:call-count (deref mock))))) ;; invite user with complaint @@ -60,35 +60,183 @@ (th/reset-mock! mock) (let [data (assoc data :email "foo@bar.com") out (th/mutation! data)] - (t/is (= {} (:result out))) + (t/is (th/success? out)) (t/is (= 1 (:call-count (deref mock))))) ;; invite user with bounce (th/reset-mock! mock) + (th/create-global-complaint-for pool {:type :bounce :email "foo@bar.com"}) (let [data (assoc data :email "foo@bar.com") - out (th/mutation! data) - error (:error out)] + out (th/mutation! data)] - (t/is (th/ex-info? error)) - (t/is (th/ex-of-type? error :validation)) - (t/is (th/ex-of-code? error :email-has-permanent-bounces)) - (t/is (= 0 (:call-count (deref mock))))) + (t/is (not (th/success? out))) + (t/is (= 0 (:call-count @mock))) + + (let [edata (-> out :error ex-data)] + (t/is (= :validation (:type edata))) + (t/is (= :email-has-permanent-bounces (:code edata))))) ;; invite internal user that is muted (th/reset-mock! mock) - (let [data (assoc data :email (:email profile3)) - out (th/mutation! data) - error (:error out)] - (t/is (th/ex-info? error)) - (t/is (th/ex-of-type? error :validation)) - (t/is (th/ex-of-code? error :member-is-muted)) - (t/is (= 0 (:call-count (deref mock))))) + (let [data (assoc data :email (:email profile3)) + out (th/mutation! data)] + + (t/is (not (th/success? out))) + (t/is (= 0 (:call-count @mock))) + + (let [edata (-> out :error ex-data)] + (t/is (= :validation (:type edata))) + (t/is (= :member-is-muted (:code edata))))) ))) +(t/deftest invitation-tokens + (with-mocks [mock {:target 'app.emails/send! :return nil}] + (let [profile1 (th/create-profile* 1 {:is-active true}) + profile2 (th/create-profile* 2 {:is-active true}) + + team (th/create-team* 1 {:profile-id (:id profile1)}) + + sprops (:app.setup/props th/*system*) + pool (:app.db/pool th/*system*)] + + ;; Try to invite a not existing user + (let [data {::th/type :invite-team-member + :email "notexisting@example.com" + :team-id (:id team) + :role :editor + :profile-id (:id profile1)} + out (th/mutation! data)] + + ;; (th/print-result! out) + (t/is (th/success? out)) + (t/is (= 1 (:call-count @mock))) + (t/is (= 1 (-> out :result count))) + + (let [token (-> out :result first) + claims (tokens/decode sprops token)] + (t/is (= :team-invitation (:iss claims))) + (t/is (= (:id profile1) (:profile-id claims))) + (t/is (= :editor (:role claims))) + (t/is (= (:id team) (:team-id claims))) + (t/is (= (:email data) (:member-email claims))) + (t/is (nil? (:member-id claims))))) + + (th/reset-mock! mock) + + ;; Try to invite existing user + (let [data {::th/type :invite-team-member + :email (:email profile2) + :team-id (:id team) + :role :editor + :profile-id (:id profile1)} + out (th/mutation! data)] + + ;; (th/print-result! out) + (t/is (th/success? out)) + (t/is (= 1 (:call-count @mock))) + (t/is (= 1 (-> out :result count))) + + (let [token (-> out :result first) + claims (tokens/decode sprops token)] + (t/is (= :team-invitation (:iss claims))) + (t/is (= (:id profile1) (:profile-id claims))) + (t/is (= :editor (:role claims))) + (t/is (= (:id team) (:team-id claims))) + (t/is (= (:email data) (:member-email claims))) + (t/is (= (:id profile2) (:member-id claims))))) + + ))) + + +(t/deftest accept-invitation-tokens + (let [profile1 (th/create-profile* 1 {:is-active true}) + profile2 (th/create-profile* 2 {:is-active true}) + + team (th/create-team* 1 {:profile-id (:id profile1)}) + + sprops (:app.setup/props th/*system*) + pool (:app.db/pool th/*system*)] + + (let [token (tokens/generate sprops + {:iss :team-invitation + :exp (dt/in-future "1h") + :profile-id (:id profile1) + :role :editor + :team-id (:id team) + :member-email (:email profile2) + :member-id (:id profile2)})] + + ;; --- Verify token as anonymous user + + (db/insert! pool :team-invitation + {:team-id (:id team) + :email-to (:email profile2) + :role "editor" + :valid-until (dt/in-future "48h")}) + + (let [data {::th/type :verify-token :token token} + out (th/mutation! data)] + ;; (th/print-result! out) + (t/is (th/success? out)) + (let [result (:result out)] + (t/is (= :created (:state result))) + (t/is (= (:email profile2) (:member-email result))) + (t/is (= (:id profile2) (:member-id result)))) + + (let [rows (db/query pool :team-profile-rel {:team-id (:id team)})] + (t/is (= 2 (count rows))))) + + ;; Clean members + (db/delete! pool :team-profile-rel + {:team-id (:id team) + :profile-id (:id profile2)}) + + + ;; --- Verify token as logged-in user + + (db/insert! pool :team-invitation + {:team-id (:id team) + :email-to (:email profile2) + :role "editor" + :valid-until (dt/in-future "48h")}) + + (let [data {::th/type :verify-token :token token :profile-id (:id profile2)} + out (th/mutation! data)] + ;; (th/print-result! out) + (t/is (th/success? out)) + (let [result (:result out)] + (t/is (= :created (:state result))) + (t/is (= (:email profile2) (:member-email result))) + (t/is (= (:id profile2) (:member-id result)))) + + (let [rows (db/query pool :team-profile-rel {:team-id (:id team)})] + (t/is (= 2 (count rows))))) + + + ;; --- Verify token as logged-in wrong user + + (db/insert! pool :team-invitation + {:team-id (:id team) + :email-to (:email profile2) + :role "editor" + :valid-until (dt/in-future "48h")}) + + (let [data {::th/type :verify-token :token token :profile-id (:id profile1)} + out (th/mutation! data)] + ;; (th/print-result! out) + (t/is (not (th/success? out))) + (let [edata (-> out :error ex-data)] + (t/is (= :validation (:type edata))) + (t/is (= :invalid-token (:code edata))))) + + ))) + + + (t/deftest invite-team-member-with-email-verification-disabled (with-mocks [mock {:target 'app.emails/send! :return nil}] (let [profile1 (th/create-profile* 1 {:is-active true}) @@ -108,20 +256,17 @@ (th/reset-mock! mock) (let [data (assoc data :email (:email profile2)) out (th/mutation! data)] - (t/is (= {} (:result out))) + (t/is (th/success? out)) (t/is (= 0 (:call-count (deref mock))))) - (let [members (db/query pool :team-profile-rel {:team-id (:id team) :profile-id (:id profile2)})] (t/is (= 1 (count members))) (t/is (true? (-> members first :can-edit)))))))) - -(t/deftest test-deletion - (let [task (:app.tasks.objects-gc/handler th/*system*) - profile1 (th/create-profile* 1 {:is-active true}) +(t/deftest team-deletion + (let [profile1 (th/create-profile* 1 {:is-active true}) team (th/create-team* 1 {:profile-id (:id profile1)}) pool (:app.db/pool th/*system*) data {::th/type :delete-team @@ -130,7 +275,7 @@ ;; team is not deleted because it does not meet all ;; conditions to be deleted. - (let [result (task {:min-age (dt/duration 0)})] + (let [result (th/run-task! :objects-gc {:min-age (dt/duration 0)})] (t/is (= 0 (:processed result)))) ;; query the list of teams @@ -138,7 +283,7 @@ :profile-id (:id profile1)} out (th/query! data)] ;; (th/print-result! out) - (t/is (nil? (:error out))) + (t/is (th/success? out)) (let [result (:result out)] (t/is (= 2 (count result))) (t/is (= (:id team) (get-in result [1 :id]))) @@ -149,21 +294,20 @@ :id (:id team) :profile-id (:id profile1)} out (th/mutation! params)] - ;; (th/print-result! out) - (t/is (nil? (:error out)))) + (t/is (th/success? out))) ;; query the list of teams after soft deletion (let [data {::th/type :teams :profile-id (:id profile1)} out (th/query! data)] ;; (th/print-result! out) - (t/is (nil? (:error out))) + (t/is (th/success? out)) (let [result (:result out)] (t/is (= 1 (count result))) (t/is (= (:default-team-id profile1) (get-in result [0 :id]))))) ;; run permanent deletion (should be noop) - (let [result (task {:min-age (dt/duration {:minutes 1})})] + (let [result (th/run-task! :objects-gc {:min-age (dt/duration {:minutes 1})})] (t/is (= 0 (:processed result)))) ;; query the list of projects after hard deletion @@ -172,13 +316,12 @@ :profile-id (:id profile1)} out (th/query! data)] ;; (th/print-result! out) - (let [error (:error out) - error-data (ex-data error)] - (t/is (th/ex-info? error)) - (t/is (= (:type error-data) :not-found)))) + (t/is (not (th/success? out))) + (let [edata (-> out :error ex-data)] + (t/is (= :not-found (:type edata))))) ;; run permanent deletion - (let [result (task {:min-age (dt/duration 0)})] + (let [result (th/run-task! :objects-gc {:min-age (dt/duration 0)})] (t/is (= 1 (:processed result)))) ;; query the list of projects of a after hard deletion @@ -187,31 +330,27 @@ :profile-id (:id profile1)} out (th/query! data)] ;; (th/print-result! out) - (let [error (:error out) - error-data (ex-data error)] - (t/is (th/ex-info? error)) - (t/is (= (:type error-data) :not-found)))) + + (t/is (not (th/success? out))) + (let [edata (-> out :error ex-data)] + (t/is (= :not-found (:type edata))))) )) - - - (t/deftest query-team-invitations - (let [prof (th/create-profile* 1 {:is-active true}) - team (th/create-team* 1 {:profile-id (:id prof)}) + (let [prof (th/create-profile* 1 {:is-active true}) + team (th/create-team* 1 {:profile-id (:id prof)}) data {::th/type :team-invitations :profile-id (:id prof) :team-id (:id team)}] - ;;insert an entry on the database with an enabled invitation + ;; insert an entry on the database with an enabled invitation (db/insert! th/*pool* :team-invitation - {:team-id (:team-id data) - :email-to "test1@mail.com" - :role "editor" - :valid-until (dt/in-future "48h")}) + {:team-id (:team-id data) + :email-to "test1@mail.com" + :role "editor" + :valid-until (dt/in-future "48h")}) - - ;;insert an entry on the database with an expired invitation + ;; insert an entry on the database with an expired invitation (db/insert! th/*pool* :team-invitation {:team-id (:team-id data) :email-to "test2@mail.com" @@ -219,27 +358,26 @@ :valid-until (dt/in-past "48h")}) (let [out (th/query! data)] - (t/is (nil? (:error out))) + (t/is (th/success? out)) (let [result (:result out) - one (first result) - two (second result)] + one (first result) + two (second result)] (t/is (= 2 (count result))) (t/is (= "test1@mail.com" (:email one))) (t/is (= "test2@mail.com" (:email two))) (t/is (false? (:expired one))) (t/is (true? (:expired two))))))) - (t/deftest update-team-invitation-role - (let [prof (th/create-profile* 1 {:is-active true}) - team (th/create-team* 1 {:profile-id (:id prof)}) + (let [prof (th/create-profile* 1 {:is-active true}) + team (th/create-team* 1 {:profile-id (:id prof)}) data {::th/type :update-team-invitation-role :profile-id (:id prof) :team-id (:id team) :email "TEST1@mail.com" :role :admin}] - ;;insert an entry on the database with an invitation + ;; insert an entry on the database with an invitation (db/insert! th/*pool* :team-invitation {:team-id (:team-id data) :email-to "test1@mail.com" @@ -247,24 +385,22 @@ :valid-until (dt/in-future "48h")}) (let [out (th/mutation! data) - ;;retrieve the value from the database and check its content - result (db/get-by-params th/*pool* :team-invitation - {:team-id (:team-id data) :email-to "test1@mail.com"} - {:check-not-found false})] - (t/is (nil? (:error out))) + ;; retrieve the value from the database and check its content + res (db/get* th/*pool* :team-invitation + {:team-id (:team-id data) :email-to "test1@mail.com"})] + (t/is (th/success? out)) (t/is (nil? (:result out))) - (t/is (= "admin" (:role result)))))) - + (t/is (= "admin" (:role res)))))) (t/deftest delete-team-invitation - (let [prof (th/create-profile* 1 {:is-active true}) - team (th/create-team* 1 {:profile-id (:id prof)}) + (let [prof (th/create-profile* 1 {:is-active true}) + team (th/create-team* 1 {:profile-id (:id prof)}) data {::th/type :delete-team-invitation :profile-id (:id prof) :team-id (:id team) :email "TEST1@mail.com"}] - ;;insert an entry on the database with an invitation + ;; insert an entry on the database with an invitation (db/insert! th/*pool* :team-invitation {:team-id (:team-id data) :email-to "test1@mail.com" @@ -272,10 +408,10 @@ :valid-until (dt/in-future "48h")}) (let [out (th/mutation! data) - ;;retrieve the value from the database and check its content - result (db/get-by-params th/*pool* :team-invitation - {:team-id (:team-id data) :email-to "test1@mail.com"} - {:check-not-found false})] - (t/is (nil? (:error out))) + ;; retrieve the value from the database and check its content + res (db/get* th/*pool* :team-invitation + {:team-id (:team-id data) :email-to "test1@mail.com"})] + + (t/is (th/success? out)) (t/is (nil? (:result out))) - (t/is (nil? result))))) + (t/is (nil? res)))))