Skip to content

Commit 24b147d

Browse files
authored
gRPC: add the ability to use a custom SSL certificate in integration tests (#1942)
1 parent d8bb9b3 commit 24b147d

File tree

3 files changed

+81
-3
lines changed

3 files changed

+81
-3
lines changed

Firestore/Example/Tests/Util/FSTIntegrationTestCase.mm

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636

3737
#include "Firestore/core/src/firebase/firestore/auth/empty_credentials_provider.h"
3838
#include "Firestore/core/src/firebase/firestore/model/database_id.h"
39+
#include "Firestore/core/src/firebase/firestore/remote/grpc_connection.h"
3940
#include "Firestore/core/src/firebase/firestore/util/autoid.h"
4041
#include "Firestore/core/src/firebase/firestore/util/filesystem.h"
4142
#include "Firestore/core/src/firebase/firestore/util/path.h"
@@ -57,6 +58,7 @@
5758
using firebase::firestore::auth::EmptyCredentialsProvider;
5859
using firebase::firestore::model::DatabaseId;
5960
using firebase::firestore::testutil::AppForUnitTesting;
61+
using firebase::firestore::remote::GrpcConnection;
6062
using firebase::firestore::util::CreateAutoId;
6163
using firebase::firestore::util::Path;
6264
using firebase::firestore::util::Status;
@@ -164,6 +166,8 @@ + (void)setUpDefaults {
164166
"setup_integration_tests.py to properly configure testing SSL certificates.");
165167
}
166168
[GRPCCall useTestCertsPath:certsPath testName:@"test_cert_2" forHost:defaultSettings.host];
169+
GrpcConnection::UseTestCertificate([certsPath cStringUsingEncoding:NSASCIIStringEncoding],
170+
"test_cert_2");
167171
}
168172

169173
+ (NSString *)projectID {

Firestore/core/src/firebase/firestore/remote/grpc_connection.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_REMOTE_GRPC_CONNECTION_H_
1919

2020
#include <memory>
21+
#include <string>
2122
#include <vector>
2223

2324
#include "Firestore/core/src/firebase/firestore/auth/token.h"
@@ -78,7 +79,27 @@ class GrpcConnection {
7879
void Register(GrpcCall* call);
7980
void Unregister(GrpcCall* call);
8081

82+
/**
83+
* For tests only: use a custom root certificate file and the given SSL
84+
* target name for all connections. Call before creating any streams or calls.
85+
*/
86+
static void UseTestCertificate(absl::string_view certificate_path,
87+
absl::string_view target_name);
88+
89+
/**
90+
* For tests only: don't use SSL, send all traffic unencrypted. Call before
91+
* creating any streams or calls. Overrides a test certificate.
92+
*/
93+
static void UseInsecureChannel();
94+
8195
private:
96+
struct TestCredentials {
97+
std::string certificate_path;
98+
std::string target_name;
99+
bool use_insecure_channel = false;
100+
};
101+
static TestCredentials* test_credentials_;
102+
82103
std::unique_ptr<grpc::ClientContext> CreateContext(
83104
const auth::Token& credential) const;
84105
std::shared_ptr<grpc::Channel> CreateChannel() const;

Firestore/core/src/firebase/firestore/remote/grpc_connection.mm

Lines changed: 56 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
#include "Firestore/core/src/firebase/firestore/remote/grpc_connection.h"
1818

1919
#include <algorithm>
20+
#include <fstream>
21+
#include <sstream>
2022
#include <string>
2123
#include <utility>
2224

@@ -52,6 +54,8 @@
5254

5355
} // namespace
5456

57+
GrpcConnection::TestCredentials* GrpcConnection::test_credentials_ = nullptr;
58+
5559
GrpcConnection::GrpcConnection(const DatabaseInfo& database_info,
5660
util::AsyncQueue* worker_queue,
5761
grpc::CompletionQueue* grpc_queue,
@@ -116,9 +120,31 @@
116120
}
117121

118122
std::shared_ptr<grpc::Channel> GrpcConnection::CreateChannel() const {
119-
return grpc::CreateChannel(
120-
database_info_->host(),
121-
grpc::SslCredentials(grpc::SslCredentialsOptions()));
123+
if (!test_credentials_) {
124+
return grpc::CreateChannel(
125+
database_info_->host(),
126+
grpc::SslCredentials(grpc::SslCredentialsOptions()));
127+
}
128+
129+
if (test_credentials_->use_insecure_channel) {
130+
return grpc::CreateChannel(database_info_->host(),
131+
grpc::InsecureChannelCredentials());
132+
}
133+
134+
std::ifstream cert_file{test_credentials_->certificate_path};
135+
HARD_ASSERT(cert_file.good(),
136+
StringFormat("Unable to open root certificates at file path %s",
137+
test_credentials_->certificate_path)
138+
.c_str());
139+
std::stringstream cert_buffer;
140+
cert_buffer << cert_file.rdbuf();
141+
grpc::SslCredentialsOptions options;
142+
options.pem_root_certs = cert_buffer.str();
143+
144+
grpc::ChannelArguments args;
145+
args.SetSslTargetNameOverride(test_credentials_->target_name);
146+
return grpc::CreateCustomChannel(database_info_->host(),
147+
grpc::SslCredentials(options), args);
122148
}
123149

124150
std::unique_ptr<GrpcStream> GrpcConnection::CreateStream(
@@ -183,6 +209,33 @@
183209
active_calls_.erase(found);
184210
}
185211

212+
/*static*/ void GrpcConnection::UseTestCertificate(
213+
absl::string_view certificate_path, absl::string_view target_name) {
214+
HARD_ASSERT(!certificate_path.empty(), "Empty path to test certificate");
215+
HARD_ASSERT(!target_name.empty(), "Empty SSL target name");
216+
217+
if (!test_credentials_) {
218+
// Deliberately never deleted.
219+
test_credentials_ = new TestCredentials{};
220+
}
221+
222+
test_credentials_->certificate_path =
223+
std::string{certificate_path.data(), certificate_path.size()};
224+
test_credentials_->target_name =
225+
std::string{target_name.data(), target_name.size()};
226+
// TODO(varconst): hostname if necessary.
227+
}
228+
229+
/*static*/ void GrpcConnection::UseInsecureChannel() {
230+
if (!test_credentials_) {
231+
// Deliberately never deleted.
232+
test_credentials_ = new TestCredentials{};
233+
}
234+
235+
test_credentials_->use_insecure_channel = true;
236+
// TODO(varconst): hostname if necessary.
237+
}
238+
186239
} // namespace remote
187240
} // namespace firestore
188241
} // namespace firebase

0 commit comments

Comments
 (0)