oplik0/cherrydoor

View on GitHub
cherrydoor/cli/install.py

Summary

Maintainability
C
1 day
Test Coverage
"""
Install the app for the first time
"""

__author__ = "opliko"
__license__ = "MIT"
__version__ = "0.8.b0"
__status__ = "Prototype"
import json
import os
import sys
from getpass import getpass
from pathlib import Path
from subprocess import call  # nosec

from argon2 import PasswordHasher
from pymongo import MongoClient
from pymongo.errors import OperationFailure


def step_enabled(step, args):
    return step not in args.install_steps_excluded and (
        args.install_steps == [] or step in args.install_steps
    )


def install(args):
    if sys.platform == "linux" or 1 == 1:

        if step_enabled("dependencies", args):
            # install MongoDB and some other things if they're not installed
            try:
                call(["cherrydoor-install"], shell=False)  # nosec
            except (PermissionError, FileNotFoundError):
                print("unable to install dependencies")
                if args.fail:
                    sys.exit(1)
        # generate a configuration based on default config
        if (
            not os.path.exists(f"{Path.home()}/.config/cherrydoor/config.json")
            or "config" in args.install_steps
        ):
            config = {
                "__comment__": "This is a default config for setuptools installation - it shouldn't be used if installed from GitHub",
                "host": "127.0.0.1",
                "port": 5000,
                "mongo": {
                    "url": "localhost:27017",
                    "name": "cherrydoor",
                    "username": "cherrydoor",
                    "password": "test",
                },
                "login-translation": {
                    "username": "Nazwa użytkownika",
                    "password": "Hasło",
                    "remember-me": "Pamiętaj mnie",
                    "log-in": "Zaloguj się",
                    "message": "Musisz się zalogować by uzyskać dostęp do tej strony",
                },
                "secret-key": "\\xd7w7\\x04\\r\\xfc/q\\x1a\\x9b&",
                "https": {
                    "enabled": False,
                    "hsts-enabled": False,
                    "hsts-preload": False,
                },
                "interface": {
                    "type": "serial",
                    "baudrate": 115200,
                    "port": "/dev/serial0",
                    "encoding": "utf-8",
                },
                "manufacturer-code": "18",
            }
        else:
            with open(
                f"{Path.home()}/.config/cherrydoor/config.json", "r", encoding="utf-8"
            ) as f:
                config = json.load(f)
        if step_enabled("config", args):
            # create a random secret key
            config["secret-key"] = os.urandom(24).hex()
            # let user choose a password for the database
            if step_enabled("database", args):
                config["mongo"]["password"] = getpass("Wprowadź hasło do bazy danych: ")
            try:
                # files configuration
                if not os.path.exists(f"${Path.home()}"):
                    os.makedirs(f"{Path.home()}/.config/cherrydoor")
                with open(
                    f"{Path.home()}/.config/cherrydoor/config.json",
                    "w",
                    encoding="utf-8",
                ) as f:
                    json.dump(config, f, ensure_ascii=False, indent=4)
            except (IOError, PermissionError):
                print(
                    f"Nie udało się stworzyć plików w {Path.home()}/.config/cherrydoor. Spróbuj stworzyć ten folder manualnie i nadać mu właściwe uprawnienia",
                    file=sys.stderr,
                )
                if args.fail:
                    sys.exit(1)
        if step_enabled("service", args):
            service_config = f"""\
[Unit]
Description=Cherrydoor Service
After=network.target
[Service]
ExecStart={os.path.realpath(__file__).replace("install.py", "__init__.py")} start
Environment=PYTHONUNBUFFERED=1
Restart=always
Type=simple
User=ubuntu
[Install]
WantedBy=multi-user.target
"""
            try:
                if not os.path.exists(f"{Path.home()}/.config/systemd/user"):
                    os.makedirs(f"{Path.home()}/.config/systemd/user")
                with open(
                    f"{Path.home()}/.config/systemd/user/cherrydoor.service", "w"
                ) as f:
                    f.write(service_config)
                    print(
                        f"Plik konfiguracyjny znajduje się w folderze {Path.home()}/.config/cherrydoor"
                    )
            except (IOError, PermissionError):
                print(
                    f"Nie udało się stworzyć pliku usługi pod {Path.home()}/.config/systemd/user/cherrydoor.service - spróbuj uruchomić skrypt z właściwymi uprawnieniami lub stworzyć ten plik manualnie. Zawartość:",
                    file=sys.stderr,
                )
                print(service_config, file=sys.stderr)
                if args.fail:
                    sys.exit(1)
        hasher = PasswordHasher(
            time_cost=4,
            memory_cost=65536,
            parallelism=8,
            hash_len=16,
            salt_len=16,
            encoding="utf-8",
        )
        db = MongoClient(
            f"mongodb://{config['mongo']['url']}/{config['mongo']['name']}"
        )[config["mongo"]["name"]]
        if step_enabled("database", args):
            try:
                db.command(
                    "createUser",
                    config["mongo"]["username"],
                    pwd=config["mongo"]["password"],
                    roles=[
                        {"role": "readWrite", "db": config["mongo"]["name"]},
                        {"role": "clusterMonitor", "db": "admin"},
                    ],
                )
                db.create_collection("users")
                db.create_collection(
                    "logs", options={"size": 1073742000, "capped": True}
                )
                db.create_collection("settings")
                db.create_collection(
                    "terminal", options={"size": 1048576, "capped": True, "max": 10000}
                )
            except OperationFailure:
                pass
            user_indexes = db.users.index_information()
            if "username_index" not in user_indexes.keys():
                db.users.create_index("username", name="username_index", unique=True)
            if "cards_index" not in user_indexes.keys():
                db.users.create_index("cards", name="cards_index", sparse=True)
        # nosec - it's python3, not 2, Bandit...
        if step_enabled("user", args) and input(
            "Czy chcesz stworzć nowego użytkownika-administratora? [y/n]"
        ).lower() in ["y", "yes", "tak", "t"]:
            # nosec - it's python3, not 2, Bandit...
            username = input("Wprowadź nazwę użytkownika: ")
            password = hasher.hash(getpass("Hasło: "))
            db.users.insert({"username": username, "password": password, "cards": []})
        print("Instalacja skończona!")
        try:
            service_call_args = ["systemctl", "--user", "enable", "cherrydoor"]
            call(service_call_args, shell=False)  # nosec
        except (IOError, PermissionError):
            pass
    else:
        print("Ten system operacyjny nie jest obecnie obsługiwany")

    sys.exit()