|
4 | 4 |
|
5 | 5 | import argparse
|
6 | 6 | import configparser
|
| 7 | +import contextlib |
7 | 8 | import logging
|
8 | 9 | import os
|
9 | 10 | import stat
|
@@ -311,36 +312,60 @@ def fetch_zuliprc(zuliprc_path: str) -> None:
|
311 | 312 | print(in_color("red", "\nIncorrect Email(or Username) or Password!\n"))
|
312 | 313 | login_data = get_api_key(realm_url)
|
313 | 314 |
|
| 315 | + zulip_key_path = os.path.join( |
| 316 | + os.path.dirname(os.path.abspath(zuliprc_path)), "zulip_key" |
| 317 | + ) |
| 318 | + |
314 | 319 | preferred_realm_url, login_id, api_key = login_data
|
315 | 320 | save_zuliprc_failure = _write_zuliprc(
|
316 |
| - zuliprc_path, |
317 |
| - login_id=login_id, |
| 321 | + to_path=zuliprc_path, |
| 322 | + key_path=zulip_key_path, |
318 | 323 | api_key=api_key,
|
| 324 | + login_id=login_id, |
319 | 325 | server_url=preferred_realm_url,
|
320 | 326 | )
|
321 | 327 | if not save_zuliprc_failure:
|
322 |
| - print(f"Generated API key saved at {zuliprc_path}") |
| 328 | + print(f"Generated config file saved at {zuliprc_path}") |
323 | 329 | else:
|
324 | 330 | exit_with_error(save_zuliprc_failure)
|
325 | 331 |
|
326 | 332 |
|
327 | 333 | def _write_zuliprc(
|
328 |
| - to_path: str, *, login_id: str, api_key: str, server_url: str |
| 334 | + to_path: str, *, key_path: str, login_id: str, api_key: str, server_url: str |
329 | 335 | ) -> str:
|
330 | 336 | """
|
331 |
| - Writes a zuliprc file, returning a non-empty error string on failure |
332 |
| - Only creates new private files; errors if file already exists |
| 337 | + Writes both zuliprc and zulip_key files securely. |
| 338 | + Ensures atomicity: if one file fails to write, cleans up the other. |
| 339 | + Returns an empty string on success, or a descriptive error message on failure. |
333 | 340 | """
|
| 341 | + zuliprc_created = False |
| 342 | + |
334 | 343 | try:
|
| 344 | + # Write zuliprc |
335 | 345 | with open(
|
336 | 346 | os.open(to_path, os.O_CREAT | os.O_WRONLY | os.O_EXCL, 0o600), "w"
|
337 | 347 | ) as f:
|
338 |
| - f.write(f"[api]\nemail={login_id}\nkey={api_key}\nsite={server_url}") |
| 348 | + f.write( |
| 349 | + f"[api]\nemail={login_id}\npasscmd=cat zulip_key\nsite={server_url}" |
| 350 | + ) |
| 351 | + zuliprc_created = True |
| 352 | + # Write zulip_key |
| 353 | + with open( |
| 354 | + os.open(key_path, os.O_CREAT | os.O_WRONLY | os.O_EXCL, 0o600), "w" |
| 355 | + ) as f: |
| 356 | + f.write(api_key) |
| 357 | + |
339 | 358 | return ""
|
| 359 | + |
340 | 360 | except FileExistsError:
|
341 |
| - return f"zuliprc already exists at {to_path}" |
| 361 | + filename = to_path if not zuliprc_created else key_path |
| 362 | + return f"FileExistsError: {filename} already exists" |
342 | 363 | except OSError as ex:
|
343 |
| - return f"{ex.__class__.__name__}: zuliprc could not be created at {to_path}" |
| 364 | + if zuliprc_created: |
| 365 | + with contextlib.suppress(Exception): |
| 366 | + os.remove(to_path) |
| 367 | + filename = key_path if zuliprc_created else to_path |
| 368 | + return f"{ex.__class__.__name__}: could not create {filename} ({ex})" |
344 | 369 |
|
345 | 370 |
|
346 | 371 | def parse_zuliprc(zuliprc_str: str) -> Dict[str, SettingData]:
|
|
0 commit comments