Unauthenticated | (ns metabase.api.notification.unsubscribe (:require [medley.core :as m] [metabase.api.macros :as api.macros] [metabase.channel.email.messages :as messages] [metabase.config :as config] [metabase.events :as events] [metabase.request.core :as request] [metabase.util.i18n :refer [tru]] [metabase.util.malli.schema :as ms] [throttle.core :as throttle] [toucan2.core :as t2])) |
(def ^:private throttling-disabled? (config/config-bool :mb-disable-session-throttle)) | |
(def ^:private unsubscribe-throttler (throttle/make-throttler :notification-unsubscribe, :attempts-threshold 50)) | |
(defn- check-hash [notification-handler-id email hash ip-address] (when-not throttling-disabled? (throttle/check unsubscribe-throttler ip-address)) (when (not= hash (messages/generate-notification-unsubscribe-hash notification-handler-id email)) (throw (ex-info (tru "Invalid hash.") {:status-code 400})))) | |
(defn- notification-name-by-handler-id [notification-handler-id] (let [notification (t2/hydrate (t2/select-one :model/Notification :id [:in {:select [:notification_id] :from :notification_handler :where [:= :id notification-handler-id]}]) :payload)] (case (:payload_type notification) ;; use the card name :notification/card (->> notification :payload :card_id (t2/select-one-fn :name :model/Card)) ;; use the dashboard name :notification/dashboard (->> notification :payload :dashboard_id (t2/select-one-fn :name :model/Dashboard)) (name (:payload_type notification))))) | |
(api.macros/defendpoint :post "/" "Allow non-users to unsubscribe from notifications, with the hash given through email." [_route-params _query-params {:keys [email hash notification-handler-id]} :- [:map [:notification-handler-id ms/PositiveInt] [:email :string] [:hash :string]] request] (check-hash notification-handler-id email hash (request/ip-address request)) (t2/with-transaction [_conn] (let [recipients (t2/select :model/NotificationRecipient :notification_handler_id notification-handler-id :type :notification-recipient/raw-value) matching-recipient (m/find-first #(= email (-> % :details :value)) recipients)] (if matching-recipient (t2/delete! :model/NotificationRecipient (:id matching-recipient)) (throw (ex-info (tru "Email doesn''t exist.") {:status-code 400}))))) (events/publish-event! :event/notification-unsubscribe-ex {:details {:email email} :object {:id notification-handler-id}}) {:status :success :title (notification-name-by-handler-id notification-handler-id)}) | |
(api.macros/defendpoint :post "/undo" "Allow non-users to undo an unsubscribe from notifications, with the hash given through email." [_route-params _query-params {:keys [email hash notification-handler-id]} :- [:map [:notification-handler-id ms/PositiveInt] [:email :string] [:hash :string]] request] (check-hash notification-handler-id email hash (request/ip-address request)) (t2/with-transaction [_conn] (let [recipients (t2/select :model/NotificationRecipient :notification_handler_id notification-handler-id :type :notification-recipient/raw-value) matching-recipient (m/find-first #(= email (-> % :details :value)) recipients)] (if-not matching-recipient (t2/insert! :model/NotificationRecipient {:type :notification-recipient/raw-value :details {:value email} :notification_handler_id notification-handler-id}) (throw (ex-info (tru "Email already exist.") {:status-code 400}))))) (events/publish-event! :event/notification-unsubscribe-undo-ex {:details {:email email} :object {:id notification-handler-id}}) {:status :success :title (notification-name-by-handler-id notification-handler-id)}) | |