1 //
2 //
3 // Copyright 2023 gRPC authors.
4 //
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 // http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 //
17 //
18
19 #include "src/core/ext/xds/xds_audit_logger_registry.h"
20
21 #include <memory>
22 #include <string>
23
24 #include <google/protobuf/any.pb.h>
25
26 #include "absl/status/status.h"
27 #include "absl/status/statusor.h"
28 #include "absl/strings/str_format.h"
29 #include "absl/strings/string_view.h"
30 #include "envoy/config/rbac/v3/rbac.upb.h"
31 #include "google/protobuf/struct.pb.h"
32 #include "gtest/gtest.h"
33 #include "upb/mem/arena.hpp"
34 #include "upb/reflection/def.hpp"
35
36 #include <grpc/grpc.h>
37 #include <grpc/grpc_audit_logging.h>
38
39 #include "src/core/ext/xds/xds_bootstrap_grpc.h"
40 #include "src/core/lib/gprpp/crash.h"
41 #include "src/core/lib/json/json.h"
42 #include "src/core/lib/json/json_writer.h"
43 #include "src/core/lib/security/authorization/audit_logging.h"
44 #include "src/proto/grpc/testing/xds/v3/audit_logger_stream.pb.h"
45 #include "src/proto/grpc/testing/xds/v3/extension.pb.h"
46 #include "src/proto/grpc/testing/xds/v3/rbac.pb.h"
47 #include "src/proto/grpc/testing/xds/v3/typed_struct.pb.h"
48 #include "test/core/util/test_config.h"
49
50 namespace grpc_core {
51 namespace testing {
52 namespace {
53
54 using experimental::AuditLogger;
55 using experimental::AuditLoggerFactory;
56 using experimental::AuditLoggerRegistry;
57 using AuditLoggerConfigProto =
58 ::envoy::config::rbac::v3::RBAC::AuditLoggingOptions::AuditLoggerConfig;
59 using ::envoy::extensions::rbac::audit_loggers::stream::v3::StdoutAuditLog;
60 using ::xds::type::v3::TypedStruct;
61
62 constexpr absl::string_view kName = "test_logger";
63
ConvertAuditLoggerConfig(const AuditLoggerConfigProto & config)64 absl::StatusOr<std::string> ConvertAuditLoggerConfig(
65 const AuditLoggerConfigProto& config) {
66 std::string serialized_config = config.SerializeAsString();
67 upb::Arena arena;
68 upb::DefPool def_pool;
69 XdsResourceType::DecodeContext context = {
70 nullptr, GrpcXdsBootstrap::GrpcXdsServer(), nullptr, def_pool.ptr(),
71 arena.ptr()};
72 auto* upb_config =
73 envoy_config_rbac_v3_RBAC_AuditLoggingOptions_AuditLoggerConfig_parse(
74 serialized_config.data(), serialized_config.size(), arena.ptr());
75 ValidationErrors errors;
76 auto config_json = XdsAuditLoggerRegistry().ConvertXdsAuditLoggerConfig(
77 context, upb_config, &errors);
78 if (!errors.ok()) {
79 return errors.status(absl::StatusCode::kInvalidArgument,
80 "validation errors");
81 }
82 return JsonDump(config_json);
83 }
84
85 class TestAuditLoggerFactory : public AuditLoggerFactory {
86 public:
name() const87 absl::string_view name() const override { return kName; }
88 absl::StatusOr<std::unique_ptr<AuditLoggerFactory::Config>>
ParseAuditLoggerConfig(const Json & json)89 ParseAuditLoggerConfig(const Json& json) override {
90 if (json.object().find("bad") != json.object().end()) {
91 return absl::InvalidArgumentError("invalid test_logger config");
92 }
93 return nullptr;
94 }
CreateAuditLogger(std::unique_ptr<AuditLoggerFactory::Config>)95 std::unique_ptr<AuditLogger> CreateAuditLogger(
96 std::unique_ptr<AuditLoggerFactory::Config>) override {
97 Crash("unreachable");
98 return nullptr;
99 }
100 };
101
102 class XdsAuditLoggerRegistryTest : public ::testing::Test {
103 protected:
SetUp()104 void SetUp() override {
105 AuditLoggerRegistry::RegisterFactory(
106 std::make_unique<TestAuditLoggerFactory>());
107 }
108
TearDown()109 void TearDown() override { AuditLoggerRegistry::TestOnlyResetRegistry(); }
110 };
111
112 //
113 // StdoutLoggerTest
114 //
115
TEST(StdoutLoggerTest,BasicStdoutLogger)116 TEST(StdoutLoggerTest, BasicStdoutLogger) {
117 AuditLoggerConfigProto config;
118 config.mutable_audit_logger()->mutable_typed_config()->PackFrom(
119 StdoutAuditLog());
120 auto result = ConvertAuditLoggerConfig(config);
121 ASSERT_TRUE(result.ok()) << result.status();
122 EXPECT_EQ(*result, "{\"stdout_logger\":{}}");
123 }
124
125 //
126 // ThirdPartyLoggerTest
127 //
128
TEST_F(XdsAuditLoggerRegistryTest,ValidThirdPartyLogger)129 TEST_F(XdsAuditLoggerRegistryTest, ValidThirdPartyLogger) {
130 AuditLoggerConfigProto config;
131 TypedStruct logger;
132 logger.set_type_url(absl::StrFormat("myorg/foo/bar/%s", kName));
133 auto* fields = logger.mutable_value()->mutable_fields();
134 (*fields)["foo"].set_string_value("bar");
135 config.mutable_audit_logger()->mutable_typed_config()->PackFrom(logger);
136 auto result = ConvertAuditLoggerConfig(config);
137 ASSERT_TRUE(result.ok()) << result.status();
138 EXPECT_EQ(*result, "{\"test_logger\":{\"foo\":\"bar\"}}");
139 }
140
TEST_F(XdsAuditLoggerRegistryTest,InvalidThirdPartyLoggerConfig)141 TEST_F(XdsAuditLoggerRegistryTest, InvalidThirdPartyLoggerConfig) {
142 AuditLoggerConfigProto config;
143 TypedStruct logger;
144 logger.set_type_url(absl::StrFormat("myorg/foo/bar/%s", kName));
145 auto* fields = logger.mutable_value()->mutable_fields();
146 (*fields)["bad"].set_string_value("true");
147 config.mutable_audit_logger()->mutable_typed_config()->PackFrom(logger);
148 auto result = ConvertAuditLoggerConfig(config);
149 EXPECT_EQ(result.status().code(), absl::StatusCode::kInvalidArgument);
150 EXPECT_EQ(result.status().message(),
151 "validation errors: "
152 "[field:audit_logger.typed_config.value"
153 "[xds.type.v3.TypedStruct].value[test_logger] "
154 "error:invalid test_logger config]")
155 << result.status();
156 }
157
158 //
159 // XdsAuditLoggerRegistryTest
160 //
161
TEST_F(XdsAuditLoggerRegistryTest,EmptyAuditLoggerConfig)162 TEST_F(XdsAuditLoggerRegistryTest, EmptyAuditLoggerConfig) {
163 auto result = ConvertAuditLoggerConfig(AuditLoggerConfigProto());
164 EXPECT_EQ(result.status().code(), absl::StatusCode::kInvalidArgument);
165 EXPECT_EQ(result.status().message(),
166 "validation errors: [field:audit_logger error:field not present]")
167 << result.status();
168 }
169
TEST_F(XdsAuditLoggerRegistryTest,MissingTypedConfig)170 TEST_F(XdsAuditLoggerRegistryTest, MissingTypedConfig) {
171 AuditLoggerConfigProto config;
172 config.mutable_audit_logger();
173 auto result = ConvertAuditLoggerConfig(config);
174 EXPECT_EQ(result.status().code(), absl::StatusCode::kInvalidArgument);
175 EXPECT_EQ(result.status().message(),
176 "validation errors: [field:audit_logger.typed_config error:field "
177 "not present]")
178 << result.status();
179 }
180
TEST_F(XdsAuditLoggerRegistryTest,NoSupportedType)181 TEST_F(XdsAuditLoggerRegistryTest, NoSupportedType) {
182 AuditLoggerConfigProto config;
183 config.mutable_audit_logger()->mutable_typed_config()->PackFrom(
184 AuditLoggerConfigProto());
185 auto result = ConvertAuditLoggerConfig(config);
186 EXPECT_EQ(result.status().code(), absl::StatusCode::kInvalidArgument);
187 EXPECT_EQ(result.status().message(),
188 "validation errors: "
189 "[field:audit_logger.typed_config.value[envoy.config.rbac.v3.RBAC."
190 "AuditLoggingOptions.AuditLoggerConfig] error:unsupported audit "
191 "logger type]")
192 << result.status();
193 }
194
TEST_F(XdsAuditLoggerRegistryTest,NoSupportedTypeButIsOptional)195 TEST_F(XdsAuditLoggerRegistryTest, NoSupportedTypeButIsOptional) {
196 AuditLoggerConfigProto config;
197 config.mutable_audit_logger()->mutable_typed_config()->PackFrom(
198 AuditLoggerConfigProto());
199 config.set_is_optional(true);
200 auto result = ConvertAuditLoggerConfig(config);
201 EXPECT_EQ(result.status().code(), absl::StatusCode::kOk);
202 EXPECT_EQ(*result, "null");
203 }
204
205 } // namespace
206 } // namespace testing
207 } // namespace grpc_core
208
main(int argc,char ** argv)209 int main(int argc, char** argv) {
210 ::testing::InitGoogleTest(&argc, argv);
211 grpc::testing::TestEnvironment env(&argc, argv);
212 grpc_init();
213 auto result = RUN_ALL_TESTS();
214 grpc_shutdown();
215 return result;
216 }
217