97 lines
4.3 KiB
Python
97 lines
4.3 KiB
Python
"""Generate private cloud/preview credentials without printing secrets."""
|
|
import argparse
|
|
import hashlib
|
|
import os
|
|
from pathlib import Path
|
|
import secrets
|
|
import shutil
|
|
import tempfile
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
|
|
|
|
def hash_password(password):
|
|
salt = secrets.token_bytes(16)
|
|
key = hashlib.scrypt(password.encode(), salt=salt, n=16384, r=8, p=1, dklen=32)
|
|
return "scrypt$16384$8$1$" + salt.hex() + "$" + key.hex()
|
|
|
|
|
|
def upgrade_auth(target, access, preview):
|
|
if not target.is_file() or not access.is_file():
|
|
raise SystemExit("Existing environment and administrator credential files are required.")
|
|
original = target.read_text(encoding="utf-8")
|
|
if any(line.startswith("WOLIN_AUTH_PASSWORD_HASH=") for line in original.splitlines()):
|
|
print("Form login is already configured; credentials were not changed.")
|
|
return
|
|
credentials = dict(line.split(": ", 1) for line in access.read_text(encoding="utf-8").splitlines())
|
|
replacements = {
|
|
"WOLIN_AUTH_USER": credentials["Username"],
|
|
"WOLIN_AUTH_PASSWORD_HASH": "'" + hash_password(credentials["Password"]) + "'",
|
|
"WOLIN_SESSION_SECRET": secrets.token_urlsafe(48),
|
|
"AUTH_COOKIE_SECURE": "false" if preview else "true",
|
|
}
|
|
lines = [line for line in original.splitlines()
|
|
if line.partition("=")[0] not in {*replacements, "WOLIN_AUTH_HASH"}]
|
|
lines.extend(key + "=" + value for key, value in replacements.items())
|
|
backup = ROOT / "artifacts" / (target.name + ".before-form-login")
|
|
if not backup.exists():
|
|
shutil.copyfile(target, backup)
|
|
if os.name != "nt":
|
|
backup.chmod(0o600)
|
|
descriptor, temporary = tempfile.mkstemp(prefix="auth-env-", dir=access.parent)
|
|
try:
|
|
with os.fdopen(descriptor, "w", encoding="utf-8", newline="\n") as stream:
|
|
stream.write("\n".join(lines) + "\n")
|
|
os.replace(temporary, target)
|
|
finally:
|
|
if os.path.exists(temporary):
|
|
os.unlink(temporary)
|
|
print("Upgraded to form login; administrator password and database credentials unchanged.")
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument("--preview", action="store_true", help="Bind only to localhost:18080 for a local rehearsal")
|
|
parser.add_argument("--upgrade-auth", action="store_true", help="Replace legacy Basic Auth configuration while keeping existing passwords")
|
|
args = parser.parse_args()
|
|
target = ROOT / (".env.preview" if args.preview else ".env.cloud")
|
|
access = ROOT / "artifacts" / ("preview-access.txt" if args.preview else "cloud-access.txt")
|
|
if args.upgrade_auth:
|
|
upgrade_auth(target, access, args.preview)
|
|
return
|
|
if target.exists() or access.exists():
|
|
raise SystemExit("Refusing to overwrite existing credentials; keep your existing files.")
|
|
password = secrets.token_urlsafe(24)
|
|
password_hash = hash_password(password)
|
|
preview = args.preview
|
|
lines = [
|
|
"COMPOSE_PROJECT_NAME=" + ("wolin-cloud-preview" if preview else "wolin-cloud"),
|
|
"SITE_ADDRESS=" + ("http://localhost" if preview else "zhouxing199901.site"),
|
|
"HTTP_BIND=" + ("127.0.0.1" if preview else "0.0.0.0"),
|
|
"HTTP_PORT=" + ("18080" if preview else "80"),
|
|
"HTTPS_PORT=" + ("18443" if preview else "443"),
|
|
"DB_PASSWORD=" + secrets.token_urlsafe(32),
|
|
"DB_ROOT_PASSWORD=" + secrets.token_urlsafe(32),
|
|
"WOLIN_AUTH_USER=admin",
|
|
"WOLIN_AUTH_PASSWORD_HASH='" + password_hash + "'",
|
|
"WOLIN_SESSION_SECRET=" + secrets.token_urlsafe(48),
|
|
"AUTH_COOKIE_SECURE=" + ("false" if preview else "true"),
|
|
"DEEPSEEK_API_KEY=",
|
|
"DEEPSEEK_MODEL=deepseek-flash",
|
|
]
|
|
access.parent.mkdir(parents=True, exist_ok=True)
|
|
# Exclusive creation prevents accidentally rotating a running database's credentials.
|
|
with target.open("x", encoding="utf-8", newline="\n") as stream:
|
|
stream.write("\n".join(lines) + "\n")
|
|
with access.open("x", encoding="utf-8", newline="\n") as stream:
|
|
stream.write("Username: admin\nPassword: " + password + "\n")
|
|
if os.name != "nt":
|
|
target.chmod(0o600)
|
|
access.chmod(0o600)
|
|
print("Created", target)
|
|
print("Administrator credentials saved privately to", access)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|