Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[stable-2.14] few backports for 2.14.3 #891

Merged
merged 4 commits into from
Nov 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 68 additions & 27 deletions testsuite/tests/apicast/auth/test_basic_auth_app_id.py
Original file line number Diff line number Diff line change
@@ -1,65 +1,106 @@
"""
Service requires credentials (app_id, app_key) to be passed using the Basic Auth

Rewrite ./spec/functional_specs/auth/basic_auth_app_id_spec.rb
"""

import pytest

from threescale_api.resources import Service
from packaging.version import Version # noqa # pylint: disable=unused-import

from testsuite import TESTED_VERSION # noqa # pylint: disable=unused-import
from testsuite.capabilities import Capability
from testsuite.gateways.apicast.selfmanaged import SelfManagedApicast
from testsuite.gateways.apicast.system import SystemApicast
from testsuite.httpx import HttpxClient
from testsuite.utils import basic_auth_string


@pytest.fixture(scope="module")
def service_settings(service_settings):
"Set auth mode to app_id/app_key"
"""Set auth mode to app_id/app_key."""
service_settings.update({"backend_version": Service.AUTH_APP_ID_KEY})
return service_settings


@pytest.fixture(scope="module")
def service_proxy_settings(service_proxy_settings):
"Set credentials location to 'authorization' (Basic HTTP auth)"
"""Set credentials location to 'authorization' (Basic HTTP auth)."""
service_proxy_settings.update({"credentials_location": "authorization"})
return service_proxy_settings


@pytest.mark.smoke
def test_basic_auth_app_id_key(application, api_client):
"""Test client access with Basic HTTP Auth using app id and app key

Configure Api/Service to use App ID / App Key Authentication
and Basic HTTP Auth to pass the credentials.
@pytest.fixture(scope="module")
def http_client(application):
"""Provide an HttpxClient instance using HTTP 1.1."""
client = HttpxClient(False, application)
client.auth = None # No default authentication
yield client
client.close()

Then request made with appropriate Basic auth made has to pass as expected"""

@pytest.fixture(scope="module")
def valid_auth_headers(application):
"""Generate valid Basic Auth headers."""
creds = application.authobj().credentials
expected_authorization = basic_auth_string(creds["app_id"], creds["app_key"])

response = api_client().get("/get")
authorization = basic_auth_string(creds["app_id"], creds["app_key"])
return {"Authorization": authorization}

assert response.status_code == 200
assert response.request.headers["Authorization"] == expected_authorization

@pytest.fixture(scope="module")
def malformed_request(http_client):
"""Create a function to make requests with malformed auth headers."""

def test_basic_auth_app_id_403_with_query(application, api_client):
"Forbid access if credentials passed wrong way"
client = api_client()
def prepare_request():
headers = {"Authorization": "Basic test123?"} # Malformed authorization header
return http_client.get("/get", headers=headers)

client.auth = application.authobj(location="query")
return prepare_request

response = client.get("/get")

assert response.status_code == 403
@pytest.fixture(
scope="module",
params=[
SystemApicast,
pytest.param(SelfManagedApicast, marks=pytest.mark.required_capabilities(Capability.CUSTOM_ENVIRONMENT)),
],
)
def gateway_kind(request):
"""Gateway class to use for tests"""
return request.param


def test_basic_auth_app_id_403_without_auth(api_client):
"Forbid access if no credentials"
@pytest.mark.smoke
def test_basic_auth_success(http_client, valid_auth_headers):
"""Test valid Basic HTTP Auth using app_id and app_key."""
response = http_client.get("/get", headers=valid_auth_headers)
assert response.status_code == 200, "Valid request failed unexpectedly."
assert response.request.headers["Authorization"] == valid_auth_headers["Authorization"]


@pytest.mark.parametrize(
"auth_method, expected_status",
[
("query", 403), # Credentials passed as query parameters
(None, 403), # No credentials
],
)
def test_basic_auth_failure(api_client, application, auth_method, expected_status):
"""Test forbidden access when credentials are passed incorrectly or missing."""
client = api_client()
client.auth = application.authobj(location=auth_method) if auth_method else None
response = client.get("/get")
assert response.status_code == expected_status

client.auth = None

response = client.get("/get")
@pytest.mark.skipif("TESTED_VERSION < Version('2.14')")
@pytest.mark.issue("https://issues.redhat.com/browse/THREESCALE-11435")
# pylint: disable=unused-argument
def test_basic_auth_malformed_secret(http_client, valid_auth_headers, malformed_request, gateway_kind):
"""Test malformed Basic Auth headers."""
# Valid request
response = http_client.get("/get", headers=valid_auth_headers)
assert response.status_code == 200, "Valid request failed unexpectedly."

assert response.status_code == 403
# Malformed request
malformed_status_code = malformed_request().status_code
assert malformed_status_code == 403, "Malformed request did not return 403 as expected."
10 changes: 8 additions & 2 deletions testsuite/tests/prometheus/system/test_internal_calls.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
]


REQUEST_NUM = 42


@pytest.fixture(scope="session")
def backend_listener_url(testconfig):
"""
Expand Down Expand Up @@ -76,7 +79,8 @@ def test_utilization(url, prometheus, application, backend_listener_url, auth_he

service_id = application.service.entity_id
app_plan_id = application.entity["plan_id"]
requests.get(url.format(backend_listener_url, service_id, app_plan_id), verify=False, headers=auth_headers)
for _ in range(REQUEST_NUM):
requests.get(url.format(backend_listener_url, service_id, app_plan_id), verify=False, headers=auth_headers)

# prometheus is downloading metrics periodicity, we need to wait for next fetch
prometheus.wait_on_next_scrape("backend-worker")
Expand All @@ -91,7 +95,9 @@ def test_utilization(url, prometheus, application, backend_listener_url, auth_he
lambda x: x["metric"]["controller"].startswith("admin/api"),
)

assert counts_after == counts_before
for key, count in counts_after.items():
if int(count) >= int(counts_before[key]) + REQUEST_NUM:
assert counts_after == counts_before, f"looks like {key} is increased by internal calls"


def extract_call_metrics(prometheus, query, container, predicate=None):
Expand Down
4 changes: 3 additions & 1 deletion testsuite/tests/toolbox/test_activedoc.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
from testsuite.utils import blame, blame_desc, randomize
from testsuite import rawobj

SWAGGER_LINK = "https://raw.githubusercontent.com/OAI/OpenAPI-Specification/master/examples/v2.0/json/petstore.json"
SWAGGER_LINK = (
"https://raw.githubusercontent.com/OAI/learn.openapis.org/refs/heads/main/examples/v2.0/json/petstore.json"
)


@pytest.fixture(scope="module")
Expand Down
63 changes: 34 additions & 29 deletions testsuite/tests/ui/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -465,46 +465,51 @@ def get_resultsdir_path(node):
return path


# pylint: disable=too-many-locals
def fullpage_screenshot(driver, file_path):
"""
A full-page screenshot function. It scroll the website and screenshots it.
A full-page screenshot function. It scrolls the website and screenshots it.
- Creates multiple files
- Screenshots are made only vertically (on Y axis)
"""
# Removal of the height: 100% style, that disables scroll.
driver.execute_script("document.body.style.height = 'unset'")
driver.execute_script("document.body.parentNode.style.height = 'unset'")
try:
# Removal of the height: 100% style, that disables scroll.
driver.execute_script("document.body.style.height = 'unset'")
driver.execute_script("document.body.parentNode.style.height = 'unset'")

total_height = driver.execute_script("return document.body.parentNode.scrollHeight")
viewport_height = driver.execute_script("return window.innerHeight")
total_height = driver.execute_script("return document.body.parentNode.scrollHeight")
viewport_height = driver.execute_script("return window.innerHeight")

screenshot_bytes = driver.get_screenshot_as_png()
screenshot = Image.open(io.BytesIO(screenshot_bytes))
width, height = screenshot.size
del screenshot

scaling_constant = float(height) / float(viewport_height)
stitched_image = Image.new("RGB", (width, int(total_height * scaling_constant)))
part = 0

for scroll in range(0, total_height, viewport_height):
driver.execute_script("window.scrollTo({0}, {1})".format(0, scroll))
screenshot_bytes = driver.get_screenshot_as_png()
screenshot = Image.open(io.BytesIO(screenshot_bytes))

if scroll + viewport_height > total_height:
offset = (0, int(math.ceil((total_height - viewport_height) * scaling_constant)))
else:
offset = (0, int(math.ceil(scroll * scaling_constant)))

stitched_image.paste(screenshot, offset)
width, height = screenshot.size
del screenshot
part += 1

date = datetime.today().strftime("%Y-%m-%d-%H:%M:%S")
fullpath = f"{file_path}/{date}.png"
stitched_image.save(fullpath)
return fullpath
scaling_constant = float(height) / float(viewport_height)
stitched_image = Image.new("RGB", (width, int(total_height * scaling_constant)))
part = 0

for scroll in range(0, total_height, viewport_height):
driver.execute_script("window.scrollTo({0}, {1})".format(0, scroll))
screenshot_bytes = driver.get_screenshot_as_png()
screenshot = Image.open(io.BytesIO(screenshot_bytes))

if scroll + viewport_height > total_height:
offset = (0, int(math.ceil((total_height - viewport_height) * scaling_constant)))
else:
offset = (0, int(math.ceil(scroll * scaling_constant)))

stitched_image.paste(screenshot, offset)
del screenshot
part += 1

date = datetime.today().strftime("%Y-%m-%d-%H:%M:%S")
fullpath = f"{file_path}/{date}.png"
stitched_image.save(fullpath)
return fullpath
# pylint: disable=broad-exception-caught
except Exception as e:
return f"Error: Failed to take full-page screenshot. Details: {str(e)}"


@pytest.fixture(scope="module")
Expand Down
Loading