vaultfs 1.0.3 → 1.0.5
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/README.md +139 -232
- package/bin/postinstall.js +138 -129
- package/bin/vaultfs-npm.js +148 -147
- package/frontend/package.json +4 -4
- package/package.json +34 -34
- package/src/auth/AuthConfig.java +9 -9
- package/src/auth/AuthManager.java +267 -267
- package/version.txt +1 -1
package/package.json
CHANGED
|
@@ -1,34 +1,34 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "vaultfs",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "A CLI-based secure file system simulator with OAuth 2.0 authentication and advanced data structures",
|
|
5
|
-
"bin": {
|
|
6
|
-
"vaultfs": "bin/vaultfs-npm.js"
|
|
7
|
-
},
|
|
8
|
-
"scripts": {
|
|
9
|
-
"postinstall": "node ./bin/postinstall.js"
|
|
10
|
-
},
|
|
11
|
-
"keywords": [
|
|
12
|
-
"cli",
|
|
13
|
-
"filesystem",
|
|
14
|
-
"oauth",
|
|
15
|
-
"java",
|
|
16
|
-
"security",
|
|
17
|
-
"data-structures"
|
|
18
|
-
],
|
|
19
|
-
"author": "ThreatGuardian",
|
|
20
|
-
"license": "MIT",
|
|
21
|
-
"engines": {
|
|
22
|
-
"node": ">=18.0.0"
|
|
23
|
-
},
|
|
24
|
-
"files": [
|
|
25
|
-
"bin/",
|
|
26
|
-
"src/",
|
|
27
|
-
"frontend/",
|
|
28
|
-
"version.txt",
|
|
29
|
-
".env.example",
|
|
30
|
-
"install.sh",
|
|
31
|
-
"install.bat",
|
|
32
|
-
"uninstall.sh"
|
|
33
|
-
]
|
|
34
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "vaultfs",
|
|
3
|
+
"version": "1.0.5",
|
|
4
|
+
"description": "A CLI-based secure file system simulator with OAuth 2.0 authentication and advanced data structures",
|
|
5
|
+
"bin": {
|
|
6
|
+
"vaultfs": "bin/vaultfs-npm.js"
|
|
7
|
+
},
|
|
8
|
+
"scripts": {
|
|
9
|
+
"postinstall": "node ./bin/postinstall.js"
|
|
10
|
+
},
|
|
11
|
+
"keywords": [
|
|
12
|
+
"cli",
|
|
13
|
+
"filesystem",
|
|
14
|
+
"oauth",
|
|
15
|
+
"java",
|
|
16
|
+
"security",
|
|
17
|
+
"data-structures"
|
|
18
|
+
],
|
|
19
|
+
"author": "ThreatGuardian",
|
|
20
|
+
"license": "MIT",
|
|
21
|
+
"engines": {
|
|
22
|
+
"node": ">=18.0.0"
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"bin/",
|
|
26
|
+
"src/",
|
|
27
|
+
"frontend/",
|
|
28
|
+
"version.txt",
|
|
29
|
+
".env.example",
|
|
30
|
+
"install.sh",
|
|
31
|
+
"install.bat",
|
|
32
|
+
"uninstall.sh"
|
|
33
|
+
]
|
|
34
|
+
}
|
package/src/auth/AuthConfig.java
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
package auth;
|
|
2
|
-
|
|
3
|
-
/** Centralized configuration for VaultFS remote authentication. */
|
|
4
|
-
public class AuthConfig {
|
|
5
|
-
public static final String AUTH_SERVER_URL = "https://vaultfs-auth-server.onrender.com";
|
|
6
|
-
public static final int LOCAL_PORT = 9000;
|
|
7
|
-
public static final int POLL_INTERVAL_MS = 2000;
|
|
8
|
-
public static final int LOGIN_TIMEOUT_SECONDS = 120;
|
|
9
|
-
}
|
|
1
|
+
package auth;
|
|
2
|
+
|
|
3
|
+
/** Centralized configuration for VaultFS remote authentication. */
|
|
4
|
+
public class AuthConfig {
|
|
5
|
+
public static final String AUTH_SERVER_URL = "https://vaultfs-auth-server.onrender.com";
|
|
6
|
+
public static final int LOCAL_PORT = 9000;
|
|
7
|
+
public static final int POLL_INTERVAL_MS = 2000;
|
|
8
|
+
public static final int LOGIN_TIMEOUT_SECONDS = 120;
|
|
9
|
+
}
|
|
@@ -1,267 +1,267 @@
|
|
|
1
|
-
package auth;
|
|
2
|
-
|
|
3
|
-
import java.io.BufferedReader;
|
|
4
|
-
import java.io.File;
|
|
5
|
-
import java.io.FileReader;
|
|
6
|
-
import java.io.FileWriter;
|
|
7
|
-
import java.io.IOException;
|
|
8
|
-
import java.io.InputStream;
|
|
9
|
-
import java.net.HttpURLConnection;
|
|
10
|
-
import java.net.URI;
|
|
11
|
-
import java.net.URL;
|
|
12
|
-
import java.util.UUID;
|
|
13
|
-
|
|
14
|
-
import utils.Colors;
|
|
15
|
-
|
|
16
|
-
/** Manages VaultFS login state, device identity, and account display.
|
|
17
|
-
* Authentication is delegated to a remote auth server; the CLI polls for results. */
|
|
18
|
-
public class AuthManager {
|
|
19
|
-
|
|
20
|
-
private static final String TOKEN_DIR = System.getProperty("user.home") + "/.authfs";
|
|
21
|
-
private static final String TOKEN_FILE = TOKEN_DIR + "/token";
|
|
22
|
-
private static final String EMAIL_FILE = TOKEN_DIR + "/email";
|
|
23
|
-
private static final String NAME_FILE = TOKEN_DIR + "/name";
|
|
24
|
-
private static final String CONFIG_FILE = TOKEN_DIR + "/config";
|
|
25
|
-
|
|
26
|
-
/** Returns whether a non-empty token file exists. */
|
|
27
|
-
public static boolean isLoggedIn() {
|
|
28
|
-
File token = new File(TOKEN_FILE);
|
|
29
|
-
return token.exists() && token.length() > 0;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/** Returns the saved user email or Unknown when not available. */
|
|
33
|
-
public static String getUserEmail() {
|
|
34
|
-
String email = readFile(EMAIL_FILE);
|
|
35
|
-
if (email == null || email.isEmpty()) {
|
|
36
|
-
return "Unknown";
|
|
37
|
-
}
|
|
38
|
-
return email;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/** Returns the saved user name or Unknown when not available. */
|
|
42
|
-
public static String getUserName() {
|
|
43
|
-
String name = readFile(NAME_FILE);
|
|
44
|
-
if (name == null || name.isEmpty()) {
|
|
45
|
-
return "Unknown";
|
|
46
|
-
}
|
|
47
|
-
return name;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/** Returns the device ID from config or generates and persists a new UUID. */
|
|
51
|
-
public static String getDeviceId() {
|
|
52
|
-
String deviceId = readFile(CONFIG_FILE);
|
|
53
|
-
if (deviceId != null && !deviceId.isEmpty()) {
|
|
54
|
-
return deviceId;
|
|
55
|
-
}
|
|
56
|
-
new File(TOKEN_DIR).mkdirs();
|
|
57
|
-
String generated = UUID.randomUUID().toString();
|
|
58
|
-
writeFile(CONFIG_FILE, generated);
|
|
59
|
-
return generated;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/** Starts the remote auth-server login flow with browser + polling. */
|
|
63
|
-
public static void startLoginFlow() {
|
|
64
|
-
try {
|
|
65
|
-
// Step 1 — Get a session ID from auth server
|
|
66
|
-
String sessionId = getNewSession();
|
|
67
|
-
if (sessionId == null) {
|
|
68
|
-
System.out.println(Colors.c(Colors.YELLOW, "\u26A0\uFE0F Auth server unreachable. Continuing as Guest."));
|
|
69
|
-
persistLogin(UUID.randomUUID().toString(), "guest@vaultfs.local", "Guest");
|
|
70
|
-
return;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// Step 2 — Build login URL
|
|
74
|
-
String loginUrl = AuthConfig.AUTH_SERVER_URL + "/auth/login?sessionId=" + sessionId;
|
|
75
|
-
|
|
76
|
-
// Step 3 — Print login box
|
|
77
|
-
System.out.println();
|
|
78
|
-
System.out.println(Colors.c(Colors.CYAN, " \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"));
|
|
79
|
-
System.out.println(Colors.c(Colors.CYAN, " \u2551 VaultFS \u2014 Login \u2551"));
|
|
80
|
-
System.out.println(Colors.c(Colors.CYAN, " \u2551 \u2551"));
|
|
81
|
-
System.out.println(Colors.c(Colors.CYAN, " \u2551 Opening browser for login... \u2551"));
|
|
82
|
-
System.out.println(Colors.c(Colors.CYAN, " \u2551 \u2551"));
|
|
83
|
-
System.out.println(Colors.c(Colors.CYAN, " \u2551 If browser doesn't open, visit: \u2551"));
|
|
84
|
-
System.out.println(Colors.c(Colors.CYAN, " \u2551 ") + Colors.c(Colors.GREEN, loginUrl));
|
|
85
|
-
System.out.println(Colors.c(Colors.CYAN, " \u2551 \u2551"));
|
|
86
|
-
System.out.println(Colors.c(Colors.CYAN, " \u2551 Press ENTER to skip and continue as Guest \u2551"));
|
|
87
|
-
System.out.println(Colors.c(Colors.CYAN, " \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
|
|
88
|
-
System.out.println();
|
|
89
|
-
|
|
90
|
-
// Step 4 — Open browser
|
|
91
|
-
openBrowser(loginUrl);
|
|
92
|
-
|
|
93
|
-
// Step 5 — Poll auth server for result
|
|
94
|
-
pollForLogin(sessionId);
|
|
95
|
-
|
|
96
|
-
} catch (Exception e) {
|
|
97
|
-
System.out.println(Colors.c(Colors.YELLOW, "\u26A0\uFE0F Login failed: " + e.getMessage()));
|
|
98
|
-
persistLogin(UUID.randomUUID().toString(), "guest@vaultfs.local", "Guest");
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
/** Requests a new auth session from the remote server. */
|
|
103
|
-
private static String getNewSession() {
|
|
104
|
-
try {
|
|
105
|
-
URL url = URI.create(AuthConfig.AUTH_SERVER_URL + "/auth/session/new").toURL();
|
|
106
|
-
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
|
107
|
-
conn.setRequestMethod("GET");
|
|
108
|
-
conn.setConnectTimeout(5000);
|
|
109
|
-
conn.setReadTimeout(5000);
|
|
110
|
-
String response = readStream(conn.getInputStream());
|
|
111
|
-
return extractJsonValue(response, "sessionId");
|
|
112
|
-
} catch (Exception e) {
|
|
113
|
-
return null;
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/** Polls the auth server for login completion, with ENTER-to-skip support. */
|
|
118
|
-
private static void pollForLogin(String sessionId) {
|
|
119
|
-
final boolean[] skipLogin = {false};
|
|
120
|
-
Thread inputThread = new Thread(() -> {
|
|
121
|
-
try {
|
|
122
|
-
System.in.read();
|
|
123
|
-
skipLogin[0] = true;
|
|
124
|
-
} catch (Exception ignored) {}
|
|
125
|
-
});
|
|
126
|
-
inputThread.setDaemon(true);
|
|
127
|
-
inputThread.start();
|
|
128
|
-
|
|
129
|
-
System.out.println(Colors.c(Colors.GRAY, "\uD83D\uDD10 Waiting for login... (press ENTER to skip)"));
|
|
130
|
-
System.out.println();
|
|
131
|
-
|
|
132
|
-
int waited = 0;
|
|
133
|
-
while (waited < AuthConfig.LOGIN_TIMEOUT_SECONDS && !skipLogin[0]) {
|
|
134
|
-
try {
|
|
135
|
-
Thread.sleep(AuthConfig.POLL_INTERVAL_MS);
|
|
136
|
-
waited += 2;
|
|
137
|
-
|
|
138
|
-
URL url = URI.create(AuthConfig.AUTH_SERVER_URL + "/auth/poll?sessionId=" + sessionId).toURL();
|
|
139
|
-
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
|
140
|
-
conn.setConnectTimeout(5000);
|
|
141
|
-
conn.setReadTimeout(5000);
|
|
142
|
-
String response = readStream(conn.getInputStream());
|
|
143
|
-
|
|
144
|
-
if (response.contains("\"status\":\"done\"")) {
|
|
145
|
-
String name = extractJsonValue(response, "name");
|
|
146
|
-
String email = extractJsonValue(response, "email");
|
|
147
|
-
String provider = extractJsonValue(response, "provider");
|
|
148
|
-
System.out.println(Colors.c(Colors.GREEN, "\u2713") + " Logged in as "
|
|
149
|
-
+ Colors.c(Colors.YELLOW, name) + " via " + provider);
|
|
150
|
-
persistLogin(UUID.randomUUID().toString(), email, name);
|
|
151
|
-
return;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
if (response.contains("\"status\":\"expired\"")) {
|
|
155
|
-
break;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
if (waited % 10 == 0) {
|
|
159
|
-
System.out.println(Colors.c(Colors.GRAY, " Still waiting... ("
|
|
160
|
-
+ (AuthConfig.LOGIN_TIMEOUT_SECONDS - waited) + "s remaining, press ENTER to skip)"));
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
} catch (Exception ignored) {}
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
System.out.println(Colors.c(Colors.YELLOW, "\u26A0\uFE0F Login skipped. Continuing as Guest."));
|
|
167
|
-
persistLogin(UUID.randomUUID().toString(), "guest@vaultfs.local", "Guest");
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
/** Opens a URL in the default browser using platform-specific commands. */
|
|
171
|
-
private static void openBrowser(String url) {
|
|
172
|
-
String os = System.getProperty("os.name").toLowerCase();
|
|
173
|
-
Runtime rt = Runtime.getRuntime();
|
|
174
|
-
try {
|
|
175
|
-
if (os.contains("win")) {
|
|
176
|
-
rt.exec(new String[]{"rundll32", "url.dll,FileProtocolHandler", url});
|
|
177
|
-
} else if (os.contains("mac")) {
|
|
178
|
-
rt.exec(new String[]{"open", url});
|
|
179
|
-
} else {
|
|
180
|
-
String[] browsers = {"xdg-open", "firefox", "google-chrome", "chromium-browser"};
|
|
181
|
-
boolean opened = false;
|
|
182
|
-
for (String browser : browsers) {
|
|
183
|
-
try {
|
|
184
|
-
rt.exec(new String[]{browser, url});
|
|
185
|
-
opened = true;
|
|
186
|
-
break;
|
|
187
|
-
} catch (Exception ignored) {}
|
|
188
|
-
}
|
|
189
|
-
if (!opened) {
|
|
190
|
-
System.out.println("Please open this URL manually: " + url);
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
} catch (Exception e) {
|
|
194
|
-
System.out.println("Could not open browser automatically.");
|
|
195
|
-
System.out.println("Please open this URL manually: " + url);
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
/** Clears local auth files and logs the user out. */
|
|
200
|
-
public static void logout() {
|
|
201
|
-
new File(TOKEN_FILE).delete();
|
|
202
|
-
new File(EMAIL_FILE).delete();
|
|
203
|
-
new File(NAME_FILE).delete();
|
|
204
|
-
System.out.println(Colors.c(Colors.GREEN, "\u2713") + " Logged out successfully");
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
/** Prints formatted account details when logged in. */
|
|
208
|
-
public static void whoami() {
|
|
209
|
-
if (!isLoggedIn()) {
|
|
210
|
-
System.out.println(Colors.c(Colors.RED, "Not logged in."));
|
|
211
|
-
return;
|
|
212
|
-
}
|
|
213
|
-
System.out.println(Colors.c(Colors.GRAY, "==== Account Details ===="));
|
|
214
|
-
System.out.println("Email : " + Colors.c(Colors.YELLOW, getUserEmail()));
|
|
215
|
-
System.out.println("Device ID: " + Colors.c(Colors.CYAN, getDeviceId()));
|
|
216
|
-
System.out.println("Status : " + Colors.c(Colors.GREEN, "\u25CF Online"));
|
|
217
|
-
System.out.println(Colors.c(Colors.GRAY, "========================="));
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
/** Writes file content to the given path and reports failures. */
|
|
221
|
-
private static void writeFile(String path, String content) {
|
|
222
|
-
try (FileWriter writer = new FileWriter(path)) {
|
|
223
|
-
writer.write(content == null ? "" : content);
|
|
224
|
-
} catch (IOException e) {
|
|
225
|
-
System.out.println(Colors.c(Colors.RED, "Failed to write file: " + path));
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
/** Reads and trims file content or returns null on failure. */
|
|
230
|
-
private static String readFile(String path) {
|
|
231
|
-
try (BufferedReader reader = new BufferedReader(new FileReader(path))) {
|
|
232
|
-
StringBuilder sb = new StringBuilder();
|
|
233
|
-
String line;
|
|
234
|
-
while ((line = reader.readLine()) != null) {
|
|
235
|
-
sb.append(line);
|
|
236
|
-
}
|
|
237
|
-
return sb.toString().trim();
|
|
238
|
-
} catch (IOException e) {
|
|
239
|
-
return null;
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
/** Reads all bytes from an InputStream and returns as a String. */
|
|
244
|
-
private static String readStream(InputStream is) throws IOException {
|
|
245
|
-
return new String(is.readAllBytes());
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
/** Extracts a simple JSON string value by key. */
|
|
249
|
-
private static String extractJsonValue(String json, String key) {
|
|
250
|
-
try {
|
|
251
|
-
String search = "\"" + key + "\":\"";
|
|
252
|
-
int start = json.indexOf(search) + search.length();
|
|
253
|
-
int end = json.indexOf("\"", start);
|
|
254
|
-
return json.substring(start, end);
|
|
255
|
-
} catch (Exception e) {
|
|
256
|
-
return "unknown";
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
/** Persists login credentials to local auth files. */
|
|
261
|
-
private static void persistLogin(String token, String email, String name) {
|
|
262
|
-
new File(TOKEN_DIR).mkdirs();
|
|
263
|
-
writeFile(TOKEN_FILE, token);
|
|
264
|
-
writeFile(EMAIL_FILE, email);
|
|
265
|
-
writeFile(NAME_FILE, name);
|
|
266
|
-
}
|
|
267
|
-
}
|
|
1
|
+
package auth;
|
|
2
|
+
|
|
3
|
+
import java.io.BufferedReader;
|
|
4
|
+
import java.io.File;
|
|
5
|
+
import java.io.FileReader;
|
|
6
|
+
import java.io.FileWriter;
|
|
7
|
+
import java.io.IOException;
|
|
8
|
+
import java.io.InputStream;
|
|
9
|
+
import java.net.HttpURLConnection;
|
|
10
|
+
import java.net.URI;
|
|
11
|
+
import java.net.URL;
|
|
12
|
+
import java.util.UUID;
|
|
13
|
+
|
|
14
|
+
import utils.Colors;
|
|
15
|
+
|
|
16
|
+
/** Manages VaultFS login state, device identity, and account display.
|
|
17
|
+
* Authentication is delegated to a remote auth server; the CLI polls for results. */
|
|
18
|
+
public class AuthManager {
|
|
19
|
+
|
|
20
|
+
private static final String TOKEN_DIR = System.getProperty("user.home") + "/.authfs";
|
|
21
|
+
private static final String TOKEN_FILE = TOKEN_DIR + "/token";
|
|
22
|
+
private static final String EMAIL_FILE = TOKEN_DIR + "/email";
|
|
23
|
+
private static final String NAME_FILE = TOKEN_DIR + "/name";
|
|
24
|
+
private static final String CONFIG_FILE = TOKEN_DIR + "/config";
|
|
25
|
+
|
|
26
|
+
/** Returns whether a non-empty token file exists. */
|
|
27
|
+
public static boolean isLoggedIn() {
|
|
28
|
+
File token = new File(TOKEN_FILE);
|
|
29
|
+
return token.exists() && token.length() > 0;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Returns the saved user email or Unknown when not available. */
|
|
33
|
+
public static String getUserEmail() {
|
|
34
|
+
String email = readFile(EMAIL_FILE);
|
|
35
|
+
if (email == null || email.isEmpty()) {
|
|
36
|
+
return "Unknown";
|
|
37
|
+
}
|
|
38
|
+
return email;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Returns the saved user name or Unknown when not available. */
|
|
42
|
+
public static String getUserName() {
|
|
43
|
+
String name = readFile(NAME_FILE);
|
|
44
|
+
if (name == null || name.isEmpty()) {
|
|
45
|
+
return "Unknown";
|
|
46
|
+
}
|
|
47
|
+
return name;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Returns the device ID from config or generates and persists a new UUID. */
|
|
51
|
+
public static String getDeviceId() {
|
|
52
|
+
String deviceId = readFile(CONFIG_FILE);
|
|
53
|
+
if (deviceId != null && !deviceId.isEmpty()) {
|
|
54
|
+
return deviceId;
|
|
55
|
+
}
|
|
56
|
+
new File(TOKEN_DIR).mkdirs();
|
|
57
|
+
String generated = UUID.randomUUID().toString();
|
|
58
|
+
writeFile(CONFIG_FILE, generated);
|
|
59
|
+
return generated;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Starts the remote auth-server login flow with browser + polling. */
|
|
63
|
+
public static void startLoginFlow() {
|
|
64
|
+
try {
|
|
65
|
+
// Step 1 — Get a session ID from auth server
|
|
66
|
+
String sessionId = getNewSession();
|
|
67
|
+
if (sessionId == null) {
|
|
68
|
+
System.out.println(Colors.c(Colors.YELLOW, "\u26A0\uFE0F Auth server unreachable. Continuing as Guest."));
|
|
69
|
+
persistLogin(UUID.randomUUID().toString(), "guest@vaultfs.local", "Guest");
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Step 2 — Build login URL
|
|
74
|
+
String loginUrl = AuthConfig.AUTH_SERVER_URL + "/auth/login?sessionId=" + sessionId;
|
|
75
|
+
|
|
76
|
+
// Step 3 — Print login box
|
|
77
|
+
System.out.println();
|
|
78
|
+
System.out.println(Colors.c(Colors.CYAN, " \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"));
|
|
79
|
+
System.out.println(Colors.c(Colors.CYAN, " \u2551 VaultFS \u2014 Login \u2551"));
|
|
80
|
+
System.out.println(Colors.c(Colors.CYAN, " \u2551 \u2551"));
|
|
81
|
+
System.out.println(Colors.c(Colors.CYAN, " \u2551 Opening browser for login... \u2551"));
|
|
82
|
+
System.out.println(Colors.c(Colors.CYAN, " \u2551 \u2551"));
|
|
83
|
+
System.out.println(Colors.c(Colors.CYAN, " \u2551 If browser doesn't open, visit: \u2551"));
|
|
84
|
+
System.out.println(Colors.c(Colors.CYAN, " \u2551 ") + Colors.c(Colors.GREEN, loginUrl));
|
|
85
|
+
System.out.println(Colors.c(Colors.CYAN, " \u2551 \u2551"));
|
|
86
|
+
System.out.println(Colors.c(Colors.CYAN, " \u2551 Press ENTER to skip and continue as Guest \u2551"));
|
|
87
|
+
System.out.println(Colors.c(Colors.CYAN, " \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
|
|
88
|
+
System.out.println();
|
|
89
|
+
|
|
90
|
+
// Step 4 — Open browser
|
|
91
|
+
openBrowser(loginUrl);
|
|
92
|
+
|
|
93
|
+
// Step 5 — Poll auth server for result
|
|
94
|
+
pollForLogin(sessionId);
|
|
95
|
+
|
|
96
|
+
} catch (Exception e) {
|
|
97
|
+
System.out.println(Colors.c(Colors.YELLOW, "\u26A0\uFE0F Login failed: " + e.getMessage()));
|
|
98
|
+
persistLogin(UUID.randomUUID().toString(), "guest@vaultfs.local", "Guest");
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** Requests a new auth session from the remote server. */
|
|
103
|
+
private static String getNewSession() {
|
|
104
|
+
try {
|
|
105
|
+
URL url = URI.create(AuthConfig.AUTH_SERVER_URL + "/auth/session/new").toURL();
|
|
106
|
+
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
|
107
|
+
conn.setRequestMethod("GET");
|
|
108
|
+
conn.setConnectTimeout(5000);
|
|
109
|
+
conn.setReadTimeout(5000);
|
|
110
|
+
String response = readStream(conn.getInputStream());
|
|
111
|
+
return extractJsonValue(response, "sessionId");
|
|
112
|
+
} catch (Exception e) {
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** Polls the auth server for login completion, with ENTER-to-skip support. */
|
|
118
|
+
private static void pollForLogin(String sessionId) {
|
|
119
|
+
final boolean[] skipLogin = {false};
|
|
120
|
+
Thread inputThread = new Thread(() -> {
|
|
121
|
+
try {
|
|
122
|
+
System.in.read();
|
|
123
|
+
skipLogin[0] = true;
|
|
124
|
+
} catch (Exception ignored) {}
|
|
125
|
+
});
|
|
126
|
+
inputThread.setDaemon(true);
|
|
127
|
+
inputThread.start();
|
|
128
|
+
|
|
129
|
+
System.out.println(Colors.c(Colors.GRAY, "\uD83D\uDD10 Waiting for login... (press ENTER to skip)"));
|
|
130
|
+
System.out.println();
|
|
131
|
+
|
|
132
|
+
int waited = 0;
|
|
133
|
+
while (waited < AuthConfig.LOGIN_TIMEOUT_SECONDS && !skipLogin[0]) {
|
|
134
|
+
try {
|
|
135
|
+
Thread.sleep(AuthConfig.POLL_INTERVAL_MS);
|
|
136
|
+
waited += 2;
|
|
137
|
+
|
|
138
|
+
URL url = URI.create(AuthConfig.AUTH_SERVER_URL + "/auth/poll?sessionId=" + sessionId).toURL();
|
|
139
|
+
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
|
140
|
+
conn.setConnectTimeout(5000);
|
|
141
|
+
conn.setReadTimeout(5000);
|
|
142
|
+
String response = readStream(conn.getInputStream());
|
|
143
|
+
|
|
144
|
+
if (response.contains("\"status\":\"done\"")) {
|
|
145
|
+
String name = extractJsonValue(response, "name");
|
|
146
|
+
String email = extractJsonValue(response, "email");
|
|
147
|
+
String provider = extractJsonValue(response, "provider");
|
|
148
|
+
System.out.println(Colors.c(Colors.GREEN, "\u2713") + " Logged in as "
|
|
149
|
+
+ Colors.c(Colors.YELLOW, name) + " via " + provider);
|
|
150
|
+
persistLogin(UUID.randomUUID().toString(), email, name);
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (response.contains("\"status\":\"expired\"")) {
|
|
155
|
+
break;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (waited % 10 == 0) {
|
|
159
|
+
System.out.println(Colors.c(Colors.GRAY, " Still waiting... ("
|
|
160
|
+
+ (AuthConfig.LOGIN_TIMEOUT_SECONDS - waited) + "s remaining, press ENTER to skip)"));
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
} catch (Exception ignored) {}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
System.out.println(Colors.c(Colors.YELLOW, "\u26A0\uFE0F Login skipped. Continuing as Guest."));
|
|
167
|
+
persistLogin(UUID.randomUUID().toString(), "guest@vaultfs.local", "Guest");
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/** Opens a URL in the default browser using platform-specific commands. */
|
|
171
|
+
private static void openBrowser(String url) {
|
|
172
|
+
String os = System.getProperty("os.name").toLowerCase();
|
|
173
|
+
Runtime rt = Runtime.getRuntime();
|
|
174
|
+
try {
|
|
175
|
+
if (os.contains("win")) {
|
|
176
|
+
rt.exec(new String[]{"rundll32", "url.dll,FileProtocolHandler", url});
|
|
177
|
+
} else if (os.contains("mac")) {
|
|
178
|
+
rt.exec(new String[]{"open", url});
|
|
179
|
+
} else {
|
|
180
|
+
String[] browsers = {"xdg-open", "firefox", "google-chrome", "chromium-browser"};
|
|
181
|
+
boolean opened = false;
|
|
182
|
+
for (String browser : browsers) {
|
|
183
|
+
try {
|
|
184
|
+
rt.exec(new String[]{browser, url});
|
|
185
|
+
opened = true;
|
|
186
|
+
break;
|
|
187
|
+
} catch (Exception ignored) {}
|
|
188
|
+
}
|
|
189
|
+
if (!opened) {
|
|
190
|
+
System.out.println("Please open this URL manually: " + url);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
} catch (Exception e) {
|
|
194
|
+
System.out.println("Could not open browser automatically.");
|
|
195
|
+
System.out.println("Please open this URL manually: " + url);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/** Clears local auth files and logs the user out. */
|
|
200
|
+
public static void logout() {
|
|
201
|
+
new File(TOKEN_FILE).delete();
|
|
202
|
+
new File(EMAIL_FILE).delete();
|
|
203
|
+
new File(NAME_FILE).delete();
|
|
204
|
+
System.out.println(Colors.c(Colors.GREEN, "\u2713") + " Logged out successfully");
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/** Prints formatted account details when logged in. */
|
|
208
|
+
public static void whoami() {
|
|
209
|
+
if (!isLoggedIn()) {
|
|
210
|
+
System.out.println(Colors.c(Colors.RED, "Not logged in."));
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
System.out.println(Colors.c(Colors.GRAY, "==== Account Details ===="));
|
|
214
|
+
System.out.println("Email : " + Colors.c(Colors.YELLOW, getUserEmail()));
|
|
215
|
+
System.out.println("Device ID: " + Colors.c(Colors.CYAN, getDeviceId()));
|
|
216
|
+
System.out.println("Status : " + Colors.c(Colors.GREEN, "\u25CF Online"));
|
|
217
|
+
System.out.println(Colors.c(Colors.GRAY, "========================="));
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/** Writes file content to the given path and reports failures. */
|
|
221
|
+
private static void writeFile(String path, String content) {
|
|
222
|
+
try (FileWriter writer = new FileWriter(path)) {
|
|
223
|
+
writer.write(content == null ? "" : content);
|
|
224
|
+
} catch (IOException e) {
|
|
225
|
+
System.out.println(Colors.c(Colors.RED, "Failed to write file: " + path));
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/** Reads and trims file content or returns null on failure. */
|
|
230
|
+
private static String readFile(String path) {
|
|
231
|
+
try (BufferedReader reader = new BufferedReader(new FileReader(path))) {
|
|
232
|
+
StringBuilder sb = new StringBuilder();
|
|
233
|
+
String line;
|
|
234
|
+
while ((line = reader.readLine()) != null) {
|
|
235
|
+
sb.append(line);
|
|
236
|
+
}
|
|
237
|
+
return sb.toString().trim();
|
|
238
|
+
} catch (IOException e) {
|
|
239
|
+
return null;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/** Reads all bytes from an InputStream and returns as a String. */
|
|
244
|
+
private static String readStream(InputStream is) throws IOException {
|
|
245
|
+
return new String(is.readAllBytes());
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/** Extracts a simple JSON string value by key. */
|
|
249
|
+
private static String extractJsonValue(String json, String key) {
|
|
250
|
+
try {
|
|
251
|
+
String search = "\"" + key + "\":\"";
|
|
252
|
+
int start = json.indexOf(search) + search.length();
|
|
253
|
+
int end = json.indexOf("\"", start);
|
|
254
|
+
return json.substring(start, end);
|
|
255
|
+
} catch (Exception e) {
|
|
256
|
+
return "unknown";
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/** Persists login credentials to local auth files. */
|
|
261
|
+
private static void persistLogin(String token, String email, String name) {
|
|
262
|
+
new File(TOKEN_DIR).mkdirs();
|
|
263
|
+
writeFile(TOKEN_FILE, token);
|
|
264
|
+
writeFile(EMAIL_FILE, email);
|
|
265
|
+
writeFile(NAME_FILE, name);
|
|
266
|
+
}
|
|
267
|
+
}
|
package/version.txt
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
1.0.
|
|
1
|
+
1.0.5
|