Add File Watcher Certificate Provider API
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 5bfb16b..5d07b41 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -830,6 +830,7 @@
add_dependencies(buildtests_cxx google_mesh_ca_certificate_provider_factory_test)
add_dependencies(buildtests_cxx grpc_cli)
add_dependencies(buildtests_cxx grpc_tls_certificate_distributor_test)
+ add_dependencies(buildtests_cxx grpc_tls_certificate_provider_test)
add_dependencies(buildtests_cxx grpc_tls_credentials_options_test)
if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
add_dependencies(buildtests_cxx grpc_tool_test)
@@ -11696,6 +11697,7 @@
add_executable(grpc_tls_certificate_distributor_test
test/core/security/grpc_tls_certificate_distributor_test.cc
+ test/core/security/tls_utils.cc
third_party/googletest/googletest/src/gtest-all.cc
third_party/googletest/googlemock/src/gmock-all.cc
)
@@ -11732,8 +11734,48 @@
endif()
if(gRPC_BUILD_TESTS)
+add_executable(grpc_tls_certificate_provider_test
+ test/core/security/grpc_tls_certificate_provider_test.cc
+ test/core/security/tls_utils.cc
+ third_party/googletest/googletest/src/gtest-all.cc
+ third_party/googletest/googlemock/src/gmock-all.cc
+)
+
+target_include_directories(grpc_tls_certificate_provider_test
+ PRIVATE
+ ${CMAKE_CURRENT_SOURCE_DIR}
+ ${CMAKE_CURRENT_SOURCE_DIR}/include
+ ${_gRPC_ADDRESS_SORTING_INCLUDE_DIR}
+ ${_gRPC_RE2_INCLUDE_DIR}
+ ${_gRPC_SSL_INCLUDE_DIR}
+ ${_gRPC_UPB_GENERATED_DIR}
+ ${_gRPC_UPB_GRPC_GENERATED_DIR}
+ ${_gRPC_UPB_INCLUDE_DIR}
+ ${_gRPC_ZLIB_INCLUDE_DIR}
+ third_party/googletest/googletest/include
+ third_party/googletest/googletest
+ third_party/googletest/googlemock/include
+ third_party/googletest/googlemock
+ ${_gRPC_PROTO_GENS_DIR}
+)
+
+target_link_libraries(grpc_tls_certificate_provider_test
+ ${_gRPC_PROTOBUF_LIBRARIES}
+ ${_gRPC_ALLTARGETS_LIBRARIES}
+ grpc_test_util
+ grpc
+ gpr
+ address_sorting
+ upb
+)
+
+
+endif()
+if(gRPC_BUILD_TESTS)
+
add_executable(grpc_tls_credentials_options_test
test/core/security/grpc_tls_credentials_options_test.cc
+ test/core/security/tls_utils.cc
third_party/googletest/googletest/src/gtest-all.cc
third_party/googletest/googlemock/src/gmock-all.cc
)
diff --git a/build_autogenerated.yaml b/build_autogenerated.yaml
index cd73d4b..4e94a38 100644
--- a/build_autogenerated.yaml
+++ b/build_autogenerated.yaml
@@ -6208,9 +6208,26 @@
gtest: true
build: test
language: c++
- headers: []
+ headers:
+ - test/core/security/tls_utils.h
src:
- test/core/security/grpc_tls_certificate_distributor_test.cc
+ - test/core/security/tls_utils.cc
+ deps:
+ - grpc_test_util
+ - grpc
+ - gpr
+ - address_sorting
+ - upb
+- name: grpc_tls_certificate_provider_test
+ gtest: true
+ build: test
+ language: c++
+ headers:
+ - test/core/security/tls_utils.h
+ src:
+ - test/core/security/grpc_tls_certificate_provider_test.cc
+ - test/core/security/tls_utils.cc
deps:
- grpc_test_util
- grpc
@@ -6221,9 +6238,11 @@
gtest: true
build: test
language: c++
- headers: []
+ headers:
+ - test/core/security/tls_utils.h
src:
- test/core/security/grpc_tls_credentials_options_test.cc
+ - test/core/security/tls_utils.cc
deps:
- grpc_test_util
- grpc
diff --git a/grpc.def b/grpc.def
index aecf99a..1328923 100644
--- a/grpc.def
+++ b/grpc.def
@@ -139,6 +139,7 @@
grpc_tls_identity_pairs_add_pair
grpc_tls_identity_pairs_destroy
grpc_tls_certificate_provider_static_data_create
+ grpc_tls_certificate_provider_file_watcher_create
grpc_tls_certificate_provider_release
grpc_tls_credentials_options_create
grpc_tls_credentials_options_set_cert_request_type
diff --git a/include/grpc/grpc_security.h b/include/grpc/grpc_security.h
index 12f39ec..1482d7c 100644
--- a/include/grpc/grpc_security.h
+++ b/include/grpc/grpc_security.h
@@ -808,6 +808,31 @@
const char* root_certificate, grpc_tls_identity_pairs* pem_key_cert_pairs);
/**
+ * Creates a grpc_tls_certificate_provider that will watch the credential
+ * changes on the file system. This provider will always return the up-to-date
+ * cert data for all the cert names callers set through
+ * |grpc_tls_credentials_options|. Note that this API only supports one key-cert
+ * file and hence one set of identity key-cert pair, so SNI(Server Name
+ * Indication) is not supported.
+ * - private_key_path is the file path of the private key. This must be set if
+ * |identity_certificate_path| is set. Otherwise, it could be null if no
+ * identity credentials are needed.
+ * - identity_certificate_path is the file path of the identity certificate
+ * chain. This must be set if |private_key_path| is set. Otherwise, it could
+ * be null if no identity credentials are needed.
+ * - root_cert_path is the file path to the root certificate bundle. This
+ * may be null if no root certs are needed.
+ * - refresh_interval_sec is the refreshing interval that we will check the
+ * files for updates.
+ * It does not take ownership of parameters.
+ * It is used for experimental purpose for now and subject to change.
+ */
+GRPCAPI grpc_tls_certificate_provider*
+grpc_tls_certificate_provider_file_watcher_create(
+ const char* private_key_path, const char* identity_certificate_path,
+ const char* root_cert_path, unsigned int refresh_interval_sec);
+
+/**
* Releases a grpc_tls_certificate_provider object. The creator of the
* grpc_tls_certificate_provider object is responsible for its release. It is
* used for experimental purpose for now and subject to change.
diff --git a/include/grpcpp/security/tls_certificate_provider.h b/include/grpcpp/security/tls_certificate_provider.h
index 797687c..ec006f3 100644
--- a/include/grpcpp/security/tls_certificate_provider.h
+++ b/include/grpcpp/security/tls_certificate_provider.h
@@ -66,7 +66,57 @@
const std::vector<IdentityKeyCertPair>& identity_key_cert_pairs)
: StaticDataCertificateProvider("", identity_key_cert_pairs) {}
- ~StaticDataCertificateProvider();
+ ~StaticDataCertificateProvider() override;
+
+ grpc_tls_certificate_provider* c_provider() override { return c_provider_; }
+
+ private:
+ grpc_tls_certificate_provider* c_provider_ = nullptr;
+};
+
+// A CertificateProviderInterface implementation that will watch the credential
+// changes on the file system. This provider will always return the up-to-date
+// cert data for all the cert names callers set through |TlsCredentialsOptions|.
+// Several things to note:
+// 1. This API only supports one key-cert file and hence one set of identity
+// key-cert pair, so SNI(Server Name Indication) is not supported.
+// 2. The private key and identity certificate should always match. This API
+// guarantees atomic read, and it is the callers' responsibility to do atomic
+// updates. There are many ways to atomically update the key and certs in the
+// file system. To name a few:
+// 1) creating a new directory, renaming the old directory to a new name, and
+// then renaming the new directory to the original name of the old directory.
+// 2) using a symlink for the directory. When need to change, put new
+// credential data in a new directory, and change symlink.
+class FileWatcherCertificateProvider final
+ : public CertificateProviderInterface {
+ public:
+ // Constructor to get credential updates from root and identity file paths.
+ //
+ // @param private_key_path is the file path of the private key.
+ // @param identity_certificate_path is the file path of the identity
+ // certificate chain.
+ // @param root_cert_path is the file path to the root certificate bundle.
+ // @param refresh_interval_sec is the refreshing interval that we will check
+ // the files for updates.
+ FileWatcherCertificateProvider(const std::string& private_key_path,
+ const std::string& identity_certificate_path,
+ const std::string& root_cert_path,
+ unsigned int refresh_interval_sec);
+ // Constructor to get credential updates from identity file paths only.
+ FileWatcherCertificateProvider(const std::string& private_key_path,
+ const std::string& identity_certificate_path,
+ unsigned int refresh_interval_sec)
+ : FileWatcherCertificateProvider(private_key_path,
+ identity_certificate_path, "",
+ refresh_interval_sec) {}
+ // Constructor to get credential updates from root file path only.
+ FileWatcherCertificateProvider(const std::string& root_cert_path,
+ unsigned int refresh_interval_sec)
+ : FileWatcherCertificateProvider("", "", root_cert_path,
+ refresh_interval_sec) {}
+
+ ~FileWatcherCertificateProvider() override;
grpc_tls_certificate_provider* c_provider() override { return c_provider_; }
diff --git a/src/core/lib/security/credentials/tls/grpc_tls_certificate_distributor.h b/src/core/lib/security/credentials/tls/grpc_tls_certificate_distributor.h
index 35451e7..9ce9443 100644
--- a/src/core/lib/security/credentials/tls/grpc_tls_certificate_distributor.h
+++ b/src/core/lib/security/credentials/tls/grpc_tls_certificate_distributor.h
@@ -72,12 +72,7 @@
grpc_error* identity_cert_error) = 0;
};
- // Sets the key materials based on their certificate name. Note that we are
- // not doing any copies for pem_root_certs and pem_key_cert_pairs. For
- // pem_root_certs, the original string contents need to outlive the
- // distributor; for pem_key_cert_pairs, internally it is taking two
- // unique_ptr(s) to the credential string, so the ownership is actually
- // transferred.
+ // Sets the key materials based on their certificate name.
//
// @param cert_name The name of the certificates being updated.
// @param pem_root_certs The content of root certificates.
diff --git a/src/core/lib/security/credentials/tls/grpc_tls_certificate_provider.cc b/src/core/lib/security/credentials/tls/grpc_tls_certificate_provider.cc
index 80ea4ea..9e69d98 100644
--- a/src/core/lib/security/credentials/tls/grpc_tls_certificate_provider.cc
+++ b/src/core/lib/security/credentials/tls/grpc_tls_certificate_provider.cc
@@ -22,6 +22,8 @@
#include <grpc/support/log.h>
#include <grpc/support/string_util.h>
+#include "src/core/lib/gprpp/stat.h"
+#include "src/core/lib/slice/slice_internal.h"
#include "src/core/lib/surface/api_trace.h"
namespace grpc_core {
@@ -35,20 +37,334 @@
distributor_->SetWatchStatusCallback([this](std::string cert_name,
bool root_being_watched,
bool identity_being_watched) {
- if (!root_being_watched && !identity_being_watched) return;
+ grpc_core::MutexLock lock(&mu_);
absl::optional<std::string> root_certificate;
absl::optional<grpc_core::PemKeyCertPairList> pem_key_cert_pairs;
- if (root_being_watched) {
+ StaticDataCertificateProvider::WatcherInfo& info = watcher_info_[cert_name];
+ if (!info.root_being_watched && root_being_watched &&
+ !root_certificate_.empty()) {
root_certificate = root_certificate_;
}
- if (identity_being_watched) {
+ info.root_being_watched = root_being_watched;
+ if (!info.identity_being_watched && identity_being_watched &&
+ !pem_key_cert_pairs_.empty()) {
pem_key_cert_pairs = pem_key_cert_pairs_;
}
- distributor_->SetKeyMaterials(cert_name, std::move(root_certificate),
- std::move(pem_key_cert_pairs));
+ info.identity_being_watched = identity_being_watched;
+ if (!info.root_being_watched && !info.identity_being_watched) {
+ watcher_info_.erase(cert_name);
+ }
+ const bool root_has_update = root_certificate.has_value();
+ const bool identity_has_update = pem_key_cert_pairs.has_value();
+ if (root_has_update || identity_has_update) {
+ distributor_->SetKeyMaterials(cert_name, std::move(root_certificate),
+ std::move(pem_key_cert_pairs));
+ }
+ grpc_error* root_cert_error = GRPC_ERROR_NONE;
+ grpc_error* identity_cert_error = GRPC_ERROR_NONE;
+ if (root_being_watched && !root_has_update) {
+ root_cert_error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+ "Unable to get latest root certificates.");
+ }
+ if (identity_being_watched && !identity_has_update) {
+ identity_cert_error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+ "Unable to get latest identity certificates.");
+ }
+ if (root_cert_error != GRPC_ERROR_NONE ||
+ identity_cert_error != GRPC_ERROR_NONE) {
+ distributor_->SetErrorForCert(cert_name, root_cert_error,
+ identity_cert_error);
+ }
});
}
+StaticDataCertificateProvider::~StaticDataCertificateProvider() {
+ // Reset distributor's callback to make sure the callback won't be invoked
+ // again after this object(provider) is destroyed.
+ distributor_->SetWatchStatusCallback(nullptr);
+}
+
+namespace {
+
+gpr_timespec TimeoutSecondsToDeadline(int64_t seconds) {
+ return gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC),
+ gpr_time_from_seconds(seconds, GPR_TIMESPAN));
+}
+
+} // namespace
+
+FileWatcherCertificateProvider::FileWatcherCertificateProvider(
+ std::string private_key_path, std::string identity_certificate_path,
+ std::string root_cert_path, unsigned int refresh_interval_sec)
+ : private_key_path_(std::move(private_key_path)),
+ identity_certificate_path_(std::move(identity_certificate_path)),
+ root_cert_path_(std::move(root_cert_path)),
+ refresh_interval_sec_(refresh_interval_sec),
+ distributor_(MakeRefCounted<grpc_tls_certificate_distributor>()) {
+ // Private key and identity cert files must be both set or both unset.
+ GPR_ASSERT(private_key_path_.empty() == identity_certificate_path_.empty());
+ // Must be watching either root or identity certs.
+ GPR_ASSERT(!private_key_path_.empty() || !root_cert_path_.empty());
+ gpr_event_init(&shutdown_event_);
+ ForceUpdate();
+ auto thread_lambda = [](void* arg) {
+ FileWatcherCertificateProvider* provider =
+ static_cast<FileWatcherCertificateProvider*>(arg);
+ GPR_ASSERT(provider != nullptr);
+ while (true) {
+ void* value = gpr_event_wait(
+ &provider->shutdown_event_,
+ TimeoutSecondsToDeadline(provider->refresh_interval_sec_));
+ if (value != nullptr) {
+ return;
+ };
+ provider->ForceUpdate();
+ }
+ };
+ refresh_thread_ = grpc_core::Thread(
+ "FileWatcherCertificateProvider_refreshing_thread", thread_lambda, this);
+ refresh_thread_.Start();
+ distributor_->SetWatchStatusCallback([this](std::string cert_name,
+ bool root_being_watched,
+ bool identity_being_watched) {
+ grpc_core::MutexLock lock(&mu_);
+ absl::optional<std::string> root_certificate;
+ absl::optional<grpc_core::PemKeyCertPairList> pem_key_cert_pairs;
+ FileWatcherCertificateProvider::WatcherInfo& info =
+ watcher_info_[cert_name];
+ if (!info.root_being_watched && root_being_watched &&
+ !root_certificate_.empty()) {
+ root_certificate = root_certificate_;
+ }
+ info.root_being_watched = root_being_watched;
+ if (!info.identity_being_watched && identity_being_watched &&
+ !pem_key_cert_pairs_.empty()) {
+ pem_key_cert_pairs = pem_key_cert_pairs_;
+ }
+ info.identity_being_watched = identity_being_watched;
+ if (!info.root_being_watched && !info.identity_being_watched) {
+ watcher_info_.erase(cert_name);
+ }
+ ExecCtx exec_ctx;
+ if (root_certificate.has_value() || pem_key_cert_pairs.has_value()) {
+ distributor_->SetKeyMaterials(cert_name, root_certificate,
+ pem_key_cert_pairs);
+ }
+ grpc_error* root_cert_error = GRPC_ERROR_NONE;
+ grpc_error* identity_cert_error = GRPC_ERROR_NONE;
+ if (root_being_watched && !root_certificate.has_value()) {
+ root_cert_error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+ "Unable to get latest root certificates.");
+ }
+ if (identity_being_watched && !pem_key_cert_pairs.has_value()) {
+ identity_cert_error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+ "Unable to get latest identity certificates.");
+ }
+ if (root_cert_error != GRPC_ERROR_NONE ||
+ identity_cert_error != GRPC_ERROR_NONE) {
+ distributor_->SetErrorForCert(cert_name, root_cert_error,
+ identity_cert_error);
+ }
+ });
+}
+
+FileWatcherCertificateProvider::~FileWatcherCertificateProvider() {
+ // Reset distributor's callback to make sure the callback won't be invoked
+ // again after this object(provider) is destroyed.
+ distributor_->SetWatchStatusCallback(nullptr);
+ gpr_event_set(&shutdown_event_, (void*)(1));
+ refresh_thread_.Join();
+}
+
+void FileWatcherCertificateProvider::ForceUpdate() {
+ absl::optional<std::string> root_certificate;
+ absl::optional<grpc_core::PemKeyCertPairList> pem_key_cert_pairs;
+ if (!root_cert_path_.empty()) {
+ root_certificate = ReadRootCertificatesFromFile(root_cert_path_);
+ }
+ if (!private_key_path_.empty()) {
+ pem_key_cert_pairs = ReadIdentityKeyCertPairFromFiles(
+ private_key_path_, identity_certificate_path_);
+ }
+ grpc_core::MutexLock lock(&mu_);
+ const bool root_cert_changed =
+ (!root_certificate.has_value() && !root_certificate_.empty()) ||
+ (root_certificate.has_value() && root_certificate_ != *root_certificate);
+ if (root_cert_changed) {
+ if (root_certificate.has_value()) {
+ root_certificate_ = std::move(*root_certificate);
+ } else {
+ root_certificate_ = "";
+ }
+ }
+ const bool identity_cert_changed =
+ (!pem_key_cert_pairs.has_value() && !pem_key_cert_pairs_.empty()) ||
+ (pem_key_cert_pairs.has_value() &&
+ pem_key_cert_pairs_ != *pem_key_cert_pairs);
+ if (identity_cert_changed) {
+ if (pem_key_cert_pairs.has_value()) {
+ pem_key_cert_pairs_ = std::move(*pem_key_cert_pairs);
+ } else {
+ pem_key_cert_pairs_ = {};
+ }
+ }
+ if (root_cert_changed || identity_cert_changed) {
+ ExecCtx exec_ctx;
+ grpc_error* root_cert_error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+ "Unable to get latest root certificates.");
+ grpc_error* identity_cert_error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+ "Unable to get latest identity certificates.");
+ for (const auto& p : watcher_info_) {
+ const std::string& cert_name = p.first;
+ const WatcherInfo& info = p.second;
+ absl::optional<std::string> root_to_report;
+ absl::optional<grpc_core::PemKeyCertPairList> identity_to_report;
+ // Set key materials to the distributor if their contents changed.
+ if (info.root_being_watched && !root_certificate_.empty() &&
+ root_cert_changed) {
+ root_to_report = root_certificate_;
+ }
+ if (info.identity_being_watched && !pem_key_cert_pairs_.empty() &&
+ identity_cert_changed) {
+ identity_to_report = pem_key_cert_pairs_;
+ }
+ if (root_to_report.has_value() || identity_to_report.has_value()) {
+ distributor_->SetKeyMaterials(cert_name, std::move(root_to_report),
+ std::move(identity_to_report));
+ }
+ // Report errors to the distributor if the contents are empty.
+ const bool report_root_error =
+ info.root_being_watched && root_certificate_.empty();
+ const bool report_identity_error =
+ info.identity_being_watched && pem_key_cert_pairs_.empty();
+ if (report_root_error || report_identity_error) {
+ distributor_->SetErrorForCert(
+ cert_name,
+ report_root_error ? GRPC_ERROR_REF(root_cert_error)
+ : GRPC_ERROR_NONE,
+ report_identity_error ? GRPC_ERROR_REF(identity_cert_error)
+ : GRPC_ERROR_NONE);
+ }
+ }
+ GRPC_ERROR_UNREF(root_cert_error);
+ GRPC_ERROR_UNREF(identity_cert_error);
+ }
+}
+
+absl::optional<std::string>
+FileWatcherCertificateProvider::ReadRootCertificatesFromFile(
+ const std::string& root_cert_full_path) {
+ // Read the root file.
+ grpc_slice root_slice = grpc_empty_slice();
+ grpc_error* root_error =
+ grpc_load_file(root_cert_full_path.c_str(), 0, &root_slice);
+ if (root_error != GRPC_ERROR_NONE) {
+ gpr_log(GPR_ERROR, "Reading file %s failed: %s",
+ root_cert_full_path.c_str(), grpc_error_string(root_error));
+ GRPC_ERROR_UNREF(root_error);
+ return absl::nullopt;
+ }
+ std::string root_cert(StringViewFromSlice(root_slice));
+ grpc_slice_unref_internal(root_slice);
+ return root_cert;
+}
+
+namespace {
+
+// This helper function gets the last-modified time of |filename|. When failed,
+// it logs the error and returns 0.
+time_t GetModificationTime(const char* filename) {
+ time_t ts = 0;
+ absl::Status status = grpc_core::GetFileModificationTime(filename, &ts);
+ return ts;
+}
+
+} // namespace
+
+absl::optional<PemKeyCertPairList>
+FileWatcherCertificateProvider::ReadIdentityKeyCertPairFromFiles(
+ const std::string& private_key_path,
+ const std::string& identity_certificate_path) {
+ struct SliceWrapper {
+ grpc_slice slice = grpc_empty_slice();
+ ~SliceWrapper() { grpc_slice_unref_internal(slice); }
+ };
+ const int kNumRetryAttempts = 3;
+ for (int i = 0; i < kNumRetryAttempts; ++i) {
+ // TODO(ZhenLian): replace the timestamp approach with key-match approach
+ // once the latter is implemented.
+ // Checking the last modification of identity files before reading.
+ time_t identity_key_ts_before =
+ GetModificationTime(private_key_path.c_str());
+ if (identity_key_ts_before == 0) {
+ gpr_log(
+ GPR_ERROR,
+ "Failed to get the file's modification time of %s. Start retrying...",
+ private_key_path.c_str());
+ continue;
+ }
+ time_t identity_cert_ts_before =
+ GetModificationTime(identity_certificate_path.c_str());
+ if (identity_cert_ts_before == 0) {
+ gpr_log(
+ GPR_ERROR,
+ "Failed to get the file's modification time of %s. Start retrying...",
+ identity_certificate_path.c_str());
+ continue;
+ }
+ // Read the identity files.
+ SliceWrapper key_slice, cert_slice;
+ grpc_error* key_error =
+ grpc_load_file(private_key_path.c_str(), 0, &key_slice.slice);
+ if (key_error != GRPC_ERROR_NONE) {
+ gpr_log(GPR_ERROR, "Reading file %s failed: %s. Start retrying...",
+ private_key_path.c_str(), grpc_error_string(key_error));
+ GRPC_ERROR_UNREF(key_error);
+ continue;
+ }
+ grpc_error* cert_error =
+ grpc_load_file(identity_certificate_path.c_str(), 0, &cert_slice.slice);
+ if (cert_error != GRPC_ERROR_NONE) {
+ gpr_log(GPR_ERROR, "Reading file %s failed: %s. Start retrying...",
+ identity_certificate_path.c_str(), grpc_error_string(cert_error));
+ GRPC_ERROR_UNREF(cert_error);
+ continue;
+ }
+ std::string private_key(StringViewFromSlice(key_slice.slice));
+ std::string cert_chain(StringViewFromSlice(cert_slice.slice));
+ grpc_ssl_pem_key_cert_pair* ssl_pair =
+ static_cast<grpc_ssl_pem_key_cert_pair*>(
+ gpr_malloc(sizeof(grpc_ssl_pem_key_cert_pair)));
+ ssl_pair->private_key = gpr_strdup(private_key.c_str());
+ ssl_pair->cert_chain = gpr_strdup(cert_chain.c_str());
+ PemKeyCertPairList identity_pairs;
+ identity_pairs.emplace_back(ssl_pair);
+ // Checking the last modification of identity files before reading.
+ time_t identity_key_ts_after =
+ GetModificationTime(private_key_path.c_str());
+ if (identity_key_ts_before != identity_key_ts_after) {
+ gpr_log(GPR_ERROR,
+ "Last modified time before and after reading %s is not the same. "
+ "Start retrying...",
+ private_key_path.c_str());
+ continue;
+ }
+ time_t identity_cert_ts_after =
+ GetModificationTime(identity_certificate_path.c_str());
+ if (identity_cert_ts_before != identity_cert_ts_after) {
+ gpr_log(GPR_ERROR,
+ "Last modified time before and after reading %s is not the same. "
+ "Start retrying...",
+ identity_certificate_path.c_str());
+ continue;
+ }
+ return identity_pairs;
+ }
+ gpr_log(GPR_ERROR,
+ "All retry attempts failed. Will try again after the next interval.");
+ return absl::nullopt;
+}
+
} // namespace grpc_core
/** -- Wrapper APIs declared in grpc_security.h -- **/
@@ -69,6 +385,16 @@
std::move(root_cert_core), std::move(identity_pairs_core));
}
+grpc_tls_certificate_provider*
+grpc_tls_certificate_provider_file_watcher_create(
+ const char* private_key_path, const char* identity_certificate_path,
+ const char* root_cert_path, unsigned int refresh_interval_sec) {
+ return new grpc_core::FileWatcherCertificateProvider(
+ private_key_path == nullptr ? "" : private_key_path,
+ identity_certificate_path == nullptr ? "" : identity_certificate_path,
+ root_cert_path == nullptr ? "" : root_cert_path, refresh_interval_sec);
+}
+
void grpc_tls_certificate_provider_release(
grpc_tls_certificate_provider* provider) {
GRPC_API_TRACE("grpc_tls_certificate_provider_release(provider=%p)", 1,
diff --git a/src/core/lib/security/credentials/tls/grpc_tls_certificate_provider.h b/src/core/lib/security/credentials/tls/grpc_tls_certificate_provider.h
index dae6fd2..20fbb16 100644
--- a/src/core/lib/security/credentials/tls/grpc_tls_certificate_provider.h
+++ b/src/core/lib/security/credentials/tls/grpc_tls_certificate_provider.h
@@ -26,6 +26,8 @@
#include "src/core/lib/gprpp/ref_counted.h"
#include "src/core/lib/gprpp/ref_counted_ptr.h"
+#include "src/core/lib/gprpp/thd.h"
+#include "src/core/lib/iomgr/load_file.h"
#include "src/core/lib/iomgr/pollset_set.h"
#include "src/core/lib/security/credentials/tls/grpc_tls_certificate_distributor.h"
#include "src/core/lib/security/security_connector/ssl_utils.h"
@@ -59,14 +61,76 @@
std::string root_certificate,
grpc_core::PemKeyCertPairList pem_key_cert_pairs);
+ ~StaticDataCertificateProvider() override;
+
RefCountedPtr<grpc_tls_certificate_distributor> distributor() const override {
return distributor_;
}
private:
+ struct WatcherInfo {
+ bool root_being_watched = false;
+ bool identity_being_watched = false;
+ };
RefCountedPtr<grpc_tls_certificate_distributor> distributor_;
std::string root_certificate_;
grpc_core::PemKeyCertPairList pem_key_cert_pairs_;
+ // Guards members below.
+ grpc_core::Mutex mu_;
+ // Stores each cert_name we get from the distributor callback and its watcher
+ // information.
+ std::map<std::string, WatcherInfo> watcher_info_;
+};
+
+// A provider class that will watch the credential changes on the file system.
+class FileWatcherCertificateProvider final
+ : public grpc_tls_certificate_provider {
+ public:
+ FileWatcherCertificateProvider(std::string private_key_path,
+ std::string identity_certificate_path,
+ std::string root_cert_path,
+ unsigned int refresh_interval_sec);
+
+ ~FileWatcherCertificateProvider() override;
+
+ RefCountedPtr<grpc_tls_certificate_distributor> distributor() const override {
+ return distributor_;
+ }
+
+ private:
+ struct WatcherInfo {
+ bool root_being_watched = false;
+ bool identity_being_watched = false;
+ };
+ // Force an update from the file system regardless of the interval.
+ void ForceUpdate();
+ // Read the root certificates from files and update the distributor.
+ absl::optional<std::string> ReadRootCertificatesFromFile(
+ const std::string& root_cert_full_path);
+ // Read the root certificates from files and update the distributor.
+ absl::optional<PemKeyCertPairList> ReadIdentityKeyCertPairFromFiles(
+ const std::string& private_key_file_name,
+ const std::string& identity_certificate_file_name);
+
+ // Information that is used by the refreshing thread.
+ std::string private_key_path_;
+ std::string identity_certificate_path_;
+ std::string root_cert_path_;
+ unsigned int refresh_interval_sec_ = 0;
+
+ RefCountedPtr<grpc_tls_certificate_distributor> distributor_;
+ grpc_core::Thread refresh_thread_;
+ gpr_event shutdown_event_;
+
+ // Guards members below.
+ grpc_core::Mutex mu_;
+ // The most-recent credential data. It will be empty if the most recent read
+ // attempt failed.
+ std::string root_certificate_;
+ grpc_core::PemKeyCertPairList pem_key_cert_pairs_;
+ // Stores each cert_name we get from the distributor callback and its watcher
+ // information.
+ std::map<std::string, WatcherInfo> watcher_info_;
};
} // namespace grpc_core
diff --git a/src/core/lib/security/credentials/tls/grpc_tls_credentials_options.h b/src/core/lib/security/credentials/tls/grpc_tls_credentials_options.h
index c672384..2aae29b 100644
--- a/src/core/lib/security/credentials/tls/grpc_tls_credentials_options.h
+++ b/src/core/lib/security/credentials/tls/grpc_tls_credentials_options.h
@@ -147,7 +147,6 @@
server_authorization_check_config_ = std::move(config);
}
// Sets the provider in the options.
- // This should only be used by C-core API for Tls*Creds case.
void set_certificate_provider(
grpc_core::RefCountedPtr<grpc_tls_certificate_provider> provider) {
provider_ = std::move(provider);
diff --git a/src/core/lib/security/security_connector/tls/tls_security_connector.h b/src/core/lib/security/security_connector/tls/tls_security_connector.h
index d915959..453c531 100644
--- a/src/core/lib/security/security_connector/tls/tls_security_connector.h
+++ b/src/core/lib/security/security_connector/tls/tls_security_connector.h
@@ -74,13 +74,12 @@
return client_handshaker_factory_;
};
- const absl::optional<absl::string_view>& RootCertsForTesting() {
+ absl::optional<absl::string_view> RootCertsForTesting() {
grpc_core::MutexLock lock(&mu_);
return pem_root_certs_;
}
- const absl::optional<grpc_core::PemKeyCertPairList>&
- KeyCertPairListForTesting() {
+ absl::optional<grpc_core::PemKeyCertPairList> KeyCertPairListForTesting() {
grpc_core::MutexLock lock(&mu_);
return pem_key_cert_pair_list_;
}
diff --git a/src/cpp/common/tls_certificate_provider.cc b/src/cpp/common/tls_certificate_provider.cc
index 3550e28..2deea5f 100644
--- a/src/cpp/common/tls_certificate_provider.cc
+++ b/src/cpp/common/tls_certificate_provider.cc
@@ -41,5 +41,19 @@
grpc_tls_certificate_provider_release(c_provider_);
};
+FileWatcherCertificateProvider::FileWatcherCertificateProvider(
+ const std::string& private_key_path,
+ const std::string& identity_certificate_path,
+ const std::string& root_cert_path, unsigned int refresh_interval_sec) {
+ c_provider_ = grpc_tls_certificate_provider_file_watcher_create(
+ private_key_path.c_str(), identity_certificate_path.c_str(),
+ root_cert_path.c_str(), refresh_interval_sec);
+ GPR_ASSERT(c_provider_ != nullptr);
+};
+
+FileWatcherCertificateProvider::~FileWatcherCertificateProvider() {
+ grpc_tls_certificate_provider_release(c_provider_);
+};
+
} // namespace experimental
} // namespace grpc
diff --git a/src/ruby/ext/grpc/rb_grpc_imports.generated.c b/src/ruby/ext/grpc/rb_grpc_imports.generated.c
index 9024a8e..90b23ae 100644
--- a/src/ruby/ext/grpc/rb_grpc_imports.generated.c
+++ b/src/ruby/ext/grpc/rb_grpc_imports.generated.c
@@ -162,6 +162,7 @@
grpc_tls_identity_pairs_add_pair_type grpc_tls_identity_pairs_add_pair_import;
grpc_tls_identity_pairs_destroy_type grpc_tls_identity_pairs_destroy_import;
grpc_tls_certificate_provider_static_data_create_type grpc_tls_certificate_provider_static_data_create_import;
+grpc_tls_certificate_provider_file_watcher_create_type grpc_tls_certificate_provider_file_watcher_create_import;
grpc_tls_certificate_provider_release_type grpc_tls_certificate_provider_release_import;
grpc_tls_credentials_options_create_type grpc_tls_credentials_options_create_import;
grpc_tls_credentials_options_set_cert_request_type_type grpc_tls_credentials_options_set_cert_request_type_import;
@@ -440,6 +441,7 @@
grpc_tls_identity_pairs_add_pair_import = (grpc_tls_identity_pairs_add_pair_type) GetProcAddress(library, "grpc_tls_identity_pairs_add_pair");
grpc_tls_identity_pairs_destroy_import = (grpc_tls_identity_pairs_destroy_type) GetProcAddress(library, "grpc_tls_identity_pairs_destroy");
grpc_tls_certificate_provider_static_data_create_import = (grpc_tls_certificate_provider_static_data_create_type) GetProcAddress(library, "grpc_tls_certificate_provider_static_data_create");
+ grpc_tls_certificate_provider_file_watcher_create_import = (grpc_tls_certificate_provider_file_watcher_create_type) GetProcAddress(library, "grpc_tls_certificate_provider_file_watcher_create");
grpc_tls_certificate_provider_release_import = (grpc_tls_certificate_provider_release_type) GetProcAddress(library, "grpc_tls_certificate_provider_release");
grpc_tls_credentials_options_create_import = (grpc_tls_credentials_options_create_type) GetProcAddress(library, "grpc_tls_credentials_options_create");
grpc_tls_credentials_options_set_cert_request_type_import = (grpc_tls_credentials_options_set_cert_request_type_type) GetProcAddress(library, "grpc_tls_credentials_options_set_cert_request_type");
diff --git a/src/ruby/ext/grpc/rb_grpc_imports.generated.h b/src/ruby/ext/grpc/rb_grpc_imports.generated.h
index 1079e42..0224b94 100644
--- a/src/ruby/ext/grpc/rb_grpc_imports.generated.h
+++ b/src/ruby/ext/grpc/rb_grpc_imports.generated.h
@@ -461,6 +461,9 @@
typedef grpc_tls_certificate_provider*(*grpc_tls_certificate_provider_static_data_create_type)(const char* root_certificate, grpc_tls_identity_pairs* pem_key_cert_pairs);
extern grpc_tls_certificate_provider_static_data_create_type grpc_tls_certificate_provider_static_data_create_import;
#define grpc_tls_certificate_provider_static_data_create grpc_tls_certificate_provider_static_data_create_import
+typedef grpc_tls_certificate_provider*(*grpc_tls_certificate_provider_file_watcher_create_type)(const char* private_key_path, const char* identity_certificate_path, const char* root_cert_path, unsigned int refresh_interval_sec);
+extern grpc_tls_certificate_provider_file_watcher_create_type grpc_tls_certificate_provider_file_watcher_create_import;
+#define grpc_tls_certificate_provider_file_watcher_create grpc_tls_certificate_provider_file_watcher_create_import
typedef void(*grpc_tls_certificate_provider_release_type)(grpc_tls_certificate_provider* provider);
extern grpc_tls_certificate_provider_release_type grpc_tls_certificate_provider_release_import;
#define grpc_tls_certificate_provider_release grpc_tls_certificate_provider_release_import
diff --git a/test/core/end2end/fixtures/h2_tls.cc b/test/core/end2end/fixtures/h2_tls.cc
index 4689cee..9157e1e 100644
--- a/test/core/end2end/fixtures/h2_tls.cc
+++ b/test/core/end2end/fixtures/h2_tls.cc
@@ -39,6 +39,7 @@
#include "test/core/util/port.h"
#include "test/core/util/test_config.h"
+// For normal TLS connections.
#define CA_CERT_PATH "src/core/tsi/test_creds/ca.pem"
#define SERVER_CERT_PATH "src/core/tsi/test_creds/server1.pem"
#define SERVER_KEY_PATH "src/core/tsi/test_creds/server1.key"
@@ -60,7 +61,7 @@
grpc_tls_certificate_provider* server_provider = nullptr;
};
-static grpc_end2end_test_fixture chttp2_create_fixture_secure_fullstack(
+static grpc_end2end_test_fixture chttp2_create_fixture_static_data(
grpc_channel_args* /*client_args*/, grpc_channel_args* /*server_args*/,
grpc_tls_version tls_version) {
grpc_end2end_test_fixture f;
@@ -101,16 +102,47 @@
return f;
}
-static grpc_end2end_test_fixture chttp2_create_fixture_secure_fullstack_tls1_2(
- grpc_channel_args* client_args, grpc_channel_args* server_args) {
- return chttp2_create_fixture_secure_fullstack(client_args, server_args,
- grpc_tls_version::TLS1_2);
+static grpc_end2end_test_fixture chttp2_create_fixture_cert_watcher(
+ grpc_channel_args* /*client_args*/, grpc_channel_args* /*server_args*/,
+ grpc_tls_version tls_version) {
+ grpc_end2end_test_fixture f;
+ int port = grpc_pick_unused_port_or_die();
+ fullstack_secure_fixture_data* ffd = new fullstack_secure_fixture_data();
+ memset(&f, 0, sizeof(f));
+ ffd->localaddr = grpc_core::JoinHostPort("localhost", port);
+ ffd->tls_version = tls_version;
+ ffd->client_provider = grpc_tls_certificate_provider_file_watcher_create(
+ SERVER_KEY_PATH, SERVER_CERT_PATH, CA_CERT_PATH, 1);
+ ffd->server_provider = grpc_tls_certificate_provider_file_watcher_create(
+ SERVER_KEY_PATH, SERVER_CERT_PATH, CA_CERT_PATH, 1);
+ f.fixture_data = ffd;
+ f.cq = grpc_completion_queue_create_for_next(nullptr);
+ f.shutdown_cq = grpc_completion_queue_create_for_pluck(nullptr);
+ return f;
}
-static grpc_end2end_test_fixture chttp2_create_fixture_secure_fullstack_tls1_3(
+static grpc_end2end_test_fixture chttp2_create_fixture_static_data_tls1_2(
grpc_channel_args* client_args, grpc_channel_args* server_args) {
- return chttp2_create_fixture_secure_fullstack(client_args, server_args,
- grpc_tls_version::TLS1_3);
+ return chttp2_create_fixture_static_data(client_args, server_args,
+ grpc_tls_version::TLS1_2);
+}
+
+static grpc_end2end_test_fixture chttp2_create_fixture_static_data_tls1_3(
+ grpc_channel_args* client_args, grpc_channel_args* server_args) {
+ return chttp2_create_fixture_static_data(client_args, server_args,
+ grpc_tls_version::TLS1_3);
+}
+
+static grpc_end2end_test_fixture chttp2_create_fixture_cert_watcher_tls1_2(
+ grpc_channel_args* client_args, grpc_channel_args* server_args) {
+ return chttp2_create_fixture_cert_watcher(client_args, server_args,
+ grpc_tls_version::TLS1_2);
+}
+
+static grpc_end2end_test_fixture chttp2_create_fixture_cert_watcher_tls1_3(
+ grpc_channel_args* client_args, grpc_channel_args* server_args) {
+ return chttp2_create_fixture_cert_watcher(client_args, server_args,
+ grpc_tls_version::TLS1_3);
}
static void process_auth_failure(void* state, grpc_auth_context* /*ctx*/,
@@ -262,21 +294,47 @@
}
static grpc_end2end_test_config configs[] = {
- /* client sync reload async authz + server sync reload. */
+ // client: static data provider + async custom verification
+ // server: static data provider
+ // extra: TLS 1.2
{"chttp2/simple_ssl_fullstack_tls1_2",
FEATURE_MASK_SUPPORTS_DELAYED_CONNECTION |
FEATURE_MASK_SUPPORTS_PER_CALL_CREDENTIALS |
FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL |
FEATURE_MASK_SUPPORTS_AUTHORITY_HEADER,
- "foo.test.google.fr", chttp2_create_fixture_secure_fullstack_tls1_2,
+ "foo.test.google.fr", chttp2_create_fixture_static_data_tls1_2,
chttp2_init_client, chttp2_init_server, chttp2_tear_down_secure_fullstack},
+ // client: static data provider + async custom verification
+ // server: static data provider
+ // extra: TLS 1.3
{"chttp2/simple_ssl_fullstack_tls1_3",
FEATURE_MASK_SUPPORTS_DELAYED_CONNECTION |
FEATURE_MASK_SUPPORTS_PER_CALL_CREDENTIALS |
FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL |
FEATURE_MASK_SUPPORTS_AUTHORITY_HEADER,
- "foo.test.google.fr", chttp2_create_fixture_secure_fullstack_tls1_3,
+ "foo.test.google.fr", chttp2_create_fixture_static_data_tls1_3,
chttp2_init_client, chttp2_init_server, chttp2_tear_down_secure_fullstack},
+ // client: certificate watcher provider + async custom verification
+ // server: certificate watcher provider
+ // extra: TLS 1.2
+ {"chttp2/reloading_from_files_ssl_fullstack_tls1_2",
+ FEATURE_MASK_SUPPORTS_DELAYED_CONNECTION |
+ FEATURE_MASK_SUPPORTS_PER_CALL_CREDENTIALS |
+ FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL |
+ FEATURE_MASK_SUPPORTS_AUTHORITY_HEADER,
+ "foo.test.google.fr", chttp2_create_fixture_cert_watcher_tls1_2,
+ chttp2_init_client, chttp2_init_server, chttp2_tear_down_secure_fullstack},
+ // client: certificate watcher provider + async custom verification
+ // server: certificate watcher provider
+ // extra: TLS 1.3
+ {"chttp2/reloading_from_files_ssl_fullstack_tls1_3",
+ FEATURE_MASK_SUPPORTS_DELAYED_CONNECTION |
+ FEATURE_MASK_SUPPORTS_PER_CALL_CREDENTIALS |
+ FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL |
+ FEATURE_MASK_SUPPORTS_AUTHORITY_HEADER,
+ "foo.test.google.fr", chttp2_create_fixture_cert_watcher_tls1_3,
+ chttp2_init_client, chttp2_init_server, chttp2_tear_down_secure_fullstack},
+
};
int main(int argc, char** argv) {
diff --git a/test/core/security/BUILD b/test/core/security/BUILD
index e576028..31b77a2 100644
--- a/test/core/security/BUILD
+++ b/test/core/security/BUILD
@@ -287,6 +287,15 @@
],
)
+grpc_cc_library(
+ name = "tls_utils",
+ srcs = ["tls_utils.cc"],
+ hdrs = ["tls_utils.h"],
+ language = "C++",
+ visibility = ["//test/cpp:__subpackages__"],
+ deps = ["//:grpc"],
+)
+
grpc_cc_test(
name = "tls_security_connector_test",
srcs = ["tls_security_connector_test.cc"],
@@ -318,12 +327,17 @@
srcs = ["grpc_tls_credentials_options_test.cc"],
data = [
"//src/core/tsi/test_creds:ca.pem",
+ "//src/core/tsi/test_creds:multi-domain.key",
+ "//src/core/tsi/test_creds:multi-domain.pem",
+ "//src/core/tsi/test_creds:server0.key",
+ "//src/core/tsi/test_creds:server0.pem",
"//src/core/tsi/test_creds:server1.key",
"//src/core/tsi/test_creds:server1.pem",
],
external_deps = ["gtest"],
language = "C++",
deps = [
+ ":tls_utils",
"//:gpr",
"//:grpc",
"//:grpc_secure",
@@ -337,6 +351,30 @@
external_deps = ["gtest"],
language = "C++",
deps = [
+ ":tls_utils",
+ "//:gpr",
+ "//:grpc",
+ "//:grpc_secure",
+ "//test/core/util:grpc_test_util",
+ ],
+)
+
+grpc_cc_test(
+ name = "grpc_tls_certificate_provider_test",
+ srcs = ["grpc_tls_certificate_provider_test.cc"],
+ data = [
+ "//src/core/tsi/test_creds:ca.pem",
+ "//src/core/tsi/test_creds:multi-domain.key",
+ "//src/core/tsi/test_creds:multi-domain.pem",
+ "//src/core/tsi/test_creds:server0.key",
+ "//src/core/tsi/test_creds:server0.pem",
+ "//src/core/tsi/test_creds:server1.key",
+ "//src/core/tsi/test_creds:server1.pem",
+ ],
+ external_deps = ["gtest"],
+ language = "C++",
+ deps = [
+ ":tls_utils",
"//:gpr",
"//:grpc",
"//:grpc_secure",
diff --git a/test/core/security/grpc_tls_certificate_distributor_test.cc b/test/core/security/grpc_tls_certificate_distributor_test.cc
index 61be71c..b8dfaa1 100644
--- a/test/core/security/grpc_tls_certificate_distributor_test.cc
+++ b/test/core/security/grpc_tls_certificate_distributor_test.cc
@@ -28,8 +28,11 @@
#include <thread>
#include "src/core/lib/slice/slice_internal.h"
+#include "test/core/security/tls_utils.h"
#include "test/core/util/test_config.h"
+namespace grpc_core {
+
namespace testing {
constexpr const char* kCertName1 = "cert_1_name";
@@ -53,29 +56,14 @@
// Forward declaration.
class TlsCertificatesTestWatcher;
- static grpc_core::PemKeyCertPairList MakeCertKeyPairs(const char* private_key,
- const char* certs) {
- if (strcmp(private_key, "") == 0 && strcmp(certs, "") == 0) {
- return {};
- }
- grpc_ssl_pem_key_cert_pair* ssl_pair =
- static_cast<grpc_ssl_pem_key_cert_pair*>(
- gpr_malloc(sizeof(grpc_ssl_pem_key_cert_pair)));
- ssl_pair->private_key = gpr_strdup(private_key);
- ssl_pair->cert_chain = gpr_strdup(certs);
- grpc_core::PemKeyCertPairList pem_key_cert_pairs;
- pem_key_cert_pairs.emplace_back(ssl_pair);
- return pem_key_cert_pairs;
- }
-
// CredentialInfo contains the parameters when calling OnCertificatesChanged
// of a watcher. When OnCertificatesChanged is invoked, we will push a
// CredentialInfo to the cert_update_queue of state_, and check in each test
// if the status updates are correct.
struct CredentialInfo {
std::string root_certs;
- grpc_core::PemKeyCertPairList key_cert_pairs;
- CredentialInfo(std::string root, grpc_core::PemKeyCertPairList key_cert)
+ PemKeyCertPairList key_cert_pairs;
+ CredentialInfo(std::string root, PemKeyCertPairList key_cert)
: root_certs(std::move(root)), key_cert_pairs(std::move(key_cert)) {}
bool operator==(const CredentialInfo& other) const {
return root_certs == other.root_certs &&
@@ -128,12 +116,12 @@
void OnCertificatesChanged(
absl::optional<absl::string_view> root_certs,
- absl::optional<grpc_core::PemKeyCertPairList> key_cert_pairs) override {
+ absl::optional<PemKeyCertPairList> key_cert_pairs) override {
std::string updated_root;
if (root_certs.has_value()) {
updated_root = std::string(*root_certs);
}
- grpc_core::PemKeyCertPairList updated_identity;
+ PemKeyCertPairList updated_identity;
if (key_cert_pairs.has_value()) {
updated_identity = std::move(*key_cert_pairs);
}
@@ -151,8 +139,7 @@
grpc_slice root_error_slice;
GPR_ASSERT(grpc_error_get_str(
root_cert_error, GRPC_ERROR_STR_DESCRIPTION, &root_error_slice));
- root_error_str =
- std::string(grpc_core::StringViewFromSlice(root_error_slice));
+ root_error_str = std::string(StringViewFromSlice(root_error_slice));
}
if (identity_cert_error != GRPC_ERROR_NONE) {
grpc_slice identity_error_slice;
@@ -160,7 +147,7 @@
GRPC_ERROR_STR_DESCRIPTION,
&identity_error_slice));
identity_error_str =
- std::string(grpc_core::StringViewFromSlice(identity_error_slice));
+ std::string(StringViewFromSlice(identity_error_slice));
}
state_->error_queue.emplace_back(std::move(root_error_str),
std::move(identity_error_str));
@@ -202,7 +189,7 @@
WatcherState* MakeWatcher(absl::optional<std::string> root_cert_name,
absl::optional<std::string> identity_cert_name) {
- grpc_core::MutexLock lock(&mu_);
+ MutexLock lock(&mu_);
watchers_.emplace_back();
// TlsCertificatesTestWatcher ctor takes a pointer to the WatcherState.
// It sets WatcherState::watcher to point to itself.
@@ -217,7 +204,7 @@
}
void CancelWatch(WatcherState* state) {
- grpc_core::MutexLock lock(&mu_);
+ MutexLock lock(&mu_);
distributor_.CancelTlsCertificatesWatch(state->watcher);
EXPECT_EQ(state->watcher, nullptr);
}
@@ -234,7 +221,7 @@
std::list<WatcherState> watchers_;
std::deque<CallbackStatus> callback_queue_;
// This is to make watchers_ and callback_queue_ thread-safe.
- grpc_core::Mutex mu_;
+ Mutex mu_;
};
TEST_F(GrpcTlsCertificateDistributorTest, BasicCredentialBehaviors) {
@@ -257,21 +244,21 @@
TEST_F(GrpcTlsCertificateDistributorTest, UpdateCredentialsOnAnySide) {
WatcherState* watcher_state_1 = MakeWatcher(kCertName1, kCertName1);
EXPECT_THAT(GetCallbackQueue(),
- testing::ElementsAre(CallbackStatus(kCertName1, true, true)));
+ ::testing::ElementsAre(CallbackStatus(kCertName1, true, true)));
// SetKeyMaterials should trigger watcher's OnCertificatesChanged method.
distributor_.SetKeyMaterials(
kCertName1, kRootCert1Contents,
MakeCertKeyPairs(kIdentityCert1PrivateKey, kIdentityCert1Contents));
EXPECT_THAT(
watcher_state_1->GetCredentialQueue(),
- testing::ElementsAre(CredentialInfo(
+ ::testing::ElementsAre(CredentialInfo(
kRootCert1Contents,
MakeCertKeyPairs(kIdentityCert1PrivateKey, kIdentityCert1Contents))));
// Set root certs should trigger watcher's OnCertificatesChanged again.
distributor_.SetKeyMaterials(kCertName1, kRootCert2Contents, absl::nullopt);
EXPECT_THAT(
watcher_state_1->GetCredentialQueue(),
- testing::ElementsAre(CredentialInfo(
+ ::testing::ElementsAre(CredentialInfo(
kRootCert2Contents,
MakeCertKeyPairs(kIdentityCert1PrivateKey, kIdentityCert1Contents))));
// Set identity certs should trigger watcher's OnCertificatesChanged again.
@@ -280,7 +267,7 @@
MakeCertKeyPairs(kIdentityCert2PrivateKey, kIdentityCert2Contents));
EXPECT_THAT(
watcher_state_1->GetCredentialQueue(),
- testing::ElementsAre(CredentialInfo(
+ ::testing::ElementsAre(CredentialInfo(
kRootCert2Contents,
MakeCertKeyPairs(kIdentityCert2PrivateKey, kIdentityCert2Contents))));
CancelWatch(watcher_state_1);
@@ -292,12 +279,12 @@
MakeWatcher(kRootCert1Name, kIdentityCert1Name);
EXPECT_THAT(
GetCallbackQueue(),
- testing::ElementsAre(CallbackStatus(kRootCert1Name, true, false),
- CallbackStatus(kIdentityCert1Name, false, true)));
+ ::testing::ElementsAre(CallbackStatus(kRootCert1Name, true, false),
+ CallbackStatus(kIdentityCert1Name, false, true)));
// Register watcher 2.
WatcherState* watcher_state_2 =
MakeWatcher(kRootCert2Name, kIdentityCert1Name);
- EXPECT_THAT(GetCallbackQueue(), testing::ElementsAre(CallbackStatus(
+ EXPECT_THAT(GetCallbackQueue(), ::testing::ElementsAre(CallbackStatus(
kRootCert2Name, true, false)));
// Push credential updates to kRootCert1Name and check if the status works as
// expected.
@@ -305,13 +292,13 @@
absl::nullopt);
// Check the updates are delivered to watcher 1.
EXPECT_THAT(watcher_state_1->GetCredentialQueue(),
- testing::ElementsAre(CredentialInfo(kRootCert1Contents, {})));
+ ::testing::ElementsAre(CredentialInfo(kRootCert1Contents, {})));
// Push credential updates to kRootCert2Name.
distributor_.SetKeyMaterials(kRootCert2Name, kRootCert2Contents,
absl::nullopt);
// Check the updates are delivered to watcher 2.
EXPECT_THAT(watcher_state_2->GetCredentialQueue(),
- testing::ElementsAre(CredentialInfo(kRootCert2Contents, {})));
+ ::testing::ElementsAre(CredentialInfo(kRootCert2Contents, {})));
// Push credential updates to kIdentityCert1Name and check if the status works
// as expected.
distributor_.SetKeyMaterials(
@@ -320,24 +307,24 @@
// Check the updates are delivered to watcher 1 and watcher 2.
EXPECT_THAT(
watcher_state_1->GetCredentialQueue(),
- testing::ElementsAre(CredentialInfo(
+ ::testing::ElementsAre(CredentialInfo(
kRootCert1Contents,
MakeCertKeyPairs(kIdentityCert1PrivateKey, kIdentityCert1Contents))));
EXPECT_THAT(
watcher_state_2->GetCredentialQueue(),
- testing::ElementsAre(CredentialInfo(
+ ::testing::ElementsAre(CredentialInfo(
kRootCert2Contents,
MakeCertKeyPairs(kIdentityCert1PrivateKey, kIdentityCert1Contents))));
// Cancel watcher 1.
CancelWatch(watcher_state_1);
- EXPECT_THAT(GetCallbackQueue(), testing::ElementsAre(CallbackStatus(
+ EXPECT_THAT(GetCallbackQueue(), ::testing::ElementsAre(CallbackStatus(
kRootCert1Name, false, false)));
// Cancel watcher 2.
CancelWatch(watcher_state_2);
EXPECT_THAT(
GetCallbackQueue(),
- testing::ElementsAre(CallbackStatus(kRootCert2Name, false, false),
- CallbackStatus(kIdentityCert1Name, false, false)));
+ ::testing::ElementsAre(CallbackStatus(kRootCert2Name, false, false),
+ CallbackStatus(kIdentityCert1Name, false, false)));
}
TEST_F(GrpcTlsCertificateDistributorTest, SameRootNameDiffIdentityName) {
@@ -346,12 +333,12 @@
MakeWatcher(kRootCert1Name, kIdentityCert1Name);
EXPECT_THAT(
GetCallbackQueue(),
- testing::ElementsAre(CallbackStatus(kRootCert1Name, true, false),
- CallbackStatus(kIdentityCert1Name, false, true)));
+ ::testing::ElementsAre(CallbackStatus(kRootCert1Name, true, false),
+ CallbackStatus(kIdentityCert1Name, false, true)));
// Register watcher 2.
WatcherState* watcher_state_2 =
MakeWatcher(kRootCert1Name, kIdentityCert2Name);
- EXPECT_THAT(GetCallbackQueue(), testing::ElementsAre(CallbackStatus(
+ EXPECT_THAT(GetCallbackQueue(), ::testing::ElementsAre(CallbackStatus(
kIdentityCert2Name, false, true)));
// Push credential updates to kRootCert1Name and check if the status works as
// expected.
@@ -359,10 +346,10 @@
absl::nullopt);
// Check the updates are delivered to watcher 1.
EXPECT_THAT(watcher_state_1->GetCredentialQueue(),
- testing::ElementsAre(CredentialInfo(kRootCert1Contents, {})));
+ ::testing::ElementsAre(CredentialInfo(kRootCert1Contents, {})));
// Check the updates are delivered to watcher 2.
EXPECT_THAT(watcher_state_2->GetCredentialQueue(),
- testing::ElementsAre(CredentialInfo(kRootCert1Contents, {})));
+ ::testing::ElementsAre(CredentialInfo(kRootCert1Contents, {})));
// Push credential updates to SetKeyMaterials.
distributor_.SetKeyMaterials(
kIdentityCert1Name, absl::nullopt,
@@ -370,7 +357,7 @@
// Check the updates are delivered to watcher 1.
EXPECT_THAT(
watcher_state_1->GetCredentialQueue(),
- testing::ElementsAre(CredentialInfo(
+ ::testing::ElementsAre(CredentialInfo(
kRootCert1Contents,
MakeCertKeyPairs(kIdentityCert1PrivateKey, kIdentityCert1Contents))));
// Push credential updates to kIdentityCert2Name.
@@ -380,19 +367,19 @@
// Check the updates are delivered to watcher 2.
EXPECT_THAT(
watcher_state_2->GetCredentialQueue(),
- testing::ElementsAre(CredentialInfo(
+ ::testing::ElementsAre(CredentialInfo(
kRootCert1Contents,
MakeCertKeyPairs(kIdentityCert2PrivateKey, kIdentityCert2Contents))));
// Cancel watcher 1.
CancelWatch(watcher_state_1);
- EXPECT_THAT(GetCallbackQueue(), testing::ElementsAre(CallbackStatus(
+ EXPECT_THAT(GetCallbackQueue(), ::testing::ElementsAre(CallbackStatus(
kIdentityCert1Name, false, false)));
// Cancel watcher 2.
CancelWatch(watcher_state_2);
EXPECT_THAT(
GetCallbackQueue(),
- testing::ElementsAre(CallbackStatus(kRootCert1Name, false, false),
- CallbackStatus(kIdentityCert2Name, false, false)));
+ ::testing::ElementsAre(CallbackStatus(kRootCert1Name, false, false),
+ CallbackStatus(kIdentityCert2Name, false, false)));
}
TEST_F(GrpcTlsCertificateDistributorTest,
@@ -400,7 +387,7 @@
// Register watcher 1 watching kCertName1 for both root and identity certs.
WatcherState* watcher_state_1 = MakeWatcher(kCertName1, kCertName1);
EXPECT_THAT(GetCallbackQueue(),
- testing::ElementsAre(CallbackStatus(kCertName1, true, true)));
+ ::testing::ElementsAre(CallbackStatus(kCertName1, true, true)));
// Push credential updates to kCertName1 and check if the status works as
// expected.
distributor_.SetKeyMaterials(
@@ -409,13 +396,13 @@
// Check the updates are delivered to watcher 1.
EXPECT_THAT(
watcher_state_1->GetCredentialQueue(),
- testing::ElementsAre(CredentialInfo(
+ ::testing::ElementsAre(CredentialInfo(
kRootCert1Contents,
MakeCertKeyPairs(kIdentityCert1PrivateKey, kIdentityCert1Contents))));
// Cancel watcher 1.
CancelWatch(watcher_state_1);
EXPECT_THAT(GetCallbackQueue(),
- testing::ElementsAre(CallbackStatus(kCertName1, false, false)));
+ ::testing::ElementsAre(CallbackStatus(kCertName1, false, false)));
}
TEST_F(GrpcTlsCertificateDistributorTest,
@@ -423,11 +410,11 @@
// Register watcher 1 watching kCertName1 for root certs.
WatcherState* watcher_state_1 = MakeWatcher(kCertName1, absl::nullopt);
EXPECT_THAT(GetCallbackQueue(),
- testing::ElementsAre(CallbackStatus(kCertName1, true, false)));
+ ::testing::ElementsAre(CallbackStatus(kCertName1, true, false)));
// Register watcher 2 watching kCertName1 for identity certs.
WatcherState* watcher_state_2 = MakeWatcher(absl::nullopt, kCertName1);
EXPECT_THAT(GetCallbackQueue(),
- testing::ElementsAre(CallbackStatus(kCertName1, true, true)));
+ ::testing::ElementsAre(CallbackStatus(kCertName1, true, true)));
// Push credential updates to kCertName1 and check if the status works as
// expected.
distributor_.SetKeyMaterials(
@@ -435,39 +422,39 @@
MakeCertKeyPairs(kIdentityCert1PrivateKey, kIdentityCert1Contents));
// Check the updates are delivered to watcher 1.
EXPECT_THAT(watcher_state_1->GetCredentialQueue(),
- testing::ElementsAre(CredentialInfo(kRootCert1Contents, {})));
+ ::testing::ElementsAre(CredentialInfo(kRootCert1Contents, {})));
// Check the updates are delivered to watcher 2.
EXPECT_THAT(watcher_state_2->GetCredentialQueue(),
- testing::ElementsAre(CredentialInfo(
+ ::testing::ElementsAre(CredentialInfo(
"", MakeCertKeyPairs(kIdentityCert1PrivateKey,
kIdentityCert1Contents))));
// Push root cert updates to kCertName1.
distributor_.SetKeyMaterials(kCertName1, kRootCert2Contents, absl::nullopt);
// Check the updates are delivered to watcher 1.
EXPECT_THAT(watcher_state_1->GetCredentialQueue(),
- testing::ElementsAre(CredentialInfo(kRootCert2Contents, {})));
+ ::testing::ElementsAre(CredentialInfo(kRootCert2Contents, {})));
// Check the updates are not delivered to watcher 2.
- EXPECT_THAT(watcher_state_2->GetCredentialQueue(), testing::ElementsAre());
+ EXPECT_THAT(watcher_state_2->GetCredentialQueue(), ::testing::ElementsAre());
// Push identity cert updates to kCertName1.
distributor_.SetKeyMaterials(
kCertName1, absl::nullopt,
MakeCertKeyPairs(kIdentityCert2PrivateKey, kIdentityCert2Contents));
// Check the updates are not delivered to watcher 1.
- EXPECT_THAT(watcher_state_1->GetCredentialQueue(), testing::ElementsAre());
+ EXPECT_THAT(watcher_state_1->GetCredentialQueue(), ::testing::ElementsAre());
// Check the updates are delivered to watcher 2.
EXPECT_THAT(watcher_state_2->GetCredentialQueue(),
- testing::ElementsAre(CredentialInfo(
+ ::testing::ElementsAre(CredentialInfo(
"", MakeCertKeyPairs(kIdentityCert2PrivateKey,
kIdentityCert2Contents))));
watcher_state_2->cert_update_queue.clear();
// Cancel watcher 2.
CancelWatch(watcher_state_2);
EXPECT_THAT(GetCallbackQueue(),
- testing::ElementsAre(CallbackStatus(kCertName1, true, false)));
+ ::testing::ElementsAre(CallbackStatus(kCertName1, true, false)));
// Cancel watcher 1.
CancelWatch(watcher_state_1);
EXPECT_THAT(GetCallbackQueue(),
- testing::ElementsAre(CallbackStatus(kCertName1, false, false)));
+ ::testing::ElementsAre(CallbackStatus(kCertName1, false, false)));
}
TEST_F(GrpcTlsCertificateDistributorTest,
@@ -475,11 +462,11 @@
// Register watcher 1 watching kCertName1 for identity certs.
WatcherState* watcher_state_1 = MakeWatcher(absl::nullopt, kCertName1);
EXPECT_THAT(GetCallbackQueue(),
- testing::ElementsAre(CallbackStatus(kCertName1, false, true)));
+ ::testing::ElementsAre(CallbackStatus(kCertName1, false, true)));
// Register watcher 2 watching kCertName1 for root certs.
WatcherState* watcher_state_2 = MakeWatcher(kCertName1, absl::nullopt);
EXPECT_THAT(GetCallbackQueue(),
- testing::ElementsAre(CallbackStatus(kCertName1, true, true)));
+ ::testing::ElementsAre(CallbackStatus(kCertName1, true, true)));
// Push credential updates to kCertName1 and check if the status works as
// expected.
distributor_.SetKeyMaterials(
@@ -487,38 +474,38 @@
MakeCertKeyPairs(kIdentityCert1PrivateKey, kIdentityCert1Contents));
// Check the updates are delivered to watcher 1.
EXPECT_THAT(watcher_state_1->GetCredentialQueue(),
- testing::ElementsAre(CredentialInfo(
+ ::testing::ElementsAre(CredentialInfo(
"", MakeCertKeyPairs(kIdentityCert1PrivateKey,
kIdentityCert1Contents))));
// Check the updates are delivered to watcher 2.
EXPECT_THAT(watcher_state_2->GetCredentialQueue(),
- testing::ElementsAre(CredentialInfo(kRootCert1Contents, {})));
+ ::testing::ElementsAre(CredentialInfo(kRootCert1Contents, {})));
// Push root cert updates to kCertName1.
distributor_.SetKeyMaterials(kCertName1, kRootCert2Contents, absl::nullopt);
// Check the updates are delivered to watcher 2.
EXPECT_THAT(watcher_state_2->GetCredentialQueue(),
- testing::ElementsAre(CredentialInfo(kRootCert2Contents, {})));
+ ::testing::ElementsAre(CredentialInfo(kRootCert2Contents, {})));
// Check the updates are not delivered to watcher 1.
- EXPECT_THAT(watcher_state_1->GetCredentialQueue(), testing::ElementsAre());
+ EXPECT_THAT(watcher_state_1->GetCredentialQueue(), ::testing::ElementsAre());
// Push identity cert updates to kCertName1.
distributor_.SetKeyMaterials(
kCertName1, absl::nullopt,
MakeCertKeyPairs(kIdentityCert2PrivateKey, kIdentityCert2Contents));
// Check the updates are not delivered to watcher 2.
- EXPECT_THAT(watcher_state_2->GetCredentialQueue(), testing::ElementsAre());
+ EXPECT_THAT(watcher_state_2->GetCredentialQueue(), ::testing::ElementsAre());
// Check the updates are delivered to watcher 1.
EXPECT_THAT(watcher_state_1->GetCredentialQueue(),
- testing::ElementsAre(CredentialInfo(
+ ::testing::ElementsAre(CredentialInfo(
"", MakeCertKeyPairs(kIdentityCert2PrivateKey,
kIdentityCert2Contents))));
// Cancel watcher 2.
CancelWatch(watcher_state_2);
EXPECT_THAT(GetCallbackQueue(),
- testing::ElementsAre(CallbackStatus(kCertName1, false, true)));
+ ::testing::ElementsAre(CallbackStatus(kCertName1, false, true)));
// Cancel watcher 1.
CancelWatch(watcher_state_1);
EXPECT_THAT(GetCallbackQueue(),
- testing::ElementsAre(CallbackStatus(kCertName1, false, false)));
+ ::testing::ElementsAre(CallbackStatus(kCertName1, false, false)));
}
TEST_F(GrpcTlsCertificateDistributorTest,
@@ -527,24 +514,24 @@
// certs.
WatcherState* watcher_state_1 = MakeWatcher(kCertName1, kCertName1);
EXPECT_THAT(GetCallbackQueue(),
- testing::ElementsAre(CallbackStatus(kCertName1, true, true)));
+ ::testing::ElementsAre(CallbackStatus(kCertName1, true, true)));
WatcherState* watcher_state_2 = MakeWatcher(kCertName1, kCertName1);
- EXPECT_THAT(GetCallbackQueue(), testing::ElementsAre());
+ EXPECT_THAT(GetCallbackQueue(), ::testing::ElementsAre());
// Push credential updates to kCertName1.
distributor_.SetKeyMaterials(
kCertName1, kRootCert1Contents,
MakeCertKeyPairs(kIdentityCert1PrivateKey, kIdentityCert1Contents));
// Cancel watcher 2.
CancelWatch(watcher_state_2);
- EXPECT_THAT(GetCallbackQueue(), testing::ElementsAre());
+ EXPECT_THAT(GetCallbackQueue(), ::testing::ElementsAre());
// Cancel watcher 1.
CancelWatch(watcher_state_1);
EXPECT_THAT(GetCallbackQueue(),
- testing::ElementsAre(CallbackStatus(kCertName1, false, false)));
+ ::testing::ElementsAre(CallbackStatus(kCertName1, false, false)));
// Register watcher 3 watching kCertName for root and identity certs.
WatcherState* watcher_state_3 = MakeWatcher(kCertName1, kCertName1);
EXPECT_THAT(GetCallbackQueue(),
- testing::ElementsAre(CallbackStatus(kCertName1, true, true)));
+ ::testing::ElementsAre(CallbackStatus(kCertName1, true, true)));
// Push credential updates to kCertName1.
distributor_.SetKeyMaterials(
kCertName1, kRootCert2Contents,
@@ -552,25 +539,25 @@
// Check the updates are delivered to watcher 3.
EXPECT_THAT(
watcher_state_3->GetCredentialQueue(),
- testing::ElementsAre(CredentialInfo(
+ ::testing::ElementsAre(CredentialInfo(
kRootCert2Contents,
MakeCertKeyPairs(kIdentityCert2PrivateKey, kIdentityCert2Contents))));
// Cancel watcher 3.
CancelWatch(watcher_state_3);
EXPECT_THAT(GetCallbackQueue(),
- testing::ElementsAre(CallbackStatus(kCertName1, false, false)));
+ ::testing::ElementsAre(CallbackStatus(kCertName1, false, false)));
}
TEST_F(GrpcTlsCertificateDistributorTest, ResetCallbackToNull) {
// Register watcher 1 watching kCertName1 for root and identity certs.
WatcherState* watcher_state_1 = MakeWatcher(kCertName1, kCertName1);
EXPECT_THAT(GetCallbackQueue(),
- testing::ElementsAre(CallbackStatus(kCertName1, true, true)));
+ ::testing::ElementsAre(CallbackStatus(kCertName1, true, true)));
// Reset callback to nullptr.
distributor_.SetWatchStatusCallback(nullptr);
// Cancel watcher 1 shouldn't trigger any callback.
CancelWatch(watcher_state_1);
- EXPECT_THAT(GetCallbackQueue(), testing::ElementsAre());
+ EXPECT_THAT(GetCallbackQueue(), ::testing::ElementsAre());
}
TEST_F(GrpcTlsCertificateDistributorTest, SetKeyMaterialsInCallback) {
@@ -586,7 +573,7 @@
// Check the updates are delivered to watcher 1.
EXPECT_THAT(
watcher_state_1->GetCredentialQueue(),
- testing::ElementsAre(CredentialInfo(
+ ::testing::ElementsAre(CredentialInfo(
kRootCert1Contents, MakeCertKeyPairs(kIdentityCert1PrivateKey,
kIdentityCert1Contents))));
CancelWatch(watcher_state_1);
@@ -621,7 +608,7 @@
// watcher 1 should receive the credentials right away.
EXPECT_THAT(
watcher_state_1->GetCredentialQueue(),
- testing::ElementsAre(CredentialInfo(
+ ::testing::ElementsAre(CredentialInfo(
kRootCert1Contents,
MakeCertKeyPairs(kIdentityCert1PrivateKey, kIdentityCert1Contents))));
CancelWatch(watcher_state_1);
@@ -629,13 +616,13 @@
WatcherState* watcher_state_2 = MakeWatcher(kRootCert2Name, absl::nullopt);
// watcher 2 should receive the root credentials right away.
EXPECT_THAT(watcher_state_2->GetCredentialQueue(),
- testing::ElementsAre(CredentialInfo(kRootCert2Contents, {})));
+ ::testing::ElementsAre(CredentialInfo(kRootCert2Contents, {})));
// Register watcher 3.
WatcherState* watcher_state_3 =
MakeWatcher(absl::nullopt, kIdentityCert2Name);
// watcher 3 should received the identity credentials right away.
EXPECT_THAT(watcher_state_3->GetCredentialQueue(),
- testing::ElementsAre(CredentialInfo(
+ ::testing::ElementsAre(CredentialInfo(
"", MakeCertKeyPairs(kIdentityCert2PrivateKey,
kIdentityCert2Contents))));
CancelWatch(watcher_state_2);
@@ -652,7 +639,7 @@
kCertName1, GRPC_ERROR_CREATE_FROM_STATIC_STRING(kRootErrorMessage),
GRPC_ERROR_CREATE_FROM_STATIC_STRING(kIdentityErrorMessage));
EXPECT_THAT(watcher_state_1->GetErrorQueue(),
- testing::ElementsAre(
+ ::testing::ElementsAre(
ErrorInfo(kRootErrorMessage, kIdentityErrorMessage)));
// Calling SetErrorForCert on root cert name should call OnError
// on watcher 1 again.
@@ -661,14 +648,14 @@
absl::nullopt);
EXPECT_THAT(
watcher_state_1->GetErrorQueue(),
- testing::ElementsAre(ErrorInfo(kErrorMessage, kIdentityErrorMessage)));
+ ::testing::ElementsAre(ErrorInfo(kErrorMessage, kIdentityErrorMessage)));
// Calling SetErrorForCert on identity cert name should call OnError
// on watcher 1 again.
distributor_.SetErrorForCert(
kCertName1, absl::nullopt,
GRPC_ERROR_CREATE_FROM_STATIC_STRING(kErrorMessage));
EXPECT_THAT(watcher_state_1->GetErrorQueue(),
- testing::ElementsAre(ErrorInfo(kErrorMessage, kErrorMessage)));
+ ::testing::ElementsAre(ErrorInfo(kErrorMessage, kErrorMessage)));
distributor_.CancelTlsCertificatesWatch(watcher_state_1->watcher);
EXPECT_EQ(watcher_state_1->watcher, nullptr);
}
@@ -682,18 +669,18 @@
kCertName1, GRPC_ERROR_CREATE_FROM_STATIC_STRING(kRootErrorMessage),
absl::nullopt);
EXPECT_THAT(watcher_state_1->GetErrorQueue(),
- testing::ElementsAre(ErrorInfo(kRootErrorMessage, "")));
+ ::testing::ElementsAre(ErrorInfo(kRootErrorMessage, "")));
// Calling SetErrorForCert on identity name should do nothing.
distributor_.SetErrorForCert(
kCertName1, absl::nullopt,
GRPC_ERROR_CREATE_FROM_STATIC_STRING(kIdentityErrorMessage));
- EXPECT_THAT(watcher_state_1->GetErrorQueue(), testing::ElementsAre());
+ EXPECT_THAT(watcher_state_1->GetErrorQueue(), ::testing::ElementsAre());
// Calling SetErrorForCert on both names should still get one OnError call.
distributor_.SetErrorForCert(
kCertName1, GRPC_ERROR_CREATE_FROM_STATIC_STRING(kRootErrorMessage),
GRPC_ERROR_CREATE_FROM_STATIC_STRING(kIdentityErrorMessage));
EXPECT_THAT(watcher_state_1->GetErrorQueue(),
- testing::ElementsAre(ErrorInfo(kRootErrorMessage, "")));
+ ::testing::ElementsAre(ErrorInfo(kRootErrorMessage, "")));
CancelWatch(watcher_state_1);
// Register watcher 2.
WatcherState* watcher_state_2 = MakeWatcher(absl::nullopt, kCertName1);
@@ -703,18 +690,18 @@
kCertName1, absl::nullopt,
GRPC_ERROR_CREATE_FROM_STATIC_STRING(kIdentityErrorMessage));
EXPECT_THAT(watcher_state_2->GetErrorQueue(),
- testing::ElementsAre(ErrorInfo("", kIdentityErrorMessage)));
+ ::testing::ElementsAre(ErrorInfo("", kIdentityErrorMessage)));
// Calling SetErrorForCert on root name should do nothing.
distributor_.SetErrorForCert(
kCertName1, GRPC_ERROR_CREATE_FROM_STATIC_STRING(kRootErrorMessage),
absl::nullopt);
- EXPECT_THAT(watcher_state_2->GetErrorQueue(), testing::ElementsAre());
+ EXPECT_THAT(watcher_state_2->GetErrorQueue(), ::testing::ElementsAre());
// Calling SetErrorForCert on both names should still get one OnError call.
distributor_.SetErrorForCert(
kCertName1, GRPC_ERROR_CREATE_FROM_STATIC_STRING(kRootErrorMessage),
GRPC_ERROR_CREATE_FROM_STATIC_STRING(kIdentityErrorMessage));
EXPECT_THAT(watcher_state_2->GetErrorQueue(),
- testing::ElementsAre(ErrorInfo("", kIdentityErrorMessage)));
+ ::testing::ElementsAre(ErrorInfo("", kIdentityErrorMessage)));
CancelWatch(watcher_state_2);
}
@@ -728,14 +715,14 @@
WatcherState* watcher_state_1 = MakeWatcher(kCertName1, kCertName2);
// Should trigger OnError call right away since kCertName1 has error.
EXPECT_THAT(watcher_state_1->GetErrorQueue(),
- testing::ElementsAre(ErrorInfo(kRootErrorMessage, "")));
+ ::testing::ElementsAre(ErrorInfo(kRootErrorMessage, "")));
// Calling SetErrorForCert on kCertName2 should trigger OnError with both
// errors, because kCertName1 also has error.
distributor_.SetErrorForCert(
kCertName2, absl::nullopt,
GRPC_ERROR_CREATE_FROM_STATIC_STRING(kIdentityErrorMessage));
EXPECT_THAT(watcher_state_1->GetErrorQueue(),
- testing::ElementsAre(
+ ::testing::ElementsAre(
ErrorInfo(kRootErrorMessage, kIdentityErrorMessage)));
CancelWatch(watcher_state_1);
}
@@ -750,14 +737,14 @@
WatcherState* watcher_state_1 = MakeWatcher(kCertName2, kCertName1);
// Should trigger OnError call right away since kCertName2 has error.
EXPECT_THAT(watcher_state_1->GetErrorQueue(),
- testing::ElementsAre(ErrorInfo("", kIdentityErrorMessage)));
+ ::testing::ElementsAre(ErrorInfo("", kIdentityErrorMessage)));
// Calling SetErrorForCert on kCertName2 should trigger OnError with both
// errors, because kCertName1 also has error.
distributor_.SetErrorForCert(
kCertName2, GRPC_ERROR_CREATE_FROM_STATIC_STRING(kRootErrorMessage),
absl::nullopt);
EXPECT_THAT(watcher_state_1->GetErrorQueue(),
- testing::ElementsAre(
+ ::testing::ElementsAre(
ErrorInfo(kRootErrorMessage, kIdentityErrorMessage)));
CancelWatch(watcher_state_1);
}
@@ -767,25 +754,25 @@
// Register watcher 1 for kCertName1 as root and kCertName2 as identity.
WatcherState* watcher_state_1 = MakeWatcher(kCertName1, kCertName2);
// Should not trigger OnError.
- EXPECT_THAT(watcher_state_1->GetErrorQueue(), testing::ElementsAre());
+ EXPECT_THAT(watcher_state_1->GetErrorQueue(), ::testing::ElementsAre());
// Calling SetErrorForCert on kCertName2 should trigger OnError.
distributor_.SetErrorForCert(
kCertName2, absl::nullopt,
GRPC_ERROR_CREATE_FROM_STATIC_STRING(kIdentityErrorMessage));
EXPECT_THAT(watcher_state_1->GetErrorQueue(),
- testing::ElementsAre(ErrorInfo("", kIdentityErrorMessage)));
+ ::testing::ElementsAre(ErrorInfo("", kIdentityErrorMessage)));
CancelWatch(watcher_state_1);
// Register watcher 2 for kCertName2 as identity and a non-existing name
// kRootCert1Name as root.
WatcherState* watcher_state_2 = MakeWatcher(kRootCert1Name, kCertName2);
// Should not trigger OnError.
- EXPECT_THAT(watcher_state_2->GetErrorQueue(), testing::ElementsAre());
+ EXPECT_THAT(watcher_state_2->GetErrorQueue(), ::testing::ElementsAre());
// Calling SetErrorForCert on kCertName2 should trigger OnError.
distributor_.SetErrorForCert(
kCertName2, absl::nullopt,
GRPC_ERROR_CREATE_FROM_STATIC_STRING(kIdentityErrorMessage));
EXPECT_THAT(watcher_state_2->error_queue,
- testing::ElementsAre(ErrorInfo("", kIdentityErrorMessage)));
+ ::testing::ElementsAre(ErrorInfo("", kIdentityErrorMessage)));
CancelWatch(watcher_state_2);
}
@@ -793,25 +780,25 @@
SetErrorForRootNameWithPreexistingErrorForIdentityName) {
WatcherState* watcher_state_1 = MakeWatcher(kCertName2, kCertName1);
// Should not trigger OnError.
- EXPECT_THAT(watcher_state_1->GetErrorQueue(), testing::ElementsAre());
+ EXPECT_THAT(watcher_state_1->GetErrorQueue(), ::testing::ElementsAre());
// Calling SetErrorForCert on kCertName2 should trigger OnError.
distributor_.SetErrorForCert(
kCertName2, GRPC_ERROR_CREATE_FROM_STATIC_STRING(kRootErrorMessage),
absl::nullopt);
EXPECT_THAT(watcher_state_1->GetErrorQueue(),
- testing::ElementsAre(ErrorInfo(kRootErrorMessage, "")));
+ ::testing::ElementsAre(ErrorInfo(kRootErrorMessage, "")));
CancelWatch(watcher_state_1);
// Register watcher 2 for kCertName2 as root and a non-existing name
// kIdentityCert1Name as identity.
WatcherState* watcher_state_2 = MakeWatcher(kCertName2, kIdentityCert1Name);
// Should not trigger OnError.
- EXPECT_THAT(watcher_state_2->GetErrorQueue(), testing::ElementsAre());
+ EXPECT_THAT(watcher_state_2->GetErrorQueue(), ::testing::ElementsAre());
// Calling SetErrorForCert on kCertName2 should trigger OnError.
distributor_.SetErrorForCert(
kCertName2, GRPC_ERROR_CREATE_FROM_STATIC_STRING(kRootErrorMessage),
absl::nullopt);
EXPECT_THAT(watcher_state_2->GetErrorQueue(),
- testing::ElementsAre(ErrorInfo(kRootErrorMessage, "")));
+ ::testing::ElementsAre(ErrorInfo(kRootErrorMessage, "")));
CancelWatch(watcher_state_2);
}
@@ -825,14 +812,14 @@
kCertName1, GRPC_ERROR_CREATE_FROM_STATIC_STRING(kRootErrorMessage),
GRPC_ERROR_CREATE_FROM_STATIC_STRING(kIdentityErrorMessage));
EXPECT_THAT(watcher_state_1->GetErrorQueue(),
- testing::ElementsAre(
+ ::testing::ElementsAre(
ErrorInfo(kRootErrorMessage, kIdentityErrorMessage)));
// When watcher 1 is removed, the cert info entry should be removed.
CancelWatch(watcher_state_1);
// Register watcher 2 on the same cert name.
WatcherState* watcher_state_2 = MakeWatcher(kCertName1, kCertName1);
// Should not trigger OnError call on watcher 2 right away.
- EXPECT_THAT(watcher_state_2->GetErrorQueue(), testing::ElementsAre());
+ EXPECT_THAT(watcher_state_2->GetErrorQueue(), ::testing::ElementsAre());
CancelWatch(watcher_state_2);
}
@@ -851,11 +838,11 @@
// watcher 1 should receive both the old credentials and the error right away.
EXPECT_THAT(
watcher_state_1->GetCredentialQueue(),
- testing::ElementsAre(CredentialInfo(
+ ::testing::ElementsAre(CredentialInfo(
kRootCert1Contents,
MakeCertKeyPairs(kIdentityCert1PrivateKey, kIdentityCert1Contents))));
EXPECT_THAT(watcher_state_1->GetErrorQueue(),
- testing::ElementsAre(
+ ::testing::ElementsAre(
ErrorInfo(kRootErrorMessage, kIdentityErrorMessage)));
CancelWatch(watcher_state_1);
}
@@ -876,10 +863,10 @@
// the previous error is wiped out by a successful update.
EXPECT_THAT(
watcher_state_1->GetCredentialQueue(),
- testing::ElementsAre(CredentialInfo(
+ ::testing::ElementsAre(CredentialInfo(
kRootCert1Contents,
MakeCertKeyPairs(kIdentityCert1PrivateKey, kIdentityCert1Contents))));
- EXPECT_THAT(watcher_state_1->GetErrorQueue(), testing::ElementsAre());
+ EXPECT_THAT(watcher_state_1->GetErrorQueue(), ::testing::ElementsAre());
CancelWatch(watcher_state_1);
}
@@ -893,11 +880,11 @@
MakeWatcher(absl::nullopt, kIdentityCert1Name);
distributor_.SetError(GRPC_ERROR_CREATE_FROM_STATIC_STRING(kErrorMessage));
EXPECT_THAT(watcher_state_1->GetErrorQueue(),
- testing::ElementsAre(ErrorInfo(kErrorMessage, kErrorMessage)));
+ ::testing::ElementsAre(ErrorInfo(kErrorMessage, kErrorMessage)));
EXPECT_THAT(watcher_state_2->GetErrorQueue(),
- testing::ElementsAre(ErrorInfo(kErrorMessage, "")));
+ ::testing::ElementsAre(ErrorInfo(kErrorMessage, "")));
EXPECT_THAT(watcher_state_3->GetErrorQueue(),
- testing::ElementsAre(ErrorInfo("", kErrorMessage)));
+ ::testing::ElementsAre(ErrorInfo("", kErrorMessage)));
CancelWatch(watcher_state_1);
CancelWatch(watcher_state_2);
CancelWatch(watcher_state_3);
@@ -915,12 +902,12 @@
// Register watcher 3 watching kCertName1 as root and kCertName2 as identity
// should not get the error updates.
WatcherState* watcher_state_3 = MakeWatcher(kCertName1, kCertName2);
- EXPECT_THAT(watcher_state_3->GetErrorQueue(), testing::ElementsAre());
+ EXPECT_THAT(watcher_state_3->GetErrorQueue(), ::testing::ElementsAre());
CancelWatch(watcher_state_3);
// Register watcher 4 watching kCertName2 as root and kCertName1 as identity
// should not get the error updates.
WatcherState* watcher_state_4 = MakeWatcher(kCertName2, kCertName1);
- EXPECT_THAT(watcher_state_4->GetErrorQueue(), testing::ElementsAre());
+ EXPECT_THAT(watcher_state_4->GetErrorQueue(), ::testing::ElementsAre());
CancelWatch(watcher_state_4);
}
@@ -936,7 +923,7 @@
WatcherState* watcher_state_1 = MakeWatcher(cert_name, cert_name);
// Check the errors are delivered to watcher 1.
EXPECT_THAT(watcher_state_1->GetErrorQueue(),
- testing::ElementsAre(
+ ::testing::ElementsAre(
ErrorInfo(kRootErrorMessage, kIdentityErrorMessage)));
CancelWatch(watcher_state_1);
};
@@ -955,6 +942,8 @@
} // namespace testing
+} // namespace grpc_core
+
int main(int argc, char** argv) {
grpc::testing::TestEnvironment env(argc, argv);
::testing::InitGoogleTest(&argc, argv);
diff --git a/test/core/security/grpc_tls_certificate_provider_test.cc b/test/core/security/grpc_tls_certificate_provider_test.cc
new file mode 100644
index 0000000..b8bebf7
--- /dev/null
+++ b/test/core/security/grpc_tls_certificate_provider_test.cc
@@ -0,0 +1,509 @@
+//
+// Copyright 2020 gRPC authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include "src/core/lib/security/credentials/tls/grpc_tls_certificate_provider.h"
+
+#include <gmock/gmock.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include <gtest/gtest.h>
+
+#include <deque>
+#include <list>
+
+#include "src/core/lib/gpr/tmpfile.h"
+#include "src/core/lib/iomgr/load_file.h"
+#include "src/core/lib/slice/slice_internal.h"
+#include "test/core/security/tls_utils.h"
+#include "test/core/util/test_config.h"
+
+#define CA_CERT_PATH "src/core/tsi/test_creds/ca.pem"
+#define SERVER_CERT_PATH "src/core/tsi/test_creds/server1.pem"
+#define SERVER_KEY_PATH "src/core/tsi/test_creds/server1.key"
+#define CA_CERT_PATH_2 "src/core/tsi/test_creds/multi-domain.pem"
+#define SERVER_CERT_PATH_2 "src/core/tsi/test_creds/server0.pem"
+#define SERVER_KEY_PATH_2 "src/core/tsi/test_creds/server0.key"
+#define INVALID_PATH "invalid/path"
+
+namespace grpc_core {
+
+namespace testing {
+
+constexpr const char* kCertName = "cert_name";
+constexpr const char* kRootError = "Unable to get latest root certificates.";
+constexpr const char* kIdentityError =
+ "Unable to get latest identity certificates.";
+
+class GrpcTlsCertificateProviderTest : public ::testing::Test {
+ protected:
+ // Forward declaration.
+ class TlsCertificatesTestWatcher;
+
+ // CredentialInfo contains the parameters when calling OnCertificatesChanged
+ // of a watcher. When OnCertificatesChanged is invoked, we will push a
+ // CredentialInfo to the cert_update_queue of state_, and check in each test
+ // if the status updates are correct.
+ struct CredentialInfo {
+ std::string root_certs;
+ PemKeyCertPairList key_cert_pairs;
+ CredentialInfo(std::string root, PemKeyCertPairList key_cert)
+ : root_certs(std::move(root)), key_cert_pairs(std::move(key_cert)) {}
+ bool operator==(const CredentialInfo& other) const {
+ return root_certs == other.root_certs &&
+ key_cert_pairs == other.key_cert_pairs;
+ }
+ };
+
+ // ErrorInfo contains the parameters when calling OnError of a watcher. When
+ // OnError is invoked, we will push a ErrorInfo to the error_queue of state_,
+ // and check in each test if the status updates are correct.
+ struct ErrorInfo {
+ std::string root_cert_str;
+ std::string identity_cert_str;
+ ErrorInfo(std::string root, std::string identity)
+ : root_cert_str(std::move(root)),
+ identity_cert_str(std::move(identity)) {}
+ bool operator==(const ErrorInfo& other) const {
+ return root_cert_str == other.root_cert_str &&
+ identity_cert_str == other.identity_cert_str;
+ }
+ };
+
+ struct WatcherState {
+ TlsCertificatesTestWatcher* watcher = nullptr;
+ std::deque<CredentialInfo> cert_update_queue;
+ std::deque<ErrorInfo> error_queue;
+ Mutex mu;
+
+ std::deque<CredentialInfo> GetCredentialQueue() {
+ // We move the data member value so the data member will be re-initiated
+ // with size 0, and ready for the next check.
+ MutexLock lock(&mu);
+ return std::move(cert_update_queue);
+ }
+ std::deque<ErrorInfo> GetErrorQueue() {
+ // We move the data member value so the data member will be re-initiated
+ // with size 0, and ready for the next check.
+ MutexLock lock(&mu);
+ return std::move(error_queue);
+ }
+ };
+
+ class TlsCertificatesTestWatcher : public grpc_tls_certificate_distributor::
+ TlsCertificatesWatcherInterface {
+ public:
+ // ctor sets state->watcher to this.
+ explicit TlsCertificatesTestWatcher(WatcherState* state) : state_(state) {
+ state_->watcher = this;
+ }
+
+ // dtor sets state->watcher to nullptr.
+ ~TlsCertificatesTestWatcher() override { state_->watcher = nullptr; }
+
+ void OnCertificatesChanged(
+ absl::optional<absl::string_view> root_certs,
+ absl::optional<PemKeyCertPairList> key_cert_pairs) override {
+ MutexLock lock(&state_->mu);
+ std::string updated_root;
+ if (root_certs.has_value()) {
+ updated_root = std::string(*root_certs);
+ }
+ PemKeyCertPairList updated_identity;
+ if (key_cert_pairs.has_value()) {
+ updated_identity = std::move(*key_cert_pairs);
+ }
+ state_->cert_update_queue.emplace_back(std::move(updated_root),
+ std::move(updated_identity));
+ }
+
+ void OnError(grpc_error* root_cert_error,
+ grpc_error* identity_cert_error) override {
+ MutexLock lock(&state_->mu);
+ GPR_ASSERT(root_cert_error != GRPC_ERROR_NONE ||
+ identity_cert_error != GRPC_ERROR_NONE);
+ std::string root_error_str;
+ std::string identity_error_str;
+ if (root_cert_error != GRPC_ERROR_NONE) {
+ grpc_slice root_error_slice;
+ GPR_ASSERT(grpc_error_get_str(
+ root_cert_error, GRPC_ERROR_STR_DESCRIPTION, &root_error_slice));
+ root_error_str = std::string(StringViewFromSlice(root_error_slice));
+ }
+ if (identity_cert_error != GRPC_ERROR_NONE) {
+ grpc_slice identity_error_slice;
+ GPR_ASSERT(grpc_error_get_str(identity_cert_error,
+ GRPC_ERROR_STR_DESCRIPTION,
+ &identity_error_slice));
+ identity_error_str =
+ std::string(StringViewFromSlice(identity_error_slice));
+ }
+ state_->error_queue.emplace_back(std::move(root_error_str),
+ std::move(identity_error_str));
+ GRPC_ERROR_UNREF(root_cert_error);
+ GRPC_ERROR_UNREF(identity_cert_error);
+ }
+
+ private:
+ WatcherState* state_;
+ };
+
+ void SetUp() override {
+ root_cert_ = GetFileContents(CA_CERT_PATH);
+ cert_chain_ = GetFileContents(SERVER_CERT_PATH);
+ private_key_ = GetFileContents(SERVER_KEY_PATH);
+ root_cert_2_ = GetFileContents(CA_CERT_PATH_2);
+ cert_chain_2_ = GetFileContents(SERVER_CERT_PATH_2);
+ private_key_2_ = GetFileContents(SERVER_KEY_PATH_2);
+ }
+
+ WatcherState* MakeWatcher(
+ RefCountedPtr<grpc_tls_certificate_distributor> distributor,
+ absl::optional<std::string> root_cert_name,
+ absl::optional<std::string> identity_cert_name) {
+ MutexLock lock(&mu_);
+ distributor_ = distributor;
+ watchers_.emplace_back();
+ // TlsCertificatesTestWatcher ctor takes a pointer to the WatcherState.
+ // It sets WatcherState::watcher to point to itself.
+ // The TlsCertificatesTestWatcher dtor will set WatcherState::watcher back
+ // to nullptr to indicate that it's been destroyed.
+ auto watcher =
+ absl::make_unique<TlsCertificatesTestWatcher>(&watchers_.back());
+ distributor_->WatchTlsCertificates(std::move(watcher),
+ std::move(root_cert_name),
+ std::move(identity_cert_name));
+ return &watchers_.back();
+ }
+
+ void CancelWatch(WatcherState* state) {
+ MutexLock lock(&mu_);
+ distributor_->CancelTlsCertificatesWatch(state->watcher);
+ EXPECT_EQ(state->watcher, nullptr);
+ }
+
+ std::string root_cert_;
+ std::string private_key_;
+ std::string cert_chain_;
+ std::string root_cert_2_;
+ std::string private_key_2_;
+ std::string cert_chain_2_;
+ RefCountedPtr<grpc_tls_certificate_distributor> distributor_;
+ // Use a std::list<> here to avoid the address invalidation caused by internal
+ // reallocation of std::vector<>.
+ std::list<WatcherState> watchers_;
+ // This is to make watchers_ thread-safe.
+ Mutex mu_;
+};
+
+TEST_F(GrpcTlsCertificateProviderTest, StaticDataCertificateProviderCreation) {
+ StaticDataCertificateProvider provider(
+ root_cert_, MakeCertKeyPairs(private_key_.c_str(), cert_chain_.c_str()));
+ // Watcher watching both root and identity certs.
+ WatcherState* watcher_state_1 =
+ MakeWatcher(provider.distributor(), kCertName, kCertName);
+ EXPECT_THAT(watcher_state_1->GetCredentialQueue(),
+ ::testing::ElementsAre(CredentialInfo(
+ root_cert_, MakeCertKeyPairs(private_key_.c_str(),
+ cert_chain_.c_str()))));
+ CancelWatch(watcher_state_1);
+ // Watcher watching only root certs.
+ WatcherState* watcher_state_2 =
+ MakeWatcher(provider.distributor(), kCertName, absl::nullopt);
+ EXPECT_THAT(watcher_state_2->GetCredentialQueue(),
+ ::testing::ElementsAre(CredentialInfo(root_cert_, {})));
+ CancelWatch(watcher_state_2);
+ // Watcher watching only identity certs.
+ WatcherState* watcher_state_3 =
+ MakeWatcher(provider.distributor(), absl::nullopt, kCertName);
+ EXPECT_THAT(
+ watcher_state_3->GetCredentialQueue(),
+ ::testing::ElementsAre(CredentialInfo(
+ "", MakeCertKeyPairs(private_key_.c_str(), cert_chain_.c_str()))));
+ CancelWatch(watcher_state_3);
+}
+
+TEST_F(GrpcTlsCertificateProviderTest,
+ FileWatcherCertificateProviderWithGoodPaths) {
+ FileWatcherCertificateProvider provider(SERVER_KEY_PATH, SERVER_CERT_PATH,
+ CA_CERT_PATH, 1);
+ // Watcher watching both root and identity certs.
+ WatcherState* watcher_state_1 =
+ MakeWatcher(provider.distributor(), kCertName, kCertName);
+ EXPECT_THAT(watcher_state_1->GetCredentialQueue(),
+ ::testing::ElementsAre(CredentialInfo(
+ root_cert_, MakeCertKeyPairs(private_key_.c_str(),
+ cert_chain_.c_str()))));
+ CancelWatch(watcher_state_1);
+ // Watcher watching only root certs.
+ WatcherState* watcher_state_2 =
+ MakeWatcher(provider.distributor(), kCertName, absl::nullopt);
+ EXPECT_THAT(watcher_state_2->GetCredentialQueue(),
+ ::testing::ElementsAre(CredentialInfo(root_cert_, {})));
+ CancelWatch(watcher_state_2);
+ // Watcher watching only identity certs.
+ WatcherState* watcher_state_3 =
+ MakeWatcher(provider.distributor(), absl::nullopt, kCertName);
+ EXPECT_THAT(
+ watcher_state_3->GetCredentialQueue(),
+ ::testing::ElementsAre(CredentialInfo(
+ "", MakeCertKeyPairs(private_key_.c_str(), cert_chain_.c_str()))));
+ CancelWatch(watcher_state_3);
+}
+
+TEST_F(GrpcTlsCertificateProviderTest,
+ FileWatcherCertificateProviderWithBadPaths) {
+ FileWatcherCertificateProvider provider(INVALID_PATH, INVALID_PATH,
+ INVALID_PATH, 1);
+ // Watcher watching both root and identity certs.
+ WatcherState* watcher_state_1 =
+ MakeWatcher(provider.distributor(), kCertName, kCertName);
+ EXPECT_THAT(watcher_state_1->GetErrorQueue(),
+ ::testing::ElementsAre(ErrorInfo(kRootError, kIdentityError)));
+ EXPECT_THAT(watcher_state_1->GetCredentialQueue(), ::testing::ElementsAre());
+ CancelWatch(watcher_state_1);
+ // Watcher watching only root certs.
+ WatcherState* watcher_state_2 =
+ MakeWatcher(provider.distributor(), kCertName, absl::nullopt);
+ EXPECT_THAT(watcher_state_2->GetErrorQueue(),
+ ::testing::ElementsAre(ErrorInfo(kRootError, "")));
+ EXPECT_THAT(watcher_state_2->GetCredentialQueue(), ::testing::ElementsAre());
+ CancelWatch(watcher_state_2);
+ // Watcher watching only identity certs.
+ WatcherState* watcher_state_3 =
+ MakeWatcher(provider.distributor(), absl::nullopt, kCertName);
+ EXPECT_THAT(watcher_state_3->GetErrorQueue(),
+ ::testing::ElementsAre(ErrorInfo("", kIdentityError)));
+ EXPECT_THAT(watcher_state_3->GetCredentialQueue(), ::testing::ElementsAre());
+ CancelWatch(watcher_state_3);
+}
+
+// The following tests write credential data to temporary files to test the
+// transition behavior of the provider.
+TEST_F(GrpcTlsCertificateProviderTest,
+ FileWatcherCertificateProviderOnBothCertsRefreshed) {
+ // Create temporary files and copy cert data into them.
+ TmpFile tmp_root_cert(root_cert_);
+ TmpFile tmp_identity_key(private_key_);
+ TmpFile tmp_identity_cert(cert_chain_);
+ // Create FileWatcherCertificateProvider.
+ FileWatcherCertificateProvider provider(tmp_identity_key.name(),
+ tmp_identity_cert.name(),
+ tmp_root_cert.name(), 1);
+ WatcherState* watcher_state_1 =
+ MakeWatcher(provider.distributor(), kCertName, kCertName);
+ // Expect to see the credential data.
+ EXPECT_THAT(watcher_state_1->GetCredentialQueue(),
+ ::testing::ElementsAre(CredentialInfo(
+ root_cert_, MakeCertKeyPairs(private_key_.c_str(),
+ cert_chain_.c_str()))));
+ // Copy new data to files.
+ // TODO(ZhenLian): right now it is not completely atomic. Use the real atomic
+ // update when the directory renaming is added in gpr.
+ tmp_root_cert.RewriteFile(root_cert_2_);
+ tmp_identity_key.RewriteFile(private_key_2_);
+ tmp_identity_cert.RewriteFile(cert_chain_2_);
+ // Wait 2 seconds for the provider's refresh thread to read the updated files.
+ gpr_sleep_until(gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC),
+ gpr_time_from_seconds(2, GPR_TIMESPAN)));
+ // Expect to see the new credential data.
+ EXPECT_THAT(watcher_state_1->GetCredentialQueue(),
+ ::testing::ElementsAre(CredentialInfo(
+ root_cert_2_, MakeCertKeyPairs(private_key_2_.c_str(),
+ cert_chain_2_.c_str()))));
+ // Clean up.
+ CancelWatch(watcher_state_1);
+}
+
+TEST_F(GrpcTlsCertificateProviderTest,
+ FileWatcherCertificateProviderOnRootCertsRefreshed) {
+ // Create temporary files and copy cert data into them.
+ TmpFile tmp_root_cert(root_cert_);
+ TmpFile tmp_identity_key(private_key_);
+ TmpFile tmp_identity_cert(cert_chain_);
+ // Create FileWatcherCertificateProvider.
+ FileWatcherCertificateProvider provider(tmp_identity_key.name(),
+ tmp_identity_cert.name(),
+ tmp_root_cert.name(), 1);
+ WatcherState* watcher_state_1 =
+ MakeWatcher(provider.distributor(), kCertName, kCertName);
+ // Expect to see the credential data.
+ EXPECT_THAT(watcher_state_1->GetCredentialQueue(),
+ ::testing::ElementsAre(CredentialInfo(
+ root_cert_, MakeCertKeyPairs(private_key_.c_str(),
+ cert_chain_.c_str()))));
+ // Copy new data to files.
+ // TODO(ZhenLian): right now it is not completely atomic. Use the real atomic
+ // update when the directory renaming is added in gpr.
+ tmp_root_cert.RewriteFile(root_cert_2_);
+ // Wait 2 seconds for the provider's refresh thread to read the updated files.
+ gpr_sleep_until(gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC),
+ gpr_time_from_seconds(2, GPR_TIMESPAN)));
+ // Expect to see the new credential data.
+ EXPECT_THAT(watcher_state_1->GetCredentialQueue(),
+ ::testing::ElementsAre(CredentialInfo(
+ root_cert_2_, MakeCertKeyPairs(private_key_.c_str(),
+ cert_chain_.c_str()))));
+ // Clean up.
+ CancelWatch(watcher_state_1);
+}
+
+TEST_F(GrpcTlsCertificateProviderTest,
+ FileWatcherCertificateProviderOnIdentityCertsRefreshed) {
+ // Create temporary files and copy cert data into them.
+ TmpFile tmp_root_cert(root_cert_);
+ TmpFile tmp_identity_key(private_key_);
+ TmpFile tmp_identity_cert(cert_chain_);
+ // Create FileWatcherCertificateProvider.
+ FileWatcherCertificateProvider provider(tmp_identity_key.name(),
+ tmp_identity_cert.name(),
+ tmp_root_cert.name(), 1);
+ WatcherState* watcher_state_1 =
+ MakeWatcher(provider.distributor(), kCertName, kCertName);
+ // Expect to see the credential data.
+ EXPECT_THAT(watcher_state_1->GetCredentialQueue(),
+ ::testing::ElementsAre(CredentialInfo(
+ root_cert_, MakeCertKeyPairs(private_key_.c_str(),
+ cert_chain_.c_str()))));
+ // Copy new data to files.
+ // TODO(ZhenLian): right now it is not completely atomic. Use the real atomic
+ // update when the directory renaming is added in gpr.
+ tmp_identity_key.RewriteFile(private_key_2_);
+ tmp_identity_cert.RewriteFile(cert_chain_2_);
+ // Wait 2 seconds for the provider's refresh thread to read the updated files.
+ gpr_sleep_until(gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC),
+ gpr_time_from_seconds(2, GPR_TIMESPAN)));
+ // Expect to see the new credential data.
+ EXPECT_THAT(watcher_state_1->GetCredentialQueue(),
+ ::testing::ElementsAre(CredentialInfo(
+ root_cert_, MakeCertKeyPairs(private_key_2_.c_str(),
+ cert_chain_2_.c_str()))));
+ // Clean up.
+ CancelWatch(watcher_state_1);
+}
+
+TEST_F(GrpcTlsCertificateProviderTest,
+ FileWatcherCertificateProviderWithGoodAtFirstThenDeletedBothCerts) {
+ // Create temporary files and copy cert data into it.
+ auto tmp_root_cert = absl::make_unique<TmpFile>(root_cert_);
+ auto tmp_identity_key = absl::make_unique<TmpFile>(private_key_);
+ auto tmp_identity_cert = absl::make_unique<TmpFile>(cert_chain_);
+ // Create FileWatcherCertificateProvider.
+ FileWatcherCertificateProvider provider(tmp_identity_key->name(),
+ tmp_identity_cert->name(),
+ tmp_root_cert->name(), 1);
+ WatcherState* watcher_state_1 =
+ MakeWatcher(provider.distributor(), kCertName, kCertName);
+ // The initial data is all good, so we expect to have successful credential
+ // updates.
+ EXPECT_THAT(watcher_state_1->GetCredentialQueue(),
+ ::testing::ElementsAre(CredentialInfo(
+ root_cert_, MakeCertKeyPairs(private_key_.c_str(),
+ cert_chain_.c_str()))));
+ // Delete TmpFile objects, which will remove the corresponding files.
+ tmp_root_cert.reset();
+ tmp_identity_key.reset();
+ tmp_identity_cert.reset();
+ // Wait 2 seconds for the provider's refresh thread to read the deleted files.
+ gpr_sleep_until(gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC),
+ gpr_time_from_seconds(2, GPR_TIMESPAN)));
+ // Expect to see errors sent to watchers, and no credential updates.
+ // We have no ideas on how many errors we will receive, so we only check once.
+ EXPECT_THAT(watcher_state_1->GetErrorQueue(),
+ ::testing::Contains(ErrorInfo(kRootError, kIdentityError)));
+ EXPECT_THAT(watcher_state_1->GetCredentialQueue(), ::testing::ElementsAre());
+ // Clean up.
+ CancelWatch(watcher_state_1);
+}
+
+TEST_F(GrpcTlsCertificateProviderTest,
+ FileWatcherCertificateProviderWithGoodAtFirstThenDeletedRootCerts) {
+ // Create temporary files and copy cert data into it.
+ auto tmp_root_cert = absl::make_unique<TmpFile>(root_cert_);
+ TmpFile tmp_identity_key(private_key_);
+ TmpFile tmp_identity_cert(cert_chain_);
+ // Create FileWatcherCertificateProvider.
+ FileWatcherCertificateProvider provider(tmp_identity_key.name(),
+ tmp_identity_cert.name(),
+ tmp_root_cert->name(), 1);
+ WatcherState* watcher_state_1 =
+ MakeWatcher(provider.distributor(), kCertName, kCertName);
+ // The initial data is all good, so we expect to have successful credential
+ // updates.
+ EXPECT_THAT(watcher_state_1->GetCredentialQueue(),
+ ::testing::ElementsAre(CredentialInfo(
+ root_cert_, MakeCertKeyPairs(private_key_.c_str(),
+ cert_chain_.c_str()))));
+ // Delete root TmpFile object, which will remove the corresponding file.
+ tmp_root_cert.reset();
+ // Wait 2 seconds for the provider's refresh thread to read the deleted files.
+ gpr_sleep_until(gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC),
+ gpr_time_from_seconds(2, GPR_TIMESPAN)));
+ // Expect to see errors sent to watchers, and no credential updates.
+ // We have no ideas on how many errors we will receive, so we only check once.
+ EXPECT_THAT(watcher_state_1->GetErrorQueue(),
+ ::testing::Contains(ErrorInfo(kRootError, "")));
+ EXPECT_THAT(watcher_state_1->GetCredentialQueue(), ::testing::ElementsAre());
+ // Clean up.
+ CancelWatch(watcher_state_1);
+}
+
+TEST_F(GrpcTlsCertificateProviderTest,
+ FileWatcherCertificateProviderWithGoodAtFirstThenDeletedIdentityCerts) {
+ // Create temporary files and copy cert data into it.
+ TmpFile tmp_root_cert(root_cert_);
+ auto tmp_identity_key = absl::make_unique<TmpFile>(private_key_);
+ auto tmp_identity_cert = absl::make_unique<TmpFile>(cert_chain_);
+ // Create FileWatcherCertificateProvider.
+ FileWatcherCertificateProvider provider(tmp_identity_key->name(),
+ tmp_identity_cert->name(),
+ tmp_root_cert.name(), 1);
+ WatcherState* watcher_state_1 =
+ MakeWatcher(provider.distributor(), kCertName, kCertName);
+ // The initial data is all good, so we expect to have successful credential
+ // updates.
+ EXPECT_THAT(watcher_state_1->GetCredentialQueue(),
+ ::testing::ElementsAre(CredentialInfo(
+ root_cert_, MakeCertKeyPairs(private_key_.c_str(),
+ cert_chain_.c_str()))));
+ // Delete identity TmpFile objects, which will remove the corresponding files.
+ tmp_identity_key.reset();
+ tmp_identity_cert.reset();
+ // Wait 2 seconds for the provider's refresh thread to read the deleted files.
+ gpr_sleep_until(gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC),
+ gpr_time_from_seconds(2, GPR_TIMESPAN)));
+ // Expect to see errors sent to watchers, and no credential updates.
+ // We have no ideas on how many errors we will receive, so we only check once.
+ EXPECT_THAT(watcher_state_1->GetErrorQueue(),
+ ::testing::Contains(ErrorInfo("", kIdentityError)));
+ EXPECT_THAT(watcher_state_1->GetCredentialQueue(), ::testing::ElementsAre());
+ // Clean up.
+ CancelWatch(watcher_state_1);
+}
+
+} // namespace testing
+
+} // namespace grpc_core
+
+int main(int argc, char** argv) {
+ grpc::testing::TestEnvironment env(argc, argv);
+ ::testing::InitGoogleTest(&argc, argv);
+ grpc_init();
+ int ret = RUN_ALL_TESTS();
+ grpc_shutdown();
+ return ret;
+}
diff --git a/test/core/security/grpc_tls_credentials_options_test.cc b/test/core/security/grpc_tls_credentials_options_test.cc
index dee1d47..932b478 100644
--- a/test/core/security/grpc_tls_credentials_options_test.cc
+++ b/test/core/security/grpc_tls_credentials_options_test.cc
@@ -24,24 +24,440 @@
#include <grpc/support/string_util.h>
#include <gtest/gtest.h>
+#include "src/core/lib/gpr/tmpfile.h"
#include "src/core/lib/iomgr/load_file.h"
+#include "src/core/lib/security/credentials/tls/tls_credentials.h"
+#include "src/core/lib/security/security_connector/tls/tls_security_connector.h"
+#include "test/core/security/tls_utils.h"
#include "test/core/util/test_config.h"
#define CA_CERT_PATH "src/core/tsi/test_creds/ca.pem"
#define SERVER_CERT_PATH "src/core/tsi/test_creds/server1.pem"
#define SERVER_KEY_PATH "src/core/tsi/test_creds/server1.key"
+#define CA_CERT_PATH_2 "src/core/tsi/test_creds/multi-domain.pem"
+#define SERVER_CERT_PATH_2 "src/core/tsi/test_creds/server0.pem"
+#define SERVER_KEY_PATH_2 "src/core/tsi/test_creds/server0.key"
+#define INVALID_PATH "invalid/path"
+
+namespace grpc_core {
namespace testing {
-TEST(GrpcTlsCredentialsOptionsTest, ErrorDetails) {
+class GrpcTlsCredentialsOptionsTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ root_cert_ = GetFileContents(CA_CERT_PATH);
+ cert_chain_ = GetFileContents(SERVER_CERT_PATH);
+ private_key_ = GetFileContents(SERVER_KEY_PATH);
+ root_cert_2_ = GetFileContents(CA_CERT_PATH_2);
+ cert_chain_2_ = GetFileContents(SERVER_CERT_PATH_2);
+ private_key_2_ = GetFileContents(SERVER_KEY_PATH_2);
+ }
+
+ std::string root_cert_;
+ std::string private_key_;
+ std::string cert_chain_;
+ std::string root_cert_2_;
+ std::string private_key_2_;
+ std::string cert_chain_2_;
+};
+
+TEST_F(GrpcTlsCredentialsOptionsTest, ErrorDetails) {
grpc_tls_error_details error_details;
EXPECT_STREQ(error_details.error_details().c_str(), "");
error_details.set_error_details("test error details");
EXPECT_STREQ(error_details.error_details().c_str(), "test error details");
}
+// Tests for StaticDataCertificateProvider.
+TEST_F(GrpcTlsCredentialsOptionsTest,
+ ClientOptionsWithStaticDataProviderOnBothCerts) {
+ auto options = MakeRefCounted<grpc_tls_credentials_options>();
+ auto provider = MakeRefCounted<StaticDataCertificateProvider>(
+ root_cert_, MakeCertKeyPairs(private_key_.c_str(), cert_chain_.c_str()));
+ options->set_certificate_provider(std::move(provider));
+ options->set_watch_root_cert(true);
+ options->set_watch_identity_pair(true);
+ options->set_server_verification_option(GRPC_TLS_SERVER_VERIFICATION);
+ auto credentials = MakeRefCounted<TlsCredentials>(options);
+ ASSERT_NE(credentials, nullptr);
+ grpc_channel_args* new_args = nullptr;
+ auto connector = credentials->create_security_connector(
+ nullptr, "random targets", nullptr, &new_args);
+ grpc_channel_args_destroy(new_args);
+ ASSERT_NE(connector, nullptr);
+ TlsChannelSecurityConnector* tls_connector =
+ static_cast<TlsChannelSecurityConnector*>(connector.get());
+ EXPECT_NE(tls_connector->ClientHandshakerFactoryForTesting(), nullptr);
+ EXPECT_TRUE(tls_connector->RootCertsForTesting().has_value());
+ EXPECT_TRUE(tls_connector->KeyCertPairListForTesting().has_value());
+}
+
+TEST_F(GrpcTlsCredentialsOptionsTest,
+ ClientOptionsWithStaticDataProviderOnRootCerts) {
+ auto options = MakeRefCounted<grpc_tls_credentials_options>();
+ auto provider = MakeRefCounted<StaticDataCertificateProvider>(
+ root_cert_, PemKeyCertPairList());
+ options->set_certificate_provider(std::move(provider));
+ options->set_watch_root_cert(true);
+ options->set_server_verification_option(GRPC_TLS_SERVER_VERIFICATION);
+ auto credentials = MakeRefCounted<TlsCredentials>(options);
+ ASSERT_NE(credentials, nullptr);
+ grpc_channel_args* new_args = nullptr;
+ auto connector = credentials->create_security_connector(
+ nullptr, "random targets", nullptr, &new_args);
+ grpc_channel_args_destroy(new_args);
+ ASSERT_NE(connector, nullptr);
+ TlsChannelSecurityConnector* tls_connector =
+ static_cast<TlsChannelSecurityConnector*>(connector.get());
+ EXPECT_NE(tls_connector->ClientHandshakerFactoryForTesting(), nullptr);
+ EXPECT_TRUE(tls_connector->RootCertsForTesting().has_value());
+ EXPECT_FALSE(tls_connector->KeyCertPairListForTesting().has_value());
+}
+
+TEST_F(GrpcTlsCredentialsOptionsTest,
+ ClientOptionsWithStaticDataProviderOnNotProvidedCerts) {
+ auto options = MakeRefCounted<grpc_tls_credentials_options>();
+ auto provider = MakeRefCounted<StaticDataCertificateProvider>(
+ "", MakeCertKeyPairs(private_key_.c_str(), cert_chain_.c_str()));
+ options->set_certificate_provider(std::move(provider));
+ options->set_watch_root_cert(true);
+ options->set_server_verification_option(GRPC_TLS_SERVER_VERIFICATION);
+ auto credentials = MakeRefCounted<TlsCredentials>(options);
+ ASSERT_NE(credentials, nullptr);
+ grpc_channel_args* new_args = nullptr;
+ auto connector = credentials->create_security_connector(
+ nullptr, "random targets", nullptr, &new_args);
+ grpc_channel_args_destroy(new_args);
+ ASSERT_NE(connector, nullptr);
+ TlsChannelSecurityConnector* tls_connector =
+ static_cast<TlsChannelSecurityConnector*>(connector.get());
+ EXPECT_EQ(tls_connector->ClientHandshakerFactoryForTesting(), nullptr);
+}
+
+TEST_F(GrpcTlsCredentialsOptionsTest,
+ ServerOptionsWithStaticDataProviderOnBothCerts) {
+ auto options = MakeRefCounted<grpc_tls_credentials_options>();
+ auto provider = MakeRefCounted<StaticDataCertificateProvider>(
+ root_cert_, MakeCertKeyPairs(private_key_.c_str(), cert_chain_.c_str()));
+ options->set_certificate_provider(std::move(provider));
+ options->set_watch_root_cert(true);
+ options->set_watch_identity_pair(true);
+ options->set_cert_request_type(
+ GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY);
+ auto credentials = MakeRefCounted<TlsServerCredentials>(options);
+ ASSERT_NE(credentials, nullptr);
+ auto connector = credentials->create_security_connector();
+ ASSERT_NE(connector, nullptr);
+ TlsServerSecurityConnector* tls_connector =
+ static_cast<TlsServerSecurityConnector*>(connector.get());
+ EXPECT_NE(tls_connector->ServerHandshakerFactoryForTesting(), nullptr);
+ EXPECT_TRUE(tls_connector->RootCertsForTesting().has_value());
+ EXPECT_TRUE(tls_connector->KeyCertPairListForTesting().has_value());
+}
+
+TEST_F(GrpcTlsCredentialsOptionsTest,
+ ServerOptionsWithStaticDataProviderOnIdentityCerts) {
+ auto options = MakeRefCounted<grpc_tls_credentials_options>();
+ auto provider = MakeRefCounted<StaticDataCertificateProvider>(
+ "", MakeCertKeyPairs(private_key_.c_str(), cert_chain_.c_str()));
+ options->set_certificate_provider(std::move(provider));
+ options->set_watch_identity_pair(true);
+ options->set_cert_request_type(GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE);
+ auto credentials = MakeRefCounted<TlsServerCredentials>(options);
+ ASSERT_NE(credentials, nullptr);
+ auto connector = credentials->create_security_connector();
+ ASSERT_NE(connector, nullptr);
+ TlsServerSecurityConnector* tls_connector =
+ static_cast<TlsServerSecurityConnector*>(connector.get());
+ EXPECT_NE(tls_connector->ServerHandshakerFactoryForTesting(), nullptr);
+ EXPECT_FALSE(tls_connector->RootCertsForTesting().has_value());
+ EXPECT_TRUE(tls_connector->KeyCertPairListForTesting().has_value());
+}
+
+TEST_F(GrpcTlsCredentialsOptionsTest,
+ ServerOptionsWithStaticDataProviderOnNotProvidedCerts) {
+ auto options = MakeRefCounted<grpc_tls_credentials_options>();
+ auto provider = MakeRefCounted<StaticDataCertificateProvider>(
+ root_cert_, PemKeyCertPairList());
+ options->set_certificate_provider(std::move(provider));
+ options->set_watch_identity_pair(true);
+ options->set_cert_request_type(GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE);
+ auto credentials = MakeRefCounted<TlsServerCredentials>(options);
+ ASSERT_NE(credentials, nullptr);
+ auto connector = credentials->create_security_connector();
+ ASSERT_NE(connector, nullptr);
+ TlsServerSecurityConnector* tls_connector =
+ static_cast<TlsServerSecurityConnector*>(connector.get());
+ EXPECT_EQ(tls_connector->ServerHandshakerFactoryForTesting(), nullptr);
+}
+
+//// Tests for FileWatcherCertificateProvider.
+TEST_F(GrpcTlsCredentialsOptionsTest,
+ ClientOptionsWithCertWatcherProviderOnBothCerts) {
+ auto options = MakeRefCounted<grpc_tls_credentials_options>();
+ auto provider = MakeRefCounted<FileWatcherCertificateProvider>(
+ SERVER_KEY_PATH, SERVER_CERT_PATH, CA_CERT_PATH, 1);
+ options->set_certificate_provider(std::move(provider));
+ options->set_watch_root_cert(true);
+ options->set_watch_identity_pair(true);
+ options->set_server_verification_option(GRPC_TLS_SERVER_VERIFICATION);
+ auto credentials = MakeRefCounted<TlsCredentials>(options);
+ ASSERT_NE(credentials, nullptr);
+ grpc_channel_args* new_args = nullptr;
+ auto connector = credentials->create_security_connector(
+ nullptr, "random targets", nullptr, &new_args);
+ grpc_channel_args_destroy(new_args);
+ ASSERT_NE(connector, nullptr);
+ TlsChannelSecurityConnector* tls_connector =
+ static_cast<TlsChannelSecurityConnector*>(connector.get());
+ EXPECT_NE(tls_connector->ClientHandshakerFactoryForTesting(), nullptr);
+ EXPECT_TRUE(tls_connector->RootCertsForTesting().has_value());
+ EXPECT_TRUE(tls_connector->KeyCertPairListForTesting().has_value());
+}
+
+TEST_F(GrpcTlsCredentialsOptionsTest,
+ ClientOptionsWithCertWatcherProviderOnRootCerts) {
+ auto options = MakeRefCounted<grpc_tls_credentials_options>();
+ auto provider =
+ MakeRefCounted<FileWatcherCertificateProvider>("", "", CA_CERT_PATH, 1);
+ options->set_certificate_provider(std::move(provider));
+ options->set_watch_root_cert(true);
+ options->set_server_verification_option(GRPC_TLS_SERVER_VERIFICATION);
+ auto credentials = MakeRefCounted<TlsCredentials>(options);
+ ASSERT_NE(credentials, nullptr);
+ grpc_channel_args* new_args = nullptr;
+ auto connector = credentials->create_security_connector(
+ nullptr, "random targets", nullptr, &new_args);
+ grpc_channel_args_destroy(new_args);
+ ASSERT_NE(connector, nullptr);
+ TlsChannelSecurityConnector* tls_connector =
+ static_cast<TlsChannelSecurityConnector*>(connector.get());
+ EXPECT_NE(tls_connector->ClientHandshakerFactoryForTesting(), nullptr);
+ EXPECT_TRUE(tls_connector->RootCertsForTesting().has_value());
+ EXPECT_FALSE(tls_connector->KeyCertPairListForTesting().has_value());
+}
+
+TEST_F(GrpcTlsCredentialsOptionsTest,
+ ClientOptionsWithCertWatcherProviderOnNotProvidedCerts) {
+ auto options = MakeRefCounted<grpc_tls_credentials_options>();
+ auto provider = MakeRefCounted<FileWatcherCertificateProvider>(
+ SERVER_KEY_PATH, SERVER_CERT_PATH, "", 1);
+ options->set_certificate_provider(std::move(provider));
+ options->set_watch_root_cert(true);
+ options->set_server_verification_option(GRPC_TLS_SERVER_VERIFICATION);
+ auto credentials = MakeRefCounted<TlsCredentials>(options);
+ ASSERT_NE(credentials, nullptr);
+ grpc_channel_args* new_args = nullptr;
+ auto connector = credentials->create_security_connector(
+ nullptr, "random targets", nullptr, &new_args);
+ grpc_channel_args_destroy(new_args);
+ ASSERT_NE(connector, nullptr);
+ TlsChannelSecurityConnector* tls_connector =
+ static_cast<TlsChannelSecurityConnector*>(connector.get());
+ EXPECT_EQ(tls_connector->ClientHandshakerFactoryForTesting(), nullptr);
+}
+
+TEST_F(GrpcTlsCredentialsOptionsTest,
+ ClientOptionsWithCertWatcherProviderOnBadTrustCerts) {
+ auto options = MakeRefCounted<grpc_tls_credentials_options>();
+ auto provider =
+ MakeRefCounted<FileWatcherCertificateProvider>("", "", INVALID_PATH, 1);
+ options->set_certificate_provider(std::move(provider));
+ options->set_watch_root_cert(true);
+ options->set_server_verification_option(GRPC_TLS_SERVER_VERIFICATION);
+ auto credentials = MakeRefCounted<TlsCredentials>(options);
+ ASSERT_NE(credentials, nullptr);
+ grpc_channel_args* new_args = nullptr;
+ auto connector = credentials->create_security_connector(
+ nullptr, "random targets", nullptr, &new_args);
+ grpc_channel_args_destroy(new_args);
+ ASSERT_NE(connector, nullptr);
+ TlsChannelSecurityConnector* tls_connector =
+ static_cast<TlsChannelSecurityConnector*>(connector.get());
+ EXPECT_EQ(tls_connector->ClientHandshakerFactoryForTesting(), nullptr);
+}
+
+TEST_F(GrpcTlsCredentialsOptionsTest,
+ ServerOptionsWithCertWatcherProviderOnBothCerts) {
+ auto options = MakeRefCounted<grpc_tls_credentials_options>();
+ auto provider = MakeRefCounted<FileWatcherCertificateProvider>(
+ SERVER_KEY_PATH, SERVER_CERT_PATH, CA_CERT_PATH, 1);
+ options->set_certificate_provider(std::move(provider));
+ options->set_watch_root_cert(true);
+ options->set_watch_identity_pair(true);
+ options->set_cert_request_type(
+ GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY);
+ auto credentials = MakeRefCounted<TlsServerCredentials>(options);
+ ASSERT_NE(credentials, nullptr);
+ auto connector = credentials->create_security_connector();
+ ASSERT_NE(connector, nullptr);
+ TlsServerSecurityConnector* tls_connector =
+ static_cast<TlsServerSecurityConnector*>(connector.get());
+ EXPECT_NE(tls_connector->ServerHandshakerFactoryForTesting(), nullptr);
+ EXPECT_TRUE(tls_connector->RootCertsForTesting().has_value());
+ EXPECT_TRUE(tls_connector->KeyCertPairListForTesting().has_value());
+}
+
+TEST_F(GrpcTlsCredentialsOptionsTest,
+ ServerOptionsWithCertWatcherProviderOnIdentityCerts) {
+ auto options = MakeRefCounted<grpc_tls_credentials_options>();
+ auto provider = MakeRefCounted<FileWatcherCertificateProvider>(
+ SERVER_KEY_PATH, SERVER_CERT_PATH, "", 1);
+ options->set_certificate_provider(std::move(provider));
+ options->set_watch_identity_pair(true);
+ options->set_cert_request_type(GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE);
+ auto credentials = MakeRefCounted<TlsServerCredentials>(options);
+ ASSERT_NE(credentials, nullptr);
+ auto connector = credentials->create_security_connector();
+ ASSERT_NE(connector, nullptr);
+ TlsServerSecurityConnector* tls_connector =
+ static_cast<TlsServerSecurityConnector*>(connector.get());
+ EXPECT_NE(tls_connector->ServerHandshakerFactoryForTesting(), nullptr);
+ EXPECT_FALSE(tls_connector->RootCertsForTesting().has_value());
+ EXPECT_TRUE(tls_connector->KeyCertPairListForTesting().has_value());
+}
+
+TEST_F(GrpcTlsCredentialsOptionsTest,
+ ServerOptionsWithCertWatcherProviderOnNotProvidedCerts) {
+ auto options = MakeRefCounted<grpc_tls_credentials_options>();
+ auto provider =
+ MakeRefCounted<FileWatcherCertificateProvider>("", "", CA_CERT_PATH, 1);
+ options->set_certificate_provider(std::move(provider));
+ options->set_watch_identity_pair(true);
+ options->set_cert_request_type(GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE);
+ auto credentials = MakeRefCounted<TlsServerCredentials>(options);
+ ASSERT_NE(credentials, nullptr);
+ auto connector = credentials->create_security_connector();
+ ASSERT_NE(connector, nullptr);
+ TlsServerSecurityConnector* tls_connector =
+ static_cast<TlsServerSecurityConnector*>(connector.get());
+ EXPECT_EQ(tls_connector->ServerHandshakerFactoryForTesting(), nullptr);
+}
+
+TEST_F(GrpcTlsCredentialsOptionsTest,
+ ServerOptionsWithCertWatcherProviderOnBadIdentityCerts) {
+ auto options = MakeRefCounted<grpc_tls_credentials_options>();
+ auto provider = MakeRefCounted<FileWatcherCertificateProvider>(
+ INVALID_PATH, INVALID_PATH, "", 1);
+ options->set_certificate_provider(std::move(provider));
+ options->set_watch_identity_pair(true);
+ options->set_cert_request_type(GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE);
+ auto credentials = MakeRefCounted<TlsServerCredentials>(options);
+ ASSERT_NE(credentials, nullptr);
+ auto connector = credentials->create_security_connector();
+ ASSERT_NE(connector, nullptr);
+ TlsServerSecurityConnector* tls_connector =
+ static_cast<TlsServerSecurityConnector*>(connector.get());
+ EXPECT_EQ(tls_connector->ServerHandshakerFactoryForTesting(), nullptr);
+}
+
+// The following tests write credential data to temporary files to test the
+// transition behavior of the provider.
+TEST_F(GrpcTlsCredentialsOptionsTest,
+ ClientOptionsWithCertWatcherProviderOnCertificateRefreshed) {
+ // Create temporary files and copy cert data into them.
+ TmpFile tmp_root_cert(root_cert_);
+ TmpFile tmp_identity_key(private_key_);
+ TmpFile tmp_identity_cert(cert_chain_);
+ // Create ClientOptions using FileWatcherCertificateProvider.
+ auto options = MakeRefCounted<grpc_tls_credentials_options>();
+ auto provider = MakeRefCounted<FileWatcherCertificateProvider>(
+ tmp_identity_key.name(), tmp_identity_cert.name(), tmp_root_cert.name(),
+ 1);
+ options->set_certificate_provider(std::move(provider));
+ options->set_watch_root_cert(true);
+ options->set_watch_identity_pair(true);
+ options->set_server_verification_option(GRPC_TLS_SERVER_VERIFICATION);
+ auto credentials = MakeRefCounted<TlsCredentials>(options);
+ ASSERT_NE(credentials, nullptr);
+ grpc_channel_args* new_args = nullptr;
+ auto connector = credentials->create_security_connector(
+ nullptr, "random targets", nullptr, &new_args);
+ grpc_channel_args_destroy(new_args);
+ ASSERT_NE(connector, nullptr);
+ TlsChannelSecurityConnector* tls_connector =
+ static_cast<TlsChannelSecurityConnector*>(connector.get());
+ // Expect to see the credential data.
+ EXPECT_NE(tls_connector->ClientHandshakerFactoryForTesting(), nullptr);
+ ASSERT_TRUE(tls_connector->RootCertsForTesting().has_value());
+ EXPECT_EQ(tls_connector->RootCertsForTesting(), root_cert_);
+ ASSERT_TRUE(tls_connector->KeyCertPairListForTesting().has_value());
+ EXPECT_EQ(tls_connector->KeyCertPairListForTesting(),
+ MakeCertKeyPairs(private_key_.c_str(), cert_chain_.c_str()));
+ // Copy new data to files.
+ // TODO(ZhenLian): right now it is not completely atomic. Use the real atomic
+ // update when the directory renaming is added in gpr.
+ tmp_root_cert.RewriteFile(root_cert_2_);
+ tmp_identity_key.RewriteFile(private_key_2_);
+ tmp_identity_cert.RewriteFile(cert_chain_2_);
+ // Wait 2 seconds for the provider's refresh thread to read the updated files.
+ gpr_sleep_until(gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC),
+ gpr_time_from_seconds(2, GPR_TIMESPAN)));
+ // Expect to see new credential data loaded by the security connector.
+ EXPECT_NE(tls_connector->ClientHandshakerFactoryForTesting(), nullptr);
+ ASSERT_TRUE(tls_connector->RootCertsForTesting().has_value());
+ EXPECT_EQ(tls_connector->RootCertsForTesting(), root_cert_2_);
+ ASSERT_TRUE(tls_connector->KeyCertPairListForTesting().has_value());
+ EXPECT_EQ(tls_connector->KeyCertPairListForTesting(),
+ MakeCertKeyPairs(private_key_2_.c_str(), cert_chain_2_.c_str()));
+}
+
+TEST_F(GrpcTlsCredentialsOptionsTest,
+ ClientOptionsWithCertWatcherProviderOnDeletedFiles) {
+ // Create temporary files and copy cert data into it.
+ auto tmp_root_cert = absl::make_unique<TmpFile>(root_cert_);
+ auto tmp_identity_key = absl::make_unique<TmpFile>(private_key_);
+ auto tmp_identity_cert = absl::make_unique<TmpFile>(cert_chain_);
+ // Create ClientOptions using FileWatcherCertificateProvider.
+ auto options = MakeRefCounted<grpc_tls_credentials_options>();
+ auto provider = MakeRefCounted<FileWatcherCertificateProvider>(
+ tmp_identity_key->name(), tmp_identity_cert->name(),
+ tmp_root_cert->name(), 1);
+ options->set_certificate_provider(std::move(provider));
+ options->set_watch_root_cert(true);
+ options->set_watch_identity_pair(true);
+ options->set_server_verification_option(GRPC_TLS_SERVER_VERIFICATION);
+ auto credentials = MakeRefCounted<TlsCredentials>(options);
+ ASSERT_NE(credentials, nullptr);
+ grpc_channel_args* new_args = nullptr;
+ auto connector = credentials->create_security_connector(
+ nullptr, "random targets", nullptr, &new_args);
+ grpc_channel_args_destroy(new_args);
+ ASSERT_NE(connector, nullptr);
+ TlsChannelSecurityConnector* tls_connector =
+ static_cast<TlsChannelSecurityConnector*>(connector.get());
+ // The initial data is all good, so we expect to have successful credential
+ // updates.
+ EXPECT_NE(tls_connector->ClientHandshakerFactoryForTesting(), nullptr);
+ ASSERT_TRUE(tls_connector->RootCertsForTesting().has_value());
+ EXPECT_EQ(tls_connector->RootCertsForTesting(), root_cert_);
+ ASSERT_TRUE(tls_connector->KeyCertPairListForTesting().has_value());
+ EXPECT_EQ(tls_connector->KeyCertPairListForTesting(),
+ MakeCertKeyPairs(private_key_.c_str(), cert_chain_.c_str()));
+ // Delete TmpFile objects, which will remove the corresponding files.
+ tmp_root_cert.reset();
+ tmp_identity_key.reset();
+ tmp_identity_cert.reset();
+ // Wait 2 seconds for the provider's refresh thread to read the deleted files.
+ gpr_sleep_until(gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC),
+ gpr_time_from_seconds(2, GPR_TIMESPAN)));
+ // It's a bit hard to test if errors are sent to the security connector,
+ // because the security connector simply logs the error. We will see the err
+ // messages if we open the log.
+ // The old certs should still being used.
+ EXPECT_NE(tls_connector->ClientHandshakerFactoryForTesting(), nullptr);
+ ASSERT_TRUE(tls_connector->RootCertsForTesting().has_value());
+ EXPECT_EQ(tls_connector->RootCertsForTesting(), root_cert_);
+ ASSERT_TRUE(tls_connector->KeyCertPairListForTesting().has_value());
+ EXPECT_EQ(tls_connector->KeyCertPairListForTesting(),
+ MakeCertKeyPairs(private_key_.c_str(), cert_chain_.c_str()));
+}
+
} // namespace testing
+} // namespace grpc_core
+
int main(int argc, char** argv) {
grpc::testing::TestEnvironment env(argc, argv);
::testing::InitGoogleTest(&argc, argv);
diff --git a/test/core/security/tls_utils.cc b/test/core/security/tls_utils.cc
new file mode 100644
index 0000000..4f56ed2
--- /dev/null
+++ b/test/core/security/tls_utils.cc
@@ -0,0 +1,83 @@
+//
+// Copyright 2020 gRPC authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include "test/core/security/tls_utils.h"
+
+#include "src/core/lib/gpr/tmpfile.h"
+#include "src/core/lib/iomgr/load_file.h"
+#include "src/core/lib/slice/slice_internal.h"
+
+namespace grpc_core {
+
+namespace testing {
+
+TmpFile::TmpFile(absl::string_view credential_data) {
+ name_ = CreateTmpFileAndWriteData(credential_data);
+ GPR_ASSERT(!name_.empty());
+}
+
+TmpFile::~TmpFile() { GPR_ASSERT(remove(name_.c_str()) == 0); }
+
+void TmpFile::RewriteFile(absl::string_view credential_data) {
+ // Create a new file containing new data.
+ std::string new_name = CreateTmpFileAndWriteData(credential_data);
+ GPR_ASSERT(!new_name.empty());
+ // Remove the old file.
+ GPR_ASSERT(remove(name_.c_str()) == 0);
+ // Rename the new file to the original name.
+ GPR_ASSERT(rename(new_name.c_str(), name_.c_str()) == 0);
+}
+
+std::string TmpFile::CreateTmpFileAndWriteData(
+ absl::string_view credential_data) {
+ char* name = nullptr;
+ FILE* file_descriptor = gpr_tmpfile("GrpcTlsCertificateProviderTest", &name);
+ GPR_ASSERT(fwrite(credential_data.data(), 1, credential_data.size(),
+ file_descriptor) == credential_data.size());
+ GPR_ASSERT(fclose(file_descriptor) == 0);
+ GPR_ASSERT(file_descriptor != nullptr);
+ GPR_ASSERT(name != nullptr);
+ std::string name_to_return = name;
+ gpr_free(name);
+ return name_to_return;
+}
+
+PemKeyCertPairList MakeCertKeyPairs(const char* private_key,
+ const char* certs) {
+ if (strcmp(private_key, "") == 0 && strcmp(certs, "") == 0) {
+ return {};
+ }
+ grpc_ssl_pem_key_cert_pair* ssl_pair =
+ static_cast<grpc_ssl_pem_key_cert_pair*>(
+ gpr_malloc(sizeof(grpc_ssl_pem_key_cert_pair)));
+ ssl_pair->private_key = gpr_strdup(private_key);
+ ssl_pair->cert_chain = gpr_strdup(certs);
+ PemKeyCertPairList pem_key_cert_pairs;
+ pem_key_cert_pairs.emplace_back(ssl_pair);
+ return pem_key_cert_pairs;
+}
+
+std::string GetFileContents(const char* path) {
+ grpc_slice slice = grpc_empty_slice();
+ GPR_ASSERT(GRPC_LOG_IF_ERROR("load_file", grpc_load_file(path, 0, &slice)));
+ std::string credential = std::string(StringViewFromSlice(slice));
+ grpc_slice_unref(slice);
+ return credential;
+}
+
+} // namespace testing
+
+} // namespace grpc_core
diff --git a/test/core/security/tls_utils.h b/test/core/security/tls_utils.h
new file mode 100644
index 0000000..94d29ef
--- /dev/null
+++ b/test/core/security/tls_utils.h
@@ -0,0 +1,47 @@
+//
+// Copyright 2020 gRPC authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include "src/core/lib/security/security_connector/ssl_utils.h"
+
+namespace grpc_core {
+
+namespace testing {
+
+class TmpFile {
+ public:
+ // Create a temporary file with |credential_data| written in.
+ explicit TmpFile(absl::string_view credential_data);
+
+ ~TmpFile();
+
+ const std::string& name() { return name_; }
+
+ // Rewrite |credential_data| to the temporary file, in an atomic way.
+ void RewriteFile(absl::string_view credential_data);
+
+ private:
+ std::string CreateTmpFileAndWriteData(absl::string_view credential_data);
+
+ std::string name_;
+};
+
+PemKeyCertPairList MakeCertKeyPairs(const char* private_key, const char* certs);
+
+std::string GetFileContents(const char* path);
+
+} // namespace testing
+
+} // namespace grpc_core
diff --git a/test/core/surface/public_headers_must_be_c89.c b/test/core/surface/public_headers_must_be_c89.c
index 4b821be..cd0095f 100644
--- a/test/core/surface/public_headers_must_be_c89.c
+++ b/test/core/surface/public_headers_must_be_c89.c
@@ -206,6 +206,7 @@
printf("%lx", (unsigned long) grpc_tls_identity_pairs_add_pair);
printf("%lx", (unsigned long) grpc_tls_identity_pairs_destroy);
printf("%lx", (unsigned long) grpc_tls_certificate_provider_static_data_create);
+ printf("%lx", (unsigned long) grpc_tls_certificate_provider_file_watcher_create);
printf("%lx", (unsigned long) grpc_tls_certificate_provider_release);
printf("%lx", (unsigned long) grpc_tls_credentials_options_create);
printf("%lx", (unsigned long) grpc_tls_credentials_options_set_cert_request_type);
diff --git a/test/cpp/client/BUILD b/test/cpp/client/BUILD
index ce0f6d3..c62c633 100644
--- a/test/cpp/client/BUILD
+++ b/test/cpp/client/BUILD
@@ -21,6 +21,11 @@
grpc_cc_test(
name = "credentials_test",
srcs = ["credentials_test.cc"],
+ data = [
+ "//src/core/tsi/test_creds:ca.pem",
+ "//src/core/tsi/test_creds:server1.key",
+ "//src/core/tsi/test_creds:server1.pem",
+ ],
external_deps = [
"gtest",
],
diff --git a/test/cpp/client/credentials_test.cc b/test/cpp/client/credentials_test.cc
index 0d9eaf0..b2ec888 100644
--- a/test/cpp/client/credentials_test.cc
+++ b/test/cpp/client/credentials_test.cc
@@ -32,6 +32,10 @@
#include "src/cpp/client/secure_credentials.h"
#include "src/cpp/common/tls_credentials_options_util.h"
+#define CA_CERT_PATH "src/core/tsi/test_creds/ca.pem"
+#define SERVER_CERT_PATH "src/core/tsi/test_creds/server1.pem"
+#define SERVER_KEY_PATH "src/core/tsi/test_creds/server1.key"
+
namespace {
constexpr const char* kRootCertName = "root_cert_name";
@@ -40,6 +44,7 @@
constexpr const char* kIdentityCertPrivateKey = "identity_private_key";
constexpr const char* kIdentityCertContents = "identity_cert_contents";
+using ::grpc::experimental::FileWatcherCertificateProvider;
using ::grpc::experimental::StaticDataCertificateProvider;
using ::grpc::experimental::TlsServerAuthorizationCheckArg;
using ::grpc::experimental::TlsServerAuthorizationCheckConfig;
@@ -407,6 +412,52 @@
GPR_ASSERT(channel_credentials.get() != nullptr);
}
+TEST(
+ CredentialsTest,
+ TlsChannelCredentialsWithFileWatcherCertificateProviderLoadingRootAndIdentity) {
+ auto certificate_provider = std::make_shared<FileWatcherCertificateProvider>(
+ SERVER_KEY_PATH, SERVER_CERT_PATH, CA_CERT_PATH, 1);
+ grpc::experimental::TlsChannelCredentialsOptions options(
+ certificate_provider);
+ options.watch_root_certs();
+ options.set_root_cert_name(kRootCertName);
+ options.watch_identity_key_cert_pairs();
+ options.set_identity_cert_name(kIdentityCertName);
+ options.set_server_verification_option(GRPC_TLS_SERVER_VERIFICATION);
+ auto test_server_authorization_check =
+ std::make_shared<TestTlsServerAuthorizationCheck>();
+ auto server_authorization_check_config =
+ std::make_shared<TlsServerAuthorizationCheckConfig>(
+ test_server_authorization_check);
+ options.set_server_authorization_check_config(
+ server_authorization_check_config);
+ auto channel_credentials = grpc::experimental::TlsCredentials(options);
+ GPR_ASSERT(channel_credentials.get() != nullptr);
+}
+
+// ChannelCredentials should always have root credential presented.
+// Otherwise the system root certificates will be loaded, which will cause
+// failure in some tests under MacOS/Windows.
+TEST(CredentialsTest,
+ TlsChannelCredentialsWithFileWatcherCertificateProviderLoadingRootOnly) {
+ auto certificate_provider =
+ std::make_shared<FileWatcherCertificateProvider>(CA_CERT_PATH, 1);
+ grpc::experimental::TlsChannelCredentialsOptions options(
+ certificate_provider);
+ options.watch_root_certs();
+ options.set_root_cert_name(kRootCertName);
+ options.set_server_verification_option(GRPC_TLS_SERVER_VERIFICATION);
+ auto test_server_authorization_check =
+ std::make_shared<TestTlsServerAuthorizationCheck>();
+ auto server_authorization_check_config =
+ std::make_shared<TlsServerAuthorizationCheckConfig>(
+ test_server_authorization_check);
+ options.set_server_authorization_check_config(
+ server_authorization_check_config);
+ auto channel_credentials = grpc::experimental::TlsCredentials(options);
+ GPR_ASSERT(channel_credentials.get() != nullptr);
+}
+
TEST(CredentialsTest, TlsServerAuthorizationCheckConfigErrorMessages) {
std::shared_ptr<TlsServerAuthorizationCheckConfig> config(
new TlsServerAuthorizationCheckConfig(nullptr));
diff --git a/test/cpp/server/BUILD b/test/cpp/server/BUILD
index c2ccd8a..cb1972a 100644
--- a/test/cpp/server/BUILD
+++ b/test/cpp/server/BUILD
@@ -63,6 +63,11 @@
grpc_cc_test(
name = "credentials_test",
srcs = ["credentials_test.cc"],
+ data = [
+ "//src/core/tsi/test_creds:ca.pem",
+ "//src/core/tsi/test_creds:server1.key",
+ "//src/core/tsi/test_creds:server1.pem",
+ ],
external_deps = [
"gtest",
],
diff --git a/test/cpp/server/credentials_test.cc b/test/cpp/server/credentials_test.cc
index 8e7fc17..3c8dcf7 100644
--- a/test/cpp/server/credentials_test.cc
+++ b/test/cpp/server/credentials_test.cc
@@ -27,6 +27,10 @@
#include "test/core/util/port.h"
#include "test/core/util/test_config.h"
+#define CA_CERT_PATH "src/core/tsi/test_creds/ca.pem"
+#define SERVER_CERT_PATH "src/core/tsi/test_creds/server1.pem"
+#define SERVER_KEY_PATH "src/core/tsi/test_creds/server1.key"
+
namespace {
constexpr const char* kRootCertName = "root_cert_name";
@@ -35,6 +39,7 @@
constexpr const char* kIdentityCertPrivateKey = "identity_private_key";
constexpr const char* kIdentityCertContents = "identity_cert_contents";
+using ::grpc::experimental::FileWatcherCertificateProvider;
using ::grpc::experimental::StaticDataCertificateProvider;
} // namespace
@@ -86,6 +91,38 @@
GPR_ASSERT(server_credentials.get() != nullptr);
}
+TEST(
+ CredentialsTest,
+ TlsServerCredentialsWithFileWatcherCertificateProviderLoadingRootAndIdentity) {
+ auto certificate_provider = std::make_shared<FileWatcherCertificateProvider>(
+ SERVER_KEY_PATH, SERVER_CERT_PATH, CA_CERT_PATH, 1);
+ grpc::experimental::TlsServerCredentialsOptions options(certificate_provider);
+ options.watch_root_certs();
+ options.set_root_cert_name(kRootCertName);
+ options.watch_identity_key_cert_pairs();
+ options.set_identity_cert_name(kIdentityCertName);
+ options.set_cert_request_type(
+ GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY);
+ auto server_credentials = grpc::experimental::TlsServerCredentials(options);
+ GPR_ASSERT(server_credentials.get() != nullptr);
+}
+
+// ServerCredentials should always have identity credential presented.
+// Otherwise gRPC stack will fail.
+TEST(
+ CredentialsTest,
+ TlsServerCredentialsWithFileWatcherCertificateProviderLoadingIdentityOnly) {
+ auto certificate_provider = std::make_shared<FileWatcherCertificateProvider>(
+ SERVER_KEY_PATH, SERVER_CERT_PATH, 1);
+ grpc::experimental::TlsServerCredentialsOptions options(certificate_provider);
+ options.watch_identity_key_cert_pairs();
+ options.set_identity_cert_name(kIdentityCertName);
+ options.set_cert_request_type(
+ GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY);
+ auto server_credentials = grpc::experimental::TlsServerCredentials(options);
+ GPR_ASSERT(server_credentials.get() != nullptr);
+}
+
} // namespace
} // namespace testing
} // namespace grpc
diff --git a/tools/run_tests/generated/tests.json b/tools/run_tests/generated/tests.json
index 61ac2ff..48bb34f 100644
--- a/tools/run_tests/generated/tests.json
+++ b/tools/run_tests/generated/tests.json
@@ -4658,6 +4658,30 @@
"flaky": false,
"gtest": true,
"language": "c++",
+ "name": "grpc_tls_certificate_provider_test",
+ "platforms": [
+ "linux",
+ "mac",
+ "posix",
+ "windows"
+ ],
+ "uses_polling": true
+ },
+ {
+ "args": [],
+ "benchmark": false,
+ "ci_platforms": [
+ "linux",
+ "mac",
+ "posix",
+ "windows"
+ ],
+ "cpu_cost": 1.0,
+ "exclude_configs": [],
+ "exclude_iomgrs": [],
+ "flaky": false,
+ "gtest": true,
+ "language": "c++",
"name": "grpc_tls_credentials_options_test",
"platforms": [
"linux",