1 //
2 // Copyright 2023 gRPC authors.
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //     http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16 
17 #include <grpc/support/port_platform.h>
18 
19 #include "src/core/ext/xds/xds_audit_logger_registry.h"
20 
21 #include <string>
22 #include <utility>
23 
24 #include "absl/status/status.h"
25 #include "absl/status/statusor.h"
26 #include "absl/strings/string_view.h"
27 #include "absl/types/optional.h"
28 #include "envoy/config/core/v3/extension.upb.h"
29 #include "envoy/config/rbac/v3/rbac.upb.h"
30 
31 #include "src/core/ext/xds/xds_common_types.h"
32 #include "src/core/lib/gprpp/match.h"
33 #include "src/core/lib/gprpp/validation_errors.h"
34 #include "src/core/lib/security/authorization/audit_logging.h"
35 
36 namespace grpc_core {
37 
38 namespace {
39 
40 using experimental::AuditLoggerRegistry;
41 
42 class StdoutLoggerConfigFactory : public XdsAuditLoggerRegistry::ConfigFactory {
43  public:
ConvertXdsAuditLoggerConfig(const XdsResourceType::DecodeContext &,absl::string_view,ValidationErrors *)44   Json::Object ConvertXdsAuditLoggerConfig(
45       const XdsResourceType::DecodeContext& /*context*/,
46       absl::string_view /*configuration*/,
47       ValidationErrors* /*errors*/) override {
48     // Stdout logger has no configuration right now. So we don't process the
49     // config protobuf.
50     return {};
51   }
52 
type()53   absl::string_view type() override { return Type(); }
name()54   absl::string_view name() override { return "stdout_logger"; }
55 
Type()56   static absl::string_view Type() {
57     return "envoy.extensions.rbac.audit_loggers.stream.v3.StdoutAuditLog";
58   }
59 };
60 
61 }  // namespace
62 
XdsAuditLoggerRegistry()63 XdsAuditLoggerRegistry::XdsAuditLoggerRegistry() {
64   audit_logger_config_factories_.emplace(
65       StdoutLoggerConfigFactory::Type(),
66       std::make_unique<StdoutLoggerConfigFactory>());
67 }
68 
ConvertXdsAuditLoggerConfig(const XdsResourceType::DecodeContext & context,const envoy_config_rbac_v3_RBAC_AuditLoggingOptions_AuditLoggerConfig * logger_config,ValidationErrors * errors) const69 Json XdsAuditLoggerRegistry::ConvertXdsAuditLoggerConfig(
70     const XdsResourceType::DecodeContext& context,
71     const envoy_config_rbac_v3_RBAC_AuditLoggingOptions_AuditLoggerConfig*
72         logger_config,
73     ValidationErrors* errors) const {
74   const auto* typed_extension_config =
75       envoy_config_rbac_v3_RBAC_AuditLoggingOptions_AuditLoggerConfig_audit_logger(
76           logger_config);
77   ValidationErrors::ScopedField field(errors, ".audit_logger");
78   if (typed_extension_config == nullptr) {
79     errors->AddError("field not present");
80     return Json();  // A null Json object.
81   }
82   ValidationErrors::ScopedField field2(errors, ".typed_config");
83   const auto* typed_config =
84       envoy_config_core_v3_TypedExtensionConfig_typed_config(
85           typed_extension_config);
86   auto extension = ExtractXdsExtension(context, typed_config, errors);
87   if (!extension.has_value()) return Json();
88   absl::string_view name;
89   Json config;
90   Match(
91       extension->value,
92       // Built-in logger types.
93       [&](absl::string_view serialized_value) {
94         auto it = audit_logger_config_factories_.find(extension->type);
95         if (it == audit_logger_config_factories_.end()) return;
96         name = it->second->name();
97         config = Json::FromObject(it->second->ConvertXdsAuditLoggerConfig(
98             context, serialized_value, errors));
99       },
100       // Custom logger types.
101       [&](Json json) {
102         if (!AuditLoggerRegistry::FactoryExists(extension->type)) return;
103         name = extension->type;
104         config = json;
105       });
106   // Config not found in either case if name is empty.
107   if (name.empty()) {
108     if (!envoy_config_rbac_v3_RBAC_AuditLoggingOptions_AuditLoggerConfig_is_optional(
109             logger_config)) {
110       errors->AddError("unsupported audit logger type");
111     }
112     return Json();
113   }
114   // Validate the converted config.
115   auto result = AuditLoggerRegistry::ParseConfig(name, config);
116   if (!result.ok()) {
117     errors->AddError(result.status().message());
118     return Json();
119   }
120   return Json::FromObject({{std::string(name), std::move(config)}});
121 }
122 }  // namespace grpc_core
123