| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180 |
- (ns frontend.components.encryption
- (:require [clojure.string :as string]
- [frontend.context.i18n :refer [t]]
- [frontend.encrypt :as e]
- [frontend.handler.metadata :as metadata-handler]
- [frontend.handler.notification :as notification]
- [frontend.state :as state]
- [frontend.ui :as ui]
- [frontend.util :as util]
- [promesa.core :as p]
- [rum.core :as rum]))
- (rum/defcs encryption-dialog-inner <
- (rum/local false ::reveal-secret-phrase?)
- [state repo-url close-fn]
- (let [reveal-secret-phrase? (get state ::reveal-secret-phrase?)
- public-key (e/get-public-key repo-url)
- private-key (e/get-secret-key repo-url)]
- [:div
- [:div.sm:flex.sm:items-start
- [:div.mt-3.text-center.sm:mt-0.sm:text-left
- [:h3#modal-headline.text-lg.leading-6.font-medium
- "This graph is encrypted with " [:a {:href "https://age-encryption.org/" :target "_blank" :rel "noopener"} "age-encryption.org/v1"]]]]
- [:div.mt-1
- [:div.max-w-2xl.rounded-md.shadow-sm.sm:max-w-xl
- [:div.cursor-pointer.block.w-full.rounded-sm.p-2
- {:on-click (fn []
- (when (not @reveal-secret-phrase?)
- (reset! reveal-secret-phrase? true)))}
- [:div.font-medium "Public Key:"]
- [:div.font-mono.select-all.break-all public-key]
- (if @reveal-secret-phrase?
- [:div
- [:div.mt-1.font-medium "Private Key:"]
- [:div.font-mono.select-all.break-all private-key]]
- [:div.underline "click to view the private key"])]]]
- [:div.mt-5.sm:mt-4.sm:flex.sm:flex-row-reverse
- [:span.mt-3.flex.w-full.rounded-md.shadow-sm.sm:mt-0.sm:w-auto
- [:button.inline-flex.justify-center.w-full.rounded-md.border.border-gray-300.px-4.py-2.bg-white.text-base.leading-6.font-medium.text-gray-700.shadow-sm.hover:text-gray-500.focus:outline-none.focus:border-blue-300.focus:shadow-outline-blue.transition.ease-in-out.duration-150.sm:text-sm.sm:leading-5
- {:type "button"
- :on-click close-fn}
- (t :close)]]]]))
- (defn encryption-dialog
- [repo-url]
- (fn [close-fn]
- (encryption-dialog-inner repo-url close-fn)))
- (rum/defcs input-password-inner <
- (rum/local "" ::password)
- (rum/local "" ::password-confirm)
- [state repo-url close-fn]
- (let [password (get state ::password)
- password-confirm (get state ::password-confirm)]
- [:div
- [:div.sm:flex.sm:items-start
- [:div.mt-3.text-center.sm:mt-0.sm:text-left
- [:h3#modal-headline.text-lg.leading-6.font-medium.font-bold
- "Enter a password"]]]
- (ui/admonition
- :warning
- [:div.opacity-70
- "Choose a strong and hard to guess password.\nIf you lose your password, all the data can't be decrypted!! Please make sure you remember the password you have set, or you can keep a secure backup of the password."])
- [:input.form-input.block.w-full.sm:text-sm.sm:leading-5.my-2
- {:type "password"
- :placeholder "Password"
- :auto-focus true
- :on-change (fn [e]
- (reset! password (util/evalue e)))}]
- [:input.form-input.block.w-full.sm:text-sm.sm:leading-5.my-2
- {:type "password"
- :placeholder "Re-enter the password"
- :on-change (fn [e]
- (reset! password-confirm (util/evalue e)))}]
- [:div.mt-5.sm:mt-4.sm:flex.sm:flex-row-reverse
- [:span.flex.w-full.rounded-md.shadow-sm.sm:ml-3.sm:w-auto
- [:button.inline-flex.justify-center.w-full.rounded-md.border.border-transparent.px-4.py-2.bg-indigo-600.text-base.leading-6.font-medium.text-white.shadow-sm.hover:bg-indigo-500.focus:outline-none.focus:border-indigo-700.focus:shadow-outline-indigo.transition.ease-in-out.duration-150.sm:text-sm.sm:leading-5
- {:type "button"
- :on-click (fn []
- (let [value @password]
- (cond
- (string/blank? value)
- nil
- (not= @password @password-confirm)
- (notification/show! "The passwords are not matched." :error)
- :else
- (p/let [keys (e/generate-key-pair-and-save! repo-url)
- db-encrypted-secret (e/encrypt-with-passphrase value keys)]
- (metadata-handler/set-db-encrypted-secret! db-encrypted-secret)
- (close-fn true)))))}
- "Submit"]]]]))
- (defn input-password
- [repo-url close-fn]
- (fn [_close-fn]
- (input-password-inner repo-url close-fn)))
- (rum/defcs encryption-setup-dialog-inner
- [state repo-url close-fn]
- [:div
- [:div.sm:flex.sm:items-start
- [:div.mt-3.text-center.sm:mt-0.sm:text-left
- [:h3#modal-headline.text-lg.leading-6.font-medium
- "Do you want to create an encrypted graph?"]]]
- [:div.mt-5.sm:mt-4.sm:flex.sm:flex-row-reverse
- [:span.flex.w-full.rounded-md.shadow-sm.sm:ml-3.sm:w-auto
- [:button.inline-flex.justify-center.w-full.rounded-md.border.border-transparent.px-4.py-2.bg-indigo-600.text-base.leading-6.font-medium.text-white.shadow-sm.hover:bg-indigo-500.focus:outline-none.focus:border-indigo-700.focus:shadow-outline-indigo.transition.ease-in-out.duration-150.sm:text-sm.sm:leading-5
- {:type "button"
- :on-click (fn []
- (state/set-modal! (input-password repo-url close-fn)))}
- (t :yes)]]
- [:span.mt-3.flex.w-full.rounded-md.shadow-sm.sm:mt-0.sm:w-auto
- [:button.inline-flex.justify-center.w-full.rounded-md.border.border-gray-300.px-4.py-2.bg-white.text-base.leading-6.font-medium.text-gray-700.shadow-sm.hover:text-gray-500.focus:outline-none.focus:border-blue-300.focus:shadow-outline-blue.transition.ease-in-out.duration-150.sm:text-sm.sm:leading-5
- {:type "button"
- :on-click (fn [] (close-fn false))}
- (t :no)]]]])
- (defn encryption-setup-dialog
- [repo-url close-fn]
- (fn [close-modal-fn]
- (let [close-fn (fn [encrypted?]
- (close-fn encrypted?)
- (close-modal-fn))]
- (encryption-setup-dialog-inner repo-url close-fn))))
- (rum/defcs encryption-input-secret-inner <
- (rum/local "" ::secret)
- (rum/local false ::loading)
- [state _repo-url db-encrypted-secret close-fn]
- (let [secret (::secret state)
- loading (::loading state)
- on-click-fn (fn []
- (reset! loading true)
- (let [value @secret]
- (when-not (string/blank? value) ; TODO: length or other checks
- (let [repo (state/get-current-repo)]
- (p/do!
- (-> (e/decrypt-with-passphrase value db-encrypted-secret)
- (p/then (fn [keys]
- (e/save-key-pair! repo keys)
- (close-fn true)
- (state/set-state! :encryption/graph-parsing? false)))
- (p/catch #(notification/show! "The password is not matched." :warning true))
- (p/finally #(reset! loading false))))))))]
- [:div
- [:div.sm:flex.sm:items-start
- [:div.mt-3.text-center.sm:mt-0.sm:text-left
- [:h3#modal-headline.text-lg.leading-6.font-medium
- "Enter your password"]]]
- [:input.form-input.block.w-full.sm:text-sm.sm:leading-5.my-2
- {:type "password"
- :auto-focus true
- :on-change (fn [e]
- (reset! secret (util/evalue e)))
- :on-key-down (fn [e]
- (when (= (.-key e) "Enter")
- (on-click-fn)))}]
- [:div.mt-5.sm:mt-4.sm:flex.sm:flex-row-reverse
- [:span.flex.w-full.rounded-md.shadow-sm.sm:ml-3.sm:w-auto
- [:button.inline-flex.justify-center.w-full.rounded-md.border.border-transparent.px-4.py-2.bg-indigo-600.text-base.leading-6.font-medium.text-white.shadow-sm.hover:bg-indigo-500.focus:outline-none.focus:border-indigo-700.focus:shadow-outline-indigo.transition.ease-in-out.duration-150.sm:text-sm.sm:leading-5
- {:type "button"
- :on-click on-click-fn}
- (if @loading (ui/loading "Decrypting") "Decrypt")]]]]))
- (defn encryption-input-secret-dialog
- [repo-url db-encrypted-secret close-fn]
- (fn [close-modal-fn]
- (let [close-fn (fn [encrypted?]
- (close-fn encrypted?)
- (close-modal-fn))]
- (encryption-input-secret-inner repo-url db-encrypted-secret close-fn))))
|