on 83e9a4e help sign in
.github/FUNDING.ymldeleted−1
since splitsnapshots
unifiedsplit
@@ -1 +0,0 @@
1−custom: ["https://iko.soy/donate"]
.github/workflows/build.yamldeleted−97
@@ -1,97 +0,0 @@
1−name: CI
2−
3−on:
4− push:
5− branches: ["master"]
6− pull_request:
7− branches: ["master"]
8− release:
9− types: [prereleased]
10−
11−jobs:
12− build:
13− runs-on: ubuntu-latest
14−
15− steps:
16− - uses: cachix/install-nix-action@v19
17−
18− - name: Cachix
19− uses: cachix/cachix-action@v12
20− with:
21− name: iko
22− authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
23−
24− - uses: actions/checkout@v3
25− with:
26− submodules: recursive
27−
28− - name: Install clickable
29− run: |
30− docker --version
31− sudo apt install adb git python3 python3-pip python3-setuptools
32− pip3 install --user clickable-ut==7.10.0 --upgrade
33− echo 'export PATH="$PATH:$HOME/.local/bin"' >> ~/.bashrc
34−
35− - name: Build artifacts
36− run: ./dev/build.sh
37−
38− - name: Build Warden
39− run: clickable build --output click
40−
41− - uses: actions/upload-artifact@v3
42− with:
43− name: click
44− path: click/*
45−
46− - uses: actions/upload-artifact@v3
47− with:
48− name: dist
49− path: dist
50−
51− # Tests
52−
53− - uses: actions/setup-node@v3
54− with:
55− node-version: 16
56−
57− - name: Install test dependencies
58− working-directory: tests
59− run: |
60− npm ci
61− npx playwright install --with-deps
62−
63− - name: Run tests
64− working-directory: tests
65− run: npx playwright test
66−
67− - uses: actions/upload-artifact@v3
68− if: always()
69− with:
70− name: playwright-report
71− path: tests/playwright-report/
72−
73− # Upload release
74−
75− - name: Zip Release Assets
76− if: github.event_name == 'release'
77− shell: bash
78− run: |
79− cd dist/
80− zip -r ../../../../dist.zip .
81−
82− - name: Upload dist to release
83− if: github.event_name == 'release'
84− uses: svenstaro/upload-release-action@2.4.1
85− with:
86− repo_token: ${{ secrets.GITHUB_TOKEN }}
87− file: dist.zip
88− tag: ${{ github.ref }}
89−
90− - name: Upload click to release
91− if: github.event_name == 'release'
92− uses: svenstaro/upload-release-action@2.4.1
93− with:
94− repo_token: ${{ secrets.GITHUB_TOKEN }}
95− file: build/all/app/*.click
96− tag: ${{ github.ref }}
97− file_glob: true
.gitignore+1−12
@@ -7,16 +7,5 @@ static/backend.js
7.spago
7.spago
8output
8output
9.psc-ide-port
9.psc-ide-port
10−elm.js
11−output-es
12−elm-stuff
13−node_modules/
14−/tests/test-results/
15−/tests/playwright-report/
16−/tests/playwright/.cache/
17−generated-docs
18−backend.js.map
19−.spago2nix
20backend.js
10backend.js
21−vanilla
11+elm.js
22−dist
.gitmodulesnew file+6
@@ -0,0 +1,6 @@
1+[submodule "deps/bw"]
2+ path = deps/bw
3+ url = https://github.com/bitwarden/clients.git
4+[submodule "vanilla"]
5+ path = vanilla
6+ url = https://github.com/ilyakooo0/vanilla-compiled.git
.helix/languages.tomldeleted−3
@@ -1,3 +0,0 @@
1−[[language]]
2−name = "nix"
3−auto-format = true
.vscode/tasks.jsondeleted−31
@@ -1,31 +0,0 @@
1−{
2− // See https://go.microsoft.com/fwlink/?LinkId=733558
3− // for the documentation about the tasks.json format
4− "version": "2.0.0",
5− "tasks": [
6− {
7− "label": "Generate bridge",
8− "type": "process",
9− "command": "${workspaceFolder}/dev/generate-bridge.sh",
10− "problemMatcher": []
11− },
12− {
13− "label": "Build backend",
14− "type": "process",
15− "command": "${workspaceFolder}/dev/build-backend.sh",
16− "problemMatcher": []
17− },
18− {
19− "label": "Start Chrome",
20− "type": "process",
21− "command": "${workspaceFolder}/dev/start-chrome.sh",
22− "problemMatcher": []
23− },
24− {
25− "label": "Start Elm-live",
26− "type": "process",
27− "command": "${workspaceFolder}/dev/elm-live.sh",
28− "problemMatcher": []
29− }
30− ]
31−}
README.md+8−32
@@ -1,38 +1,14 @@
1−<p align="center">
1+# App Title
2− <img src="logo.png" style="width: 100px;" />
3−</p>
4
2
5−<h1 align="center">Warden</h1>
3+A short description of your app
6
4
7−<p align="center">
5+## License
8− <a href="https://open-store.io/app/soy.iko.warden"><img src="https://open-store.io/badges/en_US.svg" alt="OpenStore" style="width: 150px;" /></a>
9−</p>
10
6
7+Copyright (C) 2022 Your FullName
11
8
12−<p align="center">
9+This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License version 3, as published
13− <img src="screenshots/1.png" style="width: 150px;" />
10+by the Free Software Foundation.
14− <img src="screenshots/2.png" style="width: 150px;" />
15− <img src="screenshots/3.png" style="width: 150px;" />
16− <img src="screenshots/4.png" style="width: 150px;" />
17− <img src="screenshots/5.png" style="width: 150px;" />
18−</p>
19
11
20−Warden is a [Bitwarden](https://bitwarden.com)/[Vaultwarden](https://github.com/dani-garcia/vaultwarden) client for [Ubuntu Touch](https://ubuntu-touch.io).
12+This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
21
13
22−## Features
14+You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.
23−
24−1. View and edit: passwords, contact, secure notes and credit cards
25−2. Generate passwords
26−
27−## Support the project
28−
29−I fund the development of Warden out of my own pocket. If you find the project useful and don't want it to die, please consider donating what you think the app is worth to you.
30−
31−<p align="center">
32− <a href="https://iko.soy/donate"><img src="img/donate.png" alt="Donate" style="width: 150px;" /></a>
33−</p>
34−
35−
36−## Porting
37−
38−Although Warden is intended for ubuntu touch it is written completely in web technologies. This means that, if there is demand, the app can be ported to virtually any platform. The biggest problem will be [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS).
warden.apparmor → appname.apparmorrenamed+1−1
@@ -5,5 +5,5 @@
5 "audio",
5 "audio",
6 "networking"
6 "networking"
7 ],
7 ],
8− "policy_version": 20.04
8+ "policy_version": 16.04
9}
9}
warden.desktop → appname.desktoprenamed

The content is the same.

backend-main.jsdeleted−3
@@ -1,3 +0,0 @@
1−import * as Main from "./backend/Main/index.js"
2−
3−Main.main()
backend/BW.js+54−59
@@ -1,29 +1,24 @@
1−import { StateFactory } from "../../bw/factories/stateFactory";
1+import { StateFactory } from "../../deps/bw/libs/shared/dist/src/factories/stateFactory";
2−import { GlobalState } from "../../bw/models/domain/global-state";
2+import { GlobalState } from "../../deps/bw/libs/shared/dist/src/models/domain/globalState";
3−import { ApiService } from "../../bw/services/api.service";
3+import { ApiService } from "../../deps/bw/libs/shared/dist/src/services/api.service";
4−import { AppIdService } from "../../bw/services/appId.service";
4+import { AppIdService } from "../../deps/bw/libs/shared/dist/src/services/appId.service";
5−import { ConsoleLogService } from "../../bw/services/consoleLog.service";
5+import { ConsoleLogService } from "../../deps/bw/libs/shared/dist/src/services/consoleLog.service";
6−import { EnvironmentService } from "../../bw/services/environment.service";
6+import { EnvironmentService } from "../../deps/bw/libs/shared/dist/src/services/environment.service";
7−import { StateMigrationService } from "../../bw/services/stateMigration.service";
7+import { StateMigrationService } from "../../deps/bw/libs/shared/dist/src/services/stateMigration.service";
8−import { TokenService } from "../../bw/services/token.service";
8+import { TokenService } from "../../deps/bw/libs/shared/dist/src/services/token.service";
9−import { WebCryptoFunctionService } from "../../bw/services/webCryptoFunction.service";
9+import { WebCryptoFunctionService } from "../../deps/bw/libs/shared/dist/src/services/webCryptoFunction.service";
10−import { Account, AccountProfile, AccountTokens } from "../../bw/models/domain/account";
10+import { Account, AccountProfile, AccountTokens } from "../../deps/bw/libs/shared/dist/src/models/domain/account";
11−import {MemoryStorageService} from "../../bw/services/memoryStorage.service"
11+
12−
13−// import BrowserStorageService from "../../../../bw/services/browserStorage.service";
12+// import BrowserStorageService from "../../../../deps/bw/libs/shared/dist/src/services/browserStorage.service";
14−import { CryptoService } from "../../bw/services/crypto.service";
13+import { CryptoService } from "../../deps/bw/libs/shared/dist/src/services/crypto.service";
15−
14+
16−import { I18nService } from "../../bw/services/i18n.service";
15+import { I18nService } from "../../deps/bw/libs/shared/dist/src/services/i18n.service";
17−import { StateService as BaseStateService } from "../../bw/services/state.service";
16+import { StateService as BaseStateService } from "../../deps/bw/libs/shared/dist/src/services/state.service";
18−import { PasswordTokenRequest } from "../../bw/models/request/identity-token/password-token.request";
17+import { PasswordTokenRequest } from "../../deps/bw/libs/shared/dist/src/models/request/identityToken/passwordTokenRequest";
19−import { TokenTwoFactorRequest} from "../../bw/models/request/identity-token/token-two-factor.request";
18+import { TokenRequestTwoFactor } from "../../deps/bw/libs/common/src/models/request/identityToken/tokenRequestTwoFactor.ts";
20−import { CipherRequest } from "../../bw/vault/models/request/cipher.request";
19+import { CipherRequest } from "../../deps/bw/libs/common/src/models/request/cipherRequest";
21−import { CipherCreateRequest } from "../../bw/vault/models/request/cipher-create.request";
22−import { Cipher } from "../../bw/vault/models/domain/cipher";
20+import { Cipher } from "../../deps/bw/libs/common/src/models/domain/cipher";
23−import { CipherData } from "../../bw/vault/models/data/cipher.data";
21+import { CipherData } from "../../deps/bw/libs/common/src/models/data/cipherData";
24−import { PasswordGenerationService } from "../../bw/services/passwordGeneration.service";
25−import { TotpService } from "../../bw/services/totp.service"
26−import { EncryptServiceImplementation } from "../../bw/services/cryptography/encrypt.service.implementation"
27
22
28function sanitize(obj) {
23function sanitize(obj) {
29 return JSON.parse(JSON.stringify(obj));
24 return JSON.parse(JSON.stringify(obj));
@@ -38,7 +33,7 @@ export function getServices() {
38
33
39 await bg.bootstrap()
34 await bg.bootstrap()
40
35
41− await bg.environmentService.setUrls(urls)
36+ bg.environmentService.setUrls(urls)
42
37
43 if (tokenResponse != null) {
38 if (tokenResponse != null) {
44 const accountInformation = await bg.tokenService.decodeToken(tokenResponse.accessToken);
39 const accountInformation = await bg.tokenService.decodeToken(tokenResponse.accessToken);
@@ -75,25 +70,17 @@ export function getServices() {
75 req.email,
70 req.email,
76 req.masterPasswordHash,
71 req.masterPasswordHash,
77 req.captchaResponse,
72 req.captchaResponse,
78− new TokenTwoFactorRequest(
79− req.twoFactor.provider,
80− req.twoFactor.token,
81− req.twoFactor.remember
73+ new TokenRequestTwoFactor(req.twoFactor),
82− ),
83 req.device
74 req.device
84 )
75 )
85 ),
76 ),
86 getSync: () => api.getSync().then(sanitize),
77 getSync: () => api.getSync().then(sanitize),
87− postCipherCreate: (cipher) => api.postCipherCreate(makeCipherCreateRequest(cipher)),
78+ postCipher: (cipher) => api.postCipher(makeCipherRequest(cipher)),
88 putCipher: (cipher) => api.putCipher(cipher.id, makeCipherRequest(cipher)),
79 putCipher: (cipher) => api.putCipher(cipher.id, makeCipherRequest(cipher)),
89− deleteCipher: (id) => api.deleteCipher(id),
90− postTwoFactorEmail: (req) => api.postTwoFactorEmail(req),
91 }
80 }
92 },
81 },
93 crypto: bg.cryptoService,
82 crypto: bg.cryptoService,
94− cryptoFunctions: bg.cryptoFunctionService,
83+ cryptoFunctions: bg.cryptoFunctionService
95− passwordGeneration: bg.passwordGenerationService,
96− totpService: bg.totpService,
97 }
84 }
98}
85}
99
86
@@ -101,10 +88,6 @@ function makeCipherRequest(cipher) {
101 return new CipherRequest(new Cipher(new CipherData(cipher)))
88 return new CipherRequest(new Cipher(new CipherData(cipher)))
102}
89}
103
90
104−function makeCipherCreateRequest(cipher) {
105− return new CipherCreateRequest(new Cipher(new CipherData(cipher)))
106−}
107−
108class MainBackground {
91class MainBackground {
109 // messagingService; //: MessagingServiceAbstraction;
92 // messagingService; //: MessagingServiceAbstraction;
110 // storageService; //: StorageServiceAbstraction;
93 // storageService; //: StorageServiceAbstraction;
@@ -133,7 +116,6 @@ class MainBackground {
133 new StateFactory(GlobalState, Account)
116 new StateFactory(GlobalState, Account)
134 );
117 );
135 this.stateService = new StateService(
118 this.stateService = new StateService(
136− this.storageService,
137 this.storageService,
119 this.storageService,
138 this.storageService,
120 this.storageService,
139 this.logService,
121 this.logService,
@@ -164,13 +146,8 @@ class MainBackground {
164 );
146 );
165 this.i18nService = new I18nService("en");
147 this.i18nService = new I18nService("en");
166 this.cryptoFunctionService = new WebCryptoFunctionService(window);
148 this.cryptoFunctionService = new WebCryptoFunctionService(window);
167− this.encryptService = new EncryptServiceImplementation(
168− this.cryptoFunctionService,
169− this.logService
170− );
171 this.cryptoService = new BrowserCryptoService(
149 this.cryptoService = new BrowserCryptoService(
172 this.cryptoFunctionService,
150 this.cryptoFunctionService,
173− this.encryptService,
174 this.platformUtilsService,
151 this.platformUtilsService,
175 this.logService,
152 this.logService,
176 this.stateService
153 this.stateService
@@ -185,16 +162,6 @@ class MainBackground {
185 this.appIdService,
162 this.appIdService,
186 (expired) => this.logout(expired)
163 (expired) => this.logout(expired)
187 );
164 );
188− this.passwordGenerationService = new PasswordGenerationService(
189− this.cryptoService,
190− this.policyService,
191− this.stateService
192− );
193− this.totpService = new TotpService(
194− this.cryptoFunctionService,
195− this.logService,
196− this.stateService
197− );
198 }
165 }
199
166
200 async bootstrap() {
167 async bootstrap() {
@@ -214,6 +181,34 @@ class MainBackground {
214 }
181 }
215}
182}
216
183
184+class MemoryStorageService {
185+
186+ constructor() {
187+ this.storage = {};
188+ }
189+
190+ async get(key) {
191+ return this.storage[key]
192+ }
193+
194+ async has(key) {
195+ return key in this.storage;
196+ }
197+
198+ async save(key, obj) {
199+ if (obj == null) {
200+ this.remove(key)
201+ return
202+ }
203+
204+ this.storage[key] = obj
205+ }
206+
207+ async remove(key) {
208+ delete this.storage[key]
209+ }
210+}
211+
217class BrowserCryptoService extends CryptoService {
212class BrowserCryptoService extends CryptoService {
218 async retrieveKeyFromStorage(keySuffix) {
213 async retrieveKeyFromStorage(keySuffix) {
219 if (keySuffix === "biometric") {
214 if (keySuffix === "biometric") {
@@ -294,7 +289,7 @@ class BrowserPlatformUtilsService {
294 saveFile(win, blobData, blobOptions, fileName) {
289 saveFile(win, blobData, blobOptions, fileName) {
295 // BrowserApi.downloadFile(win, blobData, blobOptions, fileName);
290 // BrowserApi.downloadFile(win, blobData, blobOptions, fileName);
296 }
291 }
297− getApplicationVersionNumber() {
292+ getApplicationVersion() {
298 // return Promise.resolve(BrowserApi.getApplicationVersion());
293 // return Promise.resolve(BrowserApi.getApplicationVersion());
299 }
294 }
300 supportsWebAuthn(win) {
295 supportsWebAuthn(win) {
backend/BW.purs+30−43
@@ -1,10 +1,8 @@
1module BW where
1module BW where
2
2
3import BW.Types
3import BW.Types
4−import Bridge
5import Prelude
4import Prelude
6
5
7−import Bridge as Bridge
8import Control.Promise (Promise)
6import Control.Promise (Promise)
9import Data.Argonaut (class DecodeJson, class EncodeJson, decodeJson, encodeJson)
7import Data.Argonaut (class DecodeJson, class EncodeJson, decodeJson, encodeJson)
10import Data.ArrayBuffer.Typed as AB
8import Data.ArrayBuffer.Typed as AB
@@ -17,35 +15,24 @@ import Effect (Effect)
17import Effect.Unsafe (unsafePerformEffect)
15import Effect.Unsafe (unsafePerformEffect)
18import Untagged.Union (type (|+|))
16import Untagged.Union (type (|+|))
19
17
20−type TotpService =
21− { getCode :: String -> Promise String
22− , getTimeInterval :: String -> Int
23− }
24−
25−type CryptoService =
18+type CryptoService
26− { makeKey :: Fn4 Password String KDF KdfConfig (Promise SymmetricCryptoKey)
19+ = { makeKey :: Fn4 Password String KDF Int (Promise SymmetricCryptoKey)
27− , hashPassword :: Fn3 Password SymmetricCryptoKey (JNullable HashPurpose) (Promise StringHash)
20+ , hashPassword :: Fn3 Password SymmetricCryptoKey (JNullable HashPurpose) (Promise StringHash)
28− , decryptToUtf8 :: Fn2 EncString SymmetricCryptoKey (Promise String)
21+ , decryptToUtf8 :: Fn2 EncString SymmetricCryptoKey (Promise String)
29− , decryptToBytes :: Fn2 EncString SymmetricCryptoKey (Promise ArrayBuffer)
22+ , decryptToBytes :: Fn2 EncString SymmetricCryptoKey (Promise ArrayBuffer)
30− , encrypt :: Fn2 String SymmetricCryptoKey (Promise EncString)
23+ , encrypt :: Fn2 String SymmetricCryptoKey (Promise EncString)
31− , stretchKey :: SymmetricCryptoKey -> Promise SymmetricCryptoKey
24+ , stretchKey :: SymmetricCryptoKey -> Promise SymmetricCryptoKey
32− }
25+ }
33−
26+
34−type ApiService =
27+type ApiService
35− { postPrelogin :: PreloginRequest -> Promise PreloginResponse
28+ = { postPrelogin :: PreloginRequest -> Promise PreloginResponse
36− , getProfile :: Unit -> Promise ProfileResponse
29+ , getProfile :: Unit -> Promise ProfileResponse
37− -- TODO: There can really be more request and response types. Maybe handle this later.
30+ -- TODO: There can really be more request and response types. Maybe handle this later.
38− , postIdentityToken :: PasswordTokenRequest -> Promise (IdentityCaptchaResponse |+| IdentityTwoFactorResponse |+| IdentityTokenResponse)
31+ , postIdentityToken :: PasswordTokenRequest -> Promise (IdentityCaptchaResponse |+| IdentityTokenResponse)
39− , getSync :: Unit -> Promise SyncResponse
32+ , getSync :: Unit -> Promise SyncResponse
40− , postCipherCreate :: CipherResponse -> Promise CipherResponse
33+ , postCipher :: CipherResponse -> Promise CipherResponse
41− , putCipher :: CipherResponse -> Promise CipherResponse
34+ , putCipher :: CipherResponse -> Promise CipherResponse
42− , deleteCipher :: String -> Promise Unit
43− , postTwoFactorEmail :: TwoFactorEmailRequest -> Promise Unit
44− }
45−
46−type PasswordGeneration =
47− { generatePassword :: Bridge.PasswordGeneratorConfig -> Promise String
48− }
35+ }
49
36
50cryptoFunctionsTypeSha1 = "sha1" :: CryptoFunctionsType
37cryptoFunctionsTypeSha1 = "sha1" :: CryptoFunctionsType
51
38
@@ -55,13 +42,15 @@ cryptoFunctionsTypeSha512 = "sha512" :: CryptoFunctionsType
55
42
56cryptoFunctionsTypeMd5 = "md5" :: CryptoFunctionsType
43cryptoFunctionsTypeMd5 = "md5" :: CryptoFunctionsType
57
44
58−type CryptoFunctionsType = String
45+type CryptoFunctionsType
46+ = String
59
47
60−type CryptoFunctions =
48+type CryptoFunctions
61− { hash :: Fn2 String CryptoFunctionsType (Promise Hash)
49+ = { hash :: Fn2 String CryptoFunctionsType (Promise Hash)
62− }
50+ }
63
51
52+newtype Hash
64−newtype Hash = Hash (ArrayBuffer)
53+ = Hash (ArrayBuffer)
65
54
66hashToArray :: Hash -> Array Int
55hashToArray :: Hash -> Array Int
67hashToArray (Hash buf) =
56hashToArray (Hash buf) =
@@ -84,12 +73,10 @@ instance EncodeJson Hash where
84instance DecodeJson Hash where
73instance DecodeJson Hash where
85 decodeJson = decodeJson >>> map arrayToHash
74 decodeJson = decodeJson >>> map arrayToHash
86
75
87−type Services =
76+type Services
88− { crypto :: CryptoService
77+ = { crypto :: CryptoService
89− , getApi :: Urls -> JNullable IdentityTokenResponse -> Promise ApiService
78+ , getApi :: Urls -> JNullable IdentityTokenResponse -> Promise ApiService
90− , cryptoFunctions :: CryptoFunctions
79+ , cryptoFunctions :: CryptoFunctions
91− , passwordGeneration :: PasswordGeneration
92− , totpService :: TotpService
93− }
80+ }
94
81
95foreign import getServices :: Effect Services
82foreign import getServices :: Effect Services
backend/BW/Logic.purs+237−317
@@ -3,7 +3,6 @@ module BW.Logic where
3import BW
3import BW
4import BW.Types
4import BW.Types
5import Prelude
5import Prelude
6−
7import Bridge as Bridge
6import Bridge as Bridge
8import Control.Monad.Error.Class (throwError)
7import Control.Monad.Error.Class (throwError)
9import Control.Promise (Promise)
8import Control.Promise (Promise)
@@ -13,9 +12,8 @@ import Data.Function.Uncurried (runFn2, runFn3, runFn4)
13import Data.JNullable (JNullable, fromJNullable, jnull, nullify)
12import Data.JNullable (JNullable, fromJNullable, jnull, nullify)
14import Data.JNullable as JNullable
13import Data.JNullable as JNullable
15import Data.JOpt (JOpt(..), fromJOpt)
14import Data.JOpt (JOpt(..), fromJOpt)
16−import Data.Maybe (Maybe(..), fromMaybe, maybe)
15+import Data.Maybe (Maybe(..), fromMaybe)
17import Data.Newtype (class Newtype, unwrap, wrap)
16import Data.Newtype (class Newtype, unwrap, wrap)
18−import Data.Number.Format as Number
19import Data.String as String
17import Data.String as String
20import Data.SymmetricCryptoKey (SymmetricCryptoKey)
18import Data.SymmetricCryptoKey (SymmetricCryptoKey)
21import Data.SymmetricCryptoKey as SymmetricCryptoKey
19import Data.SymmetricCryptoKey as SymmetricCryptoKey
@@ -27,51 +25,43 @@ import Effect.Aff.Class (class MonadAff, liftAff)
27import Effect.Class (liftEffect)
25import Effect.Class (liftEffect)
28import Effect.Exception (error)
26import Effect.Exception (error)
29import Effect.Exception as Exc
27import Effect.Exception as Exc
30−import Effect.Random (random)
31−import Localstorage as Storage
32import Run (Run)
28import Run (Run)
33import Run.Reader (Reader, askAt)
29import Run.Reader (Reader, askAt)
34−import Storage (DeviceIDKey(..))
35import Type.Prelude (Proxy(..))
30import Type.Prelude (Proxy(..))
36import Untagged.Union (type (|+|))
31import Untagged.Union (type (|+|))
37−import Web.Storage.Storage (Storage)
38
32
39-- | Creates the master key
33-- | Creates the master key
40−makePreloginKey
34+makePreloginKey ::
41− :: forall r
35+ forall r.
42− . PreloginResponse
36+ PreloginResponse ->
43− -> Email
37+ Email ->
44− -> Password
38+ Password ->
45− -> Run
39+ Run
40+ ( crypto ::
46− ( crypto :: Reader CryptoService
41+ Reader CryptoService
47− , effect :: Effect
42+ , effect :: Effect
48− , aff :: Aff
43+ , aff :: Aff
49− | r
44+ | r
50− )
45+ )
51− SymmetricCryptoKey
46+ SymmetricCryptoKey
52−makePreloginKey { kdf, kdfIterations, kdfMemory, kdfParallelism } (Email email') password = do
47+makePreloginKey { kdf, kdfIterations } (Email email') password = do
53 crypto <- askAt (Proxy :: _ "crypto")
48 crypto <- askAt (Proxy :: _ "crypto")
54 let
49 let
55 email = (String.trim >>> String.toLower) email'
50 email = (String.trim >>> String.toLower) email'
56− liftPromise $ runFn4 crypto.makeKey password email kdf
51+ liftPromise $ runFn4 crypto.makeKey password email kdf kdfIterations
57− { iterations: kdfIterations
58− , memory: kdfMemory
59− , parallelism: kdfParallelism
60− }
61
52
62−makeDecryptionKey
53+makeDecryptionKey ::
63− :: forall r
54+ forall r.
64− .
65 -- | Matser key (prelogin key)
55 -- | Matser key (prelogin key)
66− SymmetricCryptoKey
56+ SymmetricCryptoKey ->
67− -> EncryptedString
57+ EncryptedString ->
68− -> Run
58+ Run
69− ( crypto :: Reader CryptoService
59+ ( crypto :: Reader CryptoService
70− , effect :: Effect
60+ , effect :: Effect
71− , aff :: Aff
61+ , aff :: Aff
72− | r
62+ | r
73− )
63+ )
74− SymmetricCryptoKey
64+ SymmetricCryptoKey
75makeDecryptionKey masterKey str = do
65makeDecryptionKey masterKey str = do
76 crypto <- askAt (Proxy :: _ "crypto")
66 crypto <- askAt (Proxy :: _ "crypto")
77 let
67 let
@@ -88,145 +78,107 @@ makeDecryptionKey masterKey str = do
88 liftEffect $ Exc.throw $ "Unsupported key encryption type: " <> show encType
78 liftEffect $ Exc.throw $ "Unsupported key encryption type: " <> show encType
89 pure $ SymmetricCryptoKey.fromArrayBuffer key
79 pure $ SymmetricCryptoKey.fromArrayBuffer key
90
80
91−getLogInRequestToken
81+getLogInRequestToken ::
92− :: forall r
82+ forall r.
93− . { prelogin :: PreloginResponse
83+ PreloginResponse ->
94− , email :: Email
84+ Email ->
95− , password :: Password
85+ Password ->
96− , captchaResponse :: Maybe String
86+ Maybe String ->
97− , secondFactor :: Bridge.Cmd_Login_secondFactor_Maybe
98− }
99− -> Run
87+ Run
100− ( api :: Reader ApiService
88+ ( api :: Reader ApiService
101− , crypto :: Reader CryptoService
89+ , crypto :: Reader CryptoService
102− , effect :: Effect
90+ , effect :: Effect
103− , aff :: Aff
91+ , aff :: Aff
104− , storage :: Reader Storage
105− | r
92+ | r
106− )
93+ )
107− (IdentityCaptchaResponse |+| IdentityTwoFactorResponse |+| IdentityTokenResponse)
94+ (IdentityCaptchaResponse |+| IdentityTokenResponse)
108−getLogInRequestToken
95+getLogInRequestToken prelogin email password captchaResponse = do
109− { prelogin
110− , email
111− , password
112− , captchaResponse
113− , secondFactor: Bridge.Cmd_Login_secondFactor_Maybe secondFactor
114− } = do
115 key <- makePreloginKey prelogin email password
96 key <- makePreloginKey prelogin email password
116 crypto <- askAt (Proxy :: _ "crypto")
97 crypto <- askAt (Proxy :: _ "crypto")
117− let
98+ api :: ApiService <- askAt (Proxy :: _ "api")
118− twoFactor =
119− maybe
120− { provider: twoFactorProviderTypeEmail
121− , token: ""
122− , remember: false
123− }
124− ( \(Bridge.Cmd_Login_secondFactor x@{ provider }) ->
125− x { provider = bridgeToSecondFactorType provider }
126− )
127− secondFactor
128 _localHashedPassword <-
99 _localHashedPassword <-
129 liftPromise
100 liftPromise
130 $ runFn3 crypto.hashPassword password key (nullify hashPurposeLocalAuthorization)
101 $ runFn3 crypto.hashPassword password key (nullify hashPurposeLocalAuthorization)
131 StringHash hashedPassword <- liftPromise $ runFn3 crypto.hashPassword password key jnull
102 StringHash hashedPassword <- liftPromise $ runFn3 crypto.hashPassword password key jnull
132− deviceId <- getDeviceID
133− api :: ApiService <- askAt (Proxy :: _ "api")
134 liftPromise
103 liftPromise
135 $ api.postIdentityToken
104 $ api.postIdentityToken
136 { email: email
105 { email: email
137 , masterPasswordHash: hashedPassword
106 , masterPasswordHash: hashedPassword
138 , captchaResponse: fromMaybe "" captchaResponse
107 , captchaResponse: fromMaybe "" captchaResponse
139− , twoFactor
108+ , twoFactor:
109+ { provider: twoFactorProviderTypeEmail
110+ , token: ""
111+ , remember: false
112+ }
140 , device:
113 , device:
141 { type: deviceTypeUnknownBrowser
114 { type: deviceTypeUnknownBrowser
142− , name: "Ubuntu Touch"
115+ , name: "Temporary device name"
143− , identifier: deviceId
116+ , identifier: "42"
144 , pushToken: jnull
117 , pushToken: jnull
145 }
118 }
146 }
119 }
147
120
148−bwPasswordStringHash
121+hashPassword ::
149− :: forall r
122+ forall r.
150− . PreloginResponse
123+ Password ->
151− -> Email
152− -> Password
153− -> Run
124+ Run
154− ( crypto :: Reader CryptoService
155− , effect :: Effect
156− , aff :: Aff
157− | r
158− )
159− StringHash
160−bwPasswordStringHash prelogin email password = do
161− key <- makePreloginKey prelogin email password
162− crypto <- askAt (Proxy :: _ "crypto")
163− _localHashedPassword <-
164− liftPromise
165− $ runFn3 crypto.hashPassword password key (nullify hashPurposeLocalAuthorization)
166− liftPromise $ runFn3 crypto.hashPassword password key jnull
167−
168−hashPassword
169− :: forall r
170− . Password
171− -> Run
172− ( cryptoFunctions :: Reader CryptoFunctions
125+ ( cryptoFunctions :: Reader CryptoFunctions
173− , aff :: Aff
126+ , aff :: Aff
174− , effect :: Effect
127+ , effect :: Effect
175− | r
128+ | r
176− )
129+ )
177− Hash
130+ Hash
178hashPassword (Password password) = do
131hashPassword (Password password) = do
179 cryptoFunctions <- askAt (Proxy :: _ "cryptoFunctions")
132 cryptoFunctions <- askAt (Proxy :: _ "cryptoFunctions")
180 liftPromise $ runFn2 cryptoFunctions.hash password cryptoFunctionsTypeSha512
133 liftPromise $ runFn2 cryptoFunctions.hash password cryptoFunctionsTypeSha512
181
134
182−decrypt
135+decrypt ::
183− :: forall r
136+ forall r.
184− . EncryptedString
137+ EncryptedString ->
185− -> Run
138+ Run
186− ( key :: Reader SymmetricCryptoKey
139+ ( key :: Reader SymmetricCryptoKey
187− , crypto :: Reader CryptoService
140+ , crypto :: Reader CryptoService
188− , aff :: Aff
141+ , aff :: Aff
189− , effect :: Effect
142+ , effect :: Effect
190− | r
143+ | r
191− )
144+ )
192− String
145+ String
193decrypt input = do
146decrypt input = do
194 crypto <- askAt (Proxy :: _ "crypto")
147 crypto <- askAt (Proxy :: _ "crypto")
195 key <- askAt (Proxy :: _ "key")
148 key <- askAt (Proxy :: _ "key")
196 liftPromise $ runFn2 crypto.decryptToUtf8 (EncString.fromString input) key
149 liftPromise $ runFn2 crypto.decryptToUtf8 (EncString.fromString input) key
197
150
198−encrypt
151+encrypt ::
199− :: forall r
152+ forall r.
200− . String
153+ String ->
201− -> Run
154+ Run
202− ( key :: Reader SymmetricCryptoKey
155+ ( key :: Reader SymmetricCryptoKey
203− , crypto :: Reader CryptoService
156+ , crypto :: Reader CryptoService
204− , aff :: Aff
157+ , aff :: Aff
205− , effect :: Effect
158+ , effect :: Effect
206− | r
159+ | r
207− )
160+ )
208− EncryptedString
161+ EncryptedString
209encrypt input = do
162encrypt input = do
210 crypto <- askAt (Proxy :: _ "crypto")
163 crypto <- askAt (Proxy :: _ "crypto")
211 key <- askAt (Proxy :: _ "key")
164 key <- askAt (Proxy :: _ "key")
212 map EncString.toString $ liftPromise $ runFn2 crypto.encrypt input key
165 map EncString.toString $ liftPromise $ runFn2 crypto.encrypt input key
213
166
214−liftPromise :: forall m a. MonadAff m => Promise a -> m a
167+liftPromise ∷ forall m a. MonadAff m ⇒ Promise a → m a
215liftPromise = liftAff <<< Promise.toAff
168liftPromise = liftAff <<< Promise.toAff
216
169
217−encodeCipher
170+encodeCipher ::
218− :: forall r
171+ forall r.
219− . Bridge.FullCipher
172+ Bridge.FullCipher ->
220− -> Run
173+ Run
221− ( key :: Reader SymmetricCryptoKey
174+ ( key :: Reader SymmetricCryptoKey
222− , crypto :: Reader CryptoService
175+ , crypto :: Reader CryptoService
223− , aff :: Aff
176+ , aff :: Aff
224− , effect :: Effect
177+ , effect :: Effect
225− | r
178+ | r
226− )
179+ )
227− CipherResponse
180+ CipherResponse
228−encodeCipher
229− (Bridge.FullCipher { name, cipher, id, favorite, reprompt, collectionIds }) = do
181+encodeCipher (Bridge.FullCipher { name, cipher, id, favorite, reprompt }) = do
230 let
182 let
231 f :: CipherResponse -> Run _ CipherResponse
183 f :: CipherResponse -> Run _ CipherResponse
232 f x = case cipher of
184 f x = case cipher of
@@ -240,14 +192,14 @@ encodeCipher
240 pure
192 pure
241 x
193 x
242 { card =
194 { card =
243− nullify
195+ nullify
244− { cardholderName
196+ { cardholderName
245− , brand
197+ , brand
246− , number
198+ , number
247− , expMonth
199+ , expMonth
248− , expYear
200+ , expYear
249− , code
201+ , code
250− }
202+ }
251 , type = cipherTypeCard
203 , type = cipherTypeCard
252 }
204 }
253 Bridge.IdentityCipher (Bridge.Cipher_IdentityCipher identity) -> do
205 Bridge.IdentityCipher (Bridge.Cipher_IdentityCipher identity) -> do
@@ -272,26 +224,26 @@ encodeCipher
272 pure
224 pure
273 x
225 x
274 { identity =
226 { identity =
275− nullify
227+ nullify
276− { address1
228+ { address1
277− , address2
229+ , address2
278− , address3
230+ , address3
279− , city
231+ , city
280− , company
232+ , company
281− , country
233+ , country
282− , email
234+ , email
283− , firstName
235+ , firstName
284− , lastName
236+ , lastName
285− , licenseNumber
237+ , licenseNumber
286− , middleName
238+ , middleName
287− , passportNumber
239+ , passportNumber
288− , phone
240+ , phone
289− , postalCode
241+ , postalCode
290− , ssn
242+ , ssn
291− , state
243+ , state
292− , title
244+ , title
293− , username
245+ , username
294− }
246+ }
295 , type = cipherTypeIdentity
247 , type = cipherTypeIdentity
296 }
248 }
297 Bridge.LoginCipher (Bridge.Cipher_LoginCipher login) -> do
249 Bridge.LoginCipher (Bridge.Cipher_LoginCipher login) -> do
@@ -299,23 +251,22 @@ encodeCipher
299 uris <-
251 uris <-
300 traverse
252 traverse
301 ( \uri -> do
253 ( \uri -> do
302− uriEnc <- nullify <$> encrypt uri
254+ uriEnc <- encrypt uri
303 pure { uri: uriEnc, match: jnull }
255 pure { uri: uriEnc, match: jnull }
304 )
256 )
305 (unwrap login.uris)
257 (unwrap login.uris)
306 username <- encryptNullable login.username
258 username <- encryptNullable login.username
307− totp <- encryptNullable login.totp
308 pure
259 pure
309 x
260 x
310 { login =
261 { login =
311− nullify
262+ nullify
312− { password
263+ { password
313− , uris: JOpt $ opt uris
264+ , uris: JOpt $ opt uris
314− , username
265+ , username
315− , passwordRevisionDate: jnull
266+ , passwordRevisionDate: jnull
316− , totp: totp
267+ , totp: jnull
317− , autofillOnPageLoad: JOpt undefined
268+ , autofillOnPageLoad: JOpt undefined
318− }
269+ }
319 , type = cipherTypeLogin
270 , type = cipherTypeLogin
320 }
271 }
321 Bridge.NoteCipher note -> do
272 Bridge.NoteCipher note -> do
@@ -345,168 +296,137 @@ encodeCipher
345 , revisionDate: jnull
296 , revisionDate: jnull
346 , attachments: jnull
297 , attachments: jnull
347 , passwordHistory: jnull
298 , passwordHistory: jnull
348− , collectionIds: nullify $ unwrap collectionIds
299+ , collectionIds: jnull
349 , deletedDate: jnull
300 , deletedDate: jnull
350 , reprompt
301 , reprompt
351 }
302 }
352
303
353−-- | Check if the cipher is supported by Warden.
304+decodeCipher ::
354−supportedCipher :: CipherResponse -> Boolean
355−-- We do not support organizations since that requires managing a separate set of
356−-- encryption keys
357−supportedCipher { organizationId } | Just _ <- JNullable.toMaybe organizationId = false
358−supportedCipher _ = true
359−
360−decodeCipher
361− :: forall r
305+ forall r.
362− . CipherResponse
306+ CipherResponse ->
363− -> Run
307+ Run
364− ( key :: Reader SymmetricCryptoKey
308+ ( key :: Reader SymmetricCryptoKey
365− , crypto :: Reader CryptoService
309+ , crypto :: Reader CryptoService
366− , aff :: Aff
310+ , aff :: Aff
367− , effect :: Effect
311+ , effect :: Effect
368− | r
312+ | r
369− )
313+ )
370− (Maybe Bridge.FullCipher)
314+ Bridge.FullCipher
371−decodeCipher cipher | not (supportedCipher cipher) =
372− pure $ Nothing
373decodeCipher cipher = do
315decodeCipher cipher = do
374 name <- decrypt cipher.name
316 name <- decrypt cipher.name
375 cipherType <- case cipher.type of
317 cipherType <- case cipher.type of
376 n
318 n
377 | cipherTypeSecureNote == n -> do
319 | cipherTypeSecureNote == n -> do
378− note <- fromJNullable "" <$> (traverse decrypt cipher.notes)
320+ note <- fromJNullable "" <$> (traverse decrypt cipher.notes)
379− pure $ Bridge.NoteCipher note
321+ pure $ Bridge.NoteCipher note
380 n
322 n
381 | cipherTypeLogin == n -> do
323 | cipherTypeLogin == n -> do
382− case JNullable.toMaybe cipher.login of
324+ case JNullable.toMaybe cipher.login of
383− Nothing -> liftEffect $ throwError $ error "Login data is missing"
325+ Nothing -> liftEffect $ throwError $ error "Login data is missing"
384− Just login -> do
326+ Just login -> do
385− username <- decryptNullable login.username
327+ username <- decryptNullable login.username
386− password <- decryptNullable login.password
328+ password <- decryptNullable login.password
387− totp <- decryptNullable login.totp
388− uris <- fromJOpt [] <$>
389− ((traverse >>> traverse) (_.uri >>> JNullable.toMaybe >>> maybe (pure "") decrypt) login.uris)
329+ uris <- fromJOpt [] <$> ((traverse >>> traverse) (_.uri >>> decrypt) login.uris)
390− pure $ Bridge.LoginCipher
330+ pure $ Bridge.LoginCipher
391− $ Bridge.Cipher_LoginCipher
331+ $ Bridge.Cipher_LoginCipher
392− { username
332+ { username
393− , password
333+ , password
394− , uris: wrap uris
334+ , uris: wrap uris
395− , totp
396− }
335+ }
397 n
336 n
398 | cipherTypeCard == n -> do
337 | cipherTypeCard == n -> do
399− case JNullable.toMaybe cipher.card of
338+ case JNullable.toMaybe cipher.card of
400− Nothing -> liftEffect $ throwError $ error "Card data is missing"
339+ Nothing -> liftEffect $ throwError $ error "Card data is missing"
401− Just card -> do
340+ Just card -> do
402− cardholderName <- decryptNullable card.cardholderName
341+ cardholderName <- decryptNullable card.cardholderName
403− number <- decryptNullable card.number
342+ number <- decryptNullable card.number
404− code <- decryptNullable card.code
343+ code <- decryptNullable card.code
405− brand <- decryptNullable card.brand
344+ brand <- decryptNullable card.brand
406− expMonth <- decryptNullable card.expMonth
345+ expMonth <- decryptNullable card.expMonth
407− expYear <- decryptNullable card.expYear
346+ expYear <- decryptNullable card.expYear
408− pure $ Bridge.CardCipher
347+ pure $ Bridge.CardCipher
409− $ Bridge.Cipher_CardCipher
348+ $ Bridge.Cipher_CardCipher
410− { brand, cardholderName, code, expMonth, expYear, number }
349+ { brand, cardholderName, code, expMonth, expYear, number }
411 n
350 n
412 | cipherTypeIdentity == n -> do
351 | cipherTypeIdentity == n -> do
413− case JNullable.toMaybe cipher.identity of
352+ case JNullable.toMaybe cipher.identity of
414− Nothing -> liftEffect $ throwError $ error "Identity data is missing"
353+ Nothing -> liftEffect $ throwError $ error "Identity data is missing"
415− Just identity -> do
354+ Just identity -> do
416− firstName <- decryptNullable identity.firstName
355+ firstName <- decryptNullable identity.firstName
417− middleName <- decryptNullable identity.middleName
356+ middleName <- decryptNullable identity.middleName
418− lastName <- decryptNullable identity.lastName
357+ lastName <- decryptNullable identity.lastName
419− address1 <- decryptNullable identity.address1
358+ address1 <- decryptNullable identity.address1
420− address2 <- decryptNullable identity.address2
359+ address2 <- decryptNullable identity.address2
421− address3 <- decryptNullable identity.address3
360+ address3 <- decryptNullable identity.address3
422− city <- decryptNullable identity.city
361+ city <- decryptNullable identity.city
423− company <- decryptNullable identity.company
362+ company <- decryptNullable identity.company
424− country <- decryptNullable identity.country
363+ country <- decryptNullable identity.country
425− email <- decryptNullable identity.email
364+ email <- decryptNullable identity.email
426− licenseNumber <- decryptNullable identity.licenseNumber
365+ licenseNumber <- decryptNullable identity.licenseNumber
427− passportNumber <- decryptNullable identity.passportNumber
366+ passportNumber <- decryptNullable identity.passportNumber
428− phone <- decryptNullable identity.phone
367+ phone <- decryptNullable identity.phone
429− postalCode <- decryptNullable identity.postalCode
368+ postalCode <- decryptNullable identity.postalCode
430− ssn <- decryptNullable identity.ssn
369+ ssn <- decryptNullable identity.ssn
431− state <- decryptNullable identity.state
370+ state <- decryptNullable identity.state
432− title <- decryptNullable identity.title
371+ title <- decryptNullable identity.title
433− username <- decryptNullable identity.username
372+ username <- decryptNullable identity.username
434− pure $ Bridge.IdentityCipher
373+ pure $ Bridge.IdentityCipher
435− $ wrap
374+ $ wrap
436− { firstName
375+ { firstName
437− , middleName
376+ , middleName
438− , lastName
377+ , lastName
439− , address1
378+ , address1
440− , address2
379+ , address2
441− , address3
380+ , address3
442− , city
381+ , city
443− , company
382+ , company
444− , country
383+ , country
445− , email
384+ , email
446− , licenseNumber
385+ , licenseNumber
447− , passportNumber
386+ , passportNumber
448− , phone
387+ , phone
449− , postalCode
388+ , postalCode
450− , ssn
389+ , ssn
451− , state
390+ , state
452− , title
391+ , title
453− , username
392+ , username
454− }
393+ }
455 n -> liftEffect $ throwError $ error $ "Unsupported cipher type: " <> show n
394 n -> liftEffect $ throwError $ error $ "Unsupported cipher type: " <> show n
456− pure $ Just
395+ pure
457 $ wrap
396 $ wrap
458 { cipher: cipherType
397 { cipher: cipherType
459 , name
398 , name
460 , id: cipher.id
399 , id: cipher.id
461 , favorite: cipher.favorite
400 , favorite: cipher.favorite
462 , reprompt: cipher.reprompt
401 , reprompt: cipher.reprompt
463− , collectionIds: wrap <<< fromJNullable [] $ cipher.collectionIds
464 }
402 }
465
403
466−decryptNullable
404+decryptNullable ::
467− :: forall x r
405+ forall x r.
468− . Newtype x (Maybe String)
406+ Newtype x (Maybe String) =>
469− => JNullable EncryptedString
407+ JNullable EncryptedString ->
470− -> Run
408+ Run
471− ( key :: Reader SymmetricCryptoKey
409+ ( key :: Reader SymmetricCryptoKey
472− , crypto :: Reader CryptoService
410+ , crypto :: Reader CryptoService
473− , aff :: Aff
411+ , aff :: Aff
474− , effect :: Effect
412+ , effect :: Effect
475− | r
413+ | r
476− )
414+ )
477− x
415+ x
478decryptNullable x = wrap <<< fromJNullable Nothing <<< map Just <$> (traverse decrypt x)
416decryptNullable x = wrap <<< fromJNullable Nothing <<< map Just <$> (traverse decrypt x)
479
417
480−encryptNullable
418+encryptNullable ::
481− :: forall x r
419+ forall x r.
482− . Newtype x (Maybe String)
420+ Newtype x (Maybe String) =>
483− => x
421+ x ->
484− -> Run
485− ( key :: Reader SymmetricCryptoKey
486− , crypto :: Reader CryptoService
487− , aff :: Aff
488− , effect :: Effect
489− | r
490− )
491− (JNullable EncryptedString)
492−encryptNullable x = case unwrap x of
493− Nothing -> pure jnull
494− Just y -> nullify <$> encrypt y
495−
496−getDeviceID ::
497− forall r.
498 Run
422 Run
423+ ( key :: Reader SymmetricCryptoKey
424+ , crypto :: Reader CryptoService
425+ , aff :: Aff
499− ( effect :: Effect
426+ , effect :: Effect
500− , storage :: Reader Storage
501 | r
427 | r
502 )
428 )
503− String
429+ (JNullable EncryptedString)
504−getDeviceID = do
430+encryptNullable x = case unwrap x of
505− storage <- askAt (Proxy :: _ "storage")
431+ Nothing -> pure jnull
506− maybeId <- liftEffect $ Storage.get storage DeviceIDKey
432+ Just y -> nullify <$> encrypt y
507− case maybeId of
508− Just id -> pure id
509− Nothing -> do
510− id <- map Number.toString $ liftEffect $ random
511− liftEffect $ Storage.store storage DeviceIDKey id
512− pure id
backend/BW/Types.purs+390−445
@@ -2,27 +2,14 @@ module BW.Types where
2
2
3import Prelude
3import Prelude
4
4
5−import Bridge as Bridge
6import Data.Argonaut (class DecodeJson, class EncodeJson)
5import Data.Argonaut (class DecodeJson, class EncodeJson)
7−import Data.Either (Either)
8−import Data.Either as Either
9−import Data.Int as Int
10import Data.JNullable (JNullable)
6import Data.JNullable (JNullable)
11import Data.JOpt (JOpt)
7import Data.JOpt (JOpt)
12−import Data.Maybe (Maybe(..))
13−import Data.Nullable (Nullable)
14import Data.ShowableJson (ShowableJson)
8import Data.ShowableJson (ShowableJson)
15import Data.Timestamp (Timestamp)
9import Data.Timestamp (Timestamp)
16−import Data.Traversable (for)
17−import Data.Tuple.Nested ((/\))
18−import Foreign as Foreign
19−import Literals.Undefined (Undefined)
20−import Type.Prelude (Proxy(..))
21−import Untagged.TypeCheck (class HasRuntimeType)
22−import Untagged.Union (type (|+|))
23−import Untagged.Union as Union
24
10
25−newtype EncryptedString = EncryptedString String
11+newtype EncryptedString
12+ = EncryptedString String
26
13
27derive newtype instance Show EncryptedString
14derive newtype instance Show EncryptedString
28derive newtype instance Ord EncryptedString
15derive newtype instance Ord EncryptedString
@@ -31,86 +18,78 @@ derive newtype instance EncodeJson EncryptedString
31derive newtype instance DecodeJson EncryptedString
18derive newtype instance DecodeJson EncryptedString
32
19
33kdfPBKDF2_SHA256 = 0 :: KDF
20kdfPBKDF2_SHA256 = 0 :: KDF
34−kdfArgon2id = 1 :: KDF
35
21
36−type KDF = Int
22+type KDF
37−
23+ = Int
24+
38−type PreloginResponse =
25+type PreloginResponse
39− { kdf :: KDF
26+ = { kdf :: KDF
40− , kdfIterations :: Int
27+ , kdfIterations :: Int
41− , kdfMemory :: JNullable Int
42− , kdfParallelism :: JNullable Int
43− }
44−
45−type KdfConfig =
46− { iterations :: Int
47− , memory :: JNullable Int
48− , parallelism :: JNullable Int
49− }
28+ }
50−
29+
51−type PreloginRequest =
30+type PreloginRequest
52− { email :: Email
31+ = { email :: String
53− }
32+ }
54−
33+
55−type ProfileResponse =
34+type ProfileResponse
56− { id :: String
35+ = { id :: String
57− , name :: JNullable String
36+ , name :: String
58− , email :: String
37+ , email :: String
59− , emailVerified :: Boolean
38+ , emailVerified :: Boolean
60− , masterPasswordHint :: JNullable String
39+ , masterPasswordHint :: JNullable String
61− -- , premiumPersonally :: Boolean
40+ , premiumPersonally :: Boolean
62− -- , premiumFromOrganization :: Boolean
41+ -- , premiumFromOrganization :: Boolean
63− , culture :: JNullable String
42+ , culture :: String
64− , twoFactorEnabled :: Boolean
43+ , twoFactorEnabled :: Boolean
65− , key :: EncryptedString
44+ , key :: EncryptedString
66− , privateKey :: EncryptedString
45+ , privateKey :: EncryptedString
67− , securityStamp :: JNullable String
46+ , securityStamp :: String
68− , forcePasswordReset :: Boolean
47+ , forcePasswordReset :: Boolean
69− , usesKeyConnector :: Boolean
48+ , usesKeyConnector :: Boolean
70− -- , organizations :: Array ProfileOrganizationResponse
49+ , organizations :: Array ProfileOrganizationResponse
71− , providers :: Array ProfileProviderResponse
50+ , providers :: Array ProfileProviderResponse
72− , providerOrganizations :: Array ProfileProviderOrganizationResponse
51+ , providerOrganizations :: Array ProfileProviderOrganizationResponse
73− }
52+ }
74−
53+
75−type ProfileOrganizationResponse =
54+type ProfileOrganizationResponse
76− { id :: String
55+ = { id :: String
77− , name :: JNullable String
56+ , name :: String
78− , usePolicies :: JNullable Boolean
57+ , usePolicies :: Boolean
79− , useGroups :: JNullable Boolean
58+ , useGroups :: Boolean
80− , useDirectory :: JNullable Boolean
59+ , useDirectory :: Boolean
81− , useEvents :: JNullable Boolean
60+ , useEvents :: Boolean
82− , useTotp :: JNullable Boolean
61+ , useTotp :: Boolean
83− , use2fa :: JNullable Boolean
62+ , use2fa :: Boolean
84− , useApi :: JNullable Boolean
63+ , useApi :: Boolean
85− , useSso :: JNullable Boolean
64+ , useSso :: Boolean
86− , useKeyConnector :: JNullable Boolean
65+ , useKeyConnector :: Boolean
87− , useResetPassword :: JNullable Boolean
66+ , useResetPassword :: Boolean
88− , selfHost :: JNullable Boolean
67+ , selfHost :: Boolean
89− , usersGetPremium :: JNullable Boolean
68+ , usersGetPremium :: Boolean
90− , seats :: JNullable Int
69+ , seats :: Int
91− , maxCollections :: JNullable Int
70+ , maxCollections :: Int
92− , maxStorageGb :: JNullable Int
71+ , maxStorageGb :: JNullable Int
93− , key :: JNullable String
72+ , key :: String
94− , hasPublicAndPrivateKeys :: JNullable Boolean
73+ , hasPublicAndPrivateKeys :: Boolean
95− , status :: JNullable OrganizationUserStatusType
74+ , status :: OrganizationUserStatusType
96− , type :: JNullable OrganizationUserType
75+ , type :: OrganizationUserType
97− , enabled :: JNullable Boolean
76+ , enabled :: Boolean
98− , ssoBound :: JNullable Boolean
77+ , ssoBound :: Boolean
99− , identifier :: JNullable String
78+ , identifier :: String
100− , permissions :: JNullable PermissionsApi
79+ , permissions :: PermissionsApi
101− , resetPasswordEnrolled :: JNullable Boolean
80+ , resetPasswordEnrolled :: Boolean
102− , userId :: JNullable String
81+ , userId :: String
103− , providerId :: JNullable String
82+ , providerId :: String
104− , providerName :: JNullable String
83+ , providerName :: String
105− , familySponsorshipFriendlyName :: JNullable String
84+ , familySponsorshipFriendlyName :: String
106− , familySponsorshipAvailable :: JNullable Boolean
85+ , familySponsorshipAvailable :: Boolean
107− , planProductType :: JNullable ProductType
86+ , planProductType :: ProductType
108− , keyConnectorEnabled :: JNullable Boolean
87+ , keyConnectorEnabled :: Boolean
109− , keyConnectorUrl :: JNullable String
88+ , keyConnectorUrl :: String
110− , familySponsorshipLastSyncDate :: JNullable Timestamp
89+ , familySponsorshipLastSyncDate :: JNullable Timestamp
111− , familySponsorshipValidUntil :: JNullable Timestamp
90+ , familySponsorshipValidUntil :: JNullable Timestamp
112− , familySponsorshipToDelete :: JNullable Boolean
91+ , familySponsorshipToDelete :: JNullable Boolean
113− }
92+ }
114
93
115organizationUserStatusTypeInvited = 0 :: OrganizationUserStatusType
94organizationUserStatusTypeInvited = 0 :: OrganizationUserStatusType
116
95
@@ -118,7 +97,8 @@ organizationUserStatusTypeAccepted = 0 :: OrganizationUserStatusType
118
97
119organizationUserStatusTypeConfirmed = 0 :: OrganizationUserStatusType
98organizationUserStatusTypeConfirmed = 0 :: OrganizationUserStatusType
120
99
121−type OrganizationUserStatusType = Int
100+type OrganizationUserStatusType
101+ = Int
122
102
123organizationUserTypeOwner = 0 :: OrganizationUserType
103organizationUserTypeOwner = 0 :: OrganizationUserType
124
104
@@ -130,26 +110,27 @@ organizationUserTypeManager = 3 :: OrganizationUserType
130
110
131organizationUserTypeCustom = 4 :: OrganizationUserType
111organizationUserTypeCustom = 4 :: OrganizationUserType
132
112
133−type OrganizationUserType = Int
113+type OrganizationUserType
134−
114+ = Int
115+
135−type PermissionsApi =
116+type PermissionsApi
136− { accessEventLogs :: Boolean
117+ = { accessEventLogs :: Boolean
137− , accessImportExport :: Boolean
118+ , accessImportExport :: Boolean
138− , accessReports :: Boolean
119+ , accessReports :: Boolean
139− , manageAllCollections :: Boolean
120+ , manageAllCollections :: Boolean
140− , createNewCollections :: Boolean
121+ , createNewCollections :: Boolean
141− , editAnyCollection :: Boolean
122+ , editAnyCollection :: Boolean
142− , deleteAnyCollection :: Boolean
123+ , deleteAnyCollection :: Boolean
143− , manageAssignedCollections :: Boolean
124+ , manageAssignedCollections :: Boolean
144− , editAssignedCollections :: Boolean
125+ , editAssignedCollections :: Boolean
145− , deleteAssignedCollections :: Boolean
126+ , deleteAssignedCollections :: Boolean
146− , manageCiphers :: Boolean
127+ , manageCiphers :: Boolean
147− , manageGroups :: Boolean
128+ , manageGroups :: Boolean
148− , manageSso :: Boolean
129+ , manageSso :: Boolean
149− , managePolicies :: Boolean
130+ , managePolicies :: Boolean
150− , manageUsers :: Boolean
131+ , manageUsers :: Boolean
151− , manageResetPassword :: Boolean
132+ , manageResetPassword :: Boolean
152− }
133+ }
153
134
154productTypeFree = 0 :: ProductType
135productTypeFree = 0 :: ProductType
155
136
@@ -159,19 +140,20 @@ productTypeTeams = 2 :: ProductType
159
140
160productTypeEnterprise = 3 :: ProductType
141productTypeEnterprise = 3 :: ProductType
161
142
162−type ProductType = Int
143+type ProductType
163−
144+ = Int
145+
164−type ProfileProviderResponse =
146+type ProfileProviderResponse
165− { id :: String
147+ = { id :: String
166− , name :: JNullable String
148+ , name :: String
167− , key :: JNullable String
149+ , key :: String
168− , status :: JNullable ProviderUserStatusType
150+ , status :: ProviderUserStatusType
169− , type :: JNullable ProviderUserType
151+ , type :: ProviderUserType
170− , enabled :: Boolean
152+ , enabled :: Boolean
171− , permissions :: PermissionsApi
153+ , permissions :: PermissionsApi
172− , userId :: JNullable String
154+ , userId :: String
173− , useEvents :: Boolean
155+ , useEvents :: Boolean
174− }
156+ }
175
157
176providerUserStatusTypeInvited = 0 :: ProviderUserStatusType
158providerUserStatusTypeInvited = 0 :: ProviderUserStatusType
177
159
@@ -179,79 +161,81 @@ providerUserStatusTypeAccepted = 1 :: ProviderUserStatusType
179
161
180providerUserStatusTypeConfirmed = 2 :: ProviderUserStatusType
162providerUserStatusTypeConfirmed = 2 :: ProviderUserStatusType
181
163
182−type ProviderUserStatusType = Int
164+type ProviderUserStatusType
165+ = Int
183
166
184providerUserTypeProviderAdmin = 0 :: ProviderUserType
167providerUserTypeProviderAdmin = 0 :: ProviderUserType
185
168
186providerUserTypeServiceUser = 1 :: ProviderUserType
169providerUserTypeServiceUser = 1 :: ProviderUserType
187
170
188−type ProviderUserType = Int
171+type ProviderUserType
189−
172+ = Int
173+
190−type ProfileProviderOrganizationResponse =
174+type ProfileProviderOrganizationResponse
191− { id :: String
175+ = { id :: String
192− , name :: JNullable String
176+ , name :: String
193− , usePolicies :: Boolean
177+ , usePolicies :: Boolean
194− , useGroups :: Boolean
178+ , useGroups :: Boolean
195− , useDirectory :: Boolean
179+ , useDirectory :: Boolean
196− , useEvents :: Boolean
180+ , useEvents :: Boolean
197− , useTotp :: Boolean
181+ , useTotp :: Boolean
198− , use2fa :: Boolean
182+ , use2fa :: Boolean
199− , useApi :: Boolean
183+ , useApi :: Boolean
200− , useSso :: Boolean
184+ , useSso :: Boolean
201− , useKeyConnector :: Boolean
185+ , useKeyConnector :: Boolean
202− , useResetPassword :: Boolean
186+ , useResetPassword :: Boolean
203− , selfHost :: Boolean
187+ , selfHost :: Boolean
204− , usersGetPremium :: Boolean
188+ , usersGetPremium :: Boolean
205− , seats :: Int
189+ , seats :: Int
206− , maxCollections :: Int
190+ , maxCollections :: Int
207− , maxStorageGb :: JNullable Int
191+ , maxStorageGb :: JNullable Int
208− , key :: JNullable String
192+ , key :: String
209− , hasPublicAndPrivateKeys :: Boolean
193+ , hasPublicAndPrivateKeys :: Boolean
210− , status :: OrganizationUserStatusType
194+ , status :: OrganizationUserStatusType
211− , type :: OrganizationUserType
195+ , type :: OrganizationUserType
212− , enabled :: Boolean
196+ , enabled :: Boolean
213− , ssoBound :: Boolean
197+ , ssoBound :: Boolean
214− , identifier :: JNullable String
198+ , identifier :: String
215− , permissions :: PermissionsApi
199+ , permissions :: PermissionsApi
216− , resetPasswordEnrolled :: Boolean
200+ , resetPasswordEnrolled :: Boolean
217− , userId :: JNullable String
201+ , userId :: String
218− , providerId :: JNullable String
202+ , providerId :: String
219− , providerName :: JNullable String
203+ , providerName :: String
220− , familySponsorshipFriendlyName :: JNullable String
204+ , familySponsorshipFriendlyName :: String
221− , familySponsorshipAvailable :: Boolean
205+ , familySponsorshipAvailable :: Boolean
222− , planProductType :: ProductType
206+ , planProductType :: ProductType
223− , keyConnectorEnabled :: Boolean
207+ , keyConnectorEnabled :: Boolean
224− , keyConnectorUrl :: JNullable String
208+ , keyConnectorUrl :: String
225− , familySponsorshipLastSyncDate :: JNullable Timestamp
209+ , familySponsorshipLastSyncDate :: JNullable Timestamp
226− , familySponsorshipValidUntil :: JNullable Timestamp
210+ , familySponsorshipValidUntil :: JNullable Timestamp
227− , familySponsorshipToDelete :: JNullable Boolean
211+ , familySponsorshipToDelete :: JNullable Boolean
228− }
212+ }
229−
213+
230−type Urls =
214+type Urls
231− { base :: JNullable String
215+ = { base :: JNullable String
232− , webVault :: JNullable String
216+ , webVault :: JNullable String
233− , api :: JNullable String
217+ , api :: JNullable String
234− , identity :: JNullable String
218+ , identity :: JNullable String
235− , icons :: JNullable String
219+ , icons :: JNullable String
236− , notifications :: JNullable String
220+ , notifications :: JNullable String
237− , events :: JNullable String
221+ , events :: JNullable String
238− , keyConnector :: JNullable String
222+ , keyConnector :: JNullable String
239− }
223+ }
240−
224+
241−type PasswordTokenRequest =
225+type PasswordTokenRequest
242− { email :: Email
226+ = { email :: Email
243− , masterPasswordHash :: String
227+ , masterPasswordHash :: String
244− , captchaResponse :: String
228+ , captchaResponse :: String
245− , twoFactor :: TokenRequestTwoFactor
229+ , twoFactor :: TokenRequestTwoFactor
246− , device :: DeviceRequest
230+ , device :: DeviceRequest
247− }
231+ }
248−
232+
249−type DeviceRequest =
233+type DeviceRequest
250− { type :: DeviceType
234+ = { type :: DeviceType
251− , name :: String
235+ , name :: String
252− , identifier :: String
236+ , identifier :: String
253− , pushToken :: JNullable String
237+ , pushToken :: JNullable String
254− }
238+ }
255
239
256deviceTypeAndroid = 0 :: DeviceType
240deviceTypeAndroid = 0 :: DeviceType
257
241
@@ -295,13 +279,14 @@ deviceTypeVivaldiExtension = 19 :: DeviceType
295
279
296deviceTypeSafariExtension = 20 :: DeviceType
280deviceTypeSafariExtension = 20 :: DeviceType
297
281
298−type DeviceType = Int
282+type DeviceType
283+ = Int
299
284
300−type TokenRequestTwoFactor =
285+type TokenRequestTwoFactor
301− { provider :: TwoFactorProviderType
286+ = { provider :: TwoFactorProviderType
302− , token :: String
287+ , token :: String
303− , remember :: Boolean
288+ , remember :: Boolean
304− }
289+ }
305
290
306twoFactorProviderTypeAuthenticator = 0 :: TwoFactorProviderType
291twoFactorProviderTypeAuthenticator = 0 :: TwoFactorProviderType
307
292
@@ -319,51 +304,24 @@ twoFactorProviderTypeOrganizationDuo = 6 :: TwoFactorProviderType
319
304
320twoFactorProviderTypeWebAuthn = 7 :: TwoFactorProviderType
305twoFactorProviderTypeWebAuthn = 7 :: TwoFactorProviderType
321
306
322−type TwoFactorProviderType = Int
307+type TwoFactorProviderType
323−
308+ = Int
324−secondFactorTypeToBridge
309+
325− :: TwoFactorProviderType
326− -> Maybe Bridge.TwoFactorProviderType
327−secondFactorTypeToBridge x = case x of
328− n
329− | n == twoFactorProviderTypeAuthenticator -> Just Bridge.Authenticator
330− n
331− | n == twoFactorProviderTypeEmail -> Just Bridge.Email
332− n
333− | n == twoFactorProviderTypeDuo -> Just Bridge.Duo
334− n
335− | n == twoFactorProviderTypeYubikey -> Just Bridge.Yubikey
336− n
337− | n == twoFactorProviderTypeU2f -> Just Bridge.U2f
338− n
339− | n == twoFactorProviderTypeRemember -> Just Bridge.Remember
340− n
341− | n == twoFactorProviderTypeOrganizationDuo -> Just Bridge.OrganizationDuo
342− n
343− | n == twoFactorProviderTypeWebAuthn -> Just Bridge.WebAuthn
344− _ -> Nothing
345−
346−bridgeToSecondFactorType :: Bridge.TwoFactorProviderType -> TwoFactorProviderType
347−bridgeToSecondFactorType Bridge.Authenticator = twoFactorProviderTypeAuthenticator
348−bridgeToSecondFactorType Bridge.Email = twoFactorProviderTypeEmail
349−bridgeToSecondFactorType Bridge.Duo = twoFactorProviderTypeDuo
350−bridgeToSecondFactorType Bridge.Yubikey = twoFactorProviderTypeYubikey
351−bridgeToSecondFactorType Bridge.U2f = twoFactorProviderTypeU2f
352−bridgeToSecondFactorType Bridge.Remember = twoFactorProviderTypeRemember
353−bridgeToSecondFactorType Bridge.OrganizationDuo = twoFactorProviderTypeOrganizationDuo
354−bridgeToSecondFactorType Bridge.WebAuthn = twoFactorProviderTypeWebAuthn
355−
356−newtype Password = Password String
310+newtype Password
311+ = Password String
357
312
358hashPurposeServerAuthorization = 1 :: HashPurpose
313hashPurposeServerAuthorization = 1 :: HashPurpose
359
314
360hashPurposeLocalAuthorization = 2 :: HashPurpose
315hashPurposeLocalAuthorization = 2 :: HashPurpose
361
316
362−type HashPurpose = Int
317+type HashPurpose
318+ = Int
363
319
364−newtype StringHash = StringHash String
320+newtype StringHash
321+ = StringHash String
365
322
366−newtype AccessToken = AccessToken String
323+newtype AccessToken
324+ = AccessToken String
367
325
368derive newtype instance Show AccessToken
326derive newtype instance Show AccessToken
369derive newtype instance Eq AccessToken
327derive newtype instance Eq AccessToken
@@ -371,7 +329,8 @@ derive newtype instance Ord AccessToken
371derive newtype instance EncodeJson AccessToken
329derive newtype instance EncodeJson AccessToken
372derive newtype instance DecodeJson AccessToken
330derive newtype instance DecodeJson AccessToken
373
331
374−newtype RefreshToken = RefreshToken String
332+newtype RefreshToken
333+ = RefreshToken String
375
334
376derive newtype instance Show RefreshToken
335derive newtype instance Show RefreshToken
377derive newtype instance Eq RefreshToken
336derive newtype instance Eq RefreshToken
@@ -383,91 +342,75 @@ type IdentityCaptchaResponse =
383 { siteKey :: String
342 { siteKey :: String
384 }
343 }
385
344
386−type IdentityTokenResponse =
345+type IdentityTokenResponse
387− { accessToken :: AccessToken
346+ = { accessToken :: AccessToken
388− , expiresIn :: Int
347+ , expiresIn :: Int
389− , refreshToken :: RefreshToken
348+ , refreshToken :: RefreshToken
390− , tokenType :: String
349+ , tokenType :: String
391− , resetMasterPassword :: Boolean
350+ , resetMasterPassword :: Boolean
392− , privateKey :: EncryptedString
351+ , privateKey :: EncryptedString
393− , key :: EncryptedString
352+ , key :: EncryptedString
394− , twoFactorToken :: JNullable String
353+ , twoFactorToken :: JNullable String
395− , kdf :: KDF
354+ , kdf :: KDF
396− , kdfIterations :: Int
355+ , kdfIterations :: Int
397− , forcePasswordReset :: JNullable Boolean
356+ , forcePasswordReset :: JNullable Boolean
398− , apiUseKeyConnector :: JNullable Boolean
357+ , apiUseKeyConnector :: JNullable Boolean
399− , keyConnectorUrl :: JNullable String
358+ , keyConnectorUrl :: JNullable String
400− }
401−
402−newtype TwoFactorProviderTypes = TwoFactorProviderTypes (Array (Int |+| String))
403−
404−instance HasRuntimeType TwoFactorProviderTypes where
405− hasRuntimeType Proxy = Foreign.isArray
406−
407−fromTwoFactorProviderType :: TwoFactorProviderTypes -> Either String (Array Bridge.TwoFactorProviderType)
408−fromTwoFactorProviderType (TwoFactorProviderTypes xs) =
409− for xs $ \rawProvider -> do
410− providerInt <- Union.reduce (Either.Right /\ (\x -> Either.note x $ Int.fromString x)) rawProvider
411− Either.note (show providerInt) $ secondFactorTypeToBridge providerInt
412−
413−type IdentityTwoFactorResponse =
414− { twoFactorProviders :: TwoFactorProviderTypes
415− -- it is a Map type and untaged unions gets confused
416− -- , twoFactorProviders2 :: Object (Object String)
417− , captchaToken :: String |+| Undefined
418− }
359+ }
419−
360+
420−newtype Email = Email String
361+newtype Email
421−
362+ = Email String
363+
422−type SyncResponse =
364+type SyncResponse
423− { profile :: ProfileResponse
365+ = { profile :: ProfileResponse
424− , folders :: Array FolderResponse
366+ , folders :: Array FolderResponse
425− , collections :: Array CollectionDetailsResponse
367+ , collections :: Array CollectionDetailsResponse
426− , ciphers :: Array CipherResponse
368+ , ciphers :: Array CipherResponse
427− , domains :: JNullable DomainsResponse
369+ , domains :: JNullable DomainsResponse
428− , policies :: JNullable (Array PolicyResponse)
370+ , policies :: JNullable (Array PolicyResponse)
429− , sends :: Array SendResponse
371+ , sends :: Array SendResponse
372+ }
373+
374+type FolderResponse
375+ = { id :: String
376+ , name :: String
377+ , revisionDate :: String
378+ }
379+
380+type CollectionDetailsResponse
381+ = { id :: String
382+ , organizationId :: JNullable String
383+ , name :: String
384+ , externalId :: JNullable String
385+ , readOnly :: Boolean
430− }
386+ }
431
387
432−type FolderResponse =
433− { id :: String
434− , name :: JNullable String
435− , revisionDate :: JNullable String
436− }
437−
438−type CollectionDetailsResponse =
439− { id :: String
440− , organizationId :: JNullable String
441− , name :: JNullable String
442− , externalId :: JNullable String
443− , readOnly :: Boolean
444− }
445
388
446type CipherId = String
389type CipherId = String
447
390
448−type CipherResponse =
391+type CipherResponse
449− { id :: CipherId
392+ = { id :: CipherId
450− , organizationId :: JNullable String
393+ , organizationId :: JNullable String
451− , folderId :: JNullable String
394+ , folderId :: JNullable String
452− , type :: CipherType
395+ , type :: CipherType
453− , name :: EncryptedString
396+ , name :: EncryptedString
454− , notes :: JNullable EncryptedString
397+ , notes :: JNullable EncryptedString
455− , fields :: JNullable (Array FieldApi)
398+ , fields :: JNullable (Array FieldApi)
456− , login :: JNullable LoginApi
399+ , login :: JNullable LoginApi
457− , card :: JNullable CardApi
400+ , card :: JNullable CardApi
458− , identity :: JNullable IdentityApi
401+ , identity :: JNullable IdentityApi
459− , secureNote :: JNullable SecureNoteApi
402+ , secureNote :: JNullable SecureNoteApi
460− , favorite :: Boolean
403+ , favorite :: Boolean
461− , edit :: JNullable Boolean
404+ , edit :: JNullable Boolean
462− , viewPassword :: JNullable Boolean
405+ , viewPassword :: JNullable Boolean
463− , organizationUseTotp :: JNullable Boolean
406+ , organizationUseTotp :: JNullable Boolean
464− , revisionDate :: JNullable Timestamp
407+ , revisionDate :: JNullable Timestamp
465− , attachments :: JNullable (Array AttachmentResponse)
408+ , attachments :: JNullable (Array AttachmentResponse)
466− , passwordHistory :: JNullable (Array PasswordHistoryResponse)
409+ , passwordHistory :: JNullable (Array PasswordHistoryResponse)
467− , collectionIds :: JNullable (Array String)
410+ , collectionIds :: JNullable (Array String)
468− , deletedDate :: JNullable String
411+ , deletedDate :: JNullable String
469− , reprompt :: CipherRepromptType
412+ , reprompt :: CipherRepromptType
470− }
413+ }
471
414
472cipherTypeLogin = 1 :: CipherType
415cipherTypeLogin = 1 :: CipherType
473
416
@@ -477,14 +420,15 @@ cipherTypeCard = 3 :: CipherType
477
420
478cipherTypeIdentity = 4 :: CipherType
421cipherTypeIdentity = 4 :: CipherType
479
422
480−type CipherType = Int
423+type CipherType
424+ = Int
481
425
482−type FieldApi =
426+type FieldApi
483− { name :: JNullable String
427+ = { name :: String
484− , value :: JNullable String
428+ , value :: String
485− , type :: FieldType
429+ , type :: FieldType
486− , linkedId :: JNullable LinkedIdType
430+ , linkedId :: JNullable LinkedIdType
487− }
431+ }
488
432
489fieldTypeText = 0 :: FieldType
433fieldTypeText = 0 :: FieldType
490
434
@@ -494,7 +438,8 @@ fieldTypeBoolean = 2 :: FieldType
494
438
495fieldTypeLinked = 3 :: FieldType
439fieldTypeLinked = 3 :: FieldType
496
440
497−type FieldType = Int
441+type FieldType
442+ = Int
498
443
499linkedIdTypeLoginLinkedIdUsername = 100 :: LinkedIdType
444linkedIdTypeLoginLinkedIdUsername = 100 :: LinkedIdType
500
445
@@ -550,22 +495,23 @@ linkedIdTypeIdentityLinkedIdLastName = 417 :: LinkedIdType
550
495
551linkedIdTypeIdentityLinkedIdFullName = 418 :: LinkedIdType
496linkedIdTypeIdentityLinkedIdFullName = 418 :: LinkedIdType
552
497
553−type LinkedIdType = Int
498+type LinkedIdType
499+ = Int
554
500
555−type LoginApi =
501+type LoginApi
556− { uris :: JOpt (Array LoginUriApi)
502+ = { uris :: JOpt (Array LoginUriApi)
557− , username :: JNullable EncryptedString
503+ , username :: JNullable EncryptedString
558− , password :: JNullable EncryptedString
504+ , password :: JNullable EncryptedString
559− , passwordRevisionDate :: JNullable String
505+ , passwordRevisionDate :: JNullable String
560− , totp :: JNullable EncryptedString
506+ , totp :: JNullable EncryptedString
561− -- This thing is undefined in some cases for some reson. Just ignoring it
507+ -- This thing is undefined in some cases for some reson. Just ignoring it
562− , autofillOnPageLoad :: JOpt (JNullable Boolean)
508+ , autofillOnPageLoad :: JOpt (JNullable Boolean)
563− }
509+ }
564
510
565−type LoginUriApi =
511+type LoginUriApi
566− { uri :: JNullable EncryptedString
512+ = { uri :: EncryptedString
567− , match :: JNullable UriMatchType
513+ , match :: JNullable UriMatchType
568− }
514+ }
569
515
570uriMatchTypeDomain = 0 :: UriMatchType
516uriMatchTypeDomain = 0 :: UriMatchType
571
517
@@ -579,85 +525,87 @@ uriMatchTypeRegularExpression = 4 :: UriMatchType
579
525
580uriMatchTypeNever = 5 :: UriMatchType
526uriMatchTypeNever = 5 :: UriMatchType
581
527
582−type UriMatchType = Int
528+type UriMatchType
583−
529+ = Int
530+
584−type CardApi =
531+type CardApi
585− { cardholderName :: JNullable EncryptedString
532+ = { cardholderName :: JNullable EncryptedString
586− , brand :: JNullable EncryptedString
533+ , brand :: JNullable EncryptedString
587− , number :: JNullable EncryptedString
534+ , number :: JNullable EncryptedString
588− , expMonth :: JNullable EncryptedString
535+ , expMonth :: JNullable EncryptedString
589− , expYear :: JNullable EncryptedString
536+ , expYear :: JNullable EncryptedString
590− , code :: JNullable EncryptedString
537+ , code :: JNullable EncryptedString
591− }
538+ }
592−
539+
593−type IdentityApi =
540+type IdentityApi
594− { title :: JNullable EncryptedString
541+ = { title :: JNullable EncryptedString
595− , firstName :: JNullable EncryptedString
542+ , firstName :: JNullable EncryptedString
596− , middleName :: JNullable EncryptedString
543+ , middleName :: JNullable EncryptedString
597− , lastName :: JNullable EncryptedString
544+ , lastName :: JNullable EncryptedString
598− , address1 :: JNullable EncryptedString
545+ , address1 :: JNullable EncryptedString
599− , address2 :: JNullable EncryptedString
546+ , address2 :: JNullable EncryptedString
600− , address3 :: JNullable EncryptedString
547+ , address3 :: JNullable EncryptedString
601− , city :: JNullable EncryptedString
548+ , city :: JNullable EncryptedString
602− , state :: JNullable EncryptedString
549+ , state :: JNullable EncryptedString
603− , postalCode :: JNullable EncryptedString
550+ , postalCode :: JNullable EncryptedString
604− , country :: JNullable EncryptedString
551+ , country :: JNullable EncryptedString
605− , company :: JNullable EncryptedString
552+ , company :: JNullable EncryptedString
606− , email :: JNullable EncryptedString
553+ , email :: JNullable EncryptedString
607− , phone :: JNullable EncryptedString
554+ , phone :: JNullable EncryptedString
608− , ssn :: JNullable EncryptedString
555+ , ssn :: JNullable EncryptedString
609− , username :: JNullable EncryptedString
556+ , username :: JNullable EncryptedString
610− , passportNumber :: JNullable EncryptedString
557+ , passportNumber :: JNullable EncryptedString
611− , licenseNumber :: JNullable EncryptedString
558+ , licenseNumber :: JNullable EncryptedString
612− }
559+ }
613
560
614secureNoteTypeGeneric = 0 :: SecureNoteType
561secureNoteTypeGeneric = 0 :: SecureNoteType
615
562
616−type SecureNoteType = Int
563+type SecureNoteType
564+ = Int
617
565
618−type SecureNoteApi =
566+type SecureNoteApi = {
619− {
620− -- type :: SecureNoteType
567+ type :: SecureNoteType
621− }
568+}
622
569
623−type AttachmentResponse =
570+type AttachmentResponse
624− { id :: String
571+ = { id :: String
625− , url :: JNullable String
572+ , url :: String
626− , fileName :: JNullable String
573+ , fileName :: String
627− , key :: JNullable String
574+ , key :: String
628− , size :: JNullable String
575+ , size :: String
629− , sizeName :: JNullable String
576+ , sizeName :: String
630− }
577+ }
631
578
632−type PasswordHistoryResponse =
579+type PasswordHistoryResponse
633− { password :: String
580+ = { password :: String
634− , lastUsedDate :: JNullable String
581+ , lastUsedDate :: String
635− }
582+ }
636
583
637cipherRepromptTypeNone = 0 :: CipherRepromptType
584cipherRepromptTypeNone = 0 :: CipherRepromptType
638
585
639cipherRepromptTypePassword = 1 :: CipherRepromptType
586cipherRepromptTypePassword = 1 :: CipherRepromptType
640
587
641−type CipherRepromptType = Int
588+type CipherRepromptType
589+ = Int
642
590
643−type DomainsResponse =
591+type DomainsResponse
644− { equivalentDomains :: Array (Array String)
592+ = { equivalentDomains :: Array (Array String)
645− , globalEquivalentDomains :: Array GlobalDomainResponse
593+ , globalEquivalentDomains :: Array GlobalDomainResponse
646− }
594+ }
647
595
648−type GlobalDomainResponse =
596+type GlobalDomainResponse
649− { type :: Int
597+ = { type :: Int
650− , domains :: Array String
598+ , domains :: Array String
651− , excluded :: Boolean
599+ , excluded :: Boolean
652− }
600+ }
653
601
654−type PolicyResponse =
602+type PolicyResponse
655− { id :: String
603+ = { id :: String
656− , organizationId :: JNullable String
604+ , organizationId :: JNullable String
657− , type :: PolicyType
605+ , type :: PolicyType
658− , data :: ShowableJson
606+ , data :: ShowableJson
659− , enabled :: Boolean
607+ , enabled :: Boolean
660− }
608+ }
661
609
662policyTypeTwoFactorAuthentication = 0 :: PolicyType -- Requires users to have 2fa enabled
610policyTypeTwoFactorAuthentication = 0 :: PolicyType -- Requires users to have 2fa enabled
663
611
@@ -681,46 +629,43 @@ policyTypeMaximumVaultTimeout = 9 :: PolicyType -- Sets the maximum allowed vaul
681
629
682policyTypeDisablePersonalVaultExport = 10 :: PolicyType -- Disable personal vault export
630policyTypeDisablePersonalVaultExport = 10 :: PolicyType -- Disable personal vault export
683
631
684−type PolicyType = Int
632+type PolicyType
685−
633+ = Int
634+
686−type SendResponse =
635+type SendResponse
687− { id :: String
636+ = { id :: String
688− , accessId :: JNullable String
637+ , accessId :: String
689− , type :: SendType
638+ , type :: SendType
690− , name :: JNullable String
639+ , name :: String
691− , notes :: JNullable String
640+ , notes :: JNullable String
692− , file :: JNullable SendFileApi
641+ , file :: JNullable SendFileApi
693− , text :: JNullable SendTextApi
642+ , text :: JNullable SendTextApi
694− , key :: JNullable String
643+ , key :: String
695− , maxAccessCount :: JNullable Int
644+ , maxAccessCount :: JNullable Int
696− , accessCount :: JNullable Int
645+ , accessCount :: Int
697− , revisionDate :: JNullable String
646+ , revisionDate :: String
698− , expirationDate :: JNullable String
647+ , expirationDate :: String
699− , deletionDate :: JNullable String
648+ , deletionDate :: JNullable String
700− , password :: JNullable String
649+ , password :: String
701− , disable :: Boolean
650+ , disable :: Boolean
702− , hideEmail :: Boolean
651+ , hideEmail :: Boolean
703− }
652+ }
704
653
705sendTypeText = 0 :: SendType
654sendTypeText = 0 :: SendType
706
655
707sendTypeFile = 1 :: SendType
656sendTypeFile = 1 :: SendType
708
657
709−type SendType = Int
658+type SendType
710−
659+ = Int
711−type SendFileApi =
712− { id :: String
713− , fileName :: JNullable String
714− , size :: JNullable String
715− , sizeName :: JNullable String
716− }
717
660
718−type SendTextApi =
661+type SendFileApi
662+ = { id :: String
663+ , fileName :: String
719− { text :: JNullable String
664+ , size :: String
720− , hidden :: Boolean
665+ , sizeName :: String
721− }
666+ }
722
667
723−type TwoFactorEmailRequest =
668+type SendTextApi
724− { email :: Email
669+ = { text :: String
725− , masterPasswordHash :: StringHash
670+ , hidden :: Boolean
726− }
671+ }
backend/Bridge.purs+1−158
@@ -362,18 +362,6 @@ derive instance genericCipher_LoginCipher_password_Maybe :: Generic Cipher_Login
362derive instance eqCipher_LoginCipher_password_Maybe :: Eq Cipher_LoginCipher_password_Maybe
362derive instance eqCipher_LoginCipher_password_Maybe :: Eq Cipher_LoginCipher_password_Maybe
363derive instance ordCipher_LoginCipher_password_Maybe :: Ord Cipher_LoginCipher_password_Maybe
363derive instance ordCipher_LoginCipher_password_Maybe :: Ord Cipher_LoginCipher_password_Maybe
364
364
365−newtype Cipher_LoginCipher_totp_Maybe =
366− Cipher_LoginCipher_totp_Maybe (Maybe String)
367−
368−derive instance newtypeCipher_LoginCipher_totp_Maybe :: Newtype Cipher_LoginCipher_totp_Maybe _
369−instance encodeJsonCipher_LoginCipher_totp_Maybe :: EncodeJson Cipher_LoginCipher_totp_Maybe where
370− encodeJson = genericEncodeAeson Argonaut.defaultOptions
371−instance decodeJsonCipher_LoginCipher_totp_Maybe :: DecodeJson Cipher_LoginCipher_totp_Maybe where
372− decodeJson = genericDecodeAeson Argonaut.defaultOptions
373−derive instance genericCipher_LoginCipher_totp_Maybe :: Generic Cipher_LoginCipher_totp_Maybe _
374−derive instance eqCipher_LoginCipher_totp_Maybe :: Eq Cipher_LoginCipher_totp_Maybe
375−derive instance ordCipher_LoginCipher_totp_Maybe :: Ord Cipher_LoginCipher_totp_Maybe
376−
377newtype Cipher_LoginCipher_uris_List =
365newtype Cipher_LoginCipher_uris_List =
378 Cipher_LoginCipher_uris_List (Array String)
366 Cipher_LoginCipher_uris_List (Array String)
379
367
@@ -401,7 +389,6 @@ derive instance ordCipher_LoginCipher_username_Maybe :: Ord Cipher_LoginCipher_u
401newtype Cipher_LoginCipher =
389newtype Cipher_LoginCipher =
402 Cipher_LoginCipher {
390 Cipher_LoginCipher {
403 password :: Cipher_LoginCipher_password_Maybe
391 password :: Cipher_LoginCipher_password_Maybe
404− , totp :: Cipher_LoginCipher_totp_Maybe
405 , uris :: Cipher_LoginCipher_uris_List
392 , uris :: Cipher_LoginCipher_uris_List
406 , username :: Cipher_LoginCipher_username_Maybe
393 , username :: Cipher_LoginCipher_username_Maybe
407 }
394 }
@@ -443,57 +430,10 @@ derive instance genericCipherType :: Generic CipherType _
443derive instance eqCipherType :: Eq CipherType
430derive instance eqCipherType :: Eq CipherType
444derive instance ordCipherType :: Ord CipherType
431derive instance ordCipherType :: Ord CipherType
445
432
446−newtype Cmd_ChooseSecondFactor =
447− Cmd_ChooseSecondFactor {
448− email :: String
449− , factor :: TwoFactorProviderType
450− , password :: String
451− , requestFromServer :: Boolean
452− , server :: String
453− }
454−
455−derive instance newtypeCmd_ChooseSecondFactor :: Newtype Cmd_ChooseSecondFactor _
456−instance encodeJsonCmd_ChooseSecondFactor :: EncodeJson Cmd_ChooseSecondFactor where
457− encodeJson = genericEncodeAeson Argonaut.defaultOptions
458−instance decodeJsonCmd_ChooseSecondFactor :: DecodeJson Cmd_ChooseSecondFactor where
459− decodeJson = genericDecodeAeson Argonaut.defaultOptions
460−derive instance genericCmd_ChooseSecondFactor :: Generic Cmd_ChooseSecondFactor _
461−derive instance eqCmd_ChooseSecondFactor :: Eq Cmd_ChooseSecondFactor
462−derive instance ordCmd_ChooseSecondFactor :: Ord Cmd_ChooseSecondFactor
463−
464−newtype Cmd_Login_secondFactor =
465− Cmd_Login_secondFactor {
466− provider :: TwoFactorProviderType
467− , remember :: Boolean
468− , token :: String
469− }
470−
471−derive instance newtypeCmd_Login_secondFactor :: Newtype Cmd_Login_secondFactor _
472−instance encodeJsonCmd_Login_secondFactor :: EncodeJson Cmd_Login_secondFactor where
473− encodeJson = genericEncodeAeson Argonaut.defaultOptions
474−instance decodeJsonCmd_Login_secondFactor :: DecodeJson Cmd_Login_secondFactor where
475− decodeJson = genericDecodeAeson Argonaut.defaultOptions
476−derive instance genericCmd_Login_secondFactor :: Generic Cmd_Login_secondFactor _
477−derive instance eqCmd_Login_secondFactor :: Eq Cmd_Login_secondFactor
478−derive instance ordCmd_Login_secondFactor :: Ord Cmd_Login_secondFactor
479−
480−newtype Cmd_Login_secondFactor_Maybe =
481− Cmd_Login_secondFactor_Maybe (Maybe Cmd_Login_secondFactor)
482−
483−derive instance newtypeCmd_Login_secondFactor_Maybe :: Newtype Cmd_Login_secondFactor_Maybe _
484−instance encodeJsonCmd_Login_secondFactor_Maybe :: EncodeJson Cmd_Login_secondFactor_Maybe where
485− encodeJson = genericEncodeAeson Argonaut.defaultOptions
486−instance decodeJsonCmd_Login_secondFactor_Maybe :: DecodeJson Cmd_Login_secondFactor_Maybe where
487− decodeJson = genericDecodeAeson Argonaut.defaultOptions
488−derive instance genericCmd_Login_secondFactor_Maybe :: Generic Cmd_Login_secondFactor_Maybe _
489−derive instance eqCmd_Login_secondFactor_Maybe :: Eq Cmd_Login_secondFactor_Maybe
490−derive instance ordCmd_Login_secondFactor_Maybe :: Ord Cmd_Login_secondFactor_Maybe
491−
492newtype Cmd_Login =
433newtype Cmd_Login =
493 Cmd_Login {
434 Cmd_Login {
494 email :: String
435 email :: String
495 , password :: String
436 , password :: String
496− , secondFactor :: Cmd_Login_secondFactor_Maybe
497 , server :: String
437 , server :: String
498 }
438 }
499
439
@@ -507,11 +447,7 @@ derive instance eqCmd_Login :: Eq Cmd_Login
507derive instance ordCmd_Login :: Ord Cmd_Login
447derive instance ordCmd_Login :: Ord Cmd_Login
508
448
509data Cmd =
449data Cmd =
510− ChooseSecondFactor Cmd_ChooseSecondFactor
511− | Copy String
450+ Copy String
512− | CreateCipher FullCipher
513− | DeleteCipher FullCipher
514− | GeneratePassword PasswordGeneratorConfig
515 | Init
451 | Init
516 | Login Cmd_Login
452 | Login Cmd_Login
517 | NeedCiphersList
453 | NeedCiphersList
@@ -519,7 +455,6 @@ data Cmd =
519 | NeedsReset
455 | NeedsReset
520 | Open String
456 | Open String
521 | RequestCipher String
457 | RequestCipher String
522− | RequestTotp String
523 | SendMasterPassword String
458 | SendMasterPassword String
524 | UpdateCipher FullCipher
459 | UpdateCipher FullCipher
525
460
@@ -531,22 +466,9 @@ derive instance genericCmd :: Generic Cmd _
531derive instance eqCmd :: Eq Cmd
466derive instance eqCmd :: Eq Cmd
532derive instance ordCmd :: Ord Cmd
467derive instance ordCmd :: Ord Cmd
533
468
534−newtype FullCipher_collectionIds_List =
535− FullCipher_collectionIds_List (Array String)
536−
537−derive instance newtypeFullCipher_collectionIds_List :: Newtype FullCipher_collectionIds_List _
538−instance encodeJsonFullCipher_collectionIds_List :: EncodeJson FullCipher_collectionIds_List where
539− encodeJson = genericEncodeAeson Argonaut.defaultOptions
540−instance decodeJsonFullCipher_collectionIds_List :: DecodeJson FullCipher_collectionIds_List where
541− decodeJson = genericDecodeAeson Argonaut.defaultOptions
542−derive instance genericFullCipher_collectionIds_List :: Generic FullCipher_collectionIds_List _
543−derive instance eqFullCipher_collectionIds_List :: Eq FullCipher_collectionIds_List
544−derive instance ordFullCipher_collectionIds_List :: Ord FullCipher_collectionIds_List
545−
546newtype FullCipher =
469newtype FullCipher =
547 FullCipher {
470 FullCipher {
548 cipher :: Cipher
471 cipher :: Cipher
549− , collectionIds :: FullCipher_collectionIds_List
550 , favorite :: Boolean
472 , favorite :: Boolean
551 , id :: String
473 , id :: String
552 , name :: String
474 , name :: String
@@ -562,34 +484,6 @@ derive instance genericFullCipher :: Generic FullCipher _
562derive instance eqFullCipher :: Eq FullCipher
484derive instance eqFullCipher :: Eq FullCipher
563derive instance ordFullCipher :: Ord FullCipher
485derive instance ordFullCipher :: Ord FullCipher
564
486
565−newtype PasswordGeneratorConfig =
566− PasswordGeneratorConfig {
567− ambiguous :: Boolean
568− , capitalize :: Boolean
569− , includeNumber :: Boolean
570− , length :: Int
571− , lowercase :: Boolean
572− , minLowercase :: Int
573− , minNumber :: Int
574− , minSpecial :: Int
575− , minUppercase :: Int
576− , numWords :: Int
577− , number :: Boolean
578− , special :: Boolean
579− , type :: String
580− , uppercase :: Boolean
581− , wordSeparator :: String
582− }
583−
584−derive instance newtypePasswordGeneratorConfig :: Newtype PasswordGeneratorConfig _
585−instance encodeJsonPasswordGeneratorConfig :: EncodeJson PasswordGeneratorConfig where
586− encodeJson = genericEncodeAeson Argonaut.defaultOptions
587−instance decodeJsonPasswordGeneratorConfig :: DecodeJson PasswordGeneratorConfig where
588− decodeJson = genericDecodeAeson Argonaut.defaultOptions
589−derive instance genericPasswordGeneratorConfig :: Generic PasswordGeneratorConfig _
590−derive instance eqPasswordGeneratorConfig :: Eq PasswordGeneratorConfig
591−derive instance ordPasswordGeneratorConfig :: Ord PasswordGeneratorConfig
592−
593newtype Sub_LoadCiphers =
487newtype Sub_LoadCiphers =
594 Sub_LoadCiphers {
488 Sub_LoadCiphers {
595 cipherType :: CipherType
489 cipherType :: CipherType
@@ -634,51 +528,18 @@ derive instance genericSub_NeedsMasterPassword :: Generic Sub_NeedsMasterPasswor
634derive instance eqSub_NeedsMasterPassword :: Eq Sub_NeedsMasterPassword
528derive instance eqSub_NeedsMasterPassword :: Eq Sub_NeedsMasterPassword
635derive instance ordSub_NeedsMasterPassword :: Ord Sub_NeedsMasterPassword
529derive instance ordSub_NeedsMasterPassword :: Ord Sub_NeedsMasterPassword
636
530
637−newtype Sub_NeedsSecondFactor_List =
638− Sub_NeedsSecondFactor_List (Array TwoFactorProviderType)
639−
640−derive instance newtypeSub_NeedsSecondFactor_List :: Newtype Sub_NeedsSecondFactor_List _
641−instance encodeJsonSub_NeedsSecondFactor_List :: EncodeJson Sub_NeedsSecondFactor_List where
642− encodeJson = genericEncodeAeson Argonaut.defaultOptions
643−instance decodeJsonSub_NeedsSecondFactor_List :: DecodeJson Sub_NeedsSecondFactor_List where
644− decodeJson = genericDecodeAeson Argonaut.defaultOptions
645−derive instance genericSub_NeedsSecondFactor_List :: Generic Sub_NeedsSecondFactor_List _
646−derive instance eqSub_NeedsSecondFactor_List :: Eq Sub_NeedsSecondFactor_List
647−derive instance ordSub_NeedsSecondFactor_List :: Ord Sub_NeedsSecondFactor_List
648−
649−newtype Sub_Totp =
650− Sub_Totp {
651− code :: String
652− , interval :: Int
653− , source :: String
654− }
655−
656−derive instance newtypeSub_Totp :: Newtype Sub_Totp _
657−instance encodeJsonSub_Totp :: EncodeJson Sub_Totp where
658− encodeJson = genericEncodeAeson Argonaut.defaultOptions
659−instance decodeJsonSub_Totp :: DecodeJson Sub_Totp where
660− decodeJson = genericDecodeAeson Argonaut.defaultOptions
661−derive instance genericSub_Totp :: Generic Sub_Totp _
662−derive instance eqSub_Totp :: Eq Sub_Totp
663−derive instance ordSub_Totp :: Ord Sub_Totp
664−
665data Sub =
531data Sub =
666 CaptchaDone
532 CaptchaDone
667 | CipherChanged FullCipher
533 | CipherChanged FullCipher
668− | CipherDeleted FullCipher
669 | Error String
534 | Error String
670− | GeneratedPassword String
671 | LoadCipher FullCipher
535 | LoadCipher FullCipher
672 | LoadCiphers Sub_LoadCiphers_List
536 | LoadCiphers Sub_LoadCiphers_List
673 | LoginSuccessful
537 | LoginSuccessful
674 | NeedsCaptcha String
538 | NeedsCaptcha String
675 | NeedsLogin
539 | NeedsLogin
676 | NeedsMasterPassword Sub_NeedsMasterPassword
540 | NeedsMasterPassword Sub_NeedsMasterPassword
677− | NeedsSecondFactor Sub_NeedsSecondFactor_List
678 | RecieveEmail String
541 | RecieveEmail String
679 | Reset
542 | Reset
680− | Totp Sub_Totp
681− | WrongPassword
682
543
683instance encodeJsonSub :: EncodeJson Sub where
544instance encodeJsonSub :: EncodeJson Sub where
684 encodeJson = genericEncodeAeson Argonaut.defaultOptions
545 encodeJson = genericEncodeAeson Argonaut.defaultOptions
@@ -688,21 +549,3 @@ derive instance genericSub :: Generic Sub _
688derive instance eqSub :: Eq Sub
549derive instance eqSub :: Eq Sub
689derive instance ordSub :: Ord Sub
550derive instance ordSub :: Ord Sub
690
551
691−data TwoFactorProviderType =
692− Authenticator
693− | Duo
694− | Email
695− | OrganizationDuo
696− | Remember
697− | U2f
698− | WebAuthn
699− | Yubikey
700−
701−instance encodeJsonTwoFactorProviderType :: EncodeJson TwoFactorProviderType where
702− encodeJson = genericEncodeAeson Argonaut.defaultOptions
703−instance decodeJsonTwoFactorProviderType :: DecodeJson TwoFactorProviderType where
704− decodeJson = genericDecodeAeson Argonaut.defaultOptions
705−derive instance genericTwoFactorProviderType :: Generic TwoFactorProviderType _
706−derive instance eqTwoFactorProviderType :: Eq TwoFactorProviderType
707−derive instance ordTwoFactorProviderType :: Ord TwoFactorProviderType
708−
backend/Data/EncString.js+2−1
@@ -1,6 +1,7 @@
1−import { EncString } from "../../bw/models/domain/enc-string"
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 const x = new EncString(str)
5 const x = new EncString(str)
5 return x
6 return x
6}
7}
backend/Data/SymmetricCryptoKey.js+2−1
@@ -1,5 +1,6 @@
1−import { SymmetricCryptoKey } from '../../bw/models/domain/symmetric-crypto-key.js'
1+import { SymmetricCryptoKey } from '../../deps/bw/libs/shared/dist/src/models/domain/symmetricCryptoKey.js';
2
2
3export function fromArrayBuffer(arr) {
3export function fromArrayBuffer(arr) {
4+ console.log("fromArrayBuffer " + arr)
4 return new SymmetricCryptoKey(arr)
5 return new SymmetricCryptoKey(arr)
5}
6}
backend/FFI.js+1−16
@@ -1,21 +1,6 @@
1−function searchForInit(obj) {
2− if (obj.init) {
3− return obj.init
4− } else {
5− for (let key in obj) {
6− if (obj.hasOwnProperty(key)) {
7− const result = searchForInit(obj[key])
8− if (result) {
9− return result
10− }
11− }
12− }
13− }
14−}
15−
16export function startElm(element) {
1export function startElm(element) {
17 return function () {
2 return function () {
18− return searchForInit(Elm)({
3+ return Elm.Main.init({
19 node: document.getElementById(element)
4 node: document.getElementById(element)
20 })
5 })
21 }
6 }
backend/Main.purs+122−225
@@ -3,20 +3,18 @@ module Main
3 ) where
3 ) where
4
4
5import Prelude
5import Prelude
6−
7import BW (ApiService, CryptoFunctions, Services, CryptoService)
6import BW (ApiService, CryptoFunctions, Services, CryptoService)
8import BW as WB
7import BW as WB
9−import BW.Logic (bwPasswordStringHash, decodeCipher, decrypt, encodeCipher, hashPassword, liftPromise, supportedCipher)
8+import BW.Logic (decodeCipher, decrypt, encodeCipher, hashPassword, liftPromise)
10import BW.Logic as Logic
9import BW.Logic as Logic
11−import BW.Types (CipherResponse, Email(..), Password(..), Urls, cipherTypeCard, cipherTypeIdentity, cipherTypeLogin, cipherTypeSecureNote, fromTwoFactorProviderType)
10+import BW.Types (CipherResponse, Email(..), Password(..), Urls, cipherTypeCard, cipherTypeIdentity, cipherTypeLogin, cipherTypeSecureNote)
12import Bridge as Bridge
11import Bridge as Bridge
13−import Control.Monad.Error.Class (catchError)
12+import Control.Monad.Error.Class (catchError, throwError)
14import Data.Argonaut (class DecodeJson)
13import Data.Argonaut (class DecodeJson)
15import Data.Array as Array
14import Data.Array as Array
16import Data.Clipboard as Clipboard
15import Data.Clipboard as Clipboard
17import Data.DateTime (DateTime)
16import Data.DateTime (DateTime)
18import Data.Either (Either(..))
17import Data.Either (Either(..))
19−import Data.Foldable (for_)
20import Data.HCaptcha (bindHCaptchToken)
18import Data.HCaptcha (bindHCaptchToken)
21import Data.JNullable (jnull, nullify)
19import Data.JNullable (jnull, nullify)
22import Data.JNullable as JNullable
20import Data.JNullable as JNullable
@@ -30,7 +28,7 @@ import Effect (Effect)
30import Effect.Aff (Aff, runAff_, try)
28import Effect.Aff (Aff, runAff_, try)
31import Effect.Class (liftEffect)
29import Effect.Class (liftEffect)
32import Effect.Class.Console (log)
30import Effect.Class.Console (log)
33−import Effect.Exception (throw)
31+import Effect.Exception (error)
34import Effect.Exception as Exc
32import Effect.Exception as Exc
35import Effect.Ref as Ref
33import Effect.Ref as Ref
36import Elm as Elm
34import Elm as Elm
@@ -62,19 +60,19 @@ main = do
62 let
60 let
63 runAff act = runElmAff app $ run $ act
61 runAff act = runElmAff app $ run $ act
64
62
65− run
63+ run ::
66− :: forall a
64+ forall a.
67− . Run
65+ Run
68− ( app :: Reader Elm
66+ ( app :: Reader Elm
69− , aff :: Aff
67+ , aff :: Aff
70− , effect :: Effect
68+ , effect :: Effect
71− , crypto :: Reader CryptoService
69+ , crypto :: Reader CryptoService
72− , storage :: Reader Storage
70+ , storage :: Reader Storage
73− , services :: Reader Services
71+ , services :: Reader Services
74− , cryptoFunctions :: Reader CryptoFunctions
72+ , cryptoFunctions :: Reader CryptoFunctions
75− )
73+ )
76− a
74+ a ->
77− -> Run (EFFECT + AFF + ()) a
75+ Run (EFFECT + AFF + ()) a
78 run act =
76 run act =
79 runReaderAt (Proxy :: _ "app") app
77 runReaderAt (Proxy :: _ "app") app
80 $ runReaderAt (Proxy :: _ "crypto") services.crypto
78 $ runReaderAt (Proxy :: _ "crypto") services.crypto
@@ -83,19 +81,19 @@ main = do
83 $ runReaderAt (Proxy :: _ "cryptoFunctions") services.cryptoFunctions
81 $ runReaderAt (Proxy :: _ "cryptoFunctions") services.cryptoFunctions
84 $ act
82 $ act
85
83
86− runWithDecryptionKey
84+ runWithDecryptionKey ::
87− :: Run
85+ Run
88− ( app :: Reader Elm
86+ ( app :: Reader Elm
89− , aff :: Aff
87+ , aff :: Aff
90− , effect :: Effect
88+ , effect :: Effect
91− , key :: Reader SymmetricCryptoKey
89+ , key :: Reader SymmetricCryptoKey
92− , crypto :: Reader CryptoService
90+ , crypto :: Reader CryptoService
93− , storage :: Reader Storage
91+ , storage :: Reader Storage
94− , services :: Reader Services
92+ , services :: Reader Services
95− , cryptoFunctions :: Reader CryptoFunctions
93+ , cryptoFunctions :: Reader CryptoFunctions
96− )
94+ )
97− Unit
95+ Unit ->
98− -> Effect Unit
96+ Effect Unit
99 runWithDecryptionKey act = do
97 runWithDecryptionKey act = do
100 maybeKey <- Ref.read masterKeyRef
98 maybeKey <- Ref.read masterKeyRef
101 runAff
99 runAff
@@ -106,10 +104,8 @@ main = do
106 runReaderAt (Proxy :: _ "key") key $ act
104 runReaderAt (Proxy :: _ "key") key $ act
107 Nothing -> requestMasterPassword
105 Nothing -> requestMasterPassword
108 Elm.subscribe app \cmd -> case cmd of
106 Elm.subscribe app \cmd -> case cmd of
109− Bridge.Login (Bridge.Cmd_Login { email: email', server, password, secondFactor }) -> do
107+ Bridge.Login (Bridge.Cmd_Login { email, server, password }) -> do
110− let
111− email = Email email'
112− captchaResponse <- Ref.read hCaptchaTokenRef
108+ captchaToken <- Ref.read hCaptchaTokenRef
113 Ref.write Nothing hCaptchaTokenRef
109 Ref.write Nothing hCaptchaTokenRef
114 let
110 let
115 urls = baseUrl server
111 urls = baseUrl server
@@ -120,27 +116,18 @@ main = do
120 loginResponse <-
116 loginResponse <-
121 liftAff
117 liftAff
122 $ catchError
118 $ catchError
123− ( do
124− resp <-
125− runBaseAff' $ run $ runReaderAt (Proxy :: _ "api") unauthedApi
119+ ( runBaseAff' $ run $ runReaderAt (Proxy :: _ "api") unauthedApi
126− $ Logic.getLogInRequestToken
120+ $ Logic.getLogInRequestToken prelogin (Email email) (Password password) captchaToken
127− { prelogin
128− , email
129− , password: Password password
130− , captchaResponse
131− , secondFactor
132− }
133− pure $ Right resp
134 )
121 )
135− ( \err -> do
122+ ( const
136− log $ show err
123+ $ liftEffect do
137− pure $ Left unit
124+ Elm.send app Bridge.NeedsLogin
125+ Exc.throw "Could not log in. Please check your data and try again."
138 )
126 )
139− case map (toEither1 >>> map toEither1) $ loginResponse of
127+ case toEither1 loginResponse of
140− Left _ -> send Bridge.WrongPassword
141− Right (Left { siteKey }) -> send $ Bridge.NeedsCaptcha siteKey
128+ Left { siteKey } -> send $ Bridge.NeedsCaptcha siteKey
142− Right (Right (Right token)) -> do
129+ Right token -> do
143− masterKey <- Logic.makePreloginKey prelogin email (Password password)
130+ masterKey <- Logic.makePreloginKey prelogin (Email email) (Password password)
144 api <- liftPromise $ services.getApi urls (nullify token)
131 api <- liftPromise $ services.getApi urls (nullify token)
145 sync <- liftPromise $ api.getSync unit
132 sync <- liftPromise $ api.getSync unit
146 hash <- hashPassword (Password password)
133 hash <- hashPassword (Password password)
@@ -153,20 +140,7 @@ main = do
153 Storage.store storage SyncKey sync
140 Storage.store storage SyncKey sync
154 Storage.store storage TokenKey token
141 Storage.store storage TokenKey token
155 send Bridge.LoginSuccessful
142 send Bridge.LoginSuccessful
156− Right (Right (Left { twoFactorProviders, captchaToken })) -> do
157− case toEither1 captchaToken of
158− Left captchaTokenString ->
159− liftEffect
160− $ Ref.write (Just captchaTokenString) hCaptchaTokenRef
161− Right _ -> pure unit
162− providers <- case fromTwoFactorProviderType twoFactorProviders of
163− Left x -> liftEffect $ throw $ "Unknown second factor provider: " <> x
164− Right x -> pure x
165− send $ Bridge.NeedsSecondFactor $ Bridge.Sub_NeedsSecondFactor_List providers
166− Bridge.NeedCiphersList ->
143+ Bridge.NeedCiphersList -> runWithDecryptionKey performSync
167− runWithDecryptionKey do
168− sendCiphers
169− performSync
170 Bridge.NeedsReset -> do
144 Bridge.NeedsReset -> do
171 WebStorage.clear storage
145 WebStorage.clear storage
172 Elm.send app Bridge.Reset
146 Elm.send app Bridge.Reset
@@ -186,9 +160,9 @@ main = do
186 Bridge.Init -> do
160 Bridge.Init -> do
187 Storage.get storage TokenKey
161 Storage.get storage TokenKey
188 >>= \x ->
162 >>= \x ->
189− Elm.send app case x of
163+ Elm.send app case x of
190− Just _ -> Bridge.LoginSuccessful
164+ Just _ -> Bridge.LoginSuccessful
191− Nothing -> Bridge.NeedsLogin
165+ Nothing -> Bridge.NeedsLogin
192 Bridge.RequestCipher id ->
166 Bridge.RequestCipher id ->
193 runWithDecryptionKey do
167 runWithDecryptionKey do
194 sync <- getOrReset SyncKey
168 sync <- getOrReset SyncKey
@@ -196,7 +170,8 @@ main = do
196 Nothing -> pure unit
170 Nothing -> pure unit
197 Just cipherResponse -> do
171 Just cipherResponse -> do
198 cipher <- decodeCipher cipherResponse
172 cipher <- decodeCipher cipherResponse
199− for_ cipher $ send <<< Bridge.LoadCipher
173+ send $ Bridge.LoadCipher cipher
174+ pure unit
200 pure unit
175 pure unit
201 Bridge.NeedEmail ->
176 Bridge.NeedEmail ->
202 runWithDecryptionKey do
177 runWithDecryptionKey do
@@ -212,79 +187,21 @@ main = do
212 api <- getAuthedApi
187 api <- getAuthedApi
213 cipher <- encodeCipher fullCipher
188 cipher <- encodeCipher fullCipher
214 newCipher <- liftPromise (api.putCipher cipher) >>= decodeCipher
189 newCipher <- liftPromise (api.putCipher cipher) >>= decodeCipher
215− for_ newCipher $ send <<< Bridge.CipherChanged
190+ send $ Bridge.CipherChanged newCipher
216 performSync
191 performSync
217 pure unit
192 pure unit
218− Bridge.GeneratePassword cfg ->
219− runWithDecryptionKey do
220− password <- liftPromise $ services.passwordGeneration.generatePassword cfg
221− send $ Bridge.GeneratedPassword password
222− pure unit
223− Bridge.CreateCipher fullCipher ->
224− runWithDecryptionKey do
225− api <- getAuthedApi
226− cipher <- encodeCipher fullCipher
227− newCipher <- liftPromise (api.postCipherCreate cipher) >>= decodeCipher
228− for_ newCipher $ send <<< Bridge.CipherChanged
229− performSync
230− pure unit
231− Bridge.DeleteCipher c@(Bridge.FullCipher { id }) ->
232− runWithDecryptionKey do
233− api <- getAuthedApi
234− liftPromise $ api.deleteCipher id
235− send $ Bridge.CipherDeleted c
236− performSync
237− pure unit
238− Bridge.RequestTotp totp ->
239− runWithDecryptionKey do
240− { totpService } <- askAt (Proxy :: _ "services")
241− code <- liftPromise $ totpService.getCode totp
242− let
243− interval = totpService.getTimeInterval totp
244− send $ Bridge.Totp
245− $ Bridge.Sub_Totp
246− { interval
247− , code
248− , source: totp
249− }
250− pure unit
251− Bridge.ChooseSecondFactor
252− ( Bridge.Cmd_ChooseSecondFactor
253− { email: email'
254− , factor
255− , server
256− , password
257− , requestFromServer
258− }
259− ) ->
260− when requestFromServer
261− let
262− email = Email email'
263− in
264− case factor of
265− Bridge.Email ->
266− runAff do
267− let
268− urls = baseUrl server
269− unauthedApi <- liftPromise $ services.getApi urls jnull
270− prelogin <- liftPromise $ unauthedApi.postPrelogin { email }
271− masterPasswordHash <- bwPasswordStringHash prelogin email (Password password)
272− liftPromise $ unauthedApi.postTwoFactorEmail { email, masterPasswordHash }
273− pure unit
274− _ -> pure unit
275
193
276−processCipher
194+processCipher ::
277− :: forall r
195+ forall r.
278− . CipherResponse
196+ CipherResponse ->
279− -> Run
197+ Run
280− ( crypto :: Reader CryptoService
198+ ( crypto :: Reader CryptoService
281− , key :: Reader SymmetricCryptoKey
199+ , key :: Reader SymmetricCryptoKey
282− , aff :: Aff
200+ , aff :: Aff
283− , effect :: Effect
201+ , effect :: Effect
284− | r
202+ | r
285− )
203+ )
286− (Maybe { cipher :: Bridge.Sub_LoadCiphers, date :: DateTime })
204+ { cipher :: Bridge.Sub_LoadCiphers, date :: DateTime }
287−processCipher cipher | not (supportedCipher cipher) = pure Nothing
288processCipher cipher = do
205processCipher cipher = do
289 name <- decrypt cipher.name
206 name <- decrypt cipher.name
290 cipherType <- case cipher.type of
207 cipherType <- case cipher.type of
@@ -296,12 +213,10 @@ processCipher cipher = do
296 | n == cipherTypeCard -> pure Bridge.CardType
213 | n == cipherTypeCard -> pure Bridge.CardType
297 n
214 n
298 | n == cipherTypeIdentity -> pure Bridge.IdentityType
215 | n == cipherTypeIdentity -> pure Bridge.IdentityType
299− n -> liftEffect $ throw $ "Unsupported cipher type: " <> show n
216+ n -> liftEffect $ throwError $ error $ "Unsupported cipher type: " <> show n
300 date <- liftEffect $ maybe (pure bottom) (Timestamp.toDateTime) $ JNullable.toMaybe cipher.revisionDate
217 date <- liftEffect $ maybe (pure bottom) (Timestamp.toDateTime) $ JNullable.toMaybe cipher.revisionDate
301 pure
218 pure
302− $ Just
303− $
304− { cipher:
219+ $ { cipher:
305 Bridge.Sub_LoadCiphers
220 Bridge.Sub_LoadCiphers
306 { name: name
221 { name: name
307 , date: maybe "" Timestamp.toLocalDateTimeString $ JNullable.toMaybe cipher.revisionDate
222 , date: maybe "" Timestamp.toLocalDateTimeString $ JNullable.toMaybe cipher.revisionDate
@@ -335,18 +250,18 @@ baseUrl server =
335 , keyConnector: jnull
250 , keyConnector: jnull
336 }
251 }
337
252
338−getOrReset
253+getOrReset ::
339− :: forall k t r
254+ forall k t r.
340− . StorageKey k t
255+ StorageKey k t =>
341− => DecodeJson t
256+ DecodeJson t =>
342− => k
257+ k ->
343− -> Run
258+ Run
344− ( storage :: Reader Storage
259+ ( storage :: Reader Storage
345− , app :: Reader Elm
260+ , app :: Reader Elm
346− , effect :: Effect
261+ , effect :: Effect
347− | r
262+ | r
348− )
263+ )
349− t
264+ t
350getOrReset key = do
265getOrReset key = do
351 storage <- askAt (Proxy :: _ "storage")
266 storage <- askAt (Proxy :: _ "storage")
352 app <- askAt (Proxy :: _ "app")
267 app <- askAt (Proxy :: _ "app")
@@ -357,93 +272,75 @@ getOrReset key = do
357 Exc.throw "Encountered an internal error. Resetting the app."
272 Exc.throw "Encountered an internal error. Resetting the app."
358 liftEffect $ try (Storage.get storage key)
273 liftEffect $ try (Storage.get storage key)
359 >>= \x -> case x of
274 >>= \x -> case x of
360− Left err -> do
275+ Left err -> do
361− log $ show err
276+ log $ show err
362− handleError
277+ handleError
363− Right Nothing -> handleError
278+ Right Nothing -> handleError
364− Right (Just y) -> pure y
279+ Right (Just y) -> pure y
365
280
366−getAuthedApi
281+getAuthedApi ::
367− :: forall r
282+ forall r.
368− . Run
283+ Run
369− ( storage :: Reader Storage
284+ ( storage :: Reader Storage
370− , app :: Reader Elm
285+ , app :: Reader Elm
371− , aff :: Aff
286+ , aff :: Aff
372− , effect :: Effect
287+ , effect :: Effect
373− , services :: Reader Services
288+ , services :: Reader Services
374− | r
289+ | r
375− )
290+ )
376− ApiService
291+ ApiService
377getAuthedApi = do
292getAuthedApi = do
378 services <- askAt (Proxy :: _ "services")
293 services <- askAt (Proxy :: _ "services")
379 token <- getOrReset TokenKey
294 token <- getOrReset TokenKey
380 url <- getOrReset UrlsKey
295 url <- getOrReset UrlsKey
381 liftPromise $ services.getApi (baseUrl url) (nullify token)
296 liftPromise $ services.getApi (baseUrl url) (nullify token)
382
297
383−requestMasterPassword
298+requestMasterPassword ::
384− :: forall r
299+ forall r.
385− . Run
300+ Run
386− ( storage :: Reader Storage
301+ ( storage :: Reader Storage
387− , app :: Reader Elm
302+ , app :: Reader Elm
388− , effect :: Effect
303+ , effect :: Effect
389− | r
304+ | r
390− )
305+ )
391− Unit
306+ Unit
392requestMasterPassword = do
307requestMasterPassword = do
393 url <- getOrReset UrlsKey
308 url <- getOrReset UrlsKey
394 sync <- getOrReset SyncKey
309 sync <- getOrReset SyncKey
395 send $ Bridge.NeedsMasterPassword $ Bridge.Sub_NeedsMasterPassword { server: url, login: sync.profile.email }
310 send $ Bridge.NeedsMasterPassword $ Bridge.Sub_NeedsMasterPassword { server: url, login: sync.profile.email }
396
311
397−send
312+send ::
398− :: forall r
313+ forall r.
399− . Bridge.Sub
314+ Bridge.Sub ->
400− -> Run
315+ Run
401− ( app :: Reader Elm
316+ ( app :: Reader Elm
402− , effect :: Effect
317+ , effect :: Effect
403− | r
318+ | r
404− )
319+ )
405− Unit
320+ Unit
406send sub = do
321send sub = do
407 app <- askAt (Proxy :: _ "app")
322 app <- askAt (Proxy :: _ "app")
408 liftEffect $ Elm.send app sub
323 liftEffect $ Elm.send app sub
409
324
410−performSync
325+performSync ::
411− :: forall r
326+ forall r.
412− . Run
327+ Run
413− ( storage :: Reader Storage
328+ ( storage :: Reader Storage
414− , app :: Reader Elm
329+ , app :: Reader Elm
415− , aff :: Aff
330+ , aff :: Aff
416− , effect :: Effect
331+ , effect :: Effect
417− , services :: Reader Services
332+ , services :: Reader Services
418− , crypto :: Reader CryptoService
333+ , crypto :: Reader CryptoService
419− , key :: Reader SymmetricCryptoKey
334+ , key :: Reader SymmetricCryptoKey
420− | r
335+ | r
421− )
336+ )
422− Unit
337+ Unit
423performSync = do
338performSync = do
424 api <- getAuthedApi
339 api <- getAuthedApi
425 storage <- askAt (Proxy :: _ "storage")
340 storage <- askAt (Proxy :: _ "storage")
426 newSync <- liftPromise $ api.getSync unit
341 newSync <- liftPromise $ api.getSync unit
427 liftEffect $ Storage.store storage SyncKey newSync
342 liftEffect $ Storage.store storage SyncKey newSync
428− sendCiphers
429−
430−sendCiphers
431− :: forall r
432− . Run
433− ( storage :: Reader Storage
434− , app :: Reader Elm
435− , aff :: Aff
436− , effect :: Effect
437− , services :: Reader Services
438− , crypto :: Reader CryptoService
439− , key :: Reader SymmetricCryptoKey
440− | r
441− )
442− Unit
443−sendCiphers = do
444− sync <- getOrReset SyncKey
445− ciphers <- Array.catMaybes <$> traverse processCipher sync.ciphers
343+ ciphers <- traverse processCipher newSync.ciphers
446 let
344 let
447 sortedCiphers = Array.sortWith (_.date >>> Down) ciphers
345 sortedCiphers = Array.sortWith (_.date >>> Down) ciphers
448 send $ Bridge.LoadCiphers $ Bridge.Sub_LoadCiphers_List $ map _.cipher sortedCiphers
346 send $ Bridge.LoadCiphers $ Bridge.Sub_LoadCiphers_List $ map _.cipher sortedCiphers
449− pure unit
backend/Storage.purs+1−7
@@ -2,7 +2,7 @@ module Storage where
2
2
3import Prelude
3import Prelude
4
4
5−import BW (Hash)
5+import BW (Hash(..))
6import BW.Types (IdentityTokenResponse, SyncResponse, PreloginResponse)
6import BW.Types (IdentityTokenResponse, SyncResponse, PreloginResponse)
7import Localstorage (class StorageKey)
7import Localstorage (class StorageKey)
8
8
@@ -35,9 +35,3 @@ data MasterPasswordHashKey
35
35
36instance StorageKey MasterPasswordHashKey Hash where
36instance StorageKey MasterPasswordHashKey Hash where
37 storageKey MasterPasswordHashKey = "master-password-hash"
37 storageKey MasterPasswordHashKey = "master-password-hash"
38−
39−data DeviceIDKey
40− = DeviceIDKey
41−
42−instance StorageKey DeviceIDKey String where
43− storageKey DeviceIDKey = "device-uuid"
bridge.yaml+67−120
@@ -1,159 +1,106 @@
1Sub:
1Sub:
2− - NeedsLogin
2+- NeedsLogin
3− - NeedsCaptcha: String
3+- NeedsCaptcha: String
4− - NeedsSecondFactor: { List: TwoFactorProviderType }
5− - CaptchaDone
4+- CaptchaDone
6− - Error: String
5+- Error: String
7− - LoadCiphers:
6+- LoadCiphers:
8− List:
7+ List:
9 name: String
8 name: String
10 date: String
9 date: String
11 id: String
10 id: String
12 cipherType: CipherType
11 cipherType: CipherType
13− - LoginSuccessful
12+- LoginSuccessful
14− - WrongPassword
15− - NeedsMasterPassword:
13+- NeedsMasterPassword:
16− server: String
14+ server: String
17− login: String
15+ login: String
18− - Reset
16+- Reset
19− - LoadCipher: FullCipher
17+- LoadCipher: FullCipher
20− - RecieveEmail: String
18+- RecieveEmail: String
21− - CipherChanged: FullCipher
19+- CipherChanged: FullCipher
22− - GeneratedPassword: String
23− - CipherDeleted: FullCipher
24− - Totp:
25− source: String
26− code: String
27− interval: Int
28
20
29Cmd:
21Cmd:
30− - Init
22+- Init
31− - Login:
23+- Login:
32− email: String
33− server: String
34− password: String
35− secondFactor:
36− Maybe:
37− provider: TwoFactorProviderType
38− token: String
39− remember: Bool
40− - ChooseSecondFactor:
41− factor: TwoFactorProviderType
42− email: String
24+ email: String
43− server: String
25+ server: String
44− password: String
26+ password: String
45− requestFromServer: Bool
46− - NeedCiphersList
27+- NeedCiphersList
47− - SendMasterPassword: String
28+- SendMasterPassword: String
48− - NeedsReset
29+- NeedsReset
49− - RequestCipher: String
30+- RequestCipher: String
50− - NeedEmail
31+- NeedEmail
51− - Copy: String
32+- Copy: String
52− - Open: String
33+- Open: String
53− - UpdateCipher: FullCipher
34+- UpdateCipher: FullCipher
54− - CreateCipher: FullCipher
55− - GeneratePassword: PasswordGeneratorConfig
56− - DeleteCipher: FullCipher
57− - RequestTotp: String
58
35
59FullCipher:
36FullCipher:
60− reprompt: Int
37+ reprompt: Int
61− favorite: Bool
38+ favorite: Bool
62− id: String
39+ id: String
63− name: String
40+ name: String
64− cipher: Cipher
41+ cipher: Cipher
65− collectionIds: { List: String }
66
42
67Cipher:
43Cipher:
68− - LoginCipher:
44+- LoginCipher:
69− uris: { List: String }
45+ uris: {List: String}
70− username:
46+ username:
71 Maybe: String
47 Maybe: String
72− password:
48+ password:
73 Maybe: String
49 Maybe: String
74− totp:
50+- NoteCipher: String
51+- CardCipher:
52+ cardholderName:
75 Maybe: String
53 Maybe: String
76− - NoteCipher: String
54+ brand:
77− - CardCipher:
78− cardholderName:
79 Maybe: String
55 Maybe: String
80− brand:
56+ number:
81 Maybe: String
57 Maybe: String
82− number:
58+ expMonth:
83 Maybe: String
59 Maybe: String
84− expMonth:
60+ expYear:
85 Maybe: String
61 Maybe: String
86− expYear:
62+ code:
87 Maybe: String
63 Maybe: String
88− code:
64+- IdentityCipher:
65+ title:
89 Maybe: String
66 Maybe: String
90− - IdentityCipher:
67+ firstName:
91− title:
92 Maybe: String
68 Maybe: String
93− firstName:
69+ middleName:
94 Maybe: String
70 Maybe: String
95− middleName:
71+ lastName:
96 Maybe: String
72 Maybe: String
97− lastName:
73+ address1:
98 Maybe: String
74 Maybe: String
99− address1:
75+ address2:
100 Maybe: String
76 Maybe: String
101− address2:
77+ address3:
102 Maybe: String
78 Maybe: String
103− address3:
79+ city:
104 Maybe: String
80 Maybe: String
105− city:
81+ state:
106 Maybe: String
82 Maybe: String
107− state:
83+ postalCode:
108 Maybe: String
84 Maybe: String
109− postalCode:
85+ country:
110 Maybe: String
86 Maybe: String
111− country:
87+ company:
112 Maybe: String
88 Maybe: String
113− company:
89+ email:
114 Maybe: String
90 Maybe: String
115− email:
91+ phone:
116 Maybe: String
92 Maybe: String
117− phone:
93+ ssn:
118 Maybe: String
94 Maybe: String
119− ssn:
95+ username:
120 Maybe: String
96 Maybe: String
121− username:
97+ passportNumber:
122 Maybe: String
98 Maybe: String
123− passportNumber:
124− Maybe: String
125− licenseNumber:
99+ licenseNumber:
126 Maybe: String
100 Maybe: String
127
101
128CipherType:
102CipherType:
129− - LoginType
103+- LoginType
130− - CardType
104+- CardType
131− - NoteType
105+- NoteType
132− - IdentityType
106+- IdentityType
133−
134−PasswordGeneratorConfig:
135− length: Int
136− ambiguous: Bool
137− number: Bool
138− minNumber: Int
139− uppercase: Bool
140− minUppercase: Int
141− lowercase: Bool
142− minLowercase: Int
143− special: Bool
144− minSpecial: Int
145− type: String
146− numWords: Int
147− wordSeparator: String
148− capitalize: Bool
149− includeNumber: Bool
150−
151−TwoFactorProviderType:
152− - Authenticator
153− - Email
154− - Duo
155− - Yubikey
156− - U2f
157− - Remember
158− - OrganizationDuo
159− - WebAuthn
build.sh+10−2
@@ -1,5 +1,13 @@
1−#!/bin/sh
1+#!/bin/bash
2
2
3set -e
3set -e
4
4
5−cp -r "$SRC_DIR/dist"/* "$INSTALL_DIR"
5+TEMP=$(mktemp -d)
6+
7+cp -r "$SRC_DIR"/{vanilla,appname.apparmor,appname.desktop,manifest.json,logo.png,index.html,style.css} "$INSTALL_DIR"
8+
9+elm make "$SRC_DIR/frontend/Main.elm" --optimize --output="$TEMP/elm.js"
10+
11+terser "$TEMP/elm.js" --ecma 2018 --compress 'pure_funcs=[F2,F3,F4,F5,F6,F7,F8,F9,A2,A3,A4,A5,A6,A7,A8,A9],pure_getters,keep_fargs=false,unsafe_comps,unsafe' | terser --ecma 2018 --mangle --compress --output "$INSTALL_DIR/elm.js"
12+
13+(cd "$SRC_DIR" && spago bundle-app --minify --to "$INSTALL_DIR/backend.js")
clickable.yaml+10−2
@@ -1,8 +1,16 @@
1clickable_minimum_required: 7.1.2
1clickable_minimum_required: 7.1.2
2−framework: ubuntu-sdk-20.04
3builder: custom
2builder: custom
4build: $SRC_DIR/build.sh
3build: $SRC_DIR/build.sh
5kill: webapp-container*
4kill: webapp-container*
6−docker_image: clickable/amd64-20.04-amd64
5+image_setup:
6+ env: {}
7+ # DEB_URL: http://archive.ubuntu.com/ubuntu
8+ # HOST_ARCH: amd64
9+
10+ # PATH: /opt/someprogram/bin:$PATH
11+ run:
12+
13+ - (cd /tmp && curl -L -o elm.gz https://github.com/elm/compiler/releases/download/0.19.1/binary-for-linux-64-bit.gz && gunzip elm.gz && chmod +x elm && mv elm /usr/local/bin/)
14+ - npm install -g spago esbuild purescript terser --unsafe-perm=true
7
15
8restrict_arch: all
16restrict_arch: all
deps/bwnew file+1

Submodule changed.

dev/build-backend.sh+1−7
@@ -1,9 +1,3 @@
1#!/bin/sh
1#!/bin/sh
2
2
3−set -e
3+spago bundle-app --to backend.js
4−
5−spago build
6−
7−export out="$(pwd)/backend.js"
8−
9−$(nix build .#backendBuildScript --print-out-paths) "$(pwd)/output"
dev/build-bw.sh−2
@@ -1,7 +1,5 @@
1#!/bin/sh
1#!/bin/sh
2
2
3−set -e
4−
5(cd deps/bw && npm install)
3(cd deps/bw && npm install)
6
4
7(cd deps/bw/libs/common/ && npm run clean && npm exec -c "tsc -m es2020 -t es2018")
5(cd deps/bw/libs/common/ && npm run clean && npm exec -c "tsc -m es2020 -t es2018")
dev/build.shdeleted−10
@@ -1,10 +0,0 @@
1−#!/bin/sh
2−
3−
4−rm -r dist
5−
6−mkdir -p dist
7−
8−cp -r "$(nix build ".#warden-click-src" --print-out-paths)"/* dist
9−
10−chmod -R 777 dist
dev/elm-live.sh+1−5
@@ -1,7 +1,3 @@
1#!/bin/sh
1#!/bin/sh
2
2
3−set -e
4−
5−nix build .#vanilla -o vanilla
6−
7−nix run github:NixOS/nixpkgs?rev=d9a1414346059619d9e13ab93e749bbb82e5252a#elmPackages.elm-live -- $1 --start-page=index.html --proxy-prefix=/api --proxy-host=http://localhost:6009 -- --debug --output=elm.js
3+elm-live frontend/Main.elm --start-page=index.html --proxy-prefix=/api --proxy-host=http://localhost:6009 --pushstate -- --debug --output=elm.js
dev/elm2nix.shdeleted−8
@@ -1,8 +0,0 @@
1−#!/bin/sh
2−
3−set -e
4−
5−cd "$(dirname "$0")/.."
6−
7−nix run github:NixOS/nixpkgs#elm2nix -- snapshot
8−nix run github:NixOS/nixpkgs#elm2nix -- convert > elm-srcs.nix
dev/generate-bridge.sh+1−1
@@ -1,3 +1,3 @@
1#!/bin/sh
1#!/bin/sh
2
2
3−nix run github:ilyakooo0/elm-ps-bridge -- --input bridge.yaml --elmOutput=frontend/Bridge.elm --psOutput=backend/Bridge.purs
3+elm-ps-bridge --input bridge.yaml --elmOutput=Frontend/Bridge.elm --psOutput=backend/Bridge.purs
dev/record-test.shdeleted−11
@@ -1,11 +0,0 @@
1−#!/bin/sh
2−
3−set -e
4−
5−cd tests
6−
7−npm install
8−
9−export RECORD_TEST=true
10−
11−npx playwright test
dev/spago2nix.shdeleted−3
@@ -1,3 +0,0 @@
1−#!/bin/sh
2−
3−nix run "github:justinwoo/spago2nix#spago2nix" -- generate
dev/start-chrome.sh+1−7
@@ -1,9 +1,3 @@
1#!/bin/sh
1#!/bin/sh
2
2
3−if test -x "$(which chromium)"; then
4− chromium --args --user-data-dir="/tmp/chrome_dev_test" --disable-web-security
5−elif test -x /Applications/Chromium.app/Contents/MacOS/Chromium; then
3+/Applications/Chromium.app/Contents/MacOS/Chromium --args --user-data-dir="/tmp/chrome_dev_test" --disable-web-security
6− /Applications/Chromium.app/Contents/MacOS/Chromium --args --user-data-dir="/tmp/chrome_dev_test" --disable-web-security
7−else
8− flatpak run com.github.Eloston.UngoogledChromium --args --user-data-dir="/tmp/chrome_dev_test" --disable-web-security
9−fi
dev/test.shdeleted−9
@@ -1,9 +0,0 @@
1−#!/bin/sh
2−
3−set -e
4−
5−cd tests
6−
7−npm install
8−
9−npx playwright test
dev/update-tests.shdeleted−9
@@ -1,9 +0,0 @@
1−#!/bin/sh
2−
3−set -e
4−
5−cd tests
6−
7−npm install
8−
9−npx playwright test --update-snapshots
elm-srcs.nixdeleted−62
@@ -1,62 +0,0 @@
1−{
2−
3− "FabienHenon/elm-infinite-scroll" = {
4− sha256 = "0vhh7j3lna05l5hwjazag52lbkjv11s80jj62m9wb53qxkarw948";
5− version = "3.0.3";
6− };
7−
8− "bartavelle/json-helpers" = {
9− sha256 = "0k96qra2nq1j4j4ahfl98dkpkc6f2831mq5d5xxg27mp31qwq5nn";
10− version = "2.0.2";
11− };
12−
13− "elm/browser" = {
14− sha256 = "0nagb9ajacxbbg985r4k9h0jadqpp0gp84nm94kcgbr5sf8i9x13";
15− version = "1.0.2";
16− };
17−
18− "elm/core" = {
19− sha256 = "19w0iisdd66ywjayyga4kv2p1v9rxzqjaxhckp8ni6n8i0fb2dvf";
20− version = "1.0.5";
21− };
22−
23− "elm/html" = {
24− sha256 = "1n3gpzmpqqdsldys4ipgyl1zacn0kbpc3g4v3hdpiyfjlgh8bf3k";
25− version = "1.0.0";
26− };
27−
28− "elm/json" = {
29− sha256 = "0kjwrz195z84kwywaxhhlnpl3p251qlbm5iz6byd6jky2crmyqyh";
30− version = "1.1.3";
31− };
32−
33− "elm/time" = {
34− sha256 = "0vch7i86vn0x8b850w1p69vplll1bnbkp8s383z7pinyg94cm2z1";
35− version = "1.0.0";
36− };
37−
38− "mdgriffith/elm-ui" = {
39− sha256 = "0ffcqv4a4ad400hwp824m3qq4jy82cqp5ghmhp1m0q7n004z6kgv";
40− version = "1.1.8";
41− };
42−
43− "mgold/elm-nonempty-list" = {
44− sha256 = "02d3xgasgy5knh02rgl403z9hwlbyrsnp9wavc2lhkxxa2misrgl";
45− version = "4.2.0";
46− };
47−
48− "elm/random" = {
49− sha256 = "138n2455wdjwa657w6sjq18wx2r0k60ibpc4frhbqr50sncxrfdl";
50− version = "1.0.0";
51− };
52−
53− "elm/url" = {
54− sha256 = "0av8x5syid40sgpl5vd7pry2rq0q4pga28b4yykn9gd9v12rs3l4";
55− version = "1.0.0";
56− };
57−
58− "elm/virtual-dom" = {
59− sha256 = "1yvb8px2z62xd578ag2q0r5hd1vkz9y7dfkx05355iiy1d7jwq4v";
60− version = "1.0.3";
61− };
62−}
elm.json−1
@@ -13,7 +13,6 @@
13 "elm/html": "1.0.0",
13 "elm/html": "1.0.0",
14 "elm/json": "1.1.3",
14 "elm/json": "1.1.3",
15 "elm/time": "1.0.0",
15 "elm/time": "1.0.0",
16− "mdgriffith/elm-ui": "1.1.8",
17 "mgold/elm-nonempty-list": "4.2.0"
16 "mgold/elm-nonempty-list": "4.2.0"
18 },
17 },
19 "indirect": {
18 "indirect": {
flake.lockdeleted−177
@@ -1,177 +0,0 @@
1−{
2− "nodes": {
3− "bw": {
4− "flake": false,
5− "locked": {
6− "lastModified": 1675446731,
7− "narHash": "sha256-IbFjYNYZep6W9c+dztJ9CXrwQ0cep3EKb6qnEvv4pj4=",
8− "owner": "bitwarden",
9− "repo": "clients",
10− "rev": "ff143760d4f527f2349333ee4b3004eeda68fac5",
11− "type": "github"
12− },
13− "original": {
14− "owner": "bitwarden",
15− "repo": "clients",
16− "rev": "ff143760d4f527f2349333ee4b3004eeda68fac5",
17− "type": "github"
18− }
19− },
20− "easy-ps": {
21− "flake": false,
22− "locked": {
23− "lastModified": 1671011575,
24− "narHash": "sha256-tESal32bcqqdZO+aKnBzc1GoL2mtnaDtj2y7ociCRGA=",
25− "owner": "justinwoo",
26− "repo": "easy-purescript-nix",
27− "rev": "11d3bd58ce6e32703bf69cec04dc7c38eabe14ba",
28− "type": "github"
29− },
30− "original": {
31− "owner": "justinwoo",
32− "repo": "easy-purescript-nix",
33− "type": "github"
34− }
35− },
36− "easy-purescript-nix": {
37− "flake": false,
38− "locked": {
39− "lastModified": 1670198178,
40− "narHash": "sha256-5KkyNpPakv4xIP2ba0S5GX+dcmd3AcO9kPhwa482BbA=",
41− "owner": "justinwoo",
42− "repo": "easy-purescript-nix",
43− "rev": "7a4cb3cd6ca53566ea1675692eab0aa13907ff09",
44− "type": "github"
45− },
46− "original": {
47− "owner": "justinwoo",
48− "repo": "easy-purescript-nix",
49− "type": "github"
50− }
51− },
52− "flake-utils": {
53− "locked": {
54− "lastModified": 1676283394,
55− "narHash": "sha256-XX2f9c3iySLCw54rJ/CZs+ZK6IQy7GXNY4nSOyu2QG4=",
56− "owner": "numtide",
57− "repo": "flake-utils",
58− "rev": "3db36a8b464d0c4532ba1c7dda728f4576d6d073",
59− "type": "github"
60− },
61− "original": {
62− "owner": "numtide",
63− "repo": "flake-utils",
64− "type": "github"
65− }
66− },
67− "flake-utils_2": {
68− "locked": {
69− "lastModified": 1667395993,
70− "narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=",
71− "owner": "numtide",
72− "repo": "flake-utils",
73− "rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f",
74− "type": "github"
75− },
76− "original": {
77− "owner": "numtide",
78− "repo": "flake-utils",
79− "type": "github"
80− }
81− },
82− "nix-filter": {
83− "locked": {
84− "lastModified": 1676294984,
85− "narHash": "sha256-hdLUa/3RH1VJ+gMUysQE0JGM4F2Q/tIIFbtoxAOurJQ=",
86− "owner": "numtide",
87− "repo": "nix-filter",
88− "rev": "fc282c5478e4141842f9644c239a41cfe9586732",
89− "type": "github"
90− },
91− "original": {
92− "owner": "numtide",
93− "repo": "nix-filter",
94− "type": "github"
95− }
96− },
97− "nixpkgs": {
98− "locked": {
99− "lastModified": 1677080879,
100− "narHash": "sha256-0SjW4/d3Rkw6C7hHZ5lxT4r6Pw9vzQb6Il6zYWwe2Bo=",
101− "owner": "NixOS",
102− "repo": "nixpkgs",
103− "rev": "f5dad40450d272a1ea2413f4a67ac08760649e89",
104− "type": "github"
105− },
106− "original": {
107− "id": "nixpkgs",
108− "type": "indirect"
109− }
110− },
111− "nixpkgs_2": {
112− "locked": {
113− "lastModified": 1670507980,
114− "narHash": "sha256-riNZa0xzM1it3pzxciwALeMs+0CsBMWIW2FqulzK8vM=",
115− "owner": "NixOS",
116− "repo": "nixpkgs",
117− "rev": "2787fc7d1e51404678614bf0fe92fc296746eec0",
118− "type": "github"
119− },
120− "original": {
121− "owner": "NixOS",
122− "ref": "nixos-unstable",
123− "repo": "nixpkgs",
124− "type": "github"
125− }
126− },
127− "root": {
128− "inputs": {
129− "bw": "bw",
130− "easy-ps": "easy-ps",
131− "flake-utils": "flake-utils",
132− "nix-filter": "nix-filter",
133− "nixpkgs": "nixpkgs",
134− "spago2nix": "spago2nix",
135− "vanilla": "vanilla"
136− }
137− },
138− "spago2nix": {
139− "inputs": {
140− "easy-purescript-nix": "easy-purescript-nix",
141− "flake-utils": "flake-utils_2",
142− "nixpkgs": "nixpkgs_2"
143− },
144− "locked": {
145− "lastModified": 1671225584,
146− "narHash": "sha256-h9r67pmvDuA3TV9299L4CN60XSm8RRtX1EwUoKu9Pyw=",
147− "owner": "justinwoo",
148− "repo": "spago2nix",
149− "rev": "1b8ec352bc7eac077b934d6b9f6efa0129926e59",
150− "type": "github"
151− },
152− "original": {
153− "owner": "justinwoo",
154− "repo": "spago2nix",
155− "type": "github"
156− }
157− },
158− "vanilla": {
159− "flake": false,
160− "locked": {
161− "lastModified": 1663430347,
162− "narHash": "sha256-J+KyCcCMdC8SYINKAtUsNfM0/lnbwOnsY8TKOP+yY0s=",
163− "owner": "ilyakooo0",
164− "repo": "vanilla-compiled",
165− "rev": "7a0ee46ce9b958b51c57b37c46e86a0a1868bfa9",
166− "type": "github"
167− },
168− "original": {
169− "owner": "ilyakooo0",
170− "repo": "vanilla-compiled",
171− "type": "github"
172− }
173− }
174− },
175− "root": "root",
176− "version": 7
177−}
flake.nixdeleted−133
@@ -1,133 +0,0 @@
1−{
2− inputs = {
3− flake-utils.url = "github:numtide/flake-utils";
4− bw = {
5− url =
6− # Afer this commit they started requiring path at the top level and I can't deal with this right now
7− "github:bitwarden/clients?rev=ff143760d4f527f2349333ee4b3004eeda68fac5";
8− flake = false;
9− };
10− spago2nix.url = "github:justinwoo/spago2nix";
11− easy-ps = {
12− url = "github:justinwoo/easy-purescript-nix";
13− flake = false;
14− };
15− vanilla = {
16− url = "github:ilyakooo0/vanilla-compiled";
17− flake = false;
18− };
19− nix-filter.url = "github:numtide/nix-filter";
20− };
21−
22− outputs = inputs@{ self, nixpkgs, flake-utils, spago2nix, ... }:
23− flake-utils.lib.eachDefaultSystem (system:
24− let
25− pkgs = import nixpkgs { inherit system; };
26− spagoPkgs = pkgs.callPackage ./spago-packages.nix { };
27− easy-ps = pkgs.callPackage inputs.easy-ps { };
28− nix-filter = import inputs.nix-filter;
29−
30− bw = pkgs.buildNpmPackage {
31− pname = "bw";
32− version = "latest";
33− npmDepsHash = "sha256-jQvL7/SzXqVk03ncU6Mxq8lqFFy+lIrF2QheheXrQ0w=";
34− makeCacheWritable = true;
35− src = inputs.bw;
36− nativeBuildInputs = [ pkgs.python3 ];
37− buildPhase = ''
38− (cd libs/common/ && rm -r spec && find . -name "*.spec.ts" -exec rm {} \; && npm exec -c "tsc -m es2020 -t es2018") '';
39− installPhase = ''
40− mkdir -p $out
41− cp -r libs/shared/dist/* $out
42− cp -r node_modules $out/node_modules
43− cp package.json $out/package.json
44− '';
45− ELECTRON_SKIP_BINARY_DOWNLOAD = 1;
46− };
47−
48− backendBuildScript = pkgs.writeScript "build-backend.sh" ''
49− workdir=$(mktemp -d)
50− (
51− cd $workdir
52− cp -r $1 ./backend
53− cp -r ${bw} ./bw
54− cp ${./backend-main.js} backend.js
55− ${pkgs.esbuild}/bin/esbuild backend.js --bundle --define:ENVIRONMENT_IS_WEB=true --define:ENVIRONMENT_IS_WORKER=false --define:ENVIRONMENT_IS_NODE=false --define:ENVIRONMENT_IS_SHELL=false --external:fs --external:path --loader:.wasm=base64 --outfile=$out
56− )
57−
58− '';
59− backend-js = pkgs.stdenv.mkDerivation {
60− name = "backend-js";
61− buildInputs =
62− [ spagoPkgs.installSpagoStyle spagoPkgs.buildSpagoStyle ];
63− nativeBuildInputs = [ easy-ps.purs-0_15_7 easy-ps.spago ];
64− src = nix-filter {
65− root = ./.;
66− include = [
67− ./spago.dhall
68− ./packages.dhall
69− (nix-filter.inDirectory ./backend)
70− ];
71− };
72− unpackPhase = ''
73− cp $src/spago.dhall .
74− cp $src/packages.dhall .
75− cp -r $src/backend .
76− install-spago-style
77− '';
78− buildPhase = ''
79− build-spago-style "./backend/**/*.purs"
80− '';
81− installPhase = ''
82− mkdir $out
83− cp -r output/* $out/
84− '';
85− };
86−
87− backend =
88− pkgs.runCommand "backend.js" { buildInputs = [ pkgs.esbuild ]; } ''
89− ${backendBuildScript} ${backend-js}
90− '';
91−
92− frontend = pkgs.callPackage ./frontend.nix { inherit nix-filter; };
93−
94− warden-src = pkgs.runCommand "warden-src" { } ''
95− mkdir -p $out
96−
97− cp ${backend} $out/backend.js
98− cp -r ${frontend}/Main.min.js $out/elm.js
99− cp ${./index.html} $out/index.html
100− cp ${./style.css} $out/style.css
101− cp -r ${inputs.vanilla} $out/vanilla
102− '';
103−
104− warden-click-src = pkgs.runCommand "warden-click-src" { } ''
105− mkdir -p $out
106−
107− cp ${./warden.apparmor} $out/warden.apparmor
108− cp ${./warden.desktop} $out/warden.desktop
109− cp ${./manifest.json} $out/manifest.json
110− cp ${./logo.png} $out/logo.png
111− cp -r ${warden-src}/* $out/
112− '';
113−
114− warden = pkgs.writeScript "warden" ''
115− ${pkgs.electron}/bin/electron ${warden-src}/index.html
116− '';
117−
118− in {
119− packages = {
120− inherit bw frontend warden-src backendBuildScript warden-click-src;
121− vanilla = inputs.vanilla.outPath;
122− };
123− apps = {
124− inherit (spago2nix.apps.${system}) spago2nix;
125− warden = {
126− type = "app";
127− program = warden.outPath;
128− };
129− };
130−
131− devShells.default = import ./shell.nix { inherit pkgs; };
132− });
133−}
frontend.nixdeleted−49
@@ -1,49 +0,0 @@
1−{ pkgs, nix-filter }:
2−with pkgs;
3−
4−let
5− mkDerivation = { srcs ? ./elm-srcs.nix, src, name, srcdir ? "./frontend"
6− , targets ? [ ], registryDat ? ./registry.dat, outputJavaScript ? true }:
7− stdenv.mkDerivation {
8− inherit name src;
9−
10− buildInputs = [ elmPackages.elm ]
11− ++ lib.optional outputJavaScript nodePackages.uglify-js;
12−
13− buildPhase = pkgs.elmPackages.fetchElmDeps {
14− elmPackages = import srcs;
15− elmVersion = "0.19.1";
16− inherit registryDat;
17− };
18−
19− installPhase = let
20− elmfile = module:
21− "${srcdir}/${builtins.replaceStrings [ "." ] [ "/" ] module}.elm";
22− extension = if outputJavaScript then "js" else "html";
23− in ''
24− mkdir -p $out/share/doc
25− ${lib.concatStrings (map (module: ''
26− echo "compiling ${elmfile module}"
27− elm make ${
28− elmfile module
29− } --output $out/${module}.${extension} --docs $out/share/doc/${module}.json
30− ${lib.optionalString outputJavaScript ''
31− echo "minifying ${elmfile module}"
32− uglifyjs $out/${module}.${extension} --compress 'pure_funcs="F2,F3,F4,F5,F6,F7,F8,F9,A2,A3,A4,A5,A6,A7,A8,A9",pure_getters,keep_fargs=false,unsafe_comps,unsafe' \
33− | uglifyjs --mangle --output $out/${module}.min.${extension}
34− ''}
35− '') targets)}
36− '';
37− };
38−in mkDerivation {
39− name = "frontend";
40− srcs = ./elm-srcs.nix;
41− src = nix-filter {
42− root = ./.;
43− include = [ (nix-filter.inDirectory ./frontend) ./elm.json ];
44− };
45− targets = [ "Main" ];
46− srcdir = "./frontend";
47− outputJavaScript = true;
48−}
49−
frontend/Bridge.elm+5−251
@@ -383,17 +383,6 @@ jsonEncCipher_LoginCipher_password_Maybe val = (maybeEncode (Json.Encode.string
383
383
384
384
385
385
386−type alias Cipher_LoginCipher_totp_Maybe = (Maybe String)
387−
388−jsonDecCipher_LoginCipher_totp_Maybe : Json.Decode.Decoder ( Cipher_LoginCipher_totp_Maybe )
389−jsonDecCipher_LoginCipher_totp_Maybe =
390− Json.Decode.maybe (Json.Decode.string)
391−
392−jsonEncCipher_LoginCipher_totp_Maybe : Cipher_LoginCipher_totp_Maybe -> Value
393−jsonEncCipher_LoginCipher_totp_Maybe val = (maybeEncode (Json.Encode.string)) val
394−
395−
396−
397type alias Cipher_LoginCipher_uris_List = (List String)
386type alias Cipher_LoginCipher_uris_List = (List String)
398
387
399jsonDecCipher_LoginCipher_uris_List : Json.Decode.Decoder ( Cipher_LoginCipher_uris_List )
388jsonDecCipher_LoginCipher_uris_List : Json.Decode.Decoder ( Cipher_LoginCipher_uris_List )
@@ -418,16 +407,14 @@ jsonEncCipher_LoginCipher_username_Maybe val = (maybeEncode (Json.Encode.string
418
407
419type alias Cipher_LoginCipher =
408type alias Cipher_LoginCipher =
420 { password: Cipher_LoginCipher_password_Maybe
409 { password: Cipher_LoginCipher_password_Maybe
421− , totp: Cipher_LoginCipher_totp_Maybe
422 , uris: Cipher_LoginCipher_uris_List
410 , uris: Cipher_LoginCipher_uris_List
423 , username: Cipher_LoginCipher_username_Maybe
411 , username: Cipher_LoginCipher_username_Maybe
424 }
412 }
425
413
426jsonDecCipher_LoginCipher : Json.Decode.Decoder ( Cipher_LoginCipher )
414jsonDecCipher_LoginCipher : Json.Decode.Decoder ( Cipher_LoginCipher )
427jsonDecCipher_LoginCipher =
415jsonDecCipher_LoginCipher =
428− Json.Decode.succeed (\ppassword ptotp puris pusername -> {password = ppassword, totp = ptotp, uris = puris, username = pusername})
416+ Json.Decode.succeed (\ppassword puris pusername -> {password = ppassword, uris = puris, username = pusername})
429 |> required "password" (jsonDecCipher_LoginCipher_password_Maybe)
417 |> required "password" (jsonDecCipher_LoginCipher_password_Maybe)
430− |> required "totp" (jsonDecCipher_LoginCipher_totp_Maybe)
431 |> required "uris" (jsonDecCipher_LoginCipher_uris_List)
418 |> required "uris" (jsonDecCipher_LoginCipher_uris_List)
432 |> required "username" (jsonDecCipher_LoginCipher_username_Maybe)
419 |> required "username" (jsonDecCipher_LoginCipher_username_Maybe)
433
420
@@ -435,7 +422,6 @@ jsonEncCipher_LoginCipher : Cipher_LoginCipher -> Value
435jsonEncCipher_LoginCipher val =
422jsonEncCipher_LoginCipher val =
436 Json.Encode.object
423 Json.Encode.object
437 [ ("password", jsonEncCipher_LoginCipher_password_Maybe val.password)
424 [ ("password", jsonEncCipher_LoginCipher_password_Maybe val.password)
438− , ("totp", jsonEncCipher_LoginCipher_totp_Maybe val.totp)
439 , ("uris", jsonEncCipher_LoginCipher_uris_List val.uris)
425 , ("uris", jsonEncCipher_LoginCipher_uris_List val.uris)
440 , ("username", jsonEncCipher_LoginCipher_username_Maybe val.username)
426 , ("username", jsonEncCipher_LoginCipher_username_Maybe val.username)
441 ]
427 ]
@@ -491,82 +477,17 @@ jsonEncCipherType val =
491
477
492
478
493
479
494−type alias Cmd_ChooseSecondFactor =
495− { email: String
496− , factor: TwoFactorProviderType
497− , password: String
498− , requestFromServer: Bool
499− , server: String
500− }
501−
502−jsonDecCmd_ChooseSecondFactor : Json.Decode.Decoder ( Cmd_ChooseSecondFactor )
503−jsonDecCmd_ChooseSecondFactor =
504− Json.Decode.succeed (\pemail pfactor ppassword prequestFromServer pserver -> {email = pemail, factor = pfactor, password = ppassword, requestFromServer = prequestFromServer, server = pserver})
505− |> required "email" (Json.Decode.string)
506− |> required "factor" (jsonDecTwoFactorProviderType)
507− |> required "password" (Json.Decode.string)
508− |> required "requestFromServer" (Json.Decode.bool)
509− |> required "server" (Json.Decode.string)
510−
511−jsonEncCmd_ChooseSecondFactor : Cmd_ChooseSecondFactor -> Value
512−jsonEncCmd_ChooseSecondFactor val =
513− Json.Encode.object
514− [ ("email", Json.Encode.string val.email)
515− , ("factor", jsonEncTwoFactorProviderType val.factor)
516− , ("password", Json.Encode.string val.password)
517− , ("requestFromServer", Json.Encode.bool val.requestFromServer)
518− , ("server", Json.Encode.string val.server)
519− ]
520−
521−
522−
523−type alias Cmd_Login_secondFactor =
524− { provider: TwoFactorProviderType
525− , remember: Bool
526− , token: String
527− }
528−
529−jsonDecCmd_Login_secondFactor : Json.Decode.Decoder ( Cmd_Login_secondFactor )
530−jsonDecCmd_Login_secondFactor =
531− Json.Decode.succeed (\pprovider premember ptoken -> {provider = pprovider, remember = premember, token = ptoken})
532− |> required "provider" (jsonDecTwoFactorProviderType)
533− |> required "remember" (Json.Decode.bool)
534− |> required "token" (Json.Decode.string)
535−
536−jsonEncCmd_Login_secondFactor : Cmd_Login_secondFactor -> Value
537−jsonEncCmd_Login_secondFactor val =
538− Json.Encode.object
539− [ ("provider", jsonEncTwoFactorProviderType val.provider)
540− , ("remember", Json.Encode.bool val.remember)
541− , ("token", Json.Encode.string val.token)
542− ]
543−
544−
545−
546−type alias Cmd_Login_secondFactor_Maybe = (Maybe Cmd_Login_secondFactor)
547−
548−jsonDecCmd_Login_secondFactor_Maybe : Json.Decode.Decoder ( Cmd_Login_secondFactor_Maybe )
549−jsonDecCmd_Login_secondFactor_Maybe =
550− Json.Decode.maybe (jsonDecCmd_Login_secondFactor)
551−
552−jsonEncCmd_Login_secondFactor_Maybe : Cmd_Login_secondFactor_Maybe -> Value
553−jsonEncCmd_Login_secondFactor_Maybe val = (maybeEncode (jsonEncCmd_Login_secondFactor)) val
554−
555−
556−
557type alias Cmd_Login =
480type alias Cmd_Login =
558 { email: String
481 { email: String
559 , password: String
482 , password: String
560− , secondFactor: Cmd_Login_secondFactor_Maybe
561 , server: String
483 , server: String
562 }
484 }
563
485
564jsonDecCmd_Login : Json.Decode.Decoder ( Cmd_Login )
486jsonDecCmd_Login : Json.Decode.Decoder ( Cmd_Login )
565jsonDecCmd_Login =
487jsonDecCmd_Login =
566− Json.Decode.succeed (\pemail ppassword psecondFactor pserver -> {email = pemail, password = ppassword, secondFactor = psecondFactor, server = pserver})
488+ Json.Decode.succeed (\pemail ppassword pserver -> {email = pemail, password = ppassword, server = pserver})
567 |> required "email" (Json.Decode.string)
489 |> required "email" (Json.Decode.string)
568 |> required "password" (Json.Decode.string)
490 |> required "password" (Json.Decode.string)
569− |> required "secondFactor" (jsonDecCmd_Login_secondFactor_Maybe)
570 |> required "server" (Json.Decode.string)
491 |> required "server" (Json.Decode.string)
571
492
572jsonEncCmd_Login : Cmd_Login -> Value
493jsonEncCmd_Login : Cmd_Login -> Value
@@ -574,18 +495,13 @@ jsonEncCmd_Login val =
574 Json.Encode.object
495 Json.Encode.object
575 [ ("email", Json.Encode.string val.email)
496 [ ("email", Json.Encode.string val.email)
576 , ("password", Json.Encode.string val.password)
497 , ("password", Json.Encode.string val.password)
577− , ("secondFactor", jsonEncCmd_Login_secondFactor_Maybe val.secondFactor)
578 , ("server", Json.Encode.string val.server)
498 , ("server", Json.Encode.string val.server)
579 ]
499 ]
580
500
581
501
582
502
583type Cmd =
503type Cmd =
584− ChooseSecondFactor Cmd_ChooseSecondFactor
585− | Copy String
504+ Copy String
586− | CreateCipher FullCipher
587− | DeleteCipher FullCipher
588− | GeneratePassword PasswordGeneratorConfig
589 | Init
505 | Init
590 | Login Cmd_Login
506 | Login Cmd_Login
591 | NeedCiphersList
507 | NeedCiphersList
@@ -593,18 +509,13 @@ type Cmd =
593 | NeedsReset
509 | NeedsReset
594 | Open String
510 | Open String
595 | RequestCipher String
511 | RequestCipher String
596− | RequestTotp String
597 | SendMasterPassword String
512 | SendMasterPassword String
598 | UpdateCipher FullCipher
513 | UpdateCipher FullCipher
599
514
600jsonDecCmd : Json.Decode.Decoder ( Cmd )
515jsonDecCmd : Json.Decode.Decoder ( Cmd )
601jsonDecCmd =
516jsonDecCmd =
602 let jsonDecDictCmd = Dict.fromList
517 let jsonDecDictCmd = Dict.fromList
603− [ ("ChooseSecondFactor", Json.Decode.lazy (\_ -> Json.Decode.map ChooseSecondFactor (jsonDecCmd_ChooseSecondFactor)))
604− , ("Copy", Json.Decode.lazy (\_ -> Json.Decode.map Copy (Json.Decode.string)))
518+ [ ("Copy", Json.Decode.lazy (\_ -> Json.Decode.map Copy (Json.Decode.string)))
605− , ("CreateCipher", Json.Decode.lazy (\_ -> Json.Decode.map CreateCipher (jsonDecFullCipher)))
606− , ("DeleteCipher", Json.Decode.lazy (\_ -> Json.Decode.map DeleteCipher (jsonDecFullCipher)))
607− , ("GeneratePassword", Json.Decode.lazy (\_ -> Json.Decode.map GeneratePassword (jsonDecPasswordGeneratorConfig)))
608 , ("Init", Json.Decode.lazy (\_ -> Json.Decode.succeed Init))
519 , ("Init", Json.Decode.lazy (\_ -> Json.Decode.succeed Init))
609 , ("Login", Json.Decode.lazy (\_ -> Json.Decode.map Login (jsonDecCmd_Login)))
520 , ("Login", Json.Decode.lazy (\_ -> Json.Decode.map Login (jsonDecCmd_Login)))
610 , ("NeedCiphersList", Json.Decode.lazy (\_ -> Json.Decode.succeed NeedCiphersList))
521 , ("NeedCiphersList", Json.Decode.lazy (\_ -> Json.Decode.succeed NeedCiphersList))
@@ -612,7 +523,6 @@ jsonDecCmd =
612 , ("NeedsReset", Json.Decode.lazy (\_ -> Json.Decode.succeed NeedsReset))
523 , ("NeedsReset", Json.Decode.lazy (\_ -> Json.Decode.succeed NeedsReset))
613 , ("Open", Json.Decode.lazy (\_ -> Json.Decode.map Open (Json.Decode.string)))
524 , ("Open", Json.Decode.lazy (\_ -> Json.Decode.map Open (Json.Decode.string)))
614 , ("RequestCipher", Json.Decode.lazy (\_ -> Json.Decode.map RequestCipher (Json.Decode.string)))
525 , ("RequestCipher", Json.Decode.lazy (\_ -> Json.Decode.map RequestCipher (Json.Decode.string)))
615− , ("RequestTotp", Json.Decode.lazy (\_ -> Json.Decode.map RequestTotp (Json.Decode.string)))
616 , ("SendMasterPassword", Json.Decode.lazy (\_ -> Json.Decode.map SendMasterPassword (Json.Decode.string)))
526 , ("SendMasterPassword", Json.Decode.lazy (\_ -> Json.Decode.map SendMasterPassword (Json.Decode.string)))
617 , ("UpdateCipher", Json.Decode.lazy (\_ -> Json.Decode.map UpdateCipher (jsonDecFullCipher)))
527 , ("UpdateCipher", Json.Decode.lazy (\_ -> Json.Decode.map UpdateCipher (jsonDecFullCipher)))
618 ]
528 ]
@@ -622,11 +532,7 @@ jsonDecCmd =
622jsonEncCmd : Cmd -> Value
532jsonEncCmd : Cmd -> Value
623jsonEncCmd val =
533jsonEncCmd val =
624 let keyval v = case v of
534 let keyval v = case v of
625− ChooseSecondFactor v1 -> ("ChooseSecondFactor", encodeValue (jsonEncCmd_ChooseSecondFactor v1))
626 Copy v1 -> ("Copy", encodeValue (Json.Encode.string v1))
535 Copy v1 -> ("Copy", encodeValue (Json.Encode.string v1))
627− CreateCipher v1 -> ("CreateCipher", encodeValue (jsonEncFullCipher v1))
628− DeleteCipher v1 -> ("DeleteCipher", encodeValue (jsonEncFullCipher v1))
629− GeneratePassword v1 -> ("GeneratePassword", encodeValue (jsonEncPasswordGeneratorConfig v1))
630 Init -> ("Init", encodeValue (Json.Encode.list identity []))
536 Init -> ("Init", encodeValue (Json.Encode.list identity []))
631 Login v1 -> ("Login", encodeValue (jsonEncCmd_Login v1))
537 Login v1 -> ("Login", encodeValue (jsonEncCmd_Login v1))
632 NeedCiphersList -> ("NeedCiphersList", encodeValue (Json.Encode.list identity []))
538 NeedCiphersList -> ("NeedCiphersList", encodeValue (Json.Encode.list identity []))
@@ -634,27 +540,14 @@ jsonEncCmd val =
634 NeedsReset -> ("NeedsReset", encodeValue (Json.Encode.list identity []))
540 NeedsReset -> ("NeedsReset", encodeValue (Json.Encode.list identity []))
635 Open v1 -> ("Open", encodeValue (Json.Encode.string v1))
541 Open v1 -> ("Open", encodeValue (Json.Encode.string v1))
636 RequestCipher v1 -> ("RequestCipher", encodeValue (Json.Encode.string v1))
542 RequestCipher v1 -> ("RequestCipher", encodeValue (Json.Encode.string v1))
637− RequestTotp v1 -> ("RequestTotp", encodeValue (Json.Encode.string v1))
638 SendMasterPassword v1 -> ("SendMasterPassword", encodeValue (Json.Encode.string v1))
543 SendMasterPassword v1 -> ("SendMasterPassword", encodeValue (Json.Encode.string v1))
639 UpdateCipher v1 -> ("UpdateCipher", encodeValue (jsonEncFullCipher v1))
544 UpdateCipher v1 -> ("UpdateCipher", encodeValue (jsonEncFullCipher v1))
640 in encodeSumTaggedObject "tag" "contents" keyval val
545 in encodeSumTaggedObject "tag" "contents" keyval val
641
546
642
547
643
548
644−type alias FullCipher_collectionIds_List = (List String)
645−
646−jsonDecFullCipher_collectionIds_List : Json.Decode.Decoder ( FullCipher_collectionIds_List )
647−jsonDecFullCipher_collectionIds_List =
648− Json.Decode.list (Json.Decode.string)
649−
650−jsonEncFullCipher_collectionIds_List : FullCipher_collectionIds_List -> Value
651−jsonEncFullCipher_collectionIds_List val = (Json.Encode.list Json.Encode.string) val
652−
653−
654−
655type alias FullCipher =
549type alias FullCipher =
656 { cipher: Cipher
550 { cipher: Cipher
657− , collectionIds: FullCipher_collectionIds_List
658 , favorite: Bool
551 , favorite: Bool
659 , id: String
552 , id: String
660 , name: String
553 , name: String
@@ -663,9 +556,8 @@ type alias FullCipher =
663
556
664jsonDecFullCipher : Json.Decode.Decoder ( FullCipher )
557jsonDecFullCipher : Json.Decode.Decoder ( FullCipher )
665jsonDecFullCipher =
558jsonDecFullCipher =
666− Json.Decode.succeed (\pcipher pcollectionIds pfavorite pid pname preprompt -> {cipher = pcipher, collectionIds = pcollectionIds, favorite = pfavorite, id = pid, name = pname, reprompt = preprompt})
559+ Json.Decode.succeed (\pcipher pfavorite pid pname preprompt -> {cipher = pcipher, favorite = pfavorite, id = pid, name = pname, reprompt = preprompt})
667 |> required "cipher" (jsonDecCipher)
560 |> required "cipher" (jsonDecCipher)
668− |> required "collectionIds" (jsonDecFullCipher_collectionIds_List)
669 |> required "favorite" (Json.Decode.bool)
561 |> required "favorite" (Json.Decode.bool)
670 |> required "id" (Json.Decode.string)
562 |> required "id" (Json.Decode.string)
671 |> required "name" (Json.Decode.string)
563 |> required "name" (Json.Decode.string)
@@ -675,7 +567,6 @@ jsonEncFullCipher : FullCipher -> Value
675jsonEncFullCipher val =
567jsonEncFullCipher val =
676 Json.Encode.object
568 Json.Encode.object
677 [ ("cipher", jsonEncCipher val.cipher)
569 [ ("cipher", jsonEncCipher val.cipher)
678− , ("collectionIds", jsonEncFullCipher_collectionIds_List val.collectionIds)
679 , ("favorite", Json.Encode.bool val.favorite)
570 , ("favorite", Json.Encode.bool val.favorite)
680 , ("id", Json.Encode.string val.id)
571 , ("id", Json.Encode.string val.id)
681 , ("name", Json.Encode.string val.name)
572 , ("name", Json.Encode.string val.name)
@@ -684,65 +575,6 @@ jsonEncFullCipher val =
684
575
685
576
686
577
687−type alias PasswordGeneratorConfig =
688− { ambiguous: Bool
689− , capitalize: Bool
690− , includeNumber: Bool
691− , length: Int
692− , lowercase: Bool
693− , minLowercase: Int
694− , minNumber: Int
695− , minSpecial: Int
696− , minUppercase: Int
697− , numWords: Int
698− , number: Bool
699− , special: Bool
700− , type_: String
701− , uppercase: Bool
702− , wordSeparator: String
703− }
704−
705−jsonDecPasswordGeneratorConfig : Json.Decode.Decoder ( PasswordGeneratorConfig )
706−jsonDecPasswordGeneratorConfig =
707− Json.Decode.succeed (\pambiguous pcapitalize pincludeNumber plength plowercase pminLowercase pminNumber pminSpecial pminUppercase pnumWords pnumber pspecial ptype puppercase pwordSeparator -> {ambiguous = pambiguous, capitalize = pcapitalize, includeNumber = pincludeNumber, length = plength, lowercase = plowercase, minLowercase = pminLowercase, minNumber = pminNumber, minSpecial = pminSpecial, minUppercase = pminUppercase, numWords = pnumWords, number = pnumber, special = pspecial, type_ = ptype, uppercase = puppercase, wordSeparator = pwordSeparator})
708− |> required "ambiguous" (Json.Decode.bool)
709− |> required "capitalize" (Json.Decode.bool)
710− |> required "includeNumber" (Json.Decode.bool)
711− |> required "length" (Json.Decode.int)
712− |> required "lowercase" (Json.Decode.bool)
713− |> required "minLowercase" (Json.Decode.int)
714− |> required "minNumber" (Json.Decode.int)
715− |> required "minSpecial" (Json.Decode.int)
716− |> required "minUppercase" (Json.Decode.int)
717− |> required "numWords" (Json.Decode.int)
718− |> required "number" (Json.Decode.bool)
719− |> required "special" (Json.Decode.bool)
720− |> required "type" (Json.Decode.string)
721− |> required "uppercase" (Json.Decode.bool)
722− |> required "wordSeparator" (Json.Decode.string)
723−
724−jsonEncPasswordGeneratorConfig : PasswordGeneratorConfig -> Value
725−jsonEncPasswordGeneratorConfig val =
726− Json.Encode.object
727− [ ("ambiguous", Json.Encode.bool val.ambiguous)
728− , ("capitalize", Json.Encode.bool val.capitalize)
729− , ("includeNumber", Json.Encode.bool val.includeNumber)
730− , ("length", Json.Encode.int val.length)
731− , ("lowercase", Json.Encode.bool val.lowercase)
732− , ("minLowercase", Json.Encode.int val.minLowercase)
733− , ("minNumber", Json.Encode.int val.minNumber)
734− , ("minSpecial", Json.Encode.int val.minSpecial)
735− , ("minUppercase", Json.Encode.int val.minUppercase)
736− , ("numWords", Json.Encode.int val.numWords)
737− , ("number", Json.Encode.bool val.number)
738− , ("special", Json.Encode.bool val.special)
739− , ("type", Json.Encode.string val.type_)
740− , ("uppercase", Json.Encode.bool val.uppercase)
741− , ("wordSeparator", Json.Encode.string val.wordSeparator)
742− ]
743−
744−
745−
746type alias Sub_LoadCiphers =
578type alias Sub_LoadCiphers =
747 { cipherType: CipherType
579 { cipherType: CipherType
748 , date: String
580 , date: String
@@ -800,77 +632,33 @@ jsonEncSub_NeedsMasterPassword val =
800
632
801
633
802
634
803−type alias Sub_NeedsSecondFactor_List = (List TwoFactorProviderType)
804−
805−jsonDecSub_NeedsSecondFactor_List : Json.Decode.Decoder ( Sub_NeedsSecondFactor_List )
806−jsonDecSub_NeedsSecondFactor_List =
807− Json.Decode.list (jsonDecTwoFactorProviderType)
808−
809−jsonEncSub_NeedsSecondFactor_List : Sub_NeedsSecondFactor_List -> Value
810−jsonEncSub_NeedsSecondFactor_List val = (Json.Encode.list jsonEncTwoFactorProviderType) val
811−
812−
813−
814−type alias Sub_Totp =
815− { code: String
816− , interval: Int
817− , source: String
818− }
819−
820−jsonDecSub_Totp : Json.Decode.Decoder ( Sub_Totp )
821−jsonDecSub_Totp =
822− Json.Decode.succeed (\pcode pinterval psource -> {code = pcode, interval = pinterval, source = psource})
823− |> required "code" (Json.Decode.string)
824− |> required "interval" (Json.Decode.int)
825− |> required "source" (Json.Decode.string)
826−
827−jsonEncSub_Totp : Sub_Totp -> Value
828−jsonEncSub_Totp val =
829− Json.Encode.object
830− [ ("code", Json.Encode.string val.code)
831− , ("interval", Json.Encode.int val.interval)
832− , ("source", Json.Encode.string val.source)
833− ]
834−
835−
836−
837type Sub =
635type Sub =
838 CaptchaDone
636 CaptchaDone
839 | CipherChanged FullCipher
637 | CipherChanged FullCipher
840− | CipherDeleted FullCipher
841 | Error String
638 | Error String
842− | GeneratedPassword String
843 | LoadCipher FullCipher
639 | LoadCipher FullCipher
844 | LoadCiphers Sub_LoadCiphers_List
640 | LoadCiphers Sub_LoadCiphers_List
845 | LoginSuccessful
641 | LoginSuccessful
846 | NeedsCaptcha String
642 | NeedsCaptcha String
847 | NeedsLogin
643 | NeedsLogin
848 | NeedsMasterPassword Sub_NeedsMasterPassword
644 | NeedsMasterPassword Sub_NeedsMasterPassword
849− | NeedsSecondFactor Sub_NeedsSecondFactor_List
850 | RecieveEmail String
645 | RecieveEmail String
851 | Reset
646 | Reset
852− | Totp Sub_Totp
853− | WrongPassword
854
647
855jsonDecSub : Json.Decode.Decoder ( Sub )
648jsonDecSub : Json.Decode.Decoder ( Sub )
856jsonDecSub =
649jsonDecSub =
857 let jsonDecDictSub = Dict.fromList
650 let jsonDecDictSub = Dict.fromList
858 [ ("CaptchaDone", Json.Decode.lazy (\_ -> Json.Decode.succeed CaptchaDone))
651 [ ("CaptchaDone", Json.Decode.lazy (\_ -> Json.Decode.succeed CaptchaDone))
859 , ("CipherChanged", Json.Decode.lazy (\_ -> Json.Decode.map CipherChanged (jsonDecFullCipher)))
652 , ("CipherChanged", Json.Decode.lazy (\_ -> Json.Decode.map CipherChanged (jsonDecFullCipher)))
860− , ("CipherDeleted", Json.Decode.lazy (\_ -> Json.Decode.map CipherDeleted (jsonDecFullCipher)))
861 , ("Error", Json.Decode.lazy (\_ -> Json.Decode.map Error (Json.Decode.string)))
653 , ("Error", Json.Decode.lazy (\_ -> Json.Decode.map Error (Json.Decode.string)))
862− , ("GeneratedPassword", Json.Decode.lazy (\_ -> Json.Decode.map GeneratedPassword (Json.Decode.string)))
863 , ("LoadCipher", Json.Decode.lazy (\_ -> Json.Decode.map LoadCipher (jsonDecFullCipher)))
654 , ("LoadCipher", Json.Decode.lazy (\_ -> Json.Decode.map LoadCipher (jsonDecFullCipher)))
864 , ("LoadCiphers", Json.Decode.lazy (\_ -> Json.Decode.map LoadCiphers (jsonDecSub_LoadCiphers_List)))
655 , ("LoadCiphers", Json.Decode.lazy (\_ -> Json.Decode.map LoadCiphers (jsonDecSub_LoadCiphers_List)))
865 , ("LoginSuccessful", Json.Decode.lazy (\_ -> Json.Decode.succeed LoginSuccessful))
656 , ("LoginSuccessful", Json.Decode.lazy (\_ -> Json.Decode.succeed LoginSuccessful))
866 , ("NeedsCaptcha", Json.Decode.lazy (\_ -> Json.Decode.map NeedsCaptcha (Json.Decode.string)))
657 , ("NeedsCaptcha", Json.Decode.lazy (\_ -> Json.Decode.map NeedsCaptcha (Json.Decode.string)))
867 , ("NeedsLogin", Json.Decode.lazy (\_ -> Json.Decode.succeed NeedsLogin))
658 , ("NeedsLogin", Json.Decode.lazy (\_ -> Json.Decode.succeed NeedsLogin))
868 , ("NeedsMasterPassword", Json.Decode.lazy (\_ -> Json.Decode.map NeedsMasterPassword (jsonDecSub_NeedsMasterPassword)))
659 , ("NeedsMasterPassword", Json.Decode.lazy (\_ -> Json.Decode.map NeedsMasterPassword (jsonDecSub_NeedsMasterPassword)))
869− , ("NeedsSecondFactor", Json.Decode.lazy (\_ -> Json.Decode.map NeedsSecondFactor (jsonDecSub_NeedsSecondFactor_List)))
870 , ("RecieveEmail", Json.Decode.lazy (\_ -> Json.Decode.map RecieveEmail (Json.Decode.string)))
660 , ("RecieveEmail", Json.Decode.lazy (\_ -> Json.Decode.map RecieveEmail (Json.Decode.string)))
871 , ("Reset", Json.Decode.lazy (\_ -> Json.Decode.succeed Reset))
661 , ("Reset", Json.Decode.lazy (\_ -> Json.Decode.succeed Reset))
872− , ("Totp", Json.Decode.lazy (\_ -> Json.Decode.map Totp (jsonDecSub_Totp)))
873− , ("WrongPassword", Json.Decode.lazy (\_ -> Json.Decode.succeed WrongPassword))
874 ]
662 ]
875 jsonDecObjectSetSub = Set.fromList []
663 jsonDecObjectSetSub = Set.fromList []
876 in decodeSumTaggedObject "Sub" "tag" "contents" jsonDecDictSub jsonDecObjectSetSub
664 in decodeSumTaggedObject "Sub" "tag" "contents" jsonDecDictSub jsonDecObjectSetSub
@@ -880,48 +668,14 @@ jsonEncSub val =
880 let keyval v = case v of
668 let keyval v = case v of
881 CaptchaDone -> ("CaptchaDone", encodeValue (Json.Encode.list identity []))
669 CaptchaDone -> ("CaptchaDone", encodeValue (Json.Encode.list identity []))
882 CipherChanged v1 -> ("CipherChanged", encodeValue (jsonEncFullCipher v1))
670 CipherChanged v1 -> ("CipherChanged", encodeValue (jsonEncFullCipher v1))
883− CipherDeleted v1 -> ("CipherDeleted", encodeValue (jsonEncFullCipher v1))
884 Error v1 -> ("Error", encodeValue (Json.Encode.string v1))
671 Error v1 -> ("Error", encodeValue (Json.Encode.string v1))
885− GeneratedPassword v1 -> ("GeneratedPassword", encodeValue (Json.Encode.string v1))
886 LoadCipher v1 -> ("LoadCipher", encodeValue (jsonEncFullCipher v1))
672 LoadCipher v1 -> ("LoadCipher", encodeValue (jsonEncFullCipher v1))
887 LoadCiphers v1 -> ("LoadCiphers", encodeValue (jsonEncSub_LoadCiphers_List v1))
673 LoadCiphers v1 -> ("LoadCiphers", encodeValue (jsonEncSub_LoadCiphers_List v1))
888 LoginSuccessful -> ("LoginSuccessful", encodeValue (Json.Encode.list identity []))
674 LoginSuccessful -> ("LoginSuccessful", encodeValue (Json.Encode.list identity []))
889 NeedsCaptcha v1 -> ("NeedsCaptcha", encodeValue (Json.Encode.string v1))
675 NeedsCaptcha v1 -> ("NeedsCaptcha", encodeValue (Json.Encode.string v1))
890 NeedsLogin -> ("NeedsLogin", encodeValue (Json.Encode.list identity []))
676 NeedsLogin -> ("NeedsLogin", encodeValue (Json.Encode.list identity []))
891 NeedsMasterPassword v1 -> ("NeedsMasterPassword", encodeValue (jsonEncSub_NeedsMasterPassword v1))
677 NeedsMasterPassword v1 -> ("NeedsMasterPassword", encodeValue (jsonEncSub_NeedsMasterPassword v1))
892− NeedsSecondFactor v1 -> ("NeedsSecondFactor", encodeValue (jsonEncSub_NeedsSecondFactor_List v1))
893 RecieveEmail v1 -> ("RecieveEmail", encodeValue (Json.Encode.string v1))
678 RecieveEmail v1 -> ("RecieveEmail", encodeValue (Json.Encode.string v1))
894 Reset -> ("Reset", encodeValue (Json.Encode.list identity []))
679 Reset -> ("Reset", encodeValue (Json.Encode.list identity []))
895− Totp v1 -> ("Totp", encodeValue (jsonEncSub_Totp v1))
896− WrongPassword -> ("WrongPassword", encodeValue (Json.Encode.list identity []))
897 in encodeSumTaggedObject "tag" "contents" keyval val
680 in encodeSumTaggedObject "tag" "contents" keyval val
898
681
899−
900−
901−type TwoFactorProviderType =
902− Authenticator
903− | Duo
904− | Email
905− | OrganizationDuo
906− | Remember
907− | U2f
908− | WebAuthn
909− | Yubikey
910−
911−jsonDecTwoFactorProviderType : Json.Decode.Decoder ( TwoFactorProviderType )
912−jsonDecTwoFactorProviderType =
913− let jsonDecDictTwoFactorProviderType = Dict.fromList [("Authenticator", Authenticator), ("Duo", Duo), ("Email", Email), ("OrganizationDuo", OrganizationDuo), ("Remember", Remember), ("U2f", U2f), ("WebAuthn", WebAuthn), ("Yubikey", Yubikey)]
914− in decodeSumUnaries "TwoFactorProviderType" jsonDecDictTwoFactorProviderType
915−
916−jsonEncTwoFactorProviderType : TwoFactorProviderType -> Value
917−jsonEncTwoFactorProviderType val =
918− case val of
919− Authenticator -> Json.Encode.string "Authenticator"
920− Duo -> Json.Encode.string "Duo"
921− Email -> Json.Encode.string "Email"
922− OrganizationDuo -> Json.Encode.string "OrganizationDuo"
923− Remember -> Json.Encode.string "Remember"
924− U2f -> Json.Encode.string "U2f"
925− WebAuthn -> Json.Encode.string "WebAuthn"
926− Yubikey -> Json.Encode.string "Yubikey"
927−
frontend/GlobalEvents.elm−2
@@ -5,5 +5,3 @@ import Bridge
5
5
6type Event
6type Event
7 = UpdateCipher Bridge.FullCipher
7 = UpdateCipher Bridge.FullCipher
8− | GeneratedPassword String
9− | DecodedTotp Bridge.Sub_Totp
frontend/Logic/Cipher.elm+2−3
@@ -49,15 +49,14 @@ normalizeIdentityCipher { address1, address2, address3, city, company, country,
49 , ssn = normalizeStringMaybe ssn
49 , ssn = normalizeStringMaybe ssn
50 , state = normalizeStringMaybe state
50 , state = normalizeStringMaybe state
51 , title = normalizeStringMaybe title
51 , title = normalizeStringMaybe title
52− , username = normalizeStringMaybe username
52+ , username = username
53 }
53 }
54
54
55
55
56normalizeLoginCipher : Bridge.Cipher_LoginCipher -> Bridge.Cipher_LoginCipher
56normalizeLoginCipher : Bridge.Cipher_LoginCipher -> Bridge.Cipher_LoginCipher
57−normalizeLoginCipher { password, totp, uris, username } =
57+normalizeLoginCipher { password, uris, username } =
58 { password = password
58 { password = password
59 , uris = uris |> List.map String.trim |> List.filter String.isEmpty
59 , uris = uris |> List.map String.trim |> List.filter String.isEmpty
60− , totp = normalizeStringMaybe totp
61 , username = username
60 , username = username
62 }
61 }
63
62
frontend/Logic/SecondFactor.elmdeleted−31
@@ -1,31 +0,0 @@
1−module Logic.SecondFactor exposing (secondFactorName)
2−
3−import Bridge
4−
5−
6−secondFactorName : Bridge.TwoFactorProviderType -> String
7−secondFactorName factor =
8− case factor of
9− Bridge.Authenticator ->
10− "Authenticator"
11−
12− Bridge.Duo ->
13− "Duo"
14−
15− Bridge.Email ->
16− "Email"
17−
18− Bridge.OrganizationDuo ->
19− "Organization Duo"
20−
21− Bridge.Remember ->
22− "Remember (IDK what this is)"
23−
24− Bridge.U2f ->
25− "U2f"
26−
27− Bridge.WebAuthn ->
28− "WebAuthn"
29−
30− Bridge.Yubikey ->
31− "Yubikey"
frontend/Main.elm+189−417
@@ -1,4 +1,4 @@
1−module Main exposing (main)
1+module Main exposing (..)
2
2
3import Bridge
3import Bridge
4import Browser
4import Browser
@@ -16,8 +16,6 @@ import Pages.Loader exposing (loader)
16import Pages.Login as Login
16import Pages.Login as Login
17import Pages.MasterPassword as MasterPassword
17import Pages.MasterPassword as MasterPassword
18import Pages.Navigation as Navigation exposing (TopButton(..))
18import Pages.Navigation as Navigation exposing (TopButton(..))
19−import Pages.SecondFactor as SecondFactor
20−import Pages.SecondFactorSelect as SecondFactorSelect
21import Task
19import Task
22import Time
20import Time
23import Types exposing (CipherId)
21import Types exposing (CipherId)
@@ -30,51 +28,37 @@ notificationLingerSeconds =
30
28
31
29
32type Msg
30type Msg
31+ = Noop
32+ | RecieveMessage String
33− = ShowLoginPage
33+ | ShowLoginPage
34− | ShowError String String
34+ | ShowError String
35 | ShowInfo String String
35 | ShowInfo String String
36 | CloseNotification
36 | CloseNotification
37− | SubmitLogin Bridge.Cmd_Login
37+ | SubmitLogin { email : String, password : String, server : String }
38+ | LoginMsg Login.Msg
39+ | CiphersMsg Ciphers.Msg
38 | LoadCiphers Bridge.Sub_LoadCiphers_List
40 | LoadCiphers Bridge.Sub_LoadCiphers_List
39− | LoginSuccessful
41+ | OpenCiphersScreen
40 | Reset
42 | Reset
41 | NeedsReset
43 | NeedsReset
44+ | MasterPasswordMsg MasterPassword.Msg
42 | SendMasterPassword { password : String }
45 | SendMasterPassword { password : String }
43 | ShowMasterPasswordPage { server : String, login : String }
46 | ShowMasterPasswordPage { server : String, login : String }
47+ | CipherMsg Cipher.Msg
44 | ShowCipherPage Bridge.FullCipher
48 | ShowCipherPage Bridge.FullCipher
45 | RequestCipher CipherId
49 | RequestCipher CipherId
46 | PopView
50 | PopView
47 | RecieveEmail String
51 | RecieveEmail String
48 | Copy String
52 | Copy String
49 | Open String
53 | Open String
54+ | CaptchaMsg Captcha.Msg
55+ | EditCipherMsg EditCipher.Msg
50 | ShowCaptcha Captcha.HCaptchSiteKey
56 | ShowCaptcha Captcha.HCaptchSiteKey
51 | ClearNotification { currentTime : Time.Posix }
57 | ClearNotification { currentTime : Time.Posix }
52 | UpdateLastNotificatioTime Time.Posix
58 | UpdateLastNotificatioTime Time.Posix
53 | EditCipher Bridge.FullCipher
59 | EditCipher Bridge.FullCipher
54 | UpdateCipher Bridge.FullCipher
60 | UpdateCipher Bridge.FullCipher
55− | CreateCipher Bridge.FullCipher
56 | FireGlobalEvent Event
61 | FireGlobalEvent Event
57− | GeneratePassword Bridge.PasswordGeneratorConfig
58− | OpenNewCipherEditPage Bridge.CipherType
59− | WrongPassword
60− | DeleteCipher Bridge.FullCipher
61− | RequestTotp String
62− | ShowSecondFactorSelect (List Bridge.TwoFactorProviderType)
63− | SelectSecondFactor
64− { provider : Bridge.TwoFactorProviderType
65− , requestFromServer : Bool
66− }
67− | PageMsg PageMsg
68−
69−
70−type PageMsg
71− = LoginMsg Login.Msg
72− | CiphersMsg Ciphers.Msg
73− | MasterPasswordMsg MasterPassword.Msg
74− | CipherMsg Cipher.Msg
75− | EditCipherMsg EditCipher.Msg
76− | SecondFactorSelectMsg SecondFactorSelect.Msg
77− | SecondFactorMsg SecondFactor.Msg
78
62
79
63
80type alias Model =
64type alias Model =
@@ -85,45 +69,19 @@ type alias Model =
85 }
69 }
86
70
87
71
88−findLoginDetails :
89− List PageModel
90− ->
91− Maybe
92− { server : String
93− , email : String
94− , password : String
95− }
96−findLoginDetails xs =
97− case xs of
98− (LoginModel { server, email, password }) :: _ ->
99− Just
100− { server = server
101− , email = email
102− , password = password
103− }
104−
105− _ :: rest ->
106− findLoginDetails rest
107−
108− [] ->
109− Nothing
110−
111−
112appendNotification : Notification.Config -> Model -> ( Model, Cmd Msg )
72appendNotification : Notification.Config -> Model -> ( Model, Cmd Msg )
113appendNotification cfg model =
73appendNotification cfg model =
114 ( { model | notifications = cfg :: model.notifications }, Task.perform UpdateLastNotificatioTime Time.now )
74 ( { model | notifications = cfg :: model.notifications }, Task.perform UpdateLastNotificatioTime Time.now )
115
75
116
76
117type PageModel
77type PageModel
118− = LoginModel (Login.Model Msg)
78+ = LoginModel Login.Model
119 | LoadingPage
79 | LoadingPage
120− | CiphersModel (Ciphers.Model Msg)
80+ | CiphersModel Ciphers.Model
121− | EditCipherModel (EditCipher.Model Msg)
81+ | EditCipherModel EditCipher.Model
122− | MasterPasswordModel (MasterPassword.Model Msg)
82+ | MasterPasswordModel MasterPassword.Model
123− | CipherModel (Cipher.Model Msg)
83+ | CipherModel Cipher.Model
124 | CaptchaModel Captcha.Model
84 | CaptchaModel Captcha.Model
125− | SecondFactorSelectModel (SecondFactorSelect.Model Msg)
126− | SecondFactorModel (SecondFactor.Model Msg)
127
85
128
86
129showPage :
87showPage :
@@ -138,43 +96,31 @@ showPage email page =
138 let
96 let
139 simpleBackButton =
97 simpleBackButton =
140 BackButton { action = PopView, icon = Nothing }
98 BackButton { action = PopView, icon = Nothing }
141−
142− liftPageWith :
143− (pageMsg -> PageMsg)
144− ->
145− { title : List (Html pageMsg)
146− , body : List (Html pageMsg)
147− , topButton : Maybe (Navigation.TopButton Msg)
148− }
149− ->
150− { title : List (Html Msg)
151− , body : List (Html Msg)
152− , topButton : Maybe (Navigation.TopButton Msg)
153− }
154− liftPageWith lift { title, body, topButton } =
155− { title = title |> List.map (Html.map (lift >> PageMsg))
156− , body = body |> List.map (Html.map (lift >> PageMsg))
157− , topButton = topButton
158− }
159 in
99 in
160 case page of
100 case page of
161 LoginModel model ->
101 LoginModel model ->
162− liftPageWith LoginMsg
102+ let
103+ p =
104+ Login.page loginCallbacks LoginMsg
105+ in
163− { title = [ text Login.title ]
106+ { title = p.title model
164− , body = Login.view model
107+ , body = p.view model
165− , topButton = Nothing
108+ , topButton = Nothing
166− }
109+ }
167
110
168 CiphersModel model ->
111 CiphersModel model ->
169− liftPageWith CiphersMsg
112+ let
113+ p =
114+ Ciphers.page ciphersCallbacks CiphersMsg
115+ in
170− { title = Ciphers.title model
116+ { title = p.title model
171− , body = Ciphers.view model
117+ , body = p.view model
172− , topButton =
118+ , topButton =
173− Ciphers.menuConfig email model
119+ Ciphers.menuConfig email model
174− |> Navigation.mapMenuConfig (CiphersMsg >> PageMsg)
120+ |> Navigation.mapMenuConfig CiphersMsg
175− |> Navigation.MenuButton
121+ |> Navigation.MenuButton
176− |> Just
122+ |> Just
177− }
123+ }
178
124
179 LoadingPage ->
125 LoadingPage ->
180 { title = []
126 { title = []
@@ -183,45 +129,44 @@ showPage email page =
183 }
129 }
184
130
185 MasterPasswordModel model ->
131 MasterPasswordModel model ->
186− liftPageWith MasterPasswordMsg
132+ let
133+ p =
134+ MasterPassword.page masterPasswordCallbacks MasterPasswordMsg
135+ in
187− { title = [ text MasterPassword.title ]
136+ { title = p.title model
188− , body = MasterPassword.view model
137+ , body = p.view model
189− , topButton = Nothing
138+ , topButton = Nothing
190− }
139+ }
191
140
192 CipherModel model ->
141 CipherModel model ->
193− liftPageWith CipherMsg
142+ let
143+ p =
144+ Cipher.page cipherCallbacks CipherMsg
145+ in
194− { title = Cipher.title model
146+ { title = p.title model
195− , body = Cipher.view model
147+ , body = p.view model
196− , topButton = Just simpleBackButton
148+ , topButton = Just simpleBackButton
197− }
149+ }
198
150
199 CaptchaModel model ->
151 CaptchaModel model ->
152+ let
153+ p =
154+ Captcha.page captchaCallbacks CaptchaMsg
155+ in
200− { title = [ text Captcha.title ]
156+ { title = p.title model
201− , body = Captcha.view model
157+ , body = p.view model
202 , topButton = Just simpleBackButton
158 , topButton = Just simpleBackButton
203 }
159 }
204
160
205 EditCipherModel model ->
161 EditCipherModel model ->
206− liftPageWith EditCipherMsg
162+ let
163+ p =
164+ EditCipher.page editCipherCallbacks EditCipherMsg
165+ in
207− { title = EditCipher.title model
166+ { title = p.title model
208− , body = EditCipher.view model
167+ , body = p.view model
209− , topButton = Just (BackButton { action = PopView, icon = Just "close" })
168+ , topButton = Just (BackButton { action = PopView, icon = Just "close" })
210− }
211−
212− SecondFactorSelectModel model ->
213− liftPageWith SecondFactorSelectMsg
214− { title = [ text SecondFactorSelect.title ]
215− , body = SecondFactorSelect.view model
216− , topButton = Just simpleBackButton
217− }
218−
219− SecondFactorModel model ->
220− liftPageWith SecondFactorMsg
221− { title = [ SecondFactor.title model |> text ]
222− , body = [ SecondFactor.view model ]
223− , topButton = Just simpleBackButton
224− }
169+ }
225
170
226
171
227main : Program () Model Msg
172main : Program () Model Msg
@@ -237,20 +182,20 @@ main =
237subscriptions : Model -> Sub Msg
182subscriptions : Model -> Sub Msg
238subscriptions model =
183subscriptions model =
239 Sub.batch
184 Sub.batch
240− ([ getBridge (ShowError "Something went wrong!")
185+ ([ getBridge ShowError
241 (\msg ->
186 (\msg ->
242 case msg of
187 case msg of
243 Bridge.NeedsLogin ->
188 Bridge.NeedsLogin ->
244 ShowLoginPage
189 ShowLoginPage
245
190
246 Bridge.Error err ->
191 Bridge.Error err ->
247− ShowError "Something went wrong!" err
192+ ShowError err
248
193
249 Bridge.LoadCiphers ciphers ->
194 Bridge.LoadCiphers ciphers ->
250 LoadCiphers ciphers
195 LoadCiphers ciphers
251
196
252 Bridge.LoginSuccessful ->
197 Bridge.LoginSuccessful ->
253− LoginSuccessful
198+ OpenCiphersScreen
254
199
255 Bridge.Reset ->
200 Bridge.Reset ->
256 Reset
201 Reset
@@ -271,66 +216,10 @@ subscriptions model =
271 PopView
216 PopView
272
217
273 Bridge.CipherChanged c ->
218 Bridge.CipherChanged c ->
274− ShowInfo "Entry updated" ("The entry “" ++ c.name ++ "” has been successfully updated.")
219+ ShowInfo "Entry updated" ("The entry \"" ++ c.name ++ "\" has been successfully updated.")
275−
276− Bridge.GeneratedPassword password ->
277− GlobalEvents.GeneratedPassword password |> FireGlobalEvent
278−
279− Bridge.WrongPassword ->
280− WrongPassword
281−
282− Bridge.CipherDeleted c ->
283− ShowInfo "Entry deleted" ("The entry “" ++ c.name ++ "” has been successfully deleted.")
284−
285− Bridge.Totp totp ->
286− GlobalEvents.DecodedTotp totp |> FireGlobalEvent
287−
288− Bridge.NeedsSecondFactor secondFactors ->
289− case secondFactors of
290− [ provider ] ->
291− SelectSecondFactor
292− { provider = provider
293− , requestFromServer = False
294− }
295−
296− _ ->
297− ShowSecondFactorSelect secondFactors
298 )
220 )
299 ]
221 ]
300 ++ optional (List.isEmpty model.notifications |> not) (Time.every 1000 (\t -> ClearNotification { currentTime = t }))
222 ++ optional (List.isEmpty model.notifications |> not) (Time.every 1000 (\t -> ClearNotification { currentTime = t }))
301− ++ (model.pageStack
302− |> Nonempty.toList
303− |> List.map
304− (\x ->
305− case x of
306− LoginModel _ ->
307− Sub.none
308−
309− LoadingPage ->
310− Sub.none
311−
312− CiphersModel _ ->
313− Sub.none
314−
315− EditCipherModel _ ->
316− Sub.none
317−
318− MasterPasswordModel _ ->
319− Sub.none
320−
321− CipherModel m ->
322− Cipher.subscriptions m
323−
324− CaptchaModel _ ->
325− Sub.none
326−
327− SecondFactorSelectModel _ ->
328− Sub.none
329−
330− SecondFactorModel _ ->
331− Sub.none
332− )
333− )
334 )
223 )
335
224
336
225
@@ -349,10 +238,7 @@ view : Model -> Html Msg
349view model =
238view model =
350 div []
239 div []
351 (maybeList (List.head model.notifications) (Lazy.lazy2 Notification.notification CloseNotification)
240 (maybeList (List.head model.notifications) (Lazy.lazy2 Notification.notification CloseNotification)
352− ++ [ Lazy.lazy2 Navigation.showNavigationView
353− model.pageStack
354− (showPage (Maybe.withDefault "" model.userEmail))
241+ ++ [ Lazy.lazy2 Navigation.showNavigationView model.pageStack (showPage (Maybe.withDefault "" model.userEmail)) ]
355− ]
356 )
242 )
357
243
358
244
@@ -364,6 +250,12 @@ mapHead f (Nonempty x xx) =
364doNotStoreInHistory : PageModel -> Bool
250doNotStoreInHistory : PageModel -> Bool
365doNotStoreInHistory page =
251doNotStoreInHistory page =
366 case page of
252 case page of
253+ LoginModel _ ->
254+ True
255+
256+ MasterPasswordModel _ ->
257+ True
258+
367 LoadingPage ->
259 LoadingPage ->
368 True
260 True
369
261
@@ -374,9 +266,19 @@ doNotStoreInHistory page =
374update : Msg -> Model -> ( Model, Cmd Msg )
266update : Msg -> Model -> ( Model, Cmd Msg )
375update msg model =
267update msg model =
376 let
268 let
377− updatePageStackHead : PageModel -> Model
269+ processPage : (pageModel -> PageModel) -> ( Result String pageModel, Cmd Msg ) -> ( Model, Cmd Msg )
378− updatePageStackHead mdl =
270+ processPage liftModel ( resultModel, cmd ) =
271+ case resultModel of
272+ Ok mdl ->
379− { model | pageStack = mapHead (always mdl) model.pageStack }
273+ ( { model | pageStack = mapHead (always (liftModel mdl)) model.pageStack }, cmd )
274+
275+ Err err ->
276+ appendNotification
277+ { title = "An error had occured"
278+ , message = err
279+ , severity = Notification.Error
280+ }
281+ model
380
282
381 appendPageStack : PageModel -> Model
283 appendPageStack : PageModel -> Model
382 appendPageStack mdl =
284 appendPageStack mdl =
@@ -385,11 +287,22 @@ update msg model =
385 keepStackWith : PageModel -> Model
287 keepStackWith : PageModel -> Model
386 keepStackWith mdl =
288 keepStackWith mdl =
387 { model | pageStack = Nonempty.cons mdl model.pageStack }
289 { model | pageStack = Nonempty.cons mdl model.pageStack }
290+
291+ currentPage =
292+ model.pageStack |> Nonempty.head
388 in
293 in
389 case msg of
294 case msg of
390− ShowError title err ->
295+ RecieveMessage x ->
391 appendNotification
296 appendNotification
392− { title = title
297+ { title = "Got a message!"
298+ , message = x
299+ , severity = Notification.Info
300+ }
301+ model
302+
303+ ShowError err ->
304+ appendNotification
305+ { title = "Something went wrong"
393 , message = err
306 , message = err
394 , severity = Notification.Error
307 , severity = Notification.Error
395 }
308 }
@@ -409,14 +322,29 @@ update msg model =
409 )
322 )
410
323
411 SubmitLogin data ->
324 SubmitLogin data ->
412− ( keepStackWith <| LoadingPage
325+ ( appendPageStack <| LoadingPage
413 , FFI.sendBridge (Bridge.Login data)
326 , FFI.sendBridge (Bridge.Login data)
414 )
327 )
415
328
329+ LoginMsg imsg ->
330+ case currentPage of
331+ LoginModel page ->
332+ (Login.page loginCallbacks LoginMsg).update imsg page |> processPage LoginModel
333+
334+ _ ->
335+ ( model, Cmd.none )
336+
337+ CiphersMsg imsg ->
338+ case currentPage of
339+ CiphersModel page ->
340+ (Ciphers.page ciphersCallbacks CiphersMsg).update imsg page |> processPage CiphersModel
341+
342+ _ ->
343+ ( model, Cmd.none )
344+
416 ShowLoginPage ->
345 ShowLoginPage ->
417− ( Login.init loginCallbacks |> LoginModel |> appendPageStack
346+ (Login.page loginCallbacks LoginMsg).init ()
418− , Cmd.none
347+ |> Tuple.mapFirst (\pageModel -> appendPageStack <| LoginModel pageModel)
419− )
420
348
421 LoadCiphers ciphers ->
349 LoadCiphers ciphers ->
422 let
350 let
@@ -432,6 +360,9 @@ update msg model =
432 _ ->
360 _ ->
433 False
361 False
434 )
362 )
363+
364+ ( pageModel, cmd ) =
365+ (Ciphers.page ciphersCallbacks CiphersMsg).init ciphers
435 in
366 in
436 ( if ciphersShown then
367 ( if ciphersShown then
437 { model
368 { model
@@ -440,8 +371,8 @@ update msg model =
440 |> Nonempty.map
371 |> Nonempty.map
441 (\x ->
372 (\x ->
442 case x of
373 case x of
443− CiphersModel pageModel ->
374+ CiphersModel _ ->
444− CiphersModel (Ciphers.updateCiphers ciphers pageModel)
375+ CiphersModel pageModel
445
376
446 other ->
377 other ->
447 other
378 other
@@ -449,24 +380,21 @@ update msg model =
449 }
380 }
450
381
451 else
382 else
452− appendPageStack <| CiphersModel (Ciphers.init ciphersCallbacks ciphers)
383+ appendPageStack <| CiphersModel pageModel
453− , Cmd.none
384+ , cmd
454 )
385 )
455
386
456− LoginSuccessful ->
387+ -- (Ciphers.page ciphersCallbacks CiphersMsg).init ciphers
457− ( { model | pageStack = Nonempty.singleton LoadingPage }
388+ -- |> Tuple.mapFirst (\pageModel -> appendPageStack <| CiphersModel pageModel)
389+ OpenCiphersScreen ->
458− , Cmd.batch [ sendBridge Bridge.NeedCiphersList, sendBridge Bridge.NeedEmail ]
390+ ( appendPageStack <| LoadingPage, Cmd.batch [ sendBridge Bridge.NeedCiphersList, sendBridge Bridge.NeedEmail ] )
459− )
460
391
461 Reset ->
392 Reset ->
462 init
393 init
463
394
464 ShowMasterPasswordPage { server, login } ->
395 ShowMasterPasswordPage { server, login } ->
465− ( MasterPassword.init masterPasswordCallbacks { server = server, login = login }
396+ (MasterPassword.page masterPasswordCallbacks MasterPasswordMsg).init { server = server, login = login }
466− |> MasterPasswordModel
397+ |> Tuple.mapFirst (\pageModel -> appendPageStack <| MasterPasswordModel pageModel)
467− |> appendPageStack
468− , Cmd.none
469− )
470
398
471 NeedsReset ->
399 NeedsReset ->
472 ( appendPageStack <| LoadingPage, sendBridge Bridge.NeedsReset )
400 ( appendPageStack <| LoadingPage, sendBridge Bridge.NeedsReset )
@@ -474,8 +402,32 @@ update msg model =
474 SendMasterPassword { password } ->
402 SendMasterPassword { password } ->
475 ( appendPageStack <| LoadingPage, sendBridge (Bridge.SendMasterPassword password) )
403 ( appendPageStack <| LoadingPage, sendBridge (Bridge.SendMasterPassword password) )
476
404
405+ MasterPasswordMsg imsg ->
406+ case currentPage of
407+ MasterPasswordModel page ->
408+ (MasterPassword.page masterPasswordCallbacks MasterPasswordMsg).update imsg page |> processPage MasterPasswordModel
409+
410+ _ ->
411+ ( model, Cmd.none )
412+
413+ CipherMsg imsg ->
414+ case currentPage of
415+ CipherModel page ->
416+ (Cipher.page cipherCallbacks CipherMsg).update imsg page |> processPage CipherModel
417+
418+ _ ->
419+ ( model, Cmd.none )
420+
421+ EditCipherMsg imsg ->
422+ case currentPage of
423+ EditCipherModel page ->
424+ (EditCipher.page editCipherCallbacks EditCipherMsg).update imsg page |> processPage EditCipherModel
425+
426+ _ ->
427+ ( model, Cmd.none )
428+
477 ShowCipherPage cipher ->
429 ShowCipherPage cipher ->
478− Cipher.init cipherCallbacks cipher
430+ (Cipher.page cipherCallbacks CipherMsg).init cipher
479 |> Tuple.mapFirst (\pageModel -> appendPageStack <| CipherModel pageModel)
431 |> Tuple.mapFirst (\pageModel -> appendPageStack <| CipherModel pageModel)
480
432
481 RequestCipher id ->
433 RequestCipher id ->
@@ -496,7 +448,16 @@ update msg model =
496 ( model, FFI.sendBridge (Bridge.Open uri) )
448 ( model, FFI.sendBridge (Bridge.Open uri) )
497
449
498 ShowCaptcha uri ->
450 ShowCaptcha uri ->
499− ( Captcha.init uri |> CaptchaModel |> appendPageStack, Cmd.none )
451+ (Captcha.page captchaCallbacks CaptchaMsg).init uri
452+ |> Tuple.mapFirst (\pageModel -> keepStackWith <| CaptchaModel pageModel)
453+
454+ CaptchaMsg imsg ->
455+ case currentPage of
456+ CaptchaModel page ->
457+ (Captcha.page captchaCallbacks CaptchaMsg).update imsg page |> processPage CaptchaModel
458+
459+ _ ->
460+ ( model, Cmd.none )
500
461
501 ClearNotification { currentTime } ->
462 ClearNotification { currentTime } ->
502 if Time.posixToMillis model.lastNotificationTime + notificationLingerSeconds * 1000 > Time.posixToMillis currentTime then
463 if Time.posixToMillis model.lastNotificationTime + notificationLingerSeconds * 1000 > Time.posixToMillis currentTime then
@@ -510,8 +471,11 @@ update msg model =
510 UpdateLastNotificatioTime time ->
471 UpdateLastNotificatioTime time ->
511 ( { model | lastNotificationTime = time }, Cmd.none )
472 ( { model | lastNotificationTime = time }, Cmd.none )
512
473
474+ Noop ->
475+ ( model, Cmd.none )
476+
513 EditCipher cipher ->
477 EditCipher cipher ->
514− EditCipher.init { fullCipher = cipher, callbacks = editCipherCallbacks }
478+ (EditCipher.page editCipherCallbacks EditCipherMsg).init cipher
515 |> Tuple.mapFirst (\pageModel -> keepStackWith <| EditCipherModel pageModel)
479 |> Tuple.mapFirst (\pageModel -> keepStackWith <| EditCipherModel pageModel)
516
480
517 UpdateCipher cipher ->
481 UpdateCipher cipher ->
@@ -523,14 +487,6 @@ update msg model =
523 ]
487 ]
524 )
488 )
525
489
526− CreateCipher cipher ->
527− ( model
528− , Cmd.batch
529− [ Bridge.CreateCipher cipher |> sendBridge
530− , PopView |> pureCmd
531− ]
532− )
533−
534 FireGlobalEvent ev ->
490 FireGlobalEvent ev ->
535 ( { model
491 ( { model
536 | pageStack =
492 | pageStack =
@@ -542,227 +498,51 @@ update msg model =
542 LoadingPage
498 LoadingPage
543
499
544 LoginModel m ->
500 LoginModel m ->
545− LoginModel m
501+ (Login.page loginCallbacks LoginMsg).event m ev |> LoginModel
546
502
547 CiphersModel m ->
503 CiphersModel m ->
548− Ciphers.event m ev |> CiphersModel
504+ (Ciphers.page ciphersCallbacks CiphersMsg).event m ev |> CiphersModel
549
505
550 MasterPasswordModel m ->
506 MasterPasswordModel m ->
551− MasterPasswordModel m
507+ (MasterPassword.page masterPasswordCallbacks MasterPasswordMsg).event m ev |> MasterPasswordModel
552
508
553 CipherModel m ->
509 CipherModel m ->
554− Cipher.event m ev |> CipherModel
510+ (Cipher.page cipherCallbacks CipherMsg).event m ev |> CipherModel
555
511
556 EditCipherModel m ->
512 EditCipherModel m ->
557− EditCipher.event m ev |> EditCipherModel
513+ (EditCipher.page editCipherCallbacks EditCipherMsg).event m ev |> EditCipherModel
558
514
559 CaptchaModel m ->
515 CaptchaModel m ->
560− CaptchaModel m
516+ (Captcha.page captchaCallbacks CaptchaMsg).event m ev |> CaptchaModel
561−
562− SecondFactorSelectModel m ->
563− SecondFactorSelectModel m
564−
565− SecondFactorModel m ->
566− SecondFactorModel m
567 )
517 )
568 }
518 }
569 , Cmd.none
519 , Cmd.none
570 )
520 )
571
521
572− GeneratePassword cfg ->
573− ( model, FFI.sendBridge (Bridge.GeneratePassword cfg) )
574−
575− OpenNewCipherEditPage t ->
576− EditCipher.init
577− { callbacks = createCipherCallbacks
578− , fullCipher =
579− { reprompt = 0
580− , favorite = False
581− , id = ""
582− , name = ""
583− , collectionIds = []
584− , cipher =
585− case t of
586− Bridge.LoginType ->
587− Bridge.LoginCipher
588− { uris = []
589− , username = Nothing
590− , password = Nothing
591− , totp = Nothing
592− }
593−
594− Bridge.NoteType ->
595− Bridge.NoteCipher ""
596−
597− Bridge.CardType ->
598− Bridge.CardCipher
599− { cardholderName = Nothing
600− , brand = Nothing
601− , number = Nothing
602− , expMonth = Nothing
603− , expYear = Nothing
604− , code = Nothing
605− }
606−
607− Bridge.IdentityType ->
608− Bridge.IdentityCipher
609− { title = Nothing
610− , firstName = Nothing
611− , middleName = Nothing
612− , lastName = Nothing
613− , address1 = Nothing
614− , address2 = Nothing
615− , address3 = Nothing
616− , city = Nothing
617− , state = Nothing
618− , postalCode = Nothing
619− , country = Nothing
620− , company = Nothing
621− , email = Nothing
622− , phone = Nothing
623− , ssn = Nothing
624− , username = Nothing
625− , passportNumber = Nothing
626− , licenseNumber = Nothing
627− }
628− }
629− }
630− |> Tuple.mapFirst (\pageModel -> keepStackWith <| EditCipherModel pageModel)
631−
632− WrongPassword ->
633− ( model
634− , Cmd.batch
635− [ pureCmd PopView
636− , pureCmd (ShowError "Login failed" "The login details you provided are not valid. Please try again.")
637− ]
638− )
639−
640− DeleteCipher cipher ->
641− ( model
642− , Cmd.batch
643− [ pureCmd PopView
644− , pureCmd PopView
645− , FFI.sendBridge (Bridge.DeleteCipher cipher)
646− ]
647− )
648−
649− RequestTotp totp ->
650− ( model, FFI.sendBridge (Bridge.RequestTotp totp) )
651−
652− ShowSecondFactorSelect secondFactors ->
653− ( SecondFactorSelect.init secondFactorSelectCallbacks secondFactors
654− |> SecondFactorSelectModel
655− |> appendPageStack
656− , Cmd.none
657− )
658−
659− SelectSecondFactor { provider, requestFromServer } ->
660− case Nonempty.toList model.pageStack |> findLoginDetails of
661− Just { email, password, server } ->
662− ( SecondFactor.init
663− { provider = provider
664− , callbacks =
665− { submit =
666− \{ token, remember } ->
667− SubmitLogin
668− { email = email
669− , password = password
670− , server = server
671− , secondFactor =
672− Just
673− { provider = provider
674− , token = token
675− , remember = remember
676− }
677− }
678− }
679− }
680− |> SecondFactorModel
681− |> appendPageStack
682− , Bridge.ChooseSecondFactor
683− { factor = provider
684− , email = email
685− , password = password
686− , server = server
687− , requestFromServer = requestFromServer
688− }
689− |> FFI.sendBridge
690− )
691−
692− Nothing ->
693− ( model, Cmd.none )
694−
695− PageMsg pageMsg ->
696− case ( pageMsg, model.pageStack |> Nonempty.head ) of
697− ( SecondFactorMsg imsg, SecondFactorModel m ) ->
698− SecondFactor.update m imsg
699− |> Tuple.mapBoth
700− (SecondFactorModel >> updatePageStackHead)
701− (Maybe.map pureCmd >> Maybe.withDefault Cmd.none)
702−
703− ( LoginMsg imsg, LoginModel page ) ->
704− Login.update imsg page
705− |> Tuple.mapFirst (LoginModel >> updatePageStackHead)
706−
707− ( CiphersMsg imsg, CiphersModel page ) ->
708− Ciphers.update imsg page
709− |> Tuple.mapFirst (CiphersModel >> updatePageStackHead)
710−
711− ( MasterPasswordMsg imsg, MasterPasswordModel page ) ->
712− MasterPassword.update imsg page
713− |> Tuple.mapFirst (MasterPasswordModel >> updatePageStackHead)
714−
715− ( CipherMsg imsg, CipherModel page ) ->
716− Cipher.update imsg page
717− |> Tuple.mapFirst (CipherModel >> updatePageStackHead)
718−
719− ( EditCipherMsg imsg, EditCipherModel page ) ->
720− EditCipher.update imsg page
721− |> Tuple.mapFirst (EditCipherModel >> updatePageStackHead)
722−
723− ( SecondFactorSelectMsg imsg, SecondFactorSelectModel m ) ->
724− SecondFactorSelect.update imsg m
725− |> Tuple.mapFirst (SecondFactorSelectModel >> updatePageStackHead)
726−
727− _ ->
728− ( model, Cmd.none )
729−
730
522
731loginCallbacks : Login.Callbacks Msg
523loginCallbacks : Login.Callbacks Msg
732loginCallbacks =
524loginCallbacks =
733− { submit =
734− \{ email, server, password } ->
735− SubmitLogin
525+ { submit = SubmitLogin }
736− { email = email
737− , password = password
738− , server = server
739− , secondFactor = Nothing
740− }
741− }
742
526
743
527
744ciphersCallbacks : Ciphers.Callbacks Msg
528ciphersCallbacks : Ciphers.Callbacks Msg
745ciphersCallbacks =
529ciphersCallbacks =
746− { selected = RequestCipher
530+ { selected = RequestCipher, logOut = NeedsReset }
747− , logOut = NeedsReset
748− , createNewCipher = OpenNewCipherEditPage
749− , lift = CiphersMsg >> PageMsg
750− }
751
531
752
532
753cipherCallbacks : Cipher.Callbacks Msg
533cipherCallbacks : Cipher.Callbacks Msg
754cipherCallbacks =
534cipherCallbacks =
755− { copy = Copy, open = Open, edit = EditCipher, needTotp = RequestTotp }
535+ { copy = Copy, open = Open, edit = EditCipher }
756
536
757
537
758editCipherCallbacks : EditCipher.Callbacks Msg
538editCipherCallbacks : EditCipher.Callbacks Msg
759editCipherCallbacks =
539editCipherCallbacks =
760− { save = UpdateCipher, generatePassword = GeneratePassword, delete = Just DeleteCipher }
540+ { save = UpdateCipher }
761
541
762
542
763−createCipherCallbacks : EditCipher.Callbacks Msg
543+captchaCallbacks : Captcha.Callbacks
764−createCipherCallbacks =
544+captchaCallbacks =
765− { save = CreateCipher, generatePassword = GeneratePassword, delete = Nothing }
545+ {}
766
546
767
547
768masterPasswordCallbacks : MasterPassword.Callbacks Msg
548masterPasswordCallbacks : MasterPassword.Callbacks Msg
@@ -770,11 +550,3 @@ masterPasswordCallbacks =
770 { submit = SendMasterPassword
550 { submit = SendMasterPassword
771 , reset = NeedsReset
551 , reset = NeedsReset
772 }
552 }
773−
774−
775−secondFactorSelectCallbacks : SecondFactorSelect.Callbacks Msg
776−secondFactorSelectCallbacks =
777− { selectFactor =
778− \provider ->
779− SelectSecondFactor { provider = provider, requestFromServer = True }
780− }
frontend/Modal.elmdeleted−33
@@ -1,33 +0,0 @@
1−module Modal exposing (..)
2−
3−import Html exposing (..)
4−import Html.Attributes as Attr
5−import Html.Events as Ev
6−
7−
8−type alias ModelConfig msg =
9− { title : String
10− , body : List (Html msg)
11− , close : msg
12− , footer : List (Html msg)
13− }
14−
15−
16−modal : ModelConfig msg -> Html msg
17−modal cfg =
18− div
19− [ Attr.class "p-modal" ]
20− [ section
21− [ Attr.class "p-modal__dialog"
22− ]
23− (header
24− [ Attr.class "p-modal__header"
25− ]
26− [ h2 [ Attr.class "p-modal__title" ] [ text cfg.title ]
27− , button [ Attr.class "p-modal__close", Ev.onClick cfg.close ] [ text "Close" ]
28− ]
29− :: cfg.body
30− ++ [ footer [ Attr.class "p-modal__footer" ] cfg.footer
31− ]
32− )
33− ]
frontend/Page.elm+15−23
@@ -1,32 +1,24 @@
1module Page exposing (..)
1module Page exposing (..)
2
2
3−import Browser
3+import GlobalEvents exposing (Event)
4import Html exposing (..)
4import Html exposing (..)
5import Html.Events exposing (..)
5import Html.Events exposing (..)
6
6
7
7
8−page :
8+type alias Env =
9− { init : model
9+ {}
10− , view : model -> List (Html msg)
10+
11− , update : msg -> model -> ( model, Cmd (Maybe msg) )
12− }
13− -> Program () model (Maybe msg)
14−page { init, view, update } =
15− Browser.element
16− { subscriptions = always Sub.none
17− , init = \_ -> ( init, Cmd.none )
18− , view = view >> Html.div [] >> Html.map Just
19− , update =
20− \mmsg model ->
21− case mmsg of
22− Nothing ->
23− ( model, Cmd.none )
24
11
25− Just msg ->
12+type alias Page init model imsg emsg =
26− update msg model
13+ (imsg -> emsg)
27− }
14+ -> InitializedPage init model imsg emsg
28
15
29
16
30−noUpdate : a -> model -> ( model, Cmd msg )
17+type alias InitializedPage init model imsg emsg =
31−noUpdate _ model =
32− ( model, Cmd.none )
18+ { init : init -> ( model, Cmd emsg )
19+ , view : model -> List (Html emsg)
20+ , update : imsg -> model -> ( Result String model, Cmd emsg )
21+ , subscriptions : model -> Sub emsg
22+ , title : model -> List (Html emsg)
23+ , event : model -> Event -> model
24+ }
frontend/Pages/Captcha.elm+35−22
@@ -1,4 +1,4 @@
1−module Pages.Captcha exposing (HCaptchSiteKey, Model, init, main, title, view)
1+module Pages.Captcha exposing (Callbacks, HCaptchSiteKey, Model, Msg, page)
2
2
3import Html exposing (..)
3import Html exposing (..)
4import Html.Attributes as Attr
4import Html.Attributes as Attr
@@ -6,15 +6,6 @@ import Page exposing (..)
6import Utils exposing (..)
6import Utils exposing (..)
7
7
8
8
9−main : Program () Model (Maybe (Maybe ()))
10−main =
11− page
12− { init = init "10000000-ffff-ffff-ffff-000000000001"
13− , view = view
14− , update = noUpdate
15− }
16−
17−
18type alias HCaptchSiteKey =
9type alias HCaptchSiteKey =
19 String
10 String
20
11
@@ -24,22 +15,44 @@ type alias Model =
24 }
15 }
25
16
26
17
27−title : String
18+type Msg
28−title =
19+ = Noop
29− "Captcha"
20+
30
21
22+type alias Callbacks =
23+ {}
24+
25+
26+page : Callbacks -> Page HCaptchSiteKey Model Msg emsg
27+page callbacks liftMsg =
28+ { init = \siteKey -> Tuple.mapSecond (Cmd.map liftMsg) (init siteKey)
29+ , view = \model -> view model |> List.map (Html.map liftMsg)
30+ , update = \msg model -> update callbacks liftMsg msg model
31+ , subscriptions = \model -> subscriptions model |> Sub.map liftMsg
32+ , title = always [ text "Captcha" ]
33+ , event = \model _ -> model
34+ }
31
35
36+
32−init : HCaptchSiteKey -> Model
37+init : HCaptchSiteKey -> ( Model, Cmd Msg )
33init siteKey =
38init siteKey =
34− { siteKey = siteKey }
39+ ( { siteKey = siteKey }, Cmd.none )
40+
41+
42+update : Callbacks -> (Msg -> emsg) -> Msg -> Model -> ( Result String Model, Cmd emsg )
43+update _ _ msg model =
44+ case msg of
45+ Noop ->
46+ ( Ok model, Cmd.none )
47+
48+
49+subscriptions : Model -> Sub Msg
50+subscriptions model =
51+ Sub.none
35
52
36
53
37−view : Model -> List (Html msg)
54+view : Model -> List (Html Msg)
38view { siteKey } =
55view { siteKey } =
39− [ iframe
56+ [ div [ Attr.class "u-embedded-media" ]
40− [ Attr.src ("https://iko.soy/warden/hcaptcha.html?" ++ siteKey)
57+ [ iframe [ Attr.src ("https://iko.soy/warden/hcaptcha.html?" ++ siteKey) ] [] ]
41− , Attr.style "width" "100%"
42− , Attr.style "height" "80vh"
43− ]
44− []
45 ]
58 ]
frontend/Pages/Cipher.elm+42−117
@@ -1,14 +1,4 @@
1−module Pages.Cipher exposing
1+module Pages.Cipher exposing (..)
2− ( Callbacks
3− , Model
4− , Msg
5− , event
6− , init
7− , subscriptions
8− , title
9− , update
10− , view
11− )
12
2
13import Bridge
3import Bridge
14import GlobalEvents
4import GlobalEvents
@@ -16,16 +6,13 @@ import Html exposing (..)
16import Html.Attributes as Attr
6import Html.Attributes as Attr
17import Html.Events as Ev
7import Html.Events as Ev
18import Page exposing (..)
8import Page exposing (..)
19−import Time
20import Utils exposing (..)
9import Utils exposing (..)
21
10
22
11
23−type alias Model emsg =
12+type alias Model =
24 { cipher : Bridge.FullCipher
13 { cipher : Bridge.FullCipher
25 , passwordHidden : Bool
14 , passwordHidden : Bool
26 , cvvHidden : Bool
15 , cvvHidden : Bool
27− , decodedTotp : Maybe { code : String, interval : Int }
28− , callbacks : Callbacks emsg
29 }
16 }
30
17
31
18
@@ -34,107 +21,70 @@ type Msg
34 | ToggleCVVVisiblity
21 | ToggleCVVVisiblity
35 | Copy String
22 | Copy String
36 | Open String
23 | Open String
37− | Edit Bridge.FullCipher
38
24
39
25
40type alias Callbacks msg =
26type alias Callbacks msg =
41 { copy : String -> msg
27 { copy : String -> msg
42 , open : String -> msg
28 , open : String -> msg
43 , edit : Bridge.FullCipher -> msg
29 , edit : Bridge.FullCipher -> msg
44− , needTotp : String -> msg
45 }
30 }
46
31
47
32
48−event : Model emsg -> GlobalEvents.Event -> Model emsg
33+page : Callbacks emsg -> Page Bridge.FullCipher Model Msg emsg
49−event model ev =
34+page callbacks liftMsg =
50− case ev of
35+ { init = \data -> Tuple.mapSecond (Cmd.map liftMsg) (init data)
51− GlobalEvents.UpdateCipher c ->
36+ , view = \model -> view model |> List.map (Html.map liftMsg)
52− { model
37+ , update = \msg model -> update callbacks liftMsg msg model
53− | cipher =
38+ , subscriptions = \model -> subscriptions model |> Sub.map liftMsg
54− if c.id == model.cipher.id then
39+ , title =
55− c
56−
57− else
58− model.cipher
59− }
60−
61− GlobalEvents.DecodedTotp { source, code, interval } ->
62− case model.cipher.cipher of
63− Bridge.LoginCipher { totp } ->
64− if totp == Just source then
65− { model | decodedTotp = Just { code = code, interval = interval } }
66−
67− else
68− model
69−
70− _ ->
71− model
72−
73− _ ->
74− model
75−
76−
77−title : Model emsg -> List (Html Msg)
78−title { cipher } =
40+ \{ cipher } ->
79− [ text cipher.name
41+ [ text cipher.name
80− , span [ Attr.class "u-float-right" ] [ iconButton "edit" (Edit cipher) ]
42+ , span [ Attr.class "u-float-right" ] [ iconButton "edit" (callbacks.edit cipher) ]
81− ]
43+ ]
44+ , event =
45+ \model ev ->
46+ case ev of
47+ GlobalEvents.UpdateCipher c ->
48+ { model
49+ | cipher =
50+ if c.id == model.cipher.id then
51+ c
52+
53+ else
54+ model.cipher
55+ }
56+ }
82
57
83
58
84−init : Callbacks emsg -> Bridge.FullCipher -> ( Model emsg, Cmd emsg )
59+init : Bridge.FullCipher -> ( Model, Cmd Msg )
85−init callbacks cipher =
60+init cipher =
86 ( { cipher = cipher
61 ( { cipher = cipher
87 , passwordHidden = True
62 , passwordHidden = True
88 , cvvHidden = True
63 , cvvHidden = True
89− , decodedTotp = Nothing
90− , callbacks = callbacks
91 }
64 }
92− , case cipher.cipher of
93− Bridge.LoginCipher { totp } ->
94− case totp of
95− Just x ->
96− callbacks.needTotp x |> pureCmd
97−
98− Nothing ->
99− Cmd.none
100−
101− _ ->
102− Cmd.none
65+ , Cmd.none
103 )
66 )
104
67
105
68
106−update : Msg -> Model emsg -> ( Model emsg, Cmd emsg )
69+update : Callbacks emsg -> (Msg -> emsg) -> Msg -> Model -> ( Result String Model, Cmd emsg )
107−update msg model =
70+update { copy, open } _ msg model =
108 case msg of
71 case msg of
109 TogglePasswordVisiblity ->
72 TogglePasswordVisiblity ->
110− ( { model | passwordHidden = not model.passwordHidden }, Cmd.none )
73+ ( Ok { model | passwordHidden = not model.passwordHidden }, Cmd.none )
111
74
112 ToggleCVVVisiblity ->
75 ToggleCVVVisiblity ->
113− ( { model | cvvHidden = not model.cvvHidden }, Cmd.none )
76+ ( Ok { model | cvvHidden = not model.cvvHidden }, Cmd.none )
114
77
115 Copy text ->
78 Copy text ->
116− ( model, model.callbacks.copy text |> pureCmd )
79+ ( Ok model, copy text |> pureCmd )
117
80
118 Open uri ->
81 Open uri ->
119− ( model, model.callbacks.open uri |> pureCmd )
82+ ( Ok model, open uri |> pureCmd )
120−
121− Edit cipher ->
122− ( model, model.callbacks.edit cipher |> pureCmd )
123−
124−
125−subscriptions : Model msg -> Sub msg
126−subscriptions { cipher, callbacks } =
127− case cipher.cipher of
128− Bridge.LoginCipher { totp } ->
129− case totp of
130− Just x ->
131− Time.every 1000 (always (callbacks.needTotp x))
132
83
133− Nothing ->
134− Sub.none
135
84
136− _ ->
85+subscriptions : Model -> Sub Msg
86+subscriptions _ =
137− Sub.none
87+ Sub.none
138
88
139
89
140row : { name : String, value : String, nameIcon : String, icons : List ( String, msg ) } -> Html msg
90row : { name : String, value : String, nameIcon : String, icons : List ( String, msg ) } -> Html msg
@@ -147,10 +97,10 @@ row { name, value, nameIcon, icons } =
147 ]
97 ]
148
98
149
99
150−view : Model emsg -> List (Html Msg)
100+view : Model -> List (Html Msg)
151−view { passwordHidden, cipher, cvvHidden, decodedTotp } =
101+view { passwordHidden, cipher, cvvHidden } =
152 (case cipher.cipher of
102 (case cipher.cipher of
153− Bridge.LoginCipher { username, password, uris, totp } ->
103+ Bridge.LoginCipher { username, password, uris } ->
154 maybeList username
104 maybeList username
155 (\x ->
105 (\x ->
156 row
106 row
@@ -170,36 +120,11 @@ view { passwordHidden, cipher, cvvHidden, decodedTotp } =
170 , icons = [ ( hiddenButtonIcon passwordHidden, TogglePasswordVisiblity ), ( "copy", Copy x ) ]
120 , icons = [ ( hiddenButtonIcon passwordHidden, TogglePasswordVisiblity ), ( "copy", Copy x ) ]
171 }
121 }
172 )
122 )
173− ++ maybeList totp
174− (\_ ->
175− row
176− { name = "One-time password"
177− , value =
178− case decodedTotp of
179− Nothing ->
180− "Loading..."
181−
182− Just { code } ->
183− code
184− , nameIcon = "revisions"
185− , icons =
186− case decodedTotp of
187− Nothing ->
188− []
189−
190− Just { code } ->
191− [ ( "copy", Copy code ) ]
192− }
193− )
194 ++ (uris
123 ++ (uris
195 |> List.map
124 |> List.map
196 (\uri ->
125 (\uri ->
197 row
126 row
198− { name = "URL"
199− , value = uri
200− , nameIcon = "get-link"
201− , icons = [ ( "external-link", Open uri ), ( "copy", Copy uri ) ]
127+ { name = "URL", value = uri, nameIcon = "get-link", icons = [ ( "external-link", Open uri ), ( "copy", Copy uri ) ] }
202− }
203 )
128 )
204 )
129 )
205
130
frontend/Pages/Ciphers.elm+81−151
@@ -1,15 +1,4 @@
1−module Pages.Ciphers exposing
1+module Pages.Ciphers exposing (Callbacks, Model, Msg, menuConfig, page)
2− ( Callbacks
3− , Model
4− , Msg
5− , event
6− , init
7− , menuConfig
8− , title
9− , update
10− , updateCiphers
11− , view
12− )
13
2
14import Bridge
3import Bridge
15import GlobalEvents
4import GlobalEvents
@@ -25,23 +14,16 @@ import Search
25import Utils exposing (..)
14import Utils exposing (..)
26
15
27
16
28−type alias Model emsg =
17+type alias Model =
29 { ciphers : Bridge.Sub_LoadCiphers_List
18 { ciphers : Bridge.Sub_LoadCiphers_List
30 , search : String
19 , search : String
31 , menuVisible : Bool
20 , menuVisible : Bool
32 , ciphersListFilter : CiphersFilter
21 , ciphersListFilter : CiphersFilter
33 , scroll : InfiniteScroll.Model Msg
22 , scroll : InfiniteScroll.Model Msg
34 , sublist : { end : Int }
23 , sublist : { end : Int }
35− , cipherSelectDropdownVisivble : Bool
36− , callbacks : Callbacks emsg
37 }
24 }
38
25
39
26
40−updateCiphers : Bridge.Sub_LoadCiphers_List -> Model emsg -> Model emsg
41−updateCiphers ciphers model =
42− { model | ciphers = ciphers }
43−
44−
45type CiphersFilter
27type CiphersFilter
46 = AllCiphers
28 = AllCiphers
47 | SpecificCiphers Bridge.CipherType
29 | SpecificCiphers Bridge.CipherType
@@ -57,7 +39,7 @@ applyCipherFilter filter list =
57 list |> List.filter (.cipherType >> (==) t)
39 list |> List.filter (.cipherType >> (==) t)
58
40
59
41
60−menuConfig : String -> Model emsg -> Navigation.MenuConfig Msg
42+menuConfig : String -> Model -> Navigation.MenuConfig Msg
61menuConfig email { ciphersListFilter, menuVisible } =
43menuConfig email { ciphersListFilter, menuVisible } =
62 { title = email
44 { title = email
63 , items =
45 , items =
@@ -82,7 +64,7 @@ menuConfig email { ciphersListFilter, menuVisible } =
82 , current = ciphersListFilter == SpecificCiphers Bridge.CardType
64 , current = ciphersListFilter == SpecificCiphers Bridge.CardType
83 }
65 }
84 , { icon = "user"
66 , { icon = "user"
85− , name = "Contacts"
67+ , name = "Identities"
86 , trigger = UpdateFilter (SpecificCiphers Bridge.IdentityType)
68 , trigger = UpdateFilter (SpecificCiphers Bridge.IdentityType)
87 , current = ciphersListFilter == SpecificCiphers Bridge.IdentityType
69 , current = ciphersListFilter == SpecificCiphers Bridge.IdentityType
88 }
70 }
@@ -101,137 +83,91 @@ type Msg
101 | LogOut
83 | LogOut
102 | LoadMore InfiniteScroll.Direction
84 | LoadMore InfiniteScroll.Direction
103 | InfiniteScrollMsg InfiniteScroll.Msg
85 | InfiniteScrollMsg InfiniteScroll.Msg
104− | CreateNewCipher Bridge.CipherType
105− | ToggleSelectCipherTypeVisible
106− | SelectCipher String
107
86
108
87
109type alias Callbacks msg =
88type alias Callbacks msg =
110 { selected : String -> msg
89 { selected : String -> msg
111 , logOut : msg
90 , logOut : msg
112− , createNewCipher : Bridge.CipherType -> msg
113− , lift : Msg -> msg
114 }
91 }
115
92
116
93
117−event : Model emsg -> GlobalEvents.Event -> Model emsg
94+page : Callbacks emsg -> Page Bridge.Sub_LoadCiphers_List Model Msg emsg
118−event model ev =
95+page callbacks liftMsg =
119− case ev of
96+ { init = \ciphers -> Tuple.mapSecond (Cmd.map liftMsg) (init ciphers)
120− GlobalEvents.UpdateCipher c ->
97+ , view = view callbacks liftMsg
121− { model
98+ , update = \msg model -> update callbacks liftMsg msg model
122− | ciphers =
99+ , subscriptions = \model -> subscriptions model |> Sub.map liftMsg
123− model.ciphers
100+ , title =
124− |> List.map
125− (\cipher ->
126− if cipher.id == c.id then
127− { cipher | name = c.name }
128−
129− else
130− cipher
131− )
132− }
133−
134− _ ->
135− model
136−
137−
138−title : Model emsg -> List (Html Msg)
139−title { ciphersListFilter, cipherSelectDropdownVisivble } =
101+ \{ ciphersListFilter } ->
140− [ text
102+ [ text
141− (case ciphersListFilter of
103+ (case ciphersListFilter of
142− AllCiphers ->
104+ AllCiphers ->
143− "All items"
105+ "All items"
144−
106+
145− SpecificCiphers cipherType ->
107+ SpecificCiphers cipherType ->
146− case cipherType of
108+ case cipherType of
147− Bridge.CardType ->
109+ Bridge.CardType ->
148− "Cards"
110+ "Cards"
149−
111+
150− Bridge.IdentityType ->
112+ Bridge.IdentityType ->
151− "Contacts"
113+ "Identities"
152−
114+
153− Bridge.LoginType ->
115+ Bridge.LoginType ->
154− "Passwords"
116+ "Passwords"
155−
117+
156− Bridge.NoteType ->
118+ Bridge.NoteType ->
157− "Notes"
119+ "Notes"
158− )
120+ )
159− , span [ Attr.class "u-float-right" ]
121+ ]
160− [ span [ Attr.class "p-contextual-menu" ]
122+ , event =
161− ([ span [] [ iconButton "plus" ToggleSelectCipherTypeVisible ]
123+ \model ev ->
162− ]
124+ case ev of
163− ++ optionals cipherSelectDropdownVisivble
125+ GlobalEvents.UpdateCipher c ->
164− [ div
126+ { model
165− [ Attr.class "overlay"
127+ | ciphers =
166− , Ev.onClick ToggleSelectCipherTypeVisible
128+ model.ciphers
167− ]
129+ |> List.map
168− []
130+ (\cipher ->
169− , span
131+ if cipher.id == c.id then
170− [ Attr.class "p-contextual-menu__dropdown"
132+ { cipher | name = c.name }
171− , Attr.attribute "aria-hidden" "false"
133+
172− ]
134+ else
173− (let
135+ cipher
174− create t =
175− CreateNewCipher t
176− in
177− [ button
178− [ Attr.class "p-contextual-menu__link"
179− , Ev.onClick (create Bridge.LoginType)
180− ]
181− [ text "Login" ]
182− , button
183− [ Attr.class "p-contextual-menu__link"
184− , Ev.onClick (create Bridge.CardType)
185− ]
186− [ text "Card" ]
187− , button
188− [ Attr.class "p-contextual-menu__link"
189− , Ev.onClick (create Bridge.NoteType)
190− ]
191− [ text "Note" ]
192− , button
193− [ Attr.class "p-contextual-menu__link"
194− , Ev.onClick (create Bridge.IdentityType)
195− ]
196− [ text "Contact" ]
197− ]
198− )
136+ )
199− ]
137+ }
200− )
138+ }
201− ]
202− ]
203
139
204
140
205−init : Callbacks emsg -> Bridge.Sub_LoadCiphers_List -> Model emsg
141+init : Bridge.Sub_LoadCiphers_List -> ( Model, Cmd Msg )
206−init callbacks ciphers =
142+init ciphers =
207− { ciphers = ciphers
143+ ( { ciphers = ciphers
208− , search = ""
144+ , search = ""
209− , menuVisible = False
145+ , menuVisible = False
210− , ciphersListFilter = AllCiphers
146+ , ciphersListFilter = AllCiphers
211− , scroll = InfiniteScroll.init (LoadMore >> pureCmd)
147+ , scroll = InfiniteScroll.init (LoadMore >> pureCmd)
212− , sublist = { end = 20 }
148+ , sublist = { end = 20 }
213− , cipherSelectDropdownVisivble = False
149+ }
214− , callbacks = callbacks
150+ , Cmd.none
215− }
151+ )
216
152
217
153
218−update : Msg -> Model emsg -> ( Model emsg, Cmd emsg )
154+update : Callbacks emsg -> (Msg -> emsg) -> Msg -> Model -> ( Result String Model, Cmd emsg )
219−update msg model =
155+update { logOut } liftMsg msg model =
220 case msg of
156 case msg of
221 Noop ->
157 Noop ->
222− ( model, Cmd.none )
158+ ( Ok model, Cmd.none )
223
159
224 UpdateSearch s ->
160 UpdateSearch s ->
225− ( { model | search = s }, Cmd.none )
161+ ( Ok { model | search = s }, Cmd.none )
226
162
227 ToggleMenu ->
163 ToggleMenu ->
228− ( { model | menuVisible = not model.menuVisible }, Cmd.none )
164+ ( Ok { model | menuVisible = not model.menuVisible }, Cmd.none )
229
165
230 UpdateFilter filter ->
166 UpdateFilter filter ->
231− ( { model | ciphersListFilter = filter, menuVisible = False }, Cmd.none )
167+ ( Ok { model | ciphersListFilter = filter, menuVisible = False }, Cmd.none )
232
168
233 LogOut ->
169 LogOut ->
234− ( model, pureCmd model.callbacks.logOut )
170+ ( Ok model, pureCmd logOut )
235
171
236 LoadMore direction ->
172 LoadMore direction ->
237 let
173 let
@@ -243,54 +179,48 @@ update msg model =
243 InfiniteScroll.Bottom ->
179 InfiniteScroll.Bottom ->
244 { end = min (model.sublist.end + 10) (List.length model.ciphers) }
180 { end = min (model.sublist.end + 10) (List.length model.ciphers) }
245 in
181 in
246− ( { model | sublist = newSublist, scroll = InfiniteScroll.stopLoading model.scroll }, Cmd.none )
182+ ( Ok { model | sublist = newSublist, scroll = InfiniteScroll.stopLoading model.scroll }, Cmd.none )
247
183
248 InfiniteScrollMsg msg_ ->
184 InfiniteScrollMsg msg_ ->
249 let
185 let
250 ( scroll, cmd ) =
186 ( scroll, cmd ) =
251 InfiniteScroll.update InfiniteScrollMsg msg_ model.scroll
187 InfiniteScroll.update InfiniteScrollMsg msg_ model.scroll
252 in
188 in
253− ( { model | scroll = scroll }, Cmd.map model.callbacks.lift cmd )
189+ ( Ok { model | scroll = scroll }, Cmd.map liftMsg cmd )
254−
255− CreateNewCipher t ->
256− ( { model | cipherSelectDropdownVisivble = False }, pureCmd (model.callbacks.createNewCipher t) )
257
190
258− ToggleSelectCipherTypeVisible ->
259− ( { model | cipherSelectDropdownVisivble = not model.cipherSelectDropdownVisivble }
260− , Cmd.none
261− )
262
191
263− SelectCipher id ->
192+subscriptions : Model -> Sub Msg
264− ( model, model.callbacks.selected id |> pureCmd )
193+subscriptions _ =
194+ Sub.none
265
195
266
196
267−view : Model emsg -> List (Html Msg)
197+view : Callbacks emsg -> (Msg -> emsg) -> Model -> List (Html emsg)
268−view { ciphers, search, ciphersListFilter, sublist } =
198+view { selected } liftMsg { ciphers, search, ciphersListFilter, sublist } =
269 [ input
199 [ input
270 [ Attr.type_ "search"
200 [ Attr.type_ "search"
271 , Attr.value search
201 , Attr.value search
272− , Ev.onInput UpdateSearch
202+ , Ev.onInput (liftMsg << UpdateSearch)
273 , Attr.placeholder "Search"
203 , Attr.placeholder "Search"
274 , Attr.attribute "autocomplete" "off"
204 , Attr.attribute "autocomplete" "off"
275 ]
205 ]
276 []
206 []
277 , ciphers
207 , ciphers
278 |> applyCipherFilter ciphersListFilter
208 |> applyCipherFilter ciphersListFilter
279− |> Search.searchList search .name (List.take sublist.end >> Lazy.lazy showCiphers)
209+ |> Search.searchList search .name (List.take sublist.end >> Lazy.lazy3 showCiphers liftMsg selected)
280 ]
210 ]
281
211
282
212
283−showCiphers : Bridge.Sub_LoadCiphers_List -> Html Msg
213+showCiphers : (Msg -> msg) -> (String -> msg) -> Bridge.Sub_LoadCiphers_List -> Html msg
284−showCiphers ciphers =
214+showCiphers liftMsg selected ciphers =
285− Keyed.ul [ Attr.class "p-list--divided", InfiniteScroll.infiniteScroll InfiniteScrollMsg, Attr.class "ciphers-list no-scroll-bar" ]
215+ Keyed.ul [ Attr.class "p-list--divided", InfiniteScroll.infiniteScroll (InfiniteScrollMsg >> liftMsg), Attr.class "ciphers-list no-scroll-bar" ]
286 (ciphers
216 (ciphers
287 |> List.map
217 |> List.map
288 (\{ name, date, id } ->
218 (\{ name, date, id } ->
289 ( id
219 ( id
290− , li [ Attr.class "p-list__item cipher-row", Ev.onClick (SelectCipher id) ]
220+ , li [ Attr.class "p-list__item cipher-row", Ev.onClick (selected id) ]
291 [ div [ Attr.class "cipher-row-container" ]
221 [ div [ Attr.class "cipher-row-container" ]
292 [ p [] [ text name ]
222 [ p [] [ text name ]
293− , p [ Attr.class "p-text--small u-align-text--right u-text--muted hide-in-test" ] [ text date ]
223+ , p [ Attr.class "p-text--small u-align-text--right u-text--muted" ] [ text date ]
294 ]
224 ]
295 ]
225 ]
296 )
226 )
frontend/Pages/EditCipher.elm+77−160
@@ -1,134 +1,71 @@
1−module Pages.EditCipher exposing (Callbacks, Model, Msg, event, init, title, update, view)
1+module Pages.EditCipher exposing (..)
2
2
3import Bridge
3import Bridge
4import GlobalEvents
4import GlobalEvents
5import Html exposing (..)
5import Html exposing (..)
6import Html.Attributes as Attr
6import Html.Attributes as Attr
7import Html.Events as Ev
7import Html.Events as Ev
8−import Logic.Cipher exposing (normalizeCipher)
9−import Modal exposing (modal)
10import Page exposing (..)
8import Page exposing (..)
11−import Pages.MasterPassword exposing (Msg)
12−import Pages.PasswordGenerator as PasswordGenerator
13import Utils exposing (..)
9import Utils exposing (..)
14
10
15
11
16−type alias Model msg =
12+type alias Model =
17 { fullCipher : Bridge.FullCipher
13 { fullCipher : Bridge.FullCipher
18− , passwordGenerator : Maybe PasswordGenerator.Model
19− , callbacks : Callbacks msg
20 }
14 }
21
15
22
16
23type Msg
17type Msg
24 = EditCipher Bridge.FullCipher
18 = EditCipher Bridge.FullCipher
25− | PasswordGeneratorMsg PasswordGenerator.Msg
26− | OpenGeneratePasword
27− | CloseGeneratePassword
28− | GeneratePassword Bridge.PasswordGeneratorConfig
29− | Delete
30− | Save
31
19
32
20
33type alias Callbacks msg =
21type alias Callbacks msg =
34− { save : Bridge.FullCipher -> msg
22+ { save : Bridge.FullCipher -> msg }
35− , generatePassword : Bridge.PasswordGeneratorConfig -> msg
23+
36− , delete : Maybe (Bridge.FullCipher -> msg)
24+
37− }
25+page : Callbacks emsg -> Page Bridge.FullCipher Model Msg emsg
38−
26+page callbacks liftMsg =
39−
27+ { init = \data -> Tuple.mapSecond (Cmd.map liftMsg) (init data)
40−title : Model msg -> List (Html Msg)
28+ , view = \model -> view model |> List.map (Html.map liftMsg)
29+ , update = \msg model -> update callbacks liftMsg msg model
30+ , subscriptions = \model -> subscriptions model |> Sub.map liftMsg
31+ , title =
41−title { fullCipher } =
32+ \{ fullCipher } ->
42− [ text fullCipher.name
33+ [ text fullCipher.name
43− , span [ Attr.class "u-float-right" ]
34+ , span [ Attr.class "u-float-right" ] [ iconButton "task-outstanding" (callbacks.save fullCipher) ]
44− [ iconButton "task-outstanding" Save
45− ]
46− ]
35+ ]
47−
36+ , event =
48−
49−event : Model msg -> GlobalEvents.Event -> Model msg
50−event model ev =
37+ \model ev ->
51− case ev of
38+ case ev of
52− GlobalEvents.UpdateCipher c ->
39+ GlobalEvents.UpdateCipher c ->
53− { model
40+ { model
54− | fullCipher =
41+ | fullCipher =
55− if c.id == model.fullCipher.id then
42+ if c.id == model.fullCipher.id then
56− c
43+ c
57−
44+
58− else
45+ else
59− model.fullCipher
60− }
61−
62− GlobalEvents.GeneratedPassword password ->
63− let
64− fullCipher =
65− model.fullCipher
46+ model.fullCipher
66− in
67− { model
68− | passwordGenerator = Nothing
69− , fullCipher =
70− { fullCipher
71− | cipher =
72− case fullCipher.cipher of
73− Bridge.LoginCipher c ->
74− Bridge.LoginCipher
75− { c | password = Just password }
76−
77− c ->
78− c
79 }
47 }
80− }
48+ }
81−
82− _ ->
83− model
84
49
85
50
86−init : { fullCipher : Bridge.FullCipher, callbacks : Callbacks emsg } -> ( Model emsg, Cmd emsg )
51+init : Bridge.FullCipher -> ( Model, Cmd Msg )
87−init { fullCipher, callbacks } =
52+init cipher =
88− ( { fullCipher = fullCipher
53+ ( { fullCipher = cipher
89− , passwordGenerator = Nothing
90− , callbacks = callbacks
91 }
54 }
92 , Cmd.none
55 , Cmd.none
93 )
56 )
94
57
95
58
96−update : Msg -> Model emsg -> ( Model emsg, Cmd emsg )
59+update : Callbacks emsg -> (Msg -> emsg) -> Msg -> Model -> ( Result String Model, Cmd emsg )
97−update msg model =
60+update _ _ msg model =
98 case msg of
61 case msg of
99 EditCipher fullCipher ->
62 EditCipher fullCipher ->
100− ( { model | fullCipher = fullCipher }, Cmd.none )
63+ ( Ok { model | fullCipher = fullCipher }, Cmd.none )
101
64
102− PasswordGeneratorMsg m ->
103− ( { model | passwordGenerator = Maybe.map (PasswordGenerator.update m) model.passwordGenerator }
104− , Cmd.none
105− )
106
65
107− OpenGeneratePasword ->
66+subscriptions : Model -> Sub Msg
108− ( { model | passwordGenerator = Just PasswordGenerator.init }
67+subscriptions _ =
109− , Cmd.none
68+ Sub.none
110− )
111−
112− CloseGeneratePassword ->
113− ( { model | passwordGenerator = Nothing }, Cmd.none )
114−
115− GeneratePassword cfg ->
116− ( model, pureCmd (model.callbacks.generatePassword cfg) )
117−
118− Delete ->
119− ( model
120− , Maybe.withDefault Cmd.none
121− (Maybe.map (\f -> f model.fullCipher |> pureCmd) model.callbacks.delete)
122− )
123−
124− Save ->
125− let
126− { fullCipher } =
127− model
128− in
129− ( model
130− , model.callbacks.save { fullCipher | cipher = normalizeCipher fullCipher.cipher } |> pureCmd
131− )
132
69
133
70
134heading : String -> String -> Html msg
71heading : String -> String -> Html msg
@@ -145,39 +82,23 @@ row { name, nameIcon, attrs } =
145 ]
82 ]
146
83
147
84
148−view : Model emsg -> List (Html Msg)
85+view : Model -> List (Html Msg)
149−view { fullCipher, passwordGenerator, callbacks } =
86+view { fullCipher } =
150− maybeList passwordGenerator
151− (\x ->
152− modal
153− { title = "Password generator"
154− , close = CloseGeneratePassword
155− , footer =
156− [ button
157− [ Attr.class "p-button--positive u-no-margin"
158− , Ev.onClick <| GeneratePassword x.passwordConfig
159− ]
160− [ text "Generate password" ]
161− ]
162− , body =
163− PasswordGenerator.view x |> List.map (Html.map PasswordGeneratorMsg)
164− }
165− )
166− ++ [ row
87+ [ row
167− { name = "Entry name"
88+ { name = "Entry name"
168− , nameIcon = ""
89+ , nameIcon = ""
169− , attrs =
90+ , attrs =
170− [ Attr.type_ "text"
91+ [ Attr.type_ "text"
171− , Attr.value fullCipher.name
92+ , Attr.value fullCipher.name
172− , Ev.onInput (\x -> { fullCipher | name = x } |> EditCipher)
93+ , Ev.onInput (\x -> { fullCipher | name = x } |> EditCipher)
173− , Attr.attribute "autocomplete" "off"
94+ , Attr.attribute "autocomplete" "off"
174− ]
95+ ]
175− }
96+ }
176− ]
97+ ]
177 ++ (case fullCipher.cipher of
98 ++ (case fullCipher.cipher of
178 Bridge.LoginCipher cipher ->
99 Bridge.LoginCipher cipher ->
179 let
100 let
180− { username, password, uris, totp } =
101+ { username, password, uris } =
181 cipher
102 cipher
182
103
183 edit c =
104 edit c =
@@ -193,27 +114,14 @@ view { fullCipher, passwordGenerator, callbacks } =
193 , Attr.attribute "autocomplete" "username"
114 , Attr.attribute "autocomplete" "username"
194 ]
115 ]
195 }
116 }
196− , p []
117+ , row
197− [ heading "security" "Password"
118+ { name = "Password"
198− , br [] []
119+ , nameIcon = "security"
199− , input
120+ , attrs =
200 [ Attr.type_ "text"
121 [ Attr.type_ "text"
201 , Attr.value (Maybe.withDefault "" password)
122 , Attr.value (Maybe.withDefault "" password)
202 , Ev.onInput (\x -> edit { cipher | password = Just x })
123 , Ev.onInput (\x -> edit { cipher | password = Just x })
203 , Attr.attribute "autocomplete" "new-password"
124 , Attr.attribute "autocomplete" "new-password"
204− , Attr.style "width" "calc(100% - 3.5rem)"
205− ]
206− []
207− , iconButton "restart" OpenGeneratePasword
208− ]
209− , row
210− { name = "One-time password"
211− , nameIcon = "revisions"
212− , attrs =
213− [ Attr.type_ "text"
214− , Attr.value (Maybe.withDefault "" totp)
215− , Ev.onInput (\x -> edit { cipher | totp = Just x })
216− , Attr.attribute "autocomplete" "off"
217 ]
125 ]
218 }
126 }
219 ]
127 ]
@@ -247,7 +155,7 @@ view { fullCipher, passwordGenerator, callbacks } =
247 , br [] []
155 , br [] []
248 , textarea
156 , textarea
249 [ Attr.spellcheck True
157 [ Attr.spellcheck True
250− , Attr.style "height" "65vh"
158+ , Attr.style "height" "70vh"
251 , Attr.value contents
159 , Attr.value contents
252 , Ev.onInput (\x -> EditCipher { fullCipher | cipher = Bridge.NoteCipher x })
160 , Ev.onInput (\x -> EditCipher { fullCipher | cipher = Bridge.NoteCipher x })
253 ]
161 ]
@@ -281,8 +189,7 @@ view { fullCipher, passwordGenerator, callbacks } =
281 , Attr.type_ "text"
189 , Attr.type_ "text"
282 , Attr.value (Maybe.withDefault "" expMonth)
190 , Attr.value (Maybe.withDefault "" expMonth)
283 , Ev.onInput (\x -> edit { cipher | expMonth = Just x })
191 , Ev.onInput (\x -> edit { cipher | expMonth = Just x })
284− , Attr.style "width" "30%"
192+ , Attr.style "width" "20%"
285− , Attr.style "min-width" "initial"
286 , Attr.class "u-align--right"
193 , Attr.class "u-align--right"
287 ]
194 ]
288 []
195 []
@@ -292,8 +199,7 @@ view { fullCipher, passwordGenerator, callbacks } =
292 , Attr.type_ "text"
199 , Attr.type_ "text"
293 , Attr.value (Maybe.withDefault "" expYear)
200 , Attr.value (Maybe.withDefault "" expYear)
294 , Ev.onInput (\x -> edit { cipher | expYear = Just x })
201 , Ev.onInput (\x -> edit { cipher | expYear = Just x })
295− , Attr.style "width" "30%"
202+ , Attr.style "width" "20%"
296− , Attr.style "min-width" "initial"
297 ]
203 ]
298 []
204 []
299 ]
205 ]
@@ -459,17 +365,28 @@ view { fullCipher, passwordGenerator, callbacks } =
459 }
365 }
460 ]
366 ]
461 )
367 )
462− ++ optional (callbacks.delete /= Nothing)
368+
463− (button
369+
464− [ Attr.style "width" "100%"
370+hiddenPassword : String
465− , Attr.class "p-button--negative"
371+hiddenPassword =
466− , Ev.onClick Delete
372+ String.repeat 12 "●"
467− ]
373+
468− [ text "Delete" ]
374+
469− )
375+hiddenCvv : String
376+hiddenCvv =
377+ String.repeat 3 "●"
470
378
471
379
472iconButton : String -> msg -> Html msg
380iconButton : String -> msg -> Html msg
473iconButton name msg =
381iconButton name msg =
474 button [ Attr.class "p-button is-inline is-dense has-icon", Ev.onClick msg ]
382 button [ Attr.class "p-button is-inline is-dense has-icon", Ev.onClick msg ]
475 [ i [ Attr.class ("p-icon--" ++ name) ] [] ]
383 [ i [ Attr.class ("p-icon--" ++ name) ] [] ]
384+
385+
386+hiddenButtonIcon : Bool -> String
387+hiddenButtonIcon hidden =
388+ if hidden then
389+ "show"
390+
391+ else
392+ "hide"
frontend/Pages/Login.elm+42−38
@@ -1,4 +1,4 @@
1−module Pages.Login exposing (Callbacks, Model, Msg, init, title, update, view)
1+module Pages.Login exposing (Callbacks, Model, Msg, page)
2
2
3import Html exposing (..)
3import Html exposing (..)
4import Html.Attributes as Attr
4import Html.Attributes as Attr
@@ -7,11 +7,10 @@ import Page exposing (..)
7import Utils exposing (..)
7import Utils exposing (..)
8
8
9
9
10−type alias Model emsg =
10+type alias Model =
11 { server : String
11 { server : String
12 , email : String
12 , email : String
13 , password : String
13 , password : String
14− , callbacks : Callbacks emsg
15 }
14 }
16
15
17
16
@@ -27,49 +26,53 @@ type alias Callbacks emsg =
27 }
26 }
28
27
29
28
30−title : String
29+page : Callbacks emsg -> Page () Model Msg emsg
31−title =
30+page callbacks liftMsg =
31+ { init = \() -> Tuple.mapSecond (Cmd.map liftMsg) init
32+ , view = \model -> view model |> List.map (Html.map liftMsg)
33+ , update = \msg model -> update callbacks liftMsg msg model
34+ , subscriptions = \model -> subscriptions model |> Sub.map liftMsg
32− "Log into bitwarden"
35+ , title = always [ text "Log into bitwarden" ]
36+ , event = \model _ -> model
37+ }
33
38
34
39
35−init : Callbacks emsg -> Model emsg
40+init : ( Model, Cmd Msg )
36−init callbacks =
41+init =
37− { server = "https://vault.bitwarden.com"
42+ ( { server = "https://vault.bitwarden.com"
38− , email = ""
43+ , email = "ilyakooo0@gmail.com"
39− , password = ""
44+ , password = ""
40− , callbacks = callbacks
41− }
45+ }
46+ , Cmd.none
47+ )
42
48
43
49
44−update : Msg -> Model emsg -> ( Model emsg, Cmd emsg )
50+update : Callbacks emsg -> (Msg -> emsg) -> Msg -> Model -> ( Result String Model, Cmd emsg )
45−update msg model =
51+update { submit } liftMsg msg model =
46 case msg of
52 case msg of
47 UpdateEmail email ->
53 UpdateEmail email ->
48− ( { model | email = email }, Cmd.none )
54+ ( Ok { model | email = email }, Cmd.none )
49
55
50 UpdateServer server ->
56 UpdateServer server ->
51− ( { model | server = server }, Cmd.none )
57+ ( Ok { model | server = server }, Cmd.none )
52
58
53 UpdatePassword password ->
59 UpdatePassword password ->
54− ( { model | password = password }, Cmd.none )
60+ ( Ok { model | password = password }, Cmd.none )
55
61
56 Submit ->
62 Submit ->
57− ( model
58− , pureCmd
63+ ( Ok model, pureCmd (submit model) )
59− (model.callbacks.submit
64+
60− { email = model.email
65+
61− , password = model.password
66+subscriptions : Model -> Sub Msg
62− , server = model.server
67+subscriptions model =
63− }
68+ Sub.none
64− )
69+
65− )
70+
66−
67−
68−view : Model emsg -> List (Html Msg)
71+view : Model -> List (Html Msg)
69view model =
72view model =
70 [ form [ Ev.onSubmit Submit ]
73 [ form [ Ev.onSubmit Submit ]
71− [ label [] [ text "Server" ]
74+ ([ label [] [ text "Server" ]
72− , input
75+ , input
73 [ Attr.type_ "url"
76 [ Attr.type_ "url"
74 , Attr.value model.server
77 , Attr.value model.server
75 , Ev.onInput UpdateServer
78 , Ev.onInput UpdateServer
@@ -77,8 +80,8 @@ view model =
77 , Attr.required True
80 , Attr.required True
78 ]
81 ]
79 []
82 []
80− , label [] [ text "Email" ]
83+ , label [] [ text "Email" ]
81− , input
84+ , input
82 [ Attr.type_ "email"
85 [ Attr.type_ "email"
83 , Attr.value model.email
86 , Attr.value model.email
84 , Ev.onInput UpdateEmail
87 , Ev.onInput UpdateEmail
@@ -86,8 +89,8 @@ view model =
86 , Attr.required True
89 , Attr.required True
87 ]
90 ]
88 []
91 []
89− , label [] [ text "Password" ]
92+ , label [] [ text "Password" ]
90− , input
93+ , input
91 [ Attr.type_ "password"
94 [ Attr.type_ "password"
92 , Attr.value model.password
95 , Attr.value model.password
93 , Ev.onInput UpdatePassword
96 , Ev.onInput UpdatePassword
@@ -95,6 +98,7 @@ view model =
95 , Attr.required True
98 , Attr.required True
96 ]
99 ]
97 []
100 []
101+ ]
98− , alignRight [ button [ Attr.type_ "submit" ] [ text "Log in" ] ]
102+ ++ [ alignRight [ button [ Attr.type_ "submit" ] [ text "Log in" ] ] ]
99− ]
103+ )
100 ]
104 ]
frontend/Pages/MasterPassword.elm+30−19
@@ -1,4 +1,4 @@
1−module Pages.MasterPassword exposing (Callbacks, Model, Msg, init, title, update, view)
1+module Pages.MasterPassword exposing (..)
2
2
3import Html exposing (..)
3import Html exposing (..)
4import Html.Attributes as Attr
4import Html.Attributes as Attr
@@ -7,11 +7,10 @@ import Page exposing (..)
7import Utils exposing (..)
7import Utils exposing (..)
8
8
9
9
10−type alias Model emsg =
10+type alias Model =
11 { server : String
11 { server : String
12 , email : String
12 , email : String
13 , password : String
13 , password : String
14− , callbacks : Callbacks emsg
15 }
14 }
16
15
17
16
@@ -27,34 +26,46 @@ type alias Callbacks emsg =
27 }
26 }
28
27
29
28
30−title : String
29+page : Callbacks emsg -> Page { server : String, login : String } Model Msg emsg
31−title =
30+page callbacks liftMsg =
31+ { init = \cfg -> Tuple.mapSecond (Cmd.map liftMsg) (init cfg)
32+ , view = \model -> view model |> List.map (Html.map liftMsg)
33+ , update = \msg model -> update callbacks liftMsg msg model
34+ , subscriptions = \model -> subscriptions model |> Sub.map liftMsg
32− "Enter master password"
35+ , title = always [ text "Enter master password" ]
36+ , event = \model _ -> model
37+ }
33
38
34
39
35−init : Callbacks emsg -> { server : String, login : String } -> Model emsg
40+init : { server : String, login : String } -> ( Model, Cmd Msg )
36−init callbacks { server, login } =
41+init { server, login } =
37− { server = server
42+ ( { server = server
38− , email = login
43+ , email = login
39− , password = ""
44+ , password = ""
40− , callbacks = callbacks
45+ }
41− }
46+ , Cmd.none
47+ )
42
48
43
49
44−update : Msg -> Model emsg -> ( Model emsg, Cmd emsg )
50+update : Callbacks emsg -> (Msg -> emsg) -> Msg -> Model -> ( Result String Model, Cmd emsg )
45−update msg model =
51+update { submit, reset } _ msg model =
46 case msg of
52 case msg of
47 UpdatePassword password ->
53 UpdatePassword password ->
48− ( { model | password = password }, Cmd.none )
54+ ( Ok { model | password = password }, Cmd.none )
49
55
50 Submit ->
56 Submit ->
51− ( model, pureCmd (model.callbacks.submit { password = model.password }) )
57+ ( Ok model, pureCmd (submit { password = model.password }) )
52
58
53 Reset ->
59 Reset ->
54− ( model, pureCmd model.callbacks.reset )
60+ ( Ok model, pureCmd reset )
61+
62+
63+subscriptions : Model -> Sub Msg
64+subscriptions _ =
65+ Sub.none
55
66
56
67
57−view : Model emsg -> List (Html Msg)
68+view : Model -> List (Html Msg)
58view model =
69view model =
59 [ alignRight [ button [ Attr.class "p-button--negative", Ev.onClick Reset ] [ text "Log out" ] ]
70 [ alignRight [ button [ Attr.class "p-button--negative", Ev.onClick Reset ] [ text "Log out" ] ]
60 , table [ Attr.class "p-table--mobile-card u-no-margin" ]
71 , table [ Attr.class "p-table--mobile-card u-no-margin" ]
frontend/Pages/PasswordGenerator.elmdeleted−158
@@ -1,158 +0,0 @@
1−module Pages.PasswordGenerator exposing (Model, Msg, init, update, view)
2−
3−import Bridge
4−import Html exposing (..)
5−import Html.Attributes as Attr
6−import Html.Events as Ev
7−import Switch exposing (switch)
8−
9−
10−type Msg
11− = Noop
12− | UpdateNumberCount Int
13− | ToggleNumber Bool
14− | ToggleAmbiguousCharacters Bool
15− | ToggleCapitals Bool
16− | UpdateNumberOfCapitals Int
17− | UpdateLength Int
18− | ToggleSpecialCharacters Bool
19− | UpdateSpecialCharactersCount Int
20−
21−
22−type alias Model =
23− { passwordConfig : Bridge.PasswordGeneratorConfig
24− }
25−
26−
27−update : Msg -> Model -> Model
28−update msg { passwordConfig } =
29− case msg of
30− Noop ->
31− { passwordConfig = passwordConfig }
32−
33− UpdateNumberCount count ->
34− { passwordConfig = { passwordConfig | minNumber = count } }
35−
36− ToggleNumber enabled ->
37− { passwordConfig = { passwordConfig | includeNumber = enabled } }
38−
39− ToggleAmbiguousCharacters enabled ->
40− { passwordConfig = { passwordConfig | ambiguous = enabled } }
41−
42− ToggleCapitals enabled ->
43− { passwordConfig = { passwordConfig | capitalize = enabled } }
44−
45− UpdateNumberOfCapitals count ->
46− { passwordConfig = { passwordConfig | minUppercase = count } }
47−
48− UpdateLength count ->
49− { passwordConfig = { passwordConfig | length = count } }
50−
51− ToggleSpecialCharacters enabled ->
52− { passwordConfig = { passwordConfig | special = enabled } }
53−
54− UpdateSpecialCharactersCount count ->
55− { passwordConfig = { passwordConfig | minSpecial = count } }
56−
57−
58−init : Model
59−init =
60− { passwordConfig =
61− { ambiguous = True
62− , capitalize = True
63− , includeNumber = True
64− , length = 16
65− , lowercase = True
66− , minLowercase = 1
67− , minNumber = 1
68− , minSpecial = 1
69− , minUppercase = 1
70− , numWords = 1
71− , number = True
72− , special = True
73− , type_ = "password"
74− , uppercase = True
75− , wordSeparator = "-"
76− }
77− }
78−
79−
80−view : Model -> List (Html Msg)
81−view { passwordConfig } =
82− [ passwordGeneratorGroup
83− [ switch
84− { trigger = always Noop
85− , label = "Length"
86− , value = True
87− , hidden = True
88− }
89− , input
90− [ Attr.type_ "number"
91− , Attr.class "is-dense"
92− , Attr.value (String.fromInt passwordConfig.length)
93− , Ev.onInput (String.toInt >> Maybe.withDefault 1 >> UpdateLength)
94− , Attr.min "6"
95− ]
96− []
97− ]
98− , switchRow
99− { label = "Numbers"
100− , enabled = passwordConfig.includeNumber
101− , toggle = ToggleNumber
102− , trigger = UpdateNumberCount
103− , value = passwordConfig.minNumber
104− }
105− , switchRow
106− { label = "Uppercase Characters"
107− , enabled = passwordConfig.capitalize
108− , toggle = ToggleCapitals
109− , trigger = UpdateNumberOfCapitals
110− , value = passwordConfig.minUppercase
111− }
112− , switchRow
113− { label = "Special Characters"
114− , enabled = passwordConfig.special
115− , toggle = ToggleSpecialCharacters
116− , trigger = UpdateSpecialCharactersCount
117− , value = passwordConfig.minSpecial
118− }
119− , switch
120− { trigger = ToggleAmbiguousCharacters
121− , label = "Ambiguous Characters"
122− , value = passwordConfig.ambiguous
123− , hidden = False
124− }
125− ]
126−
127−
128−switchRow :
129− { toggle : Bool -> Msg
130− , label : String
131− , enabled : Bool
132− , value : Int
133− , trigger : Int -> Msg
134− }
135− -> Html Msg
136−switchRow { toggle, label, enabled, value, trigger } =
137− passwordGeneratorGroup
138− [ switch
139− { trigger = toggle
140− , label = label
141− , value = enabled
142− , hidden = False
143− }
144− , input
145− [ Attr.type_ "number"
146− , Attr.class "is-dense"
147− , Attr.value (String.fromInt value)
148− , Ev.onInput (String.toInt >> Maybe.withDefault 1 >> trigger)
149− , Attr.min "1"
150− , Attr.disabled (not enabled)
151− ]
152− []
153− ]
154−
155−
156−passwordGeneratorGroup : List (Html msg) -> Html msg
157−passwordGeneratorGroup =
158− div [ Attr.class "password-generator-group" ]
frontend/Pages/SecondFactor.elmdeleted−91
@@ -1,91 +0,0 @@
1−module Pages.SecondFactor exposing
2− ( Callbacks
3− , Model
4− , Msg
5− , init
6− , title
7− , update
8− , view
9− )
10−
11−import Bridge
12−import Html exposing (..)
13−import Html.Attributes as Attr
14−import Html.Events as Ev
15−import Logic.SecondFactor exposing (secondFactorName)
16−import Utils exposing (..)
17−
18−
19−type alias Callbacks msg =
20− { submit : { token : String, remember : Bool } -> msg
21− }
22−
23−
24−type Msg
25− = Submit
26− | UpdateValue String
27− | UpdateRemember Bool
28−
29−
30−type alias Model msg =
31− { token : String
32− , remember : Bool
33− , factorType : Bridge.TwoFactorProviderType
34− , callbacks : Callbacks msg
35− }
36−
37−
38−init :
39− { provider : Bridge.TwoFactorProviderType
40− , callbacks : Callbacks msg
41− }
42− -> Model msg
43−init { provider, callbacks } =
44− { token = ""
45− , remember = True
46− , factorType = provider
47− , callbacks = callbacks
48− }
49−
50−
51−title : Model msg -> String
52−title { factorType } =
53− secondFactorName factorType
54−
55−
56−update : Model emsg -> Msg -> ( Model emsg, Maybe emsg )
57−update model msg =
58− case msg of
59− Submit ->
60− ( model, model.callbacks.submit { token = model.token, remember = model.remember } |> Just )
61−
62− UpdateValue token ->
63− ( { model | token = token }, Nothing )
64−
65− UpdateRemember remember ->
66− ( { model | remember = remember }, Nothing )
67−
68−
69−view : Model emsg -> Html Msg
70−view { factorType, token, remember } =
71− form [ Ev.onSubmit Submit ]
72− [ label [] [ secondFactorName factorType ++ " code" |> text ]
73− , input
74− [ Attr.type_ "number"
75− , Attr.value token
76− , Attr.required True
77− , Ev.onInput UpdateValue
78− , Attr.attribute "autocomplete" "one-time-code"
79− ]
80− []
81− , label []
82− [ input
83− [ Attr.type_ "checkbox"
84− , Attr.checked remember
85− , Ev.onCheck UpdateRemember
86− ]
87− []
88− , text "Remember this device"
89− ]
90− , alignRight [ button [ Attr.type_ "submit" ] [ text "Submit" ] ]
91− ]
frontend/Pages/SecondFactorSelect.elmdeleted−164
@@ -1,164 +0,0 @@
1−module Pages.SecondFactorSelect exposing
2− ( Callbacks
3− , Model
4− , Msg
5− , init
6− , main
7− , title
8− , update
9− , view
10− )
11−
12−import Bridge
13−import Element as El
14−import Element.Font as Font
15−import Element.Input as Input
16−import Html
17−import Html.Attributes as Attr
18−import Logic.SecondFactor exposing (secondFactorName)
19−import Page exposing (..)
20−import Utils exposing (..)
21−
22−
23−main : Program () (Model (Maybe Msg)) (Maybe Msg)
24−main =
25− Page.page
26− { init =
27− init { selectFactor = always Nothing }
28− [ Bridge.Duo
29− , Bridge.Email
30− ]
31− , view = view
32− , update = update
33− }
34−
35−
36−type alias Model emsg =
37− { availableFactors : List Bridge.TwoFactorProviderType
38− , callbacks : Callbacks emsg
39− }
40−
41−
42−type Msg
43− = SelectFactor Bridge.TwoFactorProviderType
44−
45−
46−type alias Callbacks emsg =
47− { selectFactor : Bridge.TwoFactorProviderType -> emsg
48− }
49−
50−
51−title : String
52−title =
53− "Second factor"
54−
55−
56−init : Callbacks emsg -> List Bridge.TwoFactorProviderType -> Model emsg
57−init callbacks providers =
58− { availableFactors = providers
59− , callbacks = callbacks
60− }
61−
62−
63−update : Msg -> Model emsg -> ( Model emsg, Cmd emsg )
64−update msg model =
65− case msg of
66− SelectFactor factor ->
67− ( model, model.callbacks.selectFactor factor |> pureCmd )
68−
69−
70−view : Model emsg -> List (Html.Html Msg)
71−view { availableFactors } =
72− let
73− ( supportedProviders, unsupportedProviders ) =
74− List.partition providerIsSupported availableFactors
75− in
76− [ El.layout [ El.padding 8 ]
77− (El.column
78− [ El.width El.fill
79− , El.spacing 8
80− ]
81− ([ if List.isEmpty supportedProviders then
82− El.column []
83− [ El.paragraph []
84− [ El.text "Unfortunately, your account has no compatible 2FA providers."
85− ]
86− , El.el
87− [ El.height (El.px 16)
88− ]
89− El.none
90− ]
91−
92− else
93− El.column [ El.centerX ]
94− (supportedProviders
95− |> List.map
96− (\factor ->
97− Input.button [ El.centerX ]
98− { label =
99− Html.button [ Attr.class "p-button" ] [ secondFactorName factor |> Html.text ]
100− |> El.html
101− , onPress = SelectFactor factor |> Just
102− }
103− )
104− )
105− ]
106− ++ (if List.isEmpty unsupportedProviders |> not then
107− [ Html.div
108− [ Attr.class "p-notification--information"
109− ]
110− [ Html.div [ Attr.class "p-notification__content" ]
111− [ Html.h5 [ Attr.class "p-notification__title" ]
112− [ Html.text "The following 2FA providers are not supported"
113− ]
114− , Html.p [ Attr.class "notification__message" ]
115− [ Html.ul []
116− (List.map
117− (secondFactorName >> Html.text >> List.singleton >> Html.li [])
118− unsupportedProviders
119− )
120− ]
121− ]
122− ]
123− |> El.html
124− |> List.singleton
125− |> El.paragraph
126− [ Font.size 16
127− , El.width El.fill
128− ]
129− ]
130−
131− else
132− []
133− )
134− )
135− )
136− ]
137−
138−
139−providerIsSupported : Bridge.TwoFactorProviderType -> Bool
140−providerIsSupported provider =
141− case provider of
142− Bridge.Authenticator ->
143− True
144−
145− Bridge.Duo ->
146− False
147−
148− Bridge.Email ->
149− True
150−
151− Bridge.OrganizationDuo ->
152− False
153−
154− Bridge.Remember ->
155− False
156−
157− Bridge.U2f ->
158− False
159−
160− Bridge.WebAuthn ->
161− False
162−
163− Bridge.Yubikey ->
164− False
frontend/Switch.elmdeleted−35
@@ -1,35 +0,0 @@
1−module Switch exposing (..)
2−
3−import Html exposing (..)
4−import Html.Attributes as Attr
5−import Html.Events as Ev
6−import Utils exposing (..)
7−
8−
9−type alias SwitchConfig msg =
10− { trigger : Bool -> msg
11− , label : String
12− , value : Bool
13− , hidden : Bool
14− }
15−
16−
17−switch : SwitchConfig msg -> Html msg
18−switch cfg =
19− label
20− [ Attr.class "p-switch"
21− ]
22− [ input
23− [ Attr.type_ "checkbox"
24− , Attr.class "p-switch__input"
25− , Ev.onCheck cfg.trigger
26− , Attr.checked cfg.value
27− ]
28− []
29− , span
30− (Attr.class "p-switch__slider"
31− :: optional cfg.hidden (Attr.style "visibility" "hidden")
32− )
33− []
34− , span [ Attr.class "p-switch__label" ] [ text cfg.label ]
35− ]
frontend/Utils.elm−7
@@ -2,7 +2,6 @@ module Utils exposing (..)
2
2
3import Html exposing (..)
3import Html exposing (..)
4import Html.Attributes as Attr
4import Html.Attributes as Attr
5−import Html.Events as Ev
6import Task
5import Task
7
6
8
7
@@ -145,9 +144,3 @@ spanList f aa =
145
144
146 else
145 else
147 ( [], aa )
146 ( [], aa )
148−
149−
150−iconButton : String -> msg -> Html msg
151−iconButton name msg =
152− button [ Attr.class "p-button is-inline is-dense has-icon", Ev.onClick msg ]
153− [ i [ Attr.class ("p-icon--" ++ name) ] [] ]
img/donate.pngdeleted

Binary file not shown.

index.html+2−2
@@ -5,9 +5,9 @@
5 <meta charset="UTF-8">
5 <meta charset="UTF-8">
6 <meta http-equiv="X-UA-Compatible" content="IE=edge">
6 <meta http-equiv="X-UA-Compatible" content="IE=edge">
7 <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
7 <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
8− <link rel="stylesheet" href="vanilla/dist/style.css">
8+ <link rel="stylesheet" href="vanilla/style.css">
9 <link rel="stylesheet" href="style.css">
9 <link rel="stylesheet" href="style.css">
10− <title>Warden</title>
10+ <title>Test.</title>
11 <script src="elm.js"></script>
11 <script src="elm.js"></script>
12</head>
12</head>
13
13
manifest.json+6−6
@@ -1,15 +1,15 @@
1{
1{
2 "name": "soy.iko.warden",
2 "name": "soy.iko.warden",
3− "description": "A simple Bitwarden client",
3+ "description": "A simple bitwarden client",
4 "architecture": "@CLICK_ARCH@",
4 "architecture": "@CLICK_ARCH@",
5 "title": "Warden",
5 "title": "Warden",
6 "hooks": {
6 "hooks": {
7− "warden": {
7+ "appname": {
8− "apparmor": "warden.apparmor",
8+ "apparmor": "appname.apparmor",
9− "desktop": "warden.desktop"
9+ "desktop": "appname.desktop"
10 }
10 }
11 },
11 },
12− "version": "1.2.4",
12+ "version": "1.0.0",
13− "maintainer": "Ilia Kostiuchenko <mail@iko.soy>",
13+ "maintainer": "Ilia Kostiuchenko <email@iko.soy>",
14 "framework": "@CLICK_FRAMEWORK@"
14 "framework": "@CLICK_FRAMEWORK@"
15}
15}
package-lock.jsondeleted−959
@@ -1,959 +0,0 @@
1−{
2− "name": "warden",
3− "lockfileVersion": 3,
4− "requires": true,
5− "packages": {
6− "": {
7− "devDependencies": {
8− "cssnano": "^5.1.15"
9− }
10− },
11− "node_modules/@trysound/sax": {
12− "version": "0.2.0",
13− "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz",
14− "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==",
15− "dev": true,
16− "engines": {
17− "node": ">=10.13.0"
18− }
19− },
20− "node_modules/boolbase": {
21− "version": "1.0.0",
22− "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
23− "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==",
24− "dev": true
25− },
26− "node_modules/browserslist": {
27− "version": "4.21.5",
28− "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz",
29− "integrity": "sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==",
30− "dev": true,
31− "funding": [
32− {
33− "type": "opencollective",
34− "url": "https://opencollective.com/browserslist"
35− },
36− {
37− "type": "tidelift",
38− "url": "https://tidelift.com/funding/github/npm/browserslist"
39− }
40− ],
41− "dependencies": {
42− "caniuse-lite": "^1.0.30001449",
43− "electron-to-chromium": "^1.4.284",
44− "node-releases": "^2.0.8",
45− "update-browserslist-db": "^1.0.10"
46− },
47− "bin": {
48− "browserslist": "cli.js"
49− },
50− "engines": {
51− "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
52− }
53− },
54− "node_modules/caniuse-api": {
55− "version": "3.0.0",
56− "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz",
57− "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==",
58− "dev": true,
59− "dependencies": {
60− "browserslist": "^4.0.0",
61− "caniuse-lite": "^1.0.0",
62− "lodash.memoize": "^4.1.2",
63− "lodash.uniq": "^4.5.0"
64− }
65− },
66− "node_modules/caniuse-lite": {
67− "version": "1.0.30001457",
68− "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001457.tgz",
69− "integrity": "sha512-SDIV6bgE1aVbK6XyxdURbUE89zY7+k1BBBaOwYwkNCglXlel/E7mELiHC64HQ+W0xSKlqWhV9Wh7iHxUjMs4fA==",
70− "dev": true,
71− "funding": [
72− {
73− "type": "opencollective",
74− "url": "https://opencollective.com/browserslist"
75− },
76− {
77− "type": "tidelift",
78− "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
79− }
80− ]
81− },
82− "node_modules/colord": {
83− "version": "2.9.3",
84− "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz",
85− "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==",
86− "dev": true
87− },
88− "node_modules/commander": {
89− "version": "7.2.0",
90− "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
91− "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
92− "dev": true,
93− "engines": {
94− "node": ">= 10"
95− }
96− },
97− "node_modules/css-declaration-sorter": {
98− "version": "6.3.1",
99− "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.3.1.tgz",
100− "integrity": "sha512-fBffmak0bPAnyqc/HO8C3n2sHrp9wcqQz6ES9koRF2/mLOVAx9zIQ3Y7R29sYCteTPqMCwns4WYQoCX91Xl3+w==",
101− "dev": true,
102− "engines": {
103− "node": "^10 || ^12 || >=14"
104− },
105− "peerDependencies": {
106− "postcss": "^8.0.9"
107− }
108− },
109− "node_modules/css-select": {
110− "version": "4.3.0",
111− "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz",
112− "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==",
113− "dev": true,
114− "dependencies": {
115− "boolbase": "^1.0.0",
116− "css-what": "^6.0.1",
117− "domhandler": "^4.3.1",
118− "domutils": "^2.8.0",
119− "nth-check": "^2.0.1"
120− },
121− "funding": {
122− "url": "https://github.com/sponsors/fb55"
123− }
124− },
125− "node_modules/css-tree": {
126− "version": "1.1.3",
127− "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz",
128− "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==",
129− "dev": true,
130− "dependencies": {
131− "mdn-data": "2.0.14",
132− "source-map": "^0.6.1"
133− },
134− "engines": {
135− "node": ">=8.0.0"
136− }
137− },
138− "node_modules/css-what": {
139− "version": "6.1.0",
140− "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz",
141− "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==",
142− "dev": true,
143− "engines": {
144− "node": ">= 6"
145− },
146− "funding": {
147− "url": "https://github.com/sponsors/fb55"
148− }
149− },
150− "node_modules/cssesc": {
151− "version": "3.0.0",
152− "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
153− "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
154− "dev": true,
155− "bin": {
156− "cssesc": "bin/cssesc"
157− },
158− "engines": {
159− "node": ">=4"
160− }
161− },
162− "node_modules/cssnano": {
163− "version": "5.1.15",
164− "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-5.1.15.tgz",
165− "integrity": "sha512-j+BKgDcLDQA+eDifLx0EO4XSA56b7uut3BQFH+wbSaSTuGLuiyTa/wbRYthUXX8LC9mLg+WWKe8h+qJuwTAbHw==",
166− "dev": true,
167− "dependencies": {
168− "cssnano-preset-default": "^5.2.14",
169− "lilconfig": "^2.0.3",
170− "yaml": "^1.10.2"
171− },
172− "engines": {
173− "node": "^10 || ^12 || >=14.0"
174− },
175− "funding": {
176− "type": "opencollective",
177− "url": "https://opencollective.com/cssnano"
178− },
179− "peerDependencies": {
180− "postcss": "^8.2.15"
181− }
182− },
183− "node_modules/cssnano-preset-default": {
184− "version": "5.2.14",
185− "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-5.2.14.tgz",
186− "integrity": "sha512-t0SFesj/ZV2OTylqQVOrFgEh5uanxbO6ZAdeCrNsUQ6fVuXwYTxJPNAGvGTxHbD68ldIJNec7PyYZDBrfDQ+6A==",
187− "dev": true,
188− "dependencies": {
189− "css-declaration-sorter": "^6.3.1",
190− "cssnano-utils": "^3.1.0",
191− "postcss-calc": "^8.2.3",
192− "postcss-colormin": "^5.3.1",
193− "postcss-convert-values": "^5.1.3",
194− "postcss-discard-comments": "^5.1.2",
195− "postcss-discard-duplicates": "^5.1.0",
196− "postcss-discard-empty": "^5.1.1",
197− "postcss-discard-overridden": "^5.1.0",
198− "postcss-merge-longhand": "^5.1.7",
199− "postcss-merge-rules": "^5.1.4",
200− "postcss-minify-font-values": "^5.1.0",
201− "postcss-minify-gradients": "^5.1.1",
202− "postcss-minify-params": "^5.1.4",
203− "postcss-minify-selectors": "^5.2.1",
204− "postcss-normalize-charset": "^5.1.0",
205− "postcss-normalize-display-values": "^5.1.0",
206− "postcss-normalize-positions": "^5.1.1",
207− "postcss-normalize-repeat-style": "^5.1.1",
208− "postcss-normalize-string": "^5.1.0",
209− "postcss-normalize-timing-functions": "^5.1.0",
210− "postcss-normalize-unicode": "^5.1.1",
211− "postcss-normalize-url": "^5.1.0",
212− "postcss-normalize-whitespace": "^5.1.1",
213− "postcss-ordered-values": "^5.1.3",
214− "postcss-reduce-initial": "^5.1.2",
215− "postcss-reduce-transforms": "^5.1.0",
216− "postcss-svgo": "^5.1.0",
217− "postcss-unique-selectors": "^5.1.1"
218− },
219− "engines": {
220− "node": "^10 || ^12 || >=14.0"
221− },
222− "peerDependencies": {
223− "postcss": "^8.2.15"
224− }
225− },
226− "node_modules/cssnano-utils": {
227− "version": "3.1.0",
228− "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-3.1.0.tgz",
229− "integrity": "sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA==",
230− "dev": true,
231− "engines": {
232− "node": "^10 || ^12 || >=14.0"
233− },
234− "peerDependencies": {
235− "postcss": "^8.2.15"
236− }
237− },
238− "node_modules/csso": {
239− "version": "4.2.0",
240− "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz",
241− "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==",
242− "dev": true,
243− "dependencies": {
244− "css-tree": "^1.1.2"
245− },
246− "engines": {
247− "node": ">=8.0.0"
248− }
249− },
250− "node_modules/dom-serializer": {
251− "version": "1.4.1",
252− "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz",
253− "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==",
254− "dev": true,
255− "dependencies": {
256− "domelementtype": "^2.0.1",
257− "domhandler": "^4.2.0",
258− "entities": "^2.0.0"
259− },
260− "funding": {
261− "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
262− }
263− },
264− "node_modules/domelementtype": {
265− "version": "2.3.0",
266− "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
267− "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
268− "dev": true,
269− "funding": [
270− {
271− "type": "github",
272− "url": "https://github.com/sponsors/fb55"
273− }
274− ]
275− },
276− "node_modules/domhandler": {
277− "version": "4.3.1",
278− "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz",
279− "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==",
280− "dev": true,
281− "dependencies": {
282− "domelementtype": "^2.2.0"
283− },
284− "engines": {
285− "node": ">= 4"
286− },
287− "funding": {
288− "url": "https://github.com/fb55/domhandler?sponsor=1"
289− }
290− },
291− "node_modules/domutils": {
292− "version": "2.8.0",
293− "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",
294− "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==",
295− "dev": true,
296− "dependencies": {
297− "dom-serializer": "^1.0.1",
298− "domelementtype": "^2.2.0",
299− "domhandler": "^4.2.0"
300− },
301− "funding": {
302− "url": "https://github.com/fb55/domutils?sponsor=1"
303− }
304− },
305− "node_modules/electron-to-chromium": {
306− "version": "1.4.302",
307− "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.302.tgz",
308− "integrity": "sha512-Uk7C+7aPBryUR1Fwvk9VmipBcN9fVsqBO57jV2ZjTm+IZ6BMNqu7EDVEg2HxCNufk6QcWlFsBkhQyQroB2VWKw==",
309− "dev": true
310− },
311− "node_modules/entities": {
312− "version": "2.2.0",
313− "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
314− "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==",
315− "dev": true,
316− "funding": {
317− "url": "https://github.com/fb55/entities?sponsor=1"
318− }
319− },
320− "node_modules/escalade": {
321− "version": "3.1.1",
322− "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
323− "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
324− "dev": true,
325− "engines": {
326− "node": ">=6"
327− }
328− },
329− "node_modules/lilconfig": {
330− "version": "2.0.6",
331− "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.6.tgz",
332− "integrity": "sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg==",
333− "dev": true,
334− "engines": {
335− "node": ">=10"
336− }
337− },
338− "node_modules/lodash.memoize": {
339− "version": "4.1.2",
340− "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
341− "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==",
342− "dev": true
343− },
344− "node_modules/lodash.uniq": {
345− "version": "4.5.0",
346− "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz",
347− "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==",
348− "dev": true
349− },
350− "node_modules/mdn-data": {
351− "version": "2.0.14",
352− "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz",
353− "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==",
354− "dev": true
355− },
356− "node_modules/nanoid": {
357− "version": "3.3.4",
358− "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
359− "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==",
360− "dev": true,
361− "peer": true,
362− "bin": {
363− "nanoid": "bin/nanoid.cjs"
364− },
365− "engines": {
366− "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
367− }
368− },
369− "node_modules/node-releases": {
370− "version": "2.0.10",
371− "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz",
372− "integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==",
373− "dev": true
374− },
375− "node_modules/normalize-url": {
376− "version": "6.1.0",
377− "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz",
378− "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==",
379− "dev": true,
380− "engines": {
381− "node": ">=10"
382− },
383− "funding": {
384− "url": "https://github.com/sponsors/sindresorhus"
385− }
386− },
387− "node_modules/nth-check": {
388− "version": "2.1.1",
389− "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
390− "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
391− "dev": true,
392− "dependencies": {
393− "boolbase": "^1.0.0"
394− },
395− "funding": {
396− "url": "https://github.com/fb55/nth-check?sponsor=1"
397− }
398− },
399− "node_modules/picocolors": {
400− "version": "1.0.0",
401− "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
402− "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
403− "dev": true
404− },
405− "node_modules/postcss": {
406− "version": "8.4.21",
407− "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.21.tgz",
408− "integrity": "sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==",
409− "dev": true,
410− "funding": [
411− {
412− "type": "opencollective",
413− "url": "https://opencollective.com/postcss/"
414− },
415− {
416− "type": "tidelift",
417− "url": "https://tidelift.com/funding/github/npm/postcss"
418− }
419− ],
420− "peer": true,
421− "dependencies": {
422− "nanoid": "^3.3.4",
423− "picocolors": "^1.0.0",
424− "source-map-js": "^1.0.2"
425− },
426− "engines": {
427− "node": "^10 || ^12 || >=14"
428− }
429− },
430− "node_modules/postcss-calc": {
431− "version": "8.2.4",
432− "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-8.2.4.tgz",
433− "integrity": "sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q==",
434− "dev": true,
435− "dependencies": {
436− "postcss-selector-parser": "^6.0.9",
437− "postcss-value-parser": "^4.2.0"
438− },
439− "peerDependencies": {
440− "postcss": "^8.2.2"
441− }
442− },
443− "node_modules/postcss-colormin": {
444− "version": "5.3.1",
445− "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-5.3.1.tgz",
446− "integrity": "sha512-UsWQG0AqTFQmpBegeLLc1+c3jIqBNB0zlDGRWR+dQ3pRKJL1oeMzyqmH3o2PIfn9MBdNrVPWhDbT769LxCTLJQ==",
447− "dev": true,
448− "dependencies": {
449− "browserslist": "^4.21.4",
450− "caniuse-api": "^3.0.0",
451− "colord": "^2.9.1",
452− "postcss-value-parser": "^4.2.0"
453− },
454− "engines": {
455− "node": "^10 || ^12 || >=14.0"
456− },
457− "peerDependencies": {
458− "postcss": "^8.2.15"
459− }
460− },
461− "node_modules/postcss-convert-values": {
462− "version": "5.1.3",
463− "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-5.1.3.tgz",
464− "integrity": "sha512-82pC1xkJZtcJEfiLw6UXnXVXScgtBrjlO5CBmuDQc+dlb88ZYheFsjTn40+zBVi3DkfF7iezO0nJUPLcJK3pvA==",
465− "dev": true,
466− "dependencies": {
467− "browserslist": "^4.21.4",
468− "postcss-value-parser": "^4.2.0"
469− },
470− "engines": {
471− "node": "^10 || ^12 || >=14.0"
472− },
473− "peerDependencies": {
474− "postcss": "^8.2.15"
475− }
476− },
477− "node_modules/postcss-discard-comments": {
478− "version": "5.1.2",
479− "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-5.1.2.tgz",
480− "integrity": "sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ==",
481− "dev": true,
482− "engines": {
483− "node": "^10 || ^12 || >=14.0"
484− },
485− "peerDependencies": {
486− "postcss": "^8.2.15"
487− }
488− },
489− "node_modules/postcss-discard-duplicates": {
490− "version": "5.1.0",
491− "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-5.1.0.tgz",
492− "integrity": "sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw==",
493− "dev": true,
494− "engines": {
495− "node": "^10 || ^12 || >=14.0"
496− },
497− "peerDependencies": {
498− "postcss": "^8.2.15"
499− }
500− },
501− "node_modules/postcss-discard-empty": {
502− "version": "5.1.1",
503− "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-5.1.1.tgz",
504− "integrity": "sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A==",
505− "dev": true,
506− "engines": {
507− "node": "^10 || ^12 || >=14.0"
508− },
509− "peerDependencies": {
510− "postcss": "^8.2.15"
511− }
512− },
513− "node_modules/postcss-discard-overridden": {
514− "version": "5.1.0",
515− "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-5.1.0.tgz",
516− "integrity": "sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw==",
517− "dev": true,
518− "engines": {
519− "node": "^10 || ^12 || >=14.0"
520− },
521− "peerDependencies": {
522− "postcss": "^8.2.15"
523− }
524− },
525− "node_modules/postcss-merge-longhand": {
526− "version": "5.1.7",
527− "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-5.1.7.tgz",
528− "integrity": "sha512-YCI9gZB+PLNskrK0BB3/2OzPnGhPkBEwmwhfYk1ilBHYVAZB7/tkTHFBAnCrvBBOmeYyMYw3DMjT55SyxMBzjQ==",
529− "dev": true,
530− "dependencies": {
531− "postcss-value-parser": "^4.2.0",
532− "stylehacks": "^5.1.1"
533− },
534− "engines": {
535− "node": "^10 || ^12 || >=14.0"
536− },
537− "peerDependencies": {
538− "postcss": "^8.2.15"
539− }
540− },
541− "node_modules/postcss-merge-rules": {
542− "version": "5.1.4",
543− "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-5.1.4.tgz",
544− "integrity": "sha512-0R2IuYpgU93y9lhVbO/OylTtKMVcHb67zjWIfCiKR9rWL3GUk1677LAqD/BcHizukdZEjT8Ru3oHRoAYoJy44g==",
545− "dev": true,
546− "dependencies": {
547− "browserslist": "^4.21.4",
548− "caniuse-api": "^3.0.0",
549− "cssnano-utils": "^3.1.0",
550− "postcss-selector-parser": "^6.0.5"
551− },
552− "engines": {
553− "node": "^10 || ^12 || >=14.0"
554− },
555− "peerDependencies": {
556− "postcss": "^8.2.15"
557− }
558− },
559− "node_modules/postcss-minify-font-values": {
560− "version": "5.1.0",
561− "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-5.1.0.tgz",
562− "integrity": "sha512-el3mYTgx13ZAPPirSVsHqFzl+BBBDrXvbySvPGFnQcTI4iNslrPaFq4muTkLZmKlGk4gyFAYUBMH30+HurREyA==",
563− "dev": true,
564− "dependencies": {
565− "postcss-value-parser": "^4.2.0"
566− },
567− "engines": {
568− "node": "^10 || ^12 || >=14.0"
569− },
570− "peerDependencies": {
571− "postcss": "^8.2.15"
572− }
573− },
574− "node_modules/postcss-minify-gradients": {
575− "version": "5.1.1",
576− "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-5.1.1.tgz",
577− "integrity": "sha512-VGvXMTpCEo4qHTNSa9A0a3D+dxGFZCYwR6Jokk+/3oB6flu2/PnPXAh2x7x52EkY5xlIHLm+Le8tJxe/7TNhzw==",
578− "dev": true,
579− "dependencies": {
580− "colord": "^2.9.1",
581− "cssnano-utils": "^3.1.0",
582− "postcss-value-parser": "^4.2.0"
583− },
584− "engines": {
585− "node": "^10 || ^12 || >=14.0"
586− },
587− "peerDependencies": {
588− "postcss": "^8.2.15"
589− }
590− },
591− "node_modules/postcss-minify-params": {
592− "version": "5.1.4",
593− "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-5.1.4.tgz",
594− "integrity": "sha512-+mePA3MgdmVmv6g+30rn57USjOGSAyuxUmkfiWpzalZ8aiBkdPYjXWtHuwJGm1v5Ojy0Z0LaSYhHaLJQB0P8Jw==",
595− "dev": true,
596− "dependencies": {
597− "browserslist": "^4.21.4",
598− "cssnano-utils": "^3.1.0",
599− "postcss-value-parser": "^4.2.0"
600− },
601− "engines": {
602− "node": "^10 || ^12 || >=14.0"
603− },
604− "peerDependencies": {
605− "postcss": "^8.2.15"
606− }
607− },
608− "node_modules/postcss-minify-selectors": {
609− "version": "5.2.1",
610− "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-5.2.1.tgz",
611− "integrity": "sha512-nPJu7OjZJTsVUmPdm2TcaiohIwxP+v8ha9NehQ2ye9szv4orirRU3SDdtUmKH+10nzn0bAyOXZ0UEr7OpvLehg==",
612− "dev": true,
613− "dependencies": {
614− "postcss-selector-parser": "^6.0.5"
615− },
616− "engines": {
617− "node": "^10 || ^12 || >=14.0"
618− },
619− "peerDependencies": {
620− "postcss": "^8.2.15"
621− }
622− },
623− "node_modules/postcss-normalize-charset": {
624− "version": "5.1.0",
625− "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-5.1.0.tgz",
626− "integrity": "sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg==",
627− "dev": true,
628− "engines": {
629− "node": "^10 || ^12 || >=14.0"
630− },
631− "peerDependencies": {
632− "postcss": "^8.2.15"
633− }
634− },
635− "node_modules/postcss-normalize-display-values": {
636− "version": "5.1.0",
637− "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-5.1.0.tgz",
638− "integrity": "sha512-WP4KIM4o2dazQXWmFaqMmcvsKmhdINFblgSeRgn8BJ6vxaMyaJkwAzpPpuvSIoG/rmX3M+IrRZEz2H0glrQNEA==",
639− "dev": true,
640− "dependencies": {
641− "postcss-value-parser": "^4.2.0"
642− },
643− "engines": {
644− "node": "^10 || ^12 || >=14.0"
645− },
646− "peerDependencies": {
647− "postcss": "^8.2.15"
648− }
649− },
650− "node_modules/postcss-normalize-positions": {
651− "version": "5.1.1",
652− "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-5.1.1.tgz",
653− "integrity": "sha512-6UpCb0G4eofTCQLFVuI3EVNZzBNPiIKcA1AKVka+31fTVySphr3VUgAIULBhxZkKgwLImhzMR2Bw1ORK+37INg==",
654− "dev": true,
655− "dependencies": {
656− "postcss-value-parser": "^4.2.0"
657− },
658− "engines": {
659− "node": "^10 || ^12 || >=14.0"
660− },
661− "peerDependencies": {
662− "postcss": "^8.2.15"
663− }
664− },
665− "node_modules/postcss-normalize-repeat-style": {
666− "version": "5.1.1",
667− "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.1.1.tgz",
668− "integrity": "sha512-mFpLspGWkQtBcWIRFLmewo8aC3ImN2i/J3v8YCFUwDnPu3Xz4rLohDO26lGjwNsQxB3YF0KKRwspGzE2JEuS0g==",
669− "dev": true,
670− "dependencies": {
671− "postcss-value-parser": "^4.2.0"
672− },
673− "engines": {
674− "node": "^10 || ^12 || >=14.0"
675− },
676− "peerDependencies": {
677− "postcss": "^8.2.15"
678− }
679− },
680− "node_modules/postcss-normalize-string": {
681− "version": "5.1.0",
682− "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-5.1.0.tgz",
683− "integrity": "sha512-oYiIJOf4T9T1N4i+abeIc7Vgm/xPCGih4bZz5Nm0/ARVJ7K6xrDlLwvwqOydvyL3RHNf8qZk6vo3aatiw/go3w==",
684− "dev": true,
685− "dependencies": {
686− "postcss-value-parser": "^4.2.0"
687− },
688− "engines": {
689− "node": "^10 || ^12 || >=14.0"
690− },
691− "peerDependencies": {
692− "postcss": "^8.2.15"
693− }
694− },
695− "node_modules/postcss-normalize-timing-functions": {
696− "version": "5.1.0",
697− "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.1.0.tgz",
698− "integrity": "sha512-DOEkzJ4SAXv5xkHl0Wa9cZLF3WCBhF3o1SKVxKQAa+0pYKlueTpCgvkFAHfk+Y64ezX9+nITGrDZeVGgITJXjg==",
699− "dev": true,
700− "dependencies": {
701− "postcss-value-parser": "^4.2.0"
702− },
703− "engines": {
704− "node": "^10 || ^12 || >=14.0"
705− },
706− "peerDependencies": {
707− "postcss": "^8.2.15"
708− }
709− },
710− "node_modules/postcss-normalize-unicode": {
711− "version": "5.1.1",
712− "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-5.1.1.tgz",
713− "integrity": "sha512-qnCL5jzkNUmKVhZoENp1mJiGNPcsJCs1aaRmURmeJGES23Z/ajaln+EPTD+rBeNkSryI+2WTdW+lwcVdOikrpA==",
714− "dev": true,
715− "dependencies": {
716− "browserslist": "^4.21.4",
717− "postcss-value-parser": "^4.2.0"
718− },
719− "engines": {
720− "node": "^10 || ^12 || >=14.0"
721− },
722− "peerDependencies": {
723− "postcss": "^8.2.15"
724− }
725− },
726− "node_modules/postcss-normalize-url": {
727− "version": "5.1.0",
728− "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-5.1.0.tgz",
729− "integrity": "sha512-5upGeDO+PVthOxSmds43ZeMeZfKH+/DKgGRD7TElkkyS46JXAUhMzIKiCa7BabPeIy3AQcTkXwVVN7DbqsiCew==",
730− "dev": true,
731− "dependencies": {
732− "normalize-url": "^6.0.1",
733− "postcss-value-parser": "^4.2.0"
734− },
735− "engines": {
736− "node": "^10 || ^12 || >=14.0"
737− },
738− "peerDependencies": {
739− "postcss": "^8.2.15"
740− }
741− },
742− "node_modules/postcss-normalize-whitespace": {
743− "version": "5.1.1",
744− "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.1.1.tgz",
745− "integrity": "sha512-83ZJ4t3NUDETIHTa3uEg6asWjSBYL5EdkVB0sDncx9ERzOKBVJIUeDO9RyA9Zwtig8El1d79HBp0JEi8wvGQnA==",
746− "dev": true,
747− "dependencies": {
748− "postcss-value-parser": "^4.2.0"
749− },
750− "engines": {
751− "node": "^10 || ^12 || >=14.0"
752− },
753− "peerDependencies": {
754− "postcss": "^8.2.15"
755− }
756− },
757− "node_modules/postcss-ordered-values": {
758− "version": "5.1.3",
759− "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-5.1.3.tgz",
760− "integrity": "sha512-9UO79VUhPwEkzbb3RNpqqghc6lcYej1aveQteWY+4POIwlqkYE21HKWaLDF6lWNuqCobEAyTovVhtI32Rbv2RQ==",
761− "dev": true,
762− "dependencies": {
763− "cssnano-utils": "^3.1.0",
764− "postcss-value-parser": "^4.2.0"
765− },
766− "engines": {
767− "node": "^10 || ^12 || >=14.0"
768− },
769− "peerDependencies": {
770− "postcss": "^8.2.15"
771− }
772− },
773− "node_modules/postcss-reduce-initial": {
774− "version": "5.1.2",
775− "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-5.1.2.tgz",
776− "integrity": "sha512-dE/y2XRaqAi6OvjzD22pjTUQ8eOfc6m/natGHgKFBK9DxFmIm69YmaRVQrGgFlEfc1HePIurY0TmDeROK05rIg==",
777− "dev": true,
778− "dependencies": {
779− "browserslist": "^4.21.4",
780− "caniuse-api": "^3.0.0"
781− },
782− "engines": {
783− "node": "^10 || ^12 || >=14.0"
784− },
785− "peerDependencies": {
786− "postcss": "^8.2.15"
787− }
788− },
789− "node_modules/postcss-reduce-transforms": {
790− "version": "5.1.0",
791− "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-5.1.0.tgz",
792− "integrity": "sha512-2fbdbmgir5AvpW9RLtdONx1QoYG2/EtqpNQbFASDlixBbAYuTcJ0dECwlqNqH7VbaUnEnh8SrxOe2sRIn24XyQ==",
793− "dev": true,
794− "dependencies": {
795− "postcss-value-parser": "^4.2.0"
796− },
797− "engines": {
798− "node": "^10 || ^12 || >=14.0"
799− },
800− "peerDependencies": {
801− "postcss": "^8.2.15"
802− }
803− },
804− "node_modules/postcss-selector-parser": {
805− "version": "6.0.11",
806− "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.11.tgz",
807− "integrity": "sha512-zbARubNdogI9j7WY4nQJBiNqQf3sLS3wCP4WfOidu+p28LofJqDH1tcXypGrcmMHhDk2t9wGhCsYe/+szLTy1g==",
808− "dev": true,
809− "dependencies": {
810− "cssesc": "^3.0.0",
811− "util-deprecate": "^1.0.2"
812− },
813− "engines": {
814− "node": ">=4"
815− }
816− },
817− "node_modules/postcss-svgo": {
818− "version": "5.1.0",
819− "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-5.1.0.tgz",
820− "integrity": "sha512-D75KsH1zm5ZrHyxPakAxJWtkyXew5qwS70v56exwvw542d9CRtTo78K0WeFxZB4G7JXKKMbEZtZayTGdIky/eA==",
821− "dev": true,
822− "dependencies": {
823− "postcss-value-parser": "^4.2.0",
824− "svgo": "^2.7.0"
825− },
826− "engines": {
827− "node": "^10 || ^12 || >=14.0"
828− },
829− "peerDependencies": {
830− "postcss": "^8.2.15"
831− }
832− },
833− "node_modules/postcss-unique-selectors": {
834− "version": "5.1.1",
835− "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-5.1.1.tgz",
836− "integrity": "sha512-5JiODlELrz8L2HwxfPnhOWZYWDxVHWL83ufOv84NrcgipI7TaeRsatAhK4Tr2/ZiYldpK/wBvw5BD3qfaK96GA==",
837− "dev": true,
838− "dependencies": {
839− "postcss-selector-parser": "^6.0.5"
840− },
841− "engines": {
842− "node": "^10 || ^12 || >=14.0"
843− },
844− "peerDependencies": {
845− "postcss": "^8.2.15"
846− }
847− },
848− "node_modules/postcss-value-parser": {
849− "version": "4.2.0",
850− "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
851− "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
852− "dev": true
853− },
854− "node_modules/source-map": {
855− "version": "0.6.1",
856− "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
857− "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
858− "dev": true,
859− "engines": {
860− "node": ">=0.10.0"
861− }
862− },
863− "node_modules/source-map-js": {
864− "version": "1.0.2",
865− "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
866− "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
867− "dev": true,
868− "peer": true,
869− "engines": {
870− "node": ">=0.10.0"
871− }
872− },
873− "node_modules/stable": {
874− "version": "0.1.8",
875− "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz",
876− "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==",
877− "deprecated": "Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility",
878− "dev": true
879− },
880− "node_modules/stylehacks": {
881− "version": "5.1.1",
882− "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.1.tgz",
883− "integrity": "sha512-sBpcd5Hx7G6seo7b1LkpttvTz7ikD0LlH5RmdcBNb6fFR0Fl7LQwHDFr300q4cwUqi+IYrFGmsIHieMBfnN/Bw==",
884− "dev": true,
885− "dependencies": {
886− "browserslist": "^4.21.4",
887− "postcss-selector-parser": "^6.0.4"
888− },
889− "engines": {
890− "node": "^10 || ^12 || >=14.0"
891− },
892− "peerDependencies": {
893− "postcss": "^8.2.15"
894− }
895− },
896− "node_modules/svgo": {
897− "version": "2.8.0",
898− "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz",
899− "integrity": "sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==",
900− "dev": true,
901− "dependencies": {
902− "@trysound/sax": "0.2.0",
903− "commander": "^7.2.0",
904− "css-select": "^4.1.3",
905− "css-tree": "^1.1.3",
906− "csso": "^4.2.0",
907− "picocolors": "^1.0.0",
908− "stable": "^0.1.8"
909− },
910− "bin": {
911− "svgo": "bin/svgo"
912− },
913− "engines": {
914− "node": ">=10.13.0"
915− }
916− },
917− "node_modules/update-browserslist-db": {
918− "version": "1.0.10",
919− "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz",
920− "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==",
921− "dev": true,
922− "funding": [
923− {
924− "type": "opencollective",
925− "url": "https://opencollective.com/browserslist"
926− },
927− {
928− "type": "tidelift",
929− "url": "https://tidelift.com/funding/github/npm/browserslist"
930− }
931− ],
932− "dependencies": {
933− "escalade": "^3.1.1",
934− "picocolors": "^1.0.0"
935− },
936− "bin": {
937− "browserslist-lint": "cli.js"
938− },
939− "peerDependencies": {
940− "browserslist": ">= 4.21.0"
941− }
942− },
943− "node_modules/util-deprecate": {
944− "version": "1.0.2",
945− "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
946− "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
947− "dev": true
948− },
949− "node_modules/yaml": {
950− "version": "1.10.2",
951− "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
952− "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==",
953− "dev": true,
954− "engines": {
955− "node": ">= 6"
956− }
957− }
958− }
959−}
packages.dhall+6−6
@@ -99,8 +99,8 @@ in upstream
99-------------------------------
99-------------------------------
100-}
100-}
101let upstream =
101let upstream =
102− https://github.com/purescript/package-sets/releases/download/psc-0.15.4-20220808/packages.dhall
102+ https://github.com/purescript/package-sets/releases/download/psc-0.15.2-20220706/packages.dhall
103− sha256:60eee64b04ca0013fae3e02a69fc3b176105c6baa2f31865c67cd5f881a412fd
103+ sha256:7a24ebdbacb2bfa27b2fc6ce3da96f048093d64e54369965a2a7b5d9892b6031
104
104
105in upstream
105in upstream
106 with argonaut-aeson-generic =
106 with argonaut-aeson-generic =
@@ -125,7 +125,7 @@ in upstream
125 ]
125 ]
126 , repo =
126 , repo =
127 "https://github.com/ilyakooo0/purescript-argonaut-aeson-generic.git"
127 "https://github.com/ilyakooo0/purescript-argonaut-aeson-generic.git"
128− , version = "3e388500811ef4b6718fc776789f3be050ad7b57"
128+ , version = "purescript-0.14"
129 }
129 }
130 with untagged-union =
130 with untagged-union =
131 { dependencies =
131 { dependencies =
@@ -142,12 +142,12 @@ in upstream
142 , "tuples"
142 , "tuples"
143 , "unsafe-coerce"
143 , "unsafe-coerce"
144 ]
144 ]
145− , repo = "https://github.com/rowtype-yoga/purescript-untagged-union.git"
145+ , repo = "https://github.com/ilyakooo0/purescript-untagged-union.git"
146− , version = "6c5bccef38696a16e86ba9a65cf778b4462c4a66"
146+ , version = "iko/ps-15"
147 }
147 }
148 with literals =
148 with literals =
149 { repo = "https://github.com/ilyakooo0/purescript-literals.git"
149 { repo = "https://github.com/ilyakooo0/purescript-literals.git"
150− , version = "6875fb28026595cfb780318305a77e79b098bb01"
150+ , version = "iko/_null"
151 , dependencies =
151 , dependencies =
152 [ "assert"
152 [ "assert"
153 , "effect"
153 , "effect"
registry.datdeleted

Binary file not shown.

scratchpad/password_generator.htmldeleted−74
@@ -1,74 +0,0 @@
1−<!DOCTYPE html>
2−<html lang="en">
3−
4−<head>
5− <meta charset="UTF-8">
6− <meta http-equiv="X-UA-Compatible" content="IE=edge">
7− <meta name="viewport" content="width=device-width, initial-scale=1.0">
8− <link rel="stylesheet" href="../vanilla/dist/style.css">
9− <link rel="stylesheet" href="../style.css">
10− <title>Password generator</title>
11−
12− <style>
13− body {
14− margin: 10px;
15− }
16−
17− </style>
18−
19−</head>
20−
21−<body>
22−
23− <div class="select-second-factor-container">
24− <button class="p-button">Hello</button>
25− <button class="p-button">Bar</button>
26− <button class="p-button">Bar</button>
27− <button class="p-button">Bardi</button>
28− <button class="p-button">Wolrd</button>
29− </div>
30−
31− <div class="password-generator-group">
32− <p>Password length</p>
33− <input type="number" class="p-slider__input">
34− </div>
35−
36−
37− <div class="password-generator-group">
38− <label class="p-switch">
39− <input type="checkbox" class="p-switch__input">
40− <span class="p-switch__slider"></span>
41− <span class="p-switch__label">Numbers</span>
42− </label>
43− <input type="number" class="p-slider__input">
44− </div>
45−
46− <form action="">
47− <label><input type="checkbox" name="" id="">Hahahah</label>
48− <input type="checkbox">
49−
50− </form>
51−
52−
53−
54− <input type="range" min="0" max="100" value="50" step="1">
55− <input type="range" min="0" max="100" value="50" step="1"
56− style="background-image: -webkit-gradient(linear, 0% 0%, 100% 0%, color-stop(0.5, rgb(0, 102, 204)), color-stop(0.5, rgb(217, 217, 217)));">
57− <h1>h</h1>
58− <div class="p-slider__wrapper">
59− <input type="range" min="0" max="100" value="50" step="1" id="slider4" disabled=""
60− aria-label="Example of a disabled slider, range 0 to 100">
61− <input class="p-slider__input" type="text" maxlength="3" id="slider4-input" disabled="" tabindex="0">
62− </div>
63− <div class="p-slider__wrapper">
64− <input type="range" min="0" max="100" value="50" step="1" id="slider3" aria-label="Example slider, range 0 to 100">
65− <input class="p-slider__input" type="number" maxlength="3" id="slider3-input" tabindex="0">
66− </div>
67−
68− <form>
69− <input type="range" min="0" max="100" value="50" step="1">
70− </form>
71−
72−</body>
73−
74−</html>
screenshots/1.pngdeleted

Binary file not shown.

screenshots/2.pngdeleted

Binary file not shown.

screenshots/3.pngdeleted

Binary file not shown.

screenshots/4.pngdeleted

Binary file not shown.

screenshots/5.pngdeleted

Binary file not shown.

spago-packages.nixdeleted−1102
@@ -1,1102 +0,0 @@
1−# This file was generated by Spago2Nix
2−
3−{ pkgs ? import <nixpkgs> {} }:
4−
5−let
6− inputs = {
7−
8− "aff" = pkgs.stdenv.mkDerivation {
9− name = "aff";
10− version = "v7.0.0";
11− src = pkgs.fetchgit {
12− url = "https://github.com/purescript-contrib/purescript-aff.git";
13− rev = "2d44d9f9d0d6a416a4103fba2fb39e5160f80e36";
14− sha256 = "1d38l16s8hvfqq5w40w21j5089svdiggkraw3rwrl6ml7lijjqx4";
15− };
16− phases = "installPhase";
17− installPhase = "ln -s $src $out";
18− };
19−
20− "aff-promise" = pkgs.stdenv.mkDerivation {
21− name = "aff-promise";
22− version = "v4.0.0";
23− src = pkgs.fetchgit {
24− url = "https://github.com/nwolverson/purescript-aff-promise.git";
25− rev = "3aa74e68e3e4c3e38d821375703e0b2f49d831eb";
26− sha256 = "10gv3b05dxnlmr9zmigqs1hz4xqrx7da30aqh12kqg3zrg49k9cc";
27− };
28− phases = "installPhase";
29− installPhase = "ln -s $src $out";
30− };
31−
32− "argonaut" = pkgs.stdenv.mkDerivation {
33− name = "argonaut";
34− version = "v9.0.0";
35− src = pkgs.fetchgit {
36− url = "https://github.com/purescript-contrib/purescript-argonaut.git";
37− rev = "7505a47f2edb0c9cacaac4f11dcedf4723a3e9c8";
38− sha256 = "18yxhlrwri6q858krz1klyq29fx8nvfm16c04wm2rn91mgyasn6x";
39− };
40− phases = "installPhase";
41− installPhase = "ln -s $src $out";
42− };
43−
44− "argonaut-aeson-generic" = pkgs.stdenv.mkDerivation {
45− name = "argonaut-aeson-generic";
46− version = "3e388500811ef4b6718fc776789f3be050ad7b57";
47− src = pkgs.fetchgit {
48− url = "https://github.com/ilyakooo0/purescript-argonaut-aeson-generic.git";
49− rev = "3e388500811ef4b6718fc776789f3be050ad7b57";
50− sha256 = "0yhcxxz5lpgwxxsx93kmr884rk5nrvgy7lmpaixazqps2a2q0fhc";
51− };
52− phases = "installPhase";
53− installPhase = "ln -s $src $out";
54− };
55−
56− "argonaut-codecs" = pkgs.stdenv.mkDerivation {
57− name = "argonaut-codecs";
58− version = "v9.0.0";
59− src = pkgs.fetchgit {
60− url = "https://github.com/purescript-contrib/purescript-argonaut-codecs.git";
61− rev = "176a5dddae28fda9a2ff31ed4bf1efcc148513a4";
62− sha256 = "08ys445222syysxh24la8bc8l20lwwqi4vdfa3ph2a60l1lx3kp8";
63− };
64− phases = "installPhase";
65− installPhase = "ln -s $src $out";
66− };
67−
68− "argonaut-core" = pkgs.stdenv.mkDerivation {
69− name = "argonaut-core";
70− version = "v7.0.0";
71− src = pkgs.fetchgit {
72− url = "https://github.com/purescript-contrib/purescript-argonaut-core.git";
73− rev = "68da81dd80ec36d3b013eff46dc067a972c22e5d";
74− sha256 = "0hxl17ddbflkk0hchjgk5xj6j9fwp3b182w4g073p0dwscdl4f08";
75− };
76− phases = "installPhase";
77− installPhase = "ln -s $src $out";
78− };
79−
80− "argonaut-generic" = pkgs.stdenv.mkDerivation {
81− name = "argonaut-generic";
82− version = "v8.0.0";
83− src = pkgs.fetchgit {
84− url = "https://github.com/purescript-contrib/purescript-argonaut-generic.git";
85− rev = "2df4080f036762df91a24b22842e389395ef0bdd";
86− sha256 = "0f3bkmv37nk89vscvivdr971sjydqiw7lwk9hfdfms05id8jdjkw";
87− };
88− phases = "installPhase";
89− installPhase = "ln -s $src $out";
90− };
91−
92− "argonaut-traversals" = pkgs.stdenv.mkDerivation {
93− name = "argonaut-traversals";
94− version = "v10.0.0";
95− src = pkgs.fetchgit {
96− url = "https://github.com/purescript-contrib/purescript-argonaut-traversals.git";
97− rev = "8d2403d8d57afb568933dbb36063d5670ce770a0";
98− sha256 = "0v965bcl1hdp1hacbzxcm35kxxclixj0kpsdm816gs7m548z6l43";
99− };
100− phases = "installPhase";
101− installPhase = "ln -s $src $out";
102− };
103−
104− "arraybuffer" = pkgs.stdenv.mkDerivation {
105− name = "arraybuffer";
106− version = "v13.0.0";
107− src = pkgs.fetchgit {
108− url = "https://github.com/purescript-contrib/purescript-arraybuffer.git";
109− rev = "3b5ba3040f060057e864805546a50f319853517d";
110− sha256 = "0cfi15r95zd7ikvs8v3pf2i3rzxjqxk7209j96d8qs6jzqlypbwd";
111− };
112− phases = "installPhase";
113− installPhase = "ln -s $src $out";
114− };
115−
116− "arraybuffer-types" = pkgs.stdenv.mkDerivation {
117− name = "arraybuffer-types";
118− version = "v3.0.2";
119− src = pkgs.fetchgit {
120− url = "https://github.com/purescript-contrib/purescript-arraybuffer-types.git";
121− rev = "9b0b7a0f9ee034e039f3d3a2a9c3f74eb7c9264a";
122− sha256 = "1q3111jk6jj64bxy86lc7ik6q5mgdz87bc086wj5rm57dmlivlxx";
123− };
124− phases = "installPhase";
125− installPhase = "ln -s $src $out";
126− };
127−
128− "arrays" = pkgs.stdenv.mkDerivation {
129− name = "arrays";
130− version = "v7.0.0";
131− src = pkgs.fetchgit {
132− url = "https://github.com/purescript/purescript-arrays.git";
133− rev = "d20bae2f76db6cf29b7b75e26e82b8a54c32295e";
134− sha256 = "1ciz796yrdfnqiskjjhh6r3bn9mks1dm5zin7kwzh1nfy1i4w9pz";
135− };
136− phases = "installPhase";
137− installPhase = "ln -s $src $out";
138− };
139−
140− "assert" = pkgs.stdenv.mkDerivation {
141− name = "assert";
142− version = "v6.0.0";
143− src = pkgs.fetchgit {
144− url = "https://github.com/purescript/purescript-assert.git";
145− rev = "27c0edb57d2ee497eb5fab664f5601c35b613eda";
146− sha256 = "02qpcsijil64qq4kcikv2ynxl85zkryvv0vmzjm6752956ymzi0j";
147− };
148− phases = "installPhase";
149− installPhase = "ln -s $src $out";
150− };
151−
152− "avar" = pkgs.stdenv.mkDerivation {
153− name = "avar";
154− version = "v5.0.0";
155− src = pkgs.fetchgit {
156− url = "https://github.com/purescript-contrib/purescript-avar.git";
157− rev = "d00f5784d9cc8f079babd62740f5c52b87e5caa5";
158− sha256 = "132g0ccmjr8328xpaycww8b5wmx1vi3li4irxxqcjk5wi47i8c9h";
159− };
160− phases = "installPhase";
161− installPhase = "ln -s $src $out";
162− };
163−
164− "bifunctors" = pkgs.stdenv.mkDerivation {
165− name = "bifunctors";
166− version = "v6.0.0";
167− src = pkgs.fetchgit {
168− url = "https://github.com/purescript/purescript-bifunctors.git";
169− rev = "16ba2fb6dd7f05528ebd9e2f9ca3a068b325e5b3";
170− sha256 = "1qz5aaiq5w4g1rhjb63z47h6nqhw8hw23x1zw5alam047pq4vfkg";
171− };
172− phases = "installPhase";
173− installPhase = "ln -s $src $out";
174− };
175−
176− "catenable-lists" = pkgs.stdenv.mkDerivation {
177− name = "catenable-lists";
178− version = "v7.0.0";
179− src = pkgs.fetchgit {
180− url = "https://github.com/purescript/purescript-catenable-lists.git";
181− rev = "09abe1f4888bc00841ad2b59e56a9e7ce7ebd4ab";
182− sha256 = "0fn6caspdnb0nrlj31sy7d5hxrhndfz7a2b9wfkxf5bv5vpjyawb";
183− };
184− phases = "installPhase";
185− installPhase = "ln -s $src $out";
186− };
187−
188− "console" = pkgs.stdenv.mkDerivation {
189− name = "console";
190− version = "v6.0.0";
191− src = pkgs.fetchgit {
192− url = "https://github.com/purescript/purescript-console.git";
193− rev = "3b83d7b792d03872afeea5e62b4f686ab0f09842";
194− sha256 = "0fr5l1myhscp910mybp04cg6g8f2hy3ikjfc8fkqlb2dm2cqzdfs";
195− };
196− phases = "installPhase";
197− installPhase = "ln -s $src $out";
198− };
199−
200− "const" = pkgs.stdenv.mkDerivation {
201− name = "const";
202− version = "v6.0.0";
203− src = pkgs.fetchgit {
204− url = "https://github.com/purescript/purescript-const.git";
205− rev = "ab9570cf2b6e67f7e441178211db1231cfd75c37";
206− sha256 = "0mcpwqqf5bcwxlzk53smvsjjz1ymlnq0ypphg6sc83ibw70g64f6";
207− };
208− phases = "installPhase";
209− installPhase = "ln -s $src $out";
210− };
211−
212− "contravariant" = pkgs.stdenv.mkDerivation {
213− name = "contravariant";
214− version = "v6.0.0";
215− src = pkgs.fetchgit {
216− url = "https://github.com/purescript/purescript-contravariant.git";
217− rev = "9ad3e105b8855bcc25f4e0893c784789d05a58de";
218− sha256 = "1xkd3rfs8v20w7mj6sj6dmhfb2vb1zpmjv245xj1rahk3g16qm7m";
219− };
220− phases = "installPhase";
221− installPhase = "ln -s $src $out";
222− };
223−
224− "control" = pkgs.stdenv.mkDerivation {
225− name = "control";
226− version = "v6.0.0";
227− src = pkgs.fetchgit {
228− url = "https://github.com/purescript/purescript-control.git";
229− rev = "a6033808790879a17b2729e73747a9ed3fb2264e";
230− sha256 = "05sdgywprwpav62d2bvlb80yd39brxhnmhhl1f116mm9hw5clqia";
231− };
232− phases = "installPhase";
233− installPhase = "ln -s $src $out";
234− };
235−
236− "datetime" = pkgs.stdenv.mkDerivation {
237− name = "datetime";
238− version = "v6.0.0";
239− src = pkgs.fetchgit {
240− url = "https://github.com/purescript/purescript-datetime.git";
241− rev = "a6a0cf1b0324964ad1854bc3377ed8766ba90e6f";
242− sha256 = "1idjljxy8rjlw3wiypk1c9cc1fc4g9kf7drzdy6a0yjdg7504gad";
243− };
244− phases = "installPhase";
245− installPhase = "ln -s $src $out";
246− };
247−
248− "datetime-parsing" = pkgs.stdenv.mkDerivation {
249− name = "datetime-parsing";
250− version = "v0.2.0";
251− src = pkgs.fetchgit {
252− url = "https://github.com/flounders/purescript-datetime-parsing.git";
253− rev = "aa7d336c2681501dbe343adcbbf8f2ee51b23932";
254− sha256 = "1w0lfs64qz1hs7zijyzpvqlp2z06wmbz4jp8cjsnw9yj46jsy8nh";
255− };
256− phases = "installPhase";
257− installPhase = "ln -s $src $out";
258− };
259−
260− "distributive" = pkgs.stdenv.mkDerivation {
261− name = "distributive";
262− version = "v6.0.0";
263− src = pkgs.fetchgit {
264− url = "https://github.com/purescript/purescript-distributive.git";
265− rev = "6005e513642e855ebf6f884d24a35c2803ca252a";
266− sha256 = "0x6cfsx9ff66kvw9l10pxd5vv16r91mmfxrfc68w1xfdfi81lhdx";
267− };
268− phases = "installPhase";
269− installPhase = "ln -s $src $out";
270− };
271−
272− "effect" = pkgs.stdenv.mkDerivation {
273− name = "effect";
274− version = "v4.0.0";
275− src = pkgs.fetchgit {
276− url = "https://github.com/purescript/purescript-effect.git";
277− rev = "a192ddb923027d426d6ea3d8deb030c9aa7c7dda";
278− sha256 = "0aa10lc6h9mlf4xf3g3ziig7v6kxdqvbh20kma8ay59w0b1bhmj1";
279− };
280− phases = "installPhase";
281− installPhase = "ln -s $src $out";
282− };
283−
284− "either" = pkgs.stdenv.mkDerivation {
285− name = "either";
286− version = "v6.1.0";
287− src = pkgs.fetchgit {
288− url = "https://github.com/purescript/purescript-either.git";
289− rev = "af655a04ed2fd694b6688af39ee20d7907ad0763";
290− sha256 = "05zps4klvgmvlm06f4hrycssm8q0pysbqnjsrk26lfvmid6mmg63";
291− };
292− phases = "installPhase";
293− installPhase = "ln -s $src $out";
294− };
295−
296− "enums" = pkgs.stdenv.mkDerivation {
297− name = "enums";
298− version = "v6.0.0";
299− src = pkgs.fetchgit {
300− url = "https://github.com/purescript/purescript-enums.git";
301− rev = "bae47961d401f9acf88da38f32e87403e5f0cf2f";
302− sha256 = "08sl5q07hv6v2008dcpg4mxiqwk9z0fhq67k0bgkxvlbsmar7khg";
303− };
304− phases = "installPhase";
305− installPhase = "ln -s $src $out";
306− };
307−
308− "exceptions" = pkgs.stdenv.mkDerivation {
309− name = "exceptions";
310− version = "v6.0.0";
311− src = pkgs.fetchgit {
312− url = "https://github.com/purescript/purescript-exceptions.git";
313− rev = "afab3c07c820bb49b6c5be50049db46a964a6161";
314− sha256 = "04xrbrcjwsv8gj465ygrlqhv1majxmr6m242iazg6apxlr89hgvg";
315− };
316− phases = "installPhase";
317− installPhase = "ln -s $src $out";
318− };
319−
320− "exists" = pkgs.stdenv.mkDerivation {
321− name = "exists";
322− version = "v6.0.0";
323− src = pkgs.fetchgit {
324− url = "https://github.com/purescript/purescript-exists.git";
325− rev = "f765b4ace7869c27b9c05949e18c843881f9173b";
326− sha256 = "0xnxi8fsmi15wrpq5d99gg94glk8a45hpx7pf4pmxg5r6rn1vjvx";
327− };
328− phases = "installPhase";
329− installPhase = "ln -s $src $out";
330− };
331−
332− "float32" = pkgs.stdenv.mkDerivation {
333− name = "float32";
334− version = "v2.0.0";
335− src = pkgs.fetchgit {
336− url = "https://github.com/purescript-contrib/purescript-float32.git";
337− rev = "3b70984d823c12c1b84a45a9b3d45199a4fcc67d";
338− sha256 = "0a1lvga6bbhbb8g3crp3fr0kmpkzjmffhfq71ig6flin13xijgys";
339− };
340− phases = "installPhase";
341− installPhase = "ln -s $src $out";
342− };
343−
344− "foldable-traversable" = pkgs.stdenv.mkDerivation {
345− name = "foldable-traversable";
346− version = "v6.0.0";
347− src = pkgs.fetchgit {
348− url = "https://github.com/purescript/purescript-foldable-traversable.git";
349− rev = "b3926f870532d287ea59e2d5cd3873b81ef2a93a";
350− sha256 = "0xg8qvyc6r9wqgy7wnw1rjqljl4wpgdrkxsm5x9rsagj2k5brxip";
351− };
352− phases = "installPhase";
353− installPhase = "ln -s $src $out";
354− };
355−
356− "foreign" = pkgs.stdenv.mkDerivation {
357− name = "foreign";
358− version = "v7.0.0";
359− src = pkgs.fetchgit {
360− url = "https://github.com/purescript/purescript-foreign.git";
361− rev = "2dd222d1ec7363fa0a0a7adb0d8eaf81bb7006dd";
362− sha256 = "0ycjaal8b2rsg3zl3b7acv166vjfdvziiza3fs8nhfw0rx0xmm1m";
363− };
364− phases = "installPhase";
365− installPhase = "ln -s $src $out";
366− };
367−
368− "foreign-object" = pkgs.stdenv.mkDerivation {
369− name = "foreign-object";
370− version = "v4.0.0";
371− src = pkgs.fetchgit {
372− url = "https://github.com/purescript/purescript-foreign-object.git";
373− rev = "28a635827a9a6c251df73f68874070d51fe9f756";
374− sha256 = "0ql0vgndcb5dqaqz81mvrqkrl5c2rlgxpydgdiiaxmmfxvxwi439";
375− };
376− phases = "installPhase";
377− installPhase = "ln -s $src $out";
378− };
379−
380− "free" = pkgs.stdenv.mkDerivation {
381− name = "free";
382− version = "v7.0.0";
383− src = pkgs.fetchgit {
384− url = "https://github.com/purescript/purescript-free.git";
385− rev = "e2d8fa8023a864363857834e11393483bced5e38";
386− sha256 = "0gyy0k297m26gw76w7kx8k1zjczlqbqb3nmh2h4gb3r84gl05g6r";
387− };
388− phases = "installPhase";
389− installPhase = "ln -s $src $out";
390− };
391−
392− "functions" = pkgs.stdenv.mkDerivation {
393− name = "functions";
394− version = "v6.0.0";
395− src = pkgs.fetchgit {
396− url = "https://github.com/purescript/purescript-functions.git";
397− rev = "f626f20580483977c5b27a01aac6471e28aff367";
398− sha256 = "1bsf2y8hx103a92yrc6m05q2dv51ckag1srd7q5n9vaf2k9byrj9";
399− };
400− phases = "installPhase";
401− installPhase = "ln -s $src $out";
402− };
403−
404− "functors" = pkgs.stdenv.mkDerivation {
405− name = "functors";
406− version = "v5.0.0";
407− src = pkgs.fetchgit {
408− url = "https://github.com/purescript/purescript-functors.git";
409− rev = "022ffd7a2a7ec12080314f3d217b400674a247b4";
410− sha256 = "1pnw3r1nk0rx8mrw3ajjml40zvx9v26qrqwj77pz7hpwxigxfbff";
411− };
412− phases = "installPhase";
413− installPhase = "ln -s $src $out";
414− };
415−
416− "gen" = pkgs.stdenv.mkDerivation {
417− name = "gen";
418− version = "v4.0.0";
419− src = pkgs.fetchgit {
420− url = "https://github.com/purescript/purescript-gen.git";
421− rev = "9fbcc2a1261c32e30d79c5418edef4d96fe76931";
422− sha256 = "0kajzcp9a8dmqvb3ga27hlf18m2smbp98a0bmzzb9vv156ckw0b4";
423− };
424− phases = "installPhase";
425− installPhase = "ln -s $src $out";
426− };
427−
428− "identity" = pkgs.stdenv.mkDerivation {
429− name = "identity";
430− version = "v6.0.0";
431− src = pkgs.fetchgit {
432− url = "https://github.com/purescript/purescript-identity.git";
433− rev = "ef6768f8a52ab0bc943a85f5761ba07c257f639f";
434− sha256 = "1lh0pkwwdyd8q9bgf4l1m96gdnardki4r2s359cnhrwp94b43fyr";
435− };
436− phases = "installPhase";
437− installPhase = "ln -s $src $out";
438− };
439−
440− "integers" = pkgs.stdenv.mkDerivation {
441− name = "integers";
442− version = "v6.0.0";
443− src = pkgs.fetchgit {
444− url = "https://github.com/purescript/purescript-integers.git";
445− rev = "54d712b25c594833083d15dc9ff2418eb9c52822";
446− sha256 = "04j9sqvkgyxxvbm8jfc2118wzmgswn8jva9fysgbsnmsg9vcxfm6";
447− };
448− phases = "installPhase";
449− installPhase = "ln -s $src $out";
450− };
451−
452− "invariant" = pkgs.stdenv.mkDerivation {
453− name = "invariant";
454− version = "v6.0.0";
455− src = pkgs.fetchgit {
456− url = "https://github.com/purescript/purescript-invariant.git";
457− rev = "1d2a196d51e90623adb88496c2cfd759c6736894";
458− sha256 = "07rqag47ykdiqc5yfbc1a64ijaqliljhcw1vgbv81m7ljzzd2xna";
459− };
460− phases = "installPhase";
461− installPhase = "ln -s $src $out";
462− };
463−
464− "js-date" = pkgs.stdenv.mkDerivation {
465− name = "js-date";
466− version = "v8.0.0";
467− src = pkgs.fetchgit {
468− url = "https://github.com/purescript-contrib/purescript-js-date.git";
469− rev = "1ea020316946cc4b87195bca9c54d0c16abaa490";
470− sha256 = "03535g8hc4bzyzig25hf75dls9ypsgrdk2nc45h8zw4xyy4ha0gz";
471− };
472− phases = "installPhase";
473− installPhase = "ln -s $src $out";
474− };
475−
476− "js-timers" = pkgs.stdenv.mkDerivation {
477− name = "js-timers";
478− version = "v6.1.0";
479− src = pkgs.fetchgit {
480− url = "https://github.com/purescript-contrib/purescript-js-timers.git";
481− rev = "7cb728b3e6dc29f355143617e6e9ac770ecd9273";
482− sha256 = "0dm933kqhwgmpkylgrlpjplidn8biic8jlzs4l4nny4a1z20mr61";
483− };
484− phases = "installPhase";
485− installPhase = "ln -s $src $out";
486− };
487−
488− "lazy" = pkgs.stdenv.mkDerivation {
489− name = "lazy";
490− version = "v6.0.0";
491− src = pkgs.fetchgit {
492− url = "https://github.com/purescript/purescript-lazy.git";
493− rev = "48347841226b27af5205a1a8ec71e27a93ce86fd";
494− sha256 = "0dxlc3b2bdqsri45i7a9bvwvv0gg43cc1gq61zi41f290a29ny2q";
495− };
496− phases = "installPhase";
497− installPhase = "ln -s $src $out";
498− };
499−
500− "lcg" = pkgs.stdenv.mkDerivation {
501− name = "lcg";
502− version = "v4.0.0";
503− src = pkgs.fetchgit {
504− url = "https://github.com/purescript/purescript-lcg.git";
505− rev = "67c6c6483a563a59ae036d9dca0f1be2835326a5";
506− sha256 = "1shzn6zvc1cxd7v0bvfsk7x3xf59vxby8c5lfjvd746r6396zn65";
507− };
508− phases = "installPhase";
509− installPhase = "ln -s $src $out";
510− };
511−
512− "lists" = pkgs.stdenv.mkDerivation {
513− name = "lists";
514− version = "v7.0.0";
515− src = pkgs.fetchgit {
516− url = "https://github.com/purescript/purescript-lists.git";
517− rev = "b113451e5b41cad87d669a3165f955c71cd863e2";
518− sha256 = "1mg7vy44k8jlqkri7x4ikciixx92b17wi35887x09dwdsicjf0sx";
519− };
520− phases = "installPhase";
521− installPhase = "ln -s $src $out";
522− };
523−
524− "literals" = pkgs.stdenv.mkDerivation {
525− name = "literals";
526− version = "6875fb28026595cfb780318305a77e79b098bb01";
527− src = pkgs.fetchgit {
528− url = "https://github.com/ilyakooo0/purescript-literals.git";
529− rev = "6875fb28026595cfb780318305a77e79b098bb01";
530− sha256 = "0s45cdgpdhni3rj7irr8pch46yxwmdcmj2nrg1vpkvygydkikdmb";
531− };
532− phases = "installPhase";
533− installPhase = "ln -s $src $out";
534− };
535−
536− "maybe" = pkgs.stdenv.mkDerivation {
537− name = "maybe";
538− version = "v6.0.0";
539− src = pkgs.fetchgit {
540− url = "https://github.com/purescript/purescript-maybe.git";
541− rev = "c6f98ac1088766287106c5d9c8e30e7648d36786";
542− sha256 = "0n33g8c579vcs06ii0r7f14jjdwzjw7p583w4nmrv9h442q2cvlz";
543− };
544− phases = "installPhase";
545− installPhase = "ln -s $src $out";
546− };
547−
548− "media-types" = pkgs.stdenv.mkDerivation {
549− name = "media-types";
550− version = "v6.0.0";
551− src = pkgs.fetchgit {
552− url = "https://github.com/purescript-contrib/purescript-media-types.git";
553− rev = "af853de226592f319a953637069a943dd261cba3";
554− sha256 = "0bxpsd2yw3zg58ay649v3xld7n17n7j7n9y8l841cjih9bc7ccxx";
555− };
556− phases = "installPhase";
557− installPhase = "ln -s $src $out";
558− };
559−
560− "newtype" = pkgs.stdenv.mkDerivation {
561− name = "newtype";
562− version = "v5.0.0";
563− src = pkgs.fetchgit {
564− url = "https://github.com/purescript/purescript-newtype.git";
565− rev = "29d8e6dd77aec2c975c948364ec3faf26e14ee7b";
566− sha256 = "0kfc644zn2f9rpx8fy4gjvmz04k9mv1c2xrqis8m3s0ydnhbffd7";
567− };
568− phases = "installPhase";
569− installPhase = "ln -s $src $out";
570− };
571−
572− "nonempty" = pkgs.stdenv.mkDerivation {
573− name = "nonempty";
574− version = "v7.0.0";
575− src = pkgs.fetchgit {
576− url = "https://github.com/purescript/purescript-nonempty.git";
577− rev = "28150ecc7419238b187abd609a92a645273348bb";
578− sha256 = "1mwdc2sny2ygp67kyk7h2493vq7syf7j18s3d9a5gwf1y7jlf7gh";
579− };
580− phases = "installPhase";
581− installPhase = "ln -s $src $out";
582− };
583−
584− "now" = pkgs.stdenv.mkDerivation {
585− name = "now";
586− version = "v6.0.0";
587− src = pkgs.fetchgit {
588− url = "https://github.com/purescript-contrib/purescript-now.git";
589− rev = "b5ffed2381e5fefc063f484e607e8499e79eaf32";
590− sha256 = "01khmqs4iz7dhlvd43zxj19q0c0vvqf1m128vgvgxz5mv430zfgr";
591− };
592− phases = "installPhase";
593− installPhase = "ln -s $src $out";
594− };
595−
596− "nullable" = pkgs.stdenv.mkDerivation {
597− name = "nullable";
598− version = "v6.0.0";
599− src = pkgs.fetchgit {
600− url = "https://github.com/purescript-contrib/purescript-nullable.git";
601− rev = "3202744c6c65e8d1fbba7f4256a1c482078e7fb5";
602− sha256 = "10s3b54pjmr2rxlyzvc8yc99k1dpn0qrijnc4y65yj5z6bxf2791";
603− };
604− phases = "installPhase";
605− installPhase = "ln -s $src $out";
606− };
607−
608− "numbers" = pkgs.stdenv.mkDerivation {
609− name = "numbers";
610− version = "v9.0.0";
611− src = pkgs.fetchgit {
612− url = "https://github.com/purescript/purescript-numbers.git";
613− rev = "2a53528f18f9415334bae28e7bb3cf3be86342c2";
614− sha256 = "1h4v6ir1hq6gvkk0kla38vlcd6bv73xkc8pv71jygwqxrdwfxxgw";
615− };
616− phases = "installPhase";
617− installPhase = "ln -s $src $out";
618− };
619−
620− "ordered-collections" = pkgs.stdenv.mkDerivation {
621− name = "ordered-collections";
622− version = "v3.0.0";
623− src = pkgs.fetchgit {
624− url = "https://github.com/purescript/purescript-ordered-collections.git";
625− rev = "9826b7632d0d0a691173bde308a634195f42a419";
626− sha256 = "1wk8mcn1zsxi0yk9ybas2v7m0drb492806gld1xxxic8x1i19ws0";
627− };
628− phases = "installPhase";
629− installPhase = "ln -s $src $out";
630− };
631−
632− "orders" = pkgs.stdenv.mkDerivation {
633− name = "orders";
634− version = "v6.0.0";
635− src = pkgs.fetchgit {
636− url = "https://github.com/purescript/purescript-orders.git";
637− rev = "f86db621ec5eef1274145f8b1fd8ebbfe0ed4a2c";
638− sha256 = "14fjls3v14ia6fr2w4p0i5dqv10zk343wg28m4c77a17jmvbs9r4";
639− };
640− phases = "installPhase";
641− installPhase = "ln -s $src $out";
642− };
643−
644− "parallel" = pkgs.stdenv.mkDerivation {
645− name = "parallel";
646− version = "v6.0.0";
647− src = pkgs.fetchgit {
648− url = "https://github.com/purescript/purescript-parallel.git";
649− rev = "85290dca837771ac4870071008c933d315ef678f";
650− sha256 = "0bpwvzq4dn7vx5mfj12w1zvvnamc6w2c45c2v54b9yw36snnwpcf";
651− };
652− phases = "installPhase";
653− installPhase = "ln -s $src $out";
654− };
655−
656− "parsing" = pkgs.stdenv.mkDerivation {
657− name = "parsing";
658− version = "v9.1.0";
659− src = pkgs.fetchgit {
660− url = "https://github.com/purescript-contrib/purescript-parsing.git";
661− rev = "44ef16b7cfb7cedc3d518c00ef10c6cb73ff4fc8";
662− sha256 = "1wkk3p1w2k1cvby4airyxgavk1djfzvzc2ch6b8951b592rj8lkr";
663− };
664− phases = "installPhase";
665− installPhase = "ln -s $src $out";
666− };
667−
668− "partial" = pkgs.stdenv.mkDerivation {
669− name = "partial";
670− version = "v4.0.0";
671− src = pkgs.fetchgit {
672− url = "https://github.com/purescript/purescript-partial.git";
673− rev = "0fa0646f5ea1ec5f0c46dcbd770c705a6c9ad3ec";
674− sha256 = "04s1h0r3slyd8kcamhqqrr6piksl9y76nmf2418j0ifhp16qwxmm";
675− };
676− phases = "installPhase";
677− installPhase = "ln -s $src $out";
678− };
679−
680− "prelude" = pkgs.stdenv.mkDerivation {
681− name = "prelude";
682− version = "v6.0.0";
683− src = pkgs.fetchgit {
684− url = "https://github.com/purescript/purescript-prelude.git";
685− rev = "32787f4399c92459c41602131a5858559eb868c5";
686− sha256 = "0jkbckxw2640gmfi2f8bpf2zwnay2dxy8n24rvxl3w3dihcgd9qc";
687− };
688− phases = "installPhase";
689− installPhase = "ln -s $src $out";
690− };
691−
692− "profunctor" = pkgs.stdenv.mkDerivation {
693− name = "profunctor";
694− version = "v6.0.0";
695− src = pkgs.fetchgit {
696− url = "https://github.com/purescript/purescript-profunctor.git";
697− rev = "0a966a14e7b0c827d44657dc1710cdc712d2e034";
698− sha256 = "07rhk8micbc74my6s53xbj2smvvwah2w1drqrcmcnncxdks3lqg3";
699− };
700− phases = "installPhase";
701− installPhase = "ln -s $src $out";
702− };
703−
704− "profunctor-lenses" = pkgs.stdenv.mkDerivation {
705− name = "profunctor-lenses";
706− version = "v8.0.0";
707− src = pkgs.fetchgit {
708− url = "https://github.com/purescript-contrib/purescript-profunctor-lenses.git";
709− rev = "973d567afe458fd802cf4f0d9725b6dc35ad9297";
710− sha256 = "0axz7nznn12iqh6sn9qjg6i8qglpdsqgkp30ndwdj98czcnbnybv";
711− };
712− phases = "installPhase";
713− installPhase = "ln -s $src $out";
714− };
715−
716− "psci-support" = pkgs.stdenv.mkDerivation {
717− name = "psci-support";
718− version = "v6.0.0";
719− src = pkgs.fetchgit {
720− url = "https://github.com/purescript/purescript-psci-support.git";
721− rev = "897cdb543548cb6078d69b6413b54841404eda72";
722− sha256 = "1ix53r8avkn3fw72mngwzw7v6c6mv7j4miw5mrgjrh9hb8p2ydl1";
723− };
724− phases = "installPhase";
725− installPhase = "ln -s $src $out";
726− };
727−
728− "quickcheck" = pkgs.stdenv.mkDerivation {
729− name = "quickcheck";
730− version = "v8.0.1";
731− src = pkgs.fetchgit {
732− url = "https://github.com/purescript/purescript-quickcheck.git";
733− rev = "bf5029f97e6c0d7552d3a08d2ab793a19e2c5e3d";
734− sha256 = "142dvh57fl8b6i7mm37a38v7vkc1znbqz6l6wqa704m53hkikvyd";
735− };
736− phases = "installPhase";
737− installPhase = "ln -s $src $out";
738− };
739−
740− "random" = pkgs.stdenv.mkDerivation {
741− name = "random";
742− version = "v6.0.0";
743− src = pkgs.fetchgit {
744− url = "https://github.com/purescript/purescript-random.git";
745− rev = "9540bc965a9596da02fefd9949418bb19c92533a";
746− sha256 = "0qz14qviz5053j9h1jwpk0gnn692hcdx6fp90wckzrnw31d53174";
747− };
748− phases = "installPhase";
749− installPhase = "ln -s $src $out";
750− };
751−
752− "record" = pkgs.stdenv.mkDerivation {
753− name = "record";
754− version = "v4.0.0";
755− src = pkgs.fetchgit {
756− url = "https://github.com/purescript/purescript-record.git";
757− rev = "c89cd1ada6b636692571fc374196b1c39c4c9f70";
758− sha256 = "1g7s2h1as5cz824wpm0jhjprrh66shha5i4gq37q73yw0s5p2ahm";
759− };
760− phases = "installPhase";
761− installPhase = "ln -s $src $out";
762− };
763−
764− "refs" = pkgs.stdenv.mkDerivation {
765− name = "refs";
766− version = "v6.0.0";
767− src = pkgs.fetchgit {
768− url = "https://github.com/purescript/purescript-refs.git";
769− rev = "f8e6216da4cb9309fde1f20cd6f69ac3a3b7f9e8";
770− sha256 = "09bvfxhjfwfwv55py45s71maazwrr68k0rk4v8ynfqv91h34319h";
771− };
772− phases = "installPhase";
773− installPhase = "ln -s $src $out";
774− };
775−
776− "run" = pkgs.stdenv.mkDerivation {
777− name = "run";
778− version = "v5.0.0";
779− src = pkgs.fetchgit {
780− url = "https://github.com/natefaubion/purescript-run.git";
781− rev = "abec7c343e92154d44b9dafd52b91ee82d32a870";
782− sha256 = "059xhjxjfs1w2hmgld53kciyfws3pmjyz75w836ph99rqr4aczny";
783− };
784− phases = "installPhase";
785− installPhase = "ln -s $src $out";
786− };
787−
788− "safe-coerce" = pkgs.stdenv.mkDerivation {
789− name = "safe-coerce";
790− version = "v2.0.0";
791− src = pkgs.fetchgit {
792− url = "https://github.com/purescript/purescript-safe-coerce.git";
793− rev = "7fa799ae80a38b8d948efcb52608e58e198b3da7";
794− sha256 = "00m4l733gpl0153cbl6n5kly7jr8ids399apza2rbczif40brp9g";
795− };
796− phases = "installPhase";
797− installPhase = "ln -s $src $out";
798− };
799−
800− "st" = pkgs.stdenv.mkDerivation {
801− name = "st";
802− version = "v6.0.0";
803− src = pkgs.fetchgit {
804− url = "https://github.com/purescript/purescript-st.git";
805− rev = "2cc7ae1c3318a303378c4a9f3fa0f10ee7cc3589";
806− sha256 = "1ykk27zn03svfsv2s1mf88d6x3q6qhb9bwjzgals79krw8w0aiyl";
807− };
808− phases = "installPhase";
809− installPhase = "ln -s $src $out";
810− };
811−
812− "strings" = pkgs.stdenv.mkDerivation {
813− name = "strings";
814− version = "v6.0.0";
815− src = pkgs.fetchgit {
816− url = "https://github.com/purescript/purescript-strings.git";
817− rev = "4bc6954448d056f8aa7a659695a6ad60ad4fdf19";
818− sha256 = "101va3rawcn5inp20krfg192bbjbvm27xfzwwc1inb2ya0m98amg";
819− };
820− phases = "installPhase";
821− installPhase = "ln -s $src $out";
822− };
823−
824− "tailrec" = pkgs.stdenv.mkDerivation {
825− name = "tailrec";
826− version = "v6.0.0";
827− src = pkgs.fetchgit {
828− url = "https://github.com/purescript/purescript-tailrec.git";
829− rev = "5e2104aa734b31a17074cc805bf087d72b65afd1";
830− sha256 = "0vc3dpsd2xxz694zlm75nx1cn6c6f29nzkb8xlxkk6ydw3wpsk3w";
831− };
832− phases = "installPhase";
833− installPhase = "ln -s $src $out";
834− };
835−
836− "test-unit" = pkgs.stdenv.mkDerivation {
837− name = "test-unit";
838− version = "v17.0.0";
839− src = pkgs.fetchgit {
840− url = "https://github.com/bodil/purescript-test-unit.git";
841− rev = "3112d7ebe06d467238058a6384dc75ffd960d335";
842− sha256 = "02gfb4dqbj5v7dwifm8jhr0lh7lfp5hari5795w1z48wy3apnvvx";
843− };
844− phases = "installPhase";
845− installPhase = "ln -s $src $out";
846− };
847−
848− "transformers" = pkgs.stdenv.mkDerivation {
849− name = "transformers";
850− version = "v6.0.0";
851− src = pkgs.fetchgit {
852− url = "https://github.com/purescript/purescript-transformers.git";
853− rev = "be72ab52357d9a665cbf93d73ba1c07c4b0957ee";
854− sha256 = "0ijrdsppl8vx0rlbwc9p0vjbmvwlcy4ia7xymvk6y34zbxzjlzm6";
855− };
856− phases = "installPhase";
857− installPhase = "ln -s $src $out";
858− };
859−
860− "tuples" = pkgs.stdenv.mkDerivation {
861− name = "tuples";
862− version = "v7.0.0";
863− src = pkgs.fetchgit {
864− url = "https://github.com/purescript/purescript-tuples.git";
865− rev = "4f52da2729b448c8564369378f1232d8d2dc1d8b";
866− sha256 = "1m1ng0xxicb73945jymcl1hn2y2hmynlnmhb2k0kkn1jrjwgcc3d";
867− };
868− phases = "installPhase";
869− installPhase = "ln -s $src $out";
870− };
871−
872− "type-equality" = pkgs.stdenv.mkDerivation {
873− name = "type-equality";
874− version = "v4.0.1";
875− src = pkgs.fetchgit {
876− url = "https://github.com/purescript/purescript-type-equality.git";
877− rev = "0525b7d39e0fbd81b4209518139fb8ab02695774";
878− sha256 = "1ass38jdycsjisdimdc4drg2w8vkkwp6lkvz3kvy7q0h98vdmlbr";
879− };
880− phases = "installPhase";
881− installPhase = "ln -s $src $out";
882− };
883−
884− "typelevel-prelude" = pkgs.stdenv.mkDerivation {
885− name = "typelevel-prelude";
886− version = "v7.0.0";
887− src = pkgs.fetchgit {
888− url = "https://github.com/purescript/purescript-typelevel-prelude.git";
889− rev = "dca2fe3c8cfd5527d4fe70c4bedfda30148405bf";
890− sha256 = "0x86mrg33kpnrnsfp4p3c92j5lpyqzy87bxdynwf7smk3inqr2jc";
891− };
892− phases = "installPhase";
893− installPhase = "ln -s $src $out";
894− };
895−
896− "uint" = pkgs.stdenv.mkDerivation {
897− name = "uint";
898− version = "v7.0.0";
899− src = pkgs.fetchgit {
900− url = "https://github.com/purescript-contrib/purescript-uint.git";
901− rev = "9e4f76ffd5192472f75583844172fe8ab3c0cb9f";
902− sha256 = "173bhrd006q53s7agwyasxhfbr89x9jpz5b47vm2fr74l3jcw3lq";
903− };
904− phases = "installPhase";
905− installPhase = "ln -s $src $out";
906− };
907−
908− "undefined-is-not-a-problem" = pkgs.stdenv.mkDerivation {
909− name = "undefined-is-not-a-problem";
910− version = "v1.1.0";
911− src = pkgs.fetchgit {
912− url = "https://github.com/paluh/purescript-undefined-is-not-a-problem.git";
913− rev = "8ac77ac8525343f7319cd48bf2ceb736286f3b27";
914− sha256 = "1qv7gdyslfj86ga0a7gxkbpvykm3s1x4kzcm8zi75lhlvkpyjh3w";
915− };
916− phases = "installPhase";
917− installPhase = "ln -s $src $out";
918− };
919−
920− "unfoldable" = pkgs.stdenv.mkDerivation {
921− name = "unfoldable";
922− version = "v6.0.0";
923− src = pkgs.fetchgit {
924− url = "https://github.com/purescript/purescript-unfoldable.git";
925− rev = "493dfe04ed590e20d8f69079df2f58486882748d";
926− sha256 = "15z2k639ph8wdkrc2y838m5am1z7szw2vqymmv021skzisyn7zwf";
927− };
928− phases = "installPhase";
929− installPhase = "ln -s $src $out";
930− };
931−
932− "unicode" = pkgs.stdenv.mkDerivation {
933− name = "unicode";
934− version = "v6.0.0";
935− src = pkgs.fetchgit {
936− url = "https://github.com/purescript-contrib/purescript-unicode.git";
937− rev = "6454d59d9e1fd1bc5a72e80e985d8282022a567a";
938− sha256 = "0w2gsmqcy7pj5vd22qzpig20bdgzmbagyqxrg9mm1njjfsmcm8cv";
939− };
940− phases = "installPhase";
941− installPhase = "ln -s $src $out";
942− };
943−
944− "unsafe-coerce" = pkgs.stdenv.mkDerivation {
945− name = "unsafe-coerce";
946− version = "v6.0.0";
947− src = pkgs.fetchgit {
948− url = "https://github.com/purescript/purescript-unsafe-coerce.git";
949− rev = "ab956f82e66e633f647fb3098e8ddd3ec58d689f";
950− sha256 = "0r6d3dx8jalfzvrvkagz9v05yxwkkhgbzlpswg4w1cyl03zjcla4";
951− };
952− phases = "installPhase";
953− installPhase = "ln -s $src $out";
954− };
955−
956− "untagged-union" = pkgs.stdenv.mkDerivation {
957− name = "untagged-union";
958− version = "6c5bccef38696a16e86ba9a65cf778b4462c4a66";
959− src = pkgs.fetchgit {
960− url = "https://github.com/rowtype-yoga/purescript-untagged-union.git";
961− rev = "6c5bccef38696a16e86ba9a65cf778b4462c4a66";
962− sha256 = "0s4pik6xcr7zqhlrjxjqgzp8hfg09kgd58zq3xjj0inw9ly35ywn";
963− };
964− phases = "installPhase";
965− installPhase = "ln -s $src $out";
966− };
967−
968− "variant" = pkgs.stdenv.mkDerivation {
969− name = "variant";
970− version = "v8.0.0";
971− src = pkgs.fetchgit {
972− url = "https://github.com/natefaubion/purescript-variant.git";
973− rev = "8b2bd73a4202257d80a955c4dd36f6055f149823";
974− sha256 = "18nyxnaqqg1zzydwd2hh9hbzr31j509g5yb1lnd0m60bma4l9h5g";
975− };
976− phases = "installPhase";
977− installPhase = "ln -s $src $out";
978− };
979−
980− "web-dom" = pkgs.stdenv.mkDerivation {
981− name = "web-dom";
982− version = "v6.0.0";
983− src = pkgs.fetchgit {
984− url = "https://github.com/purescript-web/purescript-web-dom.git";
985− rev = "568a1ee158b29e6e739e7a9aaed3e35ca4c4305a";
986− sha256 = "0angs86ld0h3ld889ag55lh62101dfn3fknyqrfv0imgn40vwm44";
987− };
988− phases = "installPhase";
989− installPhase = "ln -s $src $out";
990− };
991−
992− "web-events" = pkgs.stdenv.mkDerivation {
993− name = "web-events";
994− version = "v4.0.0";
995− src = pkgs.fetchgit {
996− url = "https://github.com/purescript-web/purescript-web-events.git";
997− rev = "2124356117be7b764a2f3948032255ac4dab7051";
998− sha256 = "0l47gsgj71x0gjbhjqwsar2y44ljhq3xa0nkgr1qvqxmcm00llqf";
999− };
1000− phases = "installPhase";
1001− installPhase = "ln -s $src $out";
1002− };
1003−
1004− "web-file" = pkgs.stdenv.mkDerivation {
1005− name = "web-file";
1006− version = "v4.0.0";
1007− src = pkgs.fetchgit {
1008− url = "https://github.com/purescript-web/purescript-web-file.git";
1009− rev = "023786ae62bbb8bf58156dd7f02011fa38221ef1";
1010− sha256 = "1wjf1m5y2qsba83is297icv9zyl7jilxdga0192mm1rp0j16jjln";
1011− };
1012− phases = "installPhase";
1013− installPhase = "ln -s $src $out";
1014− };
1015−
1016− "web-html" = pkgs.stdenv.mkDerivation {
1017− name = "web-html";
1018− version = "v4.0.0";
1019− src = pkgs.fetchgit {
1020− url = "https://github.com/purescript-web/purescript-web-html.git";
1021− rev = "be189cf91b9a19cf493637423522e2fe4a0088cc";
1022− sha256 = "0yspv6sci3mkflvqwf5jvz3a8nclmhbvazx9n34acyj3aai7k6bs";
1023− };
1024− phases = "installPhase";
1025− installPhase = "ln -s $src $out";
1026− };
1027−
1028− "web-storage" = pkgs.stdenv.mkDerivation {
1029− name = "web-storage";
1030− version = "v5.0.0";
1031− src = pkgs.fetchgit {
1032− url = "https://github.com/purescript-web/purescript-web-storage.git";
1033− rev = "6b74461e136755db70c271dc898d51776363d7e2";
1034− sha256 = "0q1grh8cp6h1szsqg3xmwlzrc1bbz8yzlwxlcl1iym5mjiyzgkcs";
1035− };
1036− phases = "installPhase";
1037− installPhase = "ln -s $src $out";
1038− };
1039−
1040− };
1041−
1042− cpPackage = pkg:
1043− let
1044− target = ".spago/${pkg.name}/${pkg.version}";
1045− in ''
1046− if [ ! -e ${target} ]; then
1047− echo "Installing ${target}."
1048− mkdir -p ${target}
1049− cp --no-preserve=mode,ownership,timestamp -r ${toString pkg.outPath}/* ${target}
1050− else
1051− echo "${target} already exists. Skipping."
1052− fi
1053− '';
1054−
1055− getGlob = pkg: ''".spago/${pkg.name}/${pkg.version}/src/**/*.purs"'';
1056−
1057− getStoreGlob = pkg: ''"${pkg.outPath}/src/**/*.purs"'';
1058−
1059−in {
1060− inherit inputs;
1061−
1062− installSpagoStyle = pkgs.writeShellScriptBin "install-spago-style" ''
1063− set -e
1064− echo installing dependencies...
1065− ${builtins.toString (builtins.map cpPackage (builtins.attrValues inputs))}
1066− echo "echo done."
1067− '';
1068−
1069− buildSpagoStyle = pkgs.writeShellScriptBin "build-spago-style" ''
1070− set -e
1071− echo building project...
1072− purs compile ${builtins.toString (builtins.map getGlob (builtins.attrValues inputs))} "$@"
1073− echo done.
1074− '';
1075−
1076− buildFromNixStore = pkgs.writeShellScriptBin "build-from-store" ''
1077− set -e
1078− echo building project using sources from nix store...
1079− purs compile ${builtins.toString (
1080− builtins.map getStoreGlob (builtins.attrValues inputs))} "$@"
1081− echo done.
1082− '';
1083−
1084− mkBuildProjectOutput =
1085− { src, purs }:
1086−
1087− pkgs.stdenv.mkDerivation {
1088− name = "build-project-output";
1089− src = src;
1090−
1091− buildInputs = [ purs ];
1092−
1093− installPhase = ''
1094− mkdir -p $out
1095− purs compile "$src/**/*.purs" ${builtins.toString
1096− (builtins.map
1097− (x: ''"${x.outPath}/src/**/*.purs"'')
1098− (builtins.attrValues inputs))}
1099− mv output $out
1100− '';
1101− };
1102−}
spago.dhall−5
@@ -28,17 +28,12 @@ to generate this file without the comments in this block.
28 , "either"
28 , "either"
29 , "exceptions"
29 , "exceptions"
30 , "foldable-traversable"
30 , "foldable-traversable"
31− , "foreign"
32 , "functions"
31 , "functions"
33− , "integers"
34− , "literals"
35 , "maybe"
32 , "maybe"
36 , "newtype"
33 , "newtype"
37 , "nullable"
34 , "nullable"
38− , "numbers"
39 , "orders"
35 , "orders"
40 , "prelude"
36 , "prelude"
41− , "random"
42 , "refs"
37 , "refs"
43 , "run"
38 , "run"
44 , "strings"
39 , "strings"
style.css+13−67
@@ -1,11 +1,13 @@
1+
1:root {
2:root {
2− --color-light: #f7f7f7;
3+ --color-light: #F7F7F7;
3}
4}
4
5
5main {
6main {
6 margin: 1rem;
7 margin: 1rem;
7}
8}
8
9
10+
9nav.p-tabs__list {
11nav.p-tabs__list {
10 top: 0;
12 top: 0;
11 position: sticky;
13 position: sticky;
@@ -26,13 +28,13 @@ nav h3 {
26
28
27@keyframes floating-notification-animation {
29@keyframes floating-notification-animation {
28 from {
30 from {
29− transform: translateY(-60px);
31+ transform: translateY(-60px) ;
30− opacity: 0;
32+ opacity: 0
31 }
33 }
32
34
33 to {
35 to {
34 transform: none;
36 transform: none;
35− opacity: 1;
37+ opacity: 1
36 }
38 }
37}
39}
38
40
@@ -55,17 +57,17 @@ nav h3 {
55 align-content: center;
57 align-content: center;
56}
58}
57
59
58−.spinner {
60+.spinner{
59 padding: 1rem;
61 padding: 1rem;
60}
62}
61
63
62.cipher-row-container {
64.cipher-row-container {
63− display: flex;
65+ display: flex;
64− width: 100%;
66+ width: 100%;
65− align-items: baseline;
67+ align-items: baseline;
66− justify-content: space-between;
68+ justify-content: space-between;
67− flex-direction: row;
69+ flex-direction: row;
68− flex-wrap: wrap;
70+ flex-wrap: wrap;
69}
71}
70
72
71.cipher-row p {
73.cipher-row p {
@@ -93,59 +95,3 @@ p {
93textarea {
95textarea {
94 resize: none;
96 resize: none;
95}
97}
96−
97−.password-generator-group {
98− display: flex;
99− flex-direction: row;
100− align-items: baseline;
101− justify-content: space-between;
102−}
103−
104−.password-generator-group > input[type="number"] {
105− width: 5%;
106− margin-bottom: 0px;
107− min-width: 5rem;
108− text-align: center;
109−}
110−
111−.p-side-navigation__drawer {
112− max-width: initial;
113−}
114−
115−.select-cipher-type-container {
116− display: flex;
117− flex-direction: column;
118− align-items: center;
119− justify-content: center;
120−}
121−
122−.select-cipher-type-container > * {
123− margin: 0px;
124− margin-bottom: 1.5rem;
125−}
126−
127−.overlay {
128− position: fixed;
129− top: 0;
130− left: 0;
131− right: 0;
132− bottom: 0;
133−}
134−
135−.select-second-factor-container {
136− display: flex;
137− flex-direction: column;
138− align-items: center;
139− justify-content: center;
140−}
141−
142−.p-tabs__list {
143− overflow: initial;
144−}
145−
146−.p-modal {
147− top: 0;
148− left: 0;
149− right: 0;
150− bottom: 0;
151−}
tests/package-lock.jsondeleted−73
@@ -1,73 +0,0 @@
1−{
2− "name": "warden-tests",
3− "version": "1.0.0",
4− "lockfileVersion": 2,
5− "requires": true,
6− "packages": {
7− "": {
8− "name": "warden-tests",
9− "version": "1.0.0",
10− "devDependencies": {
11− "@playwright/test": "^1.27.1"
12− }
13− },
14− "node_modules/@playwright/test": {
15− "version": "1.27.1",
16− "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.27.1.tgz",
17− "integrity": "sha512-mrL2q0an/7tVqniQQF6RBL2saskjljXzqNcCOVMUjRIgE6Y38nCNaP+Dc2FBW06bcpD3tqIws/HT9qiMHbNU0A==",
18− "dev": true,
19− "dependencies": {
20− "@types/node": "*",
21− "playwright-core": "1.27.1"
22− },
23− "bin": {
24− "playwright": "cli.js"
25− },
26− "engines": {
27− "node": ">=14"
28− }
29− },
30− "node_modules/@types/node": {
31− "version": "18.11.9",
32− "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.9.tgz",
33− "integrity": "sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg==",
34− "dev": true
35− },
36− "node_modules/playwright-core": {
37− "version": "1.27.1",
38− "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.27.1.tgz",
39− "integrity": "sha512-9EmeXDncC2Pmp/z+teoVYlvmPWUC6ejSSYZUln7YaP89Z6lpAaiaAnqroUt/BoLo8tn7WYShcfaCh+xofZa44Q==",
40− "dev": true,
41− "bin": {
42− "playwright": "cli.js"
43− },
44− "engines": {
45− "node": ">=14"
46− }
47− }
48− },
49− "dependencies": {
50− "@playwright/test": {
51− "version": "1.27.1",
52− "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.27.1.tgz",
53− "integrity": "sha512-mrL2q0an/7tVqniQQF6RBL2saskjljXzqNcCOVMUjRIgE6Y38nCNaP+Dc2FBW06bcpD3tqIws/HT9qiMHbNU0A==",
54− "dev": true,
55− "requires": {
56− "@types/node": "*",
57− "playwright-core": "1.27.1"
58− }
59− },
60− "@types/node": {
61− "version": "18.11.9",
62− "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.9.tgz",
63− "integrity": "sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg==",
64− "dev": true
65− },
66− "playwright-core": {
67− "version": "1.27.1",
68− "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.27.1.tgz",
69− "integrity": "sha512-9EmeXDncC2Pmp/z+teoVYlvmPWUC6ejSSYZUln7YaP89Z6lpAaiaAnqroUt/BoLo8tn7WYShcfaCh+xofZa44Q==",
70− "dev": true
71− }
72− }
73−}
tests/package.jsondeleted−9
@@ -1,9 +0,0 @@
1−{
2− "name": "warden-tests",
3− "version": "1.0.0",
4− "scripts": {},
5− "keywords": [],
6− "devDependencies": {
7− "@playwright/test": "^1.27.1"
8− }
9−}
tests/playwright.config.tsdeleted−65
@@ -1,65 +0,0 @@
1−import type { PlaywrightTestConfig } from '@playwright/test';
2−import { devices } from '@playwright/test';
3−
4−/**
5− * See https://playwright.dev/docs/test-configuration.
6− */
7−const config: PlaywrightTestConfig = {
8− testDir: './src',
9− /* Maximum time one test can run for. */
10− timeout: 30 * 1000,
11− expect: {
12− /**
13− * Maximum time expect() should wait for the condition to be met.
14− * For example in `await expect(locator).toHaveText();`
15− */
16− timeout: 5000
17− },
18− /* Run tests in files in parallel */
19− fullyParallel: false,
20− /* Fail the build on CI if you accidentally left test.only in the source code. */
21− forbidOnly: !!process.env.CI,
22− /* Retry on CI only */
23− retries: 0,
24− /* Opt out of parallel tests on CI. */
25− workers: 1,
26− /* Reporter to use. See https://playwright.dev/docs/test-reporters */
27− reporter: 'html',
28− /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
29− use: {
30− /* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */
31− actionTimeout: 0,
32− /* Base URL to use in actions like `await page.goto('/')`. */
33− // baseURL: 'http://localhost:3000',
34−
35− /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
36− trace: 'on-first-retry',
37− },
38−
39− /* Configure projects for major browsers */
40− projects: [
41− {
42− name: 'Pixel 3',
43− use: {
44− ...devices['Pixel 3'],
45− launchOptions: {
46− args: ["--disable-web-security"],
47− },
48− headless: process.env.RECORD_TEST !== "true",
49− }
50− },
51−
52− ],
53−
54− /* Folder for test artifacts such as screenshots, videos, traces, etc. */
55− outputDir: 'test-results/',
56−
57− /* Run your local dev server before starting the tests */
58− webServer: {
59− command: './vaultwarden/run.sh',
60− port: 8000,
61− },
62−
63−};
64−
65−export default config;
tests/src/test.spec.tsdeleted−257
@@ -1,257 +0,0 @@
1−import { test, expect, Page } from '@playwright/test';
2−
3−async function logIn(page: Page) {
4− await page.locator('input[type="url"]').tap();
5− await page.locator('input[type="url"]').press('Control+a');
6− await page.locator('input[type="url"]').fill('http://localhost:8000');
7− await page.locator('input[type="email"]').tap();
8− await page.locator('input[type="email"]').fill('test@example.com');
9− await page.locator('input[type="password"]').tap();
10− await page.locator('input[type="password"]').fill('Password123');
11− await page.getByRole('button', { name: 'Log in' }).tap();
12− await page.locator('.p-icon--spinner.u-animation--spin.spinner').waitFor({ state: "visible" })
13− await page.locator('.p-icon--spinner.u-animation--spin.spinner').waitFor({ state: "hidden" })
14−}
15−
16−async function fillInLoginCipher(page: Page) {
17− await page.locator('p:has-text("Entry name") input[type="text"]').tap();
18− await page.locator('p:has-text("Entry name") input[type="text"]').fill('Test login entry');
19− await page.locator('p:has-text("Username") input[type="text"]').tap();
20− await page.locator('p:has-text("Username") input[type="text"]').fill('login_entry_username');
21− await page.locator('input[type="text"]').nth(2).tap();
22− await page.locator('input[type="text"]').nth(2).fill('Password123');
23− await page.locator('input[type="url"]').tap();
24− await page.locator('input[type="url"]').fill('https://example.com');
25−}
26−
27−async function fillInCardCipher(page: Page) {
28− await page.locator('p:has-text("Entry name") input[type="text"]').tap();
29− await page.locator('p:has-text("Entry name") input[type="text"]').fill('Test card');
30− await page.locator('p:has-text("Card number") input[type="text"]').tap();
31− await page.locator('p:has-text("Card number") input[type="text"]').fill('1234567890123456');
32− await page.locator('p:has-text("Expiration date /") input[type="text"]').first().tap();
33− await page.locator('p:has-text("Expiration date /") input[type="text"]').first().fill('01');
34− await page.locator('p:has-text("Expiration date /") input[type="text"]').nth(1).tap();
35− await page.locator('p:has-text("Expiration date /") input[type="text"]').nth(1).fill('01');
36− await page.locator('p:has-text("CVV") input[type="text"]').tap();
37− await page.locator('p:has-text("CVV") input[type="text"]').fill('123');
38− await page.locator('p:has-text("Cardholder name") input[type="text"]').tap();
39− await page.locator('p:has-text("Cardholder name") input[type="text"]').fill('Sampel Palnet');
40−}
41−
42−async function fillInNoteCipher(page: Page) {
43− await page.locator('input[type="text"]').tap();
44− await page.locator('input[type="text"]').fill('Secret note');
45− await page.locator('textarea').tap();
46− await page.locator('textarea').fill('This is a very secret note');
47−}
48−
49−async function fillInContractCipher(page: Page) {
50− await page.locator('p:has-text("Entry name") input[type="text"]').tap();
51− await page.locator('p:has-text("Entry name") input[type="text"]').fill('John Appleseed');
52− await page.locator('p:has-text("First name") input[type="text"]').tap();
53− await page.locator('p:has-text("First name") input[type="text"]').fill('John');
54− await page.locator('p:has-text("Middle name") input[type="text"]').tap();
55− await page.locator('p:has-text("Middle name") input[type="text"]').fill('Mathews');
56− await page.locator('p:has-text("Last name") input[type="text"]').tap();
57− await page.locator('p:has-text("Last name") input[type="text"]').fill('Appleseed');
58− await page.locator('p:has-text("Username") input[type="text"]').tap();
59− await page.locator('p:has-text("Username") input[type="text"]').fill('jappleseed');
60− await page.locator('p:has-text("Company") input[type="text"]').tap();
61− await page.locator('p:has-text("Company") input[type="text"]').fill('Very good building company');
62− await page.locator('p:has-text("Social security number") input[type="number"]').tap();
63− await page.locator('p:has-text("Social security number") input[type="number"]').fill('123456789');
64− await page.locator('p:has-text("Passport number") input[type="number"]').tap();
65− await page.locator('p:has-text("Passport number") input[type="number"]').fill('123456798');
66− await page.locator('p:has-text("License Number") input[type="number"]').tap();
67− await page.locator('p:has-text("License Number") input[type="number"]').fill('12345');
68− await page.locator('input[type="email"]').tap();
69− await page.locator('input[type="email"]').fill('john@example.com');
70− await page.locator('input[type="tel"]').tap();
71− await page.locator('input[type="tel"]').fill('123456789');
72− await page.locator('p:has-text("Address line 1") input[type="text"]').tap();
73− await page.locator('p:has-text("Address line 1") input[type="text"]').fill('Abc def');
74− await page.locator('p:has-text("Address line 2") input[type="text"]').tap();
75− await page.locator('p:has-text("Address line 2") input[type="text"]').fill('Ghijk Lmn');
76−}
77−
78−async function screenshot(page: Page) {
79− await page.waitForLoadState("networkidle")
80−
81− if (await page.locator('div.floating-notifications').isVisible()) {
82− await page.locator("button.p-notification__close").tap()
83− await screenshot(page)
84− } else if (await page.locator('.p-icon--spinner.u-animation--spin.spinner').isVisible()) {
85− await page.locator('.p-icon--spinner.u-animation--spin.spinner').waitFor({ state: "hidden" })
86− await screenshot(page)
87− } else {
88− await expect.soft(page).toHaveScreenshot({ animations: "disabled", fullPage: true, maxDiffPixelRatio: 0.01 })
89− }
90−}
91−
92−async function waitForNotification(page: Page) {
93− await page.locator('div.floating-notifications').waitFor({ state: "visible" })
94−}
95−
96−
97−/**
98− * Screenshots the very first (last edited) entry.
99− */
100−function cipherPage(name: string) {
101− test(name + " cipher page", async ({ page }) => {
102− await logIn(page)
103−
104− await page.locator('.cipher-row').first().tap()
105−
106− await screenshot(page)
107− })
108−}
109−
110−test.beforeEach(async ({ page }) => {
111− await page.goto(`file://${__dirname}/../../dist/index.html`);
112− await page.addStyleTag({ content: '.hide-in-test {opacity: 0;}' })
113−})
114−
115−if (process.env.RECORD_TEST === "true") {
116−
117− test('record', async ({ page }) => {
118− await logIn(page)
119− await page.pause()
120− })
121−
122−} else {
123− test('Login page', async ({ page }) => {
124− await screenshot(page)
125− });
126−
127− test('Empty ciphers page', async ({ page }) => {
128− await logIn(page)
129−
130− await screenshot(page)
131− })
132−
133− test('Login entry page', async ({ page }) => {
134− await logIn(page)
135−
136− await page.getByRole('button').nth(2).tap();
137− await page.getByRole('button', { name: 'Login' }).tap();
138−
139− await screenshot(page)
140−
141− await fillInLoginCipher(page)
142−
143− await screenshot(page)
144−
145− await page.getByRole('heading').locator('span').getByRole('button').tap();
146−
147− await waitForNotification(page)
148− });
149−
150− cipherPage("Login")
151−
152− test('Create Card entry page', async ({ page }) => {
153− await logIn(page)
154−
155− await page.getByRole('button').nth(2).tap();
156− await page.getByRole('button', { name: 'Card' }).tap();
157−
158− await screenshot(page)
159−
160− await fillInCardCipher(page)
161−
162− await screenshot(page)
163−
164− await page.getByRole('heading').locator('span').getByRole('button').tap();
165−
166− await waitForNotification(page)
167− })
168−
169− cipherPage("Card")
170−
171− test('Create Note entry page', async ({ page }) => {
172− await logIn(page)
173−
174− await page.getByRole('button').nth(2).tap();
175− await page.getByRole('button', { name: 'Note' }).tap();
176−
177− await screenshot(page)
178−
179− await fillInNoteCipher(page)
180−
181− await screenshot(page)
182−
183− await page.getByRole('heading').locator('span').getByRole('button').tap();
184−
185− await waitForNotification(page)
186− })
187−
188− cipherPage("Note")
189−
190− test('Create Contact entry page', async ({ page }) => {
191− await logIn(page)
192−
193− await page.getByRole('button').nth(2).tap();
194− await page.getByRole('button', { name: 'Contact' }).tap();
195−
196− await screenshot(page)
197−
198− await fillInContractCipher(page)
199−
200− await screenshot(page)
201−
202− await page.getByRole('heading').locator('span').getByRole('button').tap();
203−
204− await waitForNotification(page)
205− })
206−
207− cipherPage("Contact")
208−
209− test('Filled ciphers page', async ({ page }) => {
210− await logIn(page)
211− await screenshot(page)
212− })
213−
214− test('Side bar', async ({ page }) => {
215− await logIn(page)
216−
217− await page.getByRole('button').nth(1).tap();
218−
219− await screenshot(page)
220− })
221−
222− test('Passwords view', async ({ page }) => {
223− await logIn(page)
224−
225− await page.getByRole('button').nth(1).click();
226− await page.getByText('Passwords').click();
227−
228− await screenshot(page)
229− })
230−
231− test('Notes view', async ({ page }) => {
232− await logIn(page)
233−
234− await page.getByRole('button').nth(1).click();
235− await page.getByText('Notes').click();
236−
237− await screenshot(page)
238− })
239−
240− test('Cards view', async ({ page }) => {
241− await logIn(page)
242−
243− await page.getByRole('button').nth(1).click();
244− await page.getByText('Cards').click();
245−
246− await screenshot(page)
247− })
248−
249− test('Contacts view', async ({ page }) => {
250− await logIn(page)
251−
252− await page.getByRole('button').nth(1).click();
253− await page.getByText('Contacts').click();
254−
255− await screenshot(page)
256− })
257−}
tests/src/test.spec.ts-snapshots/Card-cipher-page-1-Pixel-3-linux.pngdeleted

Binary file not shown.

tests/src/test.spec.ts-snapshots/Cards-view-1-Pixel-3-linux.pngdeleted

Binary file not shown.

tests/src/test.spec.ts-snapshots/Contact-cipher-page-1-Pixel-3-linux.pngdeleted

Binary file not shown.

tests/src/test.spec.ts-snapshots/Contacts-view-1-Pixel-3-linux.pngdeleted

Binary file not shown.

tests/src/test.spec.ts-snapshots/Create-Card-entry-page-1-Pixel-3-linux.pngdeleted

Binary file not shown.

tests/src/test.spec.ts-snapshots/Create-Card-entry-page-2-Pixel-3-linux.pngdeleted

Binary file not shown.

tests/src/test.spec.ts-snapshots/Create-Contact-entry-page-1-Pixel-3-linux.pngdeleted

Binary file not shown.

tests/src/test.spec.ts-snapshots/Create-Contact-entry-page-2-Pixel-3-linux.pngdeleted

Binary file not shown.

tests/src/test.spec.ts-snapshots/Create-Note-entry-page-1-Pixel-3-linux.pngdeleted

Binary file not shown.

tests/src/test.spec.ts-snapshots/Create-Note-entry-page-2-Pixel-3-linux.pngdeleted

Binary file not shown.

tests/src/test.spec.ts-snapshots/Empty-ciphers-page-1-Pixel-3-linux.pngdeleted

Binary file not shown.

tests/src/test.spec.ts-snapshots/Filled-ciphers-page-1-Pixel-3-linux.pngdeleted

Binary file not shown.

tests/src/test.spec.ts-snapshots/Login-cipher-page-1-Pixel-3-linux.pngdeleted

Binary file not shown.

tests/src/test.spec.ts-snapshots/Login-entry-page-1-Pixel-3-linux.pngdeleted

Binary file not shown.

tests/src/test.spec.ts-snapshots/Login-entry-page-2-Pixel-3-linux.pngdeleted

Binary file not shown.

tests/src/test.spec.ts-snapshots/Login-page-1-Pixel-3-linux.pngdeleted

Binary file not shown.

tests/src/test.spec.ts-snapshots/Note-cipher-page-1-Pixel-3-linux.pngdeleted

Binary file not shown.

tests/src/test.spec.ts-snapshots/Notes-view-1-Pixel-3-linux.pngdeleted

Binary file not shown.

tests/src/test.spec.ts-snapshots/Passwords-view-1-Pixel-3-linux.pngdeleted

Binary file not shown.

tests/src/test.spec.ts-snapshots/Side-bar-1-Pixel-3-linux.pngdeleted

Binary file not shown.

tests/vaultwarden/.envdeleted−3
@@ -1,3 +0,0 @@
1−WEB_VAULT_ENABLED=false
2−LOGIN_RATELIMIT_SECONDS=1
3−LOGIN_RATELIMIT_MAX_BURST=100000000
tests/vaultwarden/README.mddeleted−4
@@ -1,4 +0,0 @@
1−# Test vaultwarden setup
2−
3−login: `test@example.com`
4−password: `Password123`
tests/vaultwarden/data/db.sqlite3deleted

Binary file not shown.

tests/vaultwarden/data/db.sqlite3-shmdeleted

Binary file not shown.

tests/vaultwarden/data/db.sqlite3-waldeleted

No changes to the content.

tests/vaultwarden/data/rsa_key.pemdeleted−27
@@ -1,27 +0,0 @@
1−-----BEGIN RSA PRIVATE KEY-----
2−MIIEowIBAAKCAQEArVVTdkrmMbDxyGxGY73Fse9Du7DplgSBPQ5uyOnAsqf6pa1U
3−jjWwTV7Xqzp0z7k32WC64P0eGdacTQGZiFUQxTVm6bJ4TZ8bh4fz51tB4npCAzpr
4−8EcgGRzZT+cA1GQOG7jDqv8w9edWWGdPr6mx1ENvnpXtv60tvnwfsioAUSnx3SC6
5−0oTVCxj1InyneUDXCk/UqBE1wmwl8aF5U3aOc42qcELAgr6i0aCsK1LQjH+SOREh
6−tRQgpDF17IdYAxdYUTyinqrYJnoo5kQXeC/4QoZIsDi1empas7pXwrDlVYtjNK2T
7−Oht6dWUnffc2oTUCNY+ATZ6Nsu4zItEM7yQo/QIDAQABAoIBABUX/q21JLlkXrA0
8−2QYXWKE0TGphd7O9fZrYEExcAgrb3WlfJBygVQG9aoKYfRGwaScHmgZ6VzjuwJuw
9−d6Y7L+zH+Gu1P+W2WKvQNDLyDJ0KotDwwaT+it3zgFC0fIncWUkvXYSIRMWQLxZY
10−ED8A6Da8NC+48DN02E3HnjXY3awCusn9fQkPLeparsdyrvz5s8lnYYQZ6u7FQeD9
11−gM5SUP8WVLtPl4KBidJvHl4qPDAxnu98cZEoffbWIAsg3wvKNZL9yE21ZGWdExxA
12−/czWYO4ckuGY7Tb0Y+MiRI5kBM4VbCzwVfZTLRPXnKddiL7QpLMNmYpHzEcMZut4
13−z3vcdnMCgYEA5ZIzaltIEfL8zmX76pIPlyBs5n98RaagCIxQyURNRSU4H6Sr6yW2
14−CNAszcn6iAxiteXkPUkWmPXE4BFt0ifKgecPQIDk0spaVu2khRPMwUMyR0PfwXfY
15−2fqT0zMZX0MB3VPxnDTY9sAvTi2FsskL9j+Pil7U8bYaixzdc2H+LcsCgYEAwUm2
16−5A1eyX8ojZM5xUaQ/ZAiW+9u0fI5UxJ9Q0SFUWn11LJyIoIvkw6puE10508e7UT1
17−rnYZc5v/Mr6E1oO2NGBHkOZdlfJMD346r2YAtm2Nt/HZuLZU38wyIZzg0FxS6lmZ
18−m1vTEoM6Ko5GVtjvLEst27ZZDgWRW7rJNyYEq1cCgYBm6ZJY9FCrkzUXRYqXziBl
19−DCklbzMDej21d0lj4FWPUK8HKGfVQxOGgeQD4K6JvJunlUYq3w/dRBecptOHaUUT
20−1MP2k5GAmSvEWkXtTkH0xS3N2rR84z/3/6ey/u450S0Nzeq6rsowW1kZf7i2u/+7
21−xSSQ0BA/xvXBNRBlACHU3wKBgQDAuYF0zqgOfY2v7HaT75KuYUMBLDHxgHYMEMl5
22−gx7zWJXXqL4JDonlNZ+gtwqzgL6zkijt9a/XCFpi/HhzdAz/GGkDX888rcWNRS26
23−qhhXzdCa8rlGZfcm1vG9djDrUMMUDJcOw3YoZVRgDlCwU8nW/ncRNjaBtqeDMTFk
24−nbHWLQKBgH0Xatwy6DVHDbU+GYcZBjyQP5zCz0JEjDE8hEhv/ioCfUK1rfjY58zU
25−oXDdn3GpFm6eFxcVxv9Ki4jViQDYAvAErBl0kMkVU6hvITPlMEyqBh89h65xUEnw
26−ttaX+Q2lKGwHsZT4RzA45XjO8kvBymY9siL2JQUMLB8wpu9RyTRj
27−-----END RSA PRIVATE KEY-----
tests/vaultwarden/data/rsa_key.pub.pemdeleted−9
@@ -1,9 +0,0 @@
1−-----BEGIN PUBLIC KEY-----
2−MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArVVTdkrmMbDxyGxGY73F
3−se9Du7DplgSBPQ5uyOnAsqf6pa1UjjWwTV7Xqzp0z7k32WC64P0eGdacTQGZiFUQ
4−xTVm6bJ4TZ8bh4fz51tB4npCAzpr8EcgGRzZT+cA1GQOG7jDqv8w9edWWGdPr6mx
5−1ENvnpXtv60tvnwfsioAUSnx3SC60oTVCxj1InyneUDXCk/UqBE1wmwl8aF5U3aO
6−c42qcELAgr6i0aCsK1LQjH+SOREhtRQgpDF17IdYAxdYUTyinqrYJnoo5kQXeC/4
7−QoZIsDi1empas7pXwrDlVYtjNK2TOht6dWUnffc2oTUCNY+ATZ6Nsu4zItEM7yQo
8−/QIDAQAB
9−-----END PUBLIC KEY-----
tests/vaultwarden/run.shdeleted−13
@@ -1,13 +0,0 @@
1−#!/bin/sh
2−
3−set -e
4−
5−cd "$(dirname "$0")"
6−
7−tmp="$(mktemp -d)"
8−
9−cp -r data .env "$tmp"
10−
11−cd "$tmp"
12−
13−nix run github:NixOS/nixpkgs?rev=d9a1414346059619d9e13ab93e749bbb82e5252a#vaultwarden
vanillanew file+1

Submodule changed.