def test_images_options_newer_proxy(proxy_server, signed_ticket): auth.add_signed_ticket(signed_ticket.data) path = "/images/" + signed_ticket.id # Note: the daemon does not report GET since the ticket is write only, and # it supports only "zero" (simulating old daemon). daemon_allow = {"OPTIONS", "PUT", "PATCH"} daemon_features = {"zero"} daemon_options = {"features": list(daemon_features)} daemon_body = json.dumps(daemon_options).encode("ascii") daemon_headers = {"Content-Type": "application/json", "Content-Length": "%d" % len(daemon_body), "Allow": ','.join(daemon_allow)} with requests_mock.Mocker() as m: m.options(requests_mock.ANY, status_code=200, text=daemon_body, headers=daemon_headers) res = http.request(proxy_server, "OPTIONS", path) # Validate the request. assert m.called assert m.last_request.path == path conn_timeout, read_timeout = m.last_request.timeout assert conn_timeout == proxy_server.imaged_connection_timeout_sec assert read_timeout == proxy_server.imaged_read_timeout_sec # Validate the response. assert res.status == 200 assert set(res.getheader("Allow").split(",")) == daemon_allow assert set(json.loads(res.read())["features"]) == daemon_features
def test_images_patch_error(proxy_server, signed_ticket): auth.add_signed_ticket(signed_ticket.data) response_code = 403 response_body = b"daemon response" response_headers = {"Content-Type": "text/plain"} with requests_mock.Mocker() as m: # Don't check anything, match errors are useless. m.patch( requests_mock.ANY, status_code=response_code, text=response_body) # Send a request to the proxy. res = http.patch( proxy_server, "/images/" + signed_ticket.id, {"op": "zero", "size": 1024}) # Validate response to proxy client. assert res.status == response_code # Note: requests adds charset=UTF-8 on RHEL 7. assert response_headers["Content-Type"] in res.getheader("Content-Type") error = res.read() assert response_body in error
def test_delete(proxy_server, signed_ticket): auth.add_signed_ticket(signed_ticket.data) resp = http.request( proxy_server, "DELETE", "/tickets/%s" % signed_ticket.id) assert resp.status == http_client.NO_CONTENT with pytest.raises(auth.NoSuchTicket): auth.get_ticket(signed_ticket.id)
def test_images_cors_options(proxy_server, signed_ticket): auth.add_signed_ticket(signed_ticket.data) request_headers = images_request_headers(signed_ticket.data) path = "/images/" + signed_ticket.id daemon_options = {"features": ["zero"]} daemon_body = json.dumps(daemon_options).encode("ascii") daemon_headers = {"Content-Type": "application/json", "Content-Length": "%d" % len(daemon_body), "Allow": "GET,PUT,PATCH,OPTIONS"} with requests_mock.Mocker() as m: m.options(requests_mock.ANY, status_code=200, text=daemon_body, headers=daemon_headers) res = http.request(proxy_server, "OPTIONS", path, headers=request_headers) assert m.called allowed_headers = split_header(res.getheader("access-control-allow-headers")) expected_headers = {"cache-control", "pragma", "authorization", "content-type", "content-length", "content-range", "range", "session-id"} assert res.status == 200 assert allowed_headers == expected_headers assert res.getheader("access-control-allow-origin") == "*" assert res.getheader("access-control-max-age") == "300"
def test_images_patch(proxy_server, signed_ticket): auth.add_signed_ticket(signed_ticket.data) path = "/images/" + signed_ticket.id msg = {"op": "zero", "offset": 0, "size": 1024, "flush": False} headers = {"User-Header": "user value"} with requests_mock.Mocker() as m: # Don't check anything, match errors are useless. m.patch(requests_mock.ANY, status_code=200) # Send a request to the proxy. res = http.patch(proxy_server, path, msg, headers=headers) # Validate proxy request to daemon. assert m.last_request.path == path assert m.last_request.headers["Content-Type"] == "application/json" assert m.last_request.headers["User-Header"] == "user value" body = json.dumps(msg).encode("utf-8") assert m.last_request.headers["Content-Length"] == str(len(body)) assert m.last_request.body.read() == body conn_timeout, read_timeout = m.last_request.timeout assert conn_timeout == proxy_server.imaged_connection_timeout_sec # PATCH cannot use a read timeout. assert read_timeout is None # Validate response to proxy client. assert res.status == 200 assert res.getheader("content-length") == "0"
def test_delete(proxy_server, signed_ticket): auth.add_signed_ticket(signed_ticket.data) resp = http.request(proxy_server, "DELETE", "/tickets/%s" % signed_ticket.id) assert resp.status == http_client.NO_CONTENT with pytest.raises(auth.NoSuchTicket): auth.get_ticket(signed_ticket.id)
def test_images_options_newer_daemon(proxy_server, signed_ticket): auth.add_signed_ticket(signed_ticket.data) path = "/images/" + signed_ticket.id proxy_allow = {"OPTIONS", "GET", "PUT", "PATCH"} proxy_features = {"zero", "flush"} # Note: this future daemon supports "POST" and "trim". daemon_allow = {"OPTIONS", "GET", "PUT", "PATCH", "POST"} daemon_features = {"zero", "flush", "trim"} daemon_options = {"features": list(daemon_features)} daemon_body = json.dumps(daemon_options).encode("ascii") daemon_headers = { "Content-Type": "application/json", "Content-Length": "%d" % len(daemon_body), "Allow": ','.join(daemon_allow) } with requests_mock.Mocker() as m: m.options(requests_mock.ANY, status_code=200, text=daemon_body, headers=daemon_headers) res = http.request(proxy_server, "OPTIONS", path) # Validate the request. assert m.called assert m.last_request.path == path # Validate the request. assert res.status == 200 assert set(res.getheader("Allow").split(",")) == proxy_allow assert set(json.loads(res.read())["features"]) == proxy_features
def test_authorize_ticket_installed_invalid_authorization_header( proxy_server, signed_ticket): auth.add_signed_ticket(signed_ticket.data) request = Request({"Authorization": "invalid"}) handler = RequestHandler(request) handler.decorated_method(signed_ticket.id) assert handler.ticket == auth.get_ticket(signed_ticket.id) assert handler.calls == [signed_ticket.id]
def test_authorize_ticket_installed_invalid_authorization_header(proxy_server, signed_ticket): auth.add_signed_ticket(signed_ticket.data) request = Request({"Authorization": "invalid"}) handler = RequestHandler(request) handler.decorated_method(signed_ticket.id) assert handler.ticket == auth.get_ticket(signed_ticket.id) assert handler.calls == [signed_ticket.id]
def test_authorize_ticket_expired_ignore_authorization_header( proxy_server, signed_ticket, monkeypatch): auth.add_signed_ticket(signed_ticket.data) handler = RequestHandler(None) monkeypatch.setattr(time, 'time', lambda: EXP + 1) with pytest.raises(exc.HTTPUnauthorized): handler.decorated_method(signed_ticket.id) assert handler.calls == [] assert handler.ticket is None
def test_authorize_ticket_expired(proxy_server, signed_ticket, monkeypatch): auth.add_signed_ticket(signed_ticket.data) handler = RequestHandler(Request()) monkeypatch.setattr(time, 'time', lambda: EXP+1) with pytest.raises(exc.HTTPUnauthorized): handler.decorated_method(signed_ticket.id) assert handler.calls == [] assert handler.ticket is None # Ticket expired but we don't remove it auth.get_ticket(signed_ticket.id)
def test_authorize_ticket_expired(proxy_server, signed_ticket, monkeypatch): auth.add_signed_ticket(signed_ticket.data) handler = RequestHandler(Request()) monkeypatch.setattr(time, 'time', lambda: EXP + 1) with pytest.raises(exc.HTTPUnauthorized): handler.decorated_method(signed_ticket.id) assert handler.calls == [] assert handler.ticket is None # Ticket expired but we don't remove it auth.get_ticket(signed_ticket.id)
def test_authorize_ticket_expired_ignore_authorization_header(proxy_server, signed_ticket, monkeypatch): auth.add_signed_ticket(signed_ticket.data) handler = RequestHandler(None) monkeypatch.setattr(time, 'time', lambda: EXP+1) with pytest.raises(exc.HTTPUnauthorized): handler.decorated_method(signed_ticket.id) assert handler.calls == [] assert handler.ticket is None
def test_images_put_flush(proxy_server, signed_ticket, flush): auth.add_signed_ticket(signed_ticket.data) path = "/images/" + signed_ticket.id + "?flush=" + flush + "&ignored=1" with requests_mock.Mocker() as m: # Don't check anything, match errors are useless. m.put(requests_mock.ANY, status_code=200) # Send a request to the proxy. res = http.request(proxy_server, "PUT", path, body="data") # Validate proxy request to daemon. url = urlparse(m.last_request.url) assert url.path == "/images/" + signed_ticket.id assert url.query == "flush=" + flush
def test_images_put_imaged_without_content_range(proxy_server, signed_ticket): auth.add_signed_ticket(signed_ticket.data) body = "hello" client_headers = { "Accept-Ranges": "bytes", } path = "/images/" + signed_ticket.id with requests_mock.Mocker() as m: m.put(signed_ticket.url + path, status_code=200, text=None) res = http.request(proxy_server, "PUT", path, body=body, headers=client_headers) assert m.called assert res.status == 200 assert res.getheader("content-length") == "0"
def test_images_options_newer_daemon(proxy_server, signed_ticket): auth.add_signed_ticket(signed_ticket.data) path = "/images/" + signed_ticket.id proxy_allow = {"OPTIONS", "GET", "PUT", "PATCH"} proxy_features = {"zero", "flush"} # Note: this future daemon supports "POST" and "trim". daemon_allow = {"OPTIONS", "GET", "PUT", "PATCH", "POST"} daemon_features = {"zero", "flush", "trim"} # New daemon support unix socket - proxy must not report this since it does # not support unix socket now. daemon_options = {"features": list(daemon_features), "unix_socket": "\0org.ovirt.imageio"} daemon_body = json.dumps(daemon_options).encode("ascii") daemon_headers = {"Content-Type": "application/json", "Content-Length": "%d" % len(daemon_body), "Allow": ','.join(daemon_allow)} with requests_mock.Mocker() as m: m.options(requests_mock.ANY, status_code=200, text=daemon_body, headers=daemon_headers) res = http.request( proxy_server, "OPTIONS", path) # Validate the request. assert m.called assert m.last_request.path == path # Validate the request. assert res.status == 200 assert set(res.getheader("Allow").split(",")) == proxy_allow proxy_options = json.loads(res.read()) assert set(proxy_options["features"]) == proxy_features assert "unix_socket" not in proxy_options
def test_images_get_imaged_with_installed_ticket(proxy_server, signed_ticket): auth.add_signed_ticket(signed_ticket.data) body = "hello" request_headers = { "Accept-Ranges": "bytes", } response_headers = { "Content-Length": "5", } path = "/images/" + signed_ticket.id with requests_mock.Mocker() as m: m.get(signed_ticket.url + path, status_code=200, text=body, headers=response_headers) res = http.request(proxy_server, "GET", path, headers=request_headers) assert m.called assert res.status == 200 assert res.read() == "hello" assert res.getheader("content-length") == "5"
def test_images_options_old_daemon_without_options(proxy_server, signed_ticket): auth.add_signed_ticket(signed_ticket.data) path = "/images/" + signed_ticket.id # Note: the daemon does not allow OPTIONS. assumed_daemon_allow = {"OPTIONS", "GET", "PUT"} assumed_daemon_features = [] with requests_mock.Mocker() as m: m.options(requests_mock.ANY, status_code=405, content=b'{"detail": "Invalid method OPTIONS"}') res = http.request(proxy_server, "OPTIONS", path) # Validate the request. assert m.called assert m.last_request.path == path # Validate the response. assert res.status == 200 assert set(res.getheader("Allow").split(",")) == assumed_daemon_allow assert json.loads(res.read())["features"] == assumed_daemon_features
def test_images_patch_error(proxy_server, signed_ticket): auth.add_signed_ticket(signed_ticket.data) response_code = 403 response_body = b"daemon response" response_headers = {"Content-Type": "text/plain"} with requests_mock.Mocker() as m: # Don't check anything, match errors are useless. m.patch(requests_mock.ANY, status_code=response_code, text=response_body) # Send a request to the proxy. res = http.patch(proxy_server, "/images/" + signed_ticket.id, { "op": "zero", "size": 1024 }) # Validate response to proxy client. assert res.status == response_code # Note: requests adds charset=UTF-8 on RHEL 7. assert response_headers["Content-Type"] in res.getheader("Content-Type") error = res.read() assert response_body in error
def test_images_options_newer_proxy(proxy_server, signed_ticket): auth.add_signed_ticket(signed_ticket.data) path = "/images/" + signed_ticket.id # Note: the daemon does not report GET since the ticket is write only, and # it supports only "zero" (simulating old daemon). daemon_allow = {"OPTIONS", "PUT", "PATCH"} daemon_features = {"zero"} daemon_options = {"features": list(daemon_features)} daemon_body = json.dumps(daemon_options).encode("ascii") daemon_headers = { "Content-Type": "application/json", "Content-Length": "%d" % len(daemon_body), "Allow": ','.join(daemon_allow) } with requests_mock.Mocker() as m: m.options(requests_mock.ANY, status_code=200, text=daemon_body, headers=daemon_headers) res = http.request(proxy_server, "OPTIONS", path) # Validate the request. assert m.called assert m.last_request.path == path conn_timeout, read_timeout = m.last_request.timeout assert conn_timeout == proxy_server.imaged_connection_timeout_sec assert read_timeout == proxy_server.imaged_read_timeout_sec # Validate the response. assert res.status == 200 assert set(res.getheader("Allow").split(",")) == daemon_allow assert set(json.loads(res.read())["features"]) == daemon_features
def test_images_cors_options(proxy_server, signed_ticket): auth.add_signed_ticket(signed_ticket.data) request_headers = images_request_headers(signed_ticket.data) path = "/images/" + signed_ticket.id daemon_options = {"features": ["zero"]} daemon_body = json.dumps(daemon_options).encode("ascii") daemon_headers = { "Content-Type": "application/json", "Content-Length": "%d" % len(daemon_body), "Allow": "GET,PUT,PATCH,OPTIONS" } with requests_mock.Mocker() as m: m.options(requests_mock.ANY, status_code=200, text=daemon_body, headers=daemon_headers) res = http.request(proxy_server, "OPTIONS", path, headers=request_headers) assert m.called allowed_headers = split_header( res.getheader("access-control-allow-headers")) expected_headers = { "cache-control", "pragma", "authorization", "content-type", "content-length", "content-range", "range", "session-id" } assert res.status == 200 assert allowed_headers == expected_headers assert res.getheader("access-control-allow-origin") == "*" assert res.getheader("access-control-max-age") == "300"
def test_authorize_ticket_installed(proxy_server, signed_ticket): auth.add_signed_ticket(signed_ticket.data) handler = RequestHandler(Request()) handler.decorated_method(signed_ticket.id) assert handler.ticket == auth.get_ticket(signed_ticket.id) assert handler.calls == [signed_ticket.id]
def test_images_patch_no_content(proxy_server, signed_ticket): auth.add_signed_ticket(signed_ticket.data) res = http.request(proxy_server, "PATCH", "/images/" + signed_ticket.id) assert res.status == 400