예제 #1
0
def user_ssh_remove_key(username, key):
    user = _get_user_for_ssh(username, ["homeDirectory", "uid"])
    if not user:
        raise Exception("User with username '%s' doesn't exists" % username)

    authorized_keys_file = os.path.join(
        user["homeDirectory"][0], ".ssh", "authorized_keys"
    )

    if not os.path.exists(authorized_keys_file):
        raise Exception(
            "this key doesn't exists ({} dosesn't exists)".format(authorized_keys_file)
        )

    authorized_keys_content = read_file(authorized_keys_file)

    if key not in authorized_keys_content:
        raise Exception("Key '{}' is not present in authorized_keys".format(key))

    # don't delete the previous comment because we can't verify if it's legit

    # this regex approach failed for some reasons and I don't know why :(
    # authorized_keys_content = re.sub("{} *\n?".format(key),
    #                                  "",
    #                                  authorized_keys_content,
    #                                  flags=re.MULTILINE)

    authorized_keys_content = authorized_keys_content.replace(key, "")

    write_to_file(authorized_keys_file, authorized_keys_content)
예제 #2
0
def test_write_to_new_file():

    new_file = "%s/barfile" % TMP_TEST_DIR
    assert not os.path.exists(new_file)
    write_to_file(new_file, "yolo\nswag")
    assert os.path.exists(new_file)
    assert read_file(new_file) == "yolo\nswag"
예제 #3
0
def test_write_to_new_file_badpermissions():

    switch_to_non_root_user()
    new_file = "%s/barfile" % TMP_TEST_DIR
    assert not os.path.exists(new_file)
    with pytest.raises(MoulinetteError):
        write_to_file(new_file, "yolo\nswag")
예제 #4
0
def user_ssh_add_key(username, key, comment):
    user = _get_user_for_ssh(username, ["homeDirectory", "uid"])
    if not user:
        raise Exception("User with username '%s' doesn't exists" % username)

    authorized_keys_file = os.path.join(user["homeDirectory"][0], ".ssh",
                                        "authorized_keys")

    if not os.path.exists(authorized_keys_file):
        # ensure ".ssh" exists
        mkdir(os.path.join(user["homeDirectory"][0], ".ssh"),
              force=True,
              parents=True,
              uid=user["uid"][0])

        # create empty file to set good permissions
        write_to_file(authorized_keys_file, "")
        chown(authorized_keys_file, uid=user["uid"][0])
        chmod(authorized_keys_file, 0o600)

    authorized_keys_content = read_file(authorized_keys_file)

    authorized_keys_content += "\n"
    authorized_keys_content += "\n"

    if comment and comment.strip():
        if not comment.lstrip().startswith("#"):
            comment = "# " + comment
        authorized_keys_content += comment.replace("\n", " ").strip()
        authorized_keys_content += "\n"

    authorized_keys_content += key.strip()
    authorized_keys_content += "\n"

    write_to_file(authorized_keys_file, authorized_keys_content)
예제 #5
0
def test_write_to_new_file(tmp_path):
    new_file = tmp_path / "newfile.txt"

    write_to_file(str(new_file), "yolo\nswag")

    assert os.path.exists(str(new_file))
    assert read_file(str(new_file)) == "yolo\nswag"
예제 #6
0
def test_write_to_file_exception(test_file, mocker):
    error = "foobar"

    mocker.patch("builtins.open", side_effect=Exception(error))
    with pytest.raises(MoulinetteError) as exception:
        write_to_file(str(test_file), "yolo\nswag")

    translation = m18n.g("error_writing_file", file=str(test_file), error=error)
    expected_msg = translation.format(file=str(test_file), error=error)
    assert expected_msg in str(exception)
예제 #7
0
def get_public_ip(protocol=4):

    assert protocol in [4, 6], "Invalid protocol version for get_public_ip: %s, expected 4 or 6" % protocol

    cache_file = "/var/cache/yunohost/ipv%s" % protocol
    cache_duration = 120  # 2 min
    if os.path.exists(cache_file) and abs(os.path.getctime(cache_file) - time.time()) < cache_duration:
        ip = read_file(cache_file).strip()
        ip = ip if ip else None  # Empty file (empty string) means there's no IP
        logger.debug("Reusing IPv%s from cache: %s" % (protocol, ip))
    else:
        ip = get_public_ip_from_remote_server(protocol)
        logger.debug("IP fetched: %s" % ip)
        write_to_file(cache_file, ip or "")
    return ip
예제 #8
0
def domain_main_domain(operation_logger, new_main_domain=None):
    """
    Check the current main domain, or change it

    Keyword argument:
        new_main_domain -- The new domain to be set as the main domain

    """
    from yunohost.tools import _set_hostname

    # If no new domain specified, we return the current main domain
    if not new_main_domain:
        return {"current_main_domain": _get_maindomain()}

    # Check domain exists
    if new_main_domain not in domain_list()["domains"]:
        raise YunohostValidationError("domain_name_unknown",
                                      domain=new_main_domain)

    operation_logger.related_to.append(("domain", new_main_domain))
    operation_logger.start()

    # Apply changes to ssl certs
    try:
        write_to_file("/etc/yunohost/current_host", new_main_domain)

        _set_hostname(new_main_domain)
    except Exception as e:
        logger.warning("%s" % e, exc_info=1)
        raise YunohostError("main_domain_change_failed")

    # Generate SSOwat configuration file
    app_ssowatconf()

    # Regen configurations
    if os.path.exists("/etc/yunohost/installed"):
        regen_conf()

    logger.success(m18n.n("main_domain_changed"))
예제 #9
0
def test_write_to_existing_file_badpermissions():

    assert os.path.exists(TMP_TEST_FILE)
    switch_to_non_root_user()
    with pytest.raises(MoulinetteError):
        write_to_file(TMP_TEST_FILE, "yolo\nswag")
예제 #10
0
def test_write_to_existing_file():

    assert os.path.exists(TMP_TEST_FILE)
    write_to_file(TMP_TEST_FILE, "yolo\nswag")
    assert read_file(TMP_TEST_FILE) == "yolo\nswag"
예제 #11
0
def test_write_json_inside_nonexistent_folder():

    with pytest.raises(AssertionError):
        write_to_file("/toto/test.json", ["a", "b"])
예제 #12
0
def test_write_to_file_with_a_list():

    assert os.path.exists(TMP_TEST_FILE)
    write_to_file(TMP_TEST_FILE, ["yolo", "swag"])
    assert read_file(TMP_TEST_FILE) == "yolo\nswag"
예제 #13
0
def test_write_inside_nonexistent_folder():

    with pytest.raises(AssertionError):
        write_to_file("/toto/test", "yolo\nswag")
예제 #14
0
def test_write_to_folder():

    with pytest.raises(AssertionError):
        write_to_file(TMP_TEST_DIR, "yolo\nswag")
예제 #15
0
def _remove_lock(PID_to_remove):
    # FIXME ironically not concurrency safe because it's not atomic...

    PIDs = read_file(MOULINETTE_LOCK).split("\n")
    PIDs_to_keep = [PID for PID in PIDs if int(PID) != PID_to_remove]
    write_to_file(MOULINETTE_LOCK, "\n".join(PIDs_to_keep))
예제 #16
0
def test_write_to_file_with_a_list(test_file):
    write_to_file(str(test_file), ["yolo", "swag"])
    assert read_file(str(test_file)) == "yolo\nswag"
예제 #17
0
def test_write_cannot_write_to_non_existant_folder():
    with pytest.raises(AssertionError):
        write_to_file("/toto/test", "yolo\nswag")
예제 #18
0
def test_write_cannot_write_folder(tmp_path):
    with pytest.raises(AssertionError):
        write_to_file(str(tmp_path), "yolo\nswag")
예제 #19
0
def dyndns_update(
    operation_logger,
    dyn_host="dyndns.yunohost.org",
    domain=None,
    key=None,
    ipv4=None,
    ipv6=None,
    force=False,
    dry_run=False,
):
    """
    Update IP on DynDNS platform

    Keyword argument:
        domain -- Full domain to update
        dyn_host -- Dynette DNS server to inform
        key -- Public DNS key
        ipv4 -- IP address to send
        ipv6 -- IPv6 address to send

    """
    # Get old ipv4/v6

    old_ipv4, old_ipv6 = (None, None)  # (default values)

    # If domain is not given, try to guess it from keys available...
    if domain is None:
        (domain, key) = _guess_current_dyndns_domain(dyn_host)

    if domain is None:
        raise YunohostValidationError('dyndns_no_domain_registered')

    # If key is not given, pick the first file we find with the domain given
    else:
        if key is None:
            keys = glob.glob(
                "/etc/yunohost/dyndns/K{0}.+*.private".format(domain))

            if not keys:
                raise YunohostValidationError("dyndns_key_not_found")

            key = keys[0]

    # Extract 'host', e.g. 'nohost.me' from 'foo.nohost.me'
    host = domain.split(".")[1:]
    host = ".".join(host)

    logger.debug("Building zone update file ...")

    lines = [
        "server %s" % dyn_host,
        "zone %s" % host,
    ]

    def resolve_domain(domain, rdtype):

        # FIXME make this work for IPv6-only hosts too..
        ok, result = dig(dyn_host, "A")
        dyn_host_ip = result[0] if ok == "ok" and len(result) else None
        if not dyn_host_ip:
            raise YunohostError("Failed to resolve %s" % dyn_host)

        ok, result = dig(domain, rdtype, resolvers=[dyn_host_ip])
        if ok == "ok":
            return result[0] if len(result) else None
        elif result[0] == "Timeout":
            logger.debug(
                "Timed-out while trying to resolve %s record for %s using %s" %
                (rdtype, domain, dyn_host))
        else:
            return None

        logger.debug("Falling back to external resolvers")
        ok, result = dig(domain, rdtype, resolvers="force_external")
        if ok == "ok":
            return result[0] if len(result) else None
        elif result[0] == "Timeout":
            logger.debug(
                "Timed-out while trying to resolve %s record for %s using external resolvers : %s"
                % (rdtype, domain, result))
        else:
            return None

        raise YunohostError("Failed to resolve %s for %s" % (rdtype, domain),
                            raw_msg=True)

    old_ipv4 = resolve_domain(domain, "A")
    old_ipv6 = resolve_domain(domain, "AAAA")

    # Get current IPv4 and IPv6
    ipv4_ = get_public_ip()
    ipv6_ = get_public_ip(6)

    if ipv4 is None:
        ipv4 = ipv4_

    if ipv6 is None:
        ipv6 = ipv6_

    logger.debug("Old IPv4/v6 are (%s, %s)" % (old_ipv4, old_ipv6))
    logger.debug("Requested IPv4/v6 are (%s, %s)" % (ipv4, ipv6))

    # no need to update
    if (not force and not dry_run) and (old_ipv4 == ipv4 and old_ipv6 == ipv6):
        logger.info("No updated needed.")
        return
    else:
        operation_logger.related_to.append(("domain", domain))
        operation_logger.start()
        logger.info("Updated needed, going on...")

    dns_conf = _build_dns_conf(domain)

    # Delete custom DNS records, we don't support them (have to explicitly
    # authorize them on dynette)
    for category in dns_conf.keys():
        if category not in ["basic", "mail", "xmpp", "extra"]:
            del dns_conf[category]

    # Delete the old records for all domain/subdomains

    # every dns_conf.values() is a list of :
    # [{"name": "...", "ttl": "...", "type": "...", "value": "..."}]
    for records in dns_conf.values():
        for record in records:
            action = "update delete {name}.{domain}.".format(domain=domain,
                                                             **record)
            action = action.replace(" @.", " ")
            lines.append(action)

    # Add the new records for all domain/subdomains

    for records in dns_conf.values():
        for record in records:
            # (For some reason) here we want the format with everytime the
            # entire, full domain shown explicitly, not just "muc" or "@", it
            # should be muc.the.domain.tld. or the.domain.tld
            if record["value"] == "@":
                record["value"] = domain
            record["value"] = record["value"].replace(";", r"\;")

            action = "update add {name}.{domain}. {ttl} {type} {value}".format(
                domain=domain, **record)
            action = action.replace(" @.", " ")
            lines.append(action)

    lines += ["show", "send"]

    # Write the actions to do to update to a file, to be able to pass it
    # to nsupdate as argument
    write_to_file(DYNDNS_ZONE, "\n".join(lines))

    logger.debug("Now pushing new conf to DynDNS host...")

    if not dry_run:
        try:
            command = ["/usr/bin/nsupdate", "-k", key, DYNDNS_ZONE]
            subprocess.check_call(command)
        except subprocess.CalledProcessError:
            raise YunohostError("dyndns_ip_update_failed")

        logger.success(m18n.n("dyndns_ip_updated"))
    else:
        print(read_file(DYNDNS_ZONE))
        print("")
        print(
            "Warning: dry run, this is only the generated config, it won't be applied"
        )
예제 #20
0
def test_write_to_existing_file(test_file):
    write_to_file(str(test_file), "yolo\nswag")
    assert read_file(str(test_file)) == "yolo\nswag"