diff --git a/CMakeLists.txt b/CMakeLists.txt index d91797d..b6af497 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -53,6 +53,10 @@ add_library(webfused-static STATIC src/webfused/config/config.c src/webfused/config/factory.c src/webfused/config/builder.c + src/webfused/config/auth_settings.c + src/webfused/auth/authenticator.c + src/webfused/auth/factory.c + src/webfused/auth/file_authenticator.c src/webfused/log/logger.c ) @@ -110,6 +114,8 @@ add_executable(alltests test/mock_logger.cc test/test_config_factory.cc test/test_config.cc + test/test_auth_settings.cc + test/test_auth_factory.cc test/test_log.cc ) @@ -133,6 +139,7 @@ target_link_libraries(alltests PRIVATE # copy test data configure_file(etc/webfused.conf webfused.conf COPYONLY) +configure_file(test/invalid.conf invalid.conf COPYONLY) enable_testing() gtest_discover_tests(alltests TEST_PREFIX alltests:) @@ -151,7 +158,7 @@ add_custom_target(coverage COMMAND ./alltests COMMAND lcov --capture --directory . --output-file coverage/lcov.info COMMAND lcov --remove coverage/lcov.info '/usr/*' --output-file coverage/lcov.info -# COMMAND lcov --remove coverage/lcov.info '*/test/*' --output-file coverage/lcov.info + COMMAND lcov --remove coverage/lcov.info '*/test/*' --output-file coverage/lcov.info ) add_dependencies(coverage alltests) diff --git a/src/webfused/auth/authenticator.c b/src/webfused/auth/authenticator.c new file mode 100644 index 0000000..4e9342b --- /dev/null +++ b/src/webfused/auth/authenticator.c @@ -0,0 +1,8 @@ +#include "webfused/auth/authenticator.h" + +void +wfd_authenticator_dispose( + struct wfd_authenticator authenticator) +{ + authenticator.vtable->dispose(authenticator.data); +} diff --git a/src/webfused/auth/authenticator.h b/src/webfused/auth/authenticator.h new file mode 100644 index 0000000..2300b9e --- /dev/null +++ b/src/webfused/auth/authenticator.h @@ -0,0 +1,36 @@ +#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); + +struct wfd_authenticator_vtable +{ + wfd_authenticator_dispose_fn * dispose; + wf_authenticate_fn * authenticate; +}; + +struct wfd_authenticator +{ + struct wfd_authenticator_vtable const * vtable; + void * data; +}; + +extern void +wfd_authenticator_dispose( + 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..b2744e0 --- /dev/null +++ b/src/webfused/auth/factory.c @@ -0,0 +1,25 @@ +#include "webfused/auth/factory.h" +#include "webfused/auth/settings.h" +#include "webfused/auth/file_authenticator.h" +#include "webfused/log/log.h" + +#include + +bool +wfd_authenticator_create( + struct wfd_auth_settings * settings, + struct wfd_authenticator * authenticator) +{ + bool result = false; + char const * provider_name = wfd_auth_settings_get_provider(settings); + if (0 == strcmp("file", provider_name)) + { + result = wfd_file_authenticator_create(settings, authenticator); + } + else + { + WFD_ERROR("failed to create authenticator: unknown type \"%s\"", provider_name); + } + + return result; +} diff --git a/src/webfused/auth/factory.h b/src/webfused/auth/factory.h new file mode 100644 index 0000000..5121b78 --- /dev/null +++ b/src/webfused/auth/factory.h @@ -0,0 +1,25 @@ +#ifndef WFD_AUTHENTICATION_FACTORY_H +#define WFD_AUTHENTICATION_FACTORY_H + +#ifndef __cplusplus +#include +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + +struct wfd_authenticator; +struct wfd_auth_settings; + +extern bool +wfd_authenticator_create( + struct wfd_auth_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..02ee81e --- /dev/null +++ b/src/webfused/auth/file_authenticator.c @@ -0,0 +1,9 @@ +#include "webfused/auth/file_authenticator.h" + +bool +wfd_file_authenticator_create( + struct wfd_auth_settings * settings, + struct wfd_authenticator * authenticator) +{ + return false; +} diff --git a/src/webfused/auth/file_authenticator.h b/src/webfused/auth/file_authenticator.h new file mode 100644 index 0000000..1ece0e3 --- /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_auth_settings; + +extern bool +wfd_file_authenticator_create( + struct wfd_auth_settings * settings, + struct wfd_authenticator * authenticator); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/webfused/authentication/auth_settings.h b/src/webfused/auth/settings.h similarity index 100% rename from src/webfused/authentication/auth_settings.h rename to src/webfused/auth/settings.h diff --git a/src/webfused/authentication/auth_settings_builder.h b/src/webfused/authentication/auth_settings_builder.h deleted file mode 100644 index 6de12c7..0000000 --- a/src/webfused/authentication/auth_settings_builder.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef WFD_AUTH_SETTINGS_BUILDER_H -#define WFD_AUTH_SETTINGS_BUILDER_H - -#ifdef __cplusplus -#endif - -extern struct wfd_auth_settings * -wfd_auth_settings_create(void); - -extern void -wfd_auth_settings_dispose( - struct wfd_auth_settings * settings); - -extern void -wfd_auth_settings_set_provider( - struct wfd_auth_settings * settings, - char const * provider); - -extern void -wfd_auth_settings_add( - struct wfd_auth_settings * settings, - char const * key, - char const * value); - -#ifdef __cplusplus -#endif - -#endif diff --git a/src/webfused/config/auth_settings.c b/src/webfused/config/auth_settings.c new file mode 100644 index 0000000..de319d3 --- /dev/null +++ b/src/webfused/config/auth_settings.c @@ -0,0 +1,51 @@ +#include "webfused/auth/settings.h" +#include "webfused/config/auth_settings.h" + +#include +#include +#include + +struct wfd_auth_settings +{ + char * provider_name; + struct config_setting_t * settings; +}; + +struct wfd_auth_settings * +wfd_auth_settings_create( + char const * provider_name, + struct config_setting_t * settings) +{ + struct wfd_auth_settings * auth_settings = malloc(sizeof(struct wfd_auth_settings)); + auth_settings->provider_name = strdup(provider_name); + auth_settings->settings = settings; + + return auth_settings; +} + +void +wfd_auth_settings_dispose( + struct wfd_auth_settings * settings) +{ + free(settings->provider_name); + free(settings); +} + +char const * +wfd_auth_settings_get_provider( + struct wfd_auth_settings * settings) +{ + return settings->provider_name; +} + +char const * +wfd_auth_settings_get( + struct wfd_auth_settings * settings, + char const * key) +{ + char const * result; + int rc = config_setting_lookup_string(settings->settings, key, &result); + + return (CONFIG_TRUE == rc) ? result : NULL; +} + diff --git a/src/webfused/config/auth_settings.h b/src/webfused/config/auth_settings.h new file mode 100644 index 0000000..dd2abfc --- /dev/null +++ b/src/webfused/config/auth_settings.h @@ -0,0 +1,25 @@ +#ifndef WFD_CONFIG_AUTH_SETTINGS_H +#define WFD_CONFIG_AUTH_SETTINGS_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +struct wfd_auth_settings; +struct config_setting_t; + +extern struct wfd_auth_settings * +wfd_auth_settings_create( + char const * provider_name, + struct config_setting_t * settings); + +extern void +wfd_auth_settings_dispose( + struct wfd_auth_settings * settings); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/webfused/config/builder.c b/src/webfused/config/builder.c index 7d1c6c1..e59c9d5 100644 --- a/src/webfused/config/builder.c +++ b/src/webfused/config/builder.c @@ -40,3 +40,12 @@ wfd_config_builder_set_server_document_root( builder.vtable->set_server_document_root(builder.data, document_root); } +bool +wfd_config_builder_add_auth_provider( + struct wfd_config_builder builder, + struct wfd_auth_settings * settings) +{ + return builder.vtable->add_auth_provider(builder.data, settings); +} + + diff --git a/src/webfused/config/builder.h b/src/webfused/config/builder.h index 0dbc753..0e75f2d 100644 --- a/src/webfused/config/builder.h +++ b/src/webfused/config/builder.h @@ -1,11 +1,17 @@ #ifndef WFD_CONFIG_BUILDER_H #define WFD_CONFIG_BUILDER_H +#ifndef __cplusplus +#include +#endif + #ifdef __cplusplus extern "C" { #endif +struct wfd_auth_settings; + typedef void wfd_config_builder_set_server_vhostname_fn( void * data, @@ -31,6 +37,11 @@ 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, + struct wfd_auth_settings * settings); + struct wfd_config_builder_vtable { wfd_config_builder_set_server_vhostname_fn * set_server_vhostname; @@ -38,6 +49,7 @@ struct wfd_config_builder_vtable 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; }; struct wfd_config_builder @@ -71,6 +83,11 @@ 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, + struct wfd_auth_settings * settings); + #ifdef __cplusplus } diff --git a/src/webfused/config/factory.c b/src/webfused/config/factory.c index 36096a1..6be06bf 100644 --- a/src/webfused/config/factory.c +++ b/src/webfused/config/factory.c @@ -1,4 +1,5 @@ #include "webfused/config/factory.h" +#include "webfused/config/auth_settings.h" #include "webfused/log/log.h" #include @@ -97,6 +98,51 @@ wfd_config_read_server( return true; } +static bool +wfd_config_read_authentication( + config_t * config, + struct wfd_config_builder builder) +{ + bool result = true; + + bool hasAuthentication = (NULL != config_lookup(config, "authentication")); + if (hasAuthentication) + { + char const * provider_name = NULL; + { + int rc = config_lookup_string(config, "authentication.provider", &provider_name); + if (CONFIG_TRUE != rc) + { + WFD_ERROR("missing authentication provider"); + result = false; + } + } + + struct config_setting_t * settings = NULL; + if (result) + { + settings = config_lookup(config, "authentication.settings"); + if (NULL == settings) + { + WFD_ERROR("missing authentication settings"); + result = false; + } + } + + if (result) + { + struct wfd_auth_settings * auth_settings = wfd_auth_settings_create( + provider_name, settings); + + result = wfd_config_builder_add_auth_provider(builder, auth_settings); + wfd_auth_settings_dispose(auth_settings); + } + + } + + return result; +} + static bool wfd_config_load( struct wfd_config_builder builder, @@ -105,6 +151,7 @@ wfd_config_load( bool result = wfd_config_check_version(config) && wfd_config_read_server(config, builder) + && wfd_config_read_authentication(config, builder) ; return result; 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 index 90334b7..0fb7f11 100644 --- a/test/mock_config_builder.cc +++ b/test/mock_config_builder.cc @@ -5,7 +5,7 @@ extern "C" using webfused_test::IConfigBuilder; static void -wfd_MockConifigBuilder_set_server_vhostname( +wfd_MockConfigBuilder_set_server_vhostname( void * data, char const * vhost_name) { @@ -14,7 +14,7 @@ wfd_MockConifigBuilder_set_server_vhostname( } static void -wfd_MockConifigBuilder_set_server_port( +wfd_MockConfigBuilder_set_server_port( void * data, int port) { @@ -23,7 +23,7 @@ wfd_MockConifigBuilder_set_server_port( } static void -wfd_MockConifigBuilder_set_server_key( +wfd_MockConfigBuilder_set_server_key( void * data, char const * key_path) { @@ -32,7 +32,7 @@ wfd_MockConifigBuilder_set_server_key( } static void -wfd_MockConifigBuilder_set_server_cert( +wfd_MockConfigBuilder_set_server_cert( void * data, char const * cert_path) { @@ -41,7 +41,7 @@ wfd_MockConifigBuilder_set_server_cert( } static void -wfd_MockConifigBuilder_set_server_document_root( +wfd_MockConfigBuilder_set_server_document_root( void * data, char const * document_root) { @@ -49,13 +49,24 @@ wfd_MockConifigBuilder_set_server_document_root( builder->setServerDocumentRoot(document_root); } +static bool +wfd_MockConfigBuilder_add_auth_provider( + void * data, + struct wfd_auth_settings * settings) +{ + auto * builder = reinterpret_cast(data); + builder->addAuthProvider(settings); +} + + static const wfd_config_builder_vtable wfd_MockConfigBuilder_vtable = { - &wfd_MockConifigBuilder_set_server_vhostname, - &wfd_MockConifigBuilder_set_server_port, - &wfd_MockConifigBuilder_set_server_key, - &wfd_MockConifigBuilder_set_server_cert, - &wfd_MockConifigBuilder_set_server_document_root + &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 }; } diff --git a/test/mock_config_builder.hpp b/test/mock_config_builder.hpp index 4620bf0..7141c17 100644 --- a/test/mock_config_builder.hpp +++ b/test/mock_config_builder.hpp @@ -16,6 +16,7 @@ public: 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(wfd_auth_settings * settings) = 0; }; class MockConfigBuilder: public IConfigBuilder @@ -27,6 +28,7 @@ public: 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_METHOD1(addAuthProvider, bool (wfd_auth_settings * settings)); struct wfd_config_builder getBuilder(); }; diff --git a/test/test_auth_factory.cc b/test/test_auth_factory.cc new file mode 100644 index 0000000..e3611dc --- /dev/null +++ b/test/test_auth_factory.cc @@ -0,0 +1,26 @@ +#include +#include "webfused/auth/factory.h" +#include "webfused/auth/authenticator.h" +#include "webfused/config/auth_settings.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_auth_settings * auth_settings = wfd_auth_settings_create( + "unknown", NULL); + + wfd_authenticator authenticator; + bool result = wfd_authenticator_create(auth_settings, &authenticator); + ASSERT_FALSE(result); + + wfd_auth_settings_dispose(auth_settings); +} \ No newline at end of file diff --git a/test/test_auth_settings.cc b/test/test_auth_settings.cc new file mode 100644 index 0000000..2f786a9 --- /dev/null +++ b/test/test_auth_settings.cc @@ -0,0 +1,31 @@ +#include +#include "webfused/auth/settings.h" +#include "webfused/config/auth_settings.h" +#include + +TEST(auth_settings, auth_settings) +{ + 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 * settings = config_lookup(&config, "settings"); + ASSERT_NE(nullptr, settings); + + wfd_auth_settings * auth_settings = wfd_auth_settings_create("test_provider", settings); + ASSERT_STREQ("test_provider", wfd_auth_settings_get_provider(auth_settings)); + ASSERT_STREQ("some.string", wfd_auth_settings_get(auth_settings, "string_value")); + ASSERT_EQ(nullptr, wfd_auth_settings_get(auth_settings, "int_value")); + ASSERT_EQ(nullptr, wfd_auth_settings_get(auth_settings, "invalid_value")); + + wfd_auth_settings_dispose(auth_settings); + config_destroy(&config); +} \ No newline at end of file diff --git a/test/test_config_factory.cc b/test/test_config_factory.cc index c0179b1..425d161 100644 --- a/test/test_config_factory.cc +++ b/test/test_config_factory.cc @@ -8,6 +8,7 @@ using ::testing::_; +using ::testing::Return; using ::testing::StrictMock; using ::testing::StrEq; using ::webfused_test::MockLogger; @@ -18,6 +19,7 @@ TEST(config, is_loadable) StrictMock builder; EXPECT_CALL(builder, setServerVhostname(StrEq("localhost"))).Times(1); EXPECT_CALL(builder, setServerPort(8080)).Times(1); + EXPECT_CALL(builder, addAuthProvider(_)).Times(1).WillOnce(Return(true)); bool result = wfd_config_load_file(builder.getBuilder(), "webfused.conf"); ASSERT_TRUE(result); @@ -50,6 +52,18 @@ TEST(config, invalid_config) 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; @@ -239,3 +253,83 @@ TEST(config, document_root) 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" + " provider = \"test\"\n" + " settings: { }\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" + " provider = \"test\"\n" + " settings: { }\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" + " settings: { }\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" + " provider = \"test\"\n" + "}\n" + ; + bool result = wfd_config_load_string(builder.getBuilder(), config_text); + ASSERT_FALSE(result); +}