feat(validation): implement configuration validation framework with rules for required directives and allowed values
This commit is contained in:
parent
ceab8d7aab
commit
95753eea34
@ -28,7 +28,7 @@ server {
|
||||
autoindex off;
|
||||
root ./static_site/;
|
||||
index index2.html index.html;
|
||||
allowed_methods GET POST DELETE;
|
||||
allowed_methods GET POST DELETE UPDATE;
|
||||
}
|
||||
|
||||
location /uploads {
|
||||
|
||||
@ -34,6 +34,17 @@ const ADirective *AConfig::getDirective(const std::string &name) const
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::vector<const ADirective *> AConfig::getDirectives() const
|
||||
{
|
||||
std::vector<const ADirective *> result;
|
||||
result.reserve(directives_.size());
|
||||
for (const auto &directive : directives_)
|
||||
{
|
||||
result.push_back(directive.get());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool AConfig::hasDirective(const std::string &name) const
|
||||
{
|
||||
for (const auto &directive : directives_)
|
||||
|
||||
@ -19,11 +19,14 @@ class AConfig
|
||||
AConfig &operator=(AConfig &&other) noexcept = delete;
|
||||
|
||||
virtual ~AConfig() = default;
|
||||
|
||||
[[nodiscard]] virtual std::string getName() const = 0;
|
||||
[[nodiscard]] virtual std::string getType() const = 0;
|
||||
void addDirective(const std::string &line);
|
||||
[[nodiscard]] std::string getErrorPage(int statusCode) const;
|
||||
|
||||
[[nodiscard]] bool hasDirective(const std::string &name) const;
|
||||
[[nodiscard]] const ADirective *getDirective(const std::string &name) const;
|
||||
[[nodiscard]] std::vector<const ADirective *> getDirectives() const;
|
||||
|
||||
template <typename T>
|
||||
std::optional<T> get(const std::string &name) const
|
||||
@ -37,7 +40,6 @@ class AConfig
|
||||
}
|
||||
|
||||
protected:
|
||||
[[nodiscard]] const ADirective *getDirective(const std::string &name) const;
|
||||
virtual void parseBlock(const std::string &block) = 0;
|
||||
void parseDirectives(const std::string &declarations);
|
||||
std::vector<std::unique_ptr<ADirective>>
|
||||
|
||||
@ -12,6 +12,16 @@ GlobalConfig::GlobalConfig(const std::string &block)
|
||||
parseBlock(block);
|
||||
}
|
||||
|
||||
std::string GlobalConfig::getName() const
|
||||
{
|
||||
return "global";
|
||||
}
|
||||
|
||||
std::string GlobalConfig::getType() const
|
||||
{
|
||||
return "global";
|
||||
}
|
||||
|
||||
void GlobalConfig::parseBlock(const std::string &block)
|
||||
{
|
||||
std::string directives;
|
||||
|
||||
@ -19,6 +19,10 @@ class GlobalConfig : public AConfig
|
||||
GlobalConfig &operator=(GlobalConfig &&other) noexcept = delete;
|
||||
|
||||
~GlobalConfig() override = default;
|
||||
|
||||
[[nodiscard]] std::string getName() const override;
|
||||
[[nodiscard]] std::string getType() const override;
|
||||
|
||||
[[nodiscard]] std::vector<ServerConfig *> getServerConfigs() const;
|
||||
|
||||
private:
|
||||
|
||||
@ -8,6 +8,17 @@ LocationConfig::LocationConfig(const std::string &block, const std::string &path
|
||||
parseBlock(block);
|
||||
}
|
||||
|
||||
std::string LocationConfig::getName() const
|
||||
{
|
||||
auto parentName = parent_ != nullptr ? parent_->getName() : "root";
|
||||
return parentName + ", location: " + _path;
|
||||
}
|
||||
|
||||
std::string LocationConfig::getType() const
|
||||
{
|
||||
return "location";
|
||||
}
|
||||
|
||||
void LocationConfig::parseBlock(const std::string &block)
|
||||
{
|
||||
Log::trace(LOCATION);
|
||||
|
||||
@ -17,6 +17,8 @@ class LocationConfig : public AConfig
|
||||
|
||||
~LocationConfig() override = default;
|
||||
|
||||
[[nodiscard]] std::string getName() const override;
|
||||
[[nodiscard]] std::string getType() const override;
|
||||
[[nodiscard]] const std::string &getPath() const { return _path; }
|
||||
|
||||
private:
|
||||
|
||||
@ -14,6 +14,17 @@ ServerConfig::ServerConfig(const std::string &block, const AConfig *parent) : AC
|
||||
parseBlock(block);
|
||||
}
|
||||
|
||||
std::string ServerConfig::getName() const
|
||||
{
|
||||
return "server: " + get<std::string>("server_name").value_or("unnamed") + " (port " +
|
||||
std::to_string(get<int>("listen").value_or(-1)) + ")";
|
||||
}
|
||||
|
||||
std::string ServerConfig::getType() const
|
||||
{
|
||||
return "server";
|
||||
}
|
||||
|
||||
void ServerConfig::parseBlock(const std::string &block)
|
||||
{
|
||||
Log::trace(LOCATION);
|
||||
@ -68,3 +79,4 @@ std::vector<std::string> ServerConfig::getLocationPaths() const
|
||||
}
|
||||
return paths;
|
||||
}
|
||||
|
||||
|
||||
@ -21,9 +21,13 @@ class ServerConfig : public AConfig
|
||||
|
||||
~ServerConfig() override = default;
|
||||
|
||||
[[nodiscard]] std::string getName() const override;
|
||||
[[nodiscard]] std::string getType() const override;
|
||||
|
||||
[[nodiscard]] const LocationConfig *getLocation(const std::string &path) const;
|
||||
[[nodiscard]] std::vector<std::string> getLocationPaths() const;
|
||||
|
||||
|
||||
private:
|
||||
std::map<std::string, std::unique_ptr<LocationConfig>> locations_;
|
||||
AConfig *parent_ = nullptr;
|
||||
|
||||
44
webserv/config/config_validator/AValidationRule.cpp
Normal file
44
webserv/config/config_validator/AValidationRule.cpp
Normal file
@ -0,0 +1,44 @@
|
||||
#include <webserv/config/AConfig.hpp>
|
||||
#include <webserv/config/config_validator/AValidationRule.hpp>
|
||||
#include <webserv/config/config_validator/ValidationResult.hpp>
|
||||
#include <webserv/config/directive/ADirective.hpp>
|
||||
#include <webserv/log/Log.hpp>
|
||||
|
||||
AValidationRule::AValidationRule(std::string ruleName, std::string description, bool requiresValue)
|
||||
: ruleName_(std::move(ruleName)), description_(std::move(description)), requiresValue_(requiresValue)
|
||||
{
|
||||
}
|
||||
|
||||
ValidationResult AValidationRule::validate(const AConfig *config, const std::string &directiveName) const
|
||||
{
|
||||
Log::trace(LOCATION);
|
||||
if (config == nullptr || directiveName.empty())
|
||||
{
|
||||
return ValidationResult::error("Invalid config or directive name");
|
||||
}
|
||||
|
||||
if (!config->hasDirective(directiveName))
|
||||
{
|
||||
if (requiresValue_)
|
||||
{
|
||||
return ValidationResult::error("Directive '" + directiveName + "' is missing");
|
||||
}
|
||||
return ValidationResult::success();
|
||||
}
|
||||
return validateValue(config, directiveName);
|
||||
}
|
||||
|
||||
std::string AValidationRule::getRuleName() const
|
||||
{
|
||||
return ruleName_;
|
||||
}
|
||||
|
||||
std::string AValidationRule::getDescription() const
|
||||
{
|
||||
return description_;
|
||||
}
|
||||
|
||||
bool AValidationRule::isRequired() const
|
||||
{
|
||||
return requiresValue_;
|
||||
}
|
||||
32
webserv/config/config_validator/AValidationRule.hpp
Normal file
32
webserv/config/config_validator/AValidationRule.hpp
Normal file
@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
|
||||
|
||||
class ValidationResult;
|
||||
class ADirective;
|
||||
class AConfig;
|
||||
|
||||
class AValidationRule
|
||||
{
|
||||
public:
|
||||
virtual ~AValidationRule() = default;
|
||||
|
||||
AValidationRule(const AValidationRule &other) = delete;
|
||||
AValidationRule &operator=(const AValidationRule &other) = delete;
|
||||
AValidationRule(AValidationRule &&other) noexcept = delete;
|
||||
AValidationRule &operator=(AValidationRule &&other) noexcept = delete;
|
||||
|
||||
[[nodiscard]] ValidationResult validate(const AConfig *config, const std::string &directiveName) const;
|
||||
[[nodiscard]] bool isRequired() const;
|
||||
[[nodiscard]] virtual ValidationResult validateValue(const AConfig *config, const std::string &directiveName) const = 0;
|
||||
[[nodiscard]] std::string getRuleName() const;
|
||||
[[nodiscard]] std::string getDescription() const;
|
||||
|
||||
protected:
|
||||
AValidationRule(std::string ruleName, std::string description, bool requiresValue = true);
|
||||
|
||||
private:
|
||||
std::string ruleName_;
|
||||
std::string description_;
|
||||
bool requiresValue_;
|
||||
};
|
||||
60
webserv/config/config_validator/AllowedValuesRule.cpp
Normal file
60
webserv/config/config_validator/AllowedValuesRule.cpp
Normal file
@ -0,0 +1,60 @@
|
||||
#include <webserv/config/AConfig.hpp>
|
||||
#include <webserv/config/config_validator/AllowedValuesRule.hpp>
|
||||
#include <webserv/config/config_validator/ValidationResult.hpp>
|
||||
#include <webserv/config/directive/ADirective.hpp>
|
||||
|
||||
#include <algorithm> // for find
|
||||
|
||||
#include <string> // for string, basic_string, operator+, char_traits
|
||||
#include <vector> // for vector
|
||||
|
||||
AllowedValuesRule::AllowedValuesRule(const std::vector<std::string> &allowedValues, bool requiresValue)
|
||||
: AValidationRule("AllowedValuesRule", "Ensures that the directive's value is within the allowed set",
|
||||
requiresValue),
|
||||
allowedValues_(allowedValues)
|
||||
{
|
||||
}
|
||||
|
||||
ValidationResult AllowedValuesRule::validateValue(const AConfig *config, const std::string &directiveName) const
|
||||
{
|
||||
const ADirective *directive = config->getDirective(directiveName);
|
||||
if (directive == nullptr)
|
||||
{
|
||||
return ValidationResult::error("Directive '" + directiveName + "' is missing");
|
||||
}
|
||||
|
||||
auto checkValueAllowed = [&](const std::string &val) -> bool {
|
||||
return std::ranges::find(allowedValues_, val) != allowedValues_.end();
|
||||
};
|
||||
|
||||
auto makeErrorMsg = [&](const std::string &val) -> std::string {
|
||||
return "Value '" + val + "' of directive '" + directiveName + "' is not in the allowed set";
|
||||
};
|
||||
|
||||
if (directive->getValue().holds<std::string>())
|
||||
{
|
||||
const std::string &value = directive->getValue().get<std::string>();
|
||||
|
||||
if (!checkValueAllowed(value))
|
||||
{
|
||||
return ValidationResult::error(makeErrorMsg(value));
|
||||
}
|
||||
}
|
||||
else if (directive->getValue().holds<std::vector<std::string>>())
|
||||
{
|
||||
const std::vector<std::string> &values = directive->getValue().get<std::vector<std::string>>();
|
||||
for (const std::string &val : values)
|
||||
{
|
||||
if (!checkValueAllowed(val))
|
||||
{
|
||||
return ValidationResult::error(makeErrorMsg(val));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return ValidationResult::error("Directive '" + directiveName + "' has an unsupported value type");
|
||||
}
|
||||
|
||||
return ValidationResult::success();
|
||||
}
|
||||
18
webserv/config/config_validator/AllowedValuesRule.hpp
Normal file
18
webserv/config/config_validator/AllowedValuesRule.hpp
Normal file
@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include "webserv/config/config_validator/AValidationRule.hpp"
|
||||
#include "webserv/config/config_validator/ValidationResult.hpp"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class AConfig;
|
||||
|
||||
class AllowedValuesRule : public AValidationRule
|
||||
{
|
||||
public:
|
||||
explicit AllowedValuesRule(const std::vector<std::string> &allowedValues, bool requiresValue = true);
|
||||
|
||||
private:
|
||||
[[nodiscard]] ValidationResult validateValue(const AConfig *config, const std::string &directiveName) const override;
|
||||
std::vector<std::string> allowedValues_;
|
||||
};
|
||||
38
webserv/config/config_validator/ConfigValidator.cpp
Normal file
38
webserv/config/config_validator/ConfigValidator.cpp
Normal file
@ -0,0 +1,38 @@
|
||||
#include "webserv/config/config_validator/ConfigValidator.hpp"
|
||||
|
||||
#include "webserv/config/GlobalConfig.hpp"
|
||||
#include "webserv/config/config_validator/AllowedValuesRule.hpp"
|
||||
#include "webserv/config/config_validator/PortValidationRule.hpp"
|
||||
#include "webserv/log/Log.hpp"
|
||||
#include <string>
|
||||
|
||||
ConfigValidator::ConfigValidator(const GlobalConfig *config)
|
||||
: engine_(std::make_unique<ValidationEngine>(config))
|
||||
{
|
||||
Log::trace(LOCATION);
|
||||
|
||||
engine_->addServerRule("listen", std::make_unique<PortValidationRule>());
|
||||
engine_->addLocationRule("allowed_methods", std::make_unique<AllowedValuesRule>(std::vector<std::string>{"GET", "POST", "DELETE"}));
|
||||
engine_->validate();
|
||||
}
|
||||
|
||||
std::vector<ValidationResult> ConfigValidator::getValidationResults() const
|
||||
{
|
||||
return engine_->getValidationResults();
|
||||
}
|
||||
|
||||
std::vector<ValidationResult> ConfigValidator::getErrors() const
|
||||
{
|
||||
return engine_->getErrors();
|
||||
}
|
||||
|
||||
std::vector<ValidationResult> ConfigValidator::getWarnings() const
|
||||
{
|
||||
return engine_->getWarnings();
|
||||
}
|
||||
|
||||
bool ConfigValidator::hasErrors() const
|
||||
{
|
||||
return engine_->hasErrors();
|
||||
}
|
||||
|
||||
26
webserv/config/config_validator/ConfigValidator.hpp
Normal file
26
webserv/config/config_validator/ConfigValidator.hpp
Normal file
@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
#include "webserv/config/config_validator/ValidationEngine.hpp"
|
||||
#include <memory>
|
||||
class GlobalConfig;
|
||||
class ConfigValidator
|
||||
{
|
||||
public:
|
||||
ConfigValidator(const GlobalConfig *config);
|
||||
|
||||
ConfigValidator(const ConfigValidator &other) = delete;
|
||||
ConfigValidator &operator=(const ConfigValidator &other) = delete;
|
||||
ConfigValidator(ConfigValidator &&other) noexcept = delete;
|
||||
ConfigValidator &operator=(ConfigValidator &&other) noexcept = delete;
|
||||
~ConfigValidator() = default;
|
||||
|
||||
[[nodiscard]] std::vector<ValidationResult> getValidationResults() const;
|
||||
[[nodiscard]] std::vector<ValidationResult> getErrors() const;
|
||||
[[nodiscard]] std::vector<ValidationResult> getWarnings() const;
|
||||
[[nodiscard]] bool hasErrors() const;
|
||||
|
||||
private:
|
||||
std::unique_ptr<ValidationEngine> engine_;
|
||||
|
||||
};
|
||||
@ -1,21 +0,0 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
|
||||
|
||||
class ValidationResult;
|
||||
|
||||
class IValidationRule
|
||||
{
|
||||
public:
|
||||
virtual ~IValidationRule() = default;
|
||||
|
||||
IValidationRule(const IValidationRule &other) = delete;
|
||||
IValidationRule &operator=(const IValidationRule &other) = delete;
|
||||
IValidationRule(IValidationRule &&other) noexcept = delete;
|
||||
IValidationRule &operator=(IValidationRule &&other) noexcept = delete;
|
||||
|
||||
[[nodiscard]] virtual ValidationResult validate(const std::string &Adirective) const = 0;
|
||||
[[nodiscard]] virtual std::string getRuleName() const = 0;
|
||||
[[nodiscard]] virtual std::string getDescription() const = 0;
|
||||
|
||||
};
|
||||
32
webserv/config/config_validator/PortValidationRule.cpp
Normal file
32
webserv/config/config_validator/PortValidationRule.cpp
Normal file
@ -0,0 +1,32 @@
|
||||
#include "webserv/config/config_validator/PortValidationRule.hpp"
|
||||
|
||||
#include "webserv/config/AConfig.hpp"
|
||||
#include "webserv/config/config_validator/ValidationResult.hpp"
|
||||
#include "webserv/config/directive/ADirective.hpp"
|
||||
#include "webserv/config/directive/DirectiveValue.hpp"
|
||||
#include "webserv/log/Log.hpp"
|
||||
|
||||
#include <string> // for string, basic_string, operator+, char_traits
|
||||
|
||||
PortValidationRule::PortValidationRule(bool requiresValue)
|
||||
: AValidationRule("PortValidationRule", "Validates that the port number is within the valid range (1-65535)", requiresValue)
|
||||
{
|
||||
}
|
||||
|
||||
ValidationResult PortValidationRule::validateValue(const AConfig *config, const std::string &directiveName) const
|
||||
{
|
||||
Log::trace(LOCATION);
|
||||
const ADirective *directive = config->getDirective(directiveName);
|
||||
if (!directive->getValue().holds<int>())
|
||||
{
|
||||
return ValidationResult::error("Directive '" + directive->getName() + "' does not hold an integer value");
|
||||
}
|
||||
|
||||
int port = directive->getValue().get<int>();
|
||||
if (port < 1 || port > 65535)
|
||||
{
|
||||
return ValidationResult::error("Port number " + std::to_string(port) + " is out of valid range (1-65535)");
|
||||
}
|
||||
|
||||
return ValidationResult::success();
|
||||
}
|
||||
@ -1,18 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include "webserv/config/config_validator/IValidationRule.hpp"
|
||||
class PortValidationRule : public IValidationRule
|
||||
#include "webserv/config/config_validator/AValidationRule.hpp"
|
||||
#include <string>
|
||||
|
||||
class AConfig;
|
||||
class PortValidationRule : public AValidationRule
|
||||
{
|
||||
public:
|
||||
PortValidationRule() = default;
|
||||
~PortValidationRule() override = default;
|
||||
PortValidationRule(bool requiresValue = true);
|
||||
|
||||
PortValidationRule(const PortValidationRule &other) = delete;
|
||||
PortValidationRule &operator=(const PortValidationRule &other) = delete;
|
||||
PortValidationRule(PortValidationRule &&other) noexcept = delete;
|
||||
PortValidationRule &operator=(PortValidationRule &&other) noexcept = delete;
|
||||
|
||||
[[nodiscard]] ValidationResult validate(const std::string &value) const override;
|
||||
[[nodiscard]] std::string getRuleName() const override { return "PortValidationRule"; }
|
||||
[[nodiscard]] std::string getDescription() const override { return "Validates that a port number is between 1 and 65535"; }
|
||||
private:
|
||||
[[nodiscard]] ValidationResult validateValue(const AConfig *config, const std::string &directiveName) const override;
|
||||
};
|
||||
14
webserv/config/config_validator/RequiredDirectiveRule.cpp
Normal file
14
webserv/config/config_validator/RequiredDirectiveRule.cpp
Normal file
@ -0,0 +1,14 @@
|
||||
#include "webserv/config/config_validator/ValidationResult.hpp"
|
||||
#include "webserv/config/config_validator/RequiredDirectiveRule.hpp"
|
||||
#include <webserv/config/config_validator/AValidationRule.hpp>
|
||||
#include <webserv/config/AConfig.hpp>
|
||||
|
||||
RequiredDirectiveRule::RequiredDirectiveRule()
|
||||
: AValidationRule("RequiredDirectiveRule", "Ensures that a required directive is present in the configuration", true)
|
||||
{
|
||||
}
|
||||
|
||||
ValidationResult RequiredDirectiveRule::validateValue(const AConfig *config, const std::string &directiveName) const
|
||||
{
|
||||
return ValidationResult::success();
|
||||
}
|
||||
17
webserv/config/config_validator/RequiredDirectiveRule.hpp
Normal file
17
webserv/config/config_validator/RequiredDirectiveRule.hpp
Normal file
@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include "webserv/config/config_validator/AValidationRule.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
class AConfig;
|
||||
|
||||
class RequiredDirectiveRule : public AValidationRule
|
||||
{
|
||||
public:
|
||||
RequiredDirectiveRule();
|
||||
|
||||
private:
|
||||
[[nodiscard]] ValidationResult validateValue(const AConfig *config,
|
||||
const std::string &directiveName) const override;
|
||||
};
|
||||
157
webserv/config/config_validator/ValidationEngine.cpp
Normal file
157
webserv/config/config_validator/ValidationEngine.cpp
Normal file
@ -0,0 +1,157 @@
|
||||
#include "webserv/config/config_validator/ValidationEngine.hpp"
|
||||
|
||||
#include "webserv/config/AConfig.hpp"
|
||||
#include "webserv/config/LocationConfig.hpp"
|
||||
#include "webserv/config/config_validator/AValidationRule.hpp"
|
||||
#include "webserv/config/config_validator/ValidationResult.hpp"
|
||||
#include "webserv/config/directive/ADirective.hpp"
|
||||
#include "webserv/log/Log.hpp"
|
||||
|
||||
void ValidationEngine::addGlobalRule(const std::string &directiveName, std::unique_ptr<AValidationRule> rule)
|
||||
{
|
||||
Log::trace(LOCATION);
|
||||
addRule(globalRules_, directiveName, std::move(rule));
|
||||
}
|
||||
|
||||
void ValidationEngine::addServerRule(const std::string &directiveName, std::unique_ptr<AValidationRule> rule)
|
||||
{
|
||||
Log::trace(LOCATION);
|
||||
addRule(serverRules_, directiveName, std::move(rule));
|
||||
}
|
||||
|
||||
void ValidationEngine::addLocationRule(const std::string &directiveName, std::unique_ptr<AValidationRule> rule)
|
||||
{
|
||||
Log::trace(LOCATION);
|
||||
addRule(locationRules_, directiveName, std::move(rule));
|
||||
}
|
||||
|
||||
void ValidationEngine::addRule(RuleMap &ruleMap, const std::string &directiveName,
|
||||
std::unique_ptr<AValidationRule> rule)
|
||||
{
|
||||
Log::trace(LOCATION);
|
||||
ruleMap[directiveName].emplace_back(std::move(rule));
|
||||
}
|
||||
|
||||
std::vector<ValidationResult> ValidationEngine::getValidationResults() const
|
||||
{
|
||||
return results_;
|
||||
}
|
||||
|
||||
std::vector<ValidationResult> ValidationEngine::getErrors() const
|
||||
{
|
||||
std::vector<ValidationResult> errors;
|
||||
for (const auto &result : results_)
|
||||
{
|
||||
if (!result.isValidResult())
|
||||
{
|
||||
errors.push_back(result);
|
||||
}
|
||||
}
|
||||
return errors;
|
||||
}
|
||||
|
||||
std::vector<ValidationResult> ValidationEngine::getWarnings() const
|
||||
{
|
||||
std::vector<ValidationResult> warnings;
|
||||
for (const auto &result : results_)
|
||||
{
|
||||
if (result.getType() == ValidationResult::Type::WARNING)
|
||||
{
|
||||
warnings.push_back(result);
|
||||
}
|
||||
}
|
||||
return warnings;
|
||||
}
|
||||
|
||||
bool ValidationEngine::hasErrors() const
|
||||
{
|
||||
for (const auto &result : results_)
|
||||
{
|
||||
if (!result.isValidResult())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ValidationEngine::validateConfig(RuleMap const &rulesMap, const AConfig *config)
|
||||
{
|
||||
Log::trace(LOCATION);
|
||||
for (const auto &[directiveName, rules] : rulesMap)
|
||||
{
|
||||
if (!config->hasDirective(directiveName))
|
||||
{
|
||||
// Check if any rule requires the directive
|
||||
for (const auto &rule : rules)
|
||||
{
|
||||
if (rule->isRequired())
|
||||
{
|
||||
ValidationResult result = rule->validate(config, directiveName);
|
||||
if (!result.isValidResult())
|
||||
{
|
||||
results_.push_back(result); // Only failures
|
||||
}
|
||||
}
|
||||
}
|
||||
continue; // Directive not present, skip to next
|
||||
}
|
||||
// Validate each rule for this directive
|
||||
for (const auto &rule : rules)
|
||||
{
|
||||
ValidationResult result = rule->validate(config, directiveName); // ✅
|
||||
if (!result.isValidResult())
|
||||
{
|
||||
results_.push_back(result); // Only failures
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ValidationEngine::validateLocationConfig(const std::string &path, const LocationConfig *config)
|
||||
{
|
||||
Log::trace(LOCATION);
|
||||
validateConfig(locationRules_, config);
|
||||
}
|
||||
|
||||
void ValidationEngine::validateServerConfig(const ServerConfig *config)
|
||||
{
|
||||
Log::trace(LOCATION);
|
||||
validateConfig(serverRules_, config);
|
||||
for (const auto &path : config->getLocationPaths())
|
||||
{
|
||||
const LocationConfig *locationConfig = config->getLocation(path);
|
||||
if (locationConfig != nullptr)
|
||||
{
|
||||
validateLocationConfig(path, locationConfig);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ValidationEngine::validateGlobalConfig(const GlobalConfig *config)
|
||||
{
|
||||
Log::trace(LOCATION);
|
||||
validateConfig(globalRules_, config);
|
||||
for (const auto *serverConfig : config->getServerConfigs())
|
||||
{
|
||||
validateServerConfig(serverConfig);
|
||||
}
|
||||
}
|
||||
|
||||
ValidationEngine::ValidationEngine(const GlobalConfig *globalConfig) : globalConfig_(globalConfig)
|
||||
{
|
||||
Log::trace(LOCATION);
|
||||
}
|
||||
|
||||
void ValidationEngine::validate()
|
||||
{
|
||||
Log::trace(LOCATION);
|
||||
if (globalConfig_ != nullptr)
|
||||
{
|
||||
validateGlobalConfig(globalConfig_);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log::warning("No GlobalConfig set for ValidationEngine; skipping validation.");
|
||||
}
|
||||
}
|
||||
50
webserv/config/config_validator/ValidationEngine.hpp
Normal file
50
webserv/config/config_validator/ValidationEngine.hpp
Normal file
@ -0,0 +1,50 @@
|
||||
#pragma once
|
||||
|
||||
#include "webserv/config/GlobalConfig.hpp"
|
||||
#include "webserv/config/config_validator/AValidationRule.hpp"
|
||||
#include "webserv/config/config_validator/ValidationResult.hpp"
|
||||
#include "webserv/config/LocationConfig.hpp"
|
||||
#include "webserv/config/ServerConfig.hpp"
|
||||
#include "webserv/config/AConfig.hpp"
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class ValidationEngine
|
||||
{
|
||||
using RuleMap = std::map<std::string, std::vector<std::unique_ptr<AValidationRule>>>;
|
||||
|
||||
public:
|
||||
ValidationEngine(const GlobalConfig *globalConfig = nullptr);
|
||||
ValidationEngine(const ValidationEngine &other) = delete;
|
||||
ValidationEngine &operator=(const ValidationEngine &other) = delete;
|
||||
ValidationEngine(ValidationEngine &&other) noexcept = delete;
|
||||
ValidationEngine &operator=(ValidationEngine &&other) noexcept = delete;
|
||||
~ValidationEngine() = default;
|
||||
|
||||
void addGlobalRule(const std::string &directiveName, std::unique_ptr<AValidationRule> rule);
|
||||
void addServerRule(const std::string &directiveName, std::unique_ptr<AValidationRule> rule);
|
||||
void addLocationRule(const std::string &directiveName, std::unique_ptr<AValidationRule> rule);
|
||||
|
||||
void validate();
|
||||
|
||||
[[nodiscard]] std::vector<ValidationResult> getValidationResults() const;
|
||||
[[nodiscard]] std::vector<ValidationResult> getErrors() const;
|
||||
[[nodiscard]] std::vector<ValidationResult> getWarnings() const;
|
||||
[[nodiscard]] bool hasErrors() const;
|
||||
|
||||
private:
|
||||
static void addRule(RuleMap &ruleMap, const std::string &directiveName, std::unique_ptr<AValidationRule> rule);
|
||||
|
||||
void validateConfig(RuleMap const &rules, const AConfig *config);
|
||||
void validateGlobalConfig(const GlobalConfig *config);
|
||||
void validateServerConfig(const ServerConfig *config);
|
||||
void validateLocationConfig(const std::string &path, const LocationConfig *config);
|
||||
RuleMap globalRules_;
|
||||
RuleMap serverRules_;
|
||||
RuleMap locationRules_;
|
||||
const GlobalConfig *globalConfig_;
|
||||
|
||||
std::vector<ValidationResult> results_;
|
||||
};
|
||||
36
webserv/config/config_validator/ValidationResult.cpp
Normal file
36
webserv/config/config_validator/ValidationResult.cpp
Normal file
@ -0,0 +1,36 @@
|
||||
#include <webserv/config/config_validator/ValidationResult.hpp>
|
||||
#include <webserv/log/Log.hpp>
|
||||
|
||||
ValidationResult::ValidationResult(Type type, std::string message) : type_(type), message_(std::move(message)) {}
|
||||
|
||||
ValidationResult ValidationResult::success()
|
||||
{
|
||||
return {ValidationResult::Type::SUCCESS};
|
||||
}
|
||||
|
||||
ValidationResult ValidationResult::error(const std::string &message)
|
||||
{
|
||||
Log::error(message);
|
||||
return {ValidationResult::Type::ERROR, message};
|
||||
}
|
||||
|
||||
ValidationResult ValidationResult::warning(const std::string &message)
|
||||
{
|
||||
Log::warning(message);
|
||||
return {ValidationResult::Type::WARNING, message};
|
||||
}
|
||||
|
||||
bool ValidationResult::isValidResult() const
|
||||
{
|
||||
return type_ == Type::SUCCESS;
|
||||
}
|
||||
|
||||
ValidationResult::Type ValidationResult::getType() const
|
||||
{
|
||||
return type_;
|
||||
}
|
||||
|
||||
std::string ValidationResult::getMessage() const
|
||||
{
|
||||
return message_;
|
||||
}
|
||||
@ -1,22 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
|
||||
class ValidationResult
|
||||
{
|
||||
public:
|
||||
enum class Type : uint8_t
|
||||
{
|
||||
SUCCESS,
|
||||
ERROR,
|
||||
WARNING
|
||||
};
|
||||
|
||||
~ValidationResult() = default;
|
||||
|
||||
ValidationResult(const ValidationResult &other) = delete;
|
||||
ValidationResult &operator=(const ValidationResult &other) = delete;
|
||||
ValidationResult(const ValidationResult &other) = default;
|
||||
ValidationResult &operator=(const ValidationResult &other) = default;
|
||||
ValidationResult(ValidationResult &&other) noexcept = default;
|
||||
ValidationResult &operator=(ValidationResult &&other) noexcept = default;
|
||||
|
||||
static ValidationResult success();
|
||||
static ValidationResult error(const std::string &message);
|
||||
static ValidationResult warning(const std::string &message);
|
||||
|
||||
[[nodiscard]] bool isValidResult() const;
|
||||
[[nodiscard]] ValidationResult::Type getType() const ;
|
||||
[[nodiscard]] std::string getMessage() const;
|
||||
|
||||
private:
|
||||
ValidationResult(bool isValid, std::string errorMessage = "");
|
||||
bool isValid;
|
||||
std::string errorMessage;
|
||||
ValidationResult(Type type, std::string message = "");
|
||||
Type type_;
|
||||
std::string message_;
|
||||
};
|
||||
@ -1,6 +1,7 @@
|
||||
#include <webserv/config/ConfigManager.hpp> // for ConfigManager
|
||||
#include <webserv/log/Log.hpp> // for Log, LOCATION
|
||||
#include <webserv/server/Server.hpp> // for Server
|
||||
#include <webserv/config/config_validator/ConfigValidator.hpp> // for ConfigValidator
|
||||
|
||||
#include <iostream> // for basic_ostream, operator<<, cerr, ios_base
|
||||
#include <string> // for basic_string, char_traits, allocator, operator+, operator<=>
|
||||
@ -17,8 +18,19 @@ int main(int argc, char **argv)
|
||||
Log::setStdoutChannel(Log::Level::Info);
|
||||
|
||||
Log::info("\n======================\nStarting webserv...\n======================\n");
|
||||
ConfigManager::getInstance().init(argv[1]); // NOLINT
|
||||
ConfigManager &configManager = ConfigManager::getInstance();
|
||||
configManager.init(argv[1]); // NOLINT
|
||||
|
||||
ConfigValidator validator{configManager.getGlobalConfig()};
|
||||
if (validator.hasErrors())
|
||||
{
|
||||
Log::error("Configuration validation failed with the following errors:");
|
||||
for (const auto &error : validator.getErrors())
|
||||
{
|
||||
Log::error(" - " + error.getMessage());
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
Log::debug("ConfigManager initialized successfully.");
|
||||
Server server(configManager);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user