@parlor/shell-desktop 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +10 -2
- package/assets/icons/128x128.png +0 -0
- package/assets/icons/128x128@2x.png +0 -0
- package/assets/icons/32x32.png +0 -0
- package/assets/icons/icon.icns +0 -0
- package/assets/icons/icon.ico +0 -0
- package/dist/build.d.ts +3 -0
- package/dist/build.d.ts.map +1 -0
- package/dist/build.js +18 -0
- package/dist/build.js.map +1 -0
- package/dist/dev.d.ts +9 -0
- package/dist/dev.d.ts.map +1 -0
- package/dist/dev.js +28 -0
- package/dist/dev.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/prerequisites.d.ts +4 -0
- package/dist/prerequisites.d.ts.map +1 -0
- package/dist/prerequisites.js +12 -0
- package/dist/prerequisites.js.map +1 -0
- package/dist/project.d.ts +48 -0
- package/dist/project.d.ts.map +1 -0
- package/dist/project.js +243 -0
- package/dist/project.js.map +1 -0
- package/dist/target-triple.d.ts +3 -0
- package/dist/target-triple.d.ts.map +1 -0
- package/dist/target-triple.js +10 -0
- package/dist/target-triple.js.map +1 -0
- package/dist/tauri-cli.d.ts +9 -0
- package/dist/tauri-cli.d.ts.map +1 -0
- package/dist/tauri-cli.js +42 -0
- package/dist/tauri-cli.js.map +1 -0
- package/package.json +45 -3
- package/rust/Cargo.toml +26 -0
- package/rust/src/control.rs +104 -0
- package/rust/src/device_key.rs +282 -0
- package/rust/src/dialog.rs +22 -0
- package/rust/src/host.rs +338 -0
- package/rust/src/keystore.rs +442 -0
- package/rust/src/lib.rs +20 -0
- package/rust/src/secret_store.rs +42 -0
- package/rust/src/session.rs +100 -0
- package/rust/src/vault.rs +133 -0
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
//! A build without an Apple Team ID (ad hoc or self-signed) keeps the device keys and the session in this 0600 file:
|
|
2
|
+
//! the Keychain partitions such a build's items by its cdhash, which every rebuild changes, so each rebuild would ask
|
|
3
|
+
//! for the login password.
|
|
4
|
+
|
|
5
|
+
use std::collections::BTreeMap;
|
|
6
|
+
use std::fs::{self, DirBuilder, OpenOptions};
|
|
7
|
+
use std::io::Write;
|
|
8
|
+
use std::os::unix::fs::{DirBuilderExt, OpenOptionsExt};
|
|
9
|
+
use std::path::{Path, PathBuf};
|
|
10
|
+
use std::sync::Mutex;
|
|
11
|
+
|
|
12
|
+
const FILE: &str = "parlor-vault.json";
|
|
13
|
+
|
|
14
|
+
static WRITING: Mutex<()> = Mutex::new(());
|
|
15
|
+
|
|
16
|
+
fn file_in(home: &Path, identifier: &str) -> PathBuf {
|
|
17
|
+
home.join("Library/Application Support")
|
|
18
|
+
.join(identifier)
|
|
19
|
+
.join(FILE)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
fn file(identifier: &str) -> Result<PathBuf, String> {
|
|
23
|
+
let home = std::env::var_os("HOME").ok_or("HOME is not set.")?;
|
|
24
|
+
Ok(file_in(Path::new(&home), identifier))
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
fn read(path: &Path) -> Result<BTreeMap<String, String>, String> {
|
|
28
|
+
match fs::read(path) {
|
|
29
|
+
Ok(bytes) => serde_json::from_slice(&bytes).map_err(|e| e.to_string()),
|
|
30
|
+
Err(error) if error.kind() == std::io::ErrorKind::NotFound => Ok(BTreeMap::new()),
|
|
31
|
+
Err(error) => Err(error.to_string()),
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
fn write(path: &Path, entries: &BTreeMap<String, String>) -> Result<(), String> {
|
|
36
|
+
let dir = path.parent().ok_or("The vault has no folder.")?;
|
|
37
|
+
DirBuilder::new()
|
|
38
|
+
.recursive(true)
|
|
39
|
+
.mode(0o700)
|
|
40
|
+
.create(dir)
|
|
41
|
+
.map_err(|e| e.to_string())?;
|
|
42
|
+
let staged = dir.join(format!(".{FILE}.{}", std::process::id()));
|
|
43
|
+
let mut out = OpenOptions::new()
|
|
44
|
+
.write(true)
|
|
45
|
+
.create(true)
|
|
46
|
+
.truncate(true)
|
|
47
|
+
.mode(0o600)
|
|
48
|
+
.open(&staged)
|
|
49
|
+
.map_err(|e| e.to_string())?;
|
|
50
|
+
out.write_all(&serde_json::to_vec(entries).map_err(|e| e.to_string())?)
|
|
51
|
+
.and_then(|()| out.sync_all())
|
|
52
|
+
.map_err(|e| e.to_string())?;
|
|
53
|
+
fs::rename(&staged, path).map_err(|e| e.to_string())
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
fn get_at(path: &Path, entry: &str) -> Result<Option<String>, String> {
|
|
57
|
+
Ok(read(path)?.remove(entry))
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
fn set_at(path: &Path, entry: &str, value: Option<&str>) -> Result<(), String> {
|
|
61
|
+
let _writing = WRITING.lock().map_err(|e| e.to_string())?;
|
|
62
|
+
let mut entries = read(path)?;
|
|
63
|
+
match value {
|
|
64
|
+
Some(value) => entries.insert(entry.to_string(), value.to_string()),
|
|
65
|
+
None => entries.remove(entry),
|
|
66
|
+
};
|
|
67
|
+
write(path, &entries)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
pub fn get(identifier: &str, entry: &str) -> Result<Option<String>, String> {
|
|
71
|
+
get_at(&file(identifier)?, entry)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
pub fn set(identifier: &str, entry: &str, value: Option<&str>) -> Result<(), String> {
|
|
75
|
+
set_at(&file(identifier)?, entry, value)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
#[cfg(test)]
|
|
79
|
+
mod tests {
|
|
80
|
+
use super::*;
|
|
81
|
+
use std::os::unix::fs::PermissionsExt;
|
|
82
|
+
|
|
83
|
+
fn scratch() -> PathBuf {
|
|
84
|
+
let dir = std::env::temp_dir().join(format!(
|
|
85
|
+
"parlor-vault-{}-{}",
|
|
86
|
+
std::process::id(),
|
|
87
|
+
std::time::SystemTime::now()
|
|
88
|
+
.duration_since(std::time::UNIX_EPOCH)
|
|
89
|
+
.unwrap()
|
|
90
|
+
.as_nanos()
|
|
91
|
+
));
|
|
92
|
+
file_in(&dir, "dev.parlor.test")
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
#[test]
|
|
96
|
+
fn an_entry_is_kept_until_it_is_cleared() {
|
|
97
|
+
let path = scratch();
|
|
98
|
+
assert_eq!(get_at(&path, "session").unwrap(), None);
|
|
99
|
+
set_at(&path, "session", Some("token-1")).unwrap();
|
|
100
|
+
set_at(&path, "signing", Some("key-1")).unwrap();
|
|
101
|
+
assert_eq!(
|
|
102
|
+
get_at(&path, "session").unwrap(),
|
|
103
|
+
Some("token-1".to_string())
|
|
104
|
+
);
|
|
105
|
+
set_at(&path, "session", None).unwrap();
|
|
106
|
+
assert_eq!(get_at(&path, "session").unwrap(), None);
|
|
107
|
+
assert_eq!(get_at(&path, "signing").unwrap(), Some("key-1".to_string()));
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
#[test]
|
|
111
|
+
fn only_the_user_can_read_the_vault() {
|
|
112
|
+
let path = scratch();
|
|
113
|
+
set_at(&path, "session", Some("token-1")).unwrap();
|
|
114
|
+
let file = fs::metadata(&path).unwrap().permissions().mode() & 0o777;
|
|
115
|
+
let dir = fs::metadata(path.parent().unwrap())
|
|
116
|
+
.unwrap()
|
|
117
|
+
.permissions()
|
|
118
|
+
.mode()
|
|
119
|
+
& 0o777;
|
|
120
|
+
assert_eq!(file, 0o600);
|
|
121
|
+
assert_eq!(dir, 0o700);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
#[test]
|
|
125
|
+
fn the_vault_lives_in_the_app_support_folder_of_the_app() {
|
|
126
|
+
assert_eq!(
|
|
127
|
+
file_in(Path::new("/Users/alice"), "dev.parlor.hello"),
|
|
128
|
+
PathBuf::from(
|
|
129
|
+
"/Users/alice/Library/Application Support/dev.parlor.hello/parlor-vault.json"
|
|
130
|
+
)
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
}
|