encryption.cljs 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. (ns frontend.components.encryption
  2. (:require [clojure.string :as string]
  3. [frontend.context.i18n :refer [t]]
  4. [frontend.encrypt :as e]
  5. [frontend.handler.metadata :as metadata-handler]
  6. [frontend.handler.notification :as notification]
  7. [frontend.state :as state]
  8. [frontend.ui :as ui]
  9. [frontend.util :as util]
  10. [promesa.core :as p]
  11. [rum.core :as rum]))
  12. (rum/defcs encryption-dialog-inner <
  13. (rum/local false ::reveal-secret-phrase?)
  14. [state repo-url close-fn]
  15. (let [reveal-secret-phrase? (get state ::reveal-secret-phrase?)
  16. public-key (e/get-public-key repo-url)
  17. private-key (e/get-secret-key repo-url)]
  18. [:div
  19. [:div.sm:flex.sm:items-start
  20. [:div.mt-3.text-center.sm:mt-0.sm:text-left
  21. [:h3#modal-headline.text-lg.leading-6.font-medium
  22. "This graph is encrypted with " [:a {:href "https://age-encryption.org/" :target "_blank" :rel "noopener"} "age-encryption.org/v1"]]]]
  23. [:div.mt-1
  24. [:div.max-w-2xl.rounded-md.shadow-sm.sm:max-w-xl
  25. [:div.cursor-pointer.block.w-full.rounded-sm.p-2
  26. {:on-click (fn []
  27. (when (not @reveal-secret-phrase?)
  28. (reset! reveal-secret-phrase? true)))}
  29. [:div.font-medium "Public Key:"]
  30. [:div.font-mono.select-all.break-all public-key]
  31. (if @reveal-secret-phrase?
  32. [:div
  33. [:div.mt-1.font-medium "Private Key:"]
  34. [:div.font-mono.select-all.break-all private-key]]
  35. [:div.underline "click to view the private key"])]]]
  36. [:div.mt-5.sm:mt-4.sm:flex.sm:flex-row-reverse
  37. [:span.mt-3.flex.w-full.rounded-md.shadow-sm.sm:mt-0.sm:w-auto
  38. [: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
  39. {:type "button"
  40. :on-click close-fn}
  41. (t :close)]]]]))
  42. (defn encryption-dialog
  43. [repo-url]
  44. (fn [close-fn]
  45. (encryption-dialog-inner repo-url close-fn)))
  46. (rum/defcs input-password-inner <
  47. (rum/local "" ::password)
  48. (rum/local "" ::password-confirm)
  49. [state repo-url close-fn]
  50. (let [password (get state ::password)
  51. password-confirm (get state ::password-confirm)]
  52. [:div
  53. [:div.sm:flex.sm:items-start
  54. [:div.mt-3.text-center.sm:mt-0.sm:text-left
  55. [:h3#modal-headline.text-lg.leading-6.font-medium.font-bold
  56. "Enter a password"]]]
  57. (ui/admonition
  58. :warning
  59. [:div.opacity-70
  60. "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."])
  61. [:input.form-input.block.w-full.sm:text-sm.sm:leading-5.my-2
  62. {:type "password"
  63. :placeholder "Password"
  64. :auto-focus true
  65. :on-change (fn [e]
  66. (reset! password (util/evalue e)))}]
  67. [:input.form-input.block.w-full.sm:text-sm.sm:leading-5.my-2
  68. {:type "password"
  69. :placeholder "Re-enter the password"
  70. :on-change (fn [e]
  71. (reset! password-confirm (util/evalue e)))}]
  72. [:div.mt-5.sm:mt-4.sm:flex.sm:flex-row-reverse
  73. [:span.flex.w-full.rounded-md.shadow-sm.sm:ml-3.sm:w-auto
  74. [: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
  75. {:type "button"
  76. :on-click (fn []
  77. (let [value @password]
  78. (cond
  79. (string/blank? value)
  80. nil
  81. (not= @password @password-confirm)
  82. (notification/show! "The passwords are not matched." :error)
  83. :else
  84. (p/let [keys (e/generate-key-pair-and-save! repo-url)
  85. db-encrypted-secret (e/encrypt-with-passphrase value keys)]
  86. (metadata-handler/set-db-encrypted-secret! db-encrypted-secret)
  87. (close-fn true)))))}
  88. "Submit"]]]]))
  89. (defn input-password
  90. [repo-url close-fn]
  91. (fn [_close-fn]
  92. (input-password-inner repo-url close-fn)))
  93. (rum/defcs encryption-setup-dialog-inner
  94. [state repo-url close-fn]
  95. [:div
  96. [:div.sm:flex.sm:items-start
  97. [:div.mt-3.text-center.sm:mt-0.sm:text-left
  98. [:h3#modal-headline.text-lg.leading-6.font-medium
  99. "Do you want to create an encrypted graph?"]]]
  100. [:div.mt-5.sm:mt-4.sm:flex.sm:flex-row-reverse
  101. [:span.flex.w-full.rounded-md.shadow-sm.sm:ml-3.sm:w-auto
  102. [: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
  103. {:type "button"
  104. :on-click (fn []
  105. (state/set-modal! (input-password repo-url close-fn)))}
  106. (t :yes)]]
  107. [:span.mt-3.flex.w-full.rounded-md.shadow-sm.sm:mt-0.sm:w-auto
  108. [: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
  109. {:type "button"
  110. :on-click (fn [] (close-fn false))}
  111. (t :no)]]]])
  112. (defn encryption-setup-dialog
  113. [repo-url close-fn]
  114. (fn [close-modal-fn]
  115. (let [close-fn (fn [encrypted?]
  116. (close-fn encrypted?)
  117. (close-modal-fn))]
  118. (encryption-setup-dialog-inner repo-url close-fn))))
  119. (rum/defcs encryption-input-secret-inner <
  120. (rum/local "" ::secret)
  121. (rum/local false ::loading)
  122. [state _repo-url db-encrypted-secret close-fn]
  123. (let [secret (::secret state)
  124. loading (::loading state)
  125. on-click-fn (fn []
  126. (reset! loading true)
  127. (let [value @secret]
  128. (when-not (string/blank? value) ; TODO: length or other checks
  129. (let [repo (state/get-current-repo)]
  130. (p/do!
  131. (-> (e/decrypt-with-passphrase value db-encrypted-secret)
  132. (p/then (fn [keys]
  133. (e/save-key-pair! repo keys)
  134. (close-fn true)
  135. (state/set-state! :encryption/graph-parsing? false)))
  136. (p/catch #(notification/show! "The password is not matched." :warning true))
  137. (p/finally #(reset! loading false))))))))]
  138. [:div
  139. [:div.sm:flex.sm:items-start
  140. [:div.mt-3.text-center.sm:mt-0.sm:text-left
  141. [:h3#modal-headline.text-lg.leading-6.font-medium
  142. "Enter your password"]]]
  143. [:input.form-input.block.w-full.sm:text-sm.sm:leading-5.my-2
  144. {:type "password"
  145. :auto-focus true
  146. :on-change (fn [e]
  147. (reset! secret (util/evalue e)))
  148. :on-key-down (fn [e]
  149. (when (= (.-key e) "Enter")
  150. (on-click-fn)))}]
  151. [:div.mt-5.sm:mt-4.sm:flex.sm:flex-row-reverse
  152. [:span.flex.w-full.rounded-md.shadow-sm.sm:ml-3.sm:w-auto
  153. [: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
  154. {:type "button"
  155. :on-click on-click-fn}
  156. (if @loading (ui/loading "Decrypting") "Decrypt")]]]]))
  157. (defn encryption-input-secret-dialog
  158. [repo-url db-encrypted-secret close-fn]
  159. (fn [close-modal-fn]
  160. (let [close-fn (fn [encrypted?]
  161. (close-fn encrypted?)
  162. (close-modal-fn))]
  163. (encryption-input-secret-inner repo-url db-encrypted-secret close-fn))))