Exemple #1
0
def git_clone(host, username, repo_name, clone_dir):
    # Get the default branch (we've checked previously that the host is one of
    # the following)
    api_url = {
        "github.com":
        f"https://api.github.com/repos/{username}/{repo_name}",
        "github.eii.us.es":
        f"https://github.eii.us.es/api/v3/repos/{username}/{repo_name}"
    }[host]

    api_response = requests.get(api_url)
    if api_response.status_code == 404:
        logger.error("Repo not found")
        sys.exit(1)

    branch = api_response.json()["default_branch"]

    git_url = f"https://{host}/{username}/{repo_name}"
    repo_file = f"{branch}.zip"
    repo_zip = git_url + f"/archive/{repo_file}"

    if isfile(repo_file):
        remove(repo_file)

    fn = basename(urlparse(repo_zip).path)
    zip_path = save(repo_zip, join(getcwd(), fn))

    with zipfile.ZipFile(zip_path, 'r') as zip_ref:
        zip_ref.extractall(clone_dir)

    if isfile(zip_path):
        remove(zip_path)
Exemple #2
0
def handle(args):
    query_url = f"https://api.github.com/orgs/{settings.GITHUB_TEMPLATES_OWNER}/repos"

    try:
        repo_data = requests.get(query_url).json()
    except Exception as exc:
        logger.debug(traceback.format_exc())
        logger.error(
            "An error has occurred when querying GitHub's API to obtain the list of templates."
        )
        if not settings.DEBUG_ENABLED:
            logger.error("Add --debug to see the full stack trace.")
        sys.exit(1)

    templates = []
    for repo in repo_data:
        name = repo["name"].lower()
        if name.startswith("silence-template"):
            template_name = name.replace("silence-template-", "")
            desc = repo["description"]
            templates.append({"name": template_name, "desc": desc})

    templates.sort(key=lambda x: x["name"])

    print("Available templates:")
    for tmpl in templates:
        name = tmpl["name"]
        default = " (default)" if name == settings.DEFAULT_TEMPLATE_NAME.lower(
        ) else ""
        desc = f': {tmpl["desc"]}' if tmpl["desc"] else ""

        print(f"    · {name}{default}{desc}")
Exemple #3
0
def check_auth_roles(auth_required, allowed_roles, method, route):
    # Raise an error if allowed_roles is not a list
    if not isinstance(allowed_roles, list):
        logger.error(
            f"The value '{allowed_roles}' for the allowed_roles parameter in "
            +
            f"endpoint {method.upper()} {route} is not allowed, it must be a "
            + "list of allowed roles.")
        sys.exit(1)

    # Warn if the user has specified some roles but auth_required is false,
    # since it will result in all users having access
    if not auth_required and len(allowed_roles) > 0 and allowed_roles != ["*"]:
        logger.warn(
            f"You have specified allowed roles in endpoint {method.upper()} {route}, "
            +
            "but auth_required is False. This will result in all users having access "
            + "regardless of their role.")

    # Warn if the user has specified an empty list of roles, and auth_required is true,
    # because it will result in noone having access
    if auth_required and len(allowed_roles) == 0:
        logger.warn(
            f"You have set auth_required to True in endpoint {method.upper()} {route}, "
            +
            "but the list of allowed roles is empty. This will result in noone being able "
            + "to access the endpoint.")
Exemple #4
0
def check_params_match(sql_params, user_params, route):
    sql_params_set = set(sql_params)
    user_params_set = set(user_params)
    diff = sql_params_set.difference(user_params_set)
    if "loggedId" in diff:
        diff.remove("loggedId")

    if diff:
        params_str = ", ".join(f"${param}" for param in diff)
        logger.error(
            f"Error creating endpoint {route}: the parameters " +
            f"{params_str} are expected by the SQL query but they are not provided in the URL "
            + "or the request body.")
        sys.exit(1)
Exemple #5
0
def try_endpoint_with_user(route, email, expected_access):
    token = get_token_for_user(email)
    headers = {"Token": token}

    r = requests.get(f"{BASE_URL}/{route}", headers=headers)
    expected_code = 200 if expected_access else 401

    try:
        assert int(r.status_code) == int(expected_code)
    except AssertionError:
        logger.error(
            f"The employee {email} got status code {r.status_code} when expecting {expected_code}"
        )
        raise
def try_endpoint_with_user(route, email, expected_access):
    password = email.split("@")[0]
    r = requests.post(f"{BASE_URL}/{route}",
                      data={
                          "email": email,
                          "password": password
                      })

    expected_code = 200 if expected_access else 401

    try:
        assert int(r.status_code) == int(expected_code)
    except AssertionError:
        logger.error(
            f"The employee {email} got status code {r.status_code} when expecting {expected_code}"
        )
        raise
Exemple #7
0
def check_method(sql, verb, endpoint):
    sql_op = get_sql_op(sql)

    if sql_op in OP_VERBS:
        correct_verb = OP_VERBS[sql_op]

        if correct_verb != verb.lower():
            # Warn the user about the correct verb to use
            logger.warn(
                f"The '{verb.upper()}' HTTP verb is not correct for the SQL {sql_op.upper()} "
                +
                f"operation in endpoint {verb.upper()} {endpoint}, the correct verb is {correct_verb.upper()}."
            )
    else:
        # What has the user put here?
        logger.error(
            f"The SQL query '{sql}' in the endpoint {endpoint} is not supported,"
            + " please use only SELECT/INSERT/UPDATE/DELETE.")
        sys.exit(1)
Exemple #8
0
def download_from_github(project_name, repo_url):
    # Check that the current directory does not contain a folder with the same name
    if isdir(project_name):
        logger.error(
            f"A folder named '{project_name}' already exists in the current directory."
        )
        sys.exit(1)

    # Remove the trailing .git or slash if they exist
    # We could use .removesuffix, but it was added in 3.9... maybe if we bump
    # the required Python version some time in the future
    suffixes = (".git", "/")
    for suffix in suffixes:
        if repo_url.endswith(suffix):
            repo_url = repo_url[:-len(suffix)]

    # Check that the repo URL is acceptable
    m = RE_REPO_URL.match(repo_url.lower())
    if not m:
        logger.error(
            "Invalid repo URL, please check your spelling and try again.")
        sys.exit(1)

    host, username, repo_name = m.groups()

    # Check that the host is supported
    if host not in ("github.com", "github.eii.us.es"):
        logger.error(
            "Only repos hosted in github.com or github.eii.us.es are supported."
        )
        sys.exit(1)

    # Download it (this takes care of querying the relevant API to find out
    # how the default branch is called, and exiting if the repo does not exist)
    git_clone(host, username, repo_name, project_name + "/")

    # Unpack it (everything is inside the <name>-<branch> folder)
    branch_folder_name = listdir(project_name)[0]

    # Move everything inside that folder outside
    branch_folder = join(project_name, branch_folder_name)
    for elem in listdir(branch_folder):
        move(join(branch_folder, elem), join(project_name, elem))

    # Remove the now empty folder
    rmtree(branch_folder)

    # Look for .gitkeep files and remove them (especially useful in the case
    # of the blank template project)
    for gitkeep_path in Path(project_name).rglob(".gitkeep"):
        remove(gitkeep_path)

    # Read the settings.py file of the downloaded project, removing the existing
    # SECRET_KEY if it exists and creating a new one. Will also raise a warning
    # if the project does not contain a settings.py file.
    settings_path = join(project_name, "settings.py")

    try:
        settings_lines = open(settings_path, "r", encoding="utf-8").readlines()
        settings_lines = list(
            filter(lambda line: not line.startswith("SECRET_KEY"),
                   settings_lines))

        # Generate the random string for the secret key
        # and add it to the settings.py file
        secret_key = token_urlsafe(32)

        settings_lines += [
            "\n", "# A random string that is used for security purposes\n",
            "# (this has been generated automatically upon project creation)\n",
            f'SECRET_KEY = "{secret_key}"\n'
        ]

        open(settings_path, "w", encoding="utf-8").writelines(settings_lines)

    except FileNotFoundError:
        logger.warning(
            "The downloaded project does not have a settings.py file " +
            "at its root, it may not be a valid Silence project.")
Exemple #9
0
import test_employees

# Order is important!
tests = [
    test_summary,
    test_register,
    test_login,
    test_roles,
    test_banned,
    test_loggedId,
    test_departments,
    test_employees,
]

failed_tests = []

for test in tests:
    try:
        test.run()
    except AssertionError:
        print_exc()
        logger.error(f"{test.__name__} failed. Proceeding with the next one.")
        failed_tests.append(test.__name__)

if failed_tests:
    logger.error(
        f"The following tests failed: {', '.join(failed_tests)}. Exiting with error code."
    )

exit_code = 0 if not failed_tests else 1
sys.exit(exit_code)