From ddf53e590818cd589f5b28dc17f650b81c9b88de Mon Sep 17 00:00:00 2001 From: Mystikfluu Date: Sat, 14 Jan 2023 21:20:37 +0100 Subject: [PATCH] add whole app LOL Co-authored-by: Alpisc --- .gitignore | 3 +- library | 2 +- src-tauri/Cargo.lock | 223 +++++++++++++++---- src-tauri/Cargo.toml | 6 +- src-tauri/src/main.rs | 63 +++++- src-tauri/tauri.conf.json | 17 +- src/favicon.ico | Bin 0 -> 243774 bytes src/images/security_lock_locked.png | Bin 0 -> 2028 bytes src/images/security_lock_unlocked.png | Bin 0 -> 2094 bytes src/index.html | 44 ++-- src/main.js | 294 ++++++++++++++++++++++++-- src/pwprompt.js | 59 ++++++ src/style.css | 130 ++++++++---- 13 files changed, 695 insertions(+), 146 deletions(-) create mode 100644 src/favicon.ico create mode 100644 src/images/security_lock_locked.png create mode 100644 src/images/security_lock_unlocked.png create mode 100644 src/pwprompt.js diff --git a/.gitignore b/.gitignore index 2f7f1c4..1864a66 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /src-tauri/target *_backup -*ignore* \ No newline at end of file +*ignore* +msi/ \ No newline at end of file diff --git a/library b/library index 380ae20..31b8e5b 160000 --- a/library +++ b/library @@ -1 +1 @@ -Subproject commit 380ae204378be877fffb7ee7f9167748defc2156 +Subproject commit 31b8e5bf0ab8b561ffeafb07432a382000323f7a diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 1edf75a..c3a4ba0 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -161,6 +161,12 @@ dependencies = [ "serde", ] +[[package]] +name = "bumpalo" +version = "3.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba" + [[package]] name = "bytemuck" version = "1.12.3" @@ -818,7 +824,7 @@ dependencies = [ "libc", "log", "rustversion", - "windows", + "windows 0.39.0", ] [[package]] @@ -1194,20 +1200,19 @@ dependencies = [ [[package]] name = "ip_lib" -version = "0.1.0" +version = "1.0.0" dependencies = [ "aes-gcm", "brotli", "hex", "home", "rand 0.8.5", - "rpassword", "sha2", ] [[package]] name = "ipass-gui" -version = "0.0.1" +version = "0.1.0" dependencies = [ "ip_lib", "serde", @@ -1271,6 +1276,15 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" +[[package]] +name = "js-sys" +version = "0.3.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" +dependencies = [ + "wasm-bindgen", +] + [[package]] name = "json-patch" version = "0.2.7" @@ -1538,6 +1552,17 @@ dependencies = [ "objc_exception", ] +[[package]] +name = "objc-foundation" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9" +dependencies = [ + "block", + "objc", + "objc_id", +] + [[package]] name = "objc_exception" version = "0.1.2" @@ -1568,16 +1593,6 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" -[[package]] -name = "open" -version = "3.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2078c0039e6a54a0c42c28faa984e115fb4c2d5bf2208f77d1961002df8576f8" -dependencies = [ - "pathdiff", - "windows-sys", -] - [[package]] name = "overload" version = "0.1.1" @@ -1638,12 +1653,6 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d01a5bd0424d00070b0098dd17ebca6f961a959dead1dbcbbbc1d1cd8d3deeba" -[[package]] -name = "pathdiff" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" - [[package]] name = "percent-encoding" version = "2.2.0" @@ -2031,24 +2040,27 @@ dependencies = [ ] [[package]] -name = "rpassword" -version = "7.2.0" +name = "rfd" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6678cf63ab3491898c0d021b493c94c9b221d91295294a2a5746eacbe5928322" +checksum = "0149778bd99b6959285b0933288206090c50e2327f47a9c463bfdbf45c8823ea" dependencies = [ - "libc", - "rtoolbox", - "winapi", -] - -[[package]] -name = "rtoolbox" -version = "0.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "034e22c514f5c0cb8a10ff341b9b048b5ceb21591f31c8f44c43b960f9b3524a" -dependencies = [ - "libc", - "winapi", + "block", + "dispatch", + "glib-sys", + "gobject-sys", + "gtk-sys", + "js-sys", + "lazy_static", + "log", + "objc", + "objc-foundation", + "objc_id", + "raw-window-handle", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows 0.37.0", ] [[package]] @@ -2451,7 +2463,7 @@ dependencies = [ "serde", "unicode-segmentation", "uuid 1.2.2", - "windows", + "windows 0.39.0", "windows-implement", "x11-dl", ] @@ -2488,11 +2500,10 @@ dependencies = [ "ignore", "objc", "once_cell", - "open", "percent-encoding", "rand 0.8.5", "raw-window-handle", - "regex", + "rfd", "semver 1.0.16", "serde", "serde_json", @@ -2511,7 +2522,7 @@ dependencies = [ "uuid 1.2.2", "webkit2gtk", "webview2-com", - "windows", + "windows 0.39.0", ] [[package]] @@ -2544,7 +2555,6 @@ dependencies = [ "png", "proc-macro2", "quote", - "regex", "semver 1.0.16", "serde", "serde_json", @@ -2587,7 +2597,7 @@ dependencies = [ "thiserror", "uuid 1.2.2", "webview2-com", - "windows", + "windows 0.39.0", ] [[package]] @@ -2606,7 +2616,7 @@ dependencies = [ "uuid 1.2.2", "webkit2gtk", "webview2-com", - "windows", + "windows 0.39.0", "wry", ] @@ -2635,7 +2645,7 @@ dependencies = [ "thiserror", "url", "walkdir", - "windows", + "windows 0.39.0", ] [[package]] @@ -2963,6 +2973,82 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasm-bindgen" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23639446165ca5a5de86ae1d8896b737ae80319560fbaa4c2887b7da6e7ebd7d" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" + +[[package]] +name = "web-sys" +version = "0.3.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "webkit2gtk" version = "0.18.2" @@ -3018,7 +3104,7 @@ checksum = "b4a769c9f1a64a8734bde70caafac2b96cada12cd4aefa49196b3a386b8b4178" dependencies = [ "webview2-com-macros", "webview2-com-sys", - "windows", + "windows 0.39.0", "windows-implement", ] @@ -3043,7 +3129,7 @@ dependencies = [ "serde", "serde_json", "thiserror", - "windows", + "windows 0.39.0", "windows-bindgen", "windows-metadata", ] @@ -3079,6 +3165,19 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57b543186b344cc61c85b5aab0d2e3adf4e0f99bc076eff9aa5927bcc0b8a647" +dependencies = [ + "windows_aarch64_msvc 0.37.0", + "windows_i686_gnu 0.37.0", + "windows_i686_msvc 0.37.0", + "windows_x86_64_gnu 0.37.0", + "windows_x86_64_msvc 0.37.0", +] + [[package]] name = "windows" version = "0.39.0" @@ -3146,6 +3245,12 @@ version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" +[[package]] +name = "windows_aarch64_msvc" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2623277cb2d1c216ba3b578c0f3cf9cdebeddb6e66b1b218bb33596ea7769c3a" + [[package]] name = "windows_aarch64_msvc" version = "0.39.0" @@ -3158,6 +3263,12 @@ version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" +[[package]] +name = "windows_i686_gnu" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3925fd0b0b804730d44d4b6278c50f9699703ec49bcd628020f46f4ba07d9e1" + [[package]] name = "windows_i686_gnu" version = "0.39.0" @@ -3170,6 +3281,12 @@ version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" +[[package]] +name = "windows_i686_msvc" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce907ac74fe331b524c1298683efbf598bb031bc84d5e274db2083696d07c57c" + [[package]] name = "windows_i686_msvc" version = "0.39.0" @@ -3182,6 +3299,12 @@ version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" +[[package]] +name = "windows_x86_64_gnu" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2babfba0828f2e6b32457d5341427dcbb577ceef556273229959ac23a10af33d" + [[package]] name = "windows_x86_64_gnu" version = "0.39.0" @@ -3200,6 +3323,12 @@ version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" +[[package]] +name = "windows_x86_64_msvc" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4dd6dc7df2d84cf7b33822ed5b86318fb1781948e9663bacd047fc9dd52259d" + [[package]] name = "windows_x86_64_msvc" version = "0.39.0" @@ -3255,7 +3384,7 @@ dependencies = [ "webkit2gtk", "webkit2gtk-sys", "webview2-com", - "windows", + "windows 0.39.0", "windows-implement", ] diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 0cae6fe..4aa46de 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ipass-gui" -version = "0.0.1" +version = "0.1.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -11,7 +11,7 @@ tauri-build = { version = "1.2", features = [] } [dependencies] serde_json = "1.0" serde = { version = "1.0", features = ["derive"] } -tauri = { version = "1.2", features = ["shell-open"] } +tauri = { version = "1.2", features = ["dialog-confirm"] } ip_lib = { path = "../library/" } [features] @@ -27,4 +27,4 @@ opt-level = 'z' # Optimize for size lto = true # Enable link-time optimization codegen-units = 1 # Reduce number of codegen units to increase optimizations panic = 'abort' # Abort on panic -# strip = true # Strip symbols from binary; remove pdb \ No newline at end of file +strip = true # Strip symbols from binary; remove pdb diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 9b04a47..fb766e1 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -1,24 +1,65 @@ #![cfg_attr( - all(not(debug_assertions), target_os = "linux"), - windows_subsystem = "windows" + all(not(debug_assertions), target_os = "linux"), + windows_subsystem = "windows" )] extern crate ip_lib; // Learn more about Tauri commands at https://tauri.app/v1/guides/features/command -#[tauri::command] -fn greet(name: &str) -> String { - format!("Hello, {}! You've been greeted from Rust!", name) -} #[tauri::command] fn get_version() -> String { - return option_env!("CARGO_PKG_VERSION").unwrap_or("x.x.x").to_string(); + return option_env!("CARGO_PKG_VERSION").unwrap_or("x.x.x").to_string(); +} + +#[tauri::command] +fn create_entry(name: String, username: String, pw: &str, mpw: String) -> bool { + ip_lib::create_entry(&name, username+";"+pw, mpw) +} + +#[tauri::command] +fn random_password() -> String { + ip_lib::random_password() +} + +#[tauri::command] +fn get_entry(name: String, mpw: String) -> Result<(String,String),String> { + let binding = ip_lib::get_entry(&name, mpw); + if let Ok(data) = binding { + let mut split = data.split(";"); + return Ok((split.next().unwrap().to_string(),split.next().unwrap().to_string())); + } else { + return Err(binding.expect_err("expected error")); + } +} + +#[tauri::command] +fn get_entries() -> Vec { + let mut vector: Vec = Vec::default(); + for entry in ip_lib::get_entries() { + let entry_filename = entry.unwrap().file_name(); + let ent =entry_filename.to_str().unwrap(); + vector.insert(0, ent[..ent.len()-6].to_string()); + } + + vector.sort(); + + return vector; +} + +#[tauri::command] +fn remove_entry(name: &str) -> bool { + let filepath = &(ip_lib::get_ipass_folder()+name+".ipass"); + if std::path::Path::new(filepath).exists() { + std::fs::remove_file(filepath).unwrap(); + return true; + } + return false; } fn main() { - tauri::Builder::default() - .invoke_handler(tauri::generate_handler![greet,get_version]) - .run(tauri::generate_context!()) - .expect("error while running tauri application"); + tauri::Builder::default() + .invoke_handler(tauri::generate_handler![get_version,create_entry,random_password,get_entry,get_entries,remove_entry]) + .run(tauri::generate_context!()) + .expect("error while running tauri application"); } diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index fb51191..7369f6c 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -8,14 +8,18 @@ }, "package": { "productName": "ipass", - "version": "0.0.1" + "version": "0.1.0" }, "tauri": { "allowlist": { "all": false, - "shell": { + "dialog": { "all": false, - "open": true + "open": false, + "save": false, + "ask": false, + "message": false, + "confirm": true } }, "bundle": { @@ -58,9 +62,10 @@ { "fullscreen": false, "height": 600, - "resizable": true, - "title": "ipass-gui", - "width": 800 + "resizable": false, + "title": "IPass", + "width": 880, + "theme": "Dark" } ] } diff --git a/src/favicon.ico b/src/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..3c167117e58a52b48624dd9100469bbbf98c9528 GIT binary patch literal 243774 zcmeI537izwwg1QMnHLvg{x6AQLE4A(Jkpy;?<0NWP5k^8(hE5*_r6@0djYn+vfS?0yax8un6PgW z@1uW_OQ7{tEz?@IY?*cyQY$3xhf9%PS-~^Q~!hX z8InYpKF9a|jBCG*?ITE&kUD07QQm|1%tLZ3pCQ%4&Rr{T$kYZ2Y+i_Smm&R!H+>n~ zOOVb-3d(E9o9GG0Nq{yhAbkW|bK={)|P_La64 z`zm6GOCW&9i?M$LSa}+Z{4Y{{j5YN9C%D&Xqyf0s7s#bOIb3v&mOU~=e#nuRC)z`@ z_&CxBNS`4|1nD36&M%Sn;yS%?y-Qk?%T}#CN%?HFcNpu1$)n3_Cue`f*r{9`I|xRa zg2zCfKLL-A;@$xI8NVSP-Yt0?hx7_kL+xWAkAeHI;vU6F%4Rbu74@0PtK%0z z=c|yuiT!`!cpwu_*=OF^*3u8L+KZTX`rXM8x3#n2{oIs1+WCAJ_ph?&rN4(2hiT=)y}dH~Ph?%CP%EJYcKS@)#=(?;4j_%imJ%0Ak8 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 Ty>re}Lg}HeZ<>8q2Auyt;>l)W literal 0 HcmV?d00001 diff --git a/src/images/security_lock_locked.png b/src/images/security_lock_locked.png new file mode 100644 index 0000000000000000000000000000000000000000..e67febaa95485b2b3116629f13cd328f13208e7b GIT binary patch literal 2028 zcmc(gdpOgJAIHDDVl$+rVP-C!P;&{nEIKl8O4<^=AbD0_WArYM8&`h8r^&W^G%!?1xjYL1aHSuG9Ksxy4G$*Ywneii zPUIYrSHf*~EV4Fsj`qrAy=QWxJK=uJ{OhfIE7WO;+rZ$Jfz6>vKJnV`hwl4#LC{4MC)^wgxZLB;TeV5e=|VIfvN`D|&(yX8rsoxQRA-(0CjR@$kxm-T%%(~{tz zr%W}Ui8k`72|{R9rr0snY$Pe8w13P(P2jid+tKlNV{$rfbehRnIl@1w{3DueEJ(^O z;Bro`d*dY9bA2!0aj>gpARPZ`*9ts`eZYR|x^ddE zu?}4@@w96qOJP1jx&kf;H1*Nw-{&fg%#Z1MEwQp{ugMq~Uej0>B*J%u2xLtv(-98X zA^c=pj%#?e*hv2@OvygfF0F??;OjScdKviYtyPBJMHC6Sl=aA3&T_GqwI9jKsK~h? z(^U-H4J-B!rwue@YlrJ*P@0GSwf$PMCtvp;$Lx3QMGm+}Ja!w(eK6$lDra47T%K)% z&z{S1<14fy%zTrtF(`cuvU)PQjPR-&o2r%h#AcFNc+VbnzETais6K>mvzWc^9~|R^ z`S?T^dP5oxClEmrtLIR6QX{RnT_6Qr{%zge&7>loUH83RNO`$V*# zR%xyqTw2Y$h?7>~eBa<_i^2%x(A7Os*+RGYUQ`+by=_so9 zaksr7_fG9HtIx8O!=Rvj>7~aZhtUF7!GZF%%{XUd?4;Y9RGt1A-euJ(?z5|Du~>cN zU{Aftw#1NjBNUfK`#u~%tABL;wgyz(gpCU zZ>3Bt45p--;AmHG)uumCO~uQJl!nhcnalF&c{!nc^#0jo;zZ6QP0`e=a-OT*7-_9$ z`p>cEW8Wx4Keo=l{}lW7p>#Yc^_*at7+SW%3^`2fiCDZFvA#PPfKmUBn^~9RP~dxvGUB4^HQTzG~4SvymVoyY~^?nOhfyvgO>LQrS+W>x9h!hA+Mp_JV|- zO~{pPCbx=s1&T=YvLTl?O$OL4_#tn}bM}hUR^<;L{e7OT_R?dP>D&q;Bvg}>V{=+yTps(W|LMpa1Xa@0LDvHd<5#9rjwJ1&O z6Fk<^R+~ZOD92%GK45Wi!CMgMa<4`d@cs+7P4(vH)0Ru|H>#l*?__05U4xcg6IzLJ zxV}u$O=Vo(FZo0XeXz}uFwk!z^)~*n?n7u`?XzLob-`CLsuqQF>Y7Ned+=v-zt;gq zvvHUc8=hg-KZWM5lpb&|O}kN777(*12!7$KS=m2XW%@hrRn{_biQ$1(sVAdDnsZg# YJBD9fZZivhv)P+~pVvW3y$6l+U$p3!iU0rr literal 0 HcmV?d00001 diff --git a/src/images/security_lock_unlocked.png b/src/images/security_lock_unlocked.png new file mode 100644 index 0000000000000000000000000000000000000000..c96a4241b0b53f11d52d39af7a936d92e94346d5 GIT binary patch literal 2094 zcmc(gYdq790>}UVjcsYMwahK$9u8t6*K9VIm5|FesR-#w?h|{m_>Z~QT1qahXu2r( z+w&+n<~l;TR!ZWyr1Id&CFOR!Kj+PPbv~c(=kxu({Js07xjH-Sl2Vic0ALr@k>Umb z;C6xl3cjt2B@B;k#ko3p*lj!IUd;cC*Lye8wioC`xjDN7EN_JQu`dmM$!j|&SH5e= zjgqi$%Om{eimoWTTVKe(c{RtOyRFvrVC>KOQviT8rBX;9toO4Yhohew?t#YjwHH+g z84y~yw&Am|^yuPUU6O|*Y`T8w>Bd8?;W}-@vCnOfW)1#W>~i*~9c`uKXfC0=3%=@{ z!mK-|l7}p2o+i5JeEeJLA2mC1dwVR(OGsz5R)xgIrlTDZP}nnpujIOJ)1Q)U{c?!L z_Sf&kn&nb)*Yc~?8BaT!sI0Day$)-E)3@gS998EacvqEixZiuBLBj6y;oOJe)`Fzx z{No%hvr?|ovMk)*DBPvZYe;l>F=0af&BK9s&!9WX)II?|pTK7dGs?D&`-sq4oqYXuM^%8C`2l8%LyKuTi4(U6pJ1ZT4?wP@` zPo7+BIi)IxZxDgK@D;j&OH>cKYJJL=tQpJ+9Qw}G?ndLq)j9ba;Z4)YN%m%!2i{iZ zdLCOXf+0pmRkbC-1MzU9V%eV^ou@N1hm^*@8>wdN2O_I^84a$lLMb{}X_Y8Dt~pBb zpDiu64C4|0B`ZOiH=7UF#3FqZhCi?l8gVj}kYtYFm%(qWg8^$qdUuq*^Ke*F6uTEN zaai%ckC|6caAo@0DARNIITEHn-7&_!cnh-*Z2onmGPv?|n#miGkocFYn#L(;a1w3W z+yc&8_tvx_EemE={{n?!G8I^m0$_;}tJ8o_BO3qP#uV!a`lJ`Y-!Cc{l4RXLslZYJ zHRyj3(Gm*Ye$f>U0?w714VoYeM`bes1cGAnm(<&sNhF|N_4&&gfAE;7e2gvgSoWciv`!o>`;BB z1Cy+vwjrAU4ao(W6HgS(aF0uWf6}X@Vql_lKD%c5f{YUy=@|T=z2bS#F2;McSX$Qn z*$1A8Rq<$pfsPdXto&ws5y=CSRG`)&;<-QDQX26O8QPb5Z$B^T#AM@_|dhXOjU&9(Ti*|kEba3Dsm$HE=D5d6`7Hhe2E^%0q5>xkU(4}tnkRto! zbY$Mr(AKQk?B6=I8~rxaHeK;u%erP8PoJQ~wIQpP(ybV;cu|AJ+T-Bl*yVra6C_=M z{|3|v-b(b=!)IrIYu|Fji~6TlKfZYV;zuUHQ{D`2II0N>-mE^JJ}ylhySeNJte^%v z0fx#uFBn9AfQyHeB61XCA#>r-2&jwE>;^|k9*c;I0*inW@Rdq2174$2Yz_~OQMqUi z*C_aK?kcD^WStVgca!YV9?D zo;YZXbi|Yo6OaqiePttbE;~=XKVeH)vUGYhgo<5S^SL_NTe6 zAjk*diSiJBtA`4rO3^$w;AJn_RZ65^C$A+jCd(-o62aqqJ&k*Q#Q_3bd+DMmfv&bh9Szn+wY4XcLJwbd*7J=YUxmI;d{2NcjlTcFvbHG}%sa z>1o36!HO*~P13jRZW{7LktyB_D_7P|u$MjphlK~La_T1;Jk%VW%k&FAMlaQ_D|B_w zR6ORUH=#J IPass - x.x.x +

IPass

-
- - - - - - - - - - - - - - -
Entry nameUsernamePassword
- ENTRY-NAME-HERE -
+

Locked

+ Lock +
+
+
+
Entry name
+
+
+
+
Username
+
+
+
+
Password
+
+
+
+
Action
+
+
+ diff --git a/src/main.js b/src/main.js index 9367ebb..c2cb420 100644 --- a/src/main.js +++ b/src/main.js @@ -1,23 +1,283 @@ const { invoke } = window.__TAURI__.tauri; -let greetInputEl; -let greetMsgEl; +let master_pw; +let lock_status = true; -async function greet() { - // Learn more about Tauri commands at https://tauri.app/v1/guides/features/command - greetMsgEl.textContent = await invoke("greet", { name: greetInputEl.value }); -} - -async function updateTitle() { - document.getElementById("pageTitle").innerText = `IPass - ${await invoke("get_version")}`; -} +// async function greet() { +// // Learn more about Tauri commands at https://tauri.app/v1/guides/features/command +// greetMsgEl.textContent = await invoke("greet", { name: greetInputEl.value }); +// } window.addEventListener("DOMContentLoaded", () => { - updateTitle() - - greetInputEl = document.querySelector("#greet-input"); - greetMsgEl = document.querySelector("#greet-msg"); - document - .querySelector("#greet-button") - .addEventListener("click", () => greet()); + document.getElementById("lockImg").addEventListener("click",toggleLock); + startup(); }); + +async function startup() { + + document.querySelector("#createEntry_actions > button").addEventListener("click",createEntry); + + let entries = await invoke("get_entries"); + console.log(entries); + for(let i = 0; i < entries.length; i++) { + buildEntry(entries[i]); + } + + for (let input of document.querySelectorAll('input[readonly]')) { + input.addEventListener('click', async function() { + if(!input.hasAttribute("readonly"))return + if(input.getAttribute("type")!="text" && lock_status)return + + let to_copy = input.value + + if(!lock_status) { + let isUsername = input.parentElement.classList.contains("entry_user") + let entry_name = input.parentElement.id.slice(0,input.parentElement.id.length-5) + console.log(entry_name,input.parentElement.id) + + let info = await invoke("get_entry",{ + name: entry_name, + mpw: await get_pw() + }) + + if(isUsername) { + to_copy = info[0] + } else { + if(input.parentElement.classList.contains("entry_name")) { + to_copy = entry_name + } else { + to_copy = info[1] + } + } + } + + navigator.clipboard.writeText(to_copy).then(function() { + let originalColor = input.style.borderColor; + input.style.borderColor = "lightgreen"; + setTimeout(function(){ + input.style.borderColor = originalColor; + },250) + + console.log('Text copied to clipboard'); + }, function(err) { + + let originalColor = input.style.borderColor; + input.style.borderColor = "red"; + setTimeout(function(){ + input.style.borderColor = originalColor; + },500) + + console.error('Failed to copy text: ', err); + }); + }); + + } +} + +function buildEntry(entry) { + + let nameDiv = document.createElement("div"); + nameDiv.setAttribute("class", "entry_name"); + nameDiv.setAttribute("id",`${entry.replaceAll(" ","-")}_name`) + + let nameInput = document.createElement("input"); + nameInput.setAttribute("type", "text"); + nameInput.setAttribute("placeholder", "New Entry Name"); + nameInput.setAttribute("value", entry); + nameInput.setAttribute("readonly", true); + nameInput.setAttribute("unselectable", "on") + nameDiv.appendChild(nameInput); + + let userDiv = document.createElement("div"); + userDiv.setAttribute("class", "entry_user"); + userDiv.setAttribute("id",`${entry.replaceAll(" ","-")}_user`) + + let userInput = document.createElement("input"); + userInput.setAttribute("type", "password"); + userInput.setAttribute("placeholder", "New Username"); + userInput.setAttribute("value", "PLACEHOLDER"); + userInput.setAttribute("readonly", true); + userInput.setAttribute("unselectable", "on") + userDiv.appendChild(userInput); + + let passDiv = document.createElement("div"); + passDiv.setAttribute("class", "entry_pass"); + passDiv.setAttribute("id",`${entry.replaceAll(" ","-")}_pass`) + + let passInput = document.createElement("input"); + passInput.setAttribute("type", "password"); + passInput.setAttribute("placeholder", "New Password"); + passInput.setAttribute("value", "PLACEHOLDER!"); + passInput.setAttribute("readonly", true); + passInput.setAttribute("unselectable", "on") + passDiv.appendChild(passInput); + + let showButton = document.createElement("button"); + showButton.innerText = "Show"; + showButton.addEventListener("click",showEntry.bind(showButton, entry),false); + showButton.setAttribute("class", "showbutton"); + + let editButton = document.createElement("button"); + editButton.innerText = "Edit"; + editButton.addEventListener("click", editEntry.bind(editButton, entry), false); + editButton.setAttribute("class", "editbutton"); + + let actionDiv = document.createElement("div") + actionDiv.appendChild(showButton) + actionDiv.appendChild(editButton) + actionDiv.setAttribute("id",`${entry.replaceAll(" ","-")}_actions`) + + document.getElementById("table_entries").appendChild(nameDiv) + document.getElementById("table_users").appendChild(userDiv) + document.getElementById("table_pwds").appendChild(passDiv) + document.getElementById("table_actions").appendChild(actionDiv) + +} + +async function editEntry(entry) { + let entry_user = document.querySelector(`#${entry.replaceAll(" ","-")}_user > input`); + let entry_pass = document.querySelector(`#${entry.replaceAll(" ","-")}_pass > input`); + let entry_name = document.querySelector(`#${entry.replaceAll(" ","-")}_name > input`); + let show_button = document.querySelector(`#${entry.replaceAll(" ","-")}_actions > .showbutton`); + if(this.innerText == "Edit") { + + let info = await invoke("get_entry",{ + name: entry_name.value, + mpw: await get_pw() + }) + + entry_user.value = info[0]; + entry_pass.value = info[1]; + + entry_user.removeAttribute("readonly"); + entry_user.setAttribute("unselectable", "off") + entry_user.type = "text"; + entry_pass.removeAttribute("readonly"); + entry_pass.setAttribute("unselectable", "off") + entry_pass.type = "text"; + entry_name.removeAttribute("readonly"); + entry_name.setAttribute("unselectable", "off") + show_button.disabled = true; + this.innerText = "Save"; + + this.Name = entry_name.value + } else { + + //To Delete: this.Name + + let isDeleted = await invoke("remove_entry", {name: this.Name}) + if(!isDeleted) { + alert("Could not edit entry!") + return; + } + + let success = await invoke("create_entry", {name: entry_name.value, username: entry_user.value, pw: entry_pass.value, mpw: await get_pw()}); + if(!success) { + alert("Could not edit entry!") + return; + } + + + entry_user.setAttribute("readonly", true); + entry_user.setAttribute("unselectable", "off") + entry_user.type = "password" + entry_user.value = "PLACEHOLDER" + entry_pass.setAttribute("readonly", true); + entry_pass.setAttribute("unselectable", "off") + entry_pass.type = "password" + entry_pass.value = "PLACEHOLDER!" + entry_name.setAttribute("readonly", true); + entry_name.setAttribute("unselectable", "off") + show_button.disabled = false; + this.innerText = "Edit"; + } +} + +async function ask_pw() { + return await password_prompt("Enter your master password to proceed"); +} + +async function get_pw() { + let mpw = master_pw; + + if(lock_status){ + mpw = await ask_pw(); + } + + return mpw; +} + +async function showEntry(entry_name) { + let entry_user = document.querySelector(`#${entry_name.replaceAll(" ","-")}_user > input`); + let entry_pass = document.querySelector(`#${entry_name.replaceAll(" ","-")}_pass > input`); + + if(this.innerText == "Show"){ + let info = await invoke("get_entry",{ + name: entry_name, + mpw: await get_pw() + }) + entry_user.value = info[0]; + entry_pass.value = info[1]; + entry_user.type = "text"; + entry_pass.type = "text"; + this.innerText = "Hide"; + } else { + entry_user.value = "PLACEHOLDER"; + entry_pass.value = "PLACEHOLDER!"; + entry_user.type = "password"; + entry_pass.type = "password"; + this.innerText = "Show"; + } +} + + + +async function createEntry() { + let entryNameField = document.querySelector("#createEntry_name > input"); + let entryUserField = document.querySelector("#createEntry_user > input"); + let entryPassField = document.querySelector("#createEntry_pass > input"); + + if(entryNameField.value == "" || entryUserField.value == "") { + alert("Not all needed fields filled out!"); + return; + } + + if(entryPassField.value == "") { + if(!(confirm("No password provided, do you want to generate a secure password?"))) { + alert("Cant create entry without password!"); + return; + } + entryPassField.value = await invoke("random_password"); + } + + let mpw = await get_pw() + + let success = await invoke("create_entry", {name: entryNameField.value, username: entryNameField.value, pw: entryPassField.value, mpw: mpw}); + + if(success) { + alert("Successfully created entry!"); + buildEntry(entryNameField.value) + entryNameField.value = "" + entryUserField.value = "" + entryPassField.value = "" + } else { + alert("A critical error occured during entry creation"); + } + +} + +async function toggleLock() { + let txt = (lock_status && "Unlocked") || "Locked"; + let src = (lock_status && "/images/security_lock_unlocked.png") || "/images/security_lock_locked.png"; + + if(lock_status){ + master_pw = await ask_pw(); + console.log(master_pw) + if(master_pw == "" || master_pw == null)return; + } + + document.getElementById("lockLabel").innerText = txt; + document.getElementById("lockImg").src = src; + + lock_status = !lock_status; +} \ No newline at end of file diff --git a/src/pwprompt.js b/src/pwprompt.js new file mode 100644 index 0000000..07c8567 --- /dev/null +++ b/src/pwprompt.js @@ -0,0 +1,59 @@ +window.password_prompt = async function(label_message, button_message, arg3, arg4, arg5) { + return new Promise((res,rej) => { + let callback,width,height; + if (typeof label_message !== "string") label_message = "Password:"; + if (typeof button_message !== "string") button_message = "Submit"; + if (typeof arg3 === "function") { + callback = arg3; + } + else if (typeof arg3 === "number" && typeof arg4 === "number" && typeof arg5 === "function") { + width = arg3; + height = arg4; + callback = arg5; + } + if (typeof width !== "number") width = 200; + if (typeof height !== "number") height = 100; + if (typeof callback !== "function") callback = function(){}; + + let submit = function() { + callback(input.value); + document.body.removeChild(div); + window.removeEventListener("resize", resize, false); + res(input.value) + }; + let resize = function() { + div.style.left = ((window.innerWidth / 2) - (width / 2)) + "px"; + div.style.top = ((window.innerHeight / 2) - (height / 2)) + "px"; + }; + + let div = document.createElement("div"); + div.id = "password_prompt"; + + + let label = document.createElement("label"); + label.id = "password_prompt_label"; + label.innerHTML = label_message; + label.for = "password_prompt_input"; + div.appendChild(label); + + div.appendChild(document.createElement("br")); + + let input = document.createElement("input"); + input.id = "password_prompt_input"; + input.type = "password"; + input.addEventListener("keyup", function(event) { + if (event.key == "Enter") submit(); + }, false); + div.appendChild(input); + + div.appendChild(document.createElement("br")); + + let button = document.createElement("button"); + button.innerHTML = button_message; + button.addEventListener("click", submit, false); + div.appendChild(button); + + document.body.appendChild(div); + window.addEventListener("resize", resize, false); + }) +}; \ No newline at end of file diff --git a/src/style.css b/src/style.css index 027ae55..b6d7f13 100644 --- a/src/style.css +++ b/src/style.css @@ -1,5 +1,5 @@ :root { - font-family: Inter, Avenir, Helvetica, Arial, sans-serif; + font-family: MonoLisa, Inter, Avenir, Helvetica, Arial, sans-serif; font-size: 16px; line-height: 24px; font-weight: 400; @@ -16,7 +16,6 @@ --fg-color: #303034; /* post background */ --bg-color: #1B1B1E; /* page background etc */ --text-color: #ECEAF1; /* text */ - --blue-ish: #587291; /* buttons etc */ color: var(--text-color); background-color: var(--bg-color); @@ -28,48 +27,55 @@ a:hover { input, button { - color: #ffffff; - background-color: #0f0f0f98; -} + color: var(--text-color); + background-color: var(--fg-color); -.container { - margin: 0; - padding-top: 10vh; - display: flex; - flex-direction: column; - justify-content: center; - text-align: center; -} - -.row { - display: flex; - justify-content: center; -} - -a { + border-radius: 8px; + border: 1px solid transparent; font-weight: 500; - color: #646cff; - text-decoration: inherit; + font-family: inherit; + transition: border-color 0.25s; + box-shadow: 0 2px 2px rgba(0, 0, 0, 0.2); +} + +@media only screen and (max-width: 600px) { + input, + button { + padding: 4px 8px; + font-size: 0.8em; + } +} + +@media only screen and (min-width: 600px) and (max-width: 900px) { + input, + button { + padding: 5px 10px; + font-size: 0.9em; + } +} + +@media only screen and (min-width: 900px) { + input, + button { + padding: 6.4px 12.8px; + font-size: 1em; + } +} + +tr { + width: 50%; +} + + +input:not([readonly]){ + color: var(--fg-color); + background-color: var(--text-color); } h1 { text-align: center; } -input, -button { - border-radius: 8px; - border: 1px solid transparent; - padding: 0.6em 1.2em; - font-size: 1em; - font-weight: 500; - font-family: inherit; - color: #0f0f0f; - background-color: #ffffff; - transition: border-color 0.25s; - box-shadow: 0 2px 2px rgba(0, 0, 0, 0.2); -} - button { cursor: pointer; } @@ -83,6 +89,54 @@ button { outline: none; } -#greet-input { - margin-right: 5px; +#lockImg { + position: absolute; + top: 1em; + right: 1em; +} + +#lockLabel { + position: absolute; + top: 1em; + right: 4em; +} + +#table_div { + display: flex; + flex-direction: row; + flex-wrap: nowrap; + justify-content: center; +} + +.entry_name,.entry_user,.entry_pass { + width: 70%; +} + +.entry_name > input,.entry_user > input,.entry_pass > input { + width: 100%; +} + +.table_actions>div>button { + height: 40%; +} + +input[type="password"] { + user-select: none; +} + +#password_prompt { + background: white; + color: black; + border: 1px solid black; + width: 50%; + height: 25%; + position: fixed; + left: 25%; + top: 25%; + display: flex; + justify-content: center; + align-items: center; + flex-direction: column; + border: 3px double var(--fg-color); + border-radius: 5%; } \ No newline at end of file