on master help sign in
Got sync working
fbd470biko
TODO.mdnew file+1
@@ -0,0 +1 @@
1+- alternarive login paths
backend/Bridge.purs+6−2
@@ -27,8 +27,9 @@ derive instance genericCmd_Login :: Generic Cmd_Login _
27derive instance eqCmd_Login :: Eq Cmd_Login
27derive instance eqCmd_Login :: Eq Cmd_Login
28derive instance ordCmd_Login :: Ord Cmd_Login
28derive instance ordCmd_Login :: Ord Cmd_Login
29
29
30−newtype Cmd =
30+data Cmd =
31+ Init
31− Login Cmd_Login
32+ | Login Cmd_Login
32
33
33instance encodeJsonCmd :: EncodeJson Cmd where
34instance encodeJsonCmd :: EncodeJson Cmd where
34 encodeJson = genericEncodeAeson Argonaut.defaultOptions
35 encodeJson = genericEncodeAeson Argonaut.defaultOptions
@@ -54,8 +55,10 @@ derive instance ordSub_Hello :: Ord Sub_Hello
54
55
55data Sub =
56data Sub =
56 Empty
57 Empty
58+ | Error String
57 | GotEverything String
59 | GotEverything String
58 | Hello Sub_Hello
60 | Hello Sub_Hello
61+ | NeedsLogin
59
62
60instance encodeJsonSub :: EncodeJson Sub where
63instance encodeJsonSub :: EncodeJson Sub where
61 encodeJson = genericEncodeAeson Argonaut.defaultOptions
64 encodeJson = genericEncodeAeson Argonaut.defaultOptions
@@ -64,3 +67,4 @@ instance decodeJsonSub :: DecodeJson Sub where
64derive instance genericSub :: Generic Sub _
67derive instance genericSub :: Generic Sub _
65derive instance eqSub :: Eq Sub
68derive instance eqSub :: Eq Sub
66derive instance ordSub :: Ord Sub
69derive instance ordSub :: Ord Sub
70+
backend/Elm.purs+18−8
@@ -1,24 +1,34 @@
1module Elm
1module Elm
2 ( send
2 ( send
3 , subscribe
3 , subscribe
4− )
5− where
4+ ) where
6
5
7import Data.Either
6import Data.Either
8import Prelude
7import Prelude
9−
10import Bridge as Bridge
8import Bridge as Bridge
11import Data.Argonaut (decodeJson, encodeJson, stringify)
9import Data.Argonaut (decodeJson, encodeJson, stringify)
12import Effect (Effect)
10import Effect (Effect)
13import Effect.Class.Console (log)
11import Effect.Class.Console (log)
14import FFI as FFI
12import FFI as FFI
13+import Effect.Exception (try)
15
14
16send ∷ FFI.Elm → Bridge.Sub → Effect Unit
15send ∷ FFI.Elm → Bridge.Sub → Effect Unit
17send elm a = FFI.sendElmEvent elm "bridgeSub" (encodeJson a)
16send elm a = FFI.sendElmEvent elm "bridgeSub" (encodeJson a)
18
17
19subscribe :: FFI.Elm → (Bridge.Cmd → Effect Unit) -> Effect Unit
18subscribe :: FFI.Elm → (Bridge.Cmd → Effect Unit) -> Effect Unit
19+subscribe elm f =
20−subscribe elm f = FFI.createElmSubscription elm "bridgeCmd" $ \a → do
20+ FFI.createElmSubscription elm "bridgeCmd"
21+ $ \a → do
21− log $ stringify a
22+ log $ stringify a
22− case decodeJson a of
23+ result <- case decodeJson a of
23− Right (cmd :: Bridge.Cmd) -> f cmd
24+ Right (cmd :: Bridge.Cmd) -> do
25+ result <- try $ f cmd
26+ case result of
24− Left err -> log $ show err
27+ Left err -> pure $ Left $ show err
28+ Right _ -> pure $ Right unit
29+ Left err -> pure $ Left $ show err
30+ case result of
31+ Left err -> do
32+ log err
33+ send elm $ Bridge.Error err
34+ Right _ -> pure unit
backend/FFI.purs+1−3
@@ -3,11 +3,9 @@ module FFI
3 , createElmSubscription
3 , createElmSubscription
4 , sendElmEvent
4 , sendElmEvent
5 , startElm
5 , startElm
6− )
7− where
6+ ) where
8
7
9import Prelude
8import Prelude
10−
11import Data.Argonaut (Json)
9import Data.Argonaut (Json)
12import Effect (Effect)
10import Effect (Effect)
13
11
backend/Main.purs+69−55
@@ -1,77 +1,91 @@
1−module Main where
1+module Main
2+ ( main
3+ ) where
2
4
3import Prelude
5import Prelude
4−
5import BW as WB
6import BW as WB
7+import BW.Logic (decrypt)
6import BW.Logic as Logic
8import BW.Logic as Logic
7−import BW.Types (Email(..), Password(..))
9+import BW.Types (Email(..), EncryptedString(..), Password(..), Urls)
8−import Bridge (Sub(..))
9import Bridge as Bridge
10import Bridge as Bridge
10import Control.Monad.Reader (runReaderT)
11import Control.Monad.Reader (runReaderT)
12+import Control.Promise (Promise)
11import Control.Promise as Promise
13import Control.Promise as Promise
14+import Data.Argonaut (class EncodeJson, encodeJson)
15+import Data.Argonaut as Json
12import Data.Either (Either(..))
16import Data.Either (Either(..))
13−import Data.Interval (second)
14import Data.Maybe (Maybe(..))
17import Data.Maybe (Maybe(..))
15−import Data.Nullable (null, toNullable)
18+import Data.Nullable (Nullable, null, toNullable)
19+import Data.Nullable as Nullable
20+import Data.Traversable (traverse)
16import Effect (Effect)
21import Effect (Effect)
17−import Effect.Aff (runAff_)
22+import Effect.Aff (Aff, runAff_, try)
23+import Effect.Aff as Aff
24+import Effect.Aff.Class (class MonadAff, liftAff)
18import Effect.Aff.Class as Aff
25import Effect.Aff.Class as Aff
19import Effect.Class (liftEffect)
26import Effect.Class (liftEffect)
20−import Effect.Console (log)
27+import Effect.Class.Console (log)
28+import Effect.Exception as Exc
21import Elm as Elm
29import Elm as Elm
22import FFI as FFI
30import FFI as FFI
23−
31+import Prelude as Array
32+import Unsafe.Coerce (unsafeCoerce)
24
33
25main :: Effect Unit
34main :: Effect Unit
26main = do
35main = do
27 app <- FFI.startElm "elm"
36 app <- FFI.startElm "elm"
28− let
37+ services <- WB.getServices
29− processResult res = do
38+ Elm.subscribe app \cmd -> case cmd of
30− log res
31− Elm.send app (GotEverything res)
32− Elm.subscribe app \(Bridge.Login (Bridge.Cmd_Login {email, server, password})) -> do
39+ Bridge.Login (Bridge.Cmd_Login { email, server, password }) -> do
33− log $ "Got cmd"
40+ let
34− services <- WB.getServices
41+ urls = baseUrl server
42+ runElmAff app do
43+ unauthedApi <- liftPromise $ services.getApi urls null
44+ { masterKey, token } <-
45+ flip runReaderT { api: unauthedApi, crypto: services.crypto } do
46+ token <-
47+ try (Logic.getLogInRequestToken (Email email) (Password password))
35− runAff_ (\res -> case res of
48+ >>= \et -> case et of
36− Left e -> processResult $ show e
49+ Left _ -> liftEffect $ Exc.throw "Could not log in. Please check your data and try again."
37− Right x -> processResult $ show x
50+ Right t -> pure t
38− ) do
51+ masterKey <- Logic.makePreloginKey (Email email) (Password password)
39− unauthedApi <- Aff.liftAff $ Promise.toAff $ services.getApi
52+ pure { token, masterKey }
40− { base : toNullable $ Just server,
53+ api <- liftPromise $ services.getApi urls (nullify token)
41− webVault : null,
54+ _ <-
42− api : null,
55+ flip runReaderT { api: api, crypto: services.crypto } do
43− identity : null,
56+ sync <- liftPromise $ api.getSync unit
44− icons : null,
57+ key <- Logic.makeDecryptionKey masterKey sync.profile.key
45− notifications : null,
58+ log $ show key.keyB64
46− events : null,
59+ ciphersNames <- traverse (decrypt key) $ map _.name sync.ciphers
47− keyConnector : null
60+ liftEffect $ Elm.send app $ Bridge.Hello $ Bridge.Sub_Hello { first: "hello", second: show ciphersNames }
48− }
61+ pure unit
49− null
62+ pure unit
50−
63+ Bridge.Init -> Elm.send app (Bridge.NeedsLogin)
51− runReaderT (do
52− tokens <- Logic.getLogInRequestToken (Email email) (Password password)
53−
54− api <- Aff.liftAff $ Promise.toAff $ services.getApi
55− { base : toNullable $ Just server,
56− webVault : null,
57− api : null,
58− identity : null,
59− icons : null,
60− notifications : null,
61− events : null,
62− keyConnector : null
63− }
64− (toNullable $ Just tokens)
65−
66
64
65+liftPromise ∷ forall m a. MonadAff m ⇒ Promise a → m a
67− Aff.liftAff $ Promise.toAff $ api.getSync unit
66+liftPromise = Aff.liftAff <<< Promise.toAff
68− ) {
69− api: unauthedApi,
70− crypto : services.crypto
71− }
72
67
73− Elm.send app (Bridge.Hello (Bridge.Sub_Hello {first: "f", second: "u"}))
68+runElmAff :: FFI.Elm -> Aff Unit -> Effect Unit
69+runElmAff app =
70+ runAff_
71+ $ \res -> do
72+ case res of
73+ Left e -> do
74+ log $ show e
75+ Elm.send app (Bridge.Error $ Exc.message e)
76+ Right unit -> pure unit
74
77
75− Elm.send app (Bridge.Empty)
78+baseUrl :: String -> Urls
79+baseUrl server =
80+ { base: nullify server
81+ , webVault: null
82+ , api: null
83+ , identity: null
84+ , icons: null
85+ , notifications: null
86+ , events: null
87+ , keyConnector: null
88+ }
76
89
77− log "🍝"
90+nullify :: forall a. a -> Nullable a
91+nullify = Just >>> toNullable
bridge.yaml+3−1
@@ -2,11 +2,13 @@ Sub:
2- Hello:
2- Hello:
3 first: String
3 first: String
4 second: String
4 second: String
5−
6- GotEverything: String
5- GotEverything: String
7- Empty
6- Empty
7+- NeedsLogin
8+- Error: String
8
9
9Cmd:
10Cmd:
11+- Init
10- Login:
12- Login:
11 email: String
13 email: String
12 server: String
14 server: String
bw/src/BW.purs+19−19
@@ -2,33 +2,33 @@ module BW where
2
2
3import BW.Types
3import BW.Types
4import Prelude
4import Prelude
5−
6import Control.Promise (Promise)
5import Control.Promise (Promise)
7import Data.ArrayBuffer.Types (ArrayBuffer)
6import Data.ArrayBuffer.Types (ArrayBuffer)
8import Data.EncString (EncString)
7import Data.EncString (EncString)
9import Data.Function.Uncurried (Fn2, Fn3, Fn4)
8import Data.Function.Uncurried (Fn2, Fn3, Fn4)
10import Data.Nullable (Nullable)
9import Data.Nullable (Nullable)
10+import Data.SymmetricCryptoKey (SymmetricCryptoKey)
11import Effect (Effect)
11import Effect (Effect)
12
12
13−type CryptoService = {
13+type CryptoService
14− makeKey :: Fn4 Password String KDF Int (Promise SymmetricCryptoKey),
14+ = { makeKey :: Fn4 Password String KDF Int (Promise SymmetricCryptoKey)
15− hashPassword :: Fn3 Password SymmetricCryptoKey (Nullable HashPurpose) (Promise Hash),
15+ , hashPassword :: Fn3 Password SymmetricCryptoKey (Nullable HashPurpose) (Promise Hash)
16− decryptToUtf8 :: Fn2 EncString SymmetricCryptoKey (Promise String),
16+ , decryptToUtf8 :: Fn2 EncString SymmetricCryptoKey (Promise String)
17− decryptToBytes :: Fn2 EncString SymmetricCryptoKey (Promise ArrayBuffer)
17+ , decryptToBytes :: Fn2 EncString SymmetricCryptoKey (Promise ArrayBuffer)
18+ , stretchKey :: SymmetricCryptoKey -> Promise SymmetricCryptoKey
18−}
19+ }
19−
20−type ApiService = {
21− postPrelogin :: PreloginRequest -> Promise PreloginResponse,
22− getProfile :: Unit -> Promise ProfileResponse,
23
20
21+type ApiService
22+ = { postPrelogin :: PreloginRequest -> Promise PreloginResponse
23+ , getProfile :: Unit -> Promise ProfileResponse
24− -- TODO: There can really be more request and response types. Maybe handle this later.
24+ -- TODO: There can really be more request and response types. Maybe handle this later.
25− postIdentityToken :: PasswordTokenRequest -> Promise IdentityTokenResponse,
25+ , postIdentityToken :: PasswordTokenRequest -> Promise IdentityTokenResponse
26− getSync :: Unit -> Promise SyncResponse
26+ , getSync :: Unit -> Promise SyncResponse
27−}
27+ }
28
28
29−type Services = {
29+type Services
30− crypto :: CryptoService,
30+ = { crypto :: CryptoService
31− getApi :: Urls -> Nullable IdentityTokenResponse -> Promise ApiService
31+ , getApi :: Urls -> Nullable IdentityTokenResponse -> Promise ApiService
32−}
32+ }
33
33
34foreign import getServices :: Effect Services
34foreign import getServices :: Effect Services
bw/src/BW/Logic.purs+63−25
@@ -4,13 +4,20 @@ import BW
4import BW.Types
4import BW.Types
5import Control.El
5import Control.El
6import Prelude
6import Prelude
7−
7+import Control.Monad.Error.Class (throwError)
8import Control.Promise as Promise
8import Control.Promise as Promise
9+import Data.EncString as EncString
9−import Data.Function.Uncurried (runFn3, runFn4)
10+import Data.Function.Uncurried (runFn2, runFn3, runFn4)
10import Data.Maybe (Maybe(..))
11import Data.Maybe (Maybe(..))
11import Data.Nullable (null, toNullable)
12import Data.Nullable (null, toNullable)
12import Data.String as String
13import Data.String as String
14+import Data.SymmetricCryptoKey (SymmetricCryptoKey)
15+import Data.SymmetricCryptoKey as SymmetricCryptoKey
13import Effect.Aff.Class (liftAff)
16import Effect.Aff.Class (liftAff)
17+import Effect.Class (liftEffect)
18+import Effect.Class.Console (log)
19+import Effect.Exception (error)
20+import Effect.Exception as Exc
14
21
15makePreloginKey ::
22makePreloginKey ::
16 forall r.
23 forall r.
@@ -20,11 +27,31 @@ makePreloginKey ::
20makePreloginKey (Email email') (Password password) = do
27makePreloginKey (Email email') (Password password) = do
21 api :: ApiService <- l (L :: L "api")
28 api :: ApiService <- l (L :: L "api")
22 crypto <- l (L :: L "crypto")
29 crypto <- l (L :: L "crypto")
30+ let
31+ email = (String.trim >>> String.toLower) email'
32+ { kdf, kdfIterations } <- liftPromise $ api.postPrelogin { email }
33+ liftPromise $ runFn4 crypto.makeKey (Password password) email kdf kdfIterations
23
34
24− let email = (String.trim >>> String.toLower) email'
35+makeDecryptionKey ::
36+ forall r.
37+ HasL "crypto" CryptoService r =>
38+ -- | Matser key (prelogin key)
39+ SymmetricCryptoKey -> EncryptedString -> Al r SymmetricCryptoKey
40+makeDecryptionKey masterKey str = do
41+ crypto <- l (L :: L "crypto")
42+ let
43+ encryptedKeyString = EncString.fromString str
25
44
26− {kdf, kdfIterations} <- liftAff $ Promise.toAff $ api.postPrelogin {email}
45+ encType = EncString.encryptionType encryptedKeyString
27− liftAff $ Promise.toAff $ runFn4 crypto.makeKey (Password password) email kdf kdfIterations
46+ key <-
47+ if encType == EncString.encryptionTypeAesCbc256_B64 then
48+ liftPromise $ runFn2 crypto.decryptToBytes (EncString.fromString str) masterKey
49+ else if encType == EncString.encryptionTypeAesCbc256_HmacSha256_B64 then do
50+ stretchedMasterKey <- liftPromise $ crypto.stretchKey masterKey
51+ liftPromise $ runFn2 crypto.decryptToBytes (EncString.fromString str) stretchedMasterKey
52+ else
53+ liftEffect $ Exc.throw $ "Unsupported key encryption type: " <> show encType
54+ pure $ SymmetricCryptoKey.fromArrayBuffer key
28
55
29getLogInRequestToken ::
56getLogInRequestToken ::
30 forall r.
57 forall r.
@@ -35,25 +62,36 @@ getLogInRequestToken email password = do
35 key <- makePreloginKey email password
62 key <- makePreloginKey email password
36 crypto <- l (L :: L "crypto")
63 crypto <- l (L :: L "crypto")
37 api :: ApiService <- l (L :: L "api")
64 api :: ApiService <- l (L :: L "api")
65+ _localHashedPassword <-
66+ liftPromise
67+ $ runFn3 crypto.hashPassword password key (toNullable $ Just hashPurposeLocalAuthorization)
68+ Hash hashedPassword <- liftPromise $ runFn3 crypto.hashPassword password key null
69+ liftPromise
70+ $ api.postIdentityToken
71+ { email: email
72+ , masterPasswordHash: hashedPassword
73+ , captchaResponse: ""
74+ , twoFactor:
75+ { provider: twoFactorProviderTypeEmail
76+ , token: ""
77+ , remember: false
78+ }
79+ , device:
80+ { type: deviceTypeUnknownBrowser
81+ , name: "Temporary device name"
82+ , identifier: "42"
83+ , pushToken: null
84+ }
85+ }
38
86
39− _localHashedPassword <- liftAff $ Promise.toAff $
87+decrypt ::
40− runFn3 crypto.hashPassword password key (toNullable $ Just hashPurposeLocalAuthorization)
88+ forall r.
41−
89+ HasL "crypto" CryptoService r =>
42− Hash hashedPassword <- liftAff $ Promise.toAff $ runFn3 crypto.hashPassword password key null
90+ SymmetricCryptoKey ->
91+ EncryptedString ->
92+ Al r String
93+decrypt key input = do
94+ crypto <- l (L :: L "crypto")
95+ liftPromise $ runFn2 crypto.decryptToUtf8 (EncString.fromString input) key
43
96
44− liftAff $ Promise.toAff $ api.postIdentityToken {
97+liftPromise = liftAff <<< Promise.toAff
45− email: email,
46− masterPasswordHash: hashedPassword,
47− captchaResponse: "",
48− twoFactor: {
49− provider: twoFactorProviderTypeEmail,
50− token: "",
51− remember: false
52− },
53− device: {
54− type: deviceTypeUnknownBrowser,
55− name: "Temporary device name",
56− identifier: "42",
57− pushToken: null
58− }
59− }
bw/src/BW/Types.purs+515−397
@@ -1,530 +1,648 @@
1module BW.Types where
1module BW.Types where
2
2
3import Prelude
3import Prelude
4−
5import Data.ArrayBuffer.Types (ArrayBuffer)
4import Data.ArrayBuffer.Types (ArrayBuffer)
6import Data.Date (Date)
5import Data.Date (Date)
7import Data.Nullable (Nullable)
6import Data.Nullable (Nullable)
8import Data.ShowableJson (ShowableJson)
7import Data.ShowableJson (ShowableJson)
9import Data.Undefined.NoProblem (Opt)
8import Data.Undefined.NoProblem (Opt)
10
9
10+newtype EncryptedString
11+ = EncryptedString String
12+
13+derive newtype instance Show EncryptedString
14+
11kdfPBKDF2_SHA256 = 0 :: KDF
15kdfPBKDF2_SHA256 = 0 :: KDF
16+
12−type KDF = Int
17+type KDF
13−
18+ = Int
19+
14−type PreloginResponse = {
20+type PreloginResponse
15− kdf :: KDF,
21+ = { kdf :: KDF
16− kdfIterations :: Int
22+ , kdfIterations :: Int
17−}
23+ }
18−
24+
19−type PreloginRequest = {
25+type PreloginRequest
20− email :: String
26+ = { email :: String
21−}
27+ }
22−
28+
23−type ProfileResponse = {
29+type ProfileResponse
24− id :: String,
30+ = { id :: String
25− name :: String,
31+ , name :: String
26− email :: String,
32+ , email :: String
27− emailVerified :: Boolean,
33+ , emailVerified :: Boolean
28− masterPasswordHint :: String,
34+ , masterPasswordHint :: String
29− premiumPersonally :: Boolean,
35+ , premiumPersonally :: Boolean
30− premiumFromOrganization :: Boolean,
36+ , premiumFromOrganization :: Boolean
31− culture :: String,
37+ , culture :: String
32− twoFactorEnabled :: Boolean,
38+ , twoFactorEnabled :: Boolean
33− key :: String,
39+ , key :: EncryptedString
34− privateKey :: String,
40+ , privateKey :: EncryptedString
35− securityStamp :: String,
41+ , securityStamp :: String
36− forcePasswordReset :: Boolean,
42+ , forcePasswordReset :: Boolean
37− usesKeyConnector :: Boolean,
43+ , usesKeyConnector :: Boolean
38− organizations :: Array ProfileOrganizationResponse,
44+ , organizations :: Array ProfileOrganizationResponse
39− providers :: Array ProfileProviderResponse,
45+ , providers :: Array ProfileProviderResponse
40− providerOrganizations :: Array ProfileProviderOrganizationResponse
46+ , providerOrganizations :: Array ProfileProviderOrganizationResponse
41−}
47+ }
42−
48+
43−type ProfileOrganizationResponse = {
49+type ProfileOrganizationResponse
44− id :: String,
50+ = { id :: String
45− name :: String,
51+ , name :: String
46− usePolicies :: Boolean,
52+ , usePolicies :: Boolean
47− useGroups :: Boolean,
53+ , useGroups :: Boolean
48− useDirectory :: Boolean,
54+ , useDirectory :: Boolean
49− useEvents :: Boolean,
55+ , useEvents :: Boolean
50− useTotp :: Boolean,
56+ , useTotp :: Boolean
51− use2fa :: Boolean,
57+ , use2fa :: Boolean
52− useApi :: Boolean,
58+ , useApi :: Boolean
53− useSso :: Boolean,
59+ , useSso :: Boolean
54− useKeyConnector :: Boolean,
60+ , useKeyConnector :: Boolean
55− useResetPassword :: Boolean,
61+ , useResetPassword :: Boolean
56− selfHost :: Boolean,
62+ , selfHost :: Boolean
57− usersGetPremium :: Boolean,
63+ , usersGetPremium :: Boolean
58− seats :: Int,
64+ , seats :: Int
59− maxCollections :: Int,
65+ , maxCollections :: Int
60− maxStorageGb :: Nullable Int,
66+ , maxStorageGb :: Nullable Int
61− key :: String,
67+ , key :: String
62− hasPublicAndPrivateKeys :: Boolean,
68+ , hasPublicAndPrivateKeys :: Boolean
63− status :: OrganizationUserStatusType,
69+ , status :: OrganizationUserStatusType
64− type :: OrganizationUserType,
70+ , type :: OrganizationUserType
65− enabled :: Boolean,
71+ , enabled :: Boolean
66− ssoBound :: Boolean,
72+ , ssoBound :: Boolean
67− identifier :: String,
73+ , identifier :: String
68− permissions :: PermissionsApi,
74+ , permissions :: PermissionsApi
69− resetPasswordEnrolled :: Boolean,
75+ , resetPasswordEnrolled :: Boolean
70− userId :: String,
76+ , userId :: String
71− providerId :: String,
77+ , providerId :: String
72− providerName :: String,
78+ , providerName :: String
73− familySponsorshipFriendlyName :: String,
79+ , familySponsorshipFriendlyName :: String
74− familySponsorshipAvailable :: Boolean,
80+ , familySponsorshipAvailable :: Boolean
75− planProductType :: ProductType,
81+ , planProductType :: ProductType
76− keyConnectorEnabled :: Boolean,
82+ , keyConnectorEnabled :: Boolean
77− keyConnectorUrl :: String,
83+ , keyConnectorUrl :: String
78− familySponsorshipLastSyncDate :: Nullable Date,
84+ , familySponsorshipLastSyncDate :: Nullable Date
79− familySponsorshipValidUntil :: Nullable Date,
85+ , familySponsorshipValidUntil :: Nullable Date
80− familySponsorshipToDelete :: Nullable Boolean
86+ , familySponsorshipToDelete :: Nullable Boolean
81− }
87+ }
82
88
83organizationUserStatusTypeInvited = 0 :: OrganizationUserStatusType
89organizationUserStatusTypeInvited = 0 :: OrganizationUserStatusType
90+
84organizationUserStatusTypeAccepted = 0 :: OrganizationUserStatusType
91organizationUserStatusTypeAccepted = 0 :: OrganizationUserStatusType
92+
85organizationUserStatusTypeConfirmed = 0 :: OrganizationUserStatusType
93organizationUserStatusTypeConfirmed = 0 :: OrganizationUserStatusType
94+
86−type OrganizationUserStatusType = Int
95+type OrganizationUserStatusType
96+ = Int
87
97
88organizationUserTypeOwner = 0 :: OrganizationUserType
98organizationUserTypeOwner = 0 :: OrganizationUserType
99+
89organizationUserTypeAdmin = 1 :: OrganizationUserType
100organizationUserTypeAdmin = 1 :: OrganizationUserType
101+
90organizationUserTypeUser = 2 :: OrganizationUserType
102organizationUserTypeUser = 2 :: OrganizationUserType
103+
91organizationUserTypeManager = 3 :: OrganizationUserType
104organizationUserTypeManager = 3 :: OrganizationUserType
105+
92organizationUserTypeCustom = 4 :: OrganizationUserType
106organizationUserTypeCustom = 4 :: OrganizationUserType
107+
93−type OrganizationUserType = Int
108+type OrganizationUserType
94−
109+ = Int
110+
95−type PermissionsApi = {
111+type PermissionsApi
96− accessEventLogs :: Boolean,
112+ = { accessEventLogs :: Boolean
97− accessImportExport :: Boolean,
113+ , accessImportExport :: Boolean
98− accessReports :: Boolean,
114+ , accessReports :: Boolean
99− manageAllCollections :: Boolean,
115+ , manageAllCollections :: Boolean
100− createNewCollections :: Boolean,
116+ , createNewCollections :: Boolean
101− editAnyCollection :: Boolean,
117+ , editAnyCollection :: Boolean
102− deleteAnyCollection :: Boolean,
118+ , deleteAnyCollection :: Boolean
103− manageAssignedCollections :: Boolean,
119+ , manageAssignedCollections :: Boolean
104− editAssignedCollections :: Boolean,
120+ , editAssignedCollections :: Boolean
105− deleteAssignedCollections :: Boolean,
121+ , deleteAssignedCollections :: Boolean
106− manageCiphers :: Boolean,
122+ , manageCiphers :: Boolean
107− manageGroups :: Boolean,
123+ , manageGroups :: Boolean
108− manageSso :: Boolean,
124+ , manageSso :: Boolean
109− managePolicies :: Boolean,
125+ , managePolicies :: Boolean
110− manageUsers :: Boolean,
126+ , manageUsers :: Boolean
111− manageResetPassword :: Boolean
127+ , manageResetPassword :: Boolean
112−}
128+ }
113
129
114productTypeFree = 0 :: ProductType
130productTypeFree = 0 :: ProductType
131+
115productTypeFamilies = 1 :: ProductType
132productTypeFamilies = 1 :: ProductType
133+
116productTypeTeams = 2 :: ProductType
134productTypeTeams = 2 :: ProductType
135+
117productTypeEnterprise = 3 :: ProductType
136productTypeEnterprise = 3 :: ProductType
137+
118−type ProductType = Int
138+type ProductType
119−
139+ = Int
140+
120−type ProfileProviderResponse = {
141+type ProfileProviderResponse
121− id :: String,
142+ = { id :: String
122− name :: String,
143+ , name :: String
123− key :: String,
144+ , key :: String
124− status :: ProviderUserStatusType,
145+ , status :: ProviderUserStatusType
125− type :: ProviderUserType,
146+ , type :: ProviderUserType
126− enabled :: Boolean,
147+ , enabled :: Boolean
127− permissions :: PermissionsApi,
148+ , permissions :: PermissionsApi
128− userId :: String,
149+ , userId :: String
129− useEvents :: Boolean
150+ , useEvents :: Boolean
130−}
151+ }
131
152
132providerUserStatusTypeInvited = 0 :: ProviderUserStatusType
153providerUserStatusTypeInvited = 0 :: ProviderUserStatusType
154+
133providerUserStatusTypeAccepted = 1 :: ProviderUserStatusType
155providerUserStatusTypeAccepted = 1 :: ProviderUserStatusType
156+
134providerUserStatusTypeConfirmed = 2 :: ProviderUserStatusType
157providerUserStatusTypeConfirmed = 2 :: ProviderUserStatusType
158+
135−type ProviderUserStatusType = Int
159+type ProviderUserStatusType
160+ = Int
136
161
137providerUserTypeProviderAdmin = 0 :: ProviderUserType
162providerUserTypeProviderAdmin = 0 :: ProviderUserType
163+
138providerUserTypeServiceUser = 1 :: ProviderUserType
164providerUserTypeServiceUser = 1 :: ProviderUserType
165+
139−type ProviderUserType = Int
166+type ProviderUserType
140−
167+ = Int
168+
141−type ProfileProviderOrganizationResponse = {
169+type ProfileProviderOrganizationResponse
142− id :: String,
170+ = { id :: String
143− name :: String,
171+ , name :: String
144− usePolicies :: Boolean,
172+ , usePolicies :: Boolean
145− useGroups :: Boolean,
173+ , useGroups :: Boolean
146− useDirectory :: Boolean,
174+ , useDirectory :: Boolean
147− useEvents :: Boolean,
175+ , useEvents :: Boolean
148− useTotp :: Boolean,
176+ , useTotp :: Boolean
149− use2fa :: Boolean,
177+ , use2fa :: Boolean
150− useApi :: Boolean,
178+ , useApi :: Boolean
151− useSso :: Boolean,
179+ , useSso :: Boolean
152− useKeyConnector :: Boolean,
180+ , useKeyConnector :: Boolean
153− useResetPassword :: Boolean,
181+ , useResetPassword :: Boolean
154− selfHost :: Boolean,
182+ , selfHost :: Boolean
155− usersGetPremium :: Boolean,
183+ , usersGetPremium :: Boolean
156− seats :: Int,
184+ , seats :: Int
157− maxCollections :: Int,
185+ , maxCollections :: Int
158− maxStorageGb :: Nullable Int,
186+ , maxStorageGb :: Nullable Int
159− key :: String,
187+ , key :: String
160− hasPublicAndPrivateKeys :: Boolean,
188+ , hasPublicAndPrivateKeys :: Boolean
161− status :: OrganizationUserStatusType,
189+ , status :: OrganizationUserStatusType
162− type :: OrganizationUserType,
190+ , type :: OrganizationUserType
163− enabled :: Boolean,
191+ , enabled :: Boolean
164− ssoBound :: Boolean,
192+ , ssoBound :: Boolean
165− identifier :: String,
193+ , identifier :: String
166− permissions :: PermissionsApi,
194+ , permissions :: PermissionsApi
167− resetPasswordEnrolled :: Boolean,
195+ , resetPasswordEnrolled :: Boolean
168− userId :: String,
196+ , userId :: String
169− providerId :: String,
197+ , providerId :: String
170− providerName :: String,
198+ , providerName :: String
171− familySponsorshipFriendlyName :: String,
199+ , familySponsorshipFriendlyName :: String
172− familySponsorshipAvailable :: Boolean,
200+ , familySponsorshipAvailable :: Boolean
173− planProductType :: ProductType,
201+ , planProductType :: ProductType
174− keyConnectorEnabled :: Boolean,
202+ , keyConnectorEnabled :: Boolean
175− keyConnectorUrl :: String,
203+ , keyConnectorUrl :: String
176− familySponsorshipLastSyncDate :: Nullable Date,
204+ , familySponsorshipLastSyncDate :: Nullable Date
177− familySponsorshipValidUntil :: Nullable Date,
205+ , familySponsorshipValidUntil :: Nullable Date
178− familySponsorshipToDelete :: Nullable Boolean
206+ , familySponsorshipToDelete :: Nullable Boolean
179−}
207+ }
180−
208+
181−type Urls = {
209+type Urls
182− base :: Nullable String,
210+ = { base :: Nullable String
183− webVault :: Nullable String,
211+ , webVault :: Nullable String
184− api :: Nullable String,
212+ , api :: Nullable String
185− identity :: Nullable String,
213+ , identity :: Nullable String
186− icons :: Nullable String,
214+ , icons :: Nullable String
187− notifications :: Nullable String,
215+ , notifications :: Nullable String
188− events :: Nullable String,
216+ , events :: Nullable String
189− keyConnector :: Nullable String
217+ , keyConnector :: Nullable String
190−}
218+ }
191−
219+
192−type PasswordTokenRequest = {
220+type PasswordTokenRequest
193− email :: Email,
221+ = { email :: Email
194− masterPasswordHash :: String,
222+ , masterPasswordHash :: String
195− captchaResponse :: String,
223+ , captchaResponse :: String
196− twoFactor :: TokenRequestTwoFactor,
224+ , twoFactor :: TokenRequestTwoFactor
197− device :: DeviceRequest
225+ , device :: DeviceRequest
198−}
226+ }
199−
227+
200−type DeviceRequest = {
228+type DeviceRequest
201− type :: DeviceType,
229+ = { type :: DeviceType
202− name :: String,
230+ , name :: String
203− identifier :: String,
231+ , identifier :: String
204− pushToken :: Nullable String
232+ , pushToken :: Nullable String
205−}
233+ }
206
234
207deviceTypeAndroid = 0 :: DeviceType
235deviceTypeAndroid = 0 :: DeviceType
236+
208deviceTypeiOS = 1 :: DeviceType
237deviceTypeiOS = 1 :: DeviceType
238+
209deviceTypeChromeExtension = 2 :: DeviceType
239deviceTypeChromeExtension = 2 :: DeviceType
240+
210deviceTypeFirefoxExtension = 3 :: DeviceType
241deviceTypeFirefoxExtension = 3 :: DeviceType
242+
211deviceTypeOperaExtension = 4 :: DeviceType
243deviceTypeOperaExtension = 4 :: DeviceType
244+
212deviceTypeEdgeExtension = 5 :: DeviceType
245deviceTypeEdgeExtension = 5 :: DeviceType
246+
213deviceTypeWindowsDesktop = 6 :: DeviceType
247deviceTypeWindowsDesktop = 6 :: DeviceType
248+
214deviceTypeMacOsDesktop = 7 :: DeviceType
249deviceTypeMacOsDesktop = 7 :: DeviceType
250+
215deviceTypeLinuxDesktop = 8 :: DeviceType
251deviceTypeLinuxDesktop = 8 :: DeviceType
252+
216deviceTypeChromeBrowser = 9 :: DeviceType
253deviceTypeChromeBrowser = 9 :: DeviceType
254+
217deviceTypeFirefoxBrowser = 10 :: DeviceType
255deviceTypeFirefoxBrowser = 10 :: DeviceType
256+
218deviceTypeOperaBrowser = 11 :: DeviceType
257deviceTypeOperaBrowser = 11 :: DeviceType
258+
219deviceTypeEdgeBrowser = 12 :: DeviceType
259deviceTypeEdgeBrowser = 12 :: DeviceType
260+
220deviceTypeIEBrowser = 13 :: DeviceType
261deviceTypeIEBrowser = 13 :: DeviceType
262+
221deviceTypeUnknownBrowser = 14 :: DeviceType
263deviceTypeUnknownBrowser = 14 :: DeviceType
264+
222deviceTypeAndroidAmazon = 15 :: DeviceType
265deviceTypeAndroidAmazon = 15 :: DeviceType
266+
223deviceTypeUWP = 16 :: DeviceType
267deviceTypeUWP = 16 :: DeviceType
268+
224deviceTypeSafariBrowser = 17 :: DeviceType
269deviceTypeSafariBrowser = 17 :: DeviceType
270+
225deviceTypeVivaldiBrowser = 18 :: DeviceType
271deviceTypeVivaldiBrowser = 18 :: DeviceType
272+
226deviceTypeVivaldiExtension = 19 :: DeviceType
273deviceTypeVivaldiExtension = 19 :: DeviceType
274+
227deviceTypeSafariExtension = 20 :: DeviceType
275deviceTypeSafariExtension = 20 :: DeviceType
228−type DeviceType = Int
229
276
277+type DeviceType
278+ = Int
279+
230−type TokenRequestTwoFactor = {
280+type TokenRequestTwoFactor
231− provider :: TwoFactorProviderType,
281+ = { provider :: TwoFactorProviderType
232− token :: String,
282+ , token :: String
233− remember :: Boolean
283+ , remember :: Boolean
234−}
284+ }
235
285
236twoFactorProviderTypeAuthenticator = 0 :: TwoFactorProviderType
286twoFactorProviderTypeAuthenticator = 0 :: TwoFactorProviderType
287+
237twoFactorProviderTypeEmail = 1 :: TwoFactorProviderType
288twoFactorProviderTypeEmail = 1 :: TwoFactorProviderType
289+
238twoFactorProviderTypeDuo = 2 :: TwoFactorProviderType
290twoFactorProviderTypeDuo = 2 :: TwoFactorProviderType
291+
239twoFactorProviderTypeYubikey = 3 :: TwoFactorProviderType
292twoFactorProviderTypeYubikey = 3 :: TwoFactorProviderType
293+
240twoFactorProviderTypeU2f = 4 :: TwoFactorProviderType
294twoFactorProviderTypeU2f = 4 :: TwoFactorProviderType
295+
241twoFactorProviderTypeRemember = 5 :: TwoFactorProviderType
296twoFactorProviderTypeRemember = 5 :: TwoFactorProviderType
297+
242twoFactorProviderTypeOrganizationDuo = 6 :: TwoFactorProviderType
298twoFactorProviderTypeOrganizationDuo = 6 :: TwoFactorProviderType
299+
243twoFactorProviderTypeWebAuthn = 7 :: TwoFactorProviderType
300twoFactorProviderTypeWebAuthn = 7 :: TwoFactorProviderType
301+
244−type TwoFactorProviderType = Int
302+type TwoFactorProviderType
245−
303+ = Int
246−
304+
247−type SymmetricCryptoKey = {
248− key :: ArrayBuffer,
249− encKey :: Nullable ArrayBuffer,
250− macKey :: Nullable ArrayBuffer,
251− encType :: EncryptionType,
252− keyB64 :: String,
253− encKeyB64 :: String,
254− macKeyB64 :: String
255−}
256−
257−encryptionTypeAesCbc256_B64 = 0 :: EncryptionType
258−encryptionTypeAesCbc128_HmacSha256_B64 = 1 :: EncryptionType
259−encryptionTypeAesCbc256_HmacSha256_B64 = 2 :: EncryptionType
260−encryptionTypeRsa2048_OaepSha256_B64 = 3 :: EncryptionType
261−encryptionTypeRsa2048_OaepSha1_B64 = 4 :: EncryptionType
262−encryptionTypeRsa2048_OaepSha256_HmacSha256_B64 = 5 :: EncryptionType
263−encryptionTypeRsa2048_OaepSha1_HmacSha256_B64 = 6 :: EncryptionType
264−type EncryptionType = Int
265−
266−newtype Password = Password String
305+newtype Password
306+ = Password String
267
307
268hashPurposeServerAuthorization = 1 :: HashPurpose
308hashPurposeServerAuthorization = 1 :: HashPurpose
309+
269hashPurposeLocalAuthorization = 2 :: HashPurpose
310hashPurposeLocalAuthorization = 2 :: HashPurpose
311+
270−type HashPurpose = Int
312+type HashPurpose
271−
313+ = Int
314+
272−newtype Hash = Hash String
315+newtype Hash
273−
316+ = Hash String
317+
274−newtype AccessToken = AccessToken String
318+newtype AccessToken
319+ = AccessToken String
320+
321+derive newtype instance Show AccessToken
322+
275−newtype RefreshToken = RefreshToken String
323+newtype RefreshToken
276−
324+ = RefreshToken String
325+
326+derive newtype instance Show RefreshToken
327+
328+
277−type IdentityTokenResponse = {
329+type IdentityTokenResponse
278− accessToken :: AccessToken,
330+ = { accessToken :: AccessToken
279− expiresIn :: Int,
331+ , expiresIn :: Int
280− refreshToken :: RefreshToken,
332+ , refreshToken :: RefreshToken
281− tokenType :: String,
333+ , tokenType :: String
282− resetMasterPassword :: Boolean,
334+ , resetMasterPassword :: Boolean
283− privateKey :: String,
335+ , privateKey :: EncryptedString
284− key :: String,
336+ , key :: EncryptedString
285− twoFactorToken :: Nullable String,
337+ , twoFactorToken :: Nullable String
286− kdf :: KDF,
338+ , kdf :: KDF
287− kdfIterations :: Int,
339+ , kdfIterations :: Int
288− forcePasswordReset :: Nullable Boolean,
340+ , forcePasswordReset :: Nullable Boolean
289− apiUseKeyConnector :: Nullable Boolean,
341+ , apiUseKeyConnector :: Nullable Boolean
290− keyConnectorUrl :: Nullable String
342+ , keyConnectorUrl :: Nullable String
291−}
343+ }
292−
344+
293−newtype Email = Email String
345+newtype Email
294−
346+ = Email String
347+
295−type SyncResponse = {
348+type SyncResponse
296− profile :: Nullable ProfileResponse,
349+ = { profile :: ProfileResponse
297− folders :: Array FolderResponse,
350+ , folders :: Array FolderResponse
298− collections :: Array CollectionDetailsResponse,
351+ , collections :: Array CollectionDetailsResponse
299− ciphers :: Array CipherResponse,
352+ , ciphers :: Array CipherResponse
300− domains :: Nullable DomainsResponse,
353+ , domains :: Nullable DomainsResponse
301− policies :: Nullable (Array PolicyResponse),
354+ , policies :: Nullable (Array PolicyResponse)
302− sends :: Array SendResponse
355+ , sends :: Array SendResponse
303−}
356+ }
304−
357+
305−type FolderResponse = {
358+type FolderResponse
306− id :: String,
359+ = { id :: String
307− name :: String,
360+ , name :: String
308− revisionDate :: String
361+ , revisionDate :: String
309−}
362+ }
310−
363+
311−type CollectionDetailsResponse = {
364+type CollectionDetailsResponse
312− id :: String,
365+ = { id :: String
313− organizationId :: Nullable String,
366+ , organizationId :: Nullable String
314− name :: String,
367+ , name :: String
315− externalId :: Nullable String,
368+ , externalId :: Nullable String
316− readOnly :: Boolean
369+ , readOnly :: Boolean
317−}
370+ }
318−
371+
319−type CipherResponse = {
372+type CipherResponse
320− id :: String,
373+ = { id :: String
321− organizationId :: Nullable String,
374+ , organizationId :: Nullable String
322− folderId :: Nullable String,
375+ , folderId :: Nullable String
323− type :: CipherType,
376+ , type :: CipherType
324− name :: String,
377+ , name :: EncryptedString
325− notes :: Nullable String,
378+ , notes :: Nullable EncryptedString
326− fields :: Nullable (Array FieldApi),
379+ , fields :: Nullable (Array FieldApi)
327− login :: Nullable LoginApi,
380+ , login :: Nullable LoginApi
328− card :: Nullable CardApi,
381+ , card :: Nullable CardApi
329− identity :: Nullable IdentityApi,
382+ , identity :: Nullable IdentityApi
330− secureNote :: Nullable SecureNoteApi,
383+ , secureNote :: Nullable SecureNoteApi
331− favorite :: Boolean,
384+ , favorite :: Boolean
332− edit :: Boolean,
385+ , edit :: Boolean
333− viewPassword :: Boolean,
386+ , viewPassword :: Boolean
334− organizationUseTotp :: Boolean,
387+ , organizationUseTotp :: Boolean
335− revisionDate :: String,
388+ , revisionDate :: String
336− attachments :: Nullable (Array AttachmentResponse),
389+ , attachments :: Nullable (Array AttachmentResponse)
337− passwordHistory :: Nullable (Array PasswordHistoryResponse),
390+ , passwordHistory :: Nullable (Array PasswordHistoryResponse)
338− collectionIds :: Nullable (Array String),
391+ , collectionIds :: Nullable (Array String)
339− deletedDate :: Nullable String,
392+ , deletedDate :: Nullable String
340− reprompt :: CipherRepromptType
393+ , reprompt :: CipherRepromptType
341−}
394+ }
342
395
343cipherTypeLogin = 1 :: CipherType
396cipherTypeLogin = 1 :: CipherType
397+
344cipherTypeSecureNote = 2 :: CipherType
398cipherTypeSecureNote = 2 :: CipherType
399+
345cipherTypeCard = 3 :: CipherType
400cipherTypeCard = 3 :: CipherType
401+
346cipherTypeIdentity = 4 :: CipherType
402cipherTypeIdentity = 4 :: CipherType
347−type CipherType = Int
348
403
404+type CipherType
405+ = Int
406+
349−type FieldApi = {
407+type FieldApi
350− name :: String,
408+ = { name :: String
351− value :: String,
409+ , value :: String
352− type :: FieldType,
410+ , type :: FieldType
353− linkedId :: Nullable LinkedIdType
411+ , linkedId :: Nullable LinkedIdType
354−}
412+ }
355
413
356fieldTypeText = 0 :: FieldType
414fieldTypeText = 0 :: FieldType
415+
357fieldTypeHidden = 1 :: FieldType
416fieldTypeHidden = 1 :: FieldType
417+
358fieldTypeBoolean = 2 :: FieldType
418fieldTypeBoolean = 2 :: FieldType
419+
359fieldTypeLinked = 3 :: FieldType
420fieldTypeLinked = 3 :: FieldType
421+
360−type FieldType = Int
422+type FieldType
423+ = Int
361
424
362linkedIdTypeLoginLinkedIdUsername = 100 :: LinkedIdType
425linkedIdTypeLoginLinkedIdUsername = 100 :: LinkedIdType
426+
363linkedIdTypeLoginLinkedIdPassword = 101 :: LinkedIdType
427linkedIdTypeLoginLinkedIdPassword = 101 :: LinkedIdType
428+
364linkedIdTypeCardLinkedIdCardholderName = 300 :: LinkedIdType
429linkedIdTypeCardLinkedIdCardholderName = 300 :: LinkedIdType
430+
365linkedIdTypeCardLinkedIdExpMonth = 301 :: LinkedIdType
431linkedIdTypeCardLinkedIdExpMonth = 301 :: LinkedIdType
432+
366linkedIdTypeCardLinkedIdExpYear = 302 :: LinkedIdType
433linkedIdTypeCardLinkedIdExpYear = 302 :: LinkedIdType
434+
367linkedIdTypeCardLinkedIdCode = 303 :: LinkedIdType
435linkedIdTypeCardLinkedIdCode = 303 :: LinkedIdType
436+
368linkedIdTypeCardLinkedIdBrand = 304 :: LinkedIdType
437linkedIdTypeCardLinkedIdBrand = 304 :: LinkedIdType
438+
369linkedIdTypeCardLinkedIdNumber = 305 :: LinkedIdType
439linkedIdTypeCardLinkedIdNumber = 305 :: LinkedIdType
440+
370linkedIdTypeIdentityLinkedIdTitle = 400 :: LinkedIdType
441linkedIdTypeIdentityLinkedIdTitle = 400 :: LinkedIdType
442+
371linkedIdTypeIdentityLinkedIdMiddleName = 401 :: LinkedIdType
443linkedIdTypeIdentityLinkedIdMiddleName = 401 :: LinkedIdType
444+
372linkedIdTypeIdentityLinkedIdAddress1 = 402 :: LinkedIdType
445linkedIdTypeIdentityLinkedIdAddress1 = 402 :: LinkedIdType
446+
373linkedIdTypeIdentityLinkedIdAddress2 = 403 :: LinkedIdType
447linkedIdTypeIdentityLinkedIdAddress2 = 403 :: LinkedIdType
448+
374linkedIdTypeIdentityLinkedIdAddress3 = 404 :: LinkedIdType
449linkedIdTypeIdentityLinkedIdAddress3 = 404 :: LinkedIdType
450+
375linkedIdTypeIdentityLinkedIdCity = 405 :: LinkedIdType
451linkedIdTypeIdentityLinkedIdCity = 405 :: LinkedIdType
452+
376linkedIdTypeIdentityLinkedIdState = 406 :: LinkedIdType
453linkedIdTypeIdentityLinkedIdState = 406 :: LinkedIdType
454+
377linkedIdTypeIdentityLinkedIdPostalCode = 407 :: LinkedIdType
455linkedIdTypeIdentityLinkedIdPostalCode = 407 :: LinkedIdType
456+
378linkedIdTypeIdentityLinkedIdCountry = 408 :: LinkedIdType
457linkedIdTypeIdentityLinkedIdCountry = 408 :: LinkedIdType
458+
379linkedIdTypeIdentityLinkedIdCompany = 409 :: LinkedIdType
459linkedIdTypeIdentityLinkedIdCompany = 409 :: LinkedIdType
460+
380linkedIdTypeIdentityLinkedIdEmail = 410 :: LinkedIdType
461linkedIdTypeIdentityLinkedIdEmail = 410 :: LinkedIdType
462+
381linkedIdTypeIdentityLinkedIdPhone = 411 :: LinkedIdType
463linkedIdTypeIdentityLinkedIdPhone = 411 :: LinkedIdType
464+
382linkedIdTypeIdentityLinkedIdSsn = 412 :: LinkedIdType
465linkedIdTypeIdentityLinkedIdSsn = 412 :: LinkedIdType
466+
383linkedIdTypeIdentityLinkedIdUsername = 413 :: LinkedIdType
467linkedIdTypeIdentityLinkedIdUsername = 413 :: LinkedIdType
468+
384linkedIdTypeIdentityLinkedIdPassportNumber = 414 :: LinkedIdType
469linkedIdTypeIdentityLinkedIdPassportNumber = 414 :: LinkedIdType
470+
385linkedIdTypeIdentityLinkedIdLicenseNumber = 415 :: LinkedIdType
471linkedIdTypeIdentityLinkedIdLicenseNumber = 415 :: LinkedIdType
472+
386linkedIdTypeIdentityLinkedIdFirstName = 416 :: LinkedIdType
473linkedIdTypeIdentityLinkedIdFirstName = 416 :: LinkedIdType
474+
387linkedIdTypeIdentityLinkedIdLastName = 417 :: LinkedIdType
475linkedIdTypeIdentityLinkedIdLastName = 417 :: LinkedIdType
476+
388linkedIdTypeIdentityLinkedIdFullName = 418 :: LinkedIdType
477linkedIdTypeIdentityLinkedIdFullName = 418 :: LinkedIdType
478+
389−type LinkedIdType = Int
479+type LinkedIdType
390−
480+ = Int
481+
391−type LoginApi = {
482+type LoginApi
392− uris :: Opt (Array LoginUriApi),
483+ = { uris :: Opt (Array LoginUriApi)
393− username :: Nullable String,
484+ , username :: Nullable EncryptedString
394− password :: Nullable String,
485+ , password :: Nullable EncryptedString
395− passwordRevisionDate :: Nullable String,
486+ , passwordRevisionDate :: Nullable String
396− totp :: Nullable String,
487+ , totp :: Nullable EncryptedString
397− -- This thig is undefined in some cases for some reson. Just ignoring it
488+ -- This thing is undefined in some cases for some reson. Just ignoring it
398− autofillOnPageLoad :: Opt (Nullable Boolean)
489+ , autofillOnPageLoad :: Opt (Nullable Boolean)
399−}
490+ }
400−
491+
401−type LoginUriApi = {
492+type LoginUriApi
402− uri :: String,
493+ = { uri :: String
403− match :: Nullable UriMatchType
494+ , match :: Nullable UriMatchType
404−}
495+ }
405
496
406uriMatchTypeDomain = 0 :: UriMatchType
497uriMatchTypeDomain = 0 :: UriMatchType
498+
407uriMatchTypeHost = 1 :: UriMatchType
499uriMatchTypeHost = 1 :: UriMatchType
500+
408uriMatchTypeStartsWith = 2 :: UriMatchType
501uriMatchTypeStartsWith = 2 :: UriMatchType
502+
409uriMatchTypeExact = 3 :: UriMatchType
503uriMatchTypeExact = 3 :: UriMatchType
504+
410uriMatchTypeRegularExpression = 4 :: UriMatchType
505uriMatchTypeRegularExpression = 4 :: UriMatchType
506+
411uriMatchTypeNever = 5 :: UriMatchType
507uriMatchTypeNever = 5 :: UriMatchType
508+
412−type UriMatchType = Int
509+type UriMatchType
413−
510+ = Int
511+
414−type CardApi = {
512+type CardApi
415− cardholderName :: Nullable String,
513+ = { cardholderName :: Nullable String
416− brand :: Nullable String,
514+ , brand :: Nullable String
417− number :: Nullable String,
515+ , number :: Nullable String
418− expMonth :: Nullable String,
516+ , expMonth :: Nullable String
419− expYear :: Nullable String,
517+ , expYear :: Nullable String
420− code :: Nullable String
518+ , code :: Nullable String
421−}
519+ }
422−
520+
423−type IdentityApi = {
521+type IdentityApi
424− title :: String,
522+ = { title :: String
425− firstName :: String,
523+ , firstName :: String
426− middleName :: String,
524+ , middleName :: String
427− lastName :: String,
525+ , lastName :: String
428− address1 :: String,
526+ , address1 :: String
429− address2 :: String,
527+ , address2 :: String
430− address3 :: String,
528+ , address3 :: String
431− city :: String,
529+ , city :: String
432− state :: String,
530+ , state :: String
433− postalCode :: String,
531+ , postalCode :: String
434− country :: String,
532+ , country :: String
435− company :: String,
533+ , company :: String
436− email :: String,
534+ , email :: String
437− phone :: String,
535+ , phone :: String
438− ssn :: String,
536+ , ssn :: String
439− username :: String,
537+ , username :: String
440− passportNumber :: String,
538+ , passportNumber :: String
441− licenseNumber :: String
539+ , licenseNumber :: String
442−}
540+ }
443
541
444secureNoteApiGeneric = 0 :: SecureNoteApi
542secureNoteApiGeneric = 0 :: SecureNoteApi
543+
445−type SecureNoteApi = Int
544+type SecureNoteApi
446−
545+ = Int
546+
447−type AttachmentResponse = {
547+type AttachmentResponse
448− id :: String,
548+ = { id :: String
449− url :: String,
549+ , url :: String
450− fileName :: String,
550+ , fileName :: String
451− key :: String,
551+ , key :: String
452− size :: String,
552+ , size :: String
453− sizeName :: String
553+ , sizeName :: String
454−}
554+ }
455−
555+
456−type PasswordHistoryResponse = {
556+type PasswordHistoryResponse
457− password :: String,
557+ = { password :: String
458− lastUsedDate :: String
558+ , lastUsedDate :: String
459−}
559+ }
460
560
461cipherRepromptTypeNone = 0 :: CipherRepromptType
561cipherRepromptTypeNone = 0 :: CipherRepromptType
562+
462cipherRepromptTypePassword = 1 :: CipherRepromptType
563cipherRepromptTypePassword = 1 :: CipherRepromptType
564+
463−type CipherRepromptType = Int
565+type CipherRepromptType
464−
566+ = Int
567+
465−type DomainsResponse = {
568+type DomainsResponse
466− equivalentDomains :: Array (Array String),
569+ = { equivalentDomains :: Array (Array String)
467− globalEquivalentDomains :: Array GlobalDomainResponse
570+ , globalEquivalentDomains :: Array GlobalDomainResponse
468−}
571+ }
469−
572+
470−type GlobalDomainResponse = {
573+type GlobalDomainResponse
471− type :: Int,
574+ = { type :: Int
472− domains :: Array String,
575+ , domains :: Array String
473− excluded :: Boolean
576+ , excluded :: Boolean
474−}
577+ }
475−
578+
476−type PolicyResponse = {
579+type PolicyResponse
477− id :: String,
580+ = { id :: String
478− organizationId :: Nullable String,
581+ , organizationId :: Nullable String
479− type :: PolicyType,
582+ , type :: PolicyType
480− data :: ShowableJson,
583+ , data :: ShowableJson
481− enabled :: Boolean
584+ , enabled :: Boolean
482−}
585+ }
483
586
484policyTypeTwoFactorAuthentication = 0 :: PolicyType -- Requires users to have 2fa enabled
587policyTypeTwoFactorAuthentication = 0 :: PolicyType -- Requires users to have 2fa enabled
588+
485policyTypeMasterPassword = 1 :: PolicyType -- Sets minimum requirements for master password complexity
589policyTypeMasterPassword = 1 :: PolicyType -- Sets minimum requirements for master password complexity
590+
486policyTypePasswordGenerator = 2 :: PolicyType -- Sets minimum requirements/default type for generated passwords/passphrases
591policyTypePasswordGenerator = 2 :: PolicyType -- Sets minimum requirements/default type for generated passwords/passphrases
592+
487policyTypeSingleOrg = 3 :: PolicyType -- Allows users to only be apart of one organization
593policyTypeSingleOrg = 3 :: PolicyType -- Allows users to only be apart of one organization
594+
488policyTypeRequireSso = 4 :: PolicyType -- Requires users to authenticate with SSO
595policyTypeRequireSso = 4 :: PolicyType -- Requires users to authenticate with SSO
596+
489policyTypePersonalOwnership = 5 :: PolicyType -- Disables personal vault ownership for adding/cloning items
597policyTypePersonalOwnership = 5 :: PolicyType -- Disables personal vault ownership for adding/cloning items
598+
490policyTypeDisableSend = 6 :: PolicyType -- Disables the ability to create and edit Bitwarden Sends
599policyTypeDisableSend = 6 :: PolicyType -- Disables the ability to create and edit Bitwarden Sends
600+
491policyTypeSendOptions = 7 :: PolicyType -- Sets restrictions or defaults for Bitwarden Sends
601policyTypeSendOptions = 7 :: PolicyType -- Sets restrictions or defaults for Bitwarden Sends
602+
492policyTypeResetPassword = 8 :: PolicyType -- Allows orgs to use reset password : also can enable auto-enrollment during invite flow
603policyTypeResetPassword = 8 :: PolicyType -- Allows orgs to use reset password : also can enable auto-enrollment during invite flow
604+
493policyTypeMaximumVaultTimeout = 9 :: PolicyType -- Sets the maximum allowed vault timeout
605policyTypeMaximumVaultTimeout = 9 :: PolicyType -- Sets the maximum allowed vault timeout
606+
494policyTypeDisablePersonalVaultExport = 10 :: PolicyType -- Disable personal vault export
607policyTypeDisablePersonalVaultExport = 10 :: PolicyType -- Disable personal vault export
608+
495−type PolicyType = Int
609+type PolicyType
496−
610+ = Int
611+
497−type SendResponse = {
612+type SendResponse
498− id :: String,
613+ = { id :: String
499− accessId :: String,
614+ , accessId :: String
500− type :: SendType,
615+ , type :: SendType
501− name :: String,
616+ , name :: String
502− notes :: Nullable String,
617+ , notes :: Nullable String
503− file :: Nullable SendFileApi,
618+ , file :: Nullable SendFileApi
504− text :: Nullable SendTextApi,
619+ , text :: Nullable SendTextApi
505− key :: String,
620+ , key :: String
506− maxAccessCount :: Nullable Int,
621+ , maxAccessCount :: Nullable Int
507− accessCount :: Int,
622+ , accessCount :: Int
508− revisionDate :: String,
623+ , revisionDate :: String
509− expirationDate :: String,
624+ , expirationDate :: String
510− deletionDate :: Nullable String,
625+ , deletionDate :: Nullable String
511− password :: String,
626+ , password :: String
512− disable :: Boolean,
627+ , disable :: Boolean
513− hideEmail :: Boolean
628+ , hideEmail :: Boolean
514−}
629+ }
515
630
516sendTypeText = 0 :: SendType
631sendTypeText = 0 :: SendType
632+
517sendTypeFile = 1 :: SendType
633sendTypeFile = 1 :: SendType
634+
518−type SendType = Int
635+type SendType
519−
636+ = Int
637+
520−type SendFileApi = {
638+type SendFileApi
521− id :: String,
639+ = { id :: String
522− fileName :: String,
640+ , fileName :: String
523− size :: String,
641+ , size :: String
524− sizeName :: String
642+ , sizeName :: String
525−}
643+ }
526−
644+
527−type SendTextApi = {
645+type SendTextApi
528− text :: String,
646+ = { text :: String
529− hidden :: Boolean
647+ , hidden :: Boolean
530−}
648+ }
bw/src/Data/EncString.js+8−2
@@ -1,5 +1,11 @@
1−import { EncString } from "../../../deps/bw/libs/shared/dist/src/models/domain/encString"
1+import { EncString } from "../../deps/bw/libs/shared/dist/src/models/domain/encString"
2
2
3export function fromString(str) {
3export function fromString(str) {
4+ console.log("fromString " + str)
4− return new EncString(str)
5+ const x = new EncString(str)
6+ return x
7+}
8+
9+export function encryptionType(encStr) {
10+ return encStr.encryptionType
5}
11}
bw/src/Data/EncString.purs+21−1
@@ -1,7 +1,27 @@
1module Data.EncString where
1module Data.EncString where
2
2
3import Prelude
3import Prelude
4+import BW.Types (EncryptedString(..))
4
5
5foreign import data EncString :: Type
6foreign import data EncString :: Type
6
7
8+foreign import encryptionType :: EncString -> EncryptionType
9+
10+encryptionTypeAesCbc256_B64 = 0 :: EncryptionType
11+
12+encryptionTypeAesCbc128_HmacSha256_B64 = 1 :: EncryptionType
13+
14+encryptionTypeAesCbc256_HmacSha256_B64 = 2 :: EncryptionType
15+
16+encryptionTypeRsa2048_OaepSha256_B64 = 3 :: EncryptionType
17+
18+encryptionTypeRsa2048_OaepSha1_B64 = 4 :: EncryptionType
19+
20+encryptionTypeRsa2048_OaepSha256_HmacSha256_B64 = 5 :: EncryptionType
21+
22+encryptionTypeRsa2048_OaepSha1_HmacSha256_B64 = 6 :: EncryptionType
23+
24+type EncryptionType
25+ = Int
26+
7−foreign import fromString :: String -> EncString
27+foreign import fromString :: EncryptedString -> EncString
bw/src/Data/SymmetricCryptoKey.jsnew file+6
@@ -0,0 +1,6 @@
1+import { SymmetricCryptoKey } from '../../deps/bw/libs/shared/dist/src/models/domain/symmetricCryptoKey.js';
2+
3+export function fromArrayBuffer(arr) {
4+ console.log("fromArrayBuffer " + arr)
5+ return new SymmetricCryptoKey(arr)
6+}
bw/src/Data/SymmetricCryptoKey.pursnew file+34
@@ -0,0 +1,34 @@
1+module Data.SymmetricCryptoKey where
2+
3+import Prelude
4+import Data.ArrayBuffer.Types (ArrayBuffer)
5+import Data.Nullable (Nullable)
6+
7+type SymmetricCryptoKey
8+ = { key :: ArrayBuffer
9+ , encKey :: Nullable ArrayBuffer
10+ , macKey :: Nullable ArrayBuffer
11+ , encType :: EncryptionType
12+ , keyB64 :: String
13+ , encKeyB64 :: String
14+ , macKeyB64 :: String
15+ }
16+
17+encryptionTypeAesCbc256_B64 = 0 :: EncryptionType
18+
19+encryptionTypeAesCbc128_HmacSha256_B64 = 1 :: EncryptionType
20+
21+encryptionTypeAesCbc256_HmacSha256_B64 = 2 :: EncryptionType
22+
23+encryptionTypeRsa2048_OaepSha256_B64 = 3 :: EncryptionType
24+
25+encryptionTypeRsa2048_OaepSha1_B64 = 4 :: EncryptionType
26+
27+encryptionTypeRsa2048_OaepSha256_HmacSha256_B64 = 5 :: EncryptionType
28+
29+encryptionTypeRsa2048_OaepSha1_HmacSha256_B64 = 6 :: EncryptionType
30+
31+type EncryptionType
32+ = Int
33+
34+foreign import fromArrayBuffer :: ArrayBuffer -> SymmetricCryptoKey
dev/start-chrome.shnew file+3
@@ -0,0 +1,3 @@
1+#!/bin/sh
2+
3+/Applications/Chromium.app/Contents/MacOS/Chromium --args --user-data-dir="/tmp/chrome_dev_test" --disable-web-security
frontend/Bridge.elm+19−5
@@ -32,16 +32,24 @@ jsonEncCmd_Login val =
32
32
33
33
34type Cmd =
34type Cmd =
35+ Init
35− Login Cmd_Login
36+ | Login Cmd_Login
36
37
37jsonDecCmd : Json.Decode.Decoder ( Cmd )
38jsonDecCmd : Json.Decode.Decoder ( Cmd )
38jsonDecCmd =
39jsonDecCmd =
40+ let jsonDecDictCmd = Dict.fromList
41+ [ ("Init", Json.Decode.lazy (\_ -> Json.Decode.succeed Init))
39− Json.Decode.lazy (\_ -> Json.Decode.map Login (jsonDecCmd_Login))
42+ , ("Login", Json.Decode.lazy (\_ -> Json.Decode.map Login (jsonDecCmd_Login)))
40−
43+ ]
44+ jsonDecObjectSetCmd = Set.fromList []
45+ in decodeSumTaggedObject "Cmd" "tag" "contents" jsonDecDictCmd jsonDecObjectSetCmd
41
46
42jsonEncCmd : Cmd -> Value
47jsonEncCmd : Cmd -> Value
43−jsonEncCmd (Login v1) =
48+jsonEncCmd val =
49+ let keyval v = case v of
50+ Init -> ("Init", encodeValue (Json.Encode.list identity []))
44− jsonEncCmd_Login v1
51+ Login v1 -> ("Login", encodeValue (jsonEncCmd_Login v1))
52+ in encodeSumTaggedObject "tag" "contents" keyval val
45
53
46
54
47
55
@@ -67,15 +75,19 @@ jsonEncSub_Hello val =
67
75
68type Sub =
76type Sub =
69 Empty
77 Empty
78+ | Error String
70 | GotEverything String
79 | GotEverything String
71 | Hello Sub_Hello
80 | Hello Sub_Hello
81+ | NeedsLogin
72
82
73jsonDecSub : Json.Decode.Decoder ( Sub )
83jsonDecSub : Json.Decode.Decoder ( Sub )
74jsonDecSub =
84jsonDecSub =
75 let jsonDecDictSub = Dict.fromList
85 let jsonDecDictSub = Dict.fromList
76 [ ("Empty", Json.Decode.lazy (\_ -> Json.Decode.succeed Empty))
86 [ ("Empty", Json.Decode.lazy (\_ -> Json.Decode.succeed Empty))
87+ , ("Error", Json.Decode.lazy (\_ -> Json.Decode.map Error (Json.Decode.string)))
77 , ("GotEverything", Json.Decode.lazy (\_ -> Json.Decode.map GotEverything (Json.Decode.string)))
88 , ("GotEverything", Json.Decode.lazy (\_ -> Json.Decode.map GotEverything (Json.Decode.string)))
78 , ("Hello", Json.Decode.lazy (\_ -> Json.Decode.map Hello (jsonDecSub_Hello)))
89 , ("Hello", Json.Decode.lazy (\_ -> Json.Decode.map Hello (jsonDecSub_Hello)))
90+ , ("NeedsLogin", Json.Decode.lazy (\_ -> Json.Decode.succeed NeedsLogin))
79 ]
91 ]
80 jsonDecObjectSetSub = Set.fromList []
92 jsonDecObjectSetSub = Set.fromList []
81 in decodeSumTaggedObject "Sub" "tag" "contents" jsonDecDictSub jsonDecObjectSetSub
93 in decodeSumTaggedObject "Sub" "tag" "contents" jsonDecDictSub jsonDecObjectSetSub
@@ -84,7 +96,9 @@ jsonEncSub : Sub -> Value
84jsonEncSub val =
96jsonEncSub val =
85 let keyval v = case v of
97 let keyval v = case v of
86 Empty -> ("Empty", encodeValue (Json.Encode.list identity []))
98 Empty -> ("Empty", encodeValue (Json.Encode.list identity []))
99+ Error v1 -> ("Error", encodeValue (Json.Encode.string v1))
87 GotEverything v1 -> ("GotEverything", encodeValue (Json.Encode.string v1))
100 GotEverything v1 -> ("GotEverything", encodeValue (Json.Encode.string v1))
88 Hello v1 -> ("Hello", encodeValue (jsonEncSub_Hello v1))
101 Hello v1 -> ("Hello", encodeValue (jsonEncSub_Hello v1))
102+ NeedsLogin -> ("NeedsLogin", encodeValue (Json.Encode.list identity []))
89 in encodeSumTaggedObject "tag" "contents" keyval val
103 in encodeSumTaggedObject "tag" "contents" keyval val
90
104
frontend/Main.elm+21−8
@@ -13,9 +13,10 @@ import Utils exposing (..)
13
13
14type Msg
14type Msg
15 = RecieveMessage String
15 = RecieveMessage String
16+ | ShowLoginPage
16 | ShowError String
17 | ShowError String
17 | CloseNotification
18 | CloseNotification
18− | Submit { email : String, password : String, server : String }
19+ | SubmitLogin { email : String, password : String, server : String }
19 | LoginMsg Login.Msg
20 | LoginMsg Login.Msg
20
21
21
22
@@ -52,7 +53,7 @@ showPage page =
52main : Program () Model Msg
53main : Program () Model Msg
53main =
54main =
54 Browser.element
55 Browser.element
55− { init = \_ -> ( init, Cmd.none )
56+ { init = \_ -> init
56 , view = view
57 , view = view
57 , update = update
58 , update = update
58 , subscriptions =
59 , subscriptions =
@@ -68,15 +69,23 @@ main =
68
69
69 Bridge.Empty ->
70 Bridge.Empty ->
70 RecieveMessage "Empty"
71 RecieveMessage "Empty"
72+
73+ Bridge.NeedsLogin ->
74+ ShowLoginPage
75+
76+ Bridge.Error err ->
77+ ShowError err
71 )
78 )
72 }
79 }
73
80
74
81
75−init : Model
82+init : ( Model, Cmd Msg )
76init =
83init =
77− { notifications = []
84+ ( { notifications = []
78− , page = LoadingPage
85+ , page = LoadingPage
79− }
86+ }
87+ , FFI.sendBridge Bridge.Init
88+ )
80
89
81
90
82view : Model -> Html Msg
91view : Model -> Html Msg
@@ -139,7 +148,7 @@ update msg model =
139 , Cmd.none
148 , Cmd.none
140 )
149 )
141
150
142− Submit data ->
151+ SubmitLogin data ->
143 ( model
152 ( model
144 , FFI.sendBridge (Bridge.Login data)
153 , FFI.sendBridge (Bridge.Login data)
145 )
154 )
@@ -152,7 +161,11 @@ update msg model =
152 _ ->
161 _ ->
153 ( model, Cmd.none )
162 ( model, Cmd.none )
154
163
164+ ShowLoginPage ->
165+ (Login.page loginCallbacks LoginMsg).init ()
166+ |> Tuple.mapFirst (\pageModel -> { model | page = LoginModel pageModel })
167+
155
168
156loginCallbacks : Login.Callbacks Msg
169loginCallbacks : Login.Callbacks Msg
157loginCallbacks =
170loginCallbacks =
158− { submit = Submit }
171+ { submit = SubmitLogin }
frontend/Pages/Login.elm+2−2
@@ -32,7 +32,7 @@ page callbacks liftMsg =
32 , view = \model -> view model |> List.map (Html.map liftMsg)
32 , view = \model -> view model |> List.map (Html.map liftMsg)
33 , update = \msg model -> update callbacks liftMsg msg model
33 , update = \msg model -> update callbacks liftMsg msg model
34 , subscriptions = \model -> subscriptions model |> Sub.map liftMsg
34 , subscriptions = \model -> subscriptions model |> Sub.map liftMsg
35− , title = always "Log into your bitwarden account"
35+ , title = always "Log into bitwarden"
36 }
36 }
37
37
38
38
@@ -76,6 +76,6 @@ view model =
76 , input [ Attr.type_ "email", Attr.value model.email, Ev.onInput UpdateEmail ] []
76 , input [ Attr.type_ "email", Attr.value model.email, Ev.onInput UpdateEmail ] []
77 , label [] [ text "Password" ]
77 , label [] [ text "Password" ]
78 , input [ Attr.type_ "password", Attr.value model.password, Ev.onInput UpdatePassword ] []
78 , input [ Attr.type_ "password", Attr.value model.password, Ev.onInput UpdatePassword ] []
79− , alignRight [ button [ Ev.onClick Submit ] [ text "Log in" ] ]
80 ]
79 ]
80+ , alignRight [ button [ Ev.onClick Submit ] [ text "Log in" ] ]
81 ]
81 ]
frontend/Pages/Navigation.elm+1−1
@@ -14,7 +14,7 @@ type alias Config =
14navigation : Config -> Html msg
14navigation : Config -> Html msg
15navigation { back, title } =
15navigation { back, title } =
16 nav [ Attr.class "p-tabs p-tabs__list" ]
16 nav [ Attr.class "p-tabs p-tabs__list" ]
17− [ h3 []
17+ [ h3 [ Attr.class "u-no-margin u-no-padding" ]
18 [ button
18 [ button
19 (Attr.class "p-button--base is-inline u-no-margin" :: optional (not back) (Attr.style "visibility" "hidden"))
19 (Attr.class "p-button--base is-inline u-no-margin" :: optional (not back) (Attr.style "visibility" "hidden"))
20 [ i [ Attr.class "p-icon--chevron-up ninety-counter" ] []
20 [ i [ Attr.class "p-icon--chevron-up ninety-counter" ] []
spago.dhall+4
@@ -22,12 +22,16 @@ to generate this file without the comments in this block.
22 , "datetime"
22 , "datetime"
23 , "effect"
23 , "effect"
24 , "either"
24 , "either"
25+ , "exceptions"
26+ , "foldable-traversable"
25 , "foreign"
27 , "foreign"
28+ , "lists"
26 , "maybe"
29 , "maybe"
27 , "newtype"
30 , "newtype"
28 , "nullable"
31 , "nullable"
29 , "prelude"
32 , "prelude"
30 , "transformers"
33 , "transformers"
34+ , "unsafe-coerce"
31 ]
35 ]
32, packages = ./packages.dhall
36, packages = ./packages.dhall
33, sources = [ "backend/**/*.purs" ]
37, sources = [ "backend/**/*.purs" ]