920 lines
28 KiB
C
920 lines
28 KiB
C
/*
|
|
* This file is part of Cockpit.
|
|
*
|
|
* Copyright (C) 2013 Red Hat, Inc.
|
|
*
|
|
* Cockpit is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU Lesser General Public License as published by
|
|
* the Free Software Foundation; either version 2.1 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* Cockpit is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
* along with Cockpit; If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include "mock-auth.h"
|
|
|
|
#include "cockpithandlers.h"
|
|
#include "cockpitws.h"
|
|
|
|
#include "common/cockpitconf.h"
|
|
#include "common/cockpittest.h"
|
|
#include "common/mock-io-stream.h"
|
|
#include "common/cockpitwebserver.h"
|
|
|
|
#include <glib.h>
|
|
|
|
#include <limits.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
|
|
/*
|
|
* To recalculate the checksums found in this file, do something like:
|
|
* $ XDG_DATA_DIRS=$PWD/src/bridge/mock-resource/system/ XDG_DATA_HOME=/nonexistant ./cockpit-bridge --packages
|
|
*/
|
|
#define CHECKSUM "$6d675909f0b33b83a48e67e29cea9797012ded09394546634b9cd967bbe3fbf5"
|
|
|
|
/* Mock override this from cockpitconf.c */
|
|
extern const gchar *cockpit_config_file;
|
|
|
|
#define PASSWORD "this is the password"
|
|
|
|
typedef struct {
|
|
CockpitHandlerData data;
|
|
CockpitWebServer *server;
|
|
CockpitAuth *auth;
|
|
GHashTable *headers;
|
|
GIOStream *io;
|
|
CockpitWebResponse *response;
|
|
gboolean response_done;
|
|
GMemoryOutputStream *output;
|
|
GMemoryInputStream *input;
|
|
GByteArray *buffer;
|
|
gchar *scratch;
|
|
gchar **roots;
|
|
gchar *login_html;
|
|
} Test;
|
|
|
|
static void
|
|
on_ready_get_result (GObject *source,
|
|
GAsyncResult *result,
|
|
gpointer user_data)
|
|
{
|
|
GAsyncResult **retval = user_data;
|
|
g_assert (retval != NULL);
|
|
g_assert (*retval == NULL);
|
|
*retval = g_object_ref (result);
|
|
}
|
|
|
|
static void
|
|
on_web_response_done_set_flag (CockpitWebResponse *response,
|
|
gboolean reuse,
|
|
gpointer user_data)
|
|
{
|
|
gboolean *flag = user_data;
|
|
g_assert (flag != NULL);
|
|
g_assert (*flag == FALSE);
|
|
*flag = TRUE;
|
|
}
|
|
|
|
static void
|
|
base_setup (Test *test)
|
|
{
|
|
const gchar *static_roots[] = { SRCDIR "/src/ws", SRCDIR "/src/branding/default", NULL };
|
|
GError *error = NULL;
|
|
|
|
test->server = cockpit_web_server_new (NULL, 0, NULL, NULL, &error);
|
|
g_assert_no_error (error);
|
|
|
|
cockpit_web_server_start (test->server);
|
|
|
|
/* Other test->data fields are fine NULL */
|
|
memset (&test->data, 0, sizeof (test->data));
|
|
|
|
test->auth = cockpit_auth_new (FALSE);
|
|
test->roots = cockpit_web_response_resolve_roots (static_roots);
|
|
test->login_html = g_strdup(SRCDIR "/src/ws/login.html");
|
|
|
|
test->data.auth = test->auth;
|
|
test->data.branding_roots = (const gchar **)test->roots;
|
|
test->data.login_html = (const gchar *)test->login_html;
|
|
test->data.login_po_html = NULL;
|
|
|
|
test->headers = cockpit_web_server_new_table ();
|
|
|
|
test->output = G_MEMORY_OUTPUT_STREAM (g_memory_output_stream_new (NULL, 0, g_realloc, g_free));
|
|
test->input = G_MEMORY_INPUT_STREAM (g_memory_input_stream_new ());
|
|
|
|
test->io = mock_io_stream_new (G_INPUT_STREAM (test->input),
|
|
G_OUTPUT_STREAM (test->output));
|
|
}
|
|
|
|
static void
|
|
setup (Test *test,
|
|
gconstpointer path)
|
|
{
|
|
base_setup (test);
|
|
test->response = cockpit_web_response_new (test->io, path, path, NULL, NULL);
|
|
g_signal_connect (test->response, "done",
|
|
G_CALLBACK (on_web_response_done_set_flag),
|
|
&test->response_done);
|
|
}
|
|
|
|
static void
|
|
teardown (Test *test,
|
|
gconstpointer path)
|
|
{
|
|
g_clear_object (&test->auth);
|
|
g_clear_object (&test->server);
|
|
g_clear_object (&test->output);
|
|
g_clear_object (&test->input);
|
|
g_clear_object (&test->io);
|
|
g_free (test->login_html);
|
|
g_hash_table_destroy (test->headers);
|
|
g_free (test->scratch);
|
|
g_object_unref (test->response);
|
|
g_strfreev (test->roots);
|
|
|
|
cockpit_assert_expected ();
|
|
}
|
|
|
|
static const gchar *
|
|
output_as_string (Test *test)
|
|
{
|
|
while (!test->response_done)
|
|
g_main_context_iteration (NULL, TRUE);
|
|
|
|
g_free (test->scratch);
|
|
test->scratch = g_strndup (g_memory_output_stream_get_data (test->output),
|
|
g_memory_output_stream_get_data_size (test->output));
|
|
return test->scratch;
|
|
}
|
|
|
|
static void
|
|
test_login_no_cookie (Test *test,
|
|
gconstpointer path)
|
|
{
|
|
gboolean ret;
|
|
|
|
ret = cockpit_handler_default (test->server, path, test->headers, test->response, &test->data);
|
|
|
|
g_assert (ret == TRUE);
|
|
|
|
cockpit_assert_strmatch (output_as_string (test), "HTTP/1.1 401 Authentication failed\r\n*");
|
|
}
|
|
|
|
static void
|
|
include_cookie_as_if_client (GHashTable *resp_headers,
|
|
GHashTable *req_headers)
|
|
{
|
|
gchar *cookie;
|
|
gchar *end;
|
|
|
|
cookie = g_strdup (g_hash_table_lookup (resp_headers, "Set-Cookie"));
|
|
g_assert (cookie != NULL);
|
|
end = strchr (cookie, ';');
|
|
g_assert (end != NULL);
|
|
end[0] = '\0';
|
|
|
|
g_hash_table_insert (req_headers, g_strdup ("Cookie"), cookie);
|
|
}
|
|
|
|
static void
|
|
test_login_with_cookie (Test *test,
|
|
gconstpointer path)
|
|
{
|
|
GError *error = NULL;
|
|
GAsyncResult *result = NULL;
|
|
JsonObject *response;
|
|
GHashTable *headers;
|
|
gboolean ret;
|
|
|
|
headers = mock_auth_basic_header ("me", PASSWORD);
|
|
|
|
cockpit_auth_login_async (test->auth, path, NULL, headers, on_ready_get_result, &result);
|
|
g_hash_table_unref (headers);
|
|
while (result == NULL)
|
|
g_main_context_iteration (NULL, TRUE);
|
|
response = cockpit_auth_login_finish (test->auth, result, NULL, test->headers, &error);
|
|
g_object_unref (result);
|
|
|
|
g_assert_no_error (error);
|
|
g_assert (response != NULL);
|
|
json_object_unref (response);
|
|
|
|
include_cookie_as_if_client (test->headers, test->headers);
|
|
|
|
ret = cockpit_handler_default (test->server, path, test->headers, test->response, &test->data);
|
|
|
|
g_assert (ret == TRUE);
|
|
|
|
cockpit_assert_strmatch (output_as_string (test), "HTTP/1.1 200 OK\r\n*\r\n\r\n{*");
|
|
}
|
|
|
|
static void
|
|
test_login_bad (Test *test,
|
|
gconstpointer path)
|
|
{
|
|
gboolean ret;
|
|
GHashTable *headers;
|
|
|
|
headers = cockpit_web_server_new_table ();
|
|
g_hash_table_insert (headers, g_strdup ("Authorization"), g_strdup ("booyah"));
|
|
ret = cockpit_handler_default (test->server, path, headers, test->response, &test->data);
|
|
g_hash_table_unref (headers);
|
|
|
|
g_assert (ret == TRUE);
|
|
cockpit_assert_strmatch (output_as_string (test), "HTTP/1.1 401 Authentication failed\r\n*");
|
|
}
|
|
|
|
static void
|
|
test_login_fail (Test *test,
|
|
gconstpointer path)
|
|
{
|
|
gboolean ret;
|
|
GHashTable *headers;
|
|
|
|
headers = mock_auth_basic_header ("booo", "yah");
|
|
ret = cockpit_handler_default (test->server, path, test->headers, test->response, &test->data);
|
|
g_hash_table_unref (headers);
|
|
|
|
while (!test->response_done)
|
|
g_main_context_iteration (NULL, TRUE);
|
|
|
|
g_assert (ret == TRUE);
|
|
cockpit_assert_strmatch (output_as_string (test), "HTTP/1.1 401 Authentication failed\r\n*");
|
|
}
|
|
|
|
static GHashTable *
|
|
split_headers (const gchar *output)
|
|
{
|
|
GHashTable *headers;
|
|
gchar **lines;
|
|
gchar **parts;
|
|
gint i;
|
|
|
|
headers = cockpit_web_server_new_table ();
|
|
lines = g_strsplit (output, "\r\n", -1);
|
|
for (i = 1; lines[i] != NULL && lines[i][0] != '\0'; i++)
|
|
{
|
|
parts = g_strsplit (lines[i], ":", 2);
|
|
g_hash_table_insert (headers, g_strstrip (parts[0]), g_strstrip (parts[1]));
|
|
g_free (parts);
|
|
}
|
|
|
|
g_strfreev (lines);
|
|
return headers;
|
|
}
|
|
|
|
static void
|
|
test_login_accept (Test *test,
|
|
gconstpointer path)
|
|
{
|
|
CockpitWebService *service;
|
|
gboolean ret;
|
|
const gchar *output;
|
|
GHashTable *headers;
|
|
CockpitCreds *creds;
|
|
const gchar *token;
|
|
|
|
headers = mock_auth_basic_header ("me", PASSWORD);
|
|
g_hash_table_insert (headers, g_strdup ("X-Authorize"), g_strdup ("password"));
|
|
ret = cockpit_handler_default (test->server, path, headers, test->response, &test->data);
|
|
g_hash_table_unref (headers);
|
|
|
|
g_assert (ret == TRUE);
|
|
|
|
output = output_as_string (test);
|
|
cockpit_assert_strmatch (output, "HTTP/1.1 200 OK\r\n*");
|
|
cockpit_assert_strmatch (output, "*Secure; *");
|
|
|
|
/* Check that returned cookie that works */
|
|
headers = split_headers (output);
|
|
include_cookie_as_if_client (headers, test->headers);
|
|
|
|
service = cockpit_auth_check_cookie (test->auth, "/cockpit", test->headers);
|
|
g_assert (service != NULL);
|
|
creds = cockpit_web_service_get_creds (service);
|
|
g_assert_cmpstr (cockpit_creds_get_user (creds), ==, "me");
|
|
g_assert_cmpstr (g_bytes_get_data (cockpit_creds_get_password (creds), NULL), ==, PASSWORD);
|
|
|
|
token = cockpit_creds_get_csrf_token (creds);
|
|
g_assert (strstr (output, token));
|
|
|
|
g_hash_table_destroy (headers);
|
|
g_object_unref (service);
|
|
}
|
|
|
|
static void
|
|
test_favicon_ico (Test *test,
|
|
gconstpointer path)
|
|
{
|
|
const gchar *output;
|
|
gboolean ret;
|
|
|
|
ret = cockpit_handler_root (test->server, path, test->headers, test->response, &test->data);
|
|
|
|
g_assert (ret == TRUE);
|
|
|
|
output = output_as_string (test);
|
|
cockpit_assert_strmatch (output,
|
|
"HTTP/1.1 200 OK\r\n*"
|
|
"Content-Length: *\r\n"
|
|
"*");
|
|
}
|
|
|
|
static void
|
|
test_ping (Test *test,
|
|
gconstpointer path)
|
|
{
|
|
const gchar *output;
|
|
gboolean ret;
|
|
|
|
ret = cockpit_handler_ping (test->server, path, test->headers, test->response, &test->data);
|
|
|
|
g_assert (ret == TRUE);
|
|
|
|
output = output_as_string (test);
|
|
cockpit_assert_strmatch (output,
|
|
"HTTP/1.1 200 OK\r\n*"
|
|
"Access-Control-Allow-Origin: *\r\n*"
|
|
"\"cockpit\"*");
|
|
}
|
|
|
|
typedef struct {
|
|
const gchar *path;
|
|
const gchar *org_path;
|
|
const gchar *auth;
|
|
const gchar *expect;
|
|
const gchar *config;
|
|
gboolean with_home;
|
|
} DefaultFixture;
|
|
|
|
static void
|
|
setup_default (Test *test,
|
|
gconstpointer data)
|
|
{
|
|
const DefaultFixture *fixture = data;
|
|
JsonObject *response;
|
|
GError *error = NULL;
|
|
GAsyncResult *result = NULL;
|
|
GHashTable *headers;
|
|
|
|
cockpit_config_file = fixture->config;
|
|
|
|
if (fixture->config)
|
|
g_setenv ("XDG_CONFIG_DIRS", fixture->config, TRUE);
|
|
else
|
|
g_unsetenv ("XDG_CONFIG_DIRS");
|
|
|
|
g_setenv ("XDG_DATA_DIRS", SRCDIR "/src/bridge/mock-resource/system", TRUE);
|
|
if (fixture->with_home)
|
|
g_setenv ("XDG_DATA_HOME", SRCDIR "/src/bridge/mock-resource/home", TRUE);
|
|
else
|
|
g_setenv ("XDG_DATA_HOME", "/nonexistant", TRUE);
|
|
|
|
base_setup (test);
|
|
test->response = cockpit_web_response_new (test->io,
|
|
fixture->org_path ? fixture->org_path : fixture->path,
|
|
fixture->path, NULL, NULL);
|
|
g_signal_connect (test->response, "done",
|
|
G_CALLBACK (on_web_response_done_set_flag),
|
|
&test->response_done);
|
|
|
|
if (fixture->auth)
|
|
{
|
|
headers = mock_auth_basic_header ("bridge-user", PASSWORD);
|
|
|
|
cockpit_auth_login_async (test->auth, fixture->auth, NULL, headers, on_ready_get_result, &result);
|
|
g_hash_table_unref (headers);
|
|
while (result == NULL)
|
|
g_main_context_iteration (NULL, TRUE);
|
|
response = cockpit_auth_login_finish (test->auth, result, NULL, test->headers, &error);
|
|
g_object_unref (result);
|
|
|
|
g_assert_no_error (error);
|
|
g_assert (response != NULL);
|
|
json_object_unref (response);
|
|
|
|
include_cookie_as_if_client (test->headers, test->headers);
|
|
}
|
|
}
|
|
|
|
static void
|
|
teardown_default (Test *test,
|
|
gconstpointer data)
|
|
{
|
|
const DefaultFixture *fixture = data;
|
|
|
|
g_unsetenv ("XDG_DATA_DIRS");
|
|
g_unsetenv ("XDG_DATA_HOME");
|
|
|
|
teardown (test, fixture->path);
|
|
cockpit_conf_cleanup ();
|
|
};
|
|
|
|
static void
|
|
test_default (Test *test,
|
|
gconstpointer data)
|
|
{
|
|
const DefaultFixture *fixture = data;
|
|
gboolean ret;
|
|
|
|
ret = cockpit_handler_default (test->server, fixture->path, test->headers, test->response, &test->data);
|
|
|
|
if (fixture->expect)
|
|
{
|
|
g_assert (ret == TRUE);
|
|
cockpit_assert_strmatch (output_as_string (test), fixture->expect);
|
|
}
|
|
else
|
|
{
|
|
g_assert (ret == FALSE);
|
|
}
|
|
}
|
|
|
|
static const DefaultFixture fixture_resource_checksum = {
|
|
.path = "/cockpit/" CHECKSUM "/test/sub/file.ext",
|
|
.auth = "/cockpit",
|
|
.expect = "HTTP/1.1 200*"
|
|
"These are the contents of file.ext*"
|
|
};
|
|
|
|
static void
|
|
test_resource_checksum (Test *test,
|
|
gconstpointer data)
|
|
{
|
|
CockpitWebResponse *response;
|
|
gboolean response_done = FALSE;
|
|
GInputStream *input;
|
|
GOutputStream *output;
|
|
GIOStream *io;
|
|
gchar *string;
|
|
const gchar *path;
|
|
|
|
/* Prime the checksums with dummy request */
|
|
output = g_memory_output_stream_new (NULL, 0, g_realloc, g_free);
|
|
input = g_memory_input_stream_new ();
|
|
io = mock_io_stream_new (input, output);
|
|
path = "/cockpit/@localhost/checksum";
|
|
response = cockpit_web_response_new (io, path, path, NULL, NULL);
|
|
g_signal_connect (response, "done", G_CALLBACK (on_web_response_done_set_flag), &response_done);
|
|
g_assert (cockpit_handler_default (test->server, path, test->headers, response, &test->data));
|
|
|
|
while (!response_done)
|
|
g_main_context_iteration (NULL, TRUE);
|
|
|
|
string = g_strndup (g_memory_output_stream_get_data (G_MEMORY_OUTPUT_STREAM (output)),
|
|
g_memory_output_stream_get_data_size (G_MEMORY_OUTPUT_STREAM (output)));
|
|
cockpit_assert_strmatch (string, "HTTP/1.1 200*");
|
|
g_free (string);
|
|
|
|
g_object_unref (output);
|
|
g_object_unref (input);
|
|
g_object_unref (io);
|
|
g_object_unref (response);
|
|
|
|
/* And now run the real test */
|
|
test_default (test, data);
|
|
}
|
|
|
|
|
|
static const DefaultFixture fixture_shell_path_index = {
|
|
.path = "/",
|
|
.org_path = "/path/",
|
|
.auth = "/cockpit",
|
|
.with_home = TRUE,
|
|
.expect = "HTTP/1.1 200*"
|
|
"<base href=\"/path/cockpit/@localhost/another/test.html\">*"
|
|
"<title>In home dir</title>*"
|
|
};
|
|
|
|
static const DefaultFixture fixture_shell_path_package = {
|
|
.path = "/system/host",
|
|
.org_path = "/path/system/host",
|
|
.auth = "/cockpit",
|
|
.expect = "HTTP/1.1 200*"
|
|
"<base href=\"/path/cockpit/" CHECKSUM "/another/test.html\">*"
|
|
"<title>In system dir</title>*"
|
|
};
|
|
|
|
static const DefaultFixture fixture_shell_path_host = {
|
|
.path = "/@localhost/system/host",
|
|
.org_path = "/path/@localhost/system/host",
|
|
.with_home = TRUE,
|
|
.auth = "/cockpit",
|
|
.expect = "HTTP/1.1 200*"
|
|
"<base href=\"/path/cockpit/@localhost/another/test.html\">*"
|
|
"<title>In home dir</title>*"
|
|
};
|
|
|
|
static const DefaultFixture fixture_shell_path_login = {
|
|
.path = "/system/host",
|
|
.org_path = "/path/system/host",
|
|
.auth = NULL,
|
|
.expect = "HTTP/1.1 200*"
|
|
"Set-Cookie: cockpit=deleted; PATH=/; HttpOnly\r*"
|
|
"<html>*"
|
|
"<base href=\"/path/\">*"
|
|
"login-button*"
|
|
};
|
|
|
|
static const DefaultFixture fixture_shell_index = {
|
|
.path = "/",
|
|
.auth = "/cockpit",
|
|
.with_home = TRUE,
|
|
.expect = "HTTP/1.1 200*"
|
|
"Cache-Control: no-cache, no-store*"
|
|
"<base href=\"/cockpit/@localhost/another/test.html\">*"
|
|
"<title>In home dir</title>*"
|
|
};
|
|
|
|
static const DefaultFixture fixture_machine_shell_index = {
|
|
.path = "/=machine",
|
|
.auth = "/cockpit+=machine",
|
|
.config = SRCDIR "/src/ws/mock-config/cockpit/cockpit.conf",
|
|
.expect = "HTTP/1.1 200*"
|
|
"<base href=\"/cockpit+=machine/" CHECKSUM "/second/test.html\">*"
|
|
"<title>In system dir</title>*"
|
|
};
|
|
|
|
static const DefaultFixture fixture_shell_configured_index = {
|
|
.path = "/",
|
|
.auth = "/cockpit",
|
|
.with_home = TRUE,
|
|
.config = SRCDIR "/src/ws/mock-config/cockpit/cockpit.conf",
|
|
.expect = "HTTP/1.1 200*"
|
|
"<base href=\"/cockpit/@localhost/second/test.html\">*"
|
|
"<title>In system dir</title>*"
|
|
};
|
|
|
|
static const DefaultFixture fixture_shell_package = {
|
|
.path = "/system/host",
|
|
.auth = "/cockpit",
|
|
.expect = "HTTP/1.1 200*"
|
|
"<base href=\"/cockpit/" CHECKSUM "/another/test.html\">*"
|
|
"<title>In system dir</title>*"
|
|
};
|
|
|
|
static const DefaultFixture fixture_shell_host = {
|
|
.path = "/@localhost/system/host",
|
|
.with_home = TRUE,
|
|
.auth = "/cockpit",
|
|
.expect = "HTTP/1.1 200*"
|
|
"<base href=\"/cockpit/@localhost/another/test.html\">*"
|
|
"<title>In home dir</title>*"
|
|
};
|
|
|
|
static const DefaultFixture fixture_shell_host_short = {
|
|
.path = "/@/system/page",
|
|
.auth = "/cockpit",
|
|
.expect = "HTTP/1.1 404*"
|
|
};
|
|
|
|
static const DefaultFixture fixture_shell_package_short = {
|
|
.path = "//page",
|
|
.auth = "/cockpit",
|
|
.expect = "HTTP/1.1 404*"
|
|
};
|
|
|
|
static const DefaultFixture fixture_machine_shell_package_short = {
|
|
.path = "/=/",
|
|
.auth = "/cockpit",
|
|
.expect = "HTTP/1.1 404*",
|
|
.config = SRCDIR "/src/ws/mock-config/cockpit/cockpit.conf"
|
|
};
|
|
|
|
static const DefaultFixture fixture_shell_package_invalid = {
|
|
.path = "/invalid.path/page",
|
|
.auth = "/cockpit",
|
|
.expect = "HTTP/1.1 404*"
|
|
};
|
|
|
|
static const DefaultFixture fixture_shell_login = {
|
|
.path = "/system/host",
|
|
.auth = NULL,
|
|
.expect = "HTTP/1.1 200*"
|
|
"Set-Cookie: cockpit=deleted*"
|
|
"<html>*"
|
|
"<base href=\"/\">*"
|
|
"login-button*"
|
|
};
|
|
|
|
static const DefaultFixture fixture_resource_short = {
|
|
.path = "/cockpit",
|
|
.auth = "/cockpit",
|
|
.expect = "HTTP/1.1 404*"
|
|
};
|
|
|
|
static const DefaultFixture fixture_resource_host = {
|
|
.path = "/cockpit/@localhost/test/sub/file.ext",
|
|
.auth = "/cockpit",
|
|
.expect = "HTTP/1.1 200*"
|
|
"These are the contents of file.ext*"
|
|
};
|
|
|
|
static const DefaultFixture fixture_resource_host_short = {
|
|
.path = "/cockpit/@/test/sub/file.ext",
|
|
.auth = "/cockpit",
|
|
.expect = "HTTP/1.1 404*"
|
|
};
|
|
|
|
static const DefaultFixture fixture_resource_application = {
|
|
.path = "/cockpit+application/@localhost/test/sub/file.ext",
|
|
.auth = "/cockpit+application",
|
|
.expect = "HTTP/1.1 200*"
|
|
"These are the contents of file.ext*"
|
|
};
|
|
|
|
static const DefaultFixture fixture_resource_application_short = {
|
|
.path = "/cockpit+/@localhost/test/sub/file.ext",
|
|
.auth = "/cockpit+",
|
|
.expect = "HTTP/1.1 401*"
|
|
};
|
|
|
|
static const DefaultFixture fixture_resource_missing = {
|
|
.path = "/cockpit/another/file.html",
|
|
.auth = "/cockpit",
|
|
.expect = "HTTP/1.1 404*"
|
|
};
|
|
|
|
static const DefaultFixture fixture_resource_auth = {
|
|
.path = "/cockpit/@localhost/yyy/zzz",
|
|
.auth = NULL,
|
|
.expect = "HTTP/1.1 401*"
|
|
};
|
|
|
|
static const DefaultFixture fixture_resource_login = {
|
|
.path = "/cockpit/@localhost/yyy/zzz.html",
|
|
.auth = NULL,
|
|
.expect = "HTTP/1.1 200*"
|
|
"Set-Cookie: cockpit=deleted*"
|
|
"<html>*"
|
|
"login-button*"
|
|
};
|
|
|
|
static const DefaultFixture fixture_static_simple = {
|
|
.path = "/cockpit/static/branding.css",
|
|
.auth = "/cockpit",
|
|
.expect = "HTTP/1.1 200*"
|
|
"Cache-Control: max-age=31556926, public*"
|
|
"#badge*"
|
|
"url(\"logo.png\");*"
|
|
};
|
|
|
|
static const DefaultFixture fixture_host_static = {
|
|
.path = "/cockpit+=host/static/branding.css",
|
|
.auth = "/cockpit+=host",
|
|
.config = SRCDIR "/src/ws/mock-config/cockpit/cockpit.conf",
|
|
.expect = "HTTP/1.1 200*"
|
|
"Cache-Control: max-age=86400, private*"
|
|
"#badge*"
|
|
"url(\"logo.png\");*"
|
|
};
|
|
|
|
static const DefaultFixture fixture_host_login = {
|
|
.path = "/=host/system",
|
|
.auth = NULL,
|
|
.config = SRCDIR "/src/ws/mock-config/cockpit/cockpit.conf",
|
|
.expect = "HTTP/1.1 200*"
|
|
"Set-Cookie: machine-cockpit+host=deleted*"
|
|
"<html>*"
|
|
"<base href=\"/\">*"
|
|
"login-button*"
|
|
};
|
|
|
|
static const DefaultFixture fixture_host_static_no_auth = {
|
|
.path = "/cockpit+=host/static/branding.css",
|
|
.expect = "HTTP/1.1 403*",
|
|
.config = SRCDIR "/src/ws/mock-config/cockpit/cockpit.conf"
|
|
};
|
|
|
|
static const DefaultFixture fixture_static_application = {
|
|
.path = "/cockpit+application/static/branding.css",
|
|
.auth = NULL,
|
|
.expect = "HTTP/1.1 200*"
|
|
"Cache-Control: max-age=31556926, public*"
|
|
"#badge*"
|
|
"url(\"logo.png\");*"
|
|
};
|
|
|
|
static void
|
|
make_io_streams (GIOStream **io_a,
|
|
GIOStream **io_b)
|
|
{
|
|
GSocket *socket1, *socket2;
|
|
GError *error = NULL;
|
|
int fds[2];
|
|
|
|
if (socketpair (PF_UNIX, SOCK_STREAM, 0, fds) < 0)
|
|
g_assert_not_reached ();
|
|
|
|
socket1 = g_socket_new_from_fd (fds[0], &error);
|
|
g_assert_no_error (error);
|
|
|
|
socket2 = g_socket_new_from_fd (fds[1], &error);
|
|
g_assert_no_error (error);
|
|
|
|
*io_a = G_IO_STREAM (g_socket_connection_factory_create_connection (socket1));
|
|
*io_b = G_IO_STREAM (g_socket_connection_factory_create_connection (socket2));
|
|
|
|
g_object_unref (socket1);
|
|
g_object_unref (socket2);
|
|
}
|
|
|
|
static void
|
|
on_error_not_reached (WebSocketConnection *ws,
|
|
GError *error,
|
|
gpointer user_data)
|
|
{
|
|
g_assert (error != NULL);
|
|
|
|
/* At this point we know this will fail, but is informative */
|
|
g_assert_no_error (error);
|
|
}
|
|
|
|
static void
|
|
on_message_get_bytes (WebSocketConnection *ws,
|
|
WebSocketDataType type,
|
|
GBytes *message,
|
|
gpointer user_data)
|
|
{
|
|
GBytes **received = user_data;
|
|
g_assert_cmpint (type, ==, WEB_SOCKET_DATA_TEXT);
|
|
if (*received != NULL)
|
|
{
|
|
gsize length;
|
|
gconstpointer data = g_bytes_get_data (message, &length);
|
|
g_test_message ("received unexpected extra message: %.*s", (int)length, (gchar *)data);
|
|
g_assert_not_reached ();
|
|
}
|
|
*received = g_bytes_ref (message);
|
|
}
|
|
|
|
static void
|
|
test_socket_unauthenticated (void)
|
|
{
|
|
CockpitWebServer *server;
|
|
WebSocketConnection *client;
|
|
GBytes *received = NULL;
|
|
GIOStream *io_a, *io_b;
|
|
GBytes *payload;
|
|
const gchar *problem;
|
|
const gchar *command;
|
|
const gchar *unused;
|
|
gchar *channel;
|
|
JsonObject *options;
|
|
GError *error = NULL;
|
|
|
|
make_io_streams (&io_a, &io_b);
|
|
|
|
server = cockpit_web_server_new (NULL, 0, NULL, NULL, &error);
|
|
g_assert_no_error (error);
|
|
|
|
client = g_object_new (WEB_SOCKET_TYPE_CLIENT,
|
|
"url", "ws://127.0.0.1/unused",
|
|
"origin", "http://127.0.0.1",
|
|
"io-stream", io_a,
|
|
NULL);
|
|
|
|
g_signal_connect (client, "error", G_CALLBACK (on_error_not_reached), NULL);
|
|
|
|
/* Matching the above origin */
|
|
cockpit_ws_default_host_header = "127.0.0.1";
|
|
|
|
g_assert (cockpit_handler_socket (server, "/cockpit/socket", "/cockpit/socket",
|
|
"GET", io_b, NULL, NULL, NULL));
|
|
|
|
g_signal_connect (client, "message", G_CALLBACK (on_message_get_bytes), &received);
|
|
|
|
/* Should close right after opening */
|
|
while (web_socket_connection_get_ready_state (client) != WEB_SOCKET_STATE_CLOSED)
|
|
g_main_context_iteration (NULL, TRUE);
|
|
|
|
/* And we should have received a message */
|
|
g_assert (received != NULL);
|
|
|
|
payload = cockpit_transport_parse_frame (received, &channel);
|
|
g_assert (payload != NULL);
|
|
g_assert (channel == NULL);
|
|
g_bytes_unref (received);
|
|
|
|
g_assert (cockpit_transport_parse_command (payload, &command, &unused, &options));
|
|
g_bytes_unref (payload);
|
|
|
|
g_assert_cmpstr (command, ==, "init");
|
|
g_assert (cockpit_json_get_string (options, "problem", NULL, &problem));
|
|
g_assert_cmpstr (problem, ==, "no-session");
|
|
json_object_unref (options);
|
|
|
|
g_object_unref (client);
|
|
|
|
while (g_main_context_iteration (NULL, FALSE));
|
|
|
|
g_object_unref (io_a);
|
|
g_object_unref (io_b);
|
|
g_object_unref (server);
|
|
}
|
|
|
|
int
|
|
main (int argc,
|
|
char *argv[])
|
|
{
|
|
/* See mock-resource */
|
|
cockpit_ws_shell_component = "/another/test.html";
|
|
cockpit_ws_session_program = BUILDDIR "/mock-auth-command";
|
|
|
|
cockpit_test_init (&argc, &argv);
|
|
|
|
g_test_add ("/handlers/login/no-cookie", Test, "/cockpit/login",
|
|
setup, test_login_no_cookie, teardown);
|
|
g_test_add ("/handlers/login/with-cookie", Test, "/cockpit+app/login",
|
|
setup, test_login_with_cookie, teardown);
|
|
g_test_add ("/handlers/login/post-bad", Test, "/cockpit/login",
|
|
setup, test_login_bad, teardown);
|
|
g_test_add ("/handlers/login/post-fail", Test, "/cockpit/login",
|
|
setup, test_login_fail, teardown);
|
|
g_test_add ("/handlers/login/post-accept", Test, "/cockpit/login",
|
|
setup, test_login_accept, teardown);
|
|
|
|
g_test_add ("/handlers/ping", Test, "/ping",
|
|
setup, test_ping, teardown);
|
|
|
|
g_test_add ("/handlers/shell/index", Test, &fixture_shell_index,
|
|
setup_default, test_default, teardown_default);
|
|
g_test_add ("/handlers/shell/machine-index", Test, &fixture_machine_shell_index,
|
|
setup_default, test_default, teardown_default);
|
|
g_test_add ("/handlers/shell/configured_index", Test, &fixture_shell_configured_index,
|
|
setup_default, test_default, teardown_default);
|
|
g_test_add ("/handlers/shell/package", Test, &fixture_shell_package,
|
|
setup_default, test_default, teardown_default);
|
|
g_test_add ("/handlers/shell/host", Test, &fixture_shell_host,
|
|
setup_default, test_default, teardown_default);
|
|
g_test_add ("/handlers/shell/host-short", Test, &fixture_shell_host_short,
|
|
setup_default, test_default, teardown_default);
|
|
g_test_add ("/handlers/shell/package-short", Test, &fixture_shell_package_short,
|
|
setup_default, test_default, teardown_default);
|
|
g_test_add ("/handlers/shell/machine-package-short", Test, &fixture_machine_shell_package_short,
|
|
setup_default, test_default, teardown_default);
|
|
g_test_add ("/handlers/shell/package-invalid", Test, &fixture_shell_package_invalid,
|
|
setup_default, test_default, teardown_default);
|
|
g_test_add ("/handlers/shell/login", Test, &fixture_shell_login,
|
|
setup_default, test_default, teardown_default);
|
|
g_test_add ("/handlers/shell/path-index", Test, &fixture_shell_path_index,
|
|
setup_default, test_default, teardown_default);
|
|
g_test_add ("/handlers/shell/path-package", Test, &fixture_shell_path_package,
|
|
setup_default, test_default, teardown_default);
|
|
g_test_add ("/handlers/shell/path-host", Test, &fixture_shell_path_host,
|
|
setup_default, test_default, teardown_default);
|
|
g_test_add ("/handlers/shell/path-login", Test, &fixture_shell_path_login,
|
|
setup_default, test_default, teardown_default);
|
|
|
|
g_test_add ("/handlers/resource/checksum", Test, &fixture_resource_checksum,
|
|
setup_default, test_resource_checksum, teardown_default);
|
|
|
|
g_test_add ("/handlers/resource/short", Test, &fixture_resource_short,
|
|
setup_default, test_default, teardown_default);
|
|
g_test_add ("/handlers/resource/host", Test, &fixture_resource_host,
|
|
setup_default, test_default, teardown_default);
|
|
g_test_add ("/handlers/resource/host-short", Test, &fixture_resource_host_short,
|
|
setup_default, test_default, teardown_default);
|
|
g_test_add ("/handlers/resource/application", Test, &fixture_resource_application,
|
|
setup_default, test_default, teardown_default);
|
|
g_test_add ("/handlers/resource/application-short", Test, &fixture_resource_application_short,
|
|
setup_default, test_default, teardown_default);
|
|
g_test_add ("/handlers/resource/missing", Test, &fixture_resource_missing,
|
|
setup_default, test_default, teardown_default);
|
|
g_test_add ("/handlers/resource/auth", Test, &fixture_resource_auth,
|
|
setup_default, test_default, teardown_default);
|
|
g_test_add ("/handlers/resource/login", Test, &fixture_resource_login,
|
|
setup_default, test_default, teardown_default);
|
|
|
|
g_test_add ("/handlers/static/simple", Test, &fixture_static_simple,
|
|
setup_default, test_default, teardown_default);
|
|
g_test_add ("/handlers/static/host-static", Test, &fixture_host_static,
|
|
setup_default, test_default, teardown_default);
|
|
g_test_add ("/handlers/static/host-login", Test, &fixture_host_login,
|
|
setup_default, test_default, teardown_default);
|
|
g_test_add ("/handlers/static/host-static-no-auth", Test, &fixture_host_static_no_auth,
|
|
setup_default, test_default, teardown_default);
|
|
g_test_add ("/handlers/static/application", Test, &fixture_static_application,
|
|
setup_default, test_default, teardown_default);
|
|
|
|
g_test_add ("/handlers/favicon", Test, "/favicon.ico",
|
|
setup, test_favicon_ico, teardown);
|
|
|
|
g_test_add_func ("/handlers/noauth", test_socket_unauthenticated);
|
|
|
|
return g_test_run ();
|
|
}
|