diff --git a/CHANGES.md b/CHANGES.md index 5fbaba21b..ce1b7eafe 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -23,6 +23,10 @@ - Add Libraries & Templates carousel [Taiga #3860](https://tree.taiga.io/project/penpot/us/3860) - Ungroup frames [Taiga #4012](https://tree.taiga.io/project/penpot/us/4012) - Newsletter Opt-in options for subscription categories [Taiga #3242](https://tree.taiga.io/project/penpot/us/3242) +- Print emails to console by default if smtp is disabled +- Add `email-verification` flag for enable/disable email verification + + ### :bug: Bugs fixed @@ -51,6 +55,7 @@ - Fix issue when scaling to value 0 [#2252](https://github.com/penpot/penpot/issues/2252) - Fix problem when moving shapes inside nested frames [Taiga #4113](https://tree.taiga.io/project/penpot/issue/4113) + ## 1.15.3-beta ### :bug: Bugs fixed diff --git a/backend/src/app/config.clj b/backend/src/app/config.clj index 8492b5b56..1a856acd4 100644 --- a/backend/src/app/config.clj +++ b/backend/src/app/config.clj @@ -317,7 +317,8 @@ (def default-flags [:enable-backend-api-doc :enable-backend-worker - :enable-secure-session-cookies]) + :enable-secure-session-cookies + :enable-email-verification]) (defn- parse-flags [config] diff --git a/backend/src/app/main.clj b/backend/src/app/main.clj index 65a890599..318b3a7df 100644 --- a/backend/src/app/main.clj +++ b/backend/src/app/main.clj @@ -417,6 +417,7 @@ (ig/prep) (ig/init)))) (l/info :msg "welcome to penpot" + :worker? (contains? cf/flags :backend-worker) :version (:full cf/version))) (defn stop diff --git a/backend/src/app/rpc/commands/auth.clj b/backend/src/app/rpc/commands/auth.clj index 8a525860c..5129100a1 100644 --- a/backend/src/app/rpc/commands/auth.clj +++ b/backend/src/app/rpc/commands/auth.clj @@ -324,17 +324,25 @@ params (merge params claims)] (check-profile-existence! conn params) (let [is-active (or (:is-active params) + (not (contains? cf/flags :email-verification)) + + ;; DEPRECATED: v1.15 (contains? cf/flags :insecure-register)) + profile (->> (assoc params :is-active is-active) (create-profile conn) (create-profile-relations conn) (profile/decode-profile-row)) + invitation (when-let [token (:invitation-token params)] (tokens/verify sprops {:token token :iss :team-invitation}))] (cond - ;; If invitation token comes in params, this is because the user comes from team-invitation process; - ;; in this case, regenerate token and send back to the user a new invitation token (and mark current - ;; session as logged). This happens only if the invitation email matches with the register email. + ;; If invitation token comes in params, this is because the + ;; user comes from team-invitation process; in this case, + ;; regenerate token and send back to the user a new invitation + ;; token (and mark current session as logged). This happens + ;; only if the invitation email matches with the register + ;; email. (and (some? invitation) (= (:email profile) (:member-email invitation))) (let [claims (assoc invitation :member-id (:id profile)) token (tokens/generate sprops claims) diff --git a/backend/src/app/rpc/mutations/teams.clj b/backend/src/app/rpc/mutations/teams.clj index ac01bd5f5..9370c6506 100644 --- a/backend/src/app/rpc/mutations/teams.clj +++ b/backend/src/app/rpc/mutations/teams.clj @@ -419,26 +419,51 @@ (ex/raise :type :validation :code :member-is-muted :email email - :hint "looks like the profile has reported repeatedly as spam or has permanent bounces")) + :hint "the profile has reported repeatedly as spam or has bounces")) ;; Secondly check if the invited member email is part of the global spam/bounce report. (when (eml/has-bounce-reports? conn email) (ex/raise :type :validation :code :email-has-permanent-bounces :email email - :hint "looks like the email you invite has been repeatedly reported as spam or permanent bounce")) + :hint "the email you invite has been repeatedly reported as spam or bounce")) - (db/exec-one! conn [sql:upsert-team-invitation - (:id team) (str/lower email) (name role) token-exp (name role) token-exp]) + ;; When we have email verification disabled and invitation user is + ;; already present in the database, we proceed to add it to the + ;; team as-is, without email roundtrip. - (eml/send! {::eml/conn conn - ::eml/factory eml/invite-to-team - :public-uri (:public-uri cfg) - :to email - :invited-by (:fullname profile) - :team (:name team) - :token itoken - :extra-data ptoken}))) + ;; TODO: if member does not exists and email verification is + ;; disabled, we should proceed to create the profile (?) + (if (and (not (contains? cf/flags :email-verification)) + (some? member)) + (let [params (merge {:team-id (:id team) + :profile-id (:id member)} + (role->params role))] + + ;; Insert the invited member to the team + (db/insert! conn :team-profile-rel params {:on-conflict-do-nothing true}) + + ;; If profile is not yet verified, mark it as verified because + ;; accepting an invitation link serves as verification. + (when-not (:is-active member) + (db/update! conn :profile + {:is-active true} + {:id (:id member)})) + + (assoc member :is-active true)) + + (do + (db/exec-one! conn [sql:upsert-team-invitation + (:id team) (str/lower email) (name role) + token-exp (name role) token-exp]) + (eml/send! {::eml/conn conn + ::eml/factory eml/invite-to-team + :public-uri (:public-uri cfg) + :to email + :invited-by (:fullname profile) + :team (:name team) + :token itoken + :extra-data ptoken}))))) ;; --- Mutation: Create Team & Invite Members diff --git a/backend/test/app/services_teams_test.clj b/backend/test/app/services_teams_test.clj index 1e3fe5522..766d62aec 100644 --- a/backend/test/app/services_teams_test.clj +++ b/backend/test/app/services_teams_test.clj @@ -88,6 +88,37 @@ ))) + +(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}) + profile2 (th/create-profile* 2 {:is-active true}) + profile3 (th/create-profile* 3 {:is-active true :is-muted true}) + + team (th/create-team* 1 {:profile-id (:id profile1)}) + + pool (:app.db/pool th/*system*) + data {::th/type :invite-team-member + :team-id (:id team) + :role :editor + :profile-id (:id profile1)}] + + ;; invite internal user without complaints + (with-redefs [app.config/flags #{}] + (th/reset-mock! mock) + (let [data (assoc data :email (:email profile2)) + out (th/mutation! data)] + (t/is (= {} (:result 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}) diff --git a/docker/images/config.env b/docker/images/config.env index cc880d70f..5da4a1939 100644 --- a/docker/images/config.env +++ b/docker/images/config.env @@ -6,11 +6,10 @@ ## setting. PENPOT_PUBLIC_URI=http://localhost:9001 -PENPOT_TENANT=pro ## Feature flags. -PENPOT_FLAGS="enable-registration enable-login" +PENPOT_FLAGS="enable-registration enable-login disable-email-verification" ## Temporal workaround because of bad builtin default @@ -43,7 +42,6 @@ PENPOT_TELEMETRY_ENABLED=true ## console, but for production usage is recommended to setup a real ## SMTP provider. Emails are used to confirm user registrations. -PENPOT_SMTP_ENABLED=false PENPOT_SMTP_DEFAULT_FROM=no-reply@example.com PENPOT_SMTP_DEFAULT_REPLY_TO=no-reply@example.com # PENPOT_SMTP_HOST=