On 03/16/2016 02:04 PM, Michal Židek wrote:
Hi,
these three patches add infrastructure for this libini feature
https://fedorahosted.org/sssd/wiki/DesignDocs/libini-config-file-checks
I did not add the patch for ini_allowed_sections validator. Will
add that validator when these patches are in master.
I also did not bump the version number for now. Will do this
before release.
Any comments are welcome.
Thanks
Michal
Found some small issues in the patches so I
updated them and added ini_allowed_sections
validator to the patches.
Michal
>From 675fe7aef88e2cfe4909c9c0b34e5f498233987b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Michal=20=C5=BDidek?= <[email protected]>
Date: Wed, 3 Feb 2016 18:51:49 +0100
Subject: [PATCH 1/4] ini: Add infrastructure for validators
Ticket:
https://fedorahosted.org/sssd/ticket/133
Add infrastructure for implementing
internal and extrenal config file
validators.
---
ini/ini_config_priv.h | 13 +++
ini/ini_configobj.c | 306 ++++++++++++++++++++++++++++++++++++++++++++++++++
ini/ini_configobj.h | 173 ++++++++++++++++++++++++++++
ini/libini_config.sym | 11 ++
4 files changed, 503 insertions(+)
diff --git a/ini/ini_config_priv.h b/ini/ini_config_priv.h
index 38813c9..a03eda5 100644
--- a/ini/ini_config_priv.h
+++ b/ini/ini_config_priv.h
@@ -109,5 +109,18 @@ int access_check_int(struct stat *file_stats,
mode_t mode,
mode_t mask);
+struct ini_errmsg;
+
+struct ini_errobj {
+ size_t count;
+ struct ini_errmsg *first_msg;
+ struct ini_errmsg *last_msg;
+ struct ini_errmsg *cur_msg;
+};
+
+struct ini_errmsg {
+ char *str;
+ struct ini_errmsg *next;
+};
#endif
diff --git a/ini/ini_configobj.c b/ini/ini_configobj.c
index 8ae5ea7..b2c5000 100644
--- a/ini/ini_configobj.c
+++ b/ini/ini_configobj.c
@@ -25,6 +25,7 @@
#include <string.h>
#include <stdint.h>
#include <stdlib.h>
+#include <stdarg.h>
/* For error text */
#include <libintl.h>
#define _(String) gettext (String)
@@ -1039,3 +1040,308 @@ int ini_config_get_errors(struct ini_cfgobj *cfg_ctx,
TRACE_FLOW_EXIT();
return error;
}
+
+int ini_read_rules_from_file(const char *filename,
+ struct ini_cfgobj **_rules_obj)
+{
+ int ret;
+ struct ini_cfgfile *cfgfile = NULL;
+
+ if (_rules_obj == NULL) {
+ return EINVAL;
+ }
+
+ ret = ini_config_create(_rules_obj);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ ret = ini_config_file_open(filename, 0, &cfgfile);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = ini_config_parse(cfgfile, 0 , INI_MV1S_ALLOW, 0, *_rules_obj);
+ if (ret != EOK) {
+ goto done;
+ }
+
+done:
+ if (ret != EOK) {
+ ini_config_destroy(*_rules_obj);
+ }
+
+ ini_config_file_destroy(cfgfile);
+ return ret;
+}
+
+/* This is used for testing only */
+static int ini_dummy_noerror(const char *rule_name,
+ struct ini_cfgobj *rules_obj,
+ struct ini_cfgobj *config_obj,
+ struct ini_errobj *errobj)
+{
+ return 0;
+}
+
+/* This is used for testing only */
+static int ini_dummy_error(const char *rule_name,
+ struct ini_cfgobj *rules_obj,
+ struct ini_cfgobj *config_obj,
+ struct ini_errobj *errobj)
+{
+ return ini_errobj_add_msg(errobj, "Error");
+}
+
+
+static ini_validator_func *
+get_validator(char *validator_name,
+ struct ini_validator *validators,
+ int num_validators)
+{
+ /* First we check all internal validators */
+ if (strcmp(validator_name, "ini_dummy_noerror") == 0) {
+ return ini_dummy_noerror;
+ } else if (strcmp(validator_name, "ini_dummy_error") == 0) {
+ return ini_dummy_error;
+ }
+
+ /* Now check the custom validators */
+ if (validators == NULL) {
+ return NULL;
+ }
+
+ for (int i = 0; i < num_validators; i++) {
+ if (strcmp(validator_name, validators[i].name) == 0) {
+ return validators[i].func;
+ }
+ }
+
+ return NULL;
+}
+
+int ini_rules_check(struct ini_cfgobj *rules_obj,
+ struct ini_cfgobj *config_obj,
+ struct ini_validator *extra_validators,
+ int num_extra_validators,
+ struct ini_errobj *errobj)
+{
+ char **sections;
+ int ret;
+ int num_sections;
+ char *vname;
+ ini_validator_func *vfunc;
+ struct value_obj *vo = NULL;
+ struct ini_errobj *localerr = NULL;
+
+ /* Get all sections from the rules object */
+ sections = ini_get_section_list(rules_obj, &num_sections, &ret);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ /* Now iterate through all the sections. If the section
+ * name begins with a prefix "rule/", then it is a rule
+ * name. */
+ for (int i = 0; i < num_sections; i++) {
+ if (!strncmp(sections[i], "rule/", strlen("rule/"))) {
+ ret = ini_get_config_valueobj(sections[i],
+ "validator",
+ rules_obj,
+ INI_GET_FIRST_VALUE,
+ &vo);
+ if (ret != 0) {
+ /* Failed to get value object. This should not
+ * happen. */
+ continue;
+ }
+
+ if (vo == NULL) {
+ ret = ini_errobj_add_msg(errobj,
+ "Rule '%s' has no validator.",
+ sections[i]);
+ if (ret != EOK) {
+ return ret;
+ }
+ /* Skip problematic rule */
+ continue;
+ }
+
+ vname = ini_get_string_config_value(vo, NULL);
+ vfunc = get_validator(vname, extra_validators,
+ num_extra_validators);
+ if (vfunc == NULL) {
+ ret = ini_errobj_add_msg(errobj,
+ "Rule '%s' uses unknown "
+ "validator '%s'.",
+ sections[i], vname);
+ if (ret != EOK) {
+ goto done;
+ }
+ /* Skip problematic rule */
+ free(vname);
+ continue;
+ }
+ free(vname);
+
+ /* Do not pass global errobj to validators, they
+ * could corrupt it. Create local one for each
+ * validator. */
+ ret = ini_errobj_create(&localerr);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = vfunc(sections[i], rules_obj, config_obj, localerr);
+ if (ret != 0) {
+ /* Just report the error and continue normally,
+ * maybe there are some errors in localerr */
+ ret = ini_errobj_add_msg(errobj,
+ "Rule '%s' returned error code '%d'",
+ sections[i], ret);
+ if (ret != EOK) {
+ goto done;
+ }
+ }
+
+ /* Bad validator could destroy the localerr, check
+ * for NULL */
+ if (localerr == NULL) {
+ continue;
+ }
+
+ ini_errobj_reset(localerr);
+ while (!ini_errobj_no_more_msgs(localerr)) {
+ ret = ini_errobj_add_msg(errobj,
+ "[%s]: %s",
+ sections[i],
+ ini_errobj_get_msg(localerr));
+ if (ret != EOK) {
+ goto done;
+ }
+ ini_errobj_next(localerr);
+ }
+
+ ini_errobj_destroy(&localerr);
+ }
+ }
+
+ ret = EOK;
+done:
+ ini_free_section_list(sections);
+ ini_errobj_destroy(&localerr);
+ return ret;
+}
+
+/* This is just convenience function, so that
+ * we manipulate with ini_rules_* functions. */
+void ini_rules_destroy(struct ini_cfgobj *rules)
+{
+ ini_config_destroy(rules);
+}
+
+int ini_errobj_create(struct ini_errobj **_errobj)
+{
+ struct ini_errobj *new_errobj = NULL;
+
+ if (_errobj == NULL) {
+ return EINVAL;
+ }
+
+ new_errobj = calloc(1, sizeof(struct ini_errobj));
+ if (new_errobj == NULL) {
+ return ENOMEM;
+ }
+
+ *_errobj = new_errobj;
+ return EOK;
+}
+
+void ini_errobj_destroy(struct ini_errobj **errobj)
+{
+ struct ini_errmsg *to_remove;
+
+ if (errobj == NULL || *errobj == NULL) {
+ return;
+ }
+
+ while ((*errobj)->first_msg) {
+ to_remove = (*errobj)->first_msg;
+ (*errobj)->first_msg = (*errobj)->first_msg->next;
+ free(to_remove->str);
+ free(to_remove);
+ }
+
+ free(*errobj);
+ *errobj = NULL;
+}
+
+int ini_errobj_add_msg(struct ini_errobj *errobj, const char *format, ...)
+{
+ int ret;
+ va_list args;
+ struct ini_errmsg *new;
+
+ new = calloc(1, sizeof(struct ini_errmsg));
+ if (new == NULL) {
+ return ENOMEM;
+ }
+
+ va_start(args, format);
+ ret = vasprintf(&new->str, format, args);
+ va_end(args);
+ if (ret == -1) {
+ free(new);
+ return ENOMEM;
+ }
+
+ if (errobj->count == 0) {
+ /* First addition to the list, all pointers are NULL */
+ errobj->first_msg = new;
+ errobj->last_msg = new;
+ errobj->cur_msg = new;
+ errobj->count++;
+ } else {
+ errobj->last_msg->next = new;
+ errobj->last_msg = errobj->last_msg->next;
+ errobj->count++;
+ }
+
+ return EOK;
+}
+
+void ini_errobj_reset(struct ini_errobj *errobj)
+{
+ errobj->cur_msg = errobj->first_msg;
+}
+
+const char *ini_errobj_get_msg(struct ini_errobj *errobj)
+{
+ if (errobj->cur_msg != NULL) {
+ return errobj->cur_msg->str;
+ }
+
+ /* Should this be allowed? */
+ return NULL;
+}
+
+void ini_errobj_next(struct ini_errobj *errobj)
+{
+ if (errobj->cur_msg != NULL) {
+ errobj->cur_msg = errobj->cur_msg->next;
+ }
+
+ /* If we can not move next, just return */
+ return;
+}
+
+int ini_errobj_no_more_msgs(struct ini_errobj *errobj)
+{
+ return errobj->cur_msg == NULL;
+}
+
+size_t ini_errobj_count(struct ini_errobj *errobj)
+{
+ return errobj->count;
+}
+
diff --git a/ini/ini_configobj.h b/ini/ini_configobj.h
index 6f2d692..0b82d44 100644
--- a/ini/ini_configobj.h
+++ b/ini/ini_configobj.h
@@ -2046,6 +2046,179 @@ void ini_free_long_config_array(long *array);
*/
void ini_free_double_config_array(double *array);
+/** @brief Structure that holds error messages
+ * generated by validators.
+ */
+struct ini_errobj;
+
+/**
+ * @brief Create structure to hold error messages.
+ *
+ * This function initiates structure that can be used to
+ * hold error messages from generators. To add messages to
+ * the structure use \ref ini_errobj_add_msg.
+ *
+ * @param[out] errobj container for errors.
+ *
+ * @return Zero on success, nonzero value in case of error.
+ */
+int ini_errobj_create(struct ini_errobj **_errobj);
+
+/**
+ * @brief Free structure that holds error messages.
+ *
+ * This function is used to free structure
+ * previously created by \ref ini_errobj_create.
+ *
+ * @param[in] errobj container for errors.
+ */
+void ini_errobj_destroy(struct ini_errobj **errobj);
+
+/**
+ * @brief Add new printf formated message to errobj.
+ *
+ * This function initiates structure that can be used to
+ * hold error messages from generators. To add messages to
+ * the structure use \ref ini_errobj_add_msg.
+ *
+ * @param[in] errobj container for errors previously
+ * created by \ref ini_errobj_create.
+ * @param[in] format printf format string
+ *
+ * @return Zero on success, nonzero value in case of error.
+ */
+int ini_errobj_add_msg(struct ini_errobj *errobj, const char *format, ...);
+
+/**
+ * @brief Reset iterator in errobj.
+ *
+ * After calling this function, the iterator in errobj
+ * will point to the first error message. Use this if
+ * you need to accesss the list multiple times in a loop.
+ *
+ * @param[in] errobj container for errors previously
+ * created by \ref ini_errobj_create.
+ */
+void ini_errobj_reset(struct ini_errobj *errobj);
+
+/**
+ * @brief Get pointer to current message in errobj.
+ *
+ * This function returns pointer to current message
+ * pointed by the internal iterator. The returned string can
+ * not be changed and will point to valid data only
+ * until \ref ini_errobj_destroy is called.
+ *
+ * @param[in] errobj container for errors previously
+ * created by \ref ini_errobj_create.
+ * @return String inside the errobj structure. String
+ * is valid until errobj is destroyed.
+ */
+const char *ini_errobj_get_msg(struct ini_errobj *errobj);
+
+/**
+ * @brief Move to the next message in errobj.
+ *
+ * This function moves the internal iterator of errobj
+ * to the next message in list.
+ *
+ * @param[in] errobj container for errors previously
+ * created by \ref ini_errobj_create.
+ */
+void ini_errobj_next(struct ini_errobj *errobj);
+
+/**
+ * @brief Check if errobj has more messages.
+ *
+ * This function returns true if errobj's internal iterator
+ * reached end of list and no longer points to a message
+ *
+ * @param[in] errobj container for errors previously
+ * created by \ref ini_errobj_create.
+ * @return True if internal iterator reached end of list.
+ */
+int ini_errobj_no_more_msgs(struct ini_errobj *errobj);
+
+/**
+ * @brief Return number of messages in errobj
+ *
+ * This function returns number of messages inside errobj
+ *
+ * @param[in] errobj container for errors previously
+ * created by \ref ini_errobj_create.
+ * @return Number of messages stored in errobj.
+ */
+size_t ini_errobj_count(struct ini_errobj *errobj);
+
+typedef int (ini_validator_func)(const char *rule_name,
+ struct ini_cfgobj *rules_obj,
+ struct ini_cfgobj *config_obj,
+ struct ini_errobj *errobj);
+
+/** @brief Structure used to define application specific
+ * (external to libini) validator
+ */
+struct ini_validator {
+ const char *name;
+ ini_validator_func *func;
+};
+
+/**
+ * @brief Read rules from INI file
+ *
+ * This function is used to read rules from INI file
+ * and store them in config object. This special
+ * config object is passed to \ref ini_rules_check
+ * together with config object representing the
+ * configuration that will be checked.
+ *
+ * @param[in] filename Name of file with rules
+ * @param[out] _rules_obj config object representing the rules
+ * @return Zero on success. Non zero value on error.
+ */
+int ini_read_rules_from_file(const char *filename,
+ struct ini_cfgobj **_rules_obj);
+
+/**
+ * @brief Check configuration file using rules
+ *
+ * This function is used to check if configuration
+ * file applies to rules previously loaded by
+ * \ref ini_read_rules_from_file. Any errors
+ * detected in the configuration are stored in the
+ * errobj structure. Error code returned by this
+ * function indicates some internal error with
+ * validators or memory allocation error (not
+ * rule violation).
+ *
+ * @param[in] rules_obj config object representing the rules
+ * @param[in] config_obj config object representing the
+ * configuration
+ * @param[in] extra_validators Array of extrenal validators. Can be
+ * NULL if no external validators are
+ * used.
+ * @param[in] num_extra_validators Number of external validators in
+ * extra_validators array.
+ *
+ * @param[in] errobj errobj to store generated errors
+ * from validators.
+ *
+ * @return Zero on success. Non zero value on error.
+ */
+int ini_rules_check(struct ini_cfgobj *rules_obj,
+ struct ini_cfgobj *config_obj,
+ struct ini_validator *extra_validators,
+ int num_extra_validators,
+ struct ini_errobj *errobj);
+
+/**
+ * @brief Free the rules
+ *
+ * This function is just wrapper around ini_config_destroy
+ */
+void ini_rules_destroy(struct ini_cfgobj *ini_config);
+
+
/**
* @}
*/
diff --git a/ini/libini_config.sym b/ini/libini_config.sym
index 8c34e04..2359c6b 100644
--- a/ini/libini_config.sym
+++ b/ini/libini_config.sym
@@ -90,6 +90,17 @@ global:
ini_free_string_config_array;
ini_free_long_config_array;
ini_free_double_config_array;
+ ini_errobj_create;
+ ini_errobj_destroy;
+ ini_errobj_add_msg;
+ ini_errobj_reset;
+ ini_errobj_get_msg;
+ ini_errobj_count;
+ ini_errobj_next;
+ ini_errobj_no_more_msgs;
+ ini_read_rules_from_file;
+ ini_rules_check;
+ ini_rules_destroy;
/* ini_valueobj.h */
value_create_from_refarray;
--
2.5.0
>From 71c2290fe1a18416118cb004036eada3cd744883 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Michal=20=C5=BDidek?= <[email protected]>
Date: Tue, 8 Mar 2016 17:15:54 +0100
Subject: [PATCH 2/4] ini: Add internal validator ini_allowed_options
Ticket:
https://fedorahosted.org/sssd/ticket/133
This validator allows to specify a per section
list of known options. Error message is generated
if unknown option is found.
---
ini/ini_configobj.c | 181 ++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 181 insertions(+)
diff --git a/ini/ini_configobj.c b/ini/ini_configobj.c
index b2c5000..95a3baa 100644
--- a/ini/ini_configobj.c
+++ b/ini/ini_configobj.c
@@ -20,6 +20,8 @@
*/
#include "config.h"
+#include <sys/types.h>
+#include <regex.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
@@ -1094,6 +1096,183 @@ static int ini_dummy_error(const char *rule_name,
}
+static int check_if_allowed(char *section, char *attr, char **allowed, int num_allowed,
+ struct ini_errobj *errobj)
+{
+ int is_allowed = 0;
+ int ret;
+
+ for (int i = 0; i < num_allowed; i++) {
+ if (strcmp(attr, allowed[i]) == 0) {
+ is_allowed = 1;
+ break;
+ }
+ }
+
+ if (!is_allowed) {
+ ret = ini_errobj_add_msg(errobj, "Attribute '%s' is not allowed in "
+ "section '%s'. Check for typos.",
+ attr, section);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ini_allowed_options(const char *rule_name,
+ struct ini_cfgobj *rules_obj,
+ struct ini_cfgobj *config_obj,
+ struct ini_errobj *errobj)
+{
+ struct value_obj *vo = NULL;
+ int ret;
+ char *section_regex;
+ int num_sections;
+ char **sections;
+ char **attributes;
+ int num_attributes;
+ int num_opts = 0;
+ regex_t preg;
+ size_t buf_size;
+ char *err_str = NULL;
+ int reg_err;
+ char **allowed = NULL;
+
+ /* Get section regex */
+ ret = ini_get_config_valueobj(rule_name,
+ "section_re",
+ rules_obj,
+ INI_GET_FIRST_VALUE,
+ &vo);
+ if (ret != 0) {
+ return ret;
+ }
+
+ if (vo == NULL) {
+ ret = ini_errobj_add_msg(errobj, "Validator misses 'section_re' parameter");
+ if (ret) {
+ return ret;
+ }
+ return EINVAL;
+ }
+
+ section_regex = ini_get_string_config_value(vo, NULL);
+ if (section_regex == NULL || section_regex[0] == '\0') {
+ ret = ini_errobj_add_msg(errobj, "Validator misses 'section_re' parameter");
+ if (ret) {
+ return ret;
+ }
+
+ free(section_regex);
+ return EINVAL;
+ }
+
+ /* compile the regular expression */
+ reg_err = regcomp(&preg, section_regex, REG_NOSUB);
+ if (reg_err) {
+ buf_size = regerror(reg_err, &preg, NULL, 0);
+ err_str = malloc(buf_size);
+ if (err_str == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ regerror(reg_err, &preg, err_str, buf_size);
+ ret = ini_errobj_add_msg(errobj, "Validator misses 'section_re' "
+ "parameter");
+ ret = ret ? ret : EINVAL;
+ goto done;
+ }
+
+ /* Get all sections from config_obj */
+ sections = ini_get_section_list(config_obj, &num_sections, &ret);
+ if (ret != EOK) {
+ goto done;;
+ }
+
+ /* Get number of 'option' attributes in this rule
+ * and create an array long enough to store them all */
+ attributes = ini_get_attribute_list(rules_obj,
+ rule_name,
+ &num_attributes,
+ NULL);
+ if (attributes == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ for (int i = 0; i < num_attributes; i++) {
+ if (strcmp("option", attributes[i]) == 0) {
+ num_opts++;
+ }
+ }
+
+ ini_free_attribute_list(attributes);
+ attributes = NULL;
+
+ allowed = calloc(num_opts, sizeof(char *));
+ if (allowed == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ for (int i = 0; i < num_opts; i++) {
+ ret = ini_get_config_valueobj(rule_name,
+ "option",
+ rules_obj,
+ INI_GET_NEXT_VALUE,
+ &vo);
+ if (ret) {
+ goto done;
+ }
+
+ allowed[i] = ini_get_string_config_value(vo, &ret);
+ if (ret) {
+ goto done;
+ }
+ }
+
+ for (int i = 0; i < num_sections; i++) {
+ if (regexec(&preg, sections[i], 0, NULL, 0) == 0) {
+ /* Regex matched section */
+ /* Get options from this section */
+ attributes = ini_get_attribute_list(config_obj,
+ sections[i],
+ &num_attributes,
+ NULL);
+ if (attributes == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ for (int a = 0; a < num_attributes; a++) {
+ ret = check_if_allowed(sections[i], attributes[a], allowed,
+ num_opts, errobj);
+ if (ret != 0) {
+ goto done;
+ }
+ }
+ ini_free_attribute_list(attributes);
+ attributes = NULL;
+ }
+ }
+
+ ret = 0;
+done:
+ for (int i = 0; i < num_opts; i++) {
+ free(allowed[i]);
+ }
+ free(allowed);
+ ini_free_section_list(sections);
+ free(section_regex);
+ ini_free_attribute_list(attributes);
+ regfree(&preg);
+ free(err_str);
+ return ret;
+}
+
+
+
static ini_validator_func *
get_validator(char *validator_name,
struct ini_validator *validators,
@@ -1104,6 +1283,8 @@ get_validator(char *validator_name,
return ini_dummy_noerror;
} else if (strcmp(validator_name, "ini_dummy_error") == 0) {
return ini_dummy_error;
+ } else if (strcmp(validator_name, "ini_allowed_options") == 0) {
+ return ini_allowed_options;
}
/* Now check the custom validators */
--
2.5.0
>From 2d890f004497f00362de8d82f86249bc7b2b2b2a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Michal=20=C5=BDidek?= <[email protected]>
Date: Tue, 15 Mar 2016 17:35:05 +0100
Subject: [PATCH 3/4] tests: Tests for rules/validators infrastructure
Ticket:
https://fedorahosted.org/sssd/ticket/133
---
Makefile.am | 9 +-
ini/ini_validators_ut_check.c | 551 ++++++++++++++++++++++++++++++++++++++++++
2 files changed, 558 insertions(+), 2 deletions(-)
create mode 100644 ini/ini_validators_ut_check.c
diff --git a/Makefile.am b/Makefile.am
index 5b1da33..1038860 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -69,8 +69,8 @@ libpath_utils_la_LDFLAGS = \
-Wl,--version-script=$(top_srcdir)/path_utils/libpath_utils.sym
if HAVE_CHECK
- check_PROGRAMS += path_utils_ut ini_configmod_ut_check
- TESTS += path_utils_ut ini_configmod_ut_check
+ check_PROGRAMS += path_utils_ut ini_configmod_ut_check ini_validators_ut_check
+ TESTS += path_utils_ut ini_configmod_ut_check ini_validators_ut_check
endif
path_utils_ut_SOURCES = path_utils/path_utils_ut.c
@@ -348,6 +348,11 @@ ini_configmod_ut_check_LDADD = libini_config.la libcollection.la \
libref_array.la \
$(CHECK_LIBS)
+ini_validators_ut_check_SOURCES = ini/ini_validators_ut_check.c
+ini_validators_ut_check_CFLAGS = $(AM_CFLAGS) $(CHECK_CFLAGS)
+ini_validators_ut_check_LDADD = libini_config.la $(CHECK_LIBS)
+
+
ini_save_ut_SOURCES = ini/ini_save_ut.c
ini_save_ut_LDADD = libini_config.la libcollection.la \
libbasicobjects.la libpath_utils.la libref_array.la
diff --git a/ini/ini_validators_ut_check.c b/ini/ini_validators_ut_check.c
new file mode 100644
index 0000000..cf208ed
--- /dev/null
+++ b/ini/ini_validators_ut_check.c
@@ -0,0 +1,551 @@
+/*
+ INI LIBRARY
+
+ Unit test for the configuration file validators API.
+
+ Copyright (C) Michal Zidek <[email protected]> 2016
+
+ INI Library is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ INI Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with INI Library. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "config.h"
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <check.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+/* #define TRACE_LEVEL 7 */
+#define TRACE_HOME
+#include "trace.h"
+#include "ini_configobj.h"
+#include "ini_config_priv.h"
+
+#define TEST_DIR_PATH ""
+#define TEST_RULES_FILE TEST_DIR_PATH"test_rules.ini"
+
+static void create_rules_from_str(const char *rules,
+ struct ini_cfgobj **_rules_obj)
+{
+ FILE *file;
+ size_t written;
+ int ret;
+
+ /* We want to test actual reading from file using
+ * ini_read_rules_from_file, so we create the file here */
+ file = fopen(TEST_RULES_FILE, "w");
+ fail_if(file == NULL, "fopen() failed: %s", strerror(errno));
+ written = fwrite(rules, 1, strlen(rules), file);
+ fail_unless(written == strlen(rules));
+
+ /* allow reading */
+ ret = chmod(TEST_RULES_FILE, 0664);
+ fail_unless(ret == 0, "chmod() failed: %s", strerror(errno));
+
+ fclose(file);
+
+ ret = ini_read_rules_from_file(TEST_RULES_FILE, _rules_obj);
+ fail_unless(ret == 0, "read_rules_from_file() failed: %s", strerror(ret));
+}
+
+static struct ini_cfgobj *get_ini_config_from_str(char input_data[],
+ size_t input_data_len)
+{
+ struct ini_cfgobj *in_cfg;
+ struct ini_cfgfile *file_ctx;
+ int ret;
+
+ ret = ini_config_create(&in_cfg);
+ fail_unless(ret == EOK, "Failed to create config. Error %d.\n", ret);
+
+ ret = ini_config_file_from_mem(input_data, input_data_len, &file_ctx);
+ fail_unless(ret == EOK, "Failed to load config. Error %d.\n", ret);
+
+ ret = ini_config_parse(file_ctx, INI_STOP_ON_NONE, INI_MV1S_ALLOW, 0,
+ in_cfg);
+ fail_unless(ret == EOK, "Failed to parse config. Error %d.\n", ret);
+
+ ini_config_file_destroy(file_ctx);
+
+ return in_cfg;
+}
+
+START_TEST(test_ini_errobj)
+{
+ struct ini_errobj *errobj;
+ int ret;
+ const char TEST_MSG1[] = "Test message one.";
+ const char TEST_MSG2[] = "Test message two.";
+ const char TEST_MSG3[] = "Test message three.";
+
+ ret = ini_errobj_create(&errobj);
+ fail_unless(ret == 0, "ini_errobj_create() failed: %s", strerror(ret));
+
+ /* We just created the errobj, it should be empty */
+ fail_unless(ini_errobj_no_more_msgs(errobj));
+
+ /* Now add three messages, after adding each message,
+ * check if the errobj has correct content. */
+ ret = ini_errobj_add_msg(errobj, TEST_MSG1);
+ fail_if(ret != 0, "ini_errobj_add_msg() failed: %s", strerror(ret));
+ fail_if(ini_errobj_no_more_msgs(errobj));
+ ret = strcmp(TEST_MSG1, ini_errobj_get_msg(errobj));
+ fail_if(ret != 0, "TEST_MSG1 was not found.");
+ ini_errobj_next(errobj);
+ fail_unless(ini_errobj_no_more_msgs(errobj));
+
+ ret = ini_errobj_add_msg(errobj, TEST_MSG2);
+ fail_if(ret != 0, "ini_errobj_add_msg() failed: %s", strerror(ret));
+ ini_errobj_reset(errobj); /* strart from first message */
+ fail_if(ini_errobj_no_more_msgs(errobj));
+ ret = strcmp(TEST_MSG1, ini_errobj_get_msg(errobj));
+ fail_if(ret != 0, "TEST_MSG1 was not found.");
+ ini_errobj_next(errobj);
+ fail_if(ini_errobj_no_more_msgs(errobj));
+ ret = strcmp(TEST_MSG2, ini_errobj_get_msg(errobj));
+ fail_if(ret != 0, "TEST_MSG2 was not found.");
+ ini_errobj_next(errobj);
+ fail_unless(ini_errobj_no_more_msgs(errobj));
+
+ ret = ini_errobj_add_msg(errobj, TEST_MSG3);
+ fail_if(ret != 0, "ini_errobj_add_msg() failed: %s", strerror(ret));
+ ini_errobj_reset(errobj); /* strart from first message */
+ fail_if(ini_errobj_no_more_msgs(errobj));
+ ret = strcmp(TEST_MSG1, ini_errobj_get_msg(errobj));
+ fail_if(ret != 0, "TEST_MSG1 was not found.");
+ ini_errobj_next(errobj);
+ fail_if(ini_errobj_no_more_msgs(errobj));
+ ret = strcmp(TEST_MSG2, ini_errobj_get_msg(errobj));
+ fail_if(ret != 0, "TEST_MSG2 was not found.");
+ ini_errobj_next(errobj);
+ fail_if(ini_errobj_no_more_msgs(errobj));
+ ret = strcmp(TEST_MSG3, ini_errobj_get_msg(errobj));
+ fail_if(ret != 0, "TEST_MSG3 was not found.");
+ ini_errobj_next(errobj);
+ fail_unless(ini_errobj_no_more_msgs(errobj));
+
+ ini_errobj_destroy(&errobj);
+}
+END_TEST
+
+
+START_TEST(test_ini_noerror)
+{
+ struct ini_cfgobj *rules_obj;
+ struct ini_cfgobj *cfg_obj;
+ struct ini_errobj *errobj;
+ int ret;
+
+ char input_rules[] =
+ "[rule/always_succeed]\n"
+ "validator = ini_dummy_noerror\n";
+
+ char input_cfg[] =
+ "[section]\n"
+ "# Content of this file should not matter\n";
+
+ create_rules_from_str(input_rules, &rules_obj);
+ cfg_obj = get_ini_config_from_str(input_cfg, sizeof(input_cfg));
+
+ ret = ini_errobj_create(&errobj);
+ fail_unless(ret == 0, "ini_errobj_create() failed: %s", strerror(ret));
+
+ ret = ini_rules_check(rules_obj, cfg_obj, NULL, 0, errobj);
+ fail_unless(ret == 0, "ini_rules_check() failed: %s", strerror(ret));
+ fail_unless(ini_errobj_no_more_msgs(errobj));
+
+ ini_errobj_destroy(&errobj);
+ ini_config_destroy(cfg_obj);
+ ini_rules_destroy(rules_obj);
+}
+END_TEST
+
+START_TEST(test_ini_error)
+{
+ struct ini_cfgobj *rules_obj;
+ struct ini_cfgobj *cfg_obj;
+ struct ini_errobj *errobj;
+ int ret;
+ const char *errmsg;
+
+ char input_rules[] =
+ "[rule/generate_error]\n"
+ "validator = ini_dummy_error\n";
+
+ char input_cfg[] =
+ "[section]\n"
+ "# Content of this file should not matter\n";
+
+ create_rules_from_str(input_rules, &rules_obj);
+ cfg_obj = get_ini_config_from_str(input_cfg, sizeof(input_cfg));
+
+ ret = ini_errobj_create(&errobj);
+ fail_unless(ret == 0, "ini_errobj_create() failed: %s", strerror(ret));
+
+ ret = ini_rules_check(rules_obj, cfg_obj, NULL, 0, errobj);
+ fail_unless(ret == 0, "ini_rules_check() failed: %s", strerror(ret));
+
+ /* Should generate exactly one error */
+ fail_if(ini_errobj_no_more_msgs(errobj));
+ errmsg = ini_errobj_get_msg(errobj);
+ ret = strcmp(errmsg, "[rule/generate_error]: Error");
+ fail_unless(ret == 0, "Got msg: %s", errmsg);
+ ini_errobj_next(errobj);
+ fail_unless(ini_errobj_no_more_msgs(errobj));
+
+ ini_errobj_destroy(&errobj);
+ ini_config_destroy(cfg_obj);
+ ini_rules_destroy(rules_obj);
+}
+END_TEST
+
+START_TEST(test_unknown_validator)
+{
+ struct ini_cfgobj *rules_obj;
+ struct ini_cfgobj *cfg_obj;
+ struct ini_errobj *errobj;
+ int ret;
+
+ char input_rules[] =
+ "[rule/always_succeed]\n"
+ "validator = nonexistent_validator\n";
+
+ char input_cfg[] =
+ "[section]\n"
+ "# Content of this file should not matter\n";
+
+ create_rules_from_str(input_rules, &rules_obj);
+ cfg_obj = get_ini_config_from_str(input_cfg, sizeof(input_cfg));
+
+ ret = ini_errobj_create(&errobj);
+ fail_unless(ret == 0, "ini_errobj_create() failed: %s", strerror(ret));
+
+ ret = ini_rules_check(rules_obj, cfg_obj, NULL, 0, errobj);
+ fail_unless(ret == 0, "ini_rules_check() failed: %s", strerror(ret));
+
+ /* Should generate exactly one error */
+ fail_if(ini_errobj_no_more_msgs(errobj));
+ ini_errobj_next(errobj);
+ fail_unless(ini_errobj_no_more_msgs(errobj));
+
+ ini_errobj_destroy(&errobj);
+ ini_config_destroy(cfg_obj);
+ ini_rules_destroy(rules_obj);
+}
+END_TEST
+
+static int custom_noerror(const char *rule_name,
+ struct ini_cfgobj *rules_obj,
+ struct ini_cfgobj *config_obj,
+ struct ini_errobj *errobj)
+{
+ return 0;
+}
+
+static int custom_error(const char *rule_name,
+ struct ini_cfgobj *rules_obj,
+ struct ini_cfgobj *config_obj,
+ struct ini_errobj *errobj)
+{
+ return ini_errobj_add_msg(errobj, "Error");
+}
+
+START_TEST(test_custom_noerror)
+{
+ struct ini_cfgobj *rules_obj;
+ struct ini_cfgobj *cfg_obj;
+ struct ini_errobj *errobj;
+ int ret;
+ struct ini_validator noerror = {"custom_noerror", custom_noerror};
+
+ char input_rules[] =
+ "[rule/custom_succeed]\n"
+ "validator = custom_noerror\n";
+
+ char input_cfg[] =
+ "[section]\n"
+ "# Content of this file should not matter\n";
+
+ create_rules_from_str(input_rules, &rules_obj);
+ cfg_obj = get_ini_config_from_str(input_cfg, sizeof(input_cfg));
+
+ ret = ini_errobj_create(&errobj);
+ fail_unless(ret == 0, "ini_errobj_create() failed: %s", strerror(ret));
+
+ /* Pass the custom validator to ini_rules_check() */
+ ret = ini_rules_check(rules_obj, cfg_obj, &noerror, 1, errobj);
+ fail_unless(ret == 0, "ini_rules_check() failed: %s", strerror(ret));
+
+ /* Should generate no errors */
+ fail_unless(ini_errobj_no_more_msgs(errobj));
+
+ ini_errobj_destroy(&errobj);
+ ini_config_destroy(cfg_obj);
+ ini_rules_destroy(rules_obj);
+}
+END_TEST
+
+START_TEST(test_custom_error)
+{
+ struct ini_cfgobj *rules_obj;
+ struct ini_cfgobj *cfg_obj;
+ struct ini_errobj *errobj;
+ int ret;
+ struct ini_validator error = {"custom_error", custom_error};
+ const char *errmsg;
+
+ char input_rules[] =
+ "[rule/custom_error]\n"
+ "validator = custom_error\n";
+
+ char input_cfg[] =
+ "[section]\n"
+ "# Content of this file should not matter\n";
+
+ create_rules_from_str(input_rules, &rules_obj);
+ cfg_obj = get_ini_config_from_str(input_cfg, sizeof(input_cfg));
+
+ ret = ini_errobj_create(&errobj);
+ fail_unless(ret == 0, "ini_errobj_create() failed: %s", strerror(ret));
+
+ /* Pass the custom validator to ini_rules_check() */
+ ret = ini_rules_check(rules_obj, cfg_obj, &error, 1, errobj);
+ fail_unless(ret == 0, "ini_rules_check() failed: %s", strerror(ret));
+
+ /* Should generate one error */
+ fail_if(ini_errobj_no_more_msgs(errobj));
+ errmsg = ini_errobj_get_msg(errobj);
+ ret = strcmp(errmsg, "[rule/custom_error]: Error");
+ fail_unless(ret == 0, "Got msg: %s", errmsg);
+ ini_errobj_next(errobj);
+ fail_unless(ini_errobj_no_more_msgs(errobj));
+
+ ini_errobj_destroy(&errobj);
+ ini_config_destroy(cfg_obj);
+ ini_rules_destroy(rules_obj);
+}
+END_TEST
+
+START_TEST(test_ini_allowed_options_ok)
+{
+ struct ini_cfgobj *rules_obj;
+ struct ini_cfgobj *cfg_obj;
+ struct ini_errobj *errobj;
+ int ret;
+
+ /* Only bar and baz are allowed for foo section */
+ char input_rules[] =
+ "[rule/options_for_foo]\n"
+ "validator = ini_allowed_options\n"
+ "section_re = ^foo$\n"
+ "option = bar\n"
+ "option = baz\n";
+
+ /* Should check only foo section, other sections are
+ * irrelevant and can contain any option */
+ char input_cfg[] =
+ "[foo]\n"
+ "bar = 0\n"
+ "baz = 0\n"
+ "[oof]\n"
+ "opt1 = 1\n";
+
+ create_rules_from_str(input_rules, &rules_obj);
+ cfg_obj = get_ini_config_from_str(input_cfg, sizeof(input_cfg));
+
+ ret = ini_errobj_create(&errobj);
+ fail_unless(ret == 0, "ini_errobj_create() failed: %s", strerror(ret));
+
+ ret = ini_rules_check(rules_obj, cfg_obj, NULL, 0, errobj);
+ fail_unless(ret == 0, "ini_rules_check() failed: %s", strerror(ret));
+
+ /* Should generate no errors */
+ fail_unless(ini_errobj_no_more_msgs(errobj));
+
+ ini_errobj_destroy(&errobj);
+ ini_config_destroy(cfg_obj);
+ ini_rules_destroy(rules_obj);
+}
+END_TEST
+
+START_TEST(test_ini_allowed_options_no_section)
+{
+ struct ini_cfgobj *rules_obj;
+ struct ini_cfgobj *cfg_obj;
+ struct ini_errobj *errobj;
+ int ret;
+ size_t num_err;
+
+ /* Ommit section_re to generate error */
+ char input_rules[] =
+ "[rule/options_for_foo]\n"
+ "validator = ini_allowed_options\n"
+ /* "section_re = ^foo$\n" */
+ "option = bar\n"
+ "option = baz\n";
+
+ /* Make 4 typos */
+ char input_cfg[] =
+ "[foo]\n"
+ "bar = 0\n"
+ "baz = 0\n";
+
+ create_rules_from_str(input_rules, &rules_obj);
+ cfg_obj = get_ini_config_from_str(input_cfg, sizeof(input_cfg));
+
+ ret = ini_errobj_create(&errobj);
+ fail_unless(ret == 0, "ini_errobj_create() failed: %s", strerror(ret));
+
+ ret = ini_rules_check(rules_obj, cfg_obj, NULL, 0, errobj);
+ fail_unless(ret == 0, "ini_rules_check() failed: %s", strerror(ret));
+
+ /* Should generate 2 errors (one from rules_check and one
+ * from the validator itself) */
+ fail_if(ini_errobj_no_more_msgs(errobj));
+
+ num_err = ini_errobj_count(errobj);
+ fail_unless(num_err == 2, "Expected 2 errors, got %d", num_err);
+
+ ini_errobj_destroy(&errobj);
+ ini_config_destroy(cfg_obj);
+ ini_rules_destroy(rules_obj);
+}
+END_TEST
+
+
+START_TEST(test_ini_allowed_options_typos)
+{
+ struct ini_cfgobj *rules_obj;
+ struct ini_cfgobj *cfg_obj;
+ struct ini_errobj *errobj;
+ int ret;
+ size_t num_err;
+
+ /* Only bar and baz are allowed for foo section */
+ char input_rules[] =
+ "[rule/options_for_foo]\n"
+ "validator = ini_allowed_options\n"
+ "section_re = ^foo$\n"
+ "option = bar\n"
+ "option = baz\n";
+
+ /* Make 4 typos */
+ char input_cfg[] =
+ "[foo]\n"
+ "br = 0\n"
+ "bra = 0\n"
+ "abr = 0\n"
+ "abz = 0\n";
+
+ create_rules_from_str(input_rules, &rules_obj);
+ cfg_obj = get_ini_config_from_str(input_cfg, sizeof(input_cfg));
+
+ ret = ini_errobj_create(&errobj);
+ fail_unless(ret == 0, "ini_errobj_create() failed: %s", strerror(ret));
+
+ ret = ini_rules_check(rules_obj, cfg_obj, NULL, 0, errobj);
+ fail_unless(ret == 0, "ini_rules_check() failed: %s", strerror(ret));
+
+ /* Should generate 4 errors */
+ fail_if(ini_errobj_no_more_msgs(errobj));
+
+ num_err = ini_errobj_count(errobj);
+ fail_unless(num_err == 4, "Expected 4 errors, got %d", num_err);
+
+ ini_errobj_destroy(&errobj);
+ ini_config_destroy(cfg_obj);
+ ini_rules_destroy(rules_obj);
+}
+END_TEST
+
+START_TEST(test_ini_allowed_sections_str_ok)
+{
+ struct ini_cfgobj *rules_obj;
+ struct ini_cfgobj *cfg_obj;
+ struct ini_errobj *errobj;
+ int ret;
+
+ /* Only bar and baz are allowed for foo section */
+ char input_rules[] =
+ "[rule/section_list]\n"
+ "validator = ini_allowed_sections\n"
+ "section = foo\n"
+ "section = bar\n";
+
+ /* Make 4 typos */
+ char input_cfg[] =
+ "[foo]\n"
+ "br = 0\n"
+ "bra = 0\n"
+ "[bar]\n"
+ "abz = 0\n";
+
+ create_rules_from_str(input_rules, &rules_obj);
+ cfg_obj = get_ini_config_from_str(input_cfg, sizeof(input_cfg));
+
+ ret = ini_errobj_create(&errobj);
+ fail_unless(ret == 0, "ini_errobj_create() failed: %s", strerror(ret));
+
+ ret = ini_rules_check(rules_obj, cfg_obj, NULL, 0, errobj);
+ fail_unless(ret == 0, "ini_rules_check() failed: %s", strerror(ret));
+
+ /* Should generate 0 errors */
+ fail_unless(ini_errobj_no_more_msgs(errobj), "Unexpected errors found");
+
+ ini_errobj_destroy(&errobj);
+ ini_config_destroy(cfg_obj);
+ ini_rules_destroy(rules_obj);
+
+}
+END_TEST
+
+static Suite *ini_validators_utils_suite(void)
+{
+ Suite *s = suite_create("ini_validators");
+
+ TCase *tc_infrastructure = tcase_create("infrastructure");
+ tcase_add_test(tc_infrastructure, test_ini_errobj);
+ tcase_add_test(tc_infrastructure, test_ini_noerror);
+ tcase_add_test(tc_infrastructure, test_ini_error);
+ tcase_add_test(tc_infrastructure, test_unknown_validator);
+ tcase_add_test(tc_infrastructure, test_custom_noerror);
+ tcase_add_test(tc_infrastructure, test_custom_error);
+
+ TCase *tc_allowed_options = tcase_create("ini_allowed_options");
+ tcase_add_test(tc_allowed_options, test_ini_allowed_options_ok);
+ tcase_add_test(tc_allowed_options, test_ini_allowed_options_typos);
+ tcase_add_test(tc_allowed_options, test_ini_allowed_options_no_section);
+
+ suite_add_tcase(s, tc_infrastructure);
+ suite_add_tcase(s, tc_allowed_options);
+
+ return s;
+}
+
+int main(void)
+{
+ int number_failed;
+
+ Suite *s = ini_validators_utils_suite();
+ SRunner *sr = srunner_create(s);
+ /* If CK_VERBOSITY is set, use that, otherwise it defaults to CK_NORMAL */
+ srunner_run_all(sr, CK_ENV);
+ number_failed = srunner_ntests_failed(sr);
+ srunner_free(sr);
+ return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
--
2.5.0
>From 2b4f908f40909a20c74de8889b997a983f17334f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Michal=20=C5=BDidek?= <[email protected]>
Date: Fri, 15 Apr 2016 12:13:58 +0200
Subject: [PATCH 4/4] ini: Add internal validator allowed_sections
Ticket:
https://fedorahosted.org/sssd/ticket/133
This validator allows to list all sections
that can be used in config file. Sections
can be specified using string or regular
expression.
Format is:
[rule/allowed_sections]
validator = ini_allowed_sections
section = foo
section = bar
section_re = ^baz/.*
---
ini/ini_configobj.c | 222 ++++++++++++++++++++++++++++++++++++++++++
ini/ini_validators_ut_check.c | 219 +++++++++++++++++++++++++++++++++++++++++
2 files changed, 441 insertions(+)
diff --git a/ini/ini_configobj.c b/ini/ini_configobj.c
index 95a3baa..c53e868 100644
--- a/ini/ini_configobj.c
+++ b/ini/ini_configobj.c
@@ -1095,6 +1095,226 @@ static int ini_dummy_error(const char *rule_name,
return ini_errobj_add_msg(errobj, "Error");
}
+static int is_allowed_section(const char *tested_section,
+ char **allowed_sections,
+ size_t num_sec,
+ regex_t *allowed_sections_re,
+ size_t num_sec_re,
+ int case_insensitive)
+{
+ int ret;
+
+ if (case_insensitive) {
+ for (int i = 0; i < num_sec; i++) {
+ if (strcasecmp(tested_section, allowed_sections[i]) == 0) {
+ return 1;
+ }
+ }
+ } else { /* case insensitive */
+ for (int i = 0; i < num_sec; i++) {
+ if (strcmp(tested_section, allowed_sections[i]) == 0) {
+ return 1;
+ }
+ }
+ }
+
+ for (int i = 0; i < num_sec_re; i++) {
+ ret = regexec(&allowed_sections_re[i], tested_section, 0, NULL, 0);
+ if (ret == 0) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static int ini_allowed_sections(const char *rule_name,
+ struct ini_cfgobj *rules_obj,
+ struct ini_cfgobj *config_obj,
+ struct ini_errobj *errobj)
+{
+ struct value_obj *vo = NULL;
+ int ret;
+ char *regex_str = NULL;
+ char **allowed_sections = NULL;
+ char *insensitive_str;
+ char **cfg_sections = NULL;
+ int num_cfg_sections;
+ char **attributes = NULL;
+ int num_attributes;
+ size_t num_sec = 0;
+ size_t num_sec_re = 0;
+ regex_t *allowed_sections_re;
+ size_t buf_size;
+ char *err_str = NULL;
+ int reg_err;
+ int is_allowed;
+ int case_insensitive = 0;
+ int regcomp_flags = REG_NOSUB;
+
+ /* Get number of 'section' and 'section_re' attributes
+ * in this rule */
+ attributes = ini_get_attribute_list(rules_obj,
+ rule_name,
+ &num_attributes,
+ NULL);
+ if (attributes == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ for (int i = 0; i < num_attributes; i++) {
+ if (strcmp("section", attributes[i]) == 0) {
+ num_sec++;
+ }
+
+ if (strcmp("section_re", attributes[i]) == 0) {
+ num_sec_re++;
+ }
+ }
+
+ if (num_sec == 0 && num_sec_re == 0) {
+ /* This rule is empty. */
+ ret = ini_errobj_add_msg(errobj, "No allowed sections specified. "
+ "Use 'section = default' to allow only "
+ "default section");
+ goto done;
+ }
+
+ ini_free_attribute_list(attributes);
+
+ ret = ini_get_config_valueobj(rule_name,
+ "case_insensitive",
+ rules_obj,
+ INI_GET_NEXT_VALUE,
+ &vo);
+ if (ret) {
+ goto done;
+ }
+
+ if (vo) {
+ insensitive_str = ini_get_string_config_value(vo, &ret);
+ if (ret) {
+ goto done;
+ }
+
+ if (strcasecmp(insensitive_str, "yes") == 0
+ || strcasecmp(insensitive_str, "true") == 0
+ || strcmp(insensitive_str, "1") == 0) {
+ case_insensitive = 1;
+ regcomp_flags |= REG_ICASE;
+ }
+
+ free(insensitive_str);
+ }
+
+ /* Create arrays for section_re regexes and section name
+ * strings. */
+ allowed_sections = calloc(num_sec, sizeof(char*));
+ if (allowed_sections == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ allowed_sections_re = calloc(num_sec_re, sizeof(regex_t));
+ if (allowed_sections_re == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* Get all allowed section names and store them to
+ * allowed_sections array */
+ for (size_t i = 0; i < num_sec; i++) {
+ ret = ini_get_config_valueobj(rule_name,
+ "section",
+ rules_obj,
+ INI_GET_NEXT_VALUE,
+ &vo);
+ if (ret) {
+ goto done;
+ }
+
+ allowed_sections[i] = ini_get_string_config_value(vo, &ret);
+ if (ret) {
+ goto done;
+ }
+ }
+
+ /* Get all regular section_re regular expresions and
+ * store them to allowed_sections_re array */
+ for (int i = 0; i < num_sec_re; i++) {
+ ret = ini_get_config_valueobj(rule_name,
+ "section_re",
+ rules_obj,
+ INI_GET_NEXT_VALUE,
+ &vo);
+ if (ret) {
+ goto done;
+ }
+
+ regex_str = ini_get_string_config_value(vo, &ret);
+ if (ret) {
+ goto done;
+ }
+
+ reg_err = regcomp(&allowed_sections_re[i], regex_str, regcomp_flags);
+ if (reg_err) {
+ buf_size = regerror(reg_err, &allowed_sections_re[i], NULL, 0);
+ err_str = malloc(buf_size);
+ if (err_str == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ regerror(reg_err, &allowed_sections_re[i], err_str, buf_size);
+ ret = ini_errobj_add_msg(errobj, "Validator failed to use regex "
+ "[%s]:[%s]", regex_str, err_str);
+ ret = ret ? ret : EINVAL;
+ goto done;
+ }
+ free(regex_str);
+ regex_str = NULL;
+ }
+
+ /* Finally get list of all sections in configuration and
+ * check if they are matched by some string in allowed_sections
+ * or regex in allowed_sections_re */
+ cfg_sections = ini_get_section_list(config_obj, &num_cfg_sections, &ret);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ for (int i = 0; i < num_cfg_sections; i++) {
+ is_allowed = is_allowed_section(cfg_sections[i],
+ allowed_sections,
+ num_sec,
+ allowed_sections_re,
+ num_sec_re,
+ case_insensitive);
+ if (!is_allowed) {
+ ret = ini_errobj_add_msg(errobj, "Section [%s] is not allowed. "
+ "Check for typos.", cfg_sections[i]);
+ if (ret) {
+ goto done;
+ }
+ }
+ }
+
+ ret = EOK;
+done:
+ for (int i = 0; i < num_sec; i++) {
+ free(allowed_sections[i]);
+ }
+ free(allowed_sections);
+ for (int i = 0; i < num_sec_re; i++) {
+ regfree(&allowed_sections_re[i]);
+ }
+ free(allowed_sections_re);
+ ini_free_section_list(cfg_sections);
+ free(regex_str);
+
+ return ret;
+}
static int check_if_allowed(char *section, char *attr, char **allowed, int num_allowed,
struct ini_errobj *errobj)
@@ -1285,6 +1505,8 @@ get_validator(char *validator_name,
return ini_dummy_error;
} else if (strcmp(validator_name, "ini_allowed_options") == 0) {
return ini_allowed_options;
+ } else if (strcmp(validator_name, "ini_allowed_sections") == 0) {
+ return ini_allowed_sections;
}
/* Now check the custom validators */
diff --git a/ini/ini_validators_ut_check.c b/ini/ini_validators_ut_check.c
index cf208ed..d337d36 100644
--- a/ini/ini_validators_ut_check.c
+++ b/ini/ini_validators_ut_check.c
@@ -510,10 +510,219 @@ START_TEST(test_ini_allowed_sections_str_ok)
ini_errobj_destroy(&errobj);
ini_config_destroy(cfg_obj);
ini_rules_destroy(rules_obj);
+}
+END_TEST
+
+START_TEST(test_ini_allowed_sections_str_typos)
+{
+ struct ini_cfgobj *rules_obj;
+ struct ini_cfgobj *cfg_obj;
+ struct ini_errobj *errobj;
+ int num_err;
+ int ret;
+
+ /* Only bar and baz are allowed for foo section */
+ char input_rules[] =
+ "[rule/section_list]\n"
+ "validator = ini_allowed_sections\n"
+ "section = foo\n"
+ "section = bar\n";
+
+ /* Make 4 typos */
+ char input_cfg[] =
+ "[fooo]\n"
+ "br = 0\n"
+ "bra = 0\n"
+ "[baar]\n"
+ "abz = 0\n";
+
+ create_rules_from_str(input_rules, &rules_obj);
+ cfg_obj = get_ini_config_from_str(input_cfg, sizeof(input_cfg));
+
+ ret = ini_errobj_create(&errobj);
+ fail_unless(ret == 0, "ini_errobj_create() failed: %s", strerror(ret));
+
+ ret = ini_rules_check(rules_obj, cfg_obj, NULL, 0, errobj);
+ fail_unless(ret == 0, "ini_rules_check() failed: %s", strerror(ret));
+
+ /* Should generate 2 errors */
+ fail_if(ini_errobj_no_more_msgs(errobj),
+ "Expected 2 errors but none found");
+ num_err = ini_errobj_count(errobj);
+ fail_unless(num_err == 2, "Expected 2 errors, got %d", num_err);
+
+
+ ini_errobj_destroy(&errobj);
+ ini_config_destroy(cfg_obj);
+ ini_rules_destroy(rules_obj);
+}
+END_TEST
+
+START_TEST(test_ini_allowed_sections_str_insensitive)
+{
+ struct ini_cfgobj *rules_obj;
+ struct ini_cfgobj *cfg_obj;
+ struct ini_errobj *errobj;
+ int ret;
+
+ /* Only bar and baz are allowed for foo section */
+ char input_rules[] =
+ "[rule/section_list]\n"
+ "validator = ini_allowed_sections\n"
+ "case_insensitive = yes\n"
+ "section = foo\n"
+ "section = bar\n";
+
+ /* Make 4 typos */
+ char input_cfg[] =
+ "[FOo]\n"
+ "br = 0\n"
+ "bra = 0\n"
+ "[baR]\n"
+ "abz = 0\n";
+
+ create_rules_from_str(input_rules, &rules_obj);
+ cfg_obj = get_ini_config_from_str(input_cfg, sizeof(input_cfg));
+
+ ret = ini_errobj_create(&errobj);
+ fail_unless(ret == 0, "ini_errobj_create() failed: %s", strerror(ret));
+
+ ret = ini_rules_check(rules_obj, cfg_obj, NULL, 0, errobj);
+ fail_unless(ret == 0, "ini_rules_check() failed: %s", strerror(ret));
+
+ /* Should generate 0 errors */
+ fail_unless(ini_errobj_no_more_msgs(errobj), "Unexpected errors found");
+
+ ini_errobj_destroy(&errobj);
+ ini_config_destroy(cfg_obj);
+ ini_rules_destroy(rules_obj);
+}
+END_TEST
+
+START_TEST(test_ini_allowed_sections_re_ok)
+{
+ struct ini_cfgobj *rules_obj;
+ struct ini_cfgobj *cfg_obj;
+ struct ini_errobj *errobj;
+ int ret;
+
+ char input_rules[] =
+ "[rule/section_list]\n"
+ "validator = ini_allowed_sections\n"
+ "section_re = ^foo*$\n"
+ "section_re = bar\n";
+
+ char input_cfg[] =
+ "[foooooooooooo]\n"
+ "br = 0\n"
+ "bra = 0\n"
+ "[my_bar]\n"
+ "abz = 0\n";
+
+ create_rules_from_str(input_rules, &rules_obj);
+ cfg_obj = get_ini_config_from_str(input_cfg, sizeof(input_cfg));
+
+ ret = ini_errobj_create(&errobj);
+ fail_unless(ret == 0, "ini_errobj_create() failed: %s", strerror(ret));
+
+ ret = ini_rules_check(rules_obj, cfg_obj, NULL, 0, errobj);
+ fail_unless(ret == 0, "ini_rules_check() failed: %s", strerror(ret));
+
+ /* Should generate 0 errors */
+ fail_unless(ini_errobj_no_more_msgs(errobj), "Unexpected errors found");
+
+ ini_errobj_destroy(&errobj);
+ ini_config_destroy(cfg_obj);
+ ini_rules_destroy(rules_obj);
+}
+END_TEST
+
+START_TEST(test_ini_allowed_sections_re_typos)
+{
+ struct ini_cfgobj *rules_obj;
+ struct ini_cfgobj *cfg_obj;
+ struct ini_errobj *errobj;
+ int num_err;
+ int ret;
+
+ char input_rules[] =
+ "[rule/section_list]\n"
+ "validator = ini_allowed_sections\n"
+ "section_re = ^foo*$\n"
+ "section_re = bar\n";
+
+ /* Make 4 typos */
+ char input_cfg[] =
+ "[fooooooOooooo]\n"
+ "br = 0\n"
+ "bra = 0\n"
+ "[my_bra]\n"
+ "abz = 0\n";
+
+ create_rules_from_str(input_rules, &rules_obj);
+ cfg_obj = get_ini_config_from_str(input_cfg, sizeof(input_cfg));
+
+ ret = ini_errobj_create(&errobj);
+ fail_unless(ret == 0, "ini_errobj_create() failed: %s", strerror(ret));
+
+ ret = ini_rules_check(rules_obj, cfg_obj, NULL, 0, errobj);
+ fail_unless(ret == 0, "ini_rules_check() failed: %s", strerror(ret));
+ /* Should generate 2 errors */
+ fail_if(ini_errobj_no_more_msgs(errobj),
+ "Expected 2 errors but none found");
+ num_err = ini_errobj_count(errobj);
+ fail_unless(num_err == 2, "Expected 2 errors, got %d", num_err);
+
+
+ ini_errobj_destroy(&errobj);
+ ini_config_destroy(cfg_obj);
+ ini_rules_destroy(rules_obj);
}
END_TEST
+START_TEST(test_ini_allowed_sections_re_insensitive)
+{
+ struct ini_cfgobj *rules_obj;
+ struct ini_cfgobj *cfg_obj;
+ struct ini_errobj *errobj;
+ int ret;
+
+ /* Only bar and baz are allowed for foo section */
+ char input_rules[] =
+ "[rule/section_list]\n"
+ "validator = ini_allowed_sections\n"
+ "case_insensitive = yes\n"
+ "section_re = ^foo*$\n"
+ "section_re = bar\n";
+
+ /* Make 4 typos */
+ char input_cfg[] =
+ "[FOoOoOoOoOOOOooo]\n"
+ "br = 0\n"
+ "bra = 0\n"
+ "[my_Bar]\n"
+ "abz = 0\n";
+
+ create_rules_from_str(input_rules, &rules_obj);
+ cfg_obj = get_ini_config_from_str(input_cfg, sizeof(input_cfg));
+
+ ret = ini_errobj_create(&errobj);
+ fail_unless(ret == 0, "ini_errobj_create() failed: %s", strerror(ret));
+
+ ret = ini_rules_check(rules_obj, cfg_obj, NULL, 0, errobj);
+ fail_unless(ret == 0, "ini_rules_check() failed: %s", strerror(ret));
+
+ /* Should generate 0 errors */
+ fail_unless(ini_errobj_no_more_msgs(errobj), "Unexpected errors found");
+
+ ini_errobj_destroy(&errobj);
+ ini_config_destroy(cfg_obj);
+ ini_rules_destroy(rules_obj);
+}
+END_TEST
+
+
static Suite *ini_validators_utils_suite(void)
{
Suite *s = suite_create("ini_validators");
@@ -531,8 +740,18 @@ static Suite *ini_validators_utils_suite(void)
tcase_add_test(tc_allowed_options, test_ini_allowed_options_typos);
tcase_add_test(tc_allowed_options, test_ini_allowed_options_no_section);
+ TCase *tc_allowed_sections = tcase_create("ini_allowed_sections");
+ tcase_add_test(tc_allowed_options, test_ini_allowed_sections_str_ok);
+ tcase_add_test(tc_allowed_options, test_ini_allowed_sections_str_typos);
+ tcase_add_test(tc_allowed_options, test_ini_allowed_sections_str_insensitive);
+ tcase_add_test(tc_allowed_options, test_ini_allowed_sections_re_ok);
+ tcase_add_test(tc_allowed_options, test_ini_allowed_sections_re_typos);
+ tcase_add_test(tc_allowed_options, test_ini_allowed_sections_re_insensitive);
+
+
suite_add_tcase(s, tc_infrastructure);
suite_add_tcase(s, tc_allowed_options);
+ suite_add_tcase(s, tc_allowed_sections);
return s;
}
--
2.5.0
_______________________________________________
sssd-devel mailing list
[email protected]
https://lists.fedorahosted.org/admin/lists/[email protected]