Beispiel #1
0
def modifyConfig():
    global config
    log.info("Config modification requested")
    data = request.get_json()
    temp_config = configparser.RawConfigParser(comment_prefixes="/",
                                               allow_no_value=True)
    temp_config.read(str(config_path.resolve()))
    for section in data:
        if section in temp_config and 'restart-program' not in section:
            for item in data[section]:
                temp_config[section][item] = data[section][item]
                data[section][item] = True
                log.debug(f"{section}/{item} modified")
    with open(config_path, "w") as config_file:
        temp_config.write(config_file)
    config.trigger_reload()
    log.info("Config written.")
    if 'restart-program' in data:
        if data['restart-program']:
            log.info('Restarting...')
            try:
                proc = psutil.Process(os.getpid())
                for handler in proc.open_files() + proc.connections():
                    os.close(handler.fd)
            except Exception as e:
                log.error(f'Failed restart: {type(e).__name__}')
            python = sys.executable
            os.execl(python, python, *sys.argv)
    return resp()
Beispiel #2
0
def inviteProxy(path):
    if checkInvite(path):
        log.info(f"Invite {path} used to request form")
        try:
            email = data_store.invites[path]["email"]
        except KeyError:
            email = ""
        return render_template(
            "form.html",
            bs5=config.getboolean("ui", "bs5"),
            css_file=css_file,
            contactMessage=config["ui"]["contact_message"],
            helpMessage=config["ui"]["help_message"],
            successMessage=config["ui"]["success_message"],
            jfLink=config["jellyfin"]["public_server"],
            validate=config.getboolean("password_validation", "enabled"),
            requirements=validator().getCriteria(),
            email=email,
            username=(not config.getboolean("email", "no_username")),
        )
    elif "admin.html" not in path and "admin.html" not in path:
        return app.send_static_file(path)
    else:
        log.debug("Attempted use of invalid invite")
        return render_template(
            "invalidCode.html",
            bs5=config.getboolean("ui", "bs5"),
            css_file=css_file,
            contactMessage=config["ui"]["contact_message"],
        )
Beispiel #3
0
def modifyUsers():
    data = request.get_json()
    log.debug("Email list modification requested")
    for key in data:
        uid = jf.getUsers(key, public=False)["Id"]
        data_store.emails[uid] = data[key]
        log.debug(f'Email for user "{key}" modified')
    return resp()
Beispiel #4
0
def getUsers():
    log.debug("User and email list requested")
    response = {"users": []}
    users = jf.getUsers(public=False)
    emails = data_store.emails
    for user in users:
        entry = {"name": user["Name"]}
        if user["Id"] in emails:
            entry["email"] = emails[user["Id"]]
        response["users"].append(entry)
    return jsonify(response)
Beispiel #5
0
def getConfig():
    log.debug("Config requested")
    with open(config_base_path, "r") as f:
        config_base = json.load(f)
    # config.read(config_path)
    response_config = config_base
    for section in config_base:
        for entry in config_base[section]:
            if entry in config[section]:
                response_config[section][entry]["value"] = config[section][
                    entry]
    return jsonify(response_config), 200
Beispiel #6
0
def checkInvite(code, used=False, username=None):
    current_time = datetime.datetime.now()
    invites = dict(data_store.invites)
    match = False
    for invite in invites:
        if ("remaining-uses" not in invites[invite]
                and "no-limit" not in invites[invite]):
            invites[invite]["remaining-uses"] = 1
        expiry = datetime.datetime.strptime(invites[invite]["valid_till"],
                                            "%Y-%m-%dT%H:%M:%S.%f")
        if current_time >= expiry or ("no-limit" not in invites[invite] and
                                      invites[invite]["remaining-uses"] < 1):
            log.debug(f"Housekeeping: Deleting expired invite {invite}")
            if (config.getboolean("notifications", "enabled")
                    and "notify" in invites[invite]):
                for address in invites[invite]["notify"]:
                    if "notify-expiry" in invites[invite]["notify"][address]:
                        if invites[invite]["notify"][address]["notify-expiry"]:
                            method = config["email"]["method"]
                            if method == "mailgun":
                                email = Mailgun(address)
                            elif method == "smtp":
                                email = Smtp(address)
                            if email.construct_expiry({
                                    "code": invite,
                                    "expiry": expiry
                            }):
                                threading.Thread(target=email.send).start()
            del data_store.invites[invite]
        elif invite == code:
            match = True
            if used:
                delete = False
                inv = dict(data_store.invites[code])
                if "used-by" not in inv:
                    inv["used-by"] = []
                if "remaining-uses" in inv:
                    if inv["remaining-uses"] == 1:
                        delete = True
                        del data_store.invites[code]
                    elif "no-limit" not in invites[invite]:
                        inv["remaining-uses"] -= 1
                inv["used-by"].append(
                    [username, format_datetime(current_time)])
                if not delete:
                    data_store.invites[code] = inv
    return match
Beispiel #7
0
def getInvites():
    log.debug("Invites requested")
    current_time = datetime.datetime.now()
    invites = dict(data_store.invites)
    for code in invites:
        checkInvite(code)
    invites = dict(data_store.invites)
    response = {"invites": []}
    for code in invites:
        expiry = datetime.datetime.strptime(invites[code]["valid_till"],
                                            "%Y-%m-%dT%H:%M:%S.%f")
        valid_for = expiry - current_time
        invite = {
            "code": code,
            "days": valid_for.days,
            "hours": valid_for.seconds // 3600,
            "minutes": (valid_for.seconds // 60) % 60,
        }
        if "created" in invites[code]:
            invite["created"] = invites[code]["created"]
        if "used-by" in invites[code]:
            invite["used-by"] = invites[code]["used-by"]
        if "no-limit" in invites[code]:
            invite["no-limit"] = invites[code]["no-limit"]
        if "remaining-uses" in invites[code]:
            invite["remaining-uses"] = invites[code]["remaining-uses"]
        else:
            invite["remaining-uses"] = 1
        if "email" in invites[code]:
            invite["email"] = invites[code]["email"]
        if "notify" in invites[code]:
            if config.getboolean("ui", "jellyfin_login"):
                address = data_store.emails[g.user.id]
            else:
                address = config["ui"]["email"]
            if address in invites[code]["notify"]:
                if "notify-expiry" in invites[code]["notify"][address]:
                    invite["notify-expiry"] = invites[code]["notify"][address][
                        "notify-expiry"]
                if "notify-creation" in invites[code]["notify"][address]:
                    invite["notify-creation"] = invites[code]["notify"][
                        address]["notify-creation"]
        response["invites"].append(invite)
    return jsonify(response)
Beispiel #8
0
def generateInvite():
    current_time = datetime.datetime.now()
    data = request.get_json()
    delta = datetime.timedelta(days=int(data["days"]),
                               hours=int(data["hours"]),
                               minutes=int(data["minutes"]))
    invite_code = secrets.token_urlsafe(16)
    invite = {}
    invite["created"] = format_datetime(current_time)
    if data["multiple-uses"]:
        if data["no-limit"]:
            invite["no-limit"] = True
        else:
            invite["remaining-uses"] = int(data["remaining-uses"])
    else:
        invite["remaining-uses"] = 1
    log.debug(f"Creating new invite: {invite_code}")
    valid_till = current_time + delta
    invite["valid_till"] = valid_till.strftime("%Y-%m-%dT%H:%M:%S.%f")
    if "email" in data and config.getboolean("invite_emails", "enabled"):
        address = data["email"]
        invite["email"] = address
        log.info(f"Sending invite to {address}")
        method = config["email"]["method"]
        if method == "mailgun":
            from jellyfin_accounts.email import Mailgun

            email = Mailgun(address)
        elif method == "smtp":
            from jellyfin_accounts.email import Smtp

            email = Smtp(address)
        email.construct_invite({"expiry": valid_till, "code": invite_code})
        response = email.send()
        if response is False or type(response) != bool:
            invite["email"] = f"Failed to send to {address}"
    if config.getboolean("notifications", "enabled"):
        if "notify-creation" in data:
            invite["notify-creation"] = data["notify-creation"]
        if "notify-expiry" in data:
            invite["notify-expiry"] = data["notify-expiry"]
    data_store.invites[invite_code] = invite
    log.info(f"New invite created: {invite_code}")
    return resp()
Beispiel #9
0
    def modifyConfig():
        log.info("Config modification requested")
        data = request.get_json()
        temp_config = RawConfigParser(comment_prefixes="/", allow_no_value=True)
        temp_config.read(config_path)
        for section in data:
            if section in temp_config:
                for item in data[section]:
                    if item in temp_config[section]:
                        temp_config[section][item] = data[section][item]
                        data[section][item] = True
                        log.debug(f"{section}/{item} modified")
                    else:
                        data[section][item] = False
                        log.debug(f"{section}/{item} does not exist in config")
        with open(config_path, "w") as config_file:
            temp_config.write(config_file)
        log.debug("Config written")
        log.info('Restarting...')
        try:
            p = psutil.Process(os.getpid())
            for handler in p.open_files() + p.connections():
                os.close(handler.fd)
        except:
            pass

        python = sys.executable
        os.execl(python, python, *sys.argv)
        return resp()
Beispiel #10
0
def setNotify():
    data = request.get_json()
    change = False
    for code in data:
        for key in data[code]:
            if key in ["notify-expiry", "notify-creation"]:
                inv = data_store.invites[code]
                if config.getboolean("ui", "jellyfin_login"):
                    address = data_store.emails[g.user.id]
                else:
                    address = config["ui"]["email"]
                if "notify" not in inv:
                    inv["notify"] = {}
                if address not in inv["notify"]:
                    inv["notify"][address] = {}
                inv["notify"][address][key] = data[code][key]
                log.debug(f"{code}: Notification settings changed")
                change = True
    if change:
        data_store.invites[code] = inv
        return resp()
    return resp(success=False)
Beispiel #11
0
def setDefaults():
    data = request.get_json()
    username = data["username"]
    log.debug(f"Storing default settings from user {username}")
    try:
        user = jf.getUsers(username=username, public=False)
    except Jellyfin.UserNotFoundError:
        log.error(f"Storing defaults failed: Couldn't find user {username}")
        return resp(False)
    uid = user["Id"]
    policy = user["Policy"]
    data_store.user_template = policy
    if data["homescreen"]:
        configuration = user["Configuration"]
        try:
            displayprefs = jf.getDisplayPreferences(uid)
            data_store.user_configuration = configuration
            data_store.user_displayprefs = displayprefs
        except:
            log.error("Storing defaults failed: " +
                      "couldn't store homescreen layout")
            return resp(False)
    return resp()
Beispiel #12
0
def newUser():
    data = request.get_json()
    log.debug("Attempted newUser")
    if checkInvite(data["code"]):
        validation = validator().validate(data["password"])
        valid = True
        for criterion in validation:
            if validation[criterion] is False:
                valid = False
        if valid:
            log.debug("User password valid")
            try:
                user = jf.newUser(data["username"], data["password"])
            except Jellyfin.UserExistsError:
                error = f'User already exists named {data["username"]}'
                log.debug(error)
                return jsonify({"error": error})
            except:
                return jsonify({"error": "Unknown error"})
            invites = dict(data_store.invites)
            checkInvite(data["code"], used=True, username=data["username"])
            if (config.getboolean("notifications", "enabled")
                    and "notify" in invites[data["code"]]):
                for address in invites[data["code"]]["notify"]:
                    if "notify-creation" in invites[
                            data["code"]]["notify"][address]:
                        if invites[data["code"]]["notify"][address][
                                "notify-creation"]:
                            method = config["email"]["method"]
                            if method == "mailgun":
                                email = Mailgun(address)
                            elif method == "smtp":
                                email = Smtp(address)
                            if email.construct_created({
                                    "code":
                                    data["code"],
                                    "username":
                                    data["username"],
                                    "created":
                                    datetime.datetime.now(),
                            }):
                                threading.Thread(target=email.send).start()
            if user.status_code == 200:
                try:
                    policy = data_store.user_template
                    if policy != {}:
                        jf.setPolicy(user.json()["Id"], policy)
                    else:
                        log.debug("user policy was blank")
                except:
                    log.error("Failed to set new user policy")
                try:
                    configuration = data_store.user_configuration
                    displayprefs = data_store.user_displayprefs
                    if configuration != {} and displayprefs != {}:
                        if jf.setConfiguration(user.json()["Id"],
                                               configuration):
                            jf.setDisplayPreferences(user.json()["Id"],
                                                     displayprefs)
                            log.debug("Set homescreen layout.")
                    else:
                        log.debug(
                            "user configuration and/or displayprefs were blank"
                        )
                except:
                    log.error("Failed to set new user homescreen layout")
                if config.getboolean("password_resets", "enabled"):
                    data_store.emails[user.json()["Id"]] = data["email"]
                    log.debug("Email address stored")
                log.info("New user created")
            else:
                log.error(f"New user creation failed: {user.status_code}")
                return resp(False)
        else:
            log.debug("User password invalid")
        return jsonify(validation)
    else:
        log.debug("Attempted newUser unauthorized")
        return resp(False, code=401)