Compare commits

...

51 Commits

Author SHA1 Message Date
Lucas Nogueira dac8b6331c fix(ci): commig schemas 2024-02-03 18:46:36 -03:00
Lucas Fernandes Nogueira 8502b90287 Merge pull request #859 from tauri-apps/release-v2
Publish New Versions (v2)
2024-02-03 18:28:39 -03:00
Lucas Nogueira fe10ab488f beta.1 2024-02-03 18:16:49 -03:00
Lucas Nogueira 7659b9515d merge from v2 2024-02-03 17:05:27 -03:00
Lucas Fernandes Nogueira 5673a416a8 Merge pull request #927 from tauri-apps/v1v2
chore: merge v1 into v2 for the last time
2024-02-03 16:58:35 -03:00
FabianLars ee910bfcce come on, that felt personal 2024-02-03 20:29:33 +01:00
FabianLars 331a3eaf06 unlucky typo lol 2024-02-03 20:28:12 +01:00
FabianLars cb5ff764a6 license 2024-02-03 20:26:49 +01:00
FabianLars 0688416425 lint 2024-02-03 20:23:09 +01:00
FabianLars 14f5961569 chore: merge v1 into v2 for the last time 2024-02-03 20:16:36 +01:00
Lucas Nogueira 63afaf1dfa fmt 2024-02-03 15:46:18 -03:00
Lucas Nogueira abd0176cc1 beta 2024-02-03 15:46:15 -03:00
lucasfernog 18e478aba0 publish new versions 2024-02-03 15:46:07 -03:00
Lucas Nogueira 2643a4c331 fix(ci): revert pre.json changes 2024-02-03 15:24:43 -03:00
Tillmann d198c01486 feat: update to tauri beta, add permissions (#862)
Co-authored-by: Lucas Nogueira <lucas@tauri.app>
Co-authored-by: Lucas Nogueira <lucas@crabnebula.dev>
2024-02-03 15:14:41 -03:00
Amr Bashir 506ce4835b refactor(updater): accomodate to new tauri config restructure (#924)
* refactor(updater): accomodate to new tauri config restructure

RFC#5 https://github.com/tauri-apps/rfcs/blob/f3e82a6b0c5390401e855850d47dc7b7d9afd684/texts/0005-tauri-config-restructure.md

RFC#5 PR implementation: https://github.com/tauri-apps/tauri/pull/8723

* lint and update configs
2024-02-03 09:14:39 -03:00
Lucas Fernandes Nogueira 15dc7060e8 fix(shell): deadlock on read line error (#923) 2024-02-01 00:01:23 -03:00
Shubham Parihar 18cb861b52 type: add OpenDialogReturn generic type (#919)
* type: add OpenDialogReturn generic type

* chore: run pnpm format

* type: export OpenDialogReturn type

* chore: run pnpm format
2024-01-30 02:08:46 +02:00
Lucas Fernandes Nogueira 8dea78ac7d fix(notification): scheduled notifications not working (#909)
* fix(notification): scheduled notifications not working

* do not use toJSON since it doesnt work on isolation pattern

* fmt
2024-01-24 13:02:11 -03:00
Daniel Faust 61edbbec0a feat(fs/watch): migrate to notify-debouncer-full (#885)
* Add support for notify-debouncer-full

* Add fs watch to demo

* Remove notify-debouncer-mini

* Rename RawEvent to WatchEvent

* Add full type definition for EventKind

* Remove `track file ids` option from fs watcher

* Update plugins/fs/guest-js/index.ts
2024-01-18 20:35:49 +02:00
Amr Bashir 0879a87a7e refactor(updater): migrate to tauri's new resource table (#899)
* refactor(updater): migrate to tauri's new resource table

* fmt

* fmt

* Create updater-js-started-event.md
2024-01-18 12:48:44 +01:00
Amr Bashir 8505a756b5 chore: add change file for updater proxy change (#907) 2024-01-17 17:51:20 +02:00
Zhang San 4e2e77580c feat(updater) support proxy (#891)
* add proxy support

Signed-off-by: San Zhang <sanzhang@mail.com>

* 1. change string to url for proxy type
2. add proxy option in js api

Signed-off-by: San Zhang <sanzhang@mail.com>

* fix fmt issue

Signed-off-by: San Zhang <sanzhang@mail.com>

* Update plugins/updater/guest-js/index.ts

---------

Signed-off-by: San Zhang <sanzhang@mail.com>
2024-01-17 16:47:26 +02:00
Anton Piliugin 8a3db790b8 fix(updater): fix readme and types (#903)
* Types fix

* generate files
2024-01-17 01:45:22 +02:00
Olivier Lemasle bf5a21d5b2 fix(dialog): On Android, do not add a Cancel button to message dialogs (#873) 2024-01-11 04:30:32 +02:00
阿良仔 1b1d795b58 fix(notification): export the missing Schedule class (#874)
* fix: export the missing `Schedule` class

* remove `ScheduleData`
2024-01-03 13:34:15 +01:00
Amr Bashir ae0cb92438 refactor(http): migrate to tauri's new resource table (#834)
* refactor(http): migrate to tauri's new resource table

* fmt

* change file
2024-01-02 21:55:57 +02:00
阿良仔 ea8eadce85 fix(fs): match default value of create option with js documentation (#865)
* fix(fs): inconsistency in guest-js & rust side

* Update .changes/fix-fs-write-default-option.md
2024-01-02 20:44:09 +02:00
lopo be5aebdec9 docs(log): update readme code example for v2 (#869) 2024-01-01 12:24:41 +01:00
Matthias Lohscheidt b9d29a0154 docs(sql): Add migrations section to README (#867)
Add section about migration management.
2023-12-31 16:42:22 +01:00
hygkui 1a347203a5 fix: fix http plugin fetch init with connectTimeout. (#858) 2023-12-28 18:07:52 +02:00
github-actions[bot] e80626cf81 Publish New Versions (v2) (#854)
Co-authored-by: FabianLars <FabianLars@users.noreply.github.com>
2023-12-28 10:37:04 -03:00
Amr Bashir 2e2fc8de69 fix(fs): use correct arg name for unwatch (#857)
* fix(fs): use correct arg name for unwatch

* fmt
2023-12-28 11:18:44 +01:00
Trevor Fitzgerald 85f8419682 fix(fs): Fix DebouncedEvent type to match what notify-rs returns (#848)
* fix(fs): Fix DebouncedEvent type to match what notify-rs returns

* Update fix-debounced-event-type.md
2023-12-27 15:39:05 +02:00
Amr Bashir c60123093d fix(fs): fix panic due to unwrap & truncate by default (#847)
* fix(fs): fix panic due to unwrap & truncate by default

closes #846

* fmt and change file
2023-12-27 15:27:45 +02:00
github-actions[bot] 38b5d37b54 Publish New Versions (v2) (#843)
Co-authored-by: lucasfernog <lucasfernog@users.noreply.github.com>
2023-12-20 14:29:57 -03:00
Lucas Fernandes Nogueira 8b1d821a37 fix(deep-link): issue with tauri alpha.20 emit breaking change (#844) 2023-12-20 14:05:24 -03:00
阿良仔 bfa87da848 feat(http): expose proxy configuration (#824)
* feat(http): add proxy config

* chore: build iife api

* chore: allow `too_many_arguments`

* improvement

* refactor: restructure code

* improvement

* format
2023-12-20 16:14:12 +02:00
Lucas Nogueira c2115d8d21 chore: fill description field for new crates 2023-12-20 00:10:26 -03:00
github-actions[bot] 8c6d96e645 Publish New Versions (v2) (#832)
Co-authored-by: lucasfernog <lucasfernog@users.noreply.github.com>
2023-12-19 23:56:51 -03:00
Lucas Nogueira 7bdc32079c fix(ci): set dependencies on covector config 2023-12-19 23:21:37 -03:00
Lucas Fernandes Nogueira 10b80391fc refactor(fs): use scope from tauri core (#825)
* fix(fs): scope checks on Android

On Android, when we call canonicalize() on "/data/user/0/appid" (which is the data dir), the result is a "/data/data/appid" path, so we need to adjust our scope for that.

* use scope from core

* update persisted-scope

* fix build

* dev branch
2023-12-19 23:19:01 -03:00
Tillmann 1eaf640255 Added Security Policy and Threat Model for Log Plugin (#828)
* Initial threat model and security contact

* Corrections and finalize policy
2023-12-19 22:25:42 -03:00
Amr Bashir 69a1fa099c feat(fs): improved API (#751)
* feat(fs): improved API

* fmt

* fix unix builds

* again

* clippy

* clippy

* fix import in docs examples

* fmt, clippy

* Update linux.rs

* add API for watch

* fix with `watcher` feature flag

* use baseDir for all commands

* do not export close function

* fix build

* organize and address review comments

* fmt

* generated files

* rename FsFile to FileHandle, move APIs and docs

* extend example

* extend `Resource`

* actually extend it

---------

Co-authored-by: FabianLars <fabianlars@fabianlars.de>
Co-authored-by: Lucas Nogueira <lucas@tauri.studio>
2023-12-19 22:08:34 -03:00
Lucas Fernandes Nogueira 2cf8faa3e1 chore(deps): update to tauri alpha.20, @tauri-apps/api alpha.13 (#839)
* chore(deps): update to tauri alpha.20, @tauri-apps/api alpha.13

* fix lockfile
2023-12-20 00:18:05 +02:00
Amr Bashir e5f979f91a fix(core/updater): check if installer args are not empty before passing -ArgumentList (#818) 2023-12-19 17:37:27 -03:00
Lucas Nogueira 8df28a9875 feat(mobile): add biometric plugin (#829)
* chore: update deps, make mobile script paths relative

* feat(biometric): setup plugin folder

* feat: implement iOS

* add api

* android

* fix plugin name

* also check empty info.plist entry

* add example

* fix android

* supress

* lint

* better explanation

* add partners & contributed by

* change ext

* license headers

* update vite

* add covector setup

* tauri/dox removed

* add example

* docs

---------

Co-authored-by: Lucas Nogueira <lucas@tauri.app>
2023-12-19 11:16:13 -03:00
Lucas Nogueira fe79adb5c7 feat(mobile): add NFC plugin (#830)
* feat: scaffold NFC plugin, initial iOS code

* adjust script paths (api example)

* update entitlements & plist

* update class name

* update api

* sketch api, remove desktop

* update response data

* add write fn

* remove commands

* fixes for write mode

* check nfc state before using the APIs

* fix(example): downgrade internal-ip to v7

* feat: typed iOS arguments

* update swift requirement

* android updates

* update tauri

* fix icon

* update example

* fix build

* fix notification example

* fix clipboard

* fix ios notification build

* fix info.plist

* update tauri

* add change file

* fmt

* update to new args class syntax :( [skip ci]

* add lang code handling in RTD_TEXT helper (written payload is broken without it)

* update nfc to latest tauri. use tauri from git

* update lockfile

* android: add initial nfc writer implementation

* check sdk version for pendingintent flag

* quicksaving basic ndef reading that doesn't crash

* add basic ndef reading (android)

* small cleanup

* change pending action type

* validate available state

* gradle 8.0.0

* use session class

* implement keep session alive

* fix notification crash??

* remove dox feature, fix breaking changes

* update dependencies

* fix shell tests [skip ci]

* fmt [skip ci]

* type safe args

* scan kind options

* commit .idea files

* update api

* update example

* fix app check

* alertmessage options for iOS

* default to tag on example

* fix(ios): always close session on write, remove keepsessionalive option

* add kind input to write options

* empty records if message not found

* fill tag metadata for ndef read

* add contributors

* update vite

* covector setup

* tauri/dox removed

* docs and examples

* fmt [skip ci]

---------

Co-authored-by: FabianLars-crabnebula <fabianlars@crabnebula.dev>
Co-authored-by: Lucas Nogueira <lucas@tauri.app>
Co-authored-by: Lucas Nogueira <lucas@tauri.studio>
2023-12-19 10:50:31 -03:00
Fabian-Lars 7c59e3785b feat(dialog): Re-export FileResponse (#837)
closes #835
2023-12-19 15:35:53 +02:00
Amr Bashir b1c6a7885e fix(core/dialog): reconstruct path using appropriate separator (#820)
* fix(core/dialog): reconstruct path using appropriate separator

ref: tauri-apps/tauri#8074

* Reference original issue [skip ci]
2023-12-18 21:09:04 -03:00
Lucas Fernandes Nogueira 2d6bfbfacf feat(fs): include full error message (#826) 2023-12-18 15:01:47 -03:00
638 changed files with 41282 additions and 8176 deletions
+30
View File
@@ -0,0 +1,30 @@
---
"authenticator": patch
"autostart": patch
"barcode-scanner": patch
"cli": patch
"clipboard-manager": patch
"deep-link": patch
"dialog": patch
"fs": patch
"global-shortcut": patch
"http": patch
"localhost": patch
"log-plugin": patch
"notification": patch
"os": patch
"persisted-scope": patch
"positioner": patch
"process": patch
"shell": patch
"single-instance": patch
"sql": patch
"store": patch
"stronghold": patch
"updater": patch
"upload": patch
"websocket": patch
"window-state": patch
---
Update to tauri@alpha.20.
+27
View File
@@ -0,0 +1,27 @@
---
"authenticator-js": patch
"autostart-js": patch
"barcode-scanner-js": patch
"cli-js": patch
"clipboard-manager-js": patch
"deep-link-js": patch
"dialog-js": patch
"fs-js": patch
"global-shortcut-js": patch
"http-js": patch
"log-js": patch
"notification-js": patch
"os-js": patch
"positioner-js": patch
"process-js": patch
"shell-js": patch
"sql-js": patch
"store-js": patch
"stronghold-js": patch
"updater-js": patch
"upload-js": patch
"websocket-js": patch
"window-state-js": patch
---
Update to @tauri-apps/api v2.0.0-alpha.12.
+1 -1
View File
@@ -24,4 +24,4 @@
"window-state-js": patch
---
Update to @tauri-apps/api v2.0.0-alpha.12.
Update to @tauri-apps/api v2.0.0-alpha.13.
+57
View File
@@ -0,0 +1,57 @@
---
"authenticator": patch
"autostart": patch
"barcode-scanner": patch
"biometric": patch
"cli": patch
"clipboard-manager": patch
"deep-link": patch
"dialog": patch
"fs": patch
"global-shortcut": patch
"http": patch
"localhost": patch
"log-plugin": patch
"nfc": patch
"notification": patch
"os": patch
"persisted-scope": patch
"positioner": patch
"process": patch
"shell": patch
"single-instance": patch
"sql": patch
"store": patch
"stronghold": patch
"updater": patch
"upload": patch
"websocket": patch
"window-state": patch
"authenticator-js": patch
"autostart-js": patch
"barcode-scanner-js": patch
"biometric-js": patch
"cli-js": patch
"clipboard-manager-js": patch
"deep-link-js": patch
"dialog-js": patch
"fs-js": patch
"global-shortcut-js": patch
"http-js": patch
"log-js": patch
"nfc-js": patch
"notification-js": patch
"os-js": patch
"positioner-js": patch
"process-js": patch
"shell-js": patch
"sql-js": patch
"store-js": patch
"stronghold-js": patch
"updater-js": patch
"upload-js": patch
"websocket-js": patch
"window-state-js": patch
---
Update to tauri beta.
+6
View File
@@ -0,0 +1,6 @@
---
"biometric": major
"biometric-js": major
---
Initial release.
+21 -26
View File
@@ -39,6 +39,7 @@
"publish": false,
"dependencies": [
"barcode-scanner",
"biometric",
"log-plugin",
"cli",
"clipboard-manager",
@@ -46,6 +47,7 @@
"fs",
"global-shortcut",
"http",
"nfc",
"notification",
"os",
"process",
@@ -59,6 +61,7 @@
"publish": false,
"dependencies": [
"barcode-scanner-js",
"biometric-js",
"log-js",
"cli-js",
"clipboard-manager-js",
@@ -66,6 +69,7 @@
"fs-js",
"global-shortcut-js",
"http-js",
"nfc-js",
"notification-js",
"os-js",
"process-js",
@@ -74,7 +78,6 @@
],
"postversion": "pnpm install --no-frozen-lockfile"
},
"deep-link-example-js": {
"path": "./plugins/deep-link/examples/app",
"manager": "javascript",
@@ -82,7 +85,6 @@
"dependencies": ["deep-link-js"],
"postversion": "pnpm install --no-frozen-lockfile"
},
"authenticator": {
"path": "./plugins/authenticator",
"manager": "rust"
@@ -91,7 +93,6 @@
"path": "./plugins/authenticator",
"manager": "javascript"
},
"autostart": {
"path": "./plugins/autostart",
"manager": "rust"
@@ -100,7 +101,6 @@
"path": "./plugins/autostart",
"manager": "javascript"
},
"barcode-scanner": {
"path": "./plugins/barcode-scanner",
"manager": "rust"
@@ -109,7 +109,14 @@
"path": "./plugins/barcode-scanner",
"manager": "javascript"
},
"biometric": {
"path": "./plugins/biometric",
"manager": "rust"
},
"biometric-js": {
"path": "./plugins/biometric",
"manager": "javascript"
},
"cli": {
"path": "./plugins/cli",
"manager": "rust"
@@ -118,7 +125,6 @@
"path": "./plugins/cli",
"manager": "javascript"
},
"clipboard-manager": {
"path": "./plugins/clipboard-manager",
"manager": "rust"
@@ -127,7 +133,6 @@
"path": "./plugins/clipboard-manager",
"manager": "javascript"
},
"deep-link": {
"path": "./plugins/deep-link",
"manager": "rust"
@@ -136,7 +141,6 @@
"path": "./plugins/deep-link",
"manager": "javascript"
},
"fs": {
"path": "./plugins/fs",
"manager": "rust"
@@ -145,7 +149,6 @@
"path": "./plugins/fs",
"manager": "javascript"
},
"dialog": {
"path": "./plugins/dialog",
"manager": "rust",
@@ -155,7 +158,6 @@
"path": "./plugins/dialog",
"manager": "javascript"
},
"global-shortcut": {
"path": "./plugins/global-shortcut",
"manager": "rust"
@@ -164,7 +166,6 @@
"path": "./plugins/global-shortcut",
"manager": "javascript"
},
"http": {
"path": "./plugins/http",
"manager": "rust",
@@ -174,12 +175,10 @@
"path": "./plugins/http",
"manager": "javascript"
},
"localhost": {
"path": "./plugins/localhost",
"manager": "rust"
},
"log-plugin": {
"path": "./plugins/log",
"manager": "rust"
@@ -189,6 +188,15 @@
"manager": "javascript"
},
"nfc": {
"path": "./plugins/nfc",
"manager": "rust"
},
"nfc-js": {
"path": "./plugins/nfc",
"manager": "javascript"
},
"notification": {
"path": "./plugins/notification",
"manager": "rust"
@@ -197,7 +205,6 @@
"path": "./plugins/notification",
"manager": "javascript"
},
"os": {
"path": "./plugins/os",
"manager": "rust"
@@ -206,13 +213,11 @@
"path": "./plugins/os",
"manager": "javascript"
},
"persisted-scope": {
"path": "./plugins/persisted-scope",
"manager": "rust",
"dependencies": ["fs"]
},
"positioner": {
"path": "./plugins/positioner",
"manager": "rust"
@@ -221,7 +226,6 @@
"path": "./plugins/positioner",
"manager": "javascript"
},
"process": {
"path": "./plugins/process",
"manager": "rust"
@@ -230,7 +234,6 @@
"path": "./plugins/process",
"manager": "javascript"
},
"shell": {
"path": "./plugins/shell",
"manager": "rust"
@@ -239,12 +242,10 @@
"path": "./plugins/shell",
"manager": "javascript"
},
"single-instance": {
"path": "./plugins/single-instance",
"manager": "rust"
},
"sql": {
"path": "./plugins/sql",
"manager": "rust",
@@ -274,7 +275,6 @@
"path": "./plugins/sql",
"manager": "javascript"
},
"store": {
"path": "./plugins/store",
"manager": "rust"
@@ -283,7 +283,6 @@
"path": "./plugins/store",
"manager": "javascript"
},
"stronghold": {
"path": "./plugins/stronghold",
"manager": "rust"
@@ -292,7 +291,6 @@
"path": "./plugins/stronghold",
"manager": "javascript"
},
"updater": {
"path": "./plugins/updater",
"manager": "rust"
@@ -301,7 +299,6 @@
"path": "./plugins/updater",
"manager": "javascript"
},
"upload": {
"path": "./plugins/upload",
"manager": "rust"
@@ -310,7 +307,6 @@
"path": "./plugins/upload",
"manager": "javascript"
},
"websocket": {
"path": "./plugins/websocket",
"manager": "rust"
@@ -319,7 +315,6 @@
"path": "./plugins/websocket",
"manager": "javascript"
},
"window-state": {
"path": "./plugins/window-state",
"manager": "rust"
@@ -0,0 +1,5 @@
---
"dialog": patch
---
On Windows, fix `open` dialog `defaultPath`, when invoked from JS, not working if the path uses forward slash (`/`)
+5
View File
@@ -0,0 +1,5 @@
---
"dialog": patch
---
Re-export `FileResponse` interface.
+5
View File
@@ -0,0 +1,5 @@
---
"dialog": "patch"
---
On Android, do not add a `Cancel` button to message dialogs.
+5
View File
@@ -0,0 +1,5 @@
---
"shell": patch
---
Fixes a deadlock when reading a stdout or stderr line returns an error.
+5
View File
@@ -0,0 +1,5 @@
---
"fs-js": patch
---
Fix `DebouncedEvent` type to correctly represent the actual type.
+5
View File
@@ -0,0 +1,5 @@
---
"deep-link": patch
---
Fixes issue with tauri alpha.20.
+6
View File
@@ -0,0 +1,6 @@
---
"fs": "patch"
"fs-js": "patch"
---
Fix incorrect `create` option default value for `writeFile` and `writeTextFile` which didn't match documentation.
@@ -0,0 +1,5 @@
---
"notification-js": patch
---
Export the missing `Schedule` class.
+6
View File
@@ -0,0 +1,6 @@
---
"notification": patch
"notification-js": patch
---
Fixes deserialization and implementation bugs with scheduled notifications on Android.
+6
View File
@@ -0,0 +1,6 @@
---
"fs": "patch"
"fs-js": "patch"
---
Add `createNew` option for `writeFile` and `writeTextFile` to create the file if doesn't exist and fail if it does.
+5
View File
@@ -0,0 +1,5 @@
---
"fs": patch
---
Fix promise rejection error only containing the context and stripping the actual error message.
+6
View File
@@ -0,0 +1,6 @@
---
"fs": "patch"
"fs-js": "patch"
---
The `fs` plugin received a major overhaul to add new APIs and changed existing APIs to be closer to Node.js and Deno APIs.
+6
View File
@@ -0,0 +1,6 @@
---
"fs": "patch"
"fs-js": "patch"
---
Replace `notify-debouncer-mini` with `notify-debouncer-full`. [(plugins-workspace#885)](https://github.com/tauri-apps/plugins-workspace/pull/885)
+6
View File
@@ -0,0 +1,6 @@
---
"fs": patch
"persisted-scope": patch
---
Use `tauri::scope::fs::Scope` instead of local copy of its implementation.
+6
View File
@@ -0,0 +1,6 @@
---
"fs": "patch"
"fs-js": "patch"
---
Truncate files when using `writeFile` and `writeTextFile` with `append: false`.
+5
View File
@@ -0,0 +1,5 @@
---
"fs-js": "patch"
---
Fix `invalid args id for command unwatch` error when trying to unwatch a previously watched file or directory.
+6
View File
@@ -0,0 +1,6 @@
---
"fs": "patch"
"fs-js": "patch"
---
Fix panic when using `writeFile` or `writeTextFile` without passing an option object.
@@ -0,0 +1,5 @@
---
"http-js": minor
---
Fix http fetch client option init with parameter `connectTimeout`
+6
View File
@@ -0,0 +1,6 @@
---
"http": minor
"http-js": minor
---
Add `proxy` field to `fetch` options to configure proxy.
+6
View File
@@ -0,0 +1,6 @@
---
"nfc": major
"nfc-js": major
---
Initial release.
+36
View File
@@ -0,0 +1,36 @@
---
"authenticator": patch
"autostart": patch
"barcode-scanner": patch
"barcode-scanner-js": patch
"biometric": patch
"cli": patch
"clipboard-manager": patch
"clipboard-manager-js": patch
"deep-link": patch
"dialog": patch
"fs": patch
"global-shortcut": patch
"global-shortcut-js": patch
"http": patch
"localhost": patch
"log-plugin": patch
"nfc": patch
"nfc-js": patch
"notification": patch
"os": patch
"persisted-scope": patch
"positioner": patch
"process": patch
"shell": patch
"single-instance": patch
"sql": patch
"store": patch
"stronghold": patch
"updater": patch
"upload": patch
"websocket": patch
"window-state": patch
---
Add permissions.
+33 -1
View File
@@ -4,34 +4,59 @@
".changes/alpha.16.md",
".changes/alpha.17.md",
".changes/alpha.18.md",
".changes/alpha.20.md",
".changes/api-alpha.11.md",
".changes/api-alpha.12.md",
".changes/api-alpha.13.md",
".changes/api-alpha.9.md",
".changes/beta.md",
".changes/biometric-initial-release.md",
".changes/deep-link-initial-release.md",
".changes/dialog-async-message-dialog.md",
".changes/dialog-default-path-windows-slash.md",
".changes/dialog-export-fileresponse.md",
".changes/dialog-message-android.md",
".changes/dialog-pin-rfd.md",
".changes/dialog-return-result.md",
".changes/fix-cmd-spawn-deadlock.md",
".changes/fix-debounced-event-type.md",
".changes/fix-deep-link-alpha-20.md",
".changes/fix-docs-build.md",
".changes/fix-emit-all-usage.md",
".changes/fix-fs-write-default-option.md",
".changes/fix-invoke-usage.md",
".changes/fix-notification-schedule-export.md",
".changes/fix-number-query-params.md",
".changes/fix-permission-notification.md",
".changes/fix-scheduled-notification.md",
".changes/fix-updater-macos.md",
".changes/fix-window-state-api.md",
".changes/fs-create-new.md",
".changes/fs-exists-2nd-arg.md",
".changes/fs-improve-error-message.md",
".changes/fs-improved-apis.md",
".changes/fs-replace-notify-debouncer.md",
".changes/fs-scope-tauri.md",
".changes/fs-trunacte-non-append.md",
".changes/fs-unwtach-rid.md",
".changes/fs-wiret-binary-file.md",
".changes/fs-write-panic.md",
".changes/global-shortcut-app-handle.md",
".changes/http-init-fetch-option-connectTimeout.md",
".changes/http-multipart-refactor.md",
".changes/http-plugin-refactor.md",
".changes/http-proxy-config.md",
".changes/http-remove-cmd-property.md",
".changes/http-response.md",
".changes/msrv-1.70.md",
".changes/nfc-initial-release.md",
".changes/notification-init-script.md",
".changes/notification-revert-sound.md",
".changes/notification-sound.md",
".changes/os-OsType.md",
".changes/os-plugin-refactor.md",
".changes/os-reading-undefined.md",
".changes/permissions.md",
".changes/persisted-scope-asset.md",
".changes/persisted-scope-glob.md",
".changes/positioner-tray-flag.md",
@@ -45,19 +70,26 @@
".changes/tauri-alpha-15.md",
".changes/tauri-alpha.11.md",
".changes/tauri-alpha.12.md",
".changes/tauri-http-plugin-errror-invalid-rid.md",
".changes/tray-position.md",
".changes/updater-appimage.md",
".changes/updater-escaped-path.md",
".changes/updater-js-started-event.md",
".changes/updater-nsis-admin.md",
".changes/updater-nsis-basicui.md",
".changes/updater-nsis.md",
".changes/updater-on-chunk-fn-mut.md",
".changes/updater-plugin-refactor.md",
".changes/updater-proxy.md",
".changes/updater-string-replace-round2.md",
".changes/updater-string-replace.md",
".changes/updater.md",
".changes/upload-bufwriter.md",
".changes/v2-alpha.md",
".changes/websocket-fix-arg-name.md",
".changes/websocket-headers.md",
".changes/window-state-decorated.md",
".changes/window-state-promise.md"
".changes/window-state-promise.md",
".changes/window-state-zero-positions.md"
]
}
@@ -0,0 +1,5 @@
---
"http": "patch"
---
**Breaking change** Removed `Error::InvalidRequestId` variant and added `Error::Tauri` variant.
+6
View File
@@ -0,0 +1,6 @@
---
"updater": "patch"
"updater-js": "patch"
---
Fix `Started` event not emitted to JS when downloading update.
+6
View File
@@ -0,0 +1,6 @@
---
"updater": "patch"
"updater-js": "patch"
---
Fix NSIS updater failing to launch when using `basicUi` mode.
+5
View File
@@ -0,0 +1,5 @@
---
"updater": "patch"
---
**Breaking change**: Changed `Update::download` and `Update::download_and_install` first argument to take `FnMut` instead of just `Fn`.
+6
View File
@@ -0,0 +1,6 @@
---
"updater": patch
"updater-js": patch
---
Add support for specifying proxy to use for checking and downloading updates.
+21
View File
@@ -0,0 +1,21 @@
---
"updater": "patch"
---
Refactored the updater plugin to accommodate to the new changes in tauri config:
- JSON plugin config:
- Added `pubkey` option.
- Added `windows` option which is a Windows-specific options.
- Renamed `installer_args` to `installerArgs`.
- Moved `installerArgs` option to the new `windows` object.
- Rust crate changes:
- Added `pubkey` field for `Config`.
- Added `windows` field for `Config` option which is a Windows-specific options.
- Added `WindowsConfig` and `WindowsUpdateInstallMode`.
- Moved `installerArgs` option to the new `windows` object.
- Added `Builder::pubkey` and `UpdaterBuilder::pubkey`.
- Changed `Builder::installer_args` and `UpdaterBuilder::installer_args` to add to existing installer args, instead of replacing them, use `Builder/UpdaterBuilder::clear_installer_args` to clear existing args.
- Added `Builder::installer_arg`, `Builder::clear_installer_args`, `UpdaterBuilder::installer_arg` and `UpdaterBuilder::clear_installer_args`.
+8
View File
@@ -0,0 +1,8 @@
---
"window-state": "patch"
---
Address a couple of issues with restoring positions:
- Fix restoring window positions correctly when the top-left corner of the window was outside of the monitor.
- Fix restore maximization state only maximized on main monitor.
+1 -1
View File
@@ -7,7 +7,7 @@
"prettier",
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:security/recommended"
"plugin:security/recommended-legacy"
],
"overrides": [],
"parser": "@typescript-eslint/parser",
+2
View File
@@ -37,3 +37,5 @@ jobs:
- uses: rustsec/audit-check@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
# https://github.com/tauri-apps/plugins-workspace/issues/774
ignore: ${{ github.event_name != 'schedule' && 'RUSTSEC-2023-0071' || '' }}
+2 -2
View File
@@ -1,4 +1,4 @@
target
node_modules
dist
dist-js
dist-js
dist
+2 -1
View File
@@ -8,4 +8,5 @@ Cargo.lock
build
api-iife.js
init-iife.js
intermediates/
intermediates/
*schema.json
+6 -2
View File
@@ -9,7 +9,11 @@ import readline from "readline";
const header = `Copyright 2019-2023 Tauri Programme within The Commons Conservancy
SPDX-License-Identifier: Apache-2.0
SPDX-License-Identifier: MIT`;
const ignoredLicense = "// Copyright 2021 Jonas Kruckenberg";
const ignoredLicenses = [
"// Copyright 2021 Flavio Oliveira",
"// Copyright 2021 Jonas Kruckenberg",
"// Copyright 2018-2023 the Deno authors.",
];
const extensions = [".rs", ".js", ".ts", ".yml", ".swift", ".kt"];
const ignore = [
@@ -44,7 +48,7 @@ async function checkFile(file) {
line.length === 0 ||
line.startsWith("#!") ||
line.startsWith("// swift-tools-version:") ||
line === ignoredLicense
ignoredLicenses.includes(line)
) {
continue;
}
Generated
+1092 -380
View File
File diff suppressed because it is too large Load Diff
+12 -4
View File
@@ -1,18 +1,26 @@
[workspace]
members = ["plugins/*", "plugins/*/tests/*", "plugins/*/examples/*/src-tauri", "examples/*/src-tauri"]
members = [
"plugins/*",
"plugins/*/tests/*",
"plugins/*/examples/*/src-tauri",
"examples/*/src-tauri",
]
resolver = "2"
[workspace.dependencies]
serde = { version = "1", features = ["derive"] }
log = "0.4"
tauri = "2.0.0-alpha.18"
tauri-build = "2.0.0-alpha.12"
tauri = "2.0.0-beta.1"
tauri-build = "2.0.0-beta.0"
tauri-plugin = "2.0.0-beta.0"
serde_json = "1"
thiserror = "1"
url = "2"
schemars = "0.8"
[workspace.package]
edition = "2021"
authors = [ "Tauri Programme within The Commons Conservancy" ]
authors = ["Tauri Programme within The Commons Conservancy"]
license = "Apache-2.0 OR MIT"
rust-version = "1.70"
+52
View File
@@ -1,5 +1,57 @@
# Changelog
## \[2.0.0-beta.0]
### Dependencies
- Upgraded to `barcode-scanner-js@2.0.0-beta.0`
- Upgraded to `biometric-js@2.0.0-beta.0`
- Upgraded to `cli-js@2.0.0-beta.0`
- Upgraded to `clipboard-manager-js@2.0.0-beta.0`
- Upgraded to `dialog-js@2.0.0-beta.0`
- Upgraded to `fs-js@2.0.0-beta.0`
- Upgraded to `global-shortcut-js@2.0.0-beta.0`
- Upgraded to `http-js@2.0.0-beta.0`
- Upgraded to `log-js@2.0.0-beta.0`
- Upgraded to `nfc-js@2.0.0-beta.0`
- Upgraded to `notification-js@2.0.0-beta.0`
- Upgraded to `os-js@2.0.0-beta.0`
- Upgraded to `process-js@2.0.0-beta.0`
- Upgraded to `shell-js@2.0.0-beta.0`
- Upgraded to `updater-js@2.0.0-beta.0`
## \[2.0.0-alpha.9]
### Dependencies
- Upgraded to `fs-js@2.0.0-alpha.6`
## \[2.0.0-alpha.8]
### Dependencies
- Upgraded to `http-js@2.0.0-alpha.6`
## \[2.0.0-alpha.7]
### Dependencies
- Upgraded to `barcode-scanner-js@2.0.0-alpha.4`
- Upgraded to `cli-js@2.0.0-alpha.5`
- Upgraded to `clipboard-manager-js@2.0.0-alpha.5`
- Upgraded to `dialog-js@2.0.0-alpha.5`
- Upgraded to `fs-js@2.0.0-alpha.5`
- Upgraded to `global-shortcut-js@2.0.0-alpha.5`
- Upgraded to `http-js@2.0.0-alpha.5`
- Upgraded to `log-js@2.0.0-alpha.5`
- Upgraded to `notification-js@2.0.0-alpha.5`
- Upgraded to `os-js@2.0.0-alpha.6`
- Upgraded to `process-js@2.0.0-alpha.5`
- Upgraded to `shell-js@2.0.0-alpha.5`
- Upgraded to `updater-js@2.0.0-alpha.5`
- Upgraded to `biometric-js@2.0.0-alpha.0`
- Upgraded to `nfc-js@2.0.0-alpha.0`
## \[2.0.0-alpha.6]
### Dependencies
+18 -16
View File
@@ -1,7 +1,7 @@
{
"name": "svelte-app",
"private": true,
"version": "2.0.0-alpha.6",
"version": "2.0.0-beta.0",
"type": "module",
"scripts": {
"dev": "vite --clearScreen false",
@@ -9,30 +9,32 @@
"serve": "vite preview"
},
"dependencies": {
"@tauri-apps/api": "2.0.0-alpha.12",
"@tauri-apps/plugin-barcode-scanner": "2.0.0-alpha.3",
"@tauri-apps/plugin-cli": "2.0.0-alpha.4",
"@tauri-apps/plugin-clipboard-manager": "2.0.0-alpha.4",
"@tauri-apps/plugin-dialog": "2.0.0-alpha.4",
"@tauri-apps/plugin-fs": "2.0.0-alpha.4",
"@tauri-apps/plugin-global-shortcut": "2.0.0-alpha.4",
"@tauri-apps/plugin-http": "2.0.0-alpha.4",
"@tauri-apps/plugin-notification": "2.0.0-alpha.4",
"@tauri-apps/plugin-os": "2.0.0-alpha.5",
"@tauri-apps/plugin-process": "2.0.0-alpha.4",
"@tauri-apps/plugin-shell": "2.0.0-alpha.4",
"@tauri-apps/plugin-updater": "2.0.0-alpha.4",
"@tauri-apps/api": "2.0.0-beta.0",
"@tauri-apps/plugin-barcode-scanner": "2.0.0-beta.0",
"@tauri-apps/plugin-biometric": "2.0.0-beta.0",
"@tauri-apps/plugin-cli": "2.0.0-beta.0",
"@tauri-apps/plugin-clipboard-manager": "2.0.0-beta.0",
"@tauri-apps/plugin-dialog": "2.0.0-beta.0",
"@tauri-apps/plugin-fs": "2.0.0-beta.0",
"@tauri-apps/plugin-global-shortcut": "2.0.0-beta.0",
"@tauri-apps/plugin-http": "2.0.0-beta.0",
"@tauri-apps/plugin-nfc": "2.0.0-beta.0",
"@tauri-apps/plugin-notification": "2.0.0-beta.0",
"@tauri-apps/plugin-os": "2.0.0-beta.0",
"@tauri-apps/plugin-process": "2.0.0-beta.0",
"@tauri-apps/plugin-shell": "2.0.0-beta.0",
"@tauri-apps/plugin-updater": "2.0.0-beta.0",
"@zerodevx/svelte-json-view": "1.0.7"
},
"devDependencies": {
"@iconify-json/codicon": "^1.1.37",
"@iconify-json/ph": "^1.1.8",
"@sveltejs/vite-plugin-svelte": "^3.0.1",
"@tauri-apps/cli": "2.0.0-alpha.18",
"@tauri-apps/cli": "2.0.0-beta.0",
"@unocss/extractor-svelte": "^0.58.0",
"internal-ip": "^8.0.0",
"svelte": "^4.2.8",
"unocss": "^0.58.0",
"vite": "^5.0.6"
"vite": "^5.0.12"
}
}
+54
View File
@@ -1,5 +1,59 @@
# Changelog
## \[2.0.0-beta.0]
### Dependencies
- Upgraded to `barcode-scanner@2.0.0-beta.0`
- Upgraded to `biometric@2.0.0-beta.0`
- Upgraded to `cli@2.0.0-beta.0`
- Upgraded to `clipboard-manager@2.0.0-beta.0`
- Upgraded to `dialog@2.0.0-beta.0`
- Upgraded to `fs@2.0.0-beta.0`
- Upgraded to `global-shortcut@2.0.0-beta.0`
- Upgraded to `http@2.0.0-beta.0`
- Upgraded to `log@2.0.0-beta.0`
- Upgraded to `nfc@2.0.0-beta.0`
- Upgraded to `notification@2.0.0-beta.0`
- Upgraded to `os@2.0.0-beta.0`
- Upgraded to `process@2.0.0-beta.0`
- Upgraded to `shell@2.0.0-beta.0`
- Upgraded to `updater@2.0.0-beta.0`
## \[2.0.0-alpha.11]
### Dependencies
- Upgraded to `fs@2.0.0-alpha.7`
- Upgraded to `dialog@2.0.0-alpha.7`
- Upgraded to `http@2.0.0-alpha.9`
## \[2.0.0-alpha.10]
### Dependencies
- Upgraded to `http@2.0.0-alpha.8`
## \[2.0.0-alpha.9]
### Dependencies
- Upgraded to `barcode-scanner@2.0.0-alpha.4`
- Upgraded to `cli@2.0.0-alpha.6`
- Upgraded to `clipboard-manager@2.0.0-alpha.6`
- Upgraded to `dialog@2.0.0-alpha.6`
- Upgraded to `fs@2.0.0-alpha.6`
- Upgraded to `global-shortcut@2.0.0-alpha.6`
- Upgraded to `http@2.0.0-alpha.7`
- Upgraded to `log-plugin@2.0.0-alpha.6`
- Upgraded to `notification@2.0.0-alpha.7`
- Upgraded to `os@2.0.0-alpha.6`
- Upgraded to `process@2.0.0-alpha.6`
- Upgraded to `shell@2.0.0-alpha.6`
- Upgraded to `updater@2.0.0-alpha.6`
- Upgraded to `biometric@2.0.0-alpha.0`
- Upgraded to `nfc@2.0.0-alpha.0`
## \[2.0.0-alpha.8]
### Dependencies
+16 -14
View File
@@ -1,7 +1,7 @@
[package]
name = "api"
publish = false
version = "2.0.0-alpha.8"
version = "2.0.0-beta.0"
description = "An example Tauri Application showcasing the api"
edition = "2021"
rust-version = { workspace = true }
@@ -19,15 +19,15 @@ serde_json = { workspace = true }
serde = { workspace = true }
tiny_http = "0.11"
log = { workspace = true }
tauri-plugin-log = { path = "../../../plugins/log", version = "2.0.0-alpha.5" }
tauri-plugin-fs = { path = "../../../plugins/fs", version = "2.0.0-alpha.5" }
tauri-plugin-clipboard-manager = { path = "../../../plugins/clipboard-manager", version = "2.0.0-alpha.5" }
tauri-plugin-dialog = { path = "../../../plugins/dialog", version = "2.0.0-alpha.5" }
tauri-plugin-http = { path = "../../../plugins/http", features = [ "multipart" ], version = "2.0.0-alpha.6" }
tauri-plugin-notification = { path = "../../../plugins/notification", version = "2.0.0-alpha.6", features = [ "windows7-compat" ] }
tauri-plugin-os = { path = "../../../plugins/os", version = "2.0.0-alpha.5" }
tauri-plugin-process = { path = "../../../plugins/process", version = "2.0.0-alpha.5" }
tauri-plugin-shell = { path = "../../../plugins/shell", version = "2.0.0-alpha.5" }
tauri-plugin-log = { path = "../../../plugins/log", version = "2.0.0-beta.0" }
tauri-plugin-fs = { path = "../../../plugins/fs", version = "2.0.0-beta.0", features = [ "watch" ] }
tauri-plugin-clipboard-manager = { path = "../../../plugins/clipboard-manager", version = "2.0.0-beta.0" }
tauri-plugin-dialog = { path = "../../../plugins/dialog", version = "2.0.0-beta.0" }
tauri-plugin-http = { path = "../../../plugins/http", features = [ "multipart" ], version = "2.0.0-beta.0" }
tauri-plugin-notification = { path = "../../../plugins/notification", version = "2.0.0-beta.0", features = [ "windows7-compat" ] }
tauri-plugin-os = { path = "../../../plugins/os", version = "2.0.0-beta.0" }
tauri-plugin-process = { path = "../../../plugins/process", version = "2.0.0-beta.0" }
tauri-plugin-shell = { path = "../../../plugins/shell", version = "2.0.0-beta.0" }
[dependencies.tauri]
workspace = true
@@ -41,12 +41,14 @@ tauri-plugin-shell = { path = "../../../plugins/shell", version = "2.0.0-alpha.5
]
[target."cfg(any(target_os = \"macos\", windows, target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\"))".dependencies]
tauri-plugin-cli = { path = "../../../plugins/cli", version = "2.0.0-alpha.5" }
tauri-plugin-global-shortcut = { path = "../../../plugins/global-shortcut", version = "2.0.0-alpha.5" }
tauri-plugin-updater = { path = "../../../plugins/updater", version = "2.0.0-alpha.5" }
tauri-plugin-cli = { path = "../../../plugins/cli", version = "2.0.0-beta.0" }
tauri-plugin-global-shortcut = { path = "../../../plugins/global-shortcut", version = "2.0.0-beta.0" }
tauri-plugin-updater = { path = "../../../plugins/updater", version = "2.0.0-beta.0" }
[target."cfg(any(target_os = \"android\", target_os = \"ios\"))".dependencies]
tauri-plugin-barcode-scanner = { path = "../../../plugins/barcode-scanner/", version = "2.0.0-alpha.3" }
tauri-plugin-barcode-scanner = { path = "../../../plugins/barcode-scanner/", version = "2.0.0-beta.0" }
tauri-plugin-nfc = { path = "../../../plugins/nfc", version = "2.0.0-beta.0" }
tauri-plugin-biometric = { path = "../../../plugins/biometric/", version = "2.0.0-beta.0" }
[target."cfg(target_os = \"windows\")".dependencies]
window-shadows = "0.2"
+3 -2
View File
@@ -7,6 +7,7 @@ fn main() {
if !cfg!(feature = "custom-protocol") {
codegen = codegen.dev();
}
codegen.build();
tauri_build::build();
tauri_build::try_build(tauri_build::Attributes::new().codegen(codegen))
.expect("failed to run tauri_build::try_build");
}
@@ -0,0 +1,78 @@
{
"$schema": "schemas/desktop-schema.json",
"identifier": "run-app-base",
"description": "Base permissions to run the app",
"windows": ["main"],
"permissions": [
"log:default",
{
"identifier": "http:default",
"allow": [
{
"url": "http://localhost:3003"
}
]
},
"app:default",
"resources:default",
"fs:default",
"menu:default",
"path:default",
"tray:default",
"event:default",
"window:default",
"notification:default",
"os:allow-platform",
"dialog:allow-open",
"dialog:allow-save",
"dialog:allow-confirm",
{
"identifier": "shell:allow-execute",
"allow": [
{
"name": "sh",
"cmd": "sh",
"args": [
"-c",
{
"validator": "\\S+"
}
]
},
{
"name": "cmd",
"cmd": "cmd",
"args": [
"/C",
{
"validator": "\\S+"
}
]
}
]
},
"shell:allow-kill",
"shell:allow-stdin-write",
"clipboard-manager:allow-read",
"clipboard-manager:allow-write",
"fs:allow-rename",
"fs:allow-mkdir",
"fs:allow-remove",
"fs:allow-write-text-file",
"fs:scope-download-recursive",
"fs:scope-resource-recursive",
{
"identifier": "fs:scope-appdata-recursive",
"allow": [
{
"path": "$APPDATA/db/**"
}
],
"deny": [
{
"path": "$APPDATA/db/*.stronghold"
}
]
}
]
}
@@ -0,0 +1,14 @@
{
"$schema": "./schemas/desktop-schema.json",
"identifier": "run-app-desktop",
"description": "Permissions to run the app (desktop only)",
"windows": ["main"],
"platforms": ["linux", "macOS", "windows"],
"permissions": [
"cli:default",
"updater:default",
"global-shortcut:allow-unregister",
"global-shortcut:allow-register",
"global-shortcut:allow-unregister-all"
]
}
@@ -0,0 +1,16 @@
{
"$schema": "./schemas/mobile-schema.json",
"identifier": "run-app-mobile",
"description": "Permissions to run the app (mobile only)",
"windows": ["main"],
"platforms": ["android", "iOS"],
"permissions": [
"nfc:allow-write",
"nfc:allow-scan",
"biometric:allow-authenticate",
"barcode-scanner:allow-scan",
"barcode-scanner:allow-cancel",
"barcode-scanner:allow-request-permissions",
"barcode-scanner:allow-check-permissions"
]
}
@@ -0,0 +1,3 @@
*.json
!desktop-schema.json
!mobile-schema.json
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+3
View File
@@ -0,0 +1,3 @@
# Default ignored files
/shelf/
/workspace.xml
+6
View File
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<bytecodeTargetLevel target="17" />
</component>
</project>
+28
View File
@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GradleMigrationSettings" migrationVersion="1" />
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="testRunner" value="GRADLE" />
<option name="distributionType" value="DEFAULT_WRAPPED" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleJvm" value="jbr-17" />
<option name="modules">
<set>
<option value="$USER_HOME$/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tauri-2.0.0-beta.0/mobile/android" />
<option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/app" />
<option value="$PROJECT_DIR$/buildSrc" />
<option value="$PROJECT_DIR$/../../../../../plugins/barcode-scanner/android" />
<option value="$PROJECT_DIR$/../../../../../plugins/biometric/android" />
<option value="$PROJECT_DIR$/../../../../../plugins/clipboard-manager/android" />
<option value="$PROJECT_DIR$/../../../../../plugins/dialog/android" />
<option value="$PROJECT_DIR$/../../../../../plugins/nfc/android" />
<option value="$PROJECT_DIR$/../../../../../plugins/notification/android" />
</set>
</option>
</GradleProjectSettings>
</option>
</component>
</project>
@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RemoteRepositoriesConfiguration">
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Maven Central repository" />
<option name="url" value="https://repo1.maven.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="jboss.community" />
<option name="name" value="JBoss Community repository" />
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
</remote-repository>
<remote-repository>
<option name="id" value="MavenRepo" />
<option name="name" value="MavenRepo" />
<option name="url" value="https://repo.maven.apache.org/maven2/" />
</remote-repository>
<remote-repository>
<option name="id" value="Google" />
<option name="name" value="Google" />
<option name="url" value="https://dl.google.com/dl/android/maven2/" />
</remote-repository>
</component>
</project>
+6
View File
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="KotlinJpsPluginSettings">
<option name="version" value="1.8.10" />
</component>
</project>
+10
View File
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="jbr-17" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">
<option name="id" value="Android" />
</component>
</project>
+6
View File
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$/../../../../.." vcs="Git" />
</component>
</project>
@@ -17,6 +17,26 @@
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<!-- NFC PLUGIN. AUTO-GENERATED. DO NOT REMOVE. -->
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<intent-filter>
<action android:name="android.nfc.action.TECH_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<intent-filter>
<action android:name="android.nfc.action.TAG_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<meta-data
android:name="android.nfc.action.TECH_DISCOVERED"
android:resource="@xml/nfc_tech_filter" />
<!-- NFC PLUGIN. AUTO-GENERATED. DO NOT REMOVE. -->
</activity>
<provider
@@ -1,11 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1200"
version = "1.7">
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"
runPostActionsOnFailure = "NO">
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
@@ -16,7 +15,7 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "54DC6E273C78071F3BA12EF3"
BuildableName = "api_iOS.app"
BuildableName = "Tauri API.app"
BlueprintName = "api_iOS"
ReferencedContainer = "container:api.xcodeproj">
</BuildableReference>
@@ -27,21 +26,16 @@
buildConfiguration = "debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "NO"
onlyGenerateCoverageForSpecifiedTargets = "NO">
shouldUseLaunchSchemeArgsEnv = "NO">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "54DC6E273C78071F3BA12EF3"
BuildableName = "api_iOS.app"
BuildableName = "Tauri API.app"
BlueprintName = "api_iOS"
ReferencedContainer = "container:api.xcodeproj">
</BuildableReference>
</MacroExpansion>
<Testables>
</Testables>
<CommandLineArguments>
</CommandLineArguments>
<EnvironmentVariables>
<EnvironmentVariable
key = "RUST_BACKTRACE"
@@ -54,6 +48,8 @@
isEnabled = "YES">
</EnvironmentVariable>
</EnvironmentVariables>
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "debug"
@@ -70,13 +66,11 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "54DC6E273C78071F3BA12EF3"
BuildableName = "api_iOS.app"
BuildableName = "Tauri API.app"
BlueprintName = "api_iOS"
ReferencedContainer = "container:api.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<CommandLineArguments>
</CommandLineArguments>
<EnvironmentVariables>
<EnvironmentVariable
key = "RUST_BACKTRACE"
@@ -101,13 +95,11 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "54DC6E273C78071F3BA12EF3"
BuildableName = "api_iOS.app"
BuildableName = "Tauri API.app"
BlueprintName = "api_iOS"
ReferencedContainer = "container:api.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<CommandLineArguments>
</CommandLineArguments>
<EnvironmentVariables>
<EnvironmentVariable
key = "RUST_BACKTRACE"
@@ -2,6 +2,8 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NFCReaderUsageDescription</key>
<string>NFC Test</string>
<key>NSCameraUsageDescription</key>
<string>Request camera access for barcode scanner</string>
<key>CFBundleDevelopmentRegion</key>
@@ -42,5 +44,7 @@
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>NSFaceIDUsageDescription</key>
<string>Biometric Test</string>
</dict>
</plist>
@@ -1,5 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict/>
<dict>
<key>com.apple.developer.nfc.readersession.formats</key>
<array>
<string>TAG</string>
<string>NDEF</string>
</array>
</dict>
</plist>
+14 -12
View File
@@ -7,7 +7,7 @@ mod cmd;
mod tray;
use serde::Serialize;
use tauri::{window::WindowBuilder, App, AppHandle, Manager, RunEvent, WindowUrl};
use tauri::{webview::WebviewWindowBuilder, App, AppHandle, Manager, RunEvent, WebviewUrl};
#[derive(Clone, Serialize)]
struct Reply {
@@ -47,12 +47,15 @@ pub fn run() {
#[cfg(mobile)]
{
app.handle().plugin(tauri_plugin_barcode_scanner::init())?;
app.handle().plugin(tauri_plugin_nfc::init())?;
app.handle().plugin(tauri_plugin_biometric::init())?;
}
let mut window_builder = WindowBuilder::new(app, "main", WindowUrl::default());
let mut webview_window_builder =
WebviewWindowBuilder::new(app, "main", WebviewUrl::default());
#[cfg(desktop)]
{
window_builder = window_builder
webview_window_builder = webview_window_builder
.user_agent(&format!("Tauri API - {}", std::env::consts::OS))
.title("Tauri API Validation")
.inner_size(1000., 800.)
@@ -62,7 +65,7 @@ pub fn run() {
#[cfg(target_os = "windows")]
{
window_builder = window_builder
webview_window_builder = webview_window_builder
.transparent(true)
.shadow(true)
.decorations(false);
@@ -70,15 +73,14 @@ pub fn run() {
#[cfg(target_os = "macos")]
{
window_builder = window_builder.transparent(true);
webview_window_builder = webview_window_builder.transparent(true);
}
let window = window_builder.build().unwrap();
let webview = webview_window_builder.build().unwrap();
#[cfg(debug_assertions)]
window.open_devtools();
webview.open_devtools();
#[cfg(desktop)]
std::thread::spawn(|| {
let server = match tiny_http::Server::http("localhost:3003") {
Ok(s) => s,
@@ -105,15 +107,15 @@ pub fn run() {
Ok(())
})
.on_page_load(|window, _| {
let window_ = window.clone();
window.listen("js-event", move |event| {
.on_page_load(|webview, _| {
let webview_ = webview.clone();
webview.listen("js-event", move |event| {
println!("got js-event with message '{:?}'", event.payload());
let reply = Reply {
data: "something else".to_string(),
};
window_
webview_
.emit("rust-event", Some(reply))
.expect("failed to emit");
});
+13 -12
View File
@@ -6,19 +6,20 @@ use std::sync::atomic::{AtomicBool, Ordering};
use tauri::{
menu::{Menu, MenuItem},
tray::{ClickType, TrayIconBuilder},
Manager, Runtime, WindowBuilder, WindowUrl,
Manager, Runtime, WebviewUrl, WebviewWindowBuilder,
};
pub fn create_tray<R: Runtime>(app: &tauri::AppHandle<R>) -> tauri::Result<()> {
let toggle_i = MenuItem::with_id(app, "toggle", "Toggle", true, None);
let new_window_i = MenuItem::with_id(app, "new-window", "New window", true, None);
let icon_i_1 = MenuItem::with_id(app, "icon-1", "Icon 1", true, None);
let icon_i_2 = MenuItem::with_id(app, "icon-2", "Icon 2", true, None);
let toggle_i = MenuItem::with_id(app, "toggle", "Toggle", true, None::<&str>)?;
let new_window_i = MenuItem::with_id(app, "new-window", "New window", true, None::<&str>)?;
let icon_i_1 = MenuItem::with_id(app, "icon-1", "Icon 1", true, None::<&str>)?;
let icon_i_2 = MenuItem::with_id(app, "icon-2", "Icon 2", true, None::<&str>)?;
#[cfg(target_os = "macos")]
let set_title_i = MenuItem::with_id(app, "set-title", "Set Title", true, None);
let switch_i = MenuItem::with_id(app, "switch-menu", "Switch Menu", true, None);
let quit_i = MenuItem::with_id(app, "quit", "Quit", true, None);
let remove_tray_i = MenuItem::with_id(app, "remove-tray", "Remove Tray icon", true, None);
let set_title_i = MenuItem::with_id(app, "set-title", "Set Title", true, None::<&str>)?;
let switch_i = MenuItem::with_id(app, "switch-menu", "Switch Menu", true, None::<&str>)?;
let quit_i = MenuItem::with_id(app, "quit", "Quit", true, None::<&str>)?;
let remove_tray_i =
MenuItem::with_id(app, "remove-tray", "Remove Tray icon", true, None::<&str>)?;
let menu1 = Menu::with_items(
app,
&[
@@ -53,7 +54,7 @@ pub fn create_tray<R: Runtime>(app: &tauri::AppHandle<R>) -> tauri::Result<()> {
app.remove_tray_by_id("tray-1");
}
"toggle" => {
if let Some(window) = app.get_window("main") {
if let Some(window) = app.get_webview_window("main") {
let new_title = if window.is_visible().unwrap_or_default() {
let _ = window.hide();
"Show"
@@ -66,7 +67,7 @@ pub fn create_tray<R: Runtime>(app: &tauri::AppHandle<R>) -> tauri::Result<()> {
}
}
"new-window" => {
let _ = WindowBuilder::new(app, "new", WindowUrl::App("index.html".into()))
let _ = WebviewWindowBuilder::new(app, "new", WebviewUrl::App("index.html".into()))
.title("Tauri")
.build();
}
@@ -104,7 +105,7 @@ pub fn create_tray<R: Runtime>(app: &tauri::AppHandle<R>) -> tauri::Result<()> {
.on_tray_icon_event(|tray, event| {
if event.click_type == ClickType::Left {
let app = tray.app_handle();
if let Some(window) = app.get_window("main") {
if let Some(window) = app.get_webview_window("main") {
let _ = window.show();
let _ = window.set_focus();
}
+49 -86
View File
@@ -1,15 +1,40 @@
{
"$schema": "../node_modules/@tauri-apps/cli/schema.json",
"productName": "Tauri API",
"version": "2.0.0",
"identifier": "com.tauri.api",
"build": {
"distDir": "../dist",
"devPath": "http://localhost:5173",
"devUrl": "http://localhost:5173",
"frontendDist": "../dist",
"beforeDevCommand": "yarn dev",
"beforeBuildCommand": "yarn build",
"withGlobalTauri": true
"beforeBuildCommand": "yarn build"
},
"package": {
"productName": "Tauri API",
"version": "2.0.0"
"app": {
"withGlobalTauri": true,
"macOSPrivateApi": true,
"security": {
"pattern": {
"use": "isolation",
"options": {
"dir": "../isolation-dist/"
}
},
"csp": {
"default-src": "'self' customprotocol: asset:",
"connect-src": "ipc: http://ipc.localhost",
"font-src": ["https://fonts.gstatic.com"],
"img-src": "'self' asset: http://asset.localhost blob: data:",
"style-src": "'unsafe-inline' 'self' http://fonts.googleapis.com"
},
"freezePrototype": true,
"assetProtocol": {
"enable": true,
"scope": {
"allow": ["$APPDATA/db/**", "$RESOURCE/**"],
"deny": ["$APPDATA/db/*.stronghold"]
}
}
}
},
"plugins": {
"cli": {
@@ -47,95 +72,33 @@
}
}
},
"fs": {
"scope": {
"allow": ["$APPDATA/db/**", "$DOWNLOAD/**", "$RESOURCE/**"],
"deny": ["$APPDATA/db/*.stronghold"]
}
},
"shell": {
"open": true,
"scope": [
{
"name": "sh",
"cmd": "sh",
"args": [
"-c",
{
"validator": "\\S+"
}
]
},
{
"name": "cmd",
"cmd": "cmd",
"args": [
"/C",
{
"validator": "\\S+"
}
]
}
]
},
"http": {
"scope": ["http://localhost:3003"]
"open": true
},
"updater": {
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDE5QzMxNjYwNTM5OEUwNTgKUldSWTRKaFRZQmJER1h4d1ZMYVA3dnluSjdpN2RmMldJR09hUFFlZDY0SlFqckkvRUJhZDJVZXAK",
"endpoints": [
"https://tauri-update-server.vercel.app/update/{{target}}/{{current_version}}"
]
}
},
"tauri": {
"pattern": {
"use": "isolation",
"options": {
"dir": "../isolation-dist/"
}
},
"macOSPrivateApi": true,
"bundle": {
"active": true,
"identifier": "com.tauri.api",
"icon": [
"icons/32x32.png",
"icons/128x128.png",
"icons/128x128@2x.png",
"icons/icon.icns",
"icons/icon.ico"
],
"windows": {
"wix": {
"language": {
"en-US": {},
"pt-BR": {
"localePath": "locales/pt-BR.wxl"
}
"bundle": {
"active": true,
"icon": [
"icons/32x32.png",
"icons/128x128.png",
"icons/128x128@2x.png",
"icons/icon.icns",
"icons/icon.ico"
],
"windows": {
"wix": {
"language": {
"en-US": {},
"pt-BR": {
"localePath": "locales/pt-BR.wxl"
}
}
},
"updater": {
"active": true,
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDE5QzMxNjYwNTM5OEUwNTgKUldSWTRKaFRZQmJER1h4d1ZMYVA3dnluSjdpN2RmMldJR09hUFFlZDY0SlFqckkvRUJhZDJVZXAK"
}
},
"windows": [],
"security": {
"csp": {
"default-src": "'self' customprotocol: asset:",
"connect-src": "ipc: http://ipc.localhost",
"font-src": ["https://fonts.gstatic.com"],
"img-src": "'self' asset: http://asset.localhost blob: data:",
"style-src": "'unsafe-inline' 'self' http://fonts.googleapis.com"
},
"freezePrototype": true,
"assetProtocol": {
"enable": true,
"scope": {
"allow": ["$APPDATA/db/**", "$RESOURCE/**"],
"deny": ["$APPDATA/db/*.stronghold"]
}
}
}
}
+24 -7
View File
@@ -17,9 +17,11 @@
import Clipboard from "./views/Clipboard.svelte";
import WebRTC from "./views/WebRTC.svelte";
import Scanner from "./views/Scanner.svelte";
import Biometric from "./views/Biometric.svelte";
import { onMount } from "svelte";
import { onMount, tick } from "svelte";
import { ask } from "@tauri-apps/plugin-dialog";
import Nfc from "./views/Nfc.svelte";
const appWindow = getCurrent();
@@ -107,6 +109,16 @@
component: Scanner,
icon: "i-ph-scan",
},
isMobile && {
label: "NFC",
component: Nfc,
icon: "i-ph-nfc",
},
isMobile && {
label: "Biometric",
component: Biometric,
icon: "i-ph-scan",
},
];
let selected = views[0];
@@ -166,30 +178,35 @@
// Console
let messages = writable([]);
function onMessage(value) {
let consoleTextEl;
async function onMessage(value) {
messages.update((r) => [
...r,
{
html:
`<pre><strong class="text-accent dark:text-darkAccent">[${new Date().toLocaleTimeString()}]:</strong> ` +
(typeof value === "string" ? value : JSON.stringify(value, null, 1)) +
"</pre>",
},
...r,
]);
await tick();
if (consoleTextEl) consoleTextEl.scrollTop = consoleTextEl.scrollHeight;
}
// this function is renders HTML without sanitizing it so it's insecure
// we only use it with our own input data
function insecureRenderHtml(html) {
async function insecureRenderHtml(html) {
messages.update((r) => [
...r,
{
html:
`<pre><strong class="text-accent dark:text-darkAccent">[${new Date().toLocaleTimeString()}]:</strong> ` +
html +
"</pre>",
},
...r,
]);
await tick();
if (consoleTextEl) consoleTextEl.scrollTop = consoleTextEl.scrollHeight;
}
function clear() {
@@ -221,7 +238,7 @@
let isWindows;
onMount(async () => {
isWindows = (await os.platform()) === "win32";
isWindows = (await os.platform()) === "windows";
});
// mobile
@@ -474,7 +491,7 @@
<div class="i-codicon-clear-all" />
</div>
</div>
<div class="px-2 overflow-y-auto all:font-mono code-block all:text-xs">
<div bind:this={consoleTextEl} class="px-2 overflow-y-auto all:font-mono code-block all:text-xs select-text mr-2">
{#each $messages as r}
{@html r.html}
{/each}
+30
View File
@@ -0,0 +1,30 @@
<script>
import { authenticate } from "@tauri-apps/plugin-biometric";
export let onMessage;
let allowDeviceCredential = true;
function auth() {
authenticate("Tauri API wants to show it is awesome :)", {
allowDeviceCredential,
cancelTitle: "Cancel request",
fallbackTitle: "Trying the fallback option",
title: "Tauri API Auth",
subtitle: "Please authenticate :)",
confirmationRequired: false,
maxAttemps: 1,
})
.then(onMessage)
.catch(onMessage);
}
</script>
<div>
<input
type="checkbox"
id="dllowDeviceCredential"
bind:checked={allowDeviceCredential}
/>
<label for="allowDeviceCredentiale">Allow device credential</label>
</div>
<button class="btn" id="cli-matches" on:click={auth}> Authenticate </button>
+1 -1
View File
@@ -1,6 +1,6 @@
<script>
import { getCurrent } from "@tauri-apps/api/window";
import { invoke } from "@tauri-apps/api/primitives";
import { invoke } from "@tauri-apps/api/core";
import { onMount, onDestroy } from "svelte";
const appWindow = getCurrent();
+19 -3
View File
@@ -1,6 +1,6 @@
<script>
import { open, save, confirm } from "@tauri-apps/plugin-dialog";
import { readBinaryFile } from "@tauri-apps/plugin-fs";
import { open, save, confirm, message } from "@tauri-apps/plugin-dialog";
import { readFile } from "@tauri-apps/plugin-fs";
export let onMessage;
export let insecureRenderHtml;
@@ -22,6 +22,16 @@
}
async function prompt() {
confirm("Do you want to do something?")
.then((res) =>
onMessage(
res ? "Yes" : "No"
)
)
.catch(onMessage);
}
async function promptCustom() {
confirm("Is Tauri awesome?", {
okLabel: "Absolutely",
cancelLabel: "Totally",
@@ -34,6 +44,10 @@
.catch(onMessage);
}
async function msg() {
await message("Tauri is awesome!");
}
function openDialog() {
open({
title: "My wonderful open dialog",
@@ -55,7 +69,7 @@
} else {
var pathToRead = typeof res === "string" ? res : res.path;
var isFile = pathToRead.match(/\S+\.\S+$/g);
readBinaryFile(pathToRead)
readFile(pathToRead)
.then(function (response) {
if (isFile) {
if (
@@ -130,3 +144,5 @@
>Open save dialog</button
>
<button class="btn" id="prompt-dialog" on:click={prompt}>Prompt</button>
<button class="btn" id="custom-prompt-dialog" on:click={promptCustom}>Prompt (custom)</button>
<button class="btn" id="message-dialog" on:click={msg}>Message</button>
+191 -48
View File
@@ -1,17 +1,19 @@
<script>
import {
readBinaryFile,
writeTextFile,
readDir,
Dir,
} from "@tauri-apps/plugin-fs";
import { convertFileSrc } from "@tauri-apps/api/primitives";
import * as fs from "@tauri-apps/plugin-fs";
import { convertFileSrc } from "@tauri-apps/api/core";
export let onMessage;
export let insecureRenderHtml;
let pathToRead = "";
let path = "";
let img;
let file;
let renameTo;
let watchPath = "";
let watchDebounceDelay = 0;
let watchRecursive = false;
let unwatchFn;
let unwatchPath = "";
function getDir() {
const dirSelect = document.getElementById("dir");
@@ -30,56 +32,158 @@
reader.readAsDataURL(blob);
}
const DirOptions = Object.keys(Dir)
const DirOptions = Object.keys(fs.BaseDirectory)
.filter((key) => isNaN(parseInt(key)))
.map((dir) => [dir, Dir[dir]]);
.map((dir) => [dir, fs.BaseDirectory[dir]]);
function open() {
fs.open(path, {
baseDir: getDir(),
read: true,
write: true,
create: true,
})
.then((f) => {
file = f;
onMessage(`Opened ${path}`);
})
.catch(onMessage);
}
function mkdir() {
fs.mkdir(path, { baseDir: getDir() })
.then(() => {
onMessage(`Created dir ${path}`);
})
.catch(onMessage);
}
function remove() {
fs.remove(path, { baseDir: getDir() })
.then(() => {
onMessage(`Removed ${path}`);
})
.catch(onMessage);
}
function rename() {
fs.rename(path, renameTo, {
oldPathBaseDir: getDir(),
newPathBaseDir: getDir(),
})
.then(() => {
onMessage(`Renamed ${path} to ${renameTo}`);
})
.catch(onMessage);
}
function truncate() {
file
.truncate(0)
.then(() => {
onMessage(`Truncated file`);
})
.catch(onMessage);
}
function stat() {
file
.stat()
.then((stat) => {
onMessage(`File stat ${JSON.stringify(stat)}`);
})
.catch(onMessage);
}
function read() {
const isFile = pathToRead.match(/\S+\.\S+$/g);
const opts = {
dir: getDir(),
baseDir: getDir(),
};
const promise = isFile
? readBinaryFile(pathToRead, opts)
: readDir(pathToRead, opts);
promise
.then(function (response) {
if (isFile) {
if (pathToRead.includes(".png") || pathToRead.includes(".jpg")) {
arrayBufferToBase64(new Uint8Array(response), function (base64) {
const src = "data:image/png;base64," + base64;
insecureRenderHtml('<img src="' + src + '"></img>');
});
} else {
const value = String.fromCharCode.apply(null, response);
insecureRenderHtml(
'<textarea id="file-response"></textarea><button id="file-save">Save</button>'
);
setTimeout(() => {
const fileInput = document.getElementById("file-response");
fileInput.value = value;
document
.getElementById("file-save")
.addEventListener("click", function () {
writeTextFile(pathToRead, fileInput.value, {
dir: getDir(),
}).catch(onMessage);
fs.stat(path, opts)
.then((stat) => {
const isFile = stat.isFile;
const promise = isFile
? fs.readFile(path, opts)
: fs.readDir(path, opts);
promise
.then(function (response) {
if (isFile) {
if (path.includes(".png") || path.includes(".jpg")) {
arrayBufferToBase64(
new Uint8Array(response),
function (base64) {
const src = "data:image/png;base64," + base64;
insecureRenderHtml('<img src="' + src + '"></img>');
}
);
} else {
const value = String.fromCharCode.apply(null, response);
insecureRenderHtml(
'<textarea id="file-response"></textarea><button id="file-save">Save</button>'
);
setTimeout(() => {
const fileInput = document.getElementById("file-response");
fileInput.value = value;
document
.getElementById("file-save")
.addEventListener("click", function () {
fs.writeTextFile(path, fileInput.value, {
dir: getDir(),
}).catch(onMessage);
});
});
});
}
} else {
onMessage(response);
}
}
} else {
onMessage(response);
}
})
.catch(onMessage);
})
.catch(onMessage);
}
function setSrc() {
img.src = convertFileSrc(pathToRead);
img.src = convertFileSrc(path);
}
function watch() {
unwatch();
if (watchPath) {
onMessage(`Watching ${watchPath} for changes`);
let options = {
recursive: watchRecursive,
delayMs: parseInt(watchDebounceDelay),
};
if (options.delayMs === 0) {
fs.watchImmediate(watchPath, onMessage, options)
.then((fn) => {
unwatchFn = fn;
unwatchPath = watchPath;
})
.catch(onMessage);
} else {
fs.watch(watchPath, onMessage, options)
.then((fn) => {
unwatchFn = fn;
unwatchPath = watchPath;
})
.catch(onMessage);
}
}
}
function unwatch() {
if (unwatchFn) {
onMessage(`Stopped watching ${unwatchPath} for changes`);
unwatchFn();
}
unwatchFn = undefined;
unwatchPath = undefined;
}
</script>
<form class="flex flex-col" on:submit|preventDefault={read}>
<div class="flex flex-col">
<div class="flex gap-1">
<select class="input" id="dir">
<option value="">None</option>
@@ -89,17 +193,56 @@
</select>
<input
class="input grow"
id="path-to-read"
placeholder="Type the path to read..."
bind:value={pathToRead}
bind:value={path}
/>
</div>
<br />
<div>
<button class="btn" id="read">Read</button>
<button class="btn" on:click={open}>Open</button>
<button class="btn" on:click={read}>Read</button>
<button class="btn" on:click={mkdir}>Mkdir</button>
<button class="btn" on:click={remove}>Remove</button>
<div class="flex flex-row">
<button class="btn" on:click={rename}>Rename</button>
<input class="input" bind:value={renameTo} placeholder="To" />
</div>
<button class="btn" type="button" on:click={setSrc}>Use as img src</button>
</div>
</form>
{#if file}
<div>
<button class="btn" on:click={truncate}>Truncate</button>
<button class="btn" on:click={stat}>Stat</button>
</div>
{/if}
<h3>Watch</h3>
<input
class="input grow"
placeholder="Type the path to watch..."
bind:value={watchPath}
/>
<br />
<div>
<label for="watch-debounce-delay">Debounce delay in milliseconds (`0` disables the debouncer)</label>
<input
class="input"
id="watch-debounce-delay"
bind:value={watchDebounceDelay}
/>
</div>
<br />
<div>
<input type="checkbox" id="watch-recursive" bind:checked={watchRecursive} />
<label for="watch-recursive">Recursive</label>
</div>
<br />
<div>
<button class="btn" on:click={watch}>Watch</button>
<button class="btn" on:click={unwatch}>Unwatch</button>
</div>
</div>
<br />
+126
View File
@@ -0,0 +1,126 @@
<script>
import { onMount } from "svelte";
import { write, scan, textRecord, uriRecord } from "@tauri-apps/plugin-nfc";
import * as os from "@tauri-apps/plugin-os";
export let onMessage;
const decoder = new TextDecoder();
let kind = "tag";
let writeToNfc = false;
let text = "";
let uri = "";
let scheme = "";
let host = "";
let pathPrefix = "";
let mimeType = "";
let isAndroid;
onMount(async () => {
isAndroid = (await os.platform()) === "android";
});
async function _readNfc() {
onMessage(`NFC scanning ${kind}`);
const tagResponse = await scan(
{
type: kind,
uri: {
scheme: scheme || null,
host: host || null,
pathPrefix: pathPrefix || null,
},
mimeType: mimeType || null,
},
{
keepSessionAlive: writeToNfc,
message: "Hold your iPhone near an NFC tag",
successMessage: "Tag successfully read",
}
);
onMessage({
id: decoder.decode(new Uint8Array(tagResponse.id)),
kind: tagResponse.kind,
records: tagResponse.records.map((record) => ({
id: decoder.decode(new Uint8Array(record.id)),
kind: decoder.decode(new Uint8Array(record.kind)),
payload: decoder.decode(new Uint8Array(record.payload)),
tnf: record.tnf,
})),
});
if (writeToNfc) {
const records = [];
if (text) {
records.push(textRecord(text, "tauriTextId"));
}
if (uri) {
records.push(uriRecord(uri, "tauriUriId"));
}
await write(records, {
successMessage: "Data written to tag",
});
onMessage("Wrote to tag");
}
}
function readNfc() {
_readNfc().catch(onMessage);
}
</script>
<div>
<div class="flex gap-2 children:grow items-center">
<div>
<input type="checkbox" id="nfc-write" bind:checked={writeToNfc} />
<label for="nfc-write">Write data</label>
</div>
<select class="input" id="request-method" bind:value={kind}>
<option value="tag">TAG</option>
<option value="ndef">NDEF</option>
</select>
</div>
{#if isAndroid}
<div class="flex flex-col gap-2 children:grow">
<p>Filters</p>
<div class="flex gap-2">
<input
class="input"
placeholder="Scheme"
style="width: 33%"
bind:value={scheme}
/>
<input
class="input"
placeholder="Host"
style="width: 33%"
bind:value={host}
/>
<input
class="input"
placeholder="Path prefix"
style="width: 33%"
bind:value={pathPrefix}
/>
</div>
<div class="flex gap-2">
<input class="input" placeholder="Mime type" bind:value={mimeType} />
</div>
</div>
{/if}
<div class="flex flex-col gap-2 children:grow">
<p>Write Records</p>
<div class="flex">
<input class="input" placeholder="Text record" bind:value={text} />
<input class="input" placeholder="URI record" bind:value={uri} />
</div>
</div>
<button class="btn" on:click={readNfc}>Scan</button>
</div>
+2 -2
View File
@@ -11,8 +11,8 @@ import process from "process";
// https://vitejs.dev/config/
export default defineConfig(async () => {
const host =
process.env.TAURI_PLATFORM === "android" ||
process.env.TAURI_PLATFORM === "ios"
process.env.TAURI_ENV_PLATFORM === "android" ||
process.env.TAURI_ENV_PLATFORM === "ios"
? await internalIpV4()
: "localhost";
return {
+11 -11
View File
@@ -12,20 +12,20 @@
"devDependencies": {
"@rollup/plugin-node-resolve": "15.2.3",
"@rollup/plugin-terser": "0.4.4",
"@rollup/plugin-typescript": "11.1.5",
"@typescript-eslint/eslint-plugin": "6.13.2",
"@typescript-eslint/parser": "6.13.2",
"@rollup/plugin-typescript": "11.1.6",
"@typescript-eslint/eslint-plugin": "6.20.0",
"@typescript-eslint/parser": "6.20.0",
"covector": "^0.10.2",
"eslint": "8.55.0",
"eslint": "8.56.0",
"eslint-config-prettier": "9.1.0",
"eslint-config-standard-with-typescript": "40.0.0",
"eslint-plugin-import": "2.29.0",
"eslint-plugin-n": "16.3.1",
"eslint-config-standard-with-typescript": "43.0.1",
"eslint-plugin-import": "2.29.1",
"eslint-plugin-n": "16.6.2",
"eslint-plugin-promise": "6.1.1",
"eslint-plugin-security": "1.7.1",
"prettier": "3.1.0",
"rollup": "4.6.1",
"typescript": "5.3.2"
"eslint-plugin-security": "2.1.0",
"prettier": "3.2.2",
"rollup": "4.9.6",
"typescript": "5.3.3"
},
"resolutions": {
"semver": ">=7.5.2",
+9
View File
@@ -1,5 +1,13 @@
# Changelog
## \[2.0.0-beta.0]
- [`d198c01`](https://github.com/tauri-apps/plugins-workspace/commit/d198c014863ee260cb0de88a14b7fc4356ef7474)([#862](https://github.com/tauri-apps/plugins-workspace/pull/862)) Update to tauri beta.
## \[2.0.0-alpha.5]
- [`387c2f9`](https://github.com/tauri-apps/plugins-workspace/commit/387c2f9e0ce4c75c07ffa3fd76391a25b58f5daf)([#802](https://github.com/tauri-apps/plugins-workspace/pull/802)) Update to @tauri-apps/api v2.0.0-alpha.13.
## \[2.0.0-alpha.4]
- [`387c2f9`](https://github.com/tauri-apps/plugins-workspace/commit/387c2f9e0ce4c75c07ffa3fd76391a25b58f5daf)([#802](https://github.com/tauri-apps/plugins-workspace/pull/802)) Update to @tauri-apps/api v2.0.0-alpha.12.
@@ -28,3 +36,4 @@
## \[2.0.0-alpha.0]
- [`717ae67`](https://github.com/tauri-apps/plugins-workspace/commit/717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
ae67`](https://github.com/tauri-apps/plugins-workspace/commit/717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
+9 -3
View File
@@ -1,16 +1,20 @@
[package]
name = "tauri-plugin-authenticator"
version = "2.0.0-alpha.5"
version = "2.0.0-beta.0"
description = "Use hardware security-keys in your Tauri App."
authors = { workspace = true }
license = { workspace = true }
edition = { workspace = true }
rust-version = { workspace = true }
links = "tauri-plugin-authenticator"
[package.metadata.docs.rs]
rustc-args = [ "--cfg", "docsrs" ]
rustdoc-args = [ "--cfg", "docsrs" ]
[build-dependencies]
tauri-plugin = { workspace = true, features = [ "build" ] }
[dependencies]
serde = { workspace = true }
serde_json = { workspace = true }
@@ -23,8 +27,10 @@ authenticator = "0.3.1"
once_cell = "1"
sha2 = "0.10"
base64 = "0.21"
u2f = "0.2"
chrono = ">= 0.4.0, <0.4.30"
chrono = "0.4"
bytes = "1"
byteorder = "1"
openssl = "0.10"
[dev-dependencies]
rand = "0.8"
+1 -1
View File
@@ -21,7 +21,7 @@ Install the authenticator plugin by adding the following lines to your `Cargo.to
```toml
# you can add the dependencies on the `[dependencies]` section if you do not target mobile
[target."cfg(not(any(target_os = \"android\", target_os = \"ios\")))".dependencies]
tauri-plugin-authenticator = "2.0.0-alpha"
tauri-plugin-authenticator = "2.0.0-beta"
# alternatively with Git:
tauri-plugin-authenticator = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v2" }
```
+15
View File
@@ -0,0 +1,15 @@
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
const COMMANDS: &[&str] = &[
"init_auth",
"register",
"verify_registration",
"sign",
"verify_signature",
];
fn main() {
tauri_plugin::Builder::new(COMMANDS).build();
}
+1 -1
View File
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
import { invoke } from "@tauri-apps/api/primitives";
import { invoke } from "@tauri-apps/api/core";
export class Authenticator {
async init(): Promise<void> {
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@tauri-apps/plugin-authenticator",
"version": "2.0.0-alpha.4",
"version": "2.0.0-beta.0",
"description": "Use hardware security-keys in your Tauri App.",
"license": "MIT or APACHE-2.0",
"authors": [
@@ -24,6 +24,6 @@
"LICENSE"
],
"dependencies": {
"@tauri-apps/api": "2.0.0-alpha.12"
"@tauri-apps/api": "2.0.0-beta.0"
}
}
@@ -0,0 +1,13 @@
# Automatically generated - DO NOT EDIT!
"$schema" = "../../schemas/schema.json"
[[permission]]
identifier = "allow-init-auth"
description = "Enables the init_auth command without any pre-configured scope."
commands.allow = ["init_auth"]
[[permission]]
identifier = "deny-init-auth"
description = "Denies the init_auth command without any pre-configured scope."
commands.deny = ["init_auth"]
@@ -0,0 +1,13 @@
# Automatically generated - DO NOT EDIT!
"$schema" = "../../schemas/schema.json"
[[permission]]
identifier = "allow-register"
description = "Enables the register command without any pre-configured scope."
commands.allow = ["register"]
[[permission]]
identifier = "deny-register"
description = "Denies the register command without any pre-configured scope."
commands.deny = ["register"]
@@ -0,0 +1,13 @@
# Automatically generated - DO NOT EDIT!
"$schema" = "../../schemas/schema.json"
[[permission]]
identifier = "allow-sign"
description = "Enables the sign command without any pre-configured scope."
commands.allow = ["sign"]
[[permission]]
identifier = "deny-sign"
description = "Denies the sign command without any pre-configured scope."
commands.deny = ["sign"]
@@ -0,0 +1,13 @@
# Automatically generated - DO NOT EDIT!
"$schema" = "../../schemas/schema.json"
[[permission]]
identifier = "allow-verify-registration"
description = "Enables the verify_registration command without any pre-configured scope."
commands.allow = ["verify_registration"]
[[permission]]
identifier = "deny-verify-registration"
description = "Denies the verify_registration command without any pre-configured scope."
commands.deny = ["verify_registration"]
@@ -0,0 +1,13 @@
# Automatically generated - DO NOT EDIT!
"$schema" = "../../schemas/schema.json"
[[permission]]
identifier = "allow-verify-signature"
description = "Enables the verify_signature command without any pre-configured scope."
commands.allow = ["verify_signature"]
[[permission]]
identifier = "deny-verify-signature"
description = "Denies the verify_signature command without any pre-configured scope."
commands.deny = ["verify_signature"]
@@ -0,0 +1,42 @@
# Permissions
## allow-init-auth
Enables the init_auth command without any pre-configured scope.
## deny-init-auth
Denies the init_auth command without any pre-configured scope.
## allow-register
Enables the register command without any pre-configured scope.
## deny-register
Denies the register command without any pre-configured scope.
## allow-sign
Enables the sign command without any pre-configured scope.
## deny-sign
Denies the sign command without any pre-configured scope.
## allow-verify-registration
Enables the verify_registration command without any pre-configured scope.
## deny-verify-registration
Denies the verify_registration command without any pre-configured scope.
## allow-verify-signature
Enables the verify_signature command without any pre-configured scope.
## deny-verify-signature
Denies the verify_signature command without any pre-configured scope.
@@ -0,0 +1,322 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "PermissionFile",
"description": "Permission file that can define a default permission, a set of permissions or a list of inlined permissions.",
"type": "object",
"properties": {
"default": {
"description": "The default permission set for the plugin",
"anyOf": [
{
"$ref": "#/definitions/DefaultPermission"
},
{
"type": "null"
}
]
},
"set": {
"description": "A list of permissions sets defined",
"default": [],
"type": "array",
"items": {
"$ref": "#/definitions/PermissionSet"
}
},
"permission": {
"description": "A list of inlined permissions",
"default": [],
"type": "array",
"items": {
"$ref": "#/definitions/Permission"
}
}
},
"definitions": {
"DefaultPermission": {
"description": "The default permission set of the plugin.\n\nWorks similarly to a permission with the \"default\" identifier.",
"type": "object",
"required": [
"permissions"
],
"properties": {
"version": {
"description": "The version of the permission.",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 1.0
},
"description": {
"description": "Human-readable description of what the permission does.",
"type": [
"string",
"null"
]
},
"permissions": {
"description": "All permissions this set contains.",
"type": "array",
"items": {
"type": "string"
}
}
}
},
"PermissionSet": {
"description": "A set of direct permissions grouped together under a new name.",
"type": "object",
"required": [
"description",
"identifier",
"permissions"
],
"properties": {
"identifier": {
"description": "A unique identifier for the permission.",
"type": "string"
},
"description": {
"description": "Human-readable description of what the permission does.",
"type": "string"
},
"permissions": {
"description": "All permissions this set contains.",
"type": "array",
"items": {
"$ref": "#/definitions/PermissionKind"
}
}
}
},
"Permission": {
"description": "Descriptions of explicit privileges of commands.\n\nIt can enable commands to be accessible in the frontend of the application.\n\nIf the scope is defined it can be used to fine grain control the access of individual or multiple commands.",
"type": "object",
"required": [
"identifier"
],
"properties": {
"version": {
"description": "The version of the permission.",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 1.0
},
"identifier": {
"description": "A unique identifier for the permission.",
"type": "string"
},
"description": {
"description": "Human-readable description of what the permission does.",
"type": [
"string",
"null"
]
},
"commands": {
"description": "Allowed or denied commands when using this permission.",
"default": {
"allow": [],
"deny": []
},
"allOf": [
{
"$ref": "#/definitions/Commands"
}
]
},
"scope": {
"description": "Allowed or denied scoped when using this permission.",
"default": {},
"allOf": [
{
"$ref": "#/definitions/Scopes"
}
]
}
}
},
"Commands": {
"description": "Allowed and denied commands inside a permission.\n\nIf two commands clash inside of `allow` and `deny`, it should be denied by default.",
"type": "object",
"properties": {
"allow": {
"description": "Allowed command.",
"default": [],
"type": "array",
"items": {
"type": "string"
}
},
"deny": {
"description": "Denied command, which takes priority.",
"default": [],
"type": "array",
"items": {
"type": "string"
}
}
}
},
"Scopes": {
"description": "A restriction of the command/endpoint functionality.\n\nIt can be of any serde serializable type and is used for allowing or preventing certain actions inside a Tauri command.\n\nThe scope is passed to the command and handled/enforced by the command itself.",
"type": "object",
"properties": {
"allow": {
"description": "Data that defines what is allowed by the scope.",
"type": [
"array",
"null"
],
"items": {
"$ref": "#/definitions/Value"
}
},
"deny": {
"description": "Data that defines what is denied by the scope.",
"type": [
"array",
"null"
],
"items": {
"$ref": "#/definitions/Value"
}
}
}
},
"Value": {
"description": "All supported ACL values.",
"anyOf": [
{
"description": "Represents a null JSON value.",
"type": "null"
},
{
"description": "Represents a [`bool`].",
"type": "boolean"
},
{
"description": "Represents a valid ACL [`Number`].",
"allOf": [
{
"$ref": "#/definitions/Number"
}
]
},
{
"description": "Represents a [`String`].",
"type": "string"
},
{
"description": "Represents a list of other [`Value`]s.",
"type": "array",
"items": {
"$ref": "#/definitions/Value"
}
},
{
"description": "Represents a map of [`String`] keys to [`Value`]s.",
"type": "object",
"additionalProperties": {
"$ref": "#/definitions/Value"
}
}
]
},
"Number": {
"description": "A valid ACL number.",
"anyOf": [
{
"description": "Represents an [`i64`].",
"type": "integer",
"format": "int64"
},
{
"description": "Represents a [`f64`].",
"type": "number",
"format": "double"
}
]
},
"PermissionKind": {
"type": "string",
"oneOf": [
{
"description": "allow-init-auth -> Enables the init_auth command without any pre-configured scope.",
"type": "string",
"enum": [
"allow-init-auth"
]
},
{
"description": "deny-init-auth -> Denies the init_auth command without any pre-configured scope.",
"type": "string",
"enum": [
"deny-init-auth"
]
},
{
"description": "allow-register -> Enables the register command without any pre-configured scope.",
"type": "string",
"enum": [
"allow-register"
]
},
{
"description": "deny-register -> Denies the register command without any pre-configured scope.",
"type": "string",
"enum": [
"deny-register"
]
},
{
"description": "allow-sign -> Enables the sign command without any pre-configured scope.",
"type": "string",
"enum": [
"allow-sign"
]
},
{
"description": "deny-sign -> Denies the sign command without any pre-configured scope.",
"type": "string",
"enum": [
"deny-sign"
]
},
{
"description": "allow-verify-registration -> Enables the verify_registration command without any pre-configured scope.",
"type": "string",
"enum": [
"allow-verify-registration"
]
},
{
"description": "deny-verify-registration -> Denies the verify_registration command without any pre-configured scope.",
"type": "string",
"enum": [
"deny-verify-registration"
]
},
{
"description": "allow-verify-signature -> Enables the verify_signature command without any pre-configured scope.",
"type": "string",
"enum": [
"allow-verify-signature"
]
},
{
"description": "deny-verify-signature -> Denies the verify_signature command without any pre-configured scope.",
"type": "string",
"enum": [
"deny-verify-signature"
]
}
]
}
}
}
+1 -1
View File
@@ -11,7 +11,7 @@ pub enum Error {
#[error(transparent)]
JSON(#[from] serde_json::Error),
#[error(transparent)]
U2F(#[from] u2f::u2ferror::U2fError),
U2F(#[from] crate::u2f_crate::u2ferror::U2fError),
#[error(transparent)]
Auth(#[from] authenticator::errors::AuthenticatorError),
}
+1
View File
@@ -17,6 +17,7 @@
mod auth;
mod error;
mod u2f;
mod u2f_crate;
use tauri::{
plugin::{Builder as PluginBuilder, TauriPlugin},
+3 -3
View File
@@ -2,13 +2,13 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
use crate::u2f_crate::messages::*;
use crate::u2f_crate::protocol::*;
use crate::u2f_crate::register::*;
use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine};
use chrono::prelude::*;
use serde::Serialize;
use std::convert::Into;
use u2f::messages::*;
use u2f::protocol::*;
use u2f::register::*;
static VERSION: &str = "U2F_V2";
@@ -0,0 +1,8 @@
Copyright (c) 2017
Licensed under either of
* Apache License, Version 2.0, (http://www.apache.org/licenses/LICENSE-2.0)
* MIT license (http://opensource.org/licenses/MIT)
at your option.
@@ -0,0 +1,66 @@
// Copyright 2021 Flavio Oliveira
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
use bytes::{Buf, BufMut};
use openssl::sha::sha256;
use serde::Serialize;
use std::io::Cursor;
use crate::u2f_crate::u2ferror::U2fError;
/// The `Result` type used in this crate.
type Result<T> = ::std::result::Result<T, U2fError>;
#[derive(Serialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct Authorization {
pub counter: u32,
pub user_presence: bool,
}
pub fn parse_sign_response(
app_id: String,
client_data: Vec<u8>,
public_key: Vec<u8>,
sign_data: Vec<u8>,
) -> Result<Authorization> {
if sign_data.len() <= 5 {
return Err(U2fError::InvalidSignatureData);
}
let user_presence_flag = &sign_data[0];
let counter = &sign_data[1..=4];
let signature = &sign_data[5..];
// Let's build the msg to verify the signature
let app_id_hash = sha256(&app_id.into_bytes());
let client_data_hash = sha256(&client_data[..]);
let mut msg = vec![];
msg.put(app_id_hash.as_ref());
msg.put_u8(*user_presence_flag);
msg.put(counter);
msg.put(client_data_hash.as_ref());
let public_key = super::crypto::NISTP256Key::from_bytes(&public_key)?;
// The signature is to be verified by the relying party using the public key obtained during registration.
let verified = public_key.verify_signature(signature, msg.as_ref())?;
if !verified {
return Err(U2fError::BadSignature);
}
let authorization = Authorization {
counter: get_counter(counter),
user_presence: true,
};
Ok(authorization)
}
fn get_counter(counter: &[u8]) -> u32 {
let mut buf = Cursor::new(counter);
buf.get_u32()
}
@@ -0,0 +1,157 @@
// Copyright 2021 Flavio Oliveira
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
//! Cryptographic operation wrapper for Webauthn. This module exists to
//! allow ease of auditing, safe operation wrappers for the webauthn library,
//! and cryptographic provider abstraction. This module currently uses OpenSSL
//! as the cryptographic primitive provider.
// Source can be found here: https://github.com/Firstyear/webauthn-rs/blob/master/src/crypto.rs
#![allow(non_camel_case_types)]
use openssl::{bn, ec, hash, nid, sign, x509};
use std::convert::TryFrom;
// use super::constants::*;
use crate::u2f_crate::u2ferror::U2fError;
use openssl::pkey::Public;
// use super::proto::*;
// Why OpenSSL over another rust crate?
// - Well, the openssl crate allows us to reconstruct a public key from the
// x/y group coords, where most others want a pkcs formatted structure. As
// a result, it's easiest to use openssl as it gives us exactly what we need
// for these operations, and despite it's many challenges as a library, it
// has resources and investment into it's maintenance, so we can a least
// assert a higher level of confidence in it that <backyard crypto here>.
// Object({Integer(-3): Bytes([48, 185, 178, 204, 113, 186, 105, 138, 190, 33, 160, 46, 131, 253, 100, 177, 91, 243, 126, 128, 245, 119, 209, 59, 186, 41, 215, 196, 24, 222, 46, 102]), Integer(-2): Bytes([158, 212, 171, 234, 165, 197, 86, 55, 141, 122, 253, 6, 92, 242, 242, 114, 158, 221, 238, 163, 127, 214, 120, 157, 145, 226, 232, 250, 144, 150, 218, 138]), Integer(-1): U64(1), Integer(1): U64(2), Integer(3): I64(-7)})
//
/// An X509PublicKey. This is what is otherwise known as a public certificate
/// which comprises a public key and other signed metadata related to the issuer
/// of the key.
pub struct X509PublicKey {
pubk: x509::X509,
}
impl std::fmt::Debug for X509PublicKey {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "X509PublicKey")
}
}
impl TryFrom<&[u8]> for X509PublicKey {
type Error = U2fError;
// Must be DER bytes. If you have PEM, base64decode first!
fn try_from(d: &[u8]) -> Result<Self, Self::Error> {
let pubk = x509::X509::from_der(d)?;
Ok(X509PublicKey { pubk })
}
}
impl X509PublicKey {
pub(crate) fn common_name(&self) -> Option<String> {
let cert = &self.pubk;
let subject = cert.subject_name();
let common = subject
.entries_by_nid(openssl::nid::Nid::COMMONNAME)
.next()
.map(|b| b.data().as_slice());
if let Some(common) = common {
std::str::from_utf8(common).ok().map(|s| s.to_string())
} else {
None
}
}
pub(crate) fn is_secp256r1(&self) -> Result<bool, U2fError> {
// Can we get the public key?
let pk = self.pubk.public_key()?;
let ec_key = pk.ec_key()?;
ec_key.check_key()?;
let ec_grpref = ec_key.group();
let ec_curve = ec_grpref.curve_name().ok_or(U2fError::OpenSSLNoCurveName)?;
Ok(ec_curve == nid::Nid::X9_62_PRIME256V1)
}
pub(crate) fn verify_signature(
&self,
signature: &[u8],
verification_data: &[u8],
) -> Result<bool, U2fError> {
let pkey = self.pubk.public_key()?;
// TODO: Should this determine the hash type from the x509 cert? Or other?
let mut verifier = sign::Verifier::new(hash::MessageDigest::sha256(), &pkey)?;
verifier.update(verification_data)?;
Ok(verifier.verify(signature)?)
}
}
pub struct NISTP256Key {
/// The key's public X coordinate.
pub x: [u8; 32],
/// The key's public Y coordinate.
pub y: [u8; 32],
}
impl NISTP256Key {
pub fn from_bytes(public_key_bytes: &[u8]) -> Result<Self, U2fError> {
if public_key_bytes.len() != 65 {
return Err(U2fError::InvalidPublicKey);
}
if public_key_bytes[0] != 0x04 {
return Err(U2fError::InvalidPublicKey);
}
let mut x: [u8; 32] = Default::default();
x.copy_from_slice(&public_key_bytes[1..=32]);
let mut y: [u8; 32] = Default::default();
y.copy_from_slice(&public_key_bytes[33..=64]);
Ok(NISTP256Key { x, y })
}
fn get_key(&self) -> Result<ec::EcKey<Public>, U2fError> {
let ec_group = ec::EcGroup::from_curve_name(openssl::nid::Nid::X9_62_PRIME256V1)?;
let xbn = bn::BigNum::from_slice(&self.x)?;
let ybn = bn::BigNum::from_slice(&self.y)?;
let ec_key = openssl::ec::EcKey::from_public_key_affine_coordinates(&ec_group, &xbn, &ybn)?;
// Validate the key is sound. IIRC this actually checks the values
// are correctly on the curve as specified
ec_key.check_key()?;
Ok(ec_key)
}
pub fn verify_signature(
&self,
signature: &[u8],
verification_data: &[u8],
) -> Result<bool, U2fError> {
let pkey = self.get_key()?;
let signature = openssl::ecdsa::EcdsaSig::from_der(signature)?;
let hash = openssl::sha::sha256(verification_data);
Ok(signature.verify(hash.as_ref(), &pkey)?)
}
}
@@ -0,0 +1,55 @@
// Copyright 2021 Flavio Oliveira
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
// As defined by FIDO U2F Javascript API.
// https://fidoalliance.org/specs/fido-u2f-v1.0-nfc-bt-amendment-20150514/fido-u2f-javascript-api.html#registration
use serde::{Deserialize, Serialize};
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct U2fRegisterRequest {
pub app_id: String,
pub register_requests: Vec<RegisterRequest>,
pub registered_keys: Vec<RegisteredKey>,
}
#[derive(Serialize)]
pub struct RegisterRequest {
pub version: String,
pub challenge: String,
}
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct RegisteredKey {
pub version: String,
pub key_handle: Option<String>,
pub app_id: String,
}
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RegisterResponse {
pub registration_data: String,
pub version: String,
pub client_data: String,
}
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct U2fSignRequest {
pub app_id: String,
pub challenge: String,
pub registered_keys: Vec<RegisteredKey>,
}
#[derive(Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SignResponse {
pub key_handle: String,
pub signature_data: String,
pub client_data: String,
}
@@ -0,0 +1,13 @@
// Copyright 2021 Flavio Oliveira
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
mod util;
pub mod authorization;
mod crypto;
pub mod messages;
pub mod protocol;
pub mod register;
pub mod u2ferror;
@@ -0,0 +1,192 @@
// Copyright 2021 Flavio Oliveira
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
use crate::u2f_crate::authorization::*;
use crate::u2f_crate::messages::*;
use crate::u2f_crate::register::*;
use crate::u2f_crate::u2ferror::U2fError;
use crate::u2f_crate::util::*;
use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine};
use chrono::prelude::*;
use chrono::Duration;
use serde::{Deserialize, Serialize};
type Result<T> = ::std::result::Result<T, U2fError>;
#[derive(Clone)]
pub struct U2f {
app_id: String,
}
#[derive(Deserialize, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct Challenge {
pub app_id: String,
pub challenge: String,
pub timestamp: String,
}
impl Challenge {
// Not used in this plugin.
#[allow(dead_code)]
pub fn new() -> Self {
Challenge {
app_id: String::new(),
challenge: String::new(),
timestamp: String::new(),
}
}
}
impl U2f {
// The app ID is a string used to uniquely identify an U2F app
pub fn new(app_id: String) -> Self {
U2f { app_id }
}
// Not used in this plugin.
#[allow(dead_code)]
pub fn generate_challenge(&self) -> Result<Challenge> {
let utc: DateTime<Utc> = Utc::now();
let challenge_bytes = generate_challenge(32)?;
let challenge = Challenge {
challenge: URL_SAFE_NO_PAD.encode(challenge_bytes),
timestamp: format!("{:?}", utc),
app_id: self.app_id.clone(),
};
Ok(challenge.clone())
}
// Not used in this plugin.
#[allow(dead_code)]
pub fn request(
&self,
challenge: Challenge,
registrations: Vec<Registration>,
) -> Result<U2fRegisterRequest> {
let u2f_request = U2fRegisterRequest {
app_id: self.app_id.clone(),
register_requests: self.register_request(challenge),
registered_keys: self.registered_keys(registrations),
};
Ok(u2f_request)
}
fn register_request(&self, challenge: Challenge) -> Vec<RegisterRequest> {
let mut requests: Vec<RegisterRequest> = vec![];
let request = RegisterRequest {
version: U2F_V2.into(),
challenge: challenge.challenge,
};
requests.push(request);
requests
}
pub fn register_response(
&self,
challenge: Challenge,
response: RegisterResponse,
) -> Result<Registration> {
if expiration(challenge.timestamp) > Duration::seconds(300) {
return Err(U2fError::ChallengeExpired);
}
let registration_data: Vec<u8> = URL_SAFE_NO_PAD
.decode(&response.registration_data[..])
.unwrap();
let client_data: Vec<u8> = URL_SAFE_NO_PAD.decode(&response.client_data[..]).unwrap();
parse_registration(challenge.app_id, client_data, registration_data)
}
fn registered_keys(&self, registrations: Vec<Registration>) -> Vec<RegisteredKey> {
let mut keys: Vec<RegisteredKey> = vec![];
for registration in registrations {
keys.push(get_registered_key(
self.app_id.clone(),
registration.key_handle,
));
}
keys
}
// Not used in this plugin.
#[allow(dead_code)]
pub fn sign_request(
&self,
challenge: Challenge,
registrations: Vec<Registration>,
) -> U2fSignRequest {
let mut keys: Vec<RegisteredKey> = vec![];
for registration in registrations {
keys.push(get_registered_key(
self.app_id.clone(),
registration.key_handle,
));
}
let signed_request = U2fSignRequest {
app_id: self.app_id.clone(),
challenge: URL_SAFE_NO_PAD.encode(challenge.challenge.as_bytes()),
registered_keys: keys,
};
signed_request
}
pub fn sign_response(
&self,
challenge: Challenge,
reg: Registration,
sign_resp: SignResponse,
counter: u32,
) -> Result<u32> {
if expiration(challenge.timestamp) > Duration::seconds(300) {
return Err(U2fError::ChallengeExpired);
}
if sign_resp.key_handle != get_encoded(&reg.key_handle[..]) {
return Err(U2fError::WrongKeyHandler);
}
let client_data: Vec<u8> = URL_SAFE_NO_PAD
.decode(&sign_resp.client_data[..])
.map_err(|_e| U2fError::InvalidClientData)?;
let sign_data: Vec<u8> = URL_SAFE_NO_PAD
.decode(&sign_resp.signature_data[..])
.map_err(|_e| U2fError::InvalidSignatureData)?;
let public_key = reg.pub_key;
let auth = parse_sign_response(
self.app_id.clone(),
client_data.clone(),
public_key,
sign_data.clone(),
);
match auth {
Ok(ref res) => {
// CounterTooLow is raised when the counter value received from the device is
// lower than last stored counter value.
if res.counter < counter {
Err(U2fError::CounterTooLow)
} else {
Ok(res.counter)
}
}
Err(e) => Err(e),
}
}
}
@@ -0,0 +1,102 @@
// Copyright 2021 Flavio Oliveira
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
use byteorder::{BigEndian, ByteOrder};
use bytes::{BufMut, Bytes};
use openssl::sha::sha256;
use serde::Serialize;
use crate::u2f_crate::messages::RegisteredKey;
use crate::u2f_crate::u2ferror::U2fError;
use crate::u2f_crate::util::*;
use std::convert::TryFrom;
/// The `Result` type used in this crate.
type Result<T> = ::std::result::Result<T, U2fError>;
// Single enrolment or pairing between an application and a token.
#[derive(Serialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct Registration {
pub key_handle: Vec<u8>,
pub pub_key: Vec<u8>,
// AttestationCert can be null for Authenticate requests.
pub attestation_cert: Option<Vec<u8>>,
pub device_name: Option<String>,
}
pub fn parse_registration(
app_id: String,
client_data: Vec<u8>,
registration_data: Vec<u8>,
) -> Result<Registration> {
let reserved_byte = registration_data[0];
if reserved_byte != 0x05 {
return Err(U2fError::InvalidReservedByte);
}
let mut mem = Bytes::from(registration_data);
//Start parsing ... advance the reserved byte.
let _ = mem.split_to(1);
// P-256 NIST elliptic curve
let public_key = mem.split_to(65);
// Key Handle
let key_handle_size = mem.split_to(1);
let key_len = BigEndian::read_uint(&key_handle_size[..], 1);
let key_handle = mem.split_to(key_len as usize);
// The certificate length needs to be inferred by parsing.
let cert_len = asn_length(mem.clone()).unwrap();
let attestation_certificate = mem.split_to(cert_len);
// Remaining data corresponds to the signature
let signature = mem;
// Let's build the msg to verify the signature
let app_id_hash = sha256(&app_id.into_bytes());
let client_data_hash = sha256(&client_data[..]);
let mut msg = vec![0x00]; // A byte reserved for future use [1 byte] with the value 0x00
msg.put(app_id_hash.as_ref());
msg.put(client_data_hash.as_ref());
msg.put(key_handle.clone());
msg.put(public_key.clone());
// The signature is to be verified by the relying party using the public key certified
// in the attestation certificate.
let cerificate_public_key =
super::crypto::X509PublicKey::try_from(&attestation_certificate[..])?;
if !(cerificate_public_key.is_secp256r1()?) {
return Err(U2fError::BadCertificate);
}
let verified = cerificate_public_key.verify_signature(&signature[..], &msg[..])?;
if !verified {
return Err(U2fError::BadCertificate);
}
let registration = Registration {
key_handle: key_handle[..].to_vec(),
pub_key: public_key[..].to_vec(),
attestation_cert: Some(attestation_certificate[..].to_vec()),
device_name: cerificate_public_key.common_name(),
};
Ok(registration)
}
pub fn get_registered_key(app_id: String, key_handle: Vec<u8>) -> RegisteredKey {
RegisteredKey {
app_id,
version: U2F_V2.into(),
key_handle: Some(get_encoded(key_handle.as_slice())),
}
}
@@ -0,0 +1,40 @@
// Copyright 2021 Flavio Oliveira
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
use thiserror::Error;
#[derive(Debug, Error)]
pub enum U2fError {
#[error("ASM1 Decoder error")]
Asm1DecoderError,
#[error("Not able to verify signature")]
BadSignature,
#[error("Not able to generate random bytes")]
RandomSecureBytesError,
#[error("Invalid Reserved Byte")]
InvalidReservedByte,
#[error("Challenge Expired")]
ChallengeExpired,
#[error("Wrong Key Handler")]
WrongKeyHandler,
#[error("Invalid Client Data")]
InvalidClientData,
#[error("Invalid Signature Data")]
InvalidSignatureData,
#[error("Invalid User Presence Byte")]
InvalidUserPresenceByte,
#[error("Failed to parse certificate")]
BadCertificate,
#[error("Not Trusted Anchor")]
NotTrustedAnchor,
#[error("Counter too low")]
CounterTooLow,
#[error("Invalid public key")]
OpenSSLNoCurveName,
#[error("OpenSSL no curve name")]
InvalidPublicKey,
#[error(transparent)]
OpenSSLError(#[from] openssl::error::ErrorStack),
}
@@ -0,0 +1,67 @@
// Copyright 2021 Flavio Oliveira
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
use crate::u2f_crate::u2ferror::U2fError;
use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine};
use bytes::Bytes;
use chrono::prelude::*;
use chrono::Duration;
use openssl::rand;
/// The `Result` type used in this crate.
type Result<T> = ::std::result::Result<T, U2fError>;
pub const U2F_V2: &str = "U2F_V2";
// Generates a challenge from a secure, random source.
pub fn generate_challenge(size: usize) -> Result<Vec<u8>> {
let mut bytes: Vec<u8> = vec![0; size];
rand::rand_bytes(&mut bytes).map_err(|_e| U2fError::RandomSecureBytesError)?;
Ok(bytes)
}
pub fn expiration(timestamp: String) -> Duration {
let now: DateTime<Utc> = Utc::now();
let ts = timestamp.parse::<DateTime<Utc>>();
now.signed_duration_since(ts.unwrap())
}
// Decode initial bytes of buffer as ASN and return the length of the encoded structure.
// http://en.wikipedia.org/wiki/X.690
pub fn asn_length(mem: Bytes) -> Result<usize> {
let buffer: &[u8] = &mem[..];
if mem.len() < 2 || buffer[0] != 0x30 {
// Type
return Err(U2fError::Asm1DecoderError);
}
let len = buffer[1]; // Len
if len & 0x80 == 0 {
return Ok((len & 0x7f) as usize);
}
let numbem_of_bytes = len & 0x7f;
if numbem_of_bytes == 0 {
return Err(U2fError::Asm1DecoderError);
}
let mut length: usize = 0;
for num in 0..numbem_of_bytes {
length = length * 0x100 + (buffer[(2 + num) as usize] as usize);
}
length += numbem_of_bytes as usize;
Ok(length + 2) // Add the 2 initial bytes: type and length.
}
pub fn get_encoded(data: &[u8]) -> String {
let encoded: String = URL_SAFE_NO_PAD.encode(data);
encoded.trim_end_matches('=').to_string()
}

Some files were not shown because too many files have changed in this diff Show More