diff --git a/.gitignore b/.gitignore index 359c284..48e8b24 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /build/ -/.deps/ \ No newline at end of file +/.deps/ +/.vscode/ \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 254ab38..cb75952 100644 --- a/.travis.yml +++ b/.travis.yml @@ -24,7 +24,16 @@ addons: - python3-setuptools - python3-wheel - ninja-build + - libconfig-dev + - valgrind +env: + matrix: + - BUILD_TYPE=Coverage CHECK_TARGET=check + - BUILD_TYPE=Debug CHECK_TARGET=memcheck + - BUILD_TPYE=Release CHECK_TARGET=check + - BUILD_TYPE=MinSizeRel CHECK_TARGET=check + before_install: - mkdir .deps @@ -74,12 +83,27 @@ before_install: - sudo make install - cd .. - cd .. + # gtest + - wget https://github.com/google/googletest/archive/release-1.10.0.tar.gz + - tar -xf release-1.10.0.tar.gz + - cd googletest-release-1.10.0 + - mkdir .build + - cd .build + - cmake .. + - make + - sudo make install + - cd .. + - cd .. - cd .. before_script: - mkdir build - cd build - - cmake .. + - cmake -DCMAKE_BUILD_TYPE=$BUILD_TYPE .. script: - make \ No newline at end of file + - export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/usr/local/lib:/usr/local/lib/x86_64-linux-gnu" + - make $CHECK_TARGET + +after_success: + - bash <(curl -s https://codecov.io/bash) \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 1abb051..3b84d16 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,8 @@ cmake_minimum_required (VERSION 3.10) project(webfused VERSION 0.1.0 DESCRIPTION "Webfuse daemon") +option(WITHOUT_TESTS "disable unit tests" OFF) + set(THREADS_PREFER_PTHREAD_FLAG ON) find_package(Threads REQUIRED) @@ -9,6 +11,7 @@ pkg_check_modules(FUSE3 REQUIRED fuse3) pkg_check_modules(LWS REQUIRED libwebsockets) pkg_check_modules(JANSSON REQUIRED jansson) pkg_check_modules(UUID REQUIRED uuid) +pkg_check_modules(LIBCONFIG REQUIRED libconfig) pkg_check_modules(OPENSSL REQUIRED openssl) pkg_check_modules(WEBFUSE REQUIRED libwebfuse-adapter) @@ -30,24 +33,46 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) set(C_WARNINGS -Wall -Wextra) set(CMAKE_POSITION_INDEPENDENT_CODE ON) +set(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH};${CMAKE_CURRENT_SOURCE_DIR}/cmake") +include(coverage) + add_library(userdb STATIC src/userdb/userdb.c ) target_include_directories(userdb PUBLIC + ${LIBCONFIG_INCLUDE_DIRS} ${OPENSSL_INCLUDE_DIRS} ${JANSSON_INCLUDE_DIRS} ) target_compile_options(userdb PUBLIC ${OPENSSL_CFLAGS_OTHER}) +add_library(webfused-static STATIC + src/webfused/daemon.c + src/webfused/mountpoint_factory.c + src/webfused/config/config.c + src/webfused/config/factory.c + src/webfused/config/builder.c + src/webfused/config/settings.c + src/webfused/auth/authenticator.c + src/webfused/auth/factory.c + src/webfused/auth/file_authenticator.c + src/webfused/log/log.c + src/webfused/log/logger.c + src/webfused/log/manager.c + src/webfused/log/stderr_logger.c + src/webfused/log/syslog_logger.c +) add_executable(webfused - src/daemon/main.c + src/webfused/main.c ) -target_link_libraries(webfused PUBLIC +target_link_libraries(webfused PUBLIC + webfused-static userdb + ${LIBCONFIG_LIBRARIES} ${OPENSSL_LIBRARIES} ${WEBFUSE_LIBRARIES} ${UUID_LIBRARIES} @@ -75,3 +100,95 @@ target_include_directories(webfuse-passwd PUBLIC target_compile_options(webfuse-passwd PUBLIC ${OPENSSL_CFLAGS_OTHER}) install(TARGETS webfuse-passwd DESTINATION bin) + +if(NOT WITHOUT_TESTS) + + +set(THREADS_PREFER_PTHREAD_FLAG ON) +find_package(Threads REQUIRED) + + +include (CTest) + +pkg_check_modules(GTEST gtest_main) +include(GoogleTest) +pkg_check_modules(GMOCK gmock) + +add_executable(alltests + test/mock_config_builder.cc + test/mock_logger.cc + test/mock_credentials.cc + test/mock_settings.cc + test/test_config_factory.cc + test/test_config.cc + test/test_settings.cc + test/test_auth_factory.cc + test/test_file_authenticator.cc + test/test_mountpoint_factory.cc + test/test_log.cc + test/test_log_manager.cc + test/test_stderr_logger.cc + test/test_syslog_logger.cc + test/test_daemon.cc +) + +target_include_directories(alltests PRIVATE + src + ${GMOCK_INCLUDE_DIRS} + ${GTEST_INCLUDE_DIRS} +) + +target_compile_options(alltests PRIVATE ${GMOCK_CFLAGS} ${GTEST_CFLAGS} "-pthread") + +target_link_libraries(alltests PRIVATE + -Wl,--wrap=wf_credentials_type + -Wl,--wrap=wf_credentials_get + -Wl,--wrap=wfd_settings_get_string + -Wl,--wrap=wfd_settings_get_string_or_default + -Wl,--wrap=wfd_settings_get_bool + webfused-static + userdb + ${LIBCONFIG_LIBRARIES} + ${WEBFUSE_LIBRARIES} + ${UUID_LIBRARIES} + ${OPENSSL_LIBRARIES} + ${GMOCK_LIBRARIES} + ${GTEST_LIBRARIES} + ${CMAKE_THREAD_LIBS_INIT} +) + +# copy test data +configure_file(etc/webfused.conf webfused.conf COPYONLY) +configure_file(test/invalid.conf invalid.conf COPYONLY) +configure_file(test/test_passwd.json test_passwd.json COPYONLY) + +enable_testing() +gtest_discover_tests(alltests TEST_PREFIX alltests:) + +add_custom_target(check + ./alltests) +add_dependencies(check alltests) + +add_custom_target(memcheck + valgrind ./alltests) +add_dependencies(memcheck alltests) + +add_custom_target(coverage + mkdir -p coverage + COMMAND lcov --initial --capture --directory . --output-file coverage/lcov_base.info --rc lcov_branch_coverage=1 + COMMAND ./alltests + COMMAND lcov --capture --directory . --output-file coverage/lcov.info --rc lcov_branch_coverage=1 + COMMAND lcov -a coverage/lcov_base.info -a coverage/lcov.info --output-file coverage/lcov.info --rc lcov_branch_coverage=1 + COMMAND lcov --remove coverage/lcov.info '/usr/*' --output-file coverage/lcov.info --rc lcov_branch_coverage=1 + COMMAND lcov --remove coverage/lcov.info '*/test/*' --output-file coverage/lcov.info --rc lcov_branch_coverage=1 + COMMAND lcov --remove coverage/lcov.info '*/main.c' --output-file coverage/lcov.info --rc lcov_branch_coverage=1 +) +add_dependencies(coverage alltests) + +add_custom_target(coverage-report + COMMAND genhtml --branch-coverage --highlight --legend coverage/lcov.info --output-directory coverage/report +) +add_dependencies(coverage-report coverage) + + +endif(NOT WITHOUT_TESTS) diff --git a/README.md b/README.md index 245413a..e5b8852 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ [![Build Status](https://travis-ci.org/falk-werner/webfused.svg?branch=master)](https://travis-ci.org/falk-werner/webfused) +[![codecov](https://codecov.io/gh/falk-werner/webfused/branch/config_file/graph/badge.svg)](https://codecov.io/gh/falk-werner/webfused) # webfused @@ -13,7 +14,145 @@ To install dependecies, see below. cd build cmake .. make - ./webfused -m test --port=4711 + ./webfused -f webfused.conf + +## Config file + +``` +version = { major = 1, minor = 0 } + +server: +{ + vhost_name = "localhost" + port = 8080 + + tls: + { + certificate = "/etc/webfused/cert.pem" + + key = "/etc/webfused/key.pem" + } + + document_root = "/var/www" +} + +authentication: +( + { + provider = "file" + settings: + { + file = "/etc/webfused/passwd" + } + } +) + +filesystems: +( + {name = "test", mount_point = "/tmp/webfused" } +) + +log: +{ + provider: "syslog" + level: "warning" + settings: + { + ident = "webfused" + facility = "daemon" + log_pid = true + } +} +``` + +### Version + +The version sections specifies the schema version of the config file. +Currently, there is only one schema version defined: 1.0 + +### Server + +| Setting | Type | Default value | Description | +| ------------- | ------ | ------------- | ------------------------ | +| vhostname | string | localhost | Name of the virtual host | +| port | int | 8080 | Port number of server | +| document_root | string | *-empty-* | Path of HTTP files | +| tls | object | *-empty-* | see below | + +When *document_root* is omitted, no HTTP files are served. + +#### TLS + +| Setting | Type | Default value | Description | +| ----------- | ------ | ------------- | ------------------------------------------- | +| certificate | string | *-empty-* | Path to servers own certificate (.pem file) | +| key | string | *-empty-* | Path to servers own private key (.pem file) | + +TLS is only activated, when both, *certificate* and *key* are specified. +Otherwise, plain websockes without TLS are used. + +### Authentication + +| Setting | Type | Default value | Description | +| -------- | ------ | ------------- | ----------------------------------------------- | +| provider | string | *-required-* | Name of the authentication provider (see below) | +| settings | object | *-empty-* | Provider specific settings (see below) + +Currently, only one provider is specified: + +- *file*: file based authentication + +### File Authenticaton Provider + +Allows authentication against a file containing username and password. + +| Setting | Type | Default value | Description | +| -------- | ------ | ------------- | ------------------------------- | +| file | string | *-empty-* | Path to the authentication file | + +### Filesystems + +Contains a list of file systems that can be provided by webfuse providers. + +| Setting | Type | Default value | Description | +| ----------- | ------ | ------------- | ---------------------------------- | +| name | string | *-required-* | Name of the filesystem | +| mount_point | string | *-required-* | Local path to mount the filesystem | + +### Log + +| Setting | Type | Default value | Description | +| ----------- | ------ | ------------- | -------------------------------------- | +| provider | string | *-required-* | Name of log provider (see below) | +| level | string | *-required-* | Log level (see below) | +| settings | object | *-empty-* | Provider specific settings (see below) | + +The following log levels are supported: + +- *none*: diabled logging +- *fatal*: log only fatal errors +- *error*: log all kind of errors +- *warn*: log errors and warnings +- *info*: log info messages, warnings and errors +- *debug*: log debug and info messages as well as warnings and errors +- *all*: log all kind of messages + +Currently, the following providers are available: + +- *stderr*: logs to console error output +- *syslog*: logs to syslog + +#### Stderr Logger + +This logger does not provide any settings. + +#### Syslog Logger + +| Setting | Type | Default value | Description | +| ----------- | ------ | ------------- | ------------------------------------------ | +| ident | string | webfused | Syslog ident (see syslog documentation) | +| facility | string | daemon | Syslog facility (see syslog documentation) | +| log_pid | bool | false | Add process ID to log messages | ## Dependencies @@ -22,6 +161,8 @@ To install dependecies, see below. - [libwebsockets](https://libwebsockets.org/) - [jansson](https://github.com/akheron/jansson) - [openssl](https://www.openssl.org/) +- [libconfig](https://hyperrealm.github.io/libconfig/) +- [Google Test](https://github.com/google/googletest) *(Test only)* ### Installing dependencies @@ -49,7 +190,6 @@ To install libfuse, meson is needed. Please refer to [meson quick guide](https:/ make sudo make install - #### jansson wget https://github.com/akheron/jansson/archive/v2.12.tar.gz -O jansson.tar.gz @@ -76,3 +216,21 @@ To install libfuse, meson is needed. Please refer to [meson quick guide](https:/ cmake -DWITHOUT_TESTS=ON .. make sudo make install + +#### libconfig + + sudo apt update + sudo apt install libconfig-dev + +#### GoogleTest + +Installation of GoogleTest is optional webfuse library, but required to compile tests. + + wget -O gtest-1.10.0.tar.gz https://github.com/google/googletest/archive/release-1.10.0.tar.gz + tar -xf gtest-1.10.0.tar.gz + cd googletest-release-1.10.0 + mkdir .build + cd .build + cmake .. + make + sudo make install diff --git a/cmake/coverage.cmake b/cmake/coverage.cmake new file mode 100644 index 0000000..35b6e0e --- /dev/null +++ b/cmake/coverage.cmake @@ -0,0 +1,30 @@ +set(CMAKE_C_FLAGS_COVERAGE + "${CMAKE_C_FLAGS_DEBUG} -pg --coverage -fprofile-arcs -ftest-coverage" + CACHE STRING "Flags used by the C compiler during coverage builds" + FORCE +) + +set(CMAKE_CXX_FLAGS_COVERAGE + "${CMAKE_CXX_FLAGS_DEBUG} -pg --coverage -fprofile-arcs -ftest-coverage" + CACHE STRING "Flags used by the C++ compiler during coverage builds." + FORCE +) + +set(CMAKE_EXE_LINKER_FLAGS_COVERAGE + "" + CACHE STRING "Flags used for linking binaries during coverage builds." + FORCE +) + +set(CMAKE_SHARED_LINKER_FLAGS_COVERAGE + "" + CACHE STRING "Flags used by the shared libraries linker during coverage builds." + FORCE +) + +mark_as_advanced( + CMAKE_C_FLAGS_COVERAGE + CMAKE_CXX_FLAGS_COVERAGE + CMAKE_EXE_LINKER_FLAGS_COVERAGE + CMAKE_SHARED_LINKER_FLAGS_COVERAGE +) \ No newline at end of file diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 0000000..e233cf0 --- /dev/null +++ b/codecov.yml @@ -0,0 +1,2 @@ +ignore: + - "test/**/*" diff --git a/etc/webfused.conf b/etc/webfused.conf new file mode 100644 index 0000000..eb2df66 --- /dev/null +++ b/etc/webfused.conf @@ -0,0 +1,45 @@ +# Webfuse deamon configuration file + +version = { major = 1, minor = 0 } + +server: +{ + vhost_name = "localhost" + port = 8080 + + # tls: + # { + # certificate = "/etc/webfused/cert.pem" + # key = "/etc/webfused/key.pem" + # } + + # document_root = "/var/www" +} + +authentication: +( + { + provider = "file" + settings: + { + file = "/etc/webfused/passwd" + } + } +) + +filesystems: +( + {name = "test", mount_point = "/tmp/webfused" } +) + +log: +{ + provider: "syslog" + level: "warning" + settings: + { + ident = "webfused" + facility = "daemon" + log_pid = true + } +} diff --git a/src/daemon/main.c b/src/daemon/main.c deleted file mode 100644 index 7e899dc..0000000 --- a/src/daemon/main.c +++ /dev/null @@ -1,196 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include - -#define SERVICE_TIMEOUT (1 * 1000) - -struct args -{ - struct wf_server_config * config; - char * passwd_path; - bool show_help; -}; - -static bool shutdown_requested = false; - -static void show_help(void) -{ - printf( - "webfused, Copyright (c) 2019, webfused authors \n" - "Websocket file system daemon\n" - "\n" - "Usage: webfused [m ] [-d ] [-p ]\n" - " [-c ] [-k ] [-P ]\n" - "\n" - "Options:\n" - "\t-m, --mount_point Path of mount point (required)\n" - "\t-d, --document_root Path of www directory (default: not set, www disabled)\n" - "\t-c, --server_cert_path Path of servers own certificate (default: not set, TLS disabled)\n" - "\t-k, --server_key_path Path of servers private key (default: not set, TLS disabled)\n" - "\t-n, --vhost_name Name of virtual host (default: \"localhost\")\n" - "\t-p, --port Number of servers port (default: 8080)\n" - "\t-P, --passwd_path Path to password file (default: not set, authentication disabled)\n" - "\n"); -} - -static bool authenticate(struct wf_credentials * creds, void * user_data) -{ - bool result = false; - struct args * args = user_data; - - char const * username = wf_credentials_get(creds, "username"); - char const * password = wf_credentials_get(creds, "password"); - if ((NULL != username) && (NULL != password)) - { - struct userdb * db = userdb_create(""); - result = userdb_load(db, args->passwd_path); - if (result) - { - result = userdb_check(db, username, password); - } - - userdb_dispose(db); - } - - return result; -} - -static int parse_arguments(int argc, char * argv[], struct args * args) -{ - static struct option const options[] = - { - {"mount_point", required_argument, NULL, 'm'}, - {"document_root", required_argument, NULL, 'd'}, - {"server_cert_path", required_argument, NULL, 'c'}, - {"server_key_path", required_argument, NULL, 'k'}, - {"vhost_name", required_argument, NULL, 'n'}, - {"port", required_argument, NULL, 'p'}, - {"passwd_path", required_argument, NULL, 'P'}, - {"help", no_argument, NULL, 'h'}, - {NULL, 0, NULL, 0} - }; - - bool result = EXIT_SUCCESS; - bool finished = false; - bool has_mountpoint = false; - while ((!finished) && (EXIT_SUCCESS == result)) - { - int option_index = 0; - int const c = getopt_long(argc, argv, "m:d:c:k:n:p:P:h", options, &option_index); - - switch (c) - { - case -1: - finished = true; - break; - case 'h': - args->show_help = true; - finished = true; - break; - case 'm': - wf_server_config_set_mountpoint(args->config, optarg); - has_mountpoint = true; - break; - case 'd': - wf_server_config_set_documentroot(args->config, optarg); - break; - case 'c': - wf_server_config_set_certpath(args->config, optarg); - break; - case 'k': - wf_server_config_set_keypath(args->config, optarg); - break; - case 'n': - wf_server_config_set_vhostname(args->config, optarg); - break; - case 'p': - wf_server_config_set_port(args->config, atoi(optarg)); - break; - case 'P': - free(args->passwd_path); - args->passwd_path = strdup(optarg); - wf_server_config_add_authenticator(args->config, - "username", - &authenticate, - args); - break; - default: - fprintf(stderr, "error: unknown argument\n"); - result = EXIT_FAILURE; - break; - } - } - - if ((EXIT_SUCCESS == result) && (!args->show_help)) - { - if (!has_mountpoint) - { - fprintf(stderr, "error: missing mount point\n"); - result = EXIT_FAILURE; - } - } - - if (EXIT_SUCCESS != result) - { - args->show_help = true; - } - - return result; -} - -static void on_interrupt(int signal_id) -{ - (void) signal_id; - - shutdown_requested = true; -} - -int main(int argc, char * argv[]) -{ - struct args args; - args.config = wf_server_config_create(); - wf_server_config_set_vhostname(args.config, "localhost"); - wf_server_config_set_port(args.config, 8080); - args.passwd_path = NULL; - args.show_help = false; - - int result = parse_arguments(argc, argv, &args); - - if (!args.show_help) - { - signal(SIGINT, on_interrupt); - struct wf_server * server = wf_server_create(args.config); - if (NULL != server) - { - while (!shutdown_requested) - { - wf_server_service(server, SERVICE_TIMEOUT); - } - - wf_server_dispose(server); - } - else - { - fprintf(stderr, "fatal: unable start server\n"); - result = EXIT_FAILURE; - } - } - else - { - show_help(); - } - - free(args.passwd_path); - wf_server_config_dispose(args.config); - return result; -} - diff --git a/src/webfused/auth/authenticator.c b/src/webfused/auth/authenticator.c new file mode 100644 index 0000000..dc04cf5 --- /dev/null +++ b/src/webfused/auth/authenticator.c @@ -0,0 +1,24 @@ +#include "webfused/auth/authenticator.h" + +void +wfd_authenticator_dispose( + struct wfd_authenticator authenticator) +{ + authenticator.vtable->dispose(authenticator.data); +} + +extern bool +wfd_authenticator_authenticate( + struct wfd_authenticator authenticator, + struct wf_credentials * credentials) +{ + return authenticator.vtable->authenticate( + credentials, authenticator.data); +} + +char const * +wfd_authenticator_get_type( + struct wfd_authenticator authenticator) +{ + return authenticator.vtable->get_type(authenticator.data); +} diff --git a/src/webfused/auth/authenticator.h b/src/webfused/auth/authenticator.h new file mode 100644 index 0000000..ef97305 --- /dev/null +++ b/src/webfused/auth/authenticator.h @@ -0,0 +1,49 @@ +#ifndef WFD_AUTH_AUTHENTICATOR_H +#define WFD_AUTH_AUTHENTICATOR_H + +#include "webfuse/adapter/authenticate.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +typedef void +wfd_authenticator_dispose_fn( + void * data); + +typedef char const * +wfd_authenticator_get_type_fn( + void * data); + +struct wfd_authenticator_vtable +{ + wfd_authenticator_dispose_fn * dispose; + wf_authenticate_fn * authenticate; + wfd_authenticator_get_type_fn * get_type; +}; + +struct wfd_authenticator +{ + struct wfd_authenticator_vtable const * vtable; + void * data; +}; + +extern void +wfd_authenticator_dispose( + struct wfd_authenticator authenticator); + +extern bool +wfd_authenticator_authenticate( + struct wfd_authenticator authenticator, + struct wf_credentials * credentials); + +extern char const * +wfd_authenticator_get_type( + struct wfd_authenticator authenticator); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/webfused/auth/factory.c b/src/webfused/auth/factory.c new file mode 100644 index 0000000..8bd9421 --- /dev/null +++ b/src/webfused/auth/factory.c @@ -0,0 +1,25 @@ +#include "webfused/auth/factory.h" +#include "webfused/auth/file_authenticator.h" +#include "webfused/config/settings.h" +#include "webfused/log/log.h" + +#include + +bool +wfd_authenticator_create( + char const * provider, + struct wfd_settings * settings, + struct wfd_authenticator * authenticator) +{ + bool result = false; + if (0 == strcmp("file", provider)) + { + result = wfd_file_authenticator_create(settings, authenticator); + } + else + { + WFD_ERROR("failed to create authenticator: unknown type \"%s\"", provider); + } + + return result; +} diff --git a/src/webfused/auth/factory.h b/src/webfused/auth/factory.h new file mode 100644 index 0000000..44194af --- /dev/null +++ b/src/webfused/auth/factory.h @@ -0,0 +1,26 @@ +#ifndef WFD_AUTHENTICATION_FACTORY_H +#define WFD_AUTHENTICATION_FACTORY_H + +#ifndef __cplusplus +#include +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + +struct wfd_authenticator; +struct wfd_settings; + +extern bool +wfd_authenticator_create( + char const * provider, + struct wfd_settings * settings, + struct wfd_authenticator * authenticator); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/webfused/auth/file_authenticator.c b/src/webfused/auth/file_authenticator.c new file mode 100644 index 0000000..61dae0a --- /dev/null +++ b/src/webfused/auth/file_authenticator.c @@ -0,0 +1,91 @@ +#include "webfused/auth/file_authenticator.h" +#include "webfused/auth/authenticator.h" +#include "webfused/config/settings.h" +#include "userdb/userdb.h" +#include "webfused/log/log.h" + +#include + +#include +#include + +struct wfd_file_authenticator +{ + char * filename; +}; + +static void +wfd_file_authenticator_dispose( + void * data) +{ + struct wfd_file_authenticator * authenticator = data; + + free(authenticator->filename); + free(authenticator); +} + +static bool +wfd_file_authenticator_authenticate( + struct wf_credentials * credentials, + void * user_data) +{ + bool result = false; + struct wfd_file_authenticator * authenticator = user_data; + + char const * username = wf_credentials_get(credentials, "username"); + char const * password = wf_credentials_get(credentials, "password"); + if ((NULL != username) && (NULL != password)) + { + struct userdb * db = userdb_create(""); + result = userdb_load(db, authenticator->filename); + if (result) + { + result = userdb_check(db, username, password); + } + + userdb_dispose(db); + } + + WFD_INFO("authenticate user \'%s\': %s", + (NULL != username) ? username : "", + result ? "success" : "failure"); + + return result; +} + +static char const * +wfd_file_authenticator_get_type( + void * data) +{ + (void) data; + return "username"; +} + +static struct wfd_authenticator_vtable +wfd_file_authenticator_vtable = +{ + .dispose = &wfd_file_authenticator_dispose, + .authenticate = &wfd_file_authenticator_authenticate, + .get_type = &wfd_file_authenticator_get_type +}; + +bool +wfd_file_authenticator_create( + struct wfd_settings * settings, + struct wfd_authenticator * authenticator) +{ + bool result = false; + + char const * filename = wfd_settings_get_string(settings, "file"); + if (NULL != filename) + { + struct wfd_file_authenticator * data = malloc(sizeof(struct wfd_file_authenticator)); + data->filename = strdup(filename); + + authenticator->vtable = &wfd_file_authenticator_vtable; + authenticator->data = data; + result = true; + } + + return result; +} diff --git a/src/webfused/auth/file_authenticator.h b/src/webfused/auth/file_authenticator.h new file mode 100644 index 0000000..7b406a6 --- /dev/null +++ b/src/webfused/auth/file_authenticator.h @@ -0,0 +1,26 @@ +#ifndef WFD_AUTH_FILE_AUTHENTICATOR_H +#define WFD_AUTH_FILE_AUTHENTICATOR_H + +#ifndef __cplusplus +#include +#endif + + +#ifdef __cplusplus +extern "C" +{ +#endif + +struct wfd_authenticator; +struct wfd_settings; + +extern bool +wfd_file_authenticator_create( + struct wfd_settings * settings, + struct wfd_authenticator * authenticator); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/webfused/config/builder.c b/src/webfused/config/builder.c new file mode 100644 index 0000000..6d712f4 --- /dev/null +++ b/src/webfused/config/builder.c @@ -0,0 +1,72 @@ +#include "webfused/config/builder.h" + +void +wfd_config_builder_set_server_vhostname( + struct wfd_config_builder builder, + char const * vhost_name) +{ + builder.vtable->set_server_vhostname(builder.data, vhost_name); +} + +void +wfd_config_builder_set_server_port( + struct wfd_config_builder builder, + int port) +{ + builder.vtable->set_server_port(builder.data, port); +} + +void +wfd_config_builder_set_server_key( + struct wfd_config_builder builder, + char const * key_path) +{ + builder.vtable->set_server_key(builder.data, key_path); +} + +void +wfd_config_builder_set_server_cert( + struct wfd_config_builder builder, + char const * cert_path) +{ + builder.vtable->set_server_cert(builder.data, cert_path); +} + +void +wfd_config_builder_set_server_document_root( + struct wfd_config_builder builder, + char const * document_root) +{ + builder.vtable->set_server_document_root(builder.data, document_root); +} + +bool +wfd_config_builder_add_auth_provider( + struct wfd_config_builder builder, + char const * provider, + struct wfd_settings * settings) +{ + return builder.vtable->add_auth_provider(builder.data, provider, settings); +} + +bool +wfd_config_builder_add_filesystem( + struct wfd_config_builder builder, + char const * name, + char const * mount_point) +{ + return builder.vtable->add_filesystem(builder.data, name, mount_point); +} + +bool +wfd_config_builder_set_logger( + struct wfd_config_builder builder, + char const * provider, + int level, + struct wfd_settings * settings) +{ + return builder.vtable->set_logger(builder.data, provider, level, settings); +} + + + diff --git a/src/webfused/config/builder.h b/src/webfused/config/builder.h new file mode 100644 index 0000000..01348ea --- /dev/null +++ b/src/webfused/config/builder.h @@ -0,0 +1,125 @@ +#ifndef WFD_CONFIG_BUILDER_H +#define WFD_CONFIG_BUILDER_H + +#ifndef __cplusplus +#include +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + +struct wfd_settings; + +typedef void +wfd_config_builder_set_server_vhostname_fn( + void * data, + char const * vhost_name); + +typedef void +wfd_config_builder_set_server_port_fn( + void * data, + int port); + +typedef void +wfd_config_builder_set_server_key_fn( + void * data, + char const * key_path); + +typedef void +wfd_config_builder_set_server_cert_fn( + void * data, + char const * cert_path); + +typedef void +wfd_config_builder_set_server_document_root_fn( + void * data, + char const * document_root); + +typedef bool +wfd_config_builder_add_auth_provider_fn( + void * data, + char const * provider, + struct wfd_settings * settings); + +typedef bool +wfd_config_builder_add_filesystem_fn( + void * data, + char const * name, + char const * mount_point); + +typedef bool +wfd_config_builder_set_logger_fn( + void * data, + char const * provider, + int level, + struct wfd_settings * settings); + +struct wfd_config_builder_vtable +{ + wfd_config_builder_set_server_vhostname_fn * set_server_vhostname; + wfd_config_builder_set_server_port_fn * set_server_port; + wfd_config_builder_set_server_key_fn * set_server_key; + wfd_config_builder_set_server_cert_fn * set_server_cert; + wfd_config_builder_set_server_document_root_fn * set_server_document_root; + wfd_config_builder_add_auth_provider_fn * add_auth_provider; + wfd_config_builder_add_filesystem_fn * add_filesystem; + wfd_config_builder_set_logger_fn * set_logger; +}; + +struct wfd_config_builder +{ + struct wfd_config_builder_vtable const * vtable; + void * data; +}; + +extern void +wfd_config_builder_set_server_vhostname( + struct wfd_config_builder builder, + char const * vhost_name); + +extern void +wfd_config_builder_set_server_port( + struct wfd_config_builder builder, + int port); + +extern void +wfd_config_builder_set_server_key( + struct wfd_config_builder builder, + char const * key_path); + +extern void +wfd_config_builder_set_server_cert( + struct wfd_config_builder builder, + char const * cert_path); + +extern void +wfd_config_builder_set_server_document_root( + struct wfd_config_builder builder, + char const * document_root); + +extern bool +wfd_config_builder_add_auth_provider( + struct wfd_config_builder builder, + char const * provider, + struct wfd_settings * settings); + +extern bool +wfd_config_builder_add_filesystem( + struct wfd_config_builder builder, + char const * name, + char const * mount_point); + +extern bool +wfd_config_builder_set_logger( + struct wfd_config_builder builder, + char const * provider, + int level, + struct wfd_settings * settings); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/webfused/config/config.c b/src/webfused/config/config.c new file mode 100644 index 0000000..f74fed7 --- /dev/null +++ b/src/webfused/config/config.c @@ -0,0 +1,178 @@ +#include "webfused/config/config.h" +#include "webfuse/adapter/server_config.h" +#include "webfused/auth/factory.h" +#include "webfused/auth/authenticator.h" +#include "webfused/mountpoint_factory.h" +#include "webfused/log/manager.h" + +#include + +#define WFD_CONFIG_DEFAULT_PORT (8080) +#define WFD_CONFIG_DEFAULT_VHOSTNAME ("localhost") + +struct wfd_config +{ + struct wf_server_config * server; + bool has_authenticator; + struct wfd_authenticator authenticator; + struct wfd_mountpoint_factory * mountpoint_factory; +}; + +static void +wfd_config_set_server_vhostname( + void * data, + char const * vhost_name) +{ + struct wfd_config * config = data; + wf_server_config_set_vhostname(config->server, vhost_name); +} + +static void +wfd_config_set_server_port( + void * data, + int port) +{ + struct wfd_config * config = data; + wf_server_config_set_port(config->server, port); +} + +static void +wfd_config_set_server_key( + void * data, + char const * key_path) +{ + struct wfd_config * config = data; + wf_server_config_set_keypath(config->server, key_path); +} + +static void +wfd_config_set_server_cert( + void * data, + char const * cert_path) +{ + struct wfd_config * config = data; + wf_server_config_set_certpath(config->server, cert_path); +} + +static void +wfd_config_set_server_document_root( + void * data, + char const * document_root) +{ + struct wfd_config * config = data; + wf_server_config_set_documentroot(config->server, document_root); +} + +static bool +wfd_config_add_auth_provider( + void * data, + char const * provider, + struct wfd_settings * settings) +{ + bool result = false; + struct wfd_config * config = data; + + if (!config->has_authenticator) + { + result = wfd_authenticator_create(provider, settings, &config->authenticator); + if (result) + { + wf_server_config_add_authenticator( + config->server, + wfd_authenticator_get_type(config->authenticator), + config->authenticator.vtable->authenticate, + config->authenticator.data); + + config->has_authenticator = true; + } + } + + return result; +} + +static bool +wfd_config_add_filesystem( + void * data, + char const * name, + char const * mount_point) +{ + struct wfd_config * config = data; + return wfd_mountpoint_factory_add_filesystem( + config->mountpoint_factory, name, mount_point); +} + +static bool +wfd_config_set_logger( + void * data, + char const * provider, + int level, + struct wfd_settings * settings) +{ + struct wfd_config * config = data; + return wfd_log_manager_set_logger(provider, level, settings); +} + +static const struct wfd_config_builder_vtable +wfd_config_vtable_config_builder = +{ + .set_server_vhostname = &wfd_config_set_server_vhostname, + .set_server_port = &wfd_config_set_server_port, + .set_server_key = &wfd_config_set_server_key, + .set_server_cert = &wfd_config_set_server_cert, + .set_server_document_root = &wfd_config_set_server_document_root, + .add_auth_provider = &wfd_config_add_auth_provider, + .add_filesystem = &wfd_config_add_filesystem, + .set_logger = &wfd_config_set_logger +}; + +struct wfd_config * +wfd_config_create(void) +{ + struct wfd_config * config = malloc(sizeof(struct wfd_config)); + + config->mountpoint_factory = wfd_mountpoint_factory_create(); + config->has_authenticator = false; + config->server = wf_server_config_create(); + wf_server_config_set_vhostname(config->server, WFD_CONFIG_DEFAULT_VHOSTNAME); + wf_server_config_set_port(config->server, WFD_CONFIG_DEFAULT_PORT); + wf_server_config_set_mountpoint_factory(config->server, + wfd_mountpoint_factory_create_mountpoint, + config->mountpoint_factory); + + return config; +} + +void +wfd_config_dispose( + struct wfd_config * config) +{ + wf_server_config_dispose(config->server); + if (config->has_authenticator) + { + wfd_authenticator_dispose(config->authenticator); + } + wfd_mountpoint_factory_dispose(config->mountpoint_factory); + + free(config); +} + +struct wfd_config_builder +wfd_config_get_builder( + struct wfd_config * config) +{ + struct wfd_config_builder builder = + { + &wfd_config_vtable_config_builder, + config + }; + + return builder; +} + +struct wf_server_config * +wfd_config_get_server_config( + struct wfd_config * config) +{ + return config->server; +} + diff --git a/src/webfused/config/config.h b/src/webfused/config/config.h new file mode 100644 index 0000000..b3fec87 --- /dev/null +++ b/src/webfused/config/config.h @@ -0,0 +1,40 @@ +#ifndef WFD_CONFIG_H +#define WFD_CONFIG_H + +#include "webfused/config/builder.h" + +#ifndef __cplusplus +#include +#include +#else +#include +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + +struct wfd_config; +struct wf_server_config; + +extern struct wfd_config * +wfd_config_create(void); + +extern void +wfd_config_dispose( + struct wfd_config * config); + +extern struct wfd_config_builder +wfd_config_get_builder( + struct wfd_config * config); + +extern struct wf_server_config * +wfd_config_get_server_config( + struct wfd_config * config); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/webfused/config/factory.c b/src/webfused/config/factory.c new file mode 100644 index 0000000..794b67c --- /dev/null +++ b/src/webfused/config/factory.c @@ -0,0 +1,332 @@ +#include "webfused/config/factory.h" +#include "webfused/config/settings_intern.h" +#include "webfused/log/log.h" + +#include +#include +#include + + +#if ((LIBCONFIG_VER_MAJOR != 1) || (LIBCONFIG_VER_MINOR < 5)) +#error "linconfig 1.5 or higher needed" +#endif + + +#define WFD_CONFIG_VERSION_MAJOR 1 +#define WFD_CONFIG_VERSION_MINOR 0 + +static bool +wfd_config_check_version( + config_t * config) +{ + int version_major; + int rc = config_lookup_int(config, "version.major", &version_major); + if (CONFIG_TRUE != rc) + { + WFD_ERROR("failed to load config: missing version.major"); + return false; + } + + if (WFD_CONFIG_VERSION_MAJOR != version_major) + { + WFD_ERROR("failed to load config: " + "incompatible versions: expected %d, but war %d", + WFD_CONFIG_VERSION_MAJOR, version_major); + return false; + } + + int version_minor; + rc = config_lookup_int(config, "version.minor", &version_minor); + if (CONFIG_TRUE != rc) + { + WFD_ERROR("failed to load config: missing version.minor"); + return false; + } + + if (WFD_CONFIG_VERSION_MINOR < version_minor) + { + WFD_WARN("newer config detected: some features might be disabled"); + } + else if (WFD_CONFIG_VERSION_MINOR > version_minor) + { + WFD_INFO("old config detected: some features might use default values"); + } + + return true; +} + +static bool +wfd_config_read_logger( + config_t * config, + struct wfd_config_builder builder) +{ + bool result = true; + + bool hasLogger = (NULL != config_lookup(config, "log")); + if (hasLogger) + { + char const * provider; + int rc = config_lookup_string(config, "log.provider", &provider); + if (CONFIG_TRUE != rc) + { + WFD_ERROR("missing log provider"); + result = false; + } + + char const * level_str; + if (result) + { + rc = config_lookup_string(config, "log.level", &level_str); + if (CONFIG_TRUE != rc) + { + WFD_ERROR("missing log level"); + result = false; + } + } + + int level; + if (result) + { + bool success = wfd_log_level_parse(level_str, &level); + if (!success) + { + WFD_ERROR("failed to parse log level: unknown valuie \'%s\'", level_str); + result = false; + } + } + + if (result) + { + config_setting_t * setting = config_lookup(config, "log.settings"); + struct wfd_settings settings; + wfd_settings_init(&settings, setting); + result = wfd_config_builder_set_logger(builder, provider, level, &settings); + wfd_settings_cleanup(&settings); + } + } + + return result; +} + +static bool +wfd_config_read_server( + config_t * config, + struct wfd_config_builder builder) +{ + char const * vhost_name; + int rc = config_lookup_string(config, "server.vhost_name", &vhost_name); + if (CONFIG_TRUE == rc) + { + wfd_config_builder_set_server_vhostname(builder, vhost_name); + } + + int port; + rc = config_lookup_int(config, "server.port", &port); + if (CONFIG_TRUE == rc) + { + wfd_config_builder_set_server_port(builder, port); + } + + char const * cert; + rc = config_lookup_string(config, "server.tls.certificate", &cert); + if (CONFIG_TRUE == rc) + { + wfd_config_builder_set_server_cert(builder, cert); + } + + char const * key; + rc = config_lookup_string(config, "server.tls.key", &key); + if (CONFIG_TRUE == rc) + { + wfd_config_builder_set_server_key(builder, key); + } + + char const * doc_root; + rc = config_lookup_string(config, "server.document_root", &doc_root); + if (CONFIG_TRUE == rc) + { + wfd_config_builder_set_server_document_root(builder, doc_root); + } + + return true; +} + +static bool +wfd_config_read_authenticator( + config_setting_t * authenticator, + struct wfd_config_builder builder) +{ + bool result = (NULL != authenticator); + + char const * provider_name = NULL; + if (result) + { + int rc = config_setting_lookup_string(authenticator, "provider", &provider_name); + if (CONFIG_TRUE != rc) + { + WFD_ERROR("missing authentication provider"); + result = false; + } + } + + struct config_setting_t * settings = NULL; + if (result) + { + settings = config_setting_lookup(authenticator, "settings"); + if (NULL == settings) + { + WFD_ERROR("missing authentication settings"); + result = false; + } + } + + if (result) + { + struct wfd_settings auth_settings; + wfd_settings_init(&auth_settings, settings); + + result = wfd_config_builder_add_auth_provider(builder, provider_name, &auth_settings); + wfd_settings_cleanup(&auth_settings); + } + + return result; +} + +static bool +wfd_config_read_authentication( + config_t * config, + struct wfd_config_builder builder) +{ + bool result = true; + + config_setting_t * authentication = config_lookup(config, "authentication"); + if (NULL != authentication) + { + int length = config_setting_length(authentication); + for (int i = 0; (result) && (i < length); i++) + { + config_setting_t * authenticator = config_setting_get_elem(authentication, i); + result = wfd_config_read_authenticator(authenticator, builder); + } + } + + return result; +} + +static bool +wfd_config_read_filesystems( + config_t * config, + struct wfd_config_builder builder) +{ + bool result = true; + config_setting_t * filesystems = config_lookup(config, "filesystems"); + if (NULL != filesystems) + { + int length = config_setting_length(filesystems); + for (int i = 0; i < length; i++) + { + config_setting_t * fs = config_setting_get_elem(filesystems, i); + if (NULL == fs) + { + WFD_ERROR("failed to load config: invalid filesystem section"); + result = false; + break; + } + + char const * name; + int rc = config_setting_lookup_string(fs, "name", &name); + if (rc != CONFIG_TRUE) + { + WFD_ERROR("failed to load config: missing required filesystem property \'name\'"); + result = false; + break; + } + + char const * mount_point; + rc = config_setting_lookup_string(fs, "mount_point", &mount_point); + if (rc != CONFIG_TRUE) + { + WFD_ERROR("failed to load config: missing required filesystem property \'mount_point\'"); + result = false; + break; + } + + result = wfd_config_builder_add_filesystem(builder, name, mount_point); + if (!result) + { + break; + } + } + } + + return result; +} + +static bool +wfd_config_load( + struct wfd_config_builder builder, + config_t * config) +{ + + bool result = wfd_config_check_version(config) + && wfd_config_read_logger(config, builder) + && wfd_config_read_server(config, builder) + && wfd_config_read_authentication(config, builder) + && wfd_config_read_filesystems(config, builder) + ; + + return result; +} + +bool +wfd_config_load_file( + struct wfd_config_builder builder, + char const * filename) +{ + bool result = false; + + config_t config; + config_init(&config); + int rc = config_read_file(&config, filename); + if (CONFIG_TRUE == rc) + { + result = wfd_config_load(builder, &config); + } + else + { + WFD_ERROR("failed to load config: %s: %d: %s", + config_error_file(&config), + config_error_line(&config), + config_error_text(&config)); + } + config_destroy(&config); + + + return result; +} + +bool +wfd_config_load_string( + struct wfd_config_builder builder, + char const * contents) +{ + bool result = false; + + config_t config; + config_init(&config); + int rc = config_read_string(&config, contents); + if (CONFIG_TRUE == rc) + { + result = wfd_config_load(builder, &config); + } + else + { + WFD_ERROR("failed to load config: %d: %s", + config_error_line(&config), + config_error_text(&config)); + } + config_destroy(&config); + + + return result; +} diff --git a/src/webfused/config/factory.h b/src/webfused/config/factory.h new file mode 100644 index 0000000..f2dcd5d --- /dev/null +++ b/src/webfused/config/factory.h @@ -0,0 +1,29 @@ +#ifndef WFD_CONFIG_FACTORY_H +#define WFD_CONFIG_FACTORY_H + +#ifndef __cplusplus +#include +#endif + +#include "webfused/config/builder.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +extern bool +wfd_config_load_file( + struct wfd_config_builder builder, + char const * filename); + +extern bool +wfd_config_load_string( + struct wfd_config_builder builder, + char const * contents); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/webfused/config/settings.c b/src/webfused/config/settings.c new file mode 100644 index 0000000..0c7df95 --- /dev/null +++ b/src/webfused/config/settings.c @@ -0,0 +1,60 @@ +#include "webfused/config/settings.h" +#include "webfused/config/settings_intern.h" + +#include +#include + +void +wfd_settings_init( + struct wfd_settings * settings, + struct config_setting_t * setting) +{ + settings->setting = setting; +} + +void +wfd_settings_cleanup( + struct wfd_settings * settings) +{ + settings->setting = NULL; +} + +char const * +wfd_settings_get_string( + struct wfd_settings * settings, + char const * key) +{ + if (NULL == settings) {return NULL; } + + char const * result; + int rc = config_setting_lookup_string(settings->setting, key, &result); + + return (CONFIG_TRUE == rc) ? result : NULL; +} + +char const * +wfd_settings_get_string_or_default( + struct wfd_settings * settings, + char const * key, + char const * default_value) +{ + if (NULL == settings) {return default_value; } + + char const * result; + int rc = config_setting_lookup_string(settings->setting, key, &result); + + return (CONFIG_TRUE == rc) ? result : default_value; +} + +bool +wfd_settings_get_bool( + struct wfd_settings * settings, + char const * key) +{ + if (NULL == settings) {return false; } + + int result; + int rc = config_setting_lookup_bool(settings->setting, key, &result); + + return ((CONFIG_TRUE == rc) && (CONFIG_TRUE == result)); +} diff --git a/src/webfused/config/settings.h b/src/webfused/config/settings.h new file mode 100644 index 0000000..9035367 --- /dev/null +++ b/src/webfused/config/settings.h @@ -0,0 +1,37 @@ +#ifndef WFD_CONFIG_SETTINGS_H +#define WFD_CONFIG_SETTINGS_H + +#ifndef __cplusplus +#include +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + +struct wfd_settings; + + +extern char const * +wfd_settings_get_string( + struct wfd_settings * settings, + char const * key); + +extern char const * +wfd_settings_get_string_or_default( + struct wfd_settings * settings, + char const * key, + char const * default_value); + +extern bool +wfd_settings_get_bool( + struct wfd_settings * settings, + char const * key); + +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/src/webfused/config/settings_intern.h b/src/webfused/config/settings_intern.h new file mode 100644 index 0000000..04bf395 --- /dev/null +++ b/src/webfused/config/settings_intern.h @@ -0,0 +1,30 @@ +#ifndef WFD_CONFIG_SETTINGS_INTERN_H +#define WFD_CONFIG_SETTINGS_INTERN_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +struct config_setting_t; + +struct wfd_settings +{ + struct config_setting_t * setting; +}; + + +extern void +wfd_settings_init( + struct wfd_settings * settings, + struct config_setting_t * setting); + +extern void +wfd_settings_cleanup( + struct wfd_settings * settings); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/webfused/daemon.c b/src/webfused/daemon.c new file mode 100644 index 0000000..8911777 --- /dev/null +++ b/src/webfused/daemon.c @@ -0,0 +1,151 @@ +#include "webfused/daemon.h" + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include "webfused/config/config.h" +#include "webfused/config/factory.h" +#include "webfused/log/log.h" +#include "webfused/log/logger.h" +#include "webfused/log/stderr_logger.h" + +#define WFD_SERVICE_TIMEOUT (1 * 1000) +#define WFD_DEFAULT_CONFIG_FILE ("/etc/webfuse.conf") + +struct args +{ + char * config_file; + bool show_help; +}; + +static bool shutdown_requested = false; + +static void show_help(void) +{ + printf( + "webfused, Copyright (c) 2019-2020, webfused authors \n" + "Websocket file system daemon\n" + "\n" + "Usage: webfused [-f ] | -h\n" + "\n" + "Options:\n" + "\t-f, --config-file Path to config file (default: /etc/webfuse.conf)\n" + "\t-h, --help Print this message and terminate\n" + "\n"); +} + +static int parse_arguments(int argc, char * argv[], struct args * args) +{ + static struct option const options[] = + { + {"config-file", required_argument, NULL, 'f'}, + {"help", no_argument, NULL, 'h'}, + {NULL, 0, NULL, 0} + }; + + args->config_file = strdup(WFD_DEFAULT_CONFIG_FILE); + args->show_help = false; + + bool result = EXIT_SUCCESS; + bool finished = false; + optind = 0; + while ((!finished) && (EXIT_SUCCESS == result)) + { + int option_index = 0; + int const c = getopt_long(argc, argv, "f:h", options, &option_index); + + switch (c) + { + case -1: + finished = true; + break; + case 'h': + args->show_help = true; + finished = true; + break; + case 'f': + free(args->config_file); + args->config_file = strdup(optarg); + break; + default: + fprintf(stderr, "error: unknown argument\n"); + result = EXIT_FAILURE; + break; + } + } + + if (EXIT_SUCCESS != result) + { + args->show_help = true; + } + + return result; +} + +static void on_interrupt(int signal_id) +{ + (void) signal_id; + + shutdown_requested = true; +} + +int wfd_daemon_run(int argc, char * argv[]) +{ + wfd_stderr_logger_init(WFD_LOGLEVEL_ALL, NULL); + + struct args args; + int result = parse_arguments(argc, argv, &args); + + if (!args.show_help) + { + signal(SIGINT, on_interrupt); + + struct wfd_config * config = wfd_config_create(); + struct wfd_config_builder builder = wfd_config_get_builder(config); + bool success = wfd_config_load_file(builder, args.config_file); + if (success) + { + struct wf_server_config * server_config = wfd_config_get_server_config(config); + struct wf_server * server = wf_server_create(server_config); + if (NULL != server) + { + while (!shutdown_requested) + { + wf_server_service(server, WFD_SERVICE_TIMEOUT); + } + + wf_server_dispose(server); + } + else + { + fprintf(stderr, "fatal: unable start server\n"); + result = EXIT_FAILURE; + } + } + else + { + fprintf(stderr, "fatal: failed to load server config\n"); + result = EXIT_FAILURE; + } + + wfd_config_dispose(config); + } + else + { + show_help(); + } + + wfd_logger_close(); + free(args.config_file); + shutdown_requested = false; + return result; +} + diff --git a/src/webfused/daemon.h b/src/webfused/daemon.h new file mode 100644 index 0000000..edf888c --- /dev/null +++ b/src/webfused/daemon.h @@ -0,0 +1,15 @@ +#ifndef WFD_DAEMON_H +#define WFD_DAEMON_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +extern int wfd_daemon_run(int argc, char * argv[]); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/webfused/log/log.c b/src/webfused/log/log.c new file mode 100644 index 0000000..986fa5f --- /dev/null +++ b/src/webfused/log/log.c @@ -0,0 +1,62 @@ +#include "webfused/log/log.h" +#include + +char const * +wfd_log_level_tostring(int level) +{ + switch (level) + { + case WFD_LOGLEVEL_NONE: return "none"; + case WFD_LOGLEVEL_FATAL: return "fatal"; + case WFD_LOGLEVEL_ERROR: return "error"; + case WFD_LOGLEVEL_WARN: return "warn"; + case WFD_LOGLEVEL_INFO: return "info"; + case WFD_LOGLEVEL_DEBUG: return "debug"; + case WFD_LOGLEVEL_ALL: return "all"; + default: return ""; + } +} + +bool +wfd_log_level_parse( + char const * level, + int * result) +{ + bool success = true; + + if (0 == strcasecmp("all", level)) + { + *result = WFD_LOGLEVEL_ALL; + } + else if (0 == strcasecmp("none", level)) + { + *result = WFD_LOGLEVEL_NONE; + } + else if (0 == strcasecmp("fatal", level)) + { + *result = WFD_LOGLEVEL_FATAL; + } + else if (0 == strcasecmp("error", level)) + { + *result = WFD_LOGLEVEL_ERROR; + } + else if ((0 == strcasecmp("warn", level)) || + (0 == strcasecmp("warning", level))) + { + *result = WFD_LOGLEVEL_WARN; + } + else if (0 == strcasecmp("info", level)) + { + *result = WFD_LOGLEVEL_INFO; + } + else if (0 == strcasecmp("debug", level)) + { + *result = WFD_LOGLEVEL_DEBUG; + } + else + { + return false; + } + + return success; +} diff --git a/src/webfused/log/log.h b/src/webfused/log/log.h new file mode 100644 index 0000000..3a312c1 --- /dev/null +++ b/src/webfused/log/log.h @@ -0,0 +1,68 @@ +#ifndef WFD_LOG_H +#define WFD_LOG_H + +#ifndef __cplusplus +#include +#include +#else +#include +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + +#ifndef WFD_LOGLEVEL +#define WFD_LOGLEVEL WFD_LOGLEVEL_ALL +#endif + +#define WFD_LOGLEVEL_NONE -1 +#define WFD_LOGLEVEL_FATAL 1 +#define WFD_LOGLEVEL_ERROR 3 +#define WFD_LOGLEVEL_WARN 4 +#define WFD_LOGLEVEL_INFO 5 +#define WFD_LOGLEVEL_DEBUG 7 +#define WFD_LOGLEVEL_ALL 8 + +#define WFD_FATAL(...) \ + WFD_LOG(WFD_LOGLEVEL_FATAL, __VA_ARGS__) + +#define WFD_ERROR(...) \ + WFD_LOG(WFD_LOGLEVEL_ERROR, __VA_ARGS__) + +#define WFD_WARN(...) \ + WFD_LOG(WFD_LOGLEVEL_WARN, __VA_ARGS__) + +#define WFD_INFO(...) \ + WFD_LOG(WFD_LOGLEVEL_INFO, __VA_ARGS__) + +#define WFD_DEBUG(...) \ + WFD_LOG(WFD_LOGLEVEL_DEBUG, __VA_ARGS__) + +#define WFD_LOG(level, ...) \ + do { \ + if (WFD_LOGLEVEL >= (level)) { \ + wfd_log((level), __VA_ARGS__); \ + } \ + } while (0) + +extern void +wfd_log( + int level, + char const * format, + ...); + +extern char const * +wfd_log_level_tostring(int level); + +extern bool +wfd_log_level_parse( + char const * level, + int * result); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/webfused/log/logger.c b/src/webfused/log/logger.c new file mode 100644 index 0000000..2c15fbe --- /dev/null +++ b/src/webfused/log/logger.c @@ -0,0 +1,86 @@ +#include "webfused/log/logger.h" +#include "webfused/log/log.h" + +#include + +struct wfd_logger +{ + int level; + wfd_logger_log_fn * log; + wfd_logger_onclose_fn * onclose; + void * user_data; +}; + +static void wfd_logger_default_log( + void * user_data, + int level, + char const * format, + va_list args); + +static struct wfd_logger g_wfd_logger = +{ + .level = WFD_LOGLEVEL_ALL, + .log = &wfd_logger_default_log, + .onclose = NULL, + .user_data = NULL +}; + +void +wfd_logger_init( + int level, + wfd_logger_log_fn * log, + wfd_logger_onclose_fn * onclose, + void * user_data) +{ + wfd_logger_close(); + + g_wfd_logger.level = level; + g_wfd_logger.log = log; + g_wfd_logger.onclose = onclose; + g_wfd_logger.user_data = user_data; +} + +void +wfd_logger_close(void) +{ + if (NULL != g_wfd_logger.onclose) + { + g_wfd_logger.onclose(g_wfd_logger.user_data); + } + + g_wfd_logger.level = WFD_LOGLEVEL_ALL; + g_wfd_logger.log = &wfd_logger_default_log; + g_wfd_logger.onclose = NULL; + g_wfd_logger.user_data = NULL; +} + +void +wfd_log( + int level, + char const * format, + ...) +{ + if (g_wfd_logger.level >= level) + { + va_list args; + va_start(args, format); + g_wfd_logger.log( + g_wfd_logger.user_data, + level, + format, + args); + va_end(args); + } +} + +static void wfd_logger_default_log( + void * user_data, + int level, + char const * format, + va_list args) +{ + (void) user_data; + (void) level; + (void) format; + (void) args; +} diff --git a/src/webfused/log/logger.h b/src/webfused/log/logger.h new file mode 100644 index 0000000..2944b9c --- /dev/null +++ b/src/webfused/log/logger.h @@ -0,0 +1,40 @@ +#ifndef WFD_LOGGER_H +#define WFD_LOGGER_H + +#ifndef __cplusplus +#include +#else +#include +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + +typedef void +wfd_logger_log_fn( + void * user_data, + int level, + char const * format, + va_list args); + +typedef void +wfd_logger_onclose_fn( + void * user_data); + +extern void +wfd_logger_init( + int level, + wfd_logger_log_fn * log, + wfd_logger_onclose_fn * onclose, + void * user_data); + +extern void +wfd_logger_close(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/webfused/log/manager.c b/src/webfused/log/manager.c new file mode 100644 index 0000000..c8f09b0 --- /dev/null +++ b/src/webfused/log/manager.c @@ -0,0 +1,30 @@ +#include "webfused/log/manager.h" +#include "webfused/log/log.h" +#include "webfused/log/stderr_logger.h" +#include "webfused/log/syslog_logger.h" + +#include + +bool +wfd_log_manager_set_logger( + char const * provider, + int level, + struct wfd_settings * settings) +{ + bool result = false; + + if (0 == strcmp("syslog", provider)) + { + result = wfd_syslog_logger_init(level, settings); + } + else if (0 == strcmp("stderr", provider)) + { + result = wfd_stderr_logger_init(level, settings); + } + else + { + WFD_ERROR("failed to init log: unknown provider \'%s\'", provider); + } + + return result; +} diff --git a/src/webfused/log/manager.h b/src/webfused/log/manager.h new file mode 100644 index 0000000..103418f --- /dev/null +++ b/src/webfused/log/manager.h @@ -0,0 +1,25 @@ +#ifndef WFD_LOG_MANAGER_H +#define WFD_LOG_MANAGER_H + +#ifndef __cplusplus +#include +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + +struct wfd_settings; + +extern bool +wfd_log_manager_set_logger( + char const * provider, + int level, + struct wfd_settings * settings); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/webfused/log/stderr_logger.c b/src/webfused/log/stderr_logger.c new file mode 100644 index 0000000..34c3f5f --- /dev/null +++ b/src/webfused/log/stderr_logger.c @@ -0,0 +1,29 @@ +#include "webfused/log/stderr_logger.h" +#include "webfused/log/logger.h" +#include "webfused/log/log.h" + +#include +#include + +static void +wfd_stderr_logger_log( + void * user_data, + int level, + char const * format, + va_list args) +{ + fprintf(stderr, "%s: ", wfd_log_level_tostring(level)); + vfprintf(stderr, format, args); + fprintf(stderr, "\n"); +} + +bool +wfd_stderr_logger_init( + int level, + struct wfd_settings * settings) +{ + (void) settings; + + wfd_logger_init(level, &wfd_stderr_logger_log, NULL, NULL); + return true; +} diff --git a/src/webfused/log/stderr_logger.h b/src/webfused/log/stderr_logger.h new file mode 100644 index 0000000..d1bb25e --- /dev/null +++ b/src/webfused/log/stderr_logger.h @@ -0,0 +1,24 @@ +#ifndef WFD_LOG_STDERR_LOGGER_H +#define WFD_LOG_STDERR_LOGGER_H + +#ifndef __cplusplus +#include +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + +struct wfd_settings; + +extern bool +wfd_stderr_logger_init( + int level, + struct wfd_settings * settings); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/webfused/log/syslog_logger.c b/src/webfused/log/syslog_logger.c new file mode 100644 index 0000000..0f66ad4 --- /dev/null +++ b/src/webfused/log/syslog_logger.c @@ -0,0 +1,122 @@ +#include "webfused/log/syslog_logger.h" +#include "webfused/log/logger.h" +#include "webfused/log/log.h" +#include "webfused/config/settings.h" + +#include +#include + +#include + +static int +wfd_syslog_logger_to_priority( + int level) +{ + switch (level) + { + case WFD_LOGLEVEL_FATAL: return LOG_CRIT; + case WFD_LOGLEVEL_ERROR: return LOG_ERR; + case WFD_LOGLEVEL_WARN: return LOG_WARNING; + case WFD_LOGLEVEL_INFO: return LOG_INFO; + case WFD_LOGLEVEL_DEBUG: return LOG_DEBUG; + default: return LOG_NOTICE; + } +} + +static void +wfd_syslog_logger_log( + void * user_data, + int level, + char const * format, + va_list args) +{ + (void) user_data; + + int prio = wfd_syslog_logger_to_priority(level); + vsyslog(prio, format, args); +} + +static void +wfd_syslog_logger_close( + void * user_data) +{ + closelog(); +} + +struct wfd_syslog_facility +{ + char const * name; + int value; +}; + +static bool +wfd_syslog_logger_parse_facility( + char const * facility, + int * result) +{ + static struct wfd_syslog_facility const facilities[] = + { + {"auth", LOG_AUTH}, + {"authpriv", LOG_AUTHPRIV}, + {"cron", LOG_CRON}, + {"daemon", LOG_DAEMON}, + {"fpt", LOG_FTP}, + {"kern", LOG_KERN}, + {"local0", LOG_LOCAL0}, + {"local1", LOG_LOCAL1}, + {"local2", LOG_LOCAL2}, + {"local3", LOG_LOCAL3}, + {"local4", LOG_LOCAL4}, + {"local5", LOG_LOCAL5}, + {"local6", LOG_LOCAL6}, + {"local7", LOG_LOCAL7}, + {"lpr", LOG_LPR}, + {"mail", LOG_MAIL}, + {"news", LOG_NEWS}, + {"syslog", LOG_SYSLOG}, + {"user", LOG_USER}, + {"uucp", LOG_UUCP} + }; + static size_t const facilites_count = sizeof(facilities) / sizeof(facilities[0]); + + for (size_t i = 0; i < facilites_count; i++) + { + if (0 == strcasecmp(facility, facilities[i].name)) + { + *result = facilities[i].value; + return true; + } + } + + return false; +} + + +bool +wfd_syslog_logger_init( + int level, + struct wfd_settings * settings) +{ + char const * ident = wfd_settings_get_string_or_default(settings, "ident", "webfused"); + char const * facility_str = wfd_settings_get_string_or_default(settings, "facility", "daemon"); + bool log_pid = wfd_settings_get_bool(settings, "log_pid"); + + int facility; + bool result = wfd_syslog_logger_parse_facility(facility_str, &facility); + if (result) + { + int options = (log_pid) ? LOG_PID : 0; + wfd_logger_init(level, + &wfd_syslog_logger_log, + &wfd_syslog_logger_close, + NULL); + + openlog(ident, options, facility); + } + else + { + WFD_ERROR("failed to init syslog logger: invalid log facility: \'%s\'", facility_str); + } + + return result; +} \ No newline at end of file diff --git a/src/webfused/log/syslog_logger.h b/src/webfused/log/syslog_logger.h new file mode 100644 index 0000000..6682715 --- /dev/null +++ b/src/webfused/log/syslog_logger.h @@ -0,0 +1,24 @@ +#ifndef WFD_LOG_SYSLOG_LOGGER_H +#define WFD_LOG_SYSLOG_LOGGER_H + +#ifndef __cplusplus +#include +#endif + +#ifdef __cplusplus +extern "C" + { +#endif + +struct wfd_settings; + +extern bool +wfd_syslog_logger_init( + int level, + struct wfd_settings * settings); + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/src/webfused/main.c b/src/webfused/main.c new file mode 100644 index 0000000..6faf15d --- /dev/null +++ b/src/webfused/main.c @@ -0,0 +1,6 @@ +#include "webfused/daemon.h" + +int main(int argc, char * argv[]) +{ + return wfd_daemon_run(argc, argv); +} diff --git a/src/webfused/mountpoint_factory.c b/src/webfused/mountpoint_factory.c new file mode 100644 index 0000000..3fc641e --- /dev/null +++ b/src/webfused/mountpoint_factory.c @@ -0,0 +1,150 @@ +#include "webfused/mountpoint_factory.h" +#include "webfused/log/log.h" + +#include + +#include +#include +#include + +#define WFD_FILESYSTEM_DEFAULT_CAPACITY 16 + +struct wfd_filesystem +{ + char * name; + char * mount_point; + bool in_use; +}; + +struct wfd_mountpoint_factory +{ + struct wfd_filesystem * filesystems; + size_t capacity; + size_t count; +}; + +static struct wfd_filesystem * +wfd_mountpoint_factory_find( + struct wfd_mountpoint_factory * factory, + char const * name) +{ + for (size_t i = 0; i < factory->count; i++) + { + struct wfd_filesystem * filesystem = &(factory->filesystems[i]); + if (0 == strcmp(name, filesystem->name)) + { + return filesystem; + } + } + + return NULL; +} + +static void +wfd_mountpoint_factory_release_mountpoint( + void * user_data) +{ + bool * in_use = user_data; + *in_use = false; +} + +struct wfd_mountpoint_factory * +wfd_mountpoint_factory_create(void) +{ + struct wfd_mountpoint_factory * factory = malloc(sizeof(struct wfd_mountpoint_factory)); + factory->filesystems = malloc(sizeof(struct wfd_filesystem) * WFD_FILESYSTEM_DEFAULT_CAPACITY); + factory->count = 0; + factory->capacity = WFD_FILESYSTEM_DEFAULT_CAPACITY; + + return factory; +} + +void +wfd_mountpoint_factory_dispose( + struct wfd_mountpoint_factory * factory) +{ + for(size_t i = 0; i < factory->count; i++) + { + struct wfd_filesystem * filesystem = &(factory->filesystems[i]); + free(filesystem->name); + free(filesystem->mount_point); + } + + free(factory->filesystems); + free(factory); +} + +bool +wfd_mountpoint_factory_add_filesystem( + struct wfd_mountpoint_factory * factory, + char const * name, + char const * mount_point) +{ + bool result = (NULL == wfd_mountpoint_factory_find(factory, name)); + if (!result) + { + WFD_ERROR("mount_point already defined: \'%s\'", mount_point); + } + + char * path = NULL; + if (result) + { + mkdir(mount_point, 0755); + path = realpath(mount_point, NULL); + if (NULL == path) + { + WFD_ERROR("invalid mount_point: \'%s\'", mount_point); + result = false; + } + } + + if (result) + { + if (factory->count >= factory->capacity) + { + factory->capacity *= 2; + factory->filesystems = realloc(factory->filesystems, + sizeof(struct wfd_filesystem) * factory->capacity); + } + + struct wfd_filesystem * actual = &(factory->filesystems[factory->count]); + actual->name = strdup(name); + actual->mount_point = path; + actual->in_use = false; + factory->count++; + } + + return result; +} + +extern struct wf_mountpoint * +wfd_mountpoint_factory_create_mountpoint( + char const * filesystem, + void * user_data) +{ + struct wfd_mountpoint_factory * factory = user_data; + struct wfd_filesystem * fs = wfd_mountpoint_factory_find(factory, filesystem); + if (NULL == fs) + { + WFD_INFO("failed to create mountpoint: filesystem \'%s\' not found", filesystem); + return NULL; + } + + if (fs->in_use) + { + WFD_INFO("failed to create mountpoint: filesystem \'%s\' already in use", filesystem); + return NULL; + } + + fs->in_use = true; + struct wf_mountpoint * result = wf_mountpoint_create(fs->mount_point); + wf_mountpoint_set_userdata(result, + &fs->in_use, &wfd_mountpoint_factory_release_mountpoint); + + WFD_INFO("create mountpoint \'%s\' at path \'%s\': %s", + filesystem, + fs->mount_point, + result ? "success" : "failure"); + + return result; +} diff --git a/src/webfused/mountpoint_factory.h b/src/webfused/mountpoint_factory.h new file mode 100644 index 0000000..3459ad4 --- /dev/null +++ b/src/webfused/mountpoint_factory.h @@ -0,0 +1,39 @@ +#ifndef WFD_MOUNTPOINT_FACTORY_H +#define WFD_MOUNTPOINT_FACTORY_H + +#include "webfuse/adapter/mountpoint_factory.h" + +#ifndef __cplusplus +#include +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + +struct wfd_mountpoint_factory; + +extern struct wfd_mountpoint_factory * +wfd_mountpoint_factory_create(void); + +extern void +wfd_mountpoint_factory_dispose( + struct wfd_mountpoint_factory * factory); + +extern bool +wfd_mountpoint_factory_add_filesystem( + struct wfd_mountpoint_factory * factory, + char const * name, + char const * mount_point); + +extern struct wf_mountpoint * +wfd_mountpoint_factory_create_mountpoint( + char const * filesystem, + void * user_data); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/test/invalid.conf b/test/invalid.conf new file mode 100644 index 0000000..c7665f5 --- /dev/null +++ b/test/invalid.conf @@ -0,0 +1,4 @@ +# This config file is invalid, +# since "." is not allowed as separator + +version.major = 1 diff --git a/test/mock_config_builder.cc b/test/mock_config_builder.cc new file mode 100644 index 0000000..7967178 --- /dev/null +++ b/test/mock_config_builder.cc @@ -0,0 +1,111 @@ +#include "mock_config_builder.hpp" + +extern "C" +{ +using webfused_test::IConfigBuilder; + +static void +wfd_MockConfigBuilder_set_server_vhostname( + void * data, + char const * vhost_name) +{ + auto * builder = reinterpret_cast(data); + builder->setServerVhostname(vhost_name); +} + +static void +wfd_MockConfigBuilder_set_server_port( + void * data, + int port) +{ + auto * builder = reinterpret_cast(data); + builder->setServerPort(port); +} + +static void +wfd_MockConfigBuilder_set_server_key( + void * data, + char const * key_path) +{ + auto * builder = reinterpret_cast(data); + builder->setServerKey(key_path); +} + +static void +wfd_MockConfigBuilder_set_server_cert( + void * data, + char const * cert_path) +{ + auto * builder = reinterpret_cast(data); + builder->setServerCert(cert_path); +} + +static void +wfd_MockConfigBuilder_set_server_document_root( + void * data, + char const * document_root) +{ + auto * builder = reinterpret_cast(data); + builder->setServerDocumentRoot(document_root); +} + +static bool +wfd_MockConfigBuilder_add_auth_provider( + void * data, + char const * provider, + struct wfd_settings * settings) +{ + auto * builder = reinterpret_cast(data); + return builder->addAuthProvider(provider, settings); +} + +static bool +wfd_MockConfigBuilder_add_filesystem( + void * data, + char const * name, + char const * mountpoint) +{ + auto * builder = reinterpret_cast(data); + return builder->addFilesystem(name, mountpoint); +} + +static bool +wfd_MockConfigBuilder_set_logger( + void * data, + char const * provider, + int level, + struct wfd_settings * settings) +{ + auto * builder = reinterpret_cast(data); + return builder->setLogger(provider, level, settings); +} + + +static const wfd_config_builder_vtable wfd_MockConfigBuilder_vtable = +{ + &wfd_MockConfigBuilder_set_server_vhostname, + &wfd_MockConfigBuilder_set_server_port, + &wfd_MockConfigBuilder_set_server_key, + &wfd_MockConfigBuilder_set_server_cert, + &wfd_MockConfigBuilder_set_server_document_root, + &wfd_MockConfigBuilder_add_auth_provider, + &wfd_MockConfigBuilder_add_filesystem, + &wfd_MockConfigBuilder_set_logger +}; + +} + +namespace webfused_test +{ + struct wfd_config_builder MockConfigBuilder::getBuilder() + { + IConfigBuilder * config_builder = this; + + wfd_config_builder builder = { + &wfd_MockConfigBuilder_vtable, + reinterpret_cast(config_builder) + }; + + return builder; + } +} \ No newline at end of file diff --git a/test/mock_config_builder.hpp b/test/mock_config_builder.hpp new file mode 100644 index 0000000..a512a10 --- /dev/null +++ b/test/mock_config_builder.hpp @@ -0,0 +1,42 @@ +#ifndef WFD_MOCK_CONFIG_BUILDER_HPP +#define WFD_MOCK_CONFIG_BUILDER_HPP + +#include +#include "webfused/config/builder.h" + +namespace webfused_test +{ + +class IConfigBuilder +{ +public: + virtual ~IConfigBuilder() = default; + virtual void setServerVhostname(char const * vhostname) = 0; + virtual void setServerPort(int port) = 0; + virtual void setServerKey(char const * key_path) = 0; + virtual void setServerCert(char const * cert_path) = 0; + virtual void setServerDocumentRoot(char const * document_root) = 0; + virtual bool addAuthProvider(char const * provider, wfd_settings * settings) = 0; + virtual bool addFilesystem(char const * name, char const * mountpoint) = 0; + virtual bool setLogger(char const * provider, int level, wfd_settings * settings) = 0; +}; + +class MockConfigBuilder: public IConfigBuilder +{ +public: + ~MockConfigBuilder() override = default; + MOCK_METHOD1(setServerVhostname, void (char const * vhostname)); + MOCK_METHOD1(setServerPort, void (int port)); + MOCK_METHOD1(setServerKey, void (char const * key_path)); + MOCK_METHOD1(setServerCert, void (char const * cert_path)); + MOCK_METHOD1(setServerDocumentRoot, void (char const * document_root)); + MOCK_METHOD2(addAuthProvider, bool (char const * provider, wfd_settings * settings)); + MOCK_METHOD2(addFilesystem, bool (char const * name, char const * mountpoint)); + MOCK_METHOD3(setLogger, bool (char const * provider, int level, wfd_settings * settings)); + + struct wfd_config_builder getBuilder(); +}; + +} + +#endif diff --git a/test/mock_credentials.cc b/test/mock_credentials.cc new file mode 100644 index 0000000..a6023b1 --- /dev/null +++ b/test/mock_credentials.cc @@ -0,0 +1,64 @@ +#include "mock_credentials.hpp" + + +extern "C" +{ +using webfused_test::ICredentials; + +static ICredentials * wfd_mock_credentials = nullptr; + +extern char const * +__real_wf_credentials_type( + struct wf_credentials const * credentials); + +char const * +__wrap_wf_credentials_type( + struct wf_credentials const * credentials) +{ + if (nullptr == wfd_mock_credentials) + { + return __real_wf_credentials_type(credentials); + } + else + { + return wfd_mock_credentials->type(); + } +} + +extern char const * __real_wf_credentials_get( + struct wf_credentials const * credentials, + char const * key); + + +char const * __wrap_wf_credentials_get( + struct wf_credentials const * credentials, + char const * key) +{ + if (nullptr == wfd_mock_credentials) + { + return __real_wf_credentials_get(credentials, key); + } + else + { + return wfd_mock_credentials->get(key); + } +} + + +} + + +namespace webfused_test +{ + +MockCredentials::MockCredentials() +{ + wfd_mock_credentials = this; +} + +MockCredentials::~MockCredentials() +{ + wfd_mock_credentials = nullptr; +} + +} diff --git a/test/mock_credentials.hpp b/test/mock_credentials.hpp new file mode 100644 index 0000000..6f0ca1e --- /dev/null +++ b/test/mock_credentials.hpp @@ -0,0 +1,29 @@ +#ifndef WFD_MOCK_CREDENTIALS_HPP +#define WFD_MOCK_CREDENTIALS_HPP + +#include +#include "webfuse/adapter/credentials.h" + +namespace webfused_test +{ + +class ICredentials +{ +public: + virtual ~ICredentials() = default; + virtual char const * type() = 0; + virtual char const * get(char const * key) = 0; +}; + +class MockCredentials: public ICredentials +{ +public: + MockCredentials(); + virtual ~MockCredentials(); + MOCK_METHOD0(type, char const*()); + MOCK_METHOD1(get, char const *(char const * key)); +}; + +} + +#endif diff --git a/test/mock_logger.cc b/test/mock_logger.cc new file mode 100644 index 0000000..a36d89a --- /dev/null +++ b/test/mock_logger.cc @@ -0,0 +1,58 @@ +#include "mock_logger.hpp" +#include "webfused/log/logger.h" +#include "webfused/log/log.h" + +namespace webfused_test +{ + +MockLogger::MockLogger(bool omit_init) +: close_on_destruct(!omit_init) +{ + if (!omit_init) + { + wfd_logger_init( + WFD_LOGLEVEL_ALL, + &wfd_MockLogger_log, + &wfd_MockLogger_onclose, + getUserData()); + } +} + +MockLogger::~MockLogger() +{ + if (close_on_destruct) + { + wfd_logger_close(); + } +} + +void * MockLogger::getUserData() +{ + ILogger * logger = this; + return reinterpret_cast(logger); +} + +} + +extern "C" +{ +using webfused_test::ILogger; + +void wfd_MockLogger_log( + void * user_data, + int level, + char const * format, + va_list args) +{ + ILogger * logger = reinterpret_cast(user_data); + logger->log(level, format, args); +} + +void wfd_MockLogger_onclose( + void * user_data) +{ + ILogger * logger = reinterpret_cast(user_data); + logger->onclose(); +} + +} \ No newline at end of file diff --git a/test/mock_logger.hpp b/test/mock_logger.hpp new file mode 100644 index 0000000..3c3e381 --- /dev/null +++ b/test/mock_logger.hpp @@ -0,0 +1,47 @@ +#ifndef WFD_MOCK_LOGGER_HPP +#define WFD_MOCK_LOGGER_HPP + +#include +#include + +namespace webfused_test +{ + +class ILogger +{ +public: + virtual ~ILogger() = default; + virtual void log(int level, char const * format, va_list args) = 0; + virtual void onclose() = 0; +}; + +class MockLogger: public ILogger +{ +public: + explicit MockLogger(bool omit_init = false); + ~MockLogger() override; + MOCK_METHOD3(log, void(int level, char const * format, va_list args)); + MOCK_METHOD0(onclose, void(void)); + + void * getUserData(); +private: + bool close_on_destruct; +}; + +} + +extern "C" +{ + +extern void wfd_MockLogger_log( + void * user_data, + int level, + char const * format, + va_list args); + +extern void wfd_MockLogger_onclose( + void * user_data); + +} + +#endif diff --git a/test/mock_settings.cc b/test/mock_settings.cc new file mode 100644 index 0000000..3cc36ff --- /dev/null +++ b/test/mock_settings.cc @@ -0,0 +1,87 @@ +#include "mock_settings.hpp" + +extern "C" +{ +using webfused_test::ISettings; + +static ISettings * wfd_mock_settings = nullptr; + +extern char const * +__real_wfd_settings_get_string( + struct wfd_settings * settings, + char const * key); + +extern char const * +__real_wfd_settings_get_string_or_default( + struct wfd_settings * settings, + char const * key, + char const * default_value); + +extern bool +__real_wfd_settings_get_bool( + struct wfd_settings * settings, + char const * key); + +char const * +__wrap_wfd_settings_get_string( + struct wfd_settings * settings, + char const * key) +{ + if (nullptr == wfd_mock_settings) + { + return __real_wfd_settings_get_string(settings, key); + } + else + { + return wfd_mock_settings->getString(key); + } +} + +char const * +__wrap_wfd_settings_get_string_or_default( + struct wfd_settings * settings, + char const * key, + char const * default_value) +{ + if (nullptr == wfd_mock_settings) + { + return __real_wfd_settings_get_string_or_default( + settings, key, default_value); + } + else + { + return wfd_mock_settings->getStringOrDefault(key, default_value); + } +} + +bool +__wrap_wfd_settings_get_bool( + struct wfd_settings * settings, + char const * key) +{ + if (nullptr == wfd_mock_settings) + { + return __real_wfd_settings_get_bool(settings, key); + } + else + { + return wfd_mock_settings->getBool(key); + } +} + +} + +namespace webfused_test +{ + +MockSettings::MockSettings() +{ + wfd_mock_settings = this; +} + +MockSettings::~MockSettings() +{ + wfd_mock_settings = nullptr; +} + +} \ No newline at end of file diff --git a/test/mock_settings.hpp b/test/mock_settings.hpp new file mode 100644 index 0000000..f3d8692 --- /dev/null +++ b/test/mock_settings.hpp @@ -0,0 +1,33 @@ +#ifndef WFD_MOCK_AUTH_SETTINGS_HPP +#define WFD_MOCK_AUTH_SETTINGS_HPP + +#include +#include "webfused/config/settings.h" + +namespace webfused_test +{ + +class ISettings +{ +public: + virtual ~ISettings() = default; + virtual char const * getString(char const * key) = 0; + virtual char const * getStringOrDefault(char const * key, char const * default_value) = 0; + virtual bool getBool(char const * key) = 0; +}; + +class MockSettings: public ISettings +{ +public: + MockSettings(); + ~MockSettings() override; + MOCK_METHOD1(getString, char const * (char const * key)); + MOCK_METHOD2(getStringOrDefault, char const * (char const * key, char const * default_value)); + MOCK_METHOD1(getBool, bool (char const * key)); +}; + +} + + + +#endif diff --git a/test/test_auth_factory.cc b/test/test_auth_factory.cc new file mode 100644 index 0000000..2e26cca --- /dev/null +++ b/test/test_auth_factory.cc @@ -0,0 +1,20 @@ +#include +#include "webfused/auth/factory.h" +#include "webfused/auth/authenticator.h" +#include "webfused/log/log.h" +#include "mock_logger.hpp" + +using ::testing::_; +using ::webfused_test::MockLogger; + + +TEST(auth_factory, fail_unknown_provider) +{ + MockLogger logger; + EXPECT_CALL(logger, log(WFD_LOGLEVEL_ERROR, _, _)).Times(1); + EXPECT_CALL(logger, onclose()).Times(1); + + wfd_authenticator authenticator; + bool result = wfd_authenticator_create("unknown", NULL, &authenticator); + ASSERT_FALSE(result); +} \ No newline at end of file diff --git a/test/test_config.cc b/test/test_config.cc new file mode 100644 index 0000000..4fe66be --- /dev/null +++ b/test/test_config.cc @@ -0,0 +1,105 @@ +#include +#include "webfused/config/config.h" +#include "mock_settings.hpp" + +#include "webfused/log/logger.h" +#include "webfused/log/log.h" +#include "mock_logger.hpp" +using ::webfused_test::MockLogger; +using ::testing::_; + +using ::webfused_test::MockSettings; +using ::testing::Return; +using ::testing::StrEq; + +TEST(config, server_config) +{ + wfd_config * config = wfd_config_create(); + ASSERT_NE(nullptr, config); + + wfd_config_builder builder = wfd_config_get_builder(config); + wfd_config_builder_set_server_vhostname(builder, "localhost"); + wfd_config_builder_set_server_port(builder, 8443); + wfd_config_builder_set_server_key(builder, "/path/to/key.pem"); + wfd_config_builder_set_server_cert(builder, "/path/to/cert.pem"); + wfd_config_builder_set_server_document_root(builder, "/var/www"); + + wf_server_config * server_config = wfd_config_get_server_config(config); + ASSERT_NE(nullptr, server_config); + + wfd_config_dispose(config); +} + +TEST(config, auth_config) +{ + wfd_config * config = wfd_config_create(); + ASSERT_NE(nullptr, config); + + wfd_config_builder builder = wfd_config_get_builder(config); + + MockSettings settings; + EXPECT_CALL(settings, getString(StrEq("file"))).Times(1).WillOnce(Return("/any/path")); + + bool success = wfd_config_builder_add_auth_provider(builder, "file", nullptr); + ASSERT_TRUE(success); + + wfd_config_dispose(config); +} + +TEST(config, auth_config_failed_to_add_second_provider) +{ + wfd_config * config = wfd_config_create(); + ASSERT_NE(nullptr, config); + + wfd_config_builder builder = wfd_config_get_builder(config); + + MockSettings settings; + EXPECT_CALL(settings, getString(StrEq("file"))).Times(1).WillOnce(Return("/any/path")); + + bool success = wfd_config_builder_add_auth_provider(builder, "file", nullptr); + ASSERT_TRUE(success); + + success = wfd_config_builder_add_auth_provider(builder, "file", nullptr); + ASSERT_FALSE(success); + + wfd_config_dispose(config); +} + +TEST(config, auth_config_failed_to_add_unknown_provider) +{ + wfd_config * config = wfd_config_create(); + ASSERT_NE(nullptr, config); + + wfd_config_builder builder = wfd_config_get_builder(config); + + bool success = wfd_config_builder_add_auth_provider(builder, "unknown", nullptr); + ASSERT_FALSE(success); + + wfd_config_dispose(config); +} + +TEST(config, add_filesystem) +{ + wfd_config * config = wfd_config_create(); + ASSERT_NE(nullptr, config); + + wfd_config_builder builder = wfd_config_get_builder(config); + + bool success = wfd_config_builder_add_filesystem(builder, "test", "/tmp/test"); + ASSERT_TRUE(success); + + wfd_config_dispose(config); +} + +TEST(config, set_logger) +{ + wfd_config * config = wfd_config_create(); + ASSERT_NE(nullptr, config); + + wfd_config_builder builder = wfd_config_get_builder(config); + + bool success = wfd_config_builder_set_logger(builder, "stderr", WFD_LOGLEVEL_ALL, nullptr); + ASSERT_TRUE(success); + + wfd_config_dispose(config); +} \ No newline at end of file diff --git a/test/test_config_factory.cc b/test/test_config_factory.cc new file mode 100644 index 0000000..48a6de8 --- /dev/null +++ b/test/test_config_factory.cc @@ -0,0 +1,548 @@ +#include + +#include "webfused/config/factory.h" +#include "webfused/log/logger.h" +#include "webfused/log/log.h" +#include "mock_logger.hpp" +#include "mock_config_builder.hpp" + + +using ::testing::_; +using ::testing::Return; +using ::testing::StrictMock; +using ::testing::StrEq; +using ::webfused_test::MockLogger; +using ::webfused_test::MockConfigBuilder; + +TEST(config, is_loadable) +{ + StrictMock builder; + EXPECT_CALL(builder, setLogger(_,_,_)).Times(1).WillOnce(Return(true)); + EXPECT_CALL(builder, setServerVhostname(StrEq("localhost"))).Times(1); + EXPECT_CALL(builder, setServerPort(8080)).Times(1); + EXPECT_CALL(builder, addAuthProvider(_, _)).Times(1).WillOnce(Return(true)); + EXPECT_CALL(builder, addFilesystem(_,_)).Times(1).WillOnce(Return(true)); + + bool result = wfd_config_load_file(builder.getBuilder(), "webfused.conf"); + ASSERT_TRUE(result); +} + +TEST(config, minimal_config) +{ + MockLogger logger; + EXPECT_CALL(logger, log(_, _, _)).Times(0); + EXPECT_CALL(logger, onclose()).Times(1); + + StrictMock builder; + + char const minimal[] = "version = { major = 1, minor = 0 }\n"; + bool result = wfd_config_load_string(builder.getBuilder(), minimal); + ASSERT_TRUE(result); +} + +TEST(config, invalid_config) +{ + MockLogger logger; + EXPECT_CALL(logger, log(WFD_LOGLEVEL_ERROR, _, _)).Times(1); + EXPECT_CALL(logger, onclose()).Times(1); + + StrictMock builder; + + char const syntax_error[] = "version.major = 1\n"; + + bool result = wfd_config_load_string(builder.getBuilder(), syntax_error); + ASSERT_FALSE(result); +} + +TEST(config, invalid_config_file) +{ + MockLogger logger; + EXPECT_CALL(logger, log(WFD_LOGLEVEL_ERROR, _, _)).Times(1); + EXPECT_CALL(logger, onclose()).Times(1); + + StrictMock builder; + + bool result = wfd_config_load_file(builder.getBuilder(), "invalid.conf"); + ASSERT_FALSE(result); +} + +TEST(config, invalid_major_version_too_low) +{ + MockLogger logger; + EXPECT_CALL(logger, log(WFD_LOGLEVEL_ERROR, _, _)).Times(1); + EXPECT_CALL(logger, onclose()).Times(1); + + StrictMock builder; + + char const too_low[] = "version = { major = 0, minor = 0 }\n"; + + bool result = wfd_config_load_string(builder.getBuilder(), too_low); + ASSERT_FALSE(result); +} + +TEST(config, invalid_major_version_too_high) +{ + MockLogger logger; + EXPECT_CALL(logger, log(WFD_LOGLEVEL_ERROR, _, _)).Times(1); + EXPECT_CALL(logger, onclose()).Times(1); + + StrictMock builder; + + char const too_high[] = "version = { major = 2, minor = 0 }\n"; + + bool result = wfd_config_load_string(builder.getBuilder(), too_high); + ASSERT_FALSE(result); +} + +TEST(config, invalid_missing_major_version) +{ + MockLogger logger; + EXPECT_CALL(logger, log(WFD_LOGLEVEL_ERROR, _, _)).Times(1); + EXPECT_CALL(logger, onclose()).Times(1); + + StrictMock builder; + + char const too_high[] = "version = { minor = 0 }\n"; + + bool result = wfd_config_load_string(builder.getBuilder(), too_high); + ASSERT_FALSE(result); +} + +TEST(config, invalid_missing_minor_version) +{ + MockLogger logger; + EXPECT_CALL(logger, log(WFD_LOGLEVEL_ERROR, _, _)).Times(1); + EXPECT_CALL(logger, onclose()).Times(1); + + StrictMock builder; + + char const too_high[] = "version = { major = 1 }\n"; + + bool result = wfd_config_load_string(builder.getBuilder(), too_high); + ASSERT_FALSE(result); +} + +TEST(config, valid_older_minor) +{ + MockLogger logger; + EXPECT_CALL(logger, log(WFD_LOGLEVEL_INFO, _, _)).Times(1); + EXPECT_CALL(logger, onclose()).Times(1); + + StrictMock builder; + + char const valid[] = "version = { major = 1, minor = -1 }\n"; + + bool result = wfd_config_load_string(builder.getBuilder(), valid); + ASSERT_TRUE(result); +} + +TEST(config, valid_newer_minor) +{ + MockLogger logger; + EXPECT_CALL(logger, log(WFD_LOGLEVEL_WARN, _, _)).Times(1); + EXPECT_CALL(logger, onclose()).Times(1); + + StrictMock builder; + + char const valid[] = "version = { major = 1, minor = 1 }\n"; + + bool result = wfd_config_load_string(builder.getBuilder(), valid); + ASSERT_TRUE(result); +} + +TEST(config, vhost_name) +{ + MockLogger logger; + EXPECT_CALL(logger, log(_, _, _)).Times(0); + EXPECT_CALL(logger, onclose()).Times(1); + + StrictMock builder; + EXPECT_CALL(builder, setServerVhostname(StrEq("some.host"))).Times(1); + + char const config_text[] = + "version = { major = 1, minor = 0 }\n" + "server:\n" + "{\n" + " vhost_name = \"some.host\"\n" + "}\n" + ; + bool result = wfd_config_load_string(builder.getBuilder(), config_text); + ASSERT_TRUE(result); +} + +TEST(config, port) +{ + MockLogger logger; + EXPECT_CALL(logger, log(_, _, _)).Times(0); + EXPECT_CALL(logger, onclose()).Times(1); + + StrictMock builder; + EXPECT_CALL(builder, setServerPort(54321)).Times(1); + + char const config_text[] = + "version = { major = 1, minor = 0 }\n" + "server:\n" + "{\n" + " port = 54321\n" + "}\n" + ; + bool result = wfd_config_load_string(builder.getBuilder(), config_text); + ASSERT_TRUE(result); +} + +TEST(config, tls_certificate) +{ + MockLogger logger; + EXPECT_CALL(logger, log(_, _, _)).Times(0); + EXPECT_CALL(logger, onclose()).Times(1); + + StrictMock builder; + EXPECT_CALL(builder, setServerCert(StrEq("/path/to/cert.pem"))).Times(1); + + char const config_text[] = + "version = { major = 1, minor = 0 }\n" + "server:\n" + "{\n" + " tls:\n" + " {\n" + " certificate = \"/path/to/cert.pem\"\n" + " }\n" + "}\n" + ; + bool result = wfd_config_load_string(builder.getBuilder(), config_text); + ASSERT_TRUE(result); +} + +TEST(config, tls_key) +{ + MockLogger logger; + EXPECT_CALL(logger, log(_, _, _)).Times(0); + EXPECT_CALL(logger, onclose()).Times(1); + + StrictMock builder; + EXPECT_CALL(builder, setServerKey(StrEq("/path/to/key.pem"))).Times(1); + + char const config_text[] = + "version = { major = 1, minor = 0 }\n" + "server:\n" + "{\n" + " tls:\n" + " {\n" + " key = \"/path/to/key.pem\"\n" + " }\n" + "}\n" + ; + bool result = wfd_config_load_string(builder.getBuilder(), config_text); + ASSERT_TRUE(result); +} + +TEST(config, document_root) +{ + MockLogger logger; + EXPECT_CALL(logger, log(_, _, _)).Times(0); + EXPECT_CALL(logger, onclose()).Times(1); + + StrictMock builder; + EXPECT_CALL(builder, setServerDocumentRoot(StrEq("/var/www"))).Times(1); + + char const config_text[] = + "version = { major = 1, minor = 0 }\n" + "server:\n" + "{\n" + " document_root = \"/var/www\"\n" + "}\n" + ; + bool result = wfd_config_load_string(builder.getBuilder(), config_text); + ASSERT_TRUE(result); +} + +TEST(config, authentication) +{ + MockLogger logger; + EXPECT_CALL(logger, log(_, _, _)).Times(0); + EXPECT_CALL(logger, onclose()).Times(1); + + StrictMock builder; + EXPECT_CALL(builder, addAuthProvider(_, _)).Times(1).WillOnce(Return(true)); + + char const config_text[] = + "version = { major = 1, minor = 0 }\n" + "authentication:\n" + "(\n" + " {\n" + " provider = \"test\"\n" + " settings: { }\n" + " }\n" + ")\n" + ; + bool result = wfd_config_load_string(builder.getBuilder(), config_text); + ASSERT_TRUE(result); +} + +TEST(config, failed_create_authenticator) +{ + MockLogger logger; + EXPECT_CALL(logger, log(_, _, _)).Times(0); + EXPECT_CALL(logger, onclose()).Times(1); + + StrictMock builder; + EXPECT_CALL(builder, addAuthProvider(_, _)).Times(1).WillOnce(Return(false)); + + char const config_text[] = + "version = { major = 1, minor = 0 }\n" + "authentication:\n" + "(\n" + " {\n" + " provider = \"test\"\n" + " settings: { }\n" + " }\n" + ")\n" + ; + bool result = wfd_config_load_string(builder.getBuilder(), config_text); + ASSERT_FALSE(result); +} + +TEST(config, failed_missing_auth_provider) +{ + MockLogger logger; + EXPECT_CALL(logger, log(WFD_LOGLEVEL_ERROR, _, _)).Times(1); + EXPECT_CALL(logger, onclose()).Times(1); + + StrictMock builder; + + char const config_text[] = + "version = { major = 1, minor = 0 }\n" + "authentication:\n" + "(\n" + " {\n" + " settings: { }\n" + " }\n" + ")\n" + ; + bool result = wfd_config_load_string(builder.getBuilder(), config_text); + ASSERT_FALSE(result); +} + +TEST(config, failed_missing_auth_settings) +{ + MockLogger logger; + EXPECT_CALL(logger, log(WFD_LOGLEVEL_ERROR, _, _)).Times(1); + EXPECT_CALL(logger, onclose()).Times(1); + + StrictMock builder; + + char const config_text[] = + "version = { major = 1, minor = 0 }\n" + "authentication:\n" + "(\n" + " {\n" + " provider = \"test\"\n" + " }\n" + ")\n" + ; + bool result = wfd_config_load_string(builder.getBuilder(), config_text); + ASSERT_FALSE(result); +} + +TEST(config, filesystems) +{ + MockLogger logger; + EXPECT_CALL(logger, log(_, _, _)).Times(0); + EXPECT_CALL(logger, onclose()).Times(1); + + StrictMock builder; + EXPECT_CALL(builder, addFilesystem(_, _)).Times(1).WillOnce(Return(true)); + + char const config_text[] = + "version = { major = 1, minor = 0 }\n" + "filesystems:\n" + "(\n" + " {name = \"foo\", mount_point = \"/tmp/test\" }\n" + ")\n" + ; + bool result = wfd_config_load_string(builder.getBuilder(), config_text); + ASSERT_TRUE(result); +} + +TEST(config, filesystems_empty) +{ + MockLogger logger; + EXPECT_CALL(logger, log(_, _, _)).Times(0); + EXPECT_CALL(logger, onclose()).Times(1); + + StrictMock builder; + EXPECT_CALL(builder, addFilesystem(_, _)).Times(0); + + char const config_text[] = + "version = { major = 1, minor = 0 }\n" + "filesystems:\n" + "(\n" + ")\n" + ; + bool result = wfd_config_load_string(builder.getBuilder(), config_text); + ASSERT_TRUE(result); +} + +TEST(config, filesystems_failed_add) +{ + MockLogger logger; + EXPECT_CALL(logger, log(_, _, _)).Times(0); + EXPECT_CALL(logger, onclose()).Times(1); + + StrictMock builder; + EXPECT_CALL(builder, addFilesystem(_, _)).Times(1).WillOnce(Return(false)); + + char const config_text[] = + "version = { major = 1, minor = 0 }\n" + "filesystems:\n" + "(\n" + " {name = \"foo\", mount_point = \"/tmp/test\" }\n" + ")\n" + ; + bool result = wfd_config_load_string(builder.getBuilder(), config_text); + ASSERT_FALSE(result); +} + +TEST(config, filesystems_failed_missing_name) +{ + MockLogger logger; + EXPECT_CALL(logger, log(WFD_LOGLEVEL_ERROR, _, _)).Times(1); + EXPECT_CALL(logger, onclose()).Times(1); + + StrictMock builder; + EXPECT_CALL(builder, addFilesystem(_, _)).Times(0); + + char const config_text[] = + "version = { major = 1, minor = 0 }\n" + "filesystems:\n" + "(\n" + " {mount_point = \"/tmp/test\" }\n" + ")\n" + ; + bool result = wfd_config_load_string(builder.getBuilder(), config_text); + ASSERT_FALSE(result); +} + +TEST(config, filesystems_failed_missing_mountpoint) +{ + MockLogger logger; + EXPECT_CALL(logger, log(WFD_LOGLEVEL_ERROR, _, _)).Times(1); + EXPECT_CALL(logger, onclose()).Times(1); + + StrictMock builder; + EXPECT_CALL(builder, addFilesystem(_, _)).Times(0); + + char const config_text[] = + "version = { major = 1, minor = 0 }\n" + "filesystems:\n" + "(\n" + " {name = \"foo\"}\n" + ")\n" + ; + bool result = wfd_config_load_string(builder.getBuilder(), config_text); + ASSERT_FALSE(result); +} + +TEST(config, log) +{ + MockLogger logger; + EXPECT_CALL(logger, log(_, _, _)).Times(0); + EXPECT_CALL(logger, onclose()).Times(1); + + StrictMock builder; + EXPECT_CALL(builder, setLogger(_, _, _)).Times(1).WillOnce(Return(true)); + + char const config_text[] = + "version = { major = 1, minor = 0 }\n" + "log:\n" + "{\n" + " provider = \"stderr\"\n" + " level = \"all\"\n" + "}\n" + ; + bool result = wfd_config_load_string(builder.getBuilder(), config_text); + ASSERT_TRUE(result); +} + +TEST(config, log_fail_set_logger) +{ + MockLogger logger; + EXPECT_CALL(logger, log(_, _, _)).Times(0); + EXPECT_CALL(logger, onclose()).Times(1); + + StrictMock builder; + EXPECT_CALL(builder, setLogger(_, _, _)).Times(1).WillOnce(Return(false)); + + char const config_text[] = + "version = { major = 1, minor = 0 }\n" + "log:\n" + "{\n" + " provider = \"stderr\"\n" + " level = \"all\"\n" + "}\n" + ; + bool result = wfd_config_load_string(builder.getBuilder(), config_text); + ASSERT_FALSE(result); +} + +TEST(config, log_fail_missing_provider) +{ + MockLogger logger; + EXPECT_CALL(logger, log(WFD_LOGLEVEL_ERROR, _, _)).Times(1); + EXPECT_CALL(logger, onclose()).Times(1); + + StrictMock builder; + EXPECT_CALL(builder, setLogger(_, _, _)).Times(0); + + char const config_text[] = + "version = { major = 1, minor = 0 }\n" + "log:\n" + "{\n" + " level = \"all\"\n" + "}\n" + ; + bool result = wfd_config_load_string(builder.getBuilder(), config_text); + ASSERT_FALSE(result); +} + +TEST(config, log_fail_missing_level) +{ + MockLogger logger; + EXPECT_CALL(logger, log(WFD_LOGLEVEL_ERROR, _, _)).Times(1); + EXPECT_CALL(logger, onclose()).Times(1); + + StrictMock builder; + EXPECT_CALL(builder, setLogger(_, _, _)).Times(0); + + char const config_text[] = + "version = { major = 1, minor = 0 }\n" + "log:\n" + "{\n" + " provider = \"stderr\"\n" + " level = \"fancy\"\n" + "}\n" + ; + bool result = wfd_config_load_string(builder.getBuilder(), config_text); + ASSERT_FALSE(result); +} + +TEST(config, log_fail_invalid_level) +{ + MockLogger logger; + EXPECT_CALL(logger, log(WFD_LOGLEVEL_ERROR, _, _)).Times(1); + EXPECT_CALL(logger, onclose()).Times(1); + + StrictMock builder; + EXPECT_CALL(builder, setLogger(_, _, _)).Times(0); + + char const config_text[] = + "version = { major = 1, minor = 0 }\n" + "log:\n" + "{\n" + " provider = \"stderr\"\n" + "}\n" + ; + bool result = wfd_config_load_string(builder.getBuilder(), config_text); + ASSERT_FALSE(result); +} + diff --git a/test/test_daemon.cc b/test/test_daemon.cc new file mode 100644 index 0000000..a0ed0c5 --- /dev/null +++ b/test/test_daemon.cc @@ -0,0 +1,68 @@ +#include "webfused/daemon.h" + +#include +#include +#include +#include + +TEST(daemon, print_usage) +{ + char argv0[] = "daemon"; + char argv1[] = "--help"; + char * argv[] = { argv0, argv1, NULL}; + + int exit_code = wfd_daemon_run(2, argv); + ASSERT_EQ(EXIT_SUCCESS, exit_code); +} + +TEST(daemon, print_usage_short) +{ + char argv0[] = "daemon"; + char argv1[] = "-h"; + char * argv[] = { argv0, argv1, NULL}; + + int exit_code = wfd_daemon_run(2, argv); + ASSERT_EQ(EXIT_SUCCESS, exit_code); +} + +TEST(daemon, fail_invalid_argument) +{ + char argv0[] = "daemon"; + char argv1[] = "-x"; + char * argv[] = { argv0, argv1, NULL}; + + int exit_code = wfd_daemon_run(2, argv); + ASSERT_EQ(EXIT_FAILURE, exit_code); +} + +TEST(daemon, fail_invalid_config_file) +{ + char argv0[] = "daemon"; + char argv1[] = "-f"; + char argv2[] = "invalid.conf"; + char * argv[] = { argv0, argv1, argv2, NULL}; + + int exit_code = wfd_daemon_run(3, argv); + ASSERT_EQ(EXIT_FAILURE, exit_code); +} + +void defered_raise() +{ + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + raise(SIGINT); +} + +TEST(daemon, run) +{ + char argv0[] = "daemon"; + char argv1[] = "-f"; + char argv2[] = "webfused.conf"; + char * argv[] = { argv0, argv1, argv2, NULL}; + + std::thread thread(defered_raise); + + int exit_code = wfd_daemon_run(3, argv); + ASSERT_EQ(EXIT_SUCCESS, exit_code); + + thread.join(); +} diff --git a/test/test_file_authenticator.cc b/test/test_file_authenticator.cc new file mode 100644 index 0000000..ff3293e --- /dev/null +++ b/test/test_file_authenticator.cc @@ -0,0 +1,158 @@ +#include "webfused/auth/file_authenticator.h" +#include "webfused/auth/authenticator.h" +#include "webfused/config/settings.h" +#include "webfused/auth/factory.h" + +#include "mock_credentials.hpp" +#include "mock_settings.hpp" + +#include +#include + +using ::webfused_test::MockSettings; +using ::webfused_test::MockCredentials; +using ::testing::Return; +using ::testing::StrEq; + +TEST(file_authenticator, create) +{ + MockSettings settings; + EXPECT_CALL(settings, getString(StrEq("file"))).Times(1).WillOnce(Return("test_passwd.json")); + + wfd_authenticator authenticator; + bool success = wfd_file_authenticator_create(nullptr, &authenticator); + ASSERT_TRUE(success); + + wfd_authenticator_dispose(authenticator); +} + +TEST(file_authenticator, create_fail_missing_file) +{ + MockSettings settings; + EXPECT_CALL(settings, getString(StrEq("file"))).Times(1).WillOnce(Return(nullptr)); + + wfd_authenticator authenticator; + bool success = wfd_file_authenticator_create(nullptr, &authenticator); + ASSERT_FALSE(success); +} + +TEST(file_authenticator, create_via_factory) +{ + MockSettings settings; + EXPECT_CALL(settings, getString(StrEq("file"))).Times(1).WillOnce(Return("test_passwd.json")); + + wfd_authenticator authenticator; + bool success = wfd_authenticator_create("file", nullptr, &authenticator); + ASSERT_TRUE(success); + + wfd_authenticator_dispose(authenticator); +} + +TEST(file_authenticator, authenticate) +{ + MockSettings settings; + EXPECT_CALL(settings, getString(StrEq("file"))).Times(1).WillOnce(Return("test_passwd.json")); + + wfd_authenticator authenticator; + bool success = wfd_file_authenticator_create(nullptr, &authenticator); + ASSERT_TRUE(success); + + MockCredentials creds; + EXPECT_CALL(creds, get(StrEq("username"))).Times(1).WillOnce(Return("bob")); + EXPECT_CALL(creds, get(StrEq("password"))).Times(1).WillOnce(Return("secret")); + + bool is_authenticated = wfd_authenticator_authenticate(authenticator, nullptr); + ASSERT_TRUE(is_authenticated); + + wfd_authenticator_dispose(authenticator); +} + +TEST(file_authenticator, authenticate_fail_wrong_passwd) +{ + MockSettings settings; + EXPECT_CALL(settings, getString(StrEq("file"))).Times(1).WillOnce(Return("test_passwd.json")); + + wfd_authenticator authenticator; + bool success = wfd_file_authenticator_create(nullptr, &authenticator); + ASSERT_TRUE(success); + + MockCredentials creds; + EXPECT_CALL(creds, get(StrEq("username"))).Times(1).WillOnce(Return("bob")); + EXPECT_CALL(creds, get(StrEq("password"))).Times(1).WillOnce(Return("unkown")); + + bool is_authenticated = wfd_authenticator_authenticate(authenticator, nullptr); + ASSERT_FALSE(is_authenticated); + + wfd_authenticator_dispose(authenticator); +} + +TEST(file_authenticator, authenticate_fail_no_passwd_file) +{ + MockSettings settings; + EXPECT_CALL(settings, getString(StrEq("file"))).Times(1).WillOnce(Return("unknown_passwd.json")); + + wfd_authenticator authenticator; + bool success = wfd_file_authenticator_create(nullptr, &authenticator); + ASSERT_TRUE(success); + + MockCredentials creds; + EXPECT_CALL(creds, get(StrEq("username"))).Times(1).WillOnce(Return("bob")); + EXPECT_CALL(creds, get(StrEq("password"))).Times(1).WillOnce(Return("secred")); + + bool is_authenticated = wfd_authenticator_authenticate(authenticator, nullptr); + ASSERT_FALSE(is_authenticated); + + wfd_authenticator_dispose(authenticator); +} + +TEST(file_authenticator, authenticate_fail_missing_username) +{ + MockSettings settings; + EXPECT_CALL(settings, getString(StrEq("file"))).Times(1).WillOnce(Return("test_passwd.json")); + + wfd_authenticator authenticator; + bool success = wfd_file_authenticator_create(nullptr, &authenticator); + ASSERT_TRUE(success); + + MockCredentials creds; + EXPECT_CALL(creds, get(StrEq("username"))).Times(1).WillOnce(Return(nullptr)); + EXPECT_CALL(creds, get(StrEq("password"))).Times(1).WillOnce(Return("unkown")); + + bool is_authenticated = wfd_authenticator_authenticate(authenticator, nullptr); + ASSERT_FALSE(is_authenticated); + + wfd_authenticator_dispose(authenticator); +} + +TEST(file_authenticator, authenticate_fail_missing_password) +{ + MockSettings settings; + EXPECT_CALL(settings, getString(StrEq("file"))).Times(1).WillOnce(Return("test_passwd.json")); + + wfd_authenticator authenticator; + bool success = wfd_file_authenticator_create(nullptr, &authenticator); + ASSERT_TRUE(success); + + MockCredentials creds; + EXPECT_CALL(creds, get(StrEq("username"))).Times(1).WillOnce(Return("bob")); + EXPECT_CALL(creds, get(StrEq("password"))).Times(1).WillOnce(Return(nullptr)); + + bool is_authenticated = wfd_authenticator_authenticate(authenticator, nullptr); + ASSERT_FALSE(is_authenticated); + + wfd_authenticator_dispose(authenticator); +} + +TEST(file_authenticator, get_type) +{ + MockSettings settings; + EXPECT_CALL(settings, getString(StrEq("file"))).Times(1).WillOnce(Return("/any/path")); + + wfd_authenticator authenticator; + bool success = wfd_file_authenticator_create(nullptr, &authenticator); + ASSERT_TRUE(success); + + ASSERT_STREQ("username", wfd_authenticator_get_type(authenticator)); + + wfd_authenticator_dispose(authenticator); +} diff --git a/test/test_log.cc b/test/test_log.cc new file mode 100644 index 0000000..331d0eb --- /dev/null +++ b/test/test_log.cc @@ -0,0 +1,170 @@ +#include +#include + +#include "webfused/log/logger.h" +#include "webfused/log/log.h" +#include "mock_logger.hpp" + +using ::testing::_; +using ::testing::StrEq; +using ::webfused_test::MockLogger; + +TEST(log, fatal) +{ + MockLogger logger(true); + + EXPECT_CALL(logger, log(WFD_LOGLEVEL_FATAL, StrEq("too bad"), _)).Times(1); + EXPECT_CALL(logger, onclose()).Times(1); + + wfd_logger_init(WFD_LOGLEVEL_ALL, &wfd_MockLogger_log, &wfd_MockLogger_onclose, logger.getUserData()); + WFD_FATAL("too bad"); + wfd_logger_close(); +} + +TEST(log, error) +{ + MockLogger logger(true); + + EXPECT_CALL(logger, log(WFD_LOGLEVEL_ERROR, StrEq("too bad"), _)).Times(1); + EXPECT_CALL(logger, onclose()).Times(1); + + wfd_logger_init(WFD_LOGLEVEL_ALL, &wfd_MockLogger_log, &wfd_MockLogger_onclose, logger.getUserData()); + WFD_ERROR("too bad"); + wfd_logger_close(); +} + +TEST(log, warn) +{ + MockLogger logger(true); + + EXPECT_CALL(logger, log(WFD_LOGLEVEL_WARN, StrEq("too bad"), _)).Times(1); + EXPECT_CALL(logger, onclose()).Times(1); + + wfd_logger_init(WFD_LOGLEVEL_ALL, &wfd_MockLogger_log, &wfd_MockLogger_onclose, logger.getUserData()); + WFD_WARN("too bad"); + wfd_logger_close(); +} + +TEST(log, info) +{ + MockLogger logger(true); + + EXPECT_CALL(logger, log(WFD_LOGLEVEL_INFO, StrEq("too bad"), _)).Times(1); + EXPECT_CALL(logger, onclose()).Times(1); + + wfd_logger_init(WFD_LOGLEVEL_ALL, &wfd_MockLogger_log, &wfd_MockLogger_onclose, logger.getUserData()); + WFD_INFO("too bad"); + wfd_logger_close(); +} + +TEST(log, debug) +{ + MockLogger logger(true); + + EXPECT_CALL(logger, log(WFD_LOGLEVEL_DEBUG, StrEq("too bad"), _)).Times(1); + EXPECT_CALL(logger, onclose()).Times(1); + + wfd_logger_init(WFD_LOGLEVEL_ALL, &wfd_MockLogger_log, &wfd_MockLogger_onclose, logger.getUserData()); + WFD_DEBUG("too bad"); + wfd_logger_close(); +} + +TEST(log, respect_loglevel) +{ + MockLogger logger(true); + + EXPECT_CALL(logger, log(_, _, _)).Times(0); + EXPECT_CALL(logger, onclose()).Times(1); + + wfd_logger_init(WFD_LOGLEVEL_WARN, &wfd_MockLogger_log, &wfd_MockLogger_onclose, logger.getUserData()); + WFD_DEBUG("too bad"); + wfd_logger_close(); +} + +TEST(log, log_same_loglevel) +{ + MockLogger logger(true); + + EXPECT_CALL(logger, log(WFD_LOGLEVEL_WARN, StrEq("too bad"), _)).Times(1); + EXPECT_CALL(logger, onclose()).Times(1); + + wfd_logger_init(WFD_LOGLEVEL_WARN, &wfd_MockLogger_log, &wfd_MockLogger_onclose, logger.getUserData()); + WFD_WARN("too bad"); + wfd_logger_close(); +} + +TEST(log, omit_onclose_if_nullptr) +{ + MockLogger logger(true); + + EXPECT_CALL(logger, log(WFD_LOGLEVEL_WARN, StrEq("too bad"), _)).Times(1); + EXPECT_CALL(logger, onclose()).Times(0); + + wfd_logger_init(WFD_LOGLEVEL_WARN, &wfd_MockLogger_log, nullptr, logger.getUserData()); + WFD_WARN("too bad"); + wfd_logger_close(); +} + +TEST(log, default_log) +{ + WFD_ERROR("trigger log"); +} + +TEST(log, loglevel_parse) +{ + int level; + + ASSERT_TRUE(wfd_log_level_parse("fatal", &level)); + ASSERT_EQ(WFD_LOGLEVEL_FATAL, level); + ASSERT_TRUE(wfd_log_level_parse("FATAL", &level)); + ASSERT_EQ(WFD_LOGLEVEL_FATAL, level); + + ASSERT_TRUE(wfd_log_level_parse("error", &level)); + ASSERT_EQ(WFD_LOGLEVEL_ERROR, level); + ASSERT_TRUE(wfd_log_level_parse("ERROR", &level)); + ASSERT_EQ(WFD_LOGLEVEL_ERROR, level); + + ASSERT_TRUE(wfd_log_level_parse("warn", &level)); + ASSERT_EQ(WFD_LOGLEVEL_WARN, level); + ASSERT_TRUE(wfd_log_level_parse("WARN", &level)); + ASSERT_EQ(WFD_LOGLEVEL_WARN, level); + ASSERT_TRUE(wfd_log_level_parse("warning", &level)); + ASSERT_EQ(WFD_LOGLEVEL_WARN, level); + ASSERT_TRUE(wfd_log_level_parse("WARNING", &level)); + ASSERT_EQ(WFD_LOGLEVEL_WARN, level); + + ASSERT_TRUE(wfd_log_level_parse("info", &level)); + ASSERT_EQ(WFD_LOGLEVEL_INFO, level); + ASSERT_TRUE(wfd_log_level_parse("INFO", &level)); + ASSERT_EQ(WFD_LOGLEVEL_INFO, level); + + ASSERT_TRUE(wfd_log_level_parse("debug", &level)); + ASSERT_EQ(WFD_LOGLEVEL_DEBUG, level); + ASSERT_TRUE(wfd_log_level_parse("DEBUG", &level)); + ASSERT_EQ(WFD_LOGLEVEL_DEBUG, level); + + ASSERT_FALSE(wfd_log_level_parse("", &level)); + + ASSERT_TRUE(wfd_log_level_parse("all", &level)); + ASSERT_EQ(WFD_LOGLEVEL_ALL, level); + ASSERT_TRUE(wfd_log_level_parse("ALL", &level)); + ASSERT_EQ(WFD_LOGLEVEL_ALL, level); + + ASSERT_TRUE(wfd_log_level_parse("none", &level)); + ASSERT_EQ(WFD_LOGLEVEL_NONE, level); + ASSERT_TRUE(wfd_log_level_parse("NONE", &level)); + ASSERT_EQ(WFD_LOGLEVEL_NONE, level); + +} + +TEST(log, log_level_tostring) +{ + ASSERT_STREQ("none", wfd_log_level_tostring(WFD_LOGLEVEL_NONE)); + ASSERT_STREQ("fatal", wfd_log_level_tostring(WFD_LOGLEVEL_FATAL)); + ASSERT_STREQ("error", wfd_log_level_tostring(WFD_LOGLEVEL_ERROR)); + ASSERT_STREQ("warn", wfd_log_level_tostring(WFD_LOGLEVEL_WARN)); + ASSERT_STREQ("info", wfd_log_level_tostring(WFD_LOGLEVEL_INFO)); + ASSERT_STREQ("debug", wfd_log_level_tostring(WFD_LOGLEVEL_DEBUG)); + ASSERT_STREQ("all", wfd_log_level_tostring(WFD_LOGLEVEL_ALL)); + ASSERT_STREQ("", wfd_log_level_tostring(42)); +} \ No newline at end of file diff --git a/test/test_log_manager.cc b/test/test_log_manager.cc new file mode 100644 index 0000000..35ce8bb --- /dev/null +++ b/test/test_log_manager.cc @@ -0,0 +1,10 @@ +#include "webfused/log/manager.h" +#include "webfused/log/log.h" + +#include +#include + +TEST(log_manager, set_logger_fail_unknown_provider) +{ + ASSERT_FALSE(wfd_log_manager_set_logger("unknown", WFD_LOGLEVEL_ALL, NULL)); +} diff --git a/test/test_mountpoint_factory.cc b/test/test_mountpoint_factory.cc new file mode 100644 index 0000000..0509d0f --- /dev/null +++ b/test/test_mountpoint_factory.cc @@ -0,0 +1,135 @@ +#include +#include "webfused/mountpoint_factory.h" +#include + +#include + +TEST(mountpoint_factory, create) +{ + wfd_mountpoint_factory * factory = wfd_mountpoint_factory_create(); + ASSERT_NE(nullptr, factory); + + wfd_mountpoint_factory_dispose(factory); +} + +TEST(mountpiont_factory, add_filesystem) +{ + wfd_mountpoint_factory * factory = wfd_mountpoint_factory_create(); + ASSERT_NE(nullptr, factory); + + bool success = wfd_mountpoint_factory_add_filesystem(factory, "test", "/tmp/webfused_test"); + ASSERT_TRUE(success); + + wfd_mountpoint_factory_dispose(factory); +} + +TEST(mountpiont_factory, add_filesystem_fail_to_add_twice) +{ + wfd_mountpoint_factory * factory = wfd_mountpoint_factory_create(); + ASSERT_NE(nullptr, factory); + + bool success = wfd_mountpoint_factory_add_filesystem(factory, "test", "/tmp/webfused_test"); + ASSERT_TRUE(success); + + success = wfd_mountpoint_factory_add_filesystem(factory, "test", "/tmp/webfused_test"); + ASSERT_FALSE(success); + + wfd_mountpoint_factory_dispose(factory); +} + +TEST(mountpiont_factory, add_filesystem_multi) +{ + wfd_mountpoint_factory * factory = wfd_mountpoint_factory_create(); + ASSERT_NE(nullptr, factory); + + + for (size_t i = 0; i < 24; i++) + { + char name[10]; + snprintf(name, 10, "test_%zu", i); + bool success = wfd_mountpoint_factory_add_filesystem(factory, name, "/tmp/webfused_test"); + ASSERT_TRUE(success) << i; + } + + wfd_mountpoint_factory_dispose(factory); +} + +TEST(mountpiont_factory, add_filesystem_fail_invalid_path) +{ + wfd_mountpoint_factory * factory = wfd_mountpoint_factory_create(); + ASSERT_NE(nullptr, factory); + + bool success = wfd_mountpoint_factory_add_filesystem(factory, "test", "/do/not/allow/nested/paths"); + ASSERT_FALSE(success); + + wfd_mountpoint_factory_dispose(factory); +} + +TEST(mountpiont_factory, create_mountpoint) +{ + wfd_mountpoint_factory * factory = wfd_mountpoint_factory_create(); + ASSERT_NE(nullptr, factory); + + bool success = wfd_mountpoint_factory_add_filesystem(factory, "test", "/tmp/webfuse_test"); + ASSERT_TRUE(success); + + wf_mountpoint * mountpoint = wfd_mountpoint_factory_create_mountpoint("test", factory); + ASSERT_NE(nullptr, mountpoint); + ASSERT_STREQ("/tmp/webfuse_test", wf_mountpoint_get_path(mountpoint)); + + wf_mountpoint_dispose(mountpoint); + wfd_mountpoint_factory_dispose(factory); +} + +TEST(mountpiont_factory, create_mountpoint_fail_already_in_use) +{ + wfd_mountpoint_factory * factory = wfd_mountpoint_factory_create(); + ASSERT_NE(nullptr, factory); + + bool success = wfd_mountpoint_factory_add_filesystem(factory, "test", "/tmp/webfuse_test"); + ASSERT_TRUE(success); + + wf_mountpoint * mountpoint = wfd_mountpoint_factory_create_mountpoint("test", factory); + ASSERT_NE(nullptr, mountpoint); + ASSERT_STREQ("/tmp/webfuse_test", wf_mountpoint_get_path(mountpoint)); + + wf_mountpoint * mountpoint2 = wfd_mountpoint_factory_create_mountpoint("test", factory); + ASSERT_EQ(nullptr, mountpoint2); + + wf_mountpoint_dispose(mountpoint); + wfd_mountpoint_factory_dispose(factory); +} + +TEST(mountpiont_factory, create_mountpoint_fail_unknown_filesystem) +{ + wfd_mountpoint_factory * factory = wfd_mountpoint_factory_create(); + ASSERT_NE(nullptr, factory); + + bool success = wfd_mountpoint_factory_add_filesystem(factory, "test", "/tmp/webfuse_test"); + ASSERT_TRUE(success); + + wf_mountpoint * mountpoint = wfd_mountpoint_factory_create_mountpoint("unkown", factory); + ASSERT_EQ(nullptr, mountpoint); + + wfd_mountpoint_factory_dispose(factory); +} + +TEST(mountpiont_factory, create_mountpoint_multi) +{ + wfd_mountpoint_factory * factory = wfd_mountpoint_factory_create(); + ASSERT_NE(nullptr, factory); + + bool success = wfd_mountpoint_factory_add_filesystem(factory, "test", "/tmp/webfuse_test"); + ASSERT_TRUE(success); + + for(int i = 0; i < 5; i++) + { + wf_mountpoint * mountpoint = wfd_mountpoint_factory_create_mountpoint("test", factory); + ASSERT_NE(nullptr, mountpoint) << i; + ASSERT_STREQ("/tmp/webfuse_test", wf_mountpoint_get_path(mountpoint)) << i; + + wf_mountpoint_dispose(mountpoint); + } + + wfd_mountpoint_factory_dispose(factory); +} diff --git a/test/test_passwd.json b/test/test_passwd.json new file mode 100644 index 0000000..595c06d --- /dev/null +++ b/test/test_passwd.json @@ -0,0 +1,14 @@ +{ + "meta": { + "type": "wf-userdb", + "major": 1, + "minor": 0, + "hash_algorithm": "sha512" + }, + "users": { + "bob": { + "password_hash": "e51e27ce47054feead3d83068d47f2a07307d4877ac67da668ef43e0e466fe8c7b66651af14fdb8d48c51592ef5afa0c63f874d20861c6b9ef8e6513bfcaa330", + "salt": "b3be6979921edecfea88c50d0d1ec40b7f8c383831b2276c65969ead18e47c03" + } + } +} \ No newline at end of file diff --git a/test/test_settings.cc b/test/test_settings.cc new file mode 100644 index 0000000..3f6b1a5 --- /dev/null +++ b/test/test_settings.cc @@ -0,0 +1,90 @@ +#include +#include "webfused/config/settings_intern.h" +#include "webfused/config/settings.h" +#include + +TEST(settings, get_string) +{ + char const settings_text[] = + "settings:\n" + "{\n" + " string_value = \"some.string\"\n" + " int_value = 42\n" + "}\n" + ; + config_t config; + config_init(&config); + int rc = config_read_string(&config, settings_text); + ASSERT_TRUE(CONFIG_TRUE == rc); + + config_setting_t * setting = config_lookup(&config, "settings"); + ASSERT_NE(nullptr, setting); + + wfd_settings settings; + wfd_settings_init(&settings, setting); + ASSERT_STREQ("some.string", wfd_settings_get_string(&settings, "string_value")); + ASSERT_EQ(nullptr, wfd_settings_get_string(&settings, "int_value")); + ASSERT_EQ(nullptr, wfd_settings_get_string(&settings, "invalid_value")); + ASSERT_EQ(nullptr, wfd_settings_get_string(NULL, "invalid_value")); + + wfd_settings_cleanup(&settings); + config_destroy(&config); +} + +TEST(settings, get_string_or_default) +{ + char const settings_text[] = + "settings:\n" + "{\n" + " string_value = \"some.string\"\n" + " int_value = 42\n" + "}\n" + ; + config_t config; + config_init(&config); + int rc = config_read_string(&config, settings_text); + ASSERT_TRUE(CONFIG_TRUE == rc); + + config_setting_t * setting = config_lookup(&config, "settings"); + ASSERT_NE(nullptr, setting); + + wfd_settings settings; + wfd_settings_init(&settings, setting); + ASSERT_STREQ("some.string", wfd_settings_get_string_or_default(&settings, "string_value", "default")); + ASSERT_STREQ("default", wfd_settings_get_string_or_default(&settings, "int_value", "default")); + ASSERT_STREQ("default", wfd_settings_get_string_or_default(&settings, "invalid_value", "default")); + ASSERT_STREQ("default", wfd_settings_get_string_or_default(NULL, "invalid_value", "default")); + + wfd_settings_cleanup(&settings); + config_destroy(&config); +} + +TEST(settings, get_bool) +{ + char const settings_text[] = + "settings:\n" + "{\n" + " true_value = true\n" + " false_value = false\n" + " int_value = 42\n" + "}\n" + ; + config_t config; + config_init(&config); + int rc = config_read_string(&config, settings_text); + ASSERT_TRUE(CONFIG_TRUE == rc); + + config_setting_t * setting = config_lookup(&config, "settings"); + ASSERT_NE(nullptr, setting); + + wfd_settings settings; + wfd_settings_init(&settings, setting); + ASSERT_TRUE(wfd_settings_get_bool(&settings, "true_value")); + ASSERT_FALSE(wfd_settings_get_bool(&settings, "false_value")); + ASSERT_FALSE(wfd_settings_get_bool(&settings, "int_value")); + ASSERT_FALSE(wfd_settings_get_bool(&settings, "invalid_value")); + ASSERT_FALSE(wfd_settings_get_bool(NULL, "invalid_value")); + + wfd_settings_cleanup(&settings); + config_destroy(&config); +} \ No newline at end of file diff --git a/test/test_stderr_logger.cc b/test/test_stderr_logger.cc new file mode 100644 index 0000000..923bf59 --- /dev/null +++ b/test/test_stderr_logger.cc @@ -0,0 +1,27 @@ +#include "webfused/log/stderr_logger.h" +#include "webfused/log/manager.h" +#include "webfused/log/logger.h" +#include "webfused/log/log.h" + +#include +#include + +TEST(stderr_logger, init) +{ + ASSERT_TRUE(wfd_stderr_logger_init(WFD_LOGLEVEL_ALL, NULL)); + wfd_logger_close(); +} + +TEST(stderr_logger, init_via_manager) +{ + ASSERT_TRUE(wfd_log_manager_set_logger("stderr", WFD_LOGLEVEL_ALL, NULL)); + wfd_logger_close(); +} + +TEST(stderr_logger, log) +{ + ASSERT_TRUE(wfd_stderr_logger_init(WFD_LOGLEVEL_ALL, NULL)); + WFD_INFO("some information"); + + wfd_logger_close(); +} \ No newline at end of file diff --git a/test/test_syslog_logger.cc b/test/test_syslog_logger.cc new file mode 100644 index 0000000..1d04740 --- /dev/null +++ b/test/test_syslog_logger.cc @@ -0,0 +1,45 @@ +#include "webfused/log/syslog_logger.h" +#include "webfused/log/manager.h" +#include "webfused/log/logger.h" +#include "webfused/log/log.h" + +#include "mock_settings.hpp" + +#include +#include + +using ::webfused_test::MockSettings; +using ::testing::StrEq; +using ::testing::_; +using ::testing::Return; + +TEST(syslog_logger, init) +{ + ASSERT_TRUE(wfd_syslog_logger_init(WFD_LOGLEVEL_ALL, NULL)); + wfd_logger_close(); +} + +TEST(syslog_logger, init_fail_invalid_facility) +{ + MockSettings settings; + EXPECT_CALL(settings, getStringOrDefault(StrEq("ident"), _)).Times(1).WillOnce(Return("webfused_test")); + EXPECT_CALL(settings, getStringOrDefault(StrEq("facility"), _)).Times(1).WillOnce(Return("invalid")); + EXPECT_CALL(settings, getBool(StrEq("log_pid"))).Times(1).WillOnce(Return(false)); + + wfd_settings * fake_settings = reinterpret_cast(0xDEADBEEF); + ASSERT_FALSE(wfd_syslog_logger_init(WFD_LOGLEVEL_ALL, fake_settings)); +} + +TEST(syslog_logger, init_via_manager) +{ + ASSERT_TRUE(wfd_log_manager_set_logger("syslog", WFD_LOGLEVEL_ALL, NULL)); + wfd_logger_close(); +} + +TEST(syslog_logger, log) +{ + ASSERT_TRUE(wfd_syslog_logger_init(WFD_LOGLEVEL_ALL, NULL)); + WFD_INFO("some information"); + + wfd_logger_close(); +} \ No newline at end of file