/src/config/get_config.cc

https://github.com/istio-ecosystem/authservice · C++ · 158 lines · 134 code · 22 blank · 2 comment · 37 complexity · 9faded52a496462993a1b89a58a1fd54 MD5 · raw file

  1. #include "get_config.h"
  2. #include <fmt/ostream.h>
  3. #include <google/protobuf/util/json_util.h>
  4. #include <cassert>
  5. #include <fstream>
  6. #include <iostream>
  7. #include <memory>
  8. #include "config/config.pb.validate.h"
  9. #include "envoy/http/header_map.h"
  10. #include "spdlog/spdlog.h"
  11. #include "src/common/http/http.h"
  12. using namespace std;
  13. using namespace google::protobuf::util;
  14. namespace authservice {
  15. namespace config {
  16. void ConfigValidator::ValidateAll(const Config& config) {
  17. string error;
  18. if (!Validate(config, &error)) {
  19. throw std::runtime_error(error);
  20. }
  21. for (const auto& chain : config.chains()) {
  22. for (const auto& filter : chain.filters()) {
  23. assert(!filter.has_oidc_override());
  24. if (filter.has_mock()) {
  25. continue;
  26. } else if (filter.has_oidc()) {
  27. ConfigValidator::ValidateOIDCConfig(filter.oidc());
  28. continue;
  29. }
  30. // not reached
  31. }
  32. }
  33. }
  34. void ConfigValidator::ValidateOIDCConfig(
  35. const config::oidc::OIDCConfig& config) {
  36. ValidateUri(config.authorization_uri(), "authorization_uri", "https");
  37. ValidateUri(config.callback_uri(), "callback_uri", "https");
  38. ValidateUri(config.token_uri(), "token_uri", "https");
  39. const auto proxy_uri = config.proxy_uri();
  40. if (!proxy_uri.empty()) {
  41. ValidateUri(proxy_uri, "proxy_uri", "http");
  42. }
  43. }
  44. void ConfigValidator::ValidateUri(absl::string_view uri,
  45. absl::string_view uri_type,
  46. absl::string_view required_scheme) {
  47. std::unique_ptr<common::http::Uri> parsed_uri;
  48. try {
  49. parsed_uri = std::make_unique<common::http::Uri>(uri);
  50. } catch (std::runtime_error& e) {
  51. if (std::string(e.what()).find("uri must be http or https scheme") !=
  52. std::string::npos) {
  53. throw std::runtime_error(
  54. fmt::format("invalid {}: uri must be {} scheme: {}", uri_type,
  55. required_scheme, uri));
  56. }
  57. throw std::runtime_error(fmt::format("invalid {}: ", uri_type) + e.what());
  58. }
  59. if (parsed_uri->HasQuery() || parsed_uri->HasFragment()) {
  60. throw std::runtime_error(
  61. fmt::format("invalid {}: query params and fragments not allowed: {}",
  62. uri_type, uri));
  63. }
  64. if (parsed_uri->GetScheme() != required_scheme) {
  65. throw std::runtime_error(
  66. fmt::format("invalid {}: uri must be {} scheme: {}", uri_type,
  67. required_scheme, uri));
  68. }
  69. }
  70. spdlog::level::level_enum GetConfiguredLogLevel(const Config& config) {
  71. auto log_level_string = config.log_level();
  72. spdlog::level::level_enum level;
  73. if (log_level_string == "trace" || log_level_string.empty()) {
  74. level = spdlog::level::level_enum::trace;
  75. } else if (log_level_string == "debug") {
  76. level = spdlog::level::level_enum::debug;
  77. } else if (log_level_string == "info") {
  78. level = spdlog::level::level_enum::info;
  79. } else if (log_level_string == "error") {
  80. level = spdlog::level::level_enum::err;
  81. } else if (log_level_string == "critical") {
  82. level = spdlog::level::level_enum::critical;
  83. } else {
  84. spdlog::error(
  85. "{}: Unexpected log_level config '{}': must be one of [trace, debug, "
  86. "info, error, critical]",
  87. __func__, log_level_string);
  88. abort();
  89. }
  90. return level;
  91. }
  92. unique_ptr<Config> GetConfig(const string& configFileName) {
  93. ifstream configFile(configFileName);
  94. if (!configFile) {
  95. throw runtime_error("failed to open filter config");
  96. }
  97. stringstream buf;
  98. buf << configFile.rdbuf();
  99. configFile.close();
  100. unique_ptr<Config> config(new Config);
  101. auto status = JsonStringToMessage(buf.str(), config.get());
  102. if (!status.ok()) {
  103. throw runtime_error(status.error_message().ToString());
  104. }
  105. const auto has_default_oidc_config = config->has_default_oidc_config();
  106. for (auto& filter_chain : *config->mutable_chains()) {
  107. for (auto& filter : *filter_chain.mutable_filters()) {
  108. if (filter.has_oidc_override()) {
  109. if (!has_default_oidc_config) {
  110. throw std::runtime_error(
  111. "oidc_config must be utilized with default_oidc_config");
  112. }
  113. config::oidc::OIDCConfig new_filter = config->default_oidc_config();
  114. dynamic_cast<google::protobuf::Message*>(&new_filter)
  115. ->MergeFrom(filter.oidc_override());
  116. filter.clear_oidc_override();
  117. *filter.mutable_oidc() = new_filter;
  118. }
  119. }
  120. }
  121. if (has_default_oidc_config) {
  122. config->clear_default_oidc_config();
  123. }
  124. ConfigValidator::ValidateAll(*config);
  125. // Check case-intensive properties
  126. for (auto& filter_chain : *config->mutable_chains()) {
  127. for (auto& filter : *filter_chain.mutable_filters()) {
  128. const auto lower_header =
  129. Envoy::Http::LowerCaseString(filter.oidc().id_token().header());
  130. filter.mutable_oidc()->mutable_id_token()->set_header(lower_header.get());
  131. }
  132. }
  133. return config;
  134. }
  135. } // namespace config
  136. } // namespace authservice