on master help sign in
Refactor
0517c2aiko
backend/BW/Logic.purs+105−1
@@ -4,16 +4,24 @@ import BW
4import BW.Types
4import BW.Types
5import Control.El
5import Control.El
6import Prelude
6import Prelude
7+import Bridge as Bridge
8+import Control.Monad.Error.Class (throwError)
7import Control.Promise (Promise)
9import Control.Promise (Promise)
8import Control.Promise as Promise
10import Control.Promise as Promise
9import Data.EncString as EncString
11import Data.EncString as EncString
10import Data.Function.Uncurried (runFn2, runFn3, runFn4)
12import Data.Function.Uncurried (runFn2, runFn3, runFn4)
11−import Data.JNullable (jnull, nullify)
13+import Data.JNullable (JNullable, fromJNullable, jnull, nullify)
14+import Data.JNullable as JNullable
15+import Data.JOpt (fromJOpt)
16+import Data.Maybe (Maybe(..))
17+import Data.Newtype (class Newtype, wrap)
12import Data.String as String
18import Data.String as String
13import Data.SymmetricCryptoKey (SymmetricCryptoKey)
19import Data.SymmetricCryptoKey (SymmetricCryptoKey)
14import Data.SymmetricCryptoKey as SymmetricCryptoKey
20import Data.SymmetricCryptoKey as SymmetricCryptoKey
21+import Data.Traversable (traverse)
15import Effect.Aff.Class (class MonadAff, liftAff)
22import Effect.Aff.Class (class MonadAff, liftAff)
16import Effect.Class (liftEffect)
23import Effect.Class (liftEffect)
24+import Effect.Exception (error)
17import Effect.Exception as Exc
25import Effect.Exception as Exc
18
26
19-- | Creates the master key
27-- | Creates the master key
@@ -97,3 +105,99 @@ decrypt input = do
97
105
98liftPromise ∷ forall m a. MonadAff m ⇒ Promise a → m a
106liftPromise ∷ forall m a. MonadAff m ⇒ Promise a → m a
99liftPromise = liftAff <<< Promise.toAff
107liftPromise = liftAff <<< Promise.toAff
108+
109+decodeCipher ::
110+ forall r.
111+ HasL "crypto" CryptoService r =>
112+ HasL "key" SymmetricCryptoKey r =>
113+ CipherResponse -> Al r Bridge.Sub_LoadCipher
114+decodeCipher cipher = do
115+ name <- decrypt cipher.name
116+ cipherType <- case cipher.type of
117+ n
118+ | cipherTypeSecureNote == n -> do
119+ note <- fromJNullable "" <$> (traverse decrypt cipher.notes)
120+ pure $ Bridge.NoteCipher note
121+ n
122+ | cipherTypeLogin == n -> do
123+ case JNullable.toMaybe cipher.login of
124+ Nothing -> throwError $ error "Login data is missing"
125+ Just login -> do
126+ username <- decryptNullable login.username
127+ password <- decryptNullable login.password
128+ uris <- fromJOpt [] <$> ((traverse >>> traverse) (_.uri >>> decrypt) login.uris)
129+ pure $ Bridge.LoginCipher
130+ $ Bridge.Cipher_LoginCipher
131+ { username
132+ , password
133+ , uris: wrap uris
134+ }
135+ n
136+ | cipherTypeCard == n -> do
137+ case JNullable.toMaybe cipher.card of
138+ Nothing -> throwError $ error "Card data is missing"
139+ Just card -> do
140+ cardholderName <- decryptNullable card.cardholderName
141+ number <- decryptNullable card.number
142+ code <- decryptNullable card.code
143+ brand <- decryptNullable card.brand
144+ expMonth <- decryptNullable card.expMonth
145+ expYear <- decryptNullable card.expYear
146+ pure $ Bridge.CardCipher
147+ $ Bridge.Cipher_CardCipher
148+ { brand, cardholderName, code, expMonth, expYear, number }
149+ n
150+ | cipherTypeIdentity == n -> do
151+ case JNullable.toMaybe cipher.identity of
152+ Nothing -> throwError $ error "Identity data is missing"
153+ Just identity -> do
154+ firstName <- decryptNullable identity.firstName
155+ middleName <- decryptNullable identity.middleName
156+ lastName <- decryptNullable identity.lastName
157+ address1 <- decryptNullable identity.address1
158+ address2 <- decryptNullable identity.address2
159+ address3 <- decryptNullable identity.address3
160+ city <- decryptNullable identity.city
161+ company <- decryptNullable identity.company
162+ country <- decryptNullable identity.country
163+ email <- decryptNullable identity.email
164+ licenseNumber <- decryptNullable identity.licenseNumber
165+ passportNumber <- decryptNullable identity.passportNumber
166+ phone <- decryptNullable identity.phone
167+ postalCode <- decryptNullable identity.postalCode
168+ ssn <- decryptNullable identity.ssn
169+ state <- decryptNullable identity.state
170+ title <- decryptNullable identity.title
171+ username <- decryptNullable identity.username
172+ pure $ Bridge.IdentityCipher
173+ $ wrap
174+ { firstName
175+ , middleName
176+ , lastName
177+ , address1
178+ , address2
179+ , address3
180+ , city
181+ , company
182+ , country
183+ , email
184+ , licenseNumber
185+ , passportNumber
186+ , phone
187+ , postalCode
188+ , ssn
189+ , state
190+ , title
191+ , username
192+ }
193+ n -> throwError $ error $ "Unsupported cipher type: " <> show n
194+ pure $ Bridge.Sub_LoadCipher { cipherType, name, id: cipher.id }
195+
196+decryptNullable ::
197+ forall x r.
198+ Newtype x (Maybe String) =>
199+ HasL "crypto" CryptoService r =>
200+ HasL "key" SymmetricCryptoKey r =>
201+ JNullable EncryptedString ->
202+ Al r x
203+decryptNullable x = wrap <<< fromJNullable Nothing <<< map Just <$> (traverse decrypt x)
backend/Main.purs+139−212
@@ -3,12 +3,11 @@ module Main
3 ) where
3 ) where
4
4
5import Prelude
5import Prelude
6−
7−import BW (ApiService, CryptoService, Hash, Services)
6+import BW (ApiService, CryptoService, Services)
8import BW as WB
7import BW as WB
9−import BW.Logic (decrypt, hashPassword, liftPromise)
8+import BW.Logic (decodeCipher, decrypt, hashPassword, liftPromise)
10import BW.Logic as Logic
9import BW.Logic as Logic
11−import BW.Types (CipherResponse, Email(..), IdentityTokenResponse, Password(..), PreloginResponse, SyncResponse, Urls, cipherTypeCard, cipherTypeIdentity, cipherTypeLogin, cipherTypeSecureNote)
10+import BW.Types (CipherResponse, Email(..), Password(..), PreloginResponse, Urls, cipherTypeCard, cipherTypeIdentity, cipherTypeLogin, cipherTypeSecureNote)
12import Bridge as Bridge
11import Bridge as Bridge
13import Control.El (class HasL, Al, L(..), l)
12import Control.El (class HasL, Al, L(..), l)
14import Control.Monad.Error.Class (throwError)
13import Control.Monad.Error.Class (throwError)
@@ -19,11 +18,8 @@ import Data.Clipboard as Clipboard
19import Data.DateTime (DateTime)
18import Data.DateTime (DateTime)
20import Data.Either (Either(..))
19import Data.Either (Either(..))
21import Data.HCaptcha (bindHCaptchToken)
20import Data.HCaptcha (bindHCaptchToken)
22−import Data.JNullable (fromJNullable, jnull, nullify)
21+import Data.JNullable (jnull, nullify)
23−import Data.JNullable as JNullable
24−import Data.JOpt (fromJOpt)
25import Data.Maybe (Maybe(..))
22import Data.Maybe (Maybe(..))
26−import Data.Newtype (wrap)
27import Data.OpenURL (openURL)
23import Data.OpenURL (openURL)
28import Data.Ord.Down (Down(..))
24import Data.Ord.Down (Down(..))
29import Data.SymmetricCryptoKey (SymmetricCryptoKey)
25import Data.SymmetricCryptoKey (SymmetricCryptoKey)
@@ -41,6 +37,7 @@ import FFI (Elm)
41import FFI as FFI
37import FFI as FFI
42import Localstorage (class StorageKey)
38import Localstorage (class StorageKey)
43import Localstorage as Storage
39import Localstorage as Storage
40+import Storage (MasterPasswordHashKey(..), PreloginResponseKey(..), SyncKey(..), TokenKey(..), UrlsKey(..))
44import Web.HTML as Html
41import Web.HTML as Html
45import Web.HTML.Window as Window
42import Web.HTML.Window as Window
46import Web.Storage.Storage (Storage)
43import Web.Storage.Storage (Storage)
@@ -53,218 +50,141 @@ main = do
53 services <- WB.getServices
50 services <- WB.getServices
54 masterKeyRef <- Ref.new Nothing
51 masterKeyRef <- Ref.new Nothing
55 bindHCaptchToken log
52 bindHCaptchToken log
53+ let
56− let run act = do
54+ run act = do
57− maybeKey <- Ref.read masterKeyRef
55+ maybeKey <- Ref.read masterKeyRef
58− runElmAff app $ flip runReaderT {app: app, crypto : services.crypto, storage: storage} $
56+ runElmAff app $ flip runReaderT { app: app, crypto: services.crypto, storage: storage }
59− case maybeKey of
57+ $ case maybeKey of
60− Just masterKey -> do
58+ Just masterKey -> do
61− token <- getOrReset storage TokenKey
59+ token <- getOrReset storage TokenKey
62− key <- Logic.makeDecryptionKey masterKey token.key
60+ key <- Logic.makeDecryptionKey masterKey token.key
63− liftEffect $ runElmAff app $ runReaderT act
61+ liftEffect $ runElmAff app
62+ $ runReaderT act
63+ { app: app
64+ , storage: storage
65+ , services: services
64− {app : app , storage : storage, services : services, crypto : services.crypto
66+ , crypto: services.crypto
65− , key : key}
67+ , key: key
68+ }
66− Nothing -> requestMasterPassword
69+ Nothing -> requestMasterPassword
67 Elm.subscribe app \cmd -> case cmd of
70 Elm.subscribe app \cmd -> case cmd of
68 Bridge.Login (Bridge.Cmd_Login { email, server, password }) -> do
71 Bridge.Login (Bridge.Cmd_Login { email, server, password }) -> do
69 let
72 let
70 urls = baseUrl server
73 urls = baseUrl server
71 runElmAff app do
74 runElmAff app do
72 unauthedApi <- liftPromise $ services.getApi urls jnull
75 unauthedApi <- liftPromise $ services.getApi urls jnull
76+ flip runReaderT
77+ { api: unauthedApi
73− flip runReaderT { api: unauthedApi, crypto: services.crypto, storage:
78+ , crypto: services.crypto
79+ , storage:
80+ storage
74− storage, cryptoFunctions: services.cryptoFunctions } do
81+ , cryptoFunctions: services.cryptoFunctions
82+ } do
75− {token, prelogin: (prelogin :: PreloginResponse)} <-
83+ { token, prelogin: (prelogin :: PreloginResponse) } <-
76− try (do
84+ try
85+ ( do
77− prelogin <- liftPromise $ unauthedApi.postPrelogin {email}
86+ prelogin <- liftPromise $ unauthedApi.postPrelogin { email }
78− token <- (Logic.getLogInRequestToken prelogin (Email email) (Password password))
87+ token <- (Logic.getLogInRequestToken prelogin (Email email) (Password password))
79− pure {token, prelogin}
88+ pure { token, prelogin }
80− )
89+ )
81− >>= \et -> case et of
90+ >>= \et -> case et of
82− Left _ -> liftEffect $ Exc.throw "Could not log in. Please check your data and try again."
91+ Left _ -> liftEffect $ Exc.throw "Could not log in. Please check your data and try again."
83− Right t -> pure t
92+ Right t -> pure t
84− masterKey <- Logic.makePreloginKey prelogin (Email email) (Password password)
93+ masterKey <- Logic.makePreloginKey prelogin (Email email) (Password password)
85− api <- liftPromise $ services.getApi urls (nullify token)
94+ api <- liftPromise $ services.getApi urls (nullify token)
86− sync <- liftPromise $ api.getSync unit
95+ sync <- liftPromise $ api.getSync unit
87− hash <- hashPassword (Password password)
96+ hash <- hashPassword (Password password)
88− log $ show sync
97+ log $ show sync
89− liftEffect $ Ref.write (Just masterKey) masterKeyRef
98+ liftEffect $ Ref.write (Just masterKey) masterKeyRef
90− liftEffect $ Storage.store storage UrlsKey server
99+ liftEffect $ Storage.store storage UrlsKey server
91− liftEffect $ Storage.store storage MasterPasswordHashKey hash
100+ liftEffect $ Storage.store storage MasterPasswordHashKey hash
92− liftEffect $ Storage.store storage PreloginResponseKey prelogin
101+ liftEffect $ Storage.store storage PreloginResponseKey prelogin
93− liftEffect $ Storage.store storage SyncKey sync
102+ liftEffect $ Storage.store storage SyncKey sync
94− liftEffect $ Storage.store storage TokenKey token
103+ liftEffect $ Storage.store storage TokenKey token
95− liftEffect $ Elm.send app Bridge.LoginSuccessful
104+ liftEffect $ Elm.send app Bridge.LoginSuccessful
105+ pure unit
96− pure unit
106+ pure unit
107+ Bridge.NeedCiphersList ->
108+ run do
109+ sync <- getOrReset storage SyncKey
110+ ciphers <- traverse processCipher sync.ciphers
111+ let
112+ sortedCiphers = Array.sortWith (_.date >>> Down) ciphers
113+ liftEffect $ Elm.send app $ Bridge.LoadCiphers $ Bridge.Sub_LoadCiphers_List $ map _.cipher sortedCiphers
114+ liftEffect $ Storage.store storage SyncKey sync
97 pure unit
115 pure unit
98− Bridge.NeedCiphersList -> run do
99− sync <- getOrReset storage SyncKey
100− ciphers <- traverse processCipher sync.ciphers
101− let sortedCiphers = Array.sortWith (_.date >>> Down) ciphers
102− liftEffect $ Elm.send app $ Bridge.LoadCiphers $ Bridge.Sub_LoadCiphers_List $ map _.cipher sortedCiphers
103− liftEffect $ Storage.store storage SyncKey sync
104− pure unit
105 Bridge.NeedsReset -> do
116 Bridge.NeedsReset -> do
106 WebStorage.clear storage
117 WebStorage.clear storage
107 Elm.send app Bridge.Reset
118 Elm.send app Bridge.Reset
108 pure unit
119 pure unit
109− Bridge.SendMasterPassword masterPassword -> runElmAff app $ do
120+ Bridge.SendMasterPassword masterPassword ->
121+ runElmAff app
122+ $ do
110− flip runReaderT {app: app, storage: storage, crypto: services.crypto, cryptoFunctions: services.cryptoFunctions} $ do
123+ flip runReaderT { app: app, storage: storage, crypto: services.crypto, cryptoFunctions: services.cryptoFunctions }
124+ $ do
111− hash <- getOrReset storage MasterPasswordHashKey
125+ hash <- getOrReset storage MasterPasswordHashKey
112− newHash <- hashPassword (Password masterPassword)
126+ newHash <- hashPassword (Password masterPassword)
113− if hash == newHash
127+ if hash == newHash then do
114− then do
115− sync <- getOrReset storage SyncKey
128+ sync <- getOrReset storage SyncKey
116− prelogin <- getOrReset storage PreloginResponseKey
129+ prelogin <- getOrReset storage PreloginResponseKey
117− masterKey <- Logic.makePreloginKey prelogin (Email sync.profile.email) (Password masterPassword)
130+ masterKey <- Logic.makePreloginKey prelogin (Email sync.profile.email) (Password masterPassword)
118− liftEffect $ Ref.write (Just masterKey) masterKeyRef
131+ liftEffect $ Ref.write (Just masterKey) masterKeyRef
119− liftEffect $ Elm.send app Bridge.LoginSuccessful
132+ liftEffect $ Elm.send app Bridge.LoginSuccessful
120− else do
133+ else do
121− liftEffect $ Elm.send app $ Bridge.Error "The password is wrong. Please try again."
134+ liftEffect $ Elm.send app $ Bridge.Error "The password is wrong. Please try again."
122− requestMasterPassword
135+ requestMasterPassword
123− pure unit
136+ pure unit
124 Bridge.Init -> do
137 Bridge.Init -> do
125− liftEffect $ Storage.get storage TokenKey >>= \x -> case x of
138+ liftEffect $ Storage.get storage TokenKey
139+ >>= \x -> case x of
126− Just _ -> do liftEffect $ Elm.send app Bridge.LoginSuccessful
140+ Just _ -> do liftEffect $ Elm.send app Bridge.LoginSuccessful
127− Nothing -> Elm.send app Bridge.NeedsLogin
141+ Nothing -> Elm.send app Bridge.NeedsLogin
128− Bridge.RequestCipher id -> run do
142+ Bridge.RequestCipher id ->
143+ run do
129− sync <- getOrReset storage SyncKey
144+ sync <- getOrReset storage SyncKey
130− case Array.find (\c -> c.id == id) sync.ciphers of
145+ case Array.find (\c -> c.id == id) sync.ciphers of
131− Nothing -> pure unit
146+ Nothing -> pure unit
132− Just cipher -> do
147+ Just cipherResponse -> do
133− name <- decrypt cipher.name
148+ cipher <- decodeCipher cipherResponse
134− cipherType <- do
135− case cipher.type of
136− n | cipherTypeSecureNote == n -> do
137− note <- fromJNullable "" <$> (traverse decrypt cipher.notes)
138− pure $ Bridge.NoteCipher note
139− n | cipherTypeLogin == n -> do
140− case JNullable.toMaybe cipher.login of
141− Nothing -> throwError $ error "Login data is missing"
142− Just login -> do
143− username <- wrap <<< fromJNullable Nothing <<< map Just <$> (traverse decrypt login.username)
144− password <- wrap <<< fromJNullable Nothing <<< map Just <$> (traverse decrypt login.password)
145− uris <- fromJOpt [] <$> ((traverse >>> traverse) (_.uri >>> decrypt) login.uris)
146− pure $ Bridge.LoginCipher $ Bridge.Cipher_LoginCipher
147− { username
148− , password
149− , uris: wrap uris
150− }
151− n | cipherTypeCard == n -> do
152− case JNullable.toMaybe cipher.card of
153− Nothing -> throwError $ error "Card data is missing"
154− Just card -> do
155− cardholderName <- wrap <<< fromJNullable Nothing <<< map Just <$> (traverse decrypt card.cardholderName)
156− number <- wrap <<< fromJNullable Nothing <<< map Just <$> (traverse decrypt card.number)
157− code <- wrap <<< fromJNullable Nothing <<< map Just <$> (traverse decrypt card.code)
158− brand <- wrap <<< fromJNullable Nothing <<< map Just <$> (traverse decrypt card.brand)
159− expMonth <- wrap <<< fromJNullable Nothing <<< map Just <$> (traverse decrypt card.expMonth)
160− expYear <- wrap <<< fromJNullable Nothing <<< map Just <$> (traverse decrypt card.expYear)
161− pure $ Bridge.CardCipher $ Bridge.Cipher_CardCipher
162− { brand, cardholderName, code, expMonth, expYear, number }
163− n | cipherTypeIdentity == n -> do
164− case JNullable.toMaybe cipher.identity of
165− Nothing -> throwError $ error "Identity data is missing"
166− Just identity -> do
167− firstName <- wrap <<< fromJNullable Nothing <<< map Just <$> (traverse decrypt identity.firstName)
168− middleName <- wrap <<< fromJNullable Nothing <<< map Just <$> (traverse decrypt identity.middleName)
169− lastName <- wrap <<< fromJNullable Nothing <<< map Just <$> (traverse decrypt identity.lastName)
170− address1 <- wrap <<< fromJNullable Nothing <<< map Just <$> (traverse decrypt identity.address1)
171− address2 <- wrap <<< fromJNullable Nothing <<< map Just <$> (traverse decrypt identity.address2)
172− address3 <- wrap <<< fromJNullable Nothing <<< map Just <$> (traverse decrypt identity.address3)
173− city <- wrap <<< fromJNullable Nothing <<< map Just <$> (traverse decrypt identity.city)
174− company <- wrap <<< fromJNullable Nothing <<< map Just <$> (traverse decrypt identity.company)
175− country <- wrap <<< fromJNullable Nothing <<< map Just <$> (traverse decrypt identity.country)
176− email <- wrap <<< fromJNullable Nothing <<< map Just <$> (traverse decrypt identity.email)
177− licenseNumber <- wrap <<< fromJNullable Nothing <<< map Just <$> (traverse decrypt identity.licenseNumber)
178− passportNumber <- wrap <<< fromJNullable Nothing <<< map Just <$> (traverse decrypt identity.passportNumber)
179− phone <- wrap <<< fromJNullable Nothing <<< map Just <$> (traverse decrypt identity.phone)
180− postalCode <- wrap <<< fromJNullable Nothing <<< map Just <$> (traverse decrypt identity.postalCode)
181− ssn <- wrap <<< fromJNullable Nothing <<< map Just <$> (traverse decrypt identity.ssn)
182− state <- wrap <<< fromJNullable Nothing <<< map Just <$> (traverse decrypt identity.state)
183− title <- wrap <<< fromJNullable Nothing <<< map Just <$> (traverse decrypt identity.title)
184− username <- wrap <<< fromJNullable Nothing <<< map Just <$> (traverse decrypt identity.username)
185− pure $ Bridge.IdentityCipher $ wrap
186− { firstName,
187− middleName,
188− lastName,
189− address1,
190− address2,
191− address3,
192− city,
193− company,
194− country,
195− email,
196− licenseNumber,
197− passportNumber,
198− phone,
199− postalCode,
200− ssn,
201− state,
202− title,
203− username
204− }
205− n -> throwError $ error $ "Unsupported cipher type: " <> show n
206− send $ Bridge.LoadCipher $ Bridge.Sub_LoadCipher {cipherType, name, id}
149+ send $ Bridge.LoadCipher cipher
207− pure unit
150+ pure unit
208− pure unit
151+ pure unit
209− Bridge.NeedEmail -> run do
152+ Bridge.NeedEmail ->
153+ run do
210− sync <- getOrReset storage SyncKey
154+ sync <- getOrReset storage SyncKey
211− send $ Bridge.RecieveEmail sync.profile.email
155+ send $ Bridge.RecieveEmail sync.profile.email
212− pure unit
156+ pure unit
213− Bridge.Copy text -> run do
157+ Bridge.Copy text ->
158+ run do
214− liftPromise $ Clipboard.clipboard text
159+ liftPromise $ Clipboard.clipboard text
215 Bridge.Open uri -> openURL uri
160 Bridge.Open uri -> openURL uri
216
161
217−data SyncKey = SyncKey
218−
219−instance StorageKey SyncKey SyncResponse where
220− storageKey SyncKey = "sync"
221−
222−data TokenKey
223− = TokenKey
224−
225−instance StorageKey TokenKey IdentityTokenResponse where
226− storageKey TokenKey = "token"
227−
228−data UrlsKey
229− = UrlsKey
230−
231−instance StorageKey UrlsKey String where
232− storageKey UrlsKey = "urls"
233−
234−data PreloginResponseKey
235− = PreloginResponseKey
236−
237−instance StorageKey PreloginResponseKey PreloginResponse where
238− storageKey PreloginResponseKey = "prelogin-reponse"
239−
240−data MasterPasswordHashKey
241− = MasterPasswordHashKey
242−
243−instance StorageKey MasterPasswordHashKey Hash where
244− storageKey MasterPasswordHashKey = "master-password-hash"
245−
246−
247processCipher ::
162processCipher ::
248 forall r.
163 forall r.
249 HasL "crypto" CryptoService r =>
164 HasL "crypto" CryptoService r =>
250 HasL "key" SymmetricCryptoKey r =>
165 HasL "key" SymmetricCryptoKey r =>
251− CipherResponse -> Al r {cipher :: Bridge.Sub_LoadCiphers, date :: DateTime}
166+ CipherResponse -> Al r { cipher :: Bridge.Sub_LoadCiphers, date :: DateTime }
252processCipher cipher = do
167processCipher cipher = do
253 name <- decrypt cipher.name
168 name <- decrypt cipher.name
254 cipherType <- case cipher.type of
169 cipherType <- case cipher.type of
170+ n
255− n | n == cipherTypeLogin -> pure Bridge.LoginType
171+ | n == cipherTypeLogin -> pure Bridge.LoginType
172+ n
256− n | n == cipherTypeSecureNote -> pure Bridge.NoteType
173+ | n == cipherTypeSecureNote -> pure Bridge.NoteType
174+ n
257− n | n == cipherTypeCard -> pure Bridge.CardType
175+ | n == cipherTypeCard -> pure Bridge.CardType
176+ n
258− n | n == cipherTypeIdentity -> pure Bridge.IdentityType
177+ | n == cipherTypeIdentity -> pure Bridge.IdentityType
259− n -> throwError $ error $ "Unsupported cipher type: " <> show n
178+ n -> throwError $ error $ "Unsupported cipher type: " <> show n
260 date <- liftEffect $ Timestamp.toDateTime cipher.revisionDate
179 date <- liftEffect $ Timestamp.toDateTime cipher.revisionDate
261 pure
180 pure
181+ $ { cipher:
262− $ {cipher: Bridge.Sub_LoadCiphers
182+ Bridge.Sub_LoadCiphers
263− { name: name
183+ { name: name
264− , date: Timestamp.toLocalDateTimeString cipher.revisionDate
184+ , date: Timestamp.toLocalDateTimeString cipher.revisionDate
265− , id: cipher.id
185+ , id: cipher.id
266− , cipherType
186+ , cipherType
267− }
187+ }
268 , date
188 , date
269 }
189 }
270
190
@@ -290,23 +210,30 @@ baseUrl server =
290 , keyConnector: jnull
210 , keyConnector: jnull
291 }
211 }
292
212
213+getOrReset ::
214+ forall k t r.
293−getOrReset :: forall k t r. HasL "app" Elm r =>
215+ HasL "app" Elm r =>
294 StorageKey k t => DecodeJson t => Storage -> k -> Al r t
216 StorageKey k t => DecodeJson t => Storage -> k -> Al r t
295getOrReset storage key = do
217getOrReset storage key = do
218+ let
296− let handleError = do
219+ handleError = do
297− app <- l (L :: L "app")
220+ app <- l (L :: L "app")
298− liftEffect $ WebStorage.clear storage
221+ liftEffect $ WebStorage.clear storage
299− liftEffect $ Elm.send app Bridge.Reset
222+ liftEffect $ Elm.send app Bridge.Reset
300− liftEffect $ Exc.throw "Encountered an internal error. Resetting the app."
223+ liftEffect $ Exc.throw "Encountered an internal error. Resetting the app."
301− try (liftEffect $ Storage.get storage key) >>= \x -> case x of
224+ try (liftEffect $ Storage.get storage key)
225+ >>= \x -> case x of
302− Left err -> do
226+ Left err -> do
303− liftEffect $ log $ show err
227+ liftEffect $ log $ show err
304− handleError
228+ handleError
305− Right Nothing -> handleError
229+ Right Nothing -> handleError
306− Right (Just y) -> pure y
230+ Right (Just y) -> pure y
307
231
232+getAuthedApi ::
233+ forall r.
308−getAuthedApi :: forall r. HasL "services" Services r => HasL "storage" Storage r
234+ HasL "services" Services r =>
235+ HasL "storage" Storage r =>
309− => HasL "app" Elm r =>
236+ HasL "app" Elm r =>
310 Al r ApiService
237 Al r ApiService
311getAuthedApi = do
238getAuthedApi = do
312 storage <- l (L :: L "storage")
239 storage <- l (L :: L "storage")
@@ -322,7 +249,7 @@ requestMasterPassword = do
322 url <- getOrReset storage UrlsKey
249 url <- getOrReset storage UrlsKey
323 sync <- getOrReset storage SyncKey
250 sync <- getOrReset storage SyncKey
324 liftEffect $ Elm.send app $ Bridge.NeedsMasterPassword
251 liftEffect $ Elm.send app $ Bridge.NeedsMasterPassword
325− $ Bridge.Sub_NeedsMasterPassword {server: url, login: sync.profile.email}
252+ $ Bridge.Sub_NeedsMasterPassword { server: url, login: sync.profile.email }
326
253
327send :: forall r. HasL "app" Elm r => Bridge.Sub -> Al r Unit
254send :: forall r. HasL "app" Elm r => Bridge.Sub -> Al r Unit
328send sub = do
255send sub = do
backend/Storage.pursnew file+37
@@ -0,0 +1,37 @@
1+module Storage where
2+
3+import Prelude
4+
5+import BW (Hash(..))
6+import BW.Types (IdentityTokenResponse, SyncResponse, PreloginResponse)
7+import Localstorage (class StorageKey)
8+
9+data SyncKey
10+ = SyncKey
11+
12+instance StorageKey SyncKey SyncResponse where
13+ storageKey SyncKey = "sync"
14+
15+data TokenKey
16+ = TokenKey
17+
18+instance StorageKey TokenKey IdentityTokenResponse where
19+ storageKey TokenKey = "token"
20+
21+data UrlsKey
22+ = UrlsKey
23+
24+instance StorageKey UrlsKey String where
25+ storageKey UrlsKey = "urls"
26+
27+data PreloginResponseKey
28+ = PreloginResponseKey
29+
30+instance StorageKey PreloginResponseKey PreloginResponse where
31+ storageKey PreloginResponseKey = "prelogin-reponse"
32+
33+data MasterPasswordHashKey
34+ = MasterPasswordHashKey
35+
36+instance StorageKey MasterPasswordHashKey Hash where
37+ storageKey MasterPasswordHashKey = "master-password-hash"