1#!/usr/bin/env python3 2 3# Copyright 2022 gRPC authors. 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16 17# Generator script for src/core/lib/security/credentials/tls/grpc_tls_credentials_options.h and test/core/security/grpc_tls_credentials_options_comparator_test.cc 18# Should be executed from grpc's root directory. 19 20from __future__ import print_function 21 22import collections 23from dataclasses import dataclass 24import difflib 25import filecmp 26import os 27import sys 28import tempfile 29 30 31@dataclass 32class DataMember: 33 name: str # name of the data member without the trailing '_' 34 type: str # Type (eg. std::string, bool) 35 test_name: str # The name to use for the associated test 36 test_value_1: str # Test-specific value to use for comparison 37 test_value_2: str # Test-specific value (different from test_value_1) 38 default_initializer: str = ( # If non-empty, this will be used as the default initialization of this field 39 "" 40 ) 41 getter_comment: str = "" # Comment to add before the getter for this field 42 special_getter_return_type: str = ( # Override for the return type of getter (eg. const std::string&) 43 "" 44 ) 45 override_getter: str = ( # Override for the entire getter method. Relevant for certificate_verifier and certificate_provider 46 "" 47 ) 48 setter_comment: str = "" # Commend to add before the setter for this field 49 setter_move_semantics: bool = False # Should the setter use move-semantics 50 special_comparator: str = ( # If non-empty, this will be used in `operator==` 51 "" 52 ) 53 54 55_DATA_MEMBERS = [ 56 DataMember( 57 name="cert_request_type", 58 type="grpc_ssl_client_certificate_request_type", 59 default_initializer="GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE", 60 test_name="DifferentCertRequestType", 61 test_value_1="GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE", 62 test_value_2="GRPC_SSL_REQUEST_CLIENT_CERTIFICATE_AND_VERIFY", 63 ), 64 DataMember( 65 name="verify_server_cert", 66 type="bool", 67 default_initializer="true", 68 test_name="DifferentVerifyServerCert", 69 test_value_1="false", 70 test_value_2="true", 71 ), 72 DataMember( 73 name="min_tls_version", 74 type="grpc_tls_version", 75 default_initializer="grpc_tls_version::TLS1_2", 76 test_name="DifferentMinTlsVersion", 77 test_value_1="grpc_tls_version::TLS1_2", 78 test_value_2="grpc_tls_version::TLS1_3", 79 ), 80 DataMember( 81 name="max_tls_version", 82 type="grpc_tls_version", 83 default_initializer="grpc_tls_version::TLS1_3", 84 test_name="DifferentMaxTlsVersion", 85 test_value_1="grpc_tls_version::TLS1_2", 86 test_value_2="grpc_tls_version::TLS1_3", 87 ), 88 DataMember( 89 name="certificate_verifier", 90 type="grpc_core::RefCountedPtr<grpc_tls_certificate_verifier>", 91 override_getter="""grpc_tls_certificate_verifier* certificate_verifier() { 92 return certificate_verifier_.get(); 93 }""", 94 setter_move_semantics=True, 95 special_comparator=( 96 "(certificate_verifier_ == other.certificate_verifier_ ||" 97 " (certificate_verifier_ != nullptr && other.certificate_verifier_" 98 " != nullptr &&" 99 " certificate_verifier_->Compare(other.certificate_verifier_.get())" 100 " == 0))" 101 ), 102 test_name="DifferentCertificateVerifier", 103 test_value_1="MakeRefCounted<HostNameCertificateVerifier>()", 104 test_value_2="MakeRefCounted<XdsCertificateVerifier>(nullptr)", 105 ), 106 DataMember( 107 name="check_call_host", 108 type="bool", 109 default_initializer="true", 110 test_name="DifferentCheckCallHost", 111 test_value_1="false", 112 test_value_2="true", 113 ), 114 DataMember( 115 name="certificate_provider", 116 type="grpc_core::RefCountedPtr<grpc_tls_certificate_provider>", 117 getter_comment=( 118 "Returns the distributor from certificate_provider_ if it is set," 119 " nullptr otherwise." 120 ), 121 override_getter="""grpc_tls_certificate_distributor* certificate_distributor() { 122 if (certificate_provider_ != nullptr) { return certificate_provider_->distributor().get(); } 123 return nullptr; 124 }""", 125 setter_move_semantics=True, 126 special_comparator=( 127 "(certificate_provider_ == other.certificate_provider_ ||" 128 " (certificate_provider_ != nullptr && other.certificate_provider_" 129 " != nullptr &&" 130 " certificate_provider_->Compare(other.certificate_provider_.get())" 131 " == 0))" 132 ), 133 test_name="DifferentCertificateProvider", 134 test_value_1=( 135 'MakeRefCounted<StaticDataCertificateProvider>("root_cert_1",' 136 " PemKeyCertPairList())" 137 ), 138 test_value_2=( 139 'MakeRefCounted<StaticDataCertificateProvider>("root_cert_2",' 140 " PemKeyCertPairList())" 141 ), 142 ), 143 DataMember( 144 name="watch_root_cert", 145 type="bool", 146 default_initializer="false", 147 setter_comment=( 148 "If need to watch the updates of root certificates with name" 149 " |root_cert_name|. The default value is false. If used in" 150 " tls_credentials, it should always be set to true unless the root" 151 " certificates are not needed." 152 ), 153 test_name="DifferentWatchRootCert", 154 test_value_1="false", 155 test_value_2="true", 156 ), 157 DataMember( 158 name="root_cert_name", 159 type="std::string", 160 special_getter_return_type="const std::string&", 161 setter_comment=( 162 "Sets the name of root certificates being watched, if" 163 " |set_watch_root_cert| is called. If not set, an empty string will" 164 " be used as the name." 165 ), 166 setter_move_semantics=True, 167 test_name="DifferentRootCertName", 168 test_value_1='"root_cert_name_1"', 169 test_value_2='"root_cert_name_2"', 170 ), 171 DataMember( 172 name="watch_identity_pair", 173 type="bool", 174 default_initializer="false", 175 setter_comment=( 176 "If need to watch the updates of identity certificates with name" 177 " |identity_cert_name|. The default value is false. If used in" 178 " tls_credentials, it should always be set to true unless the" 179 " identity key-cert pairs are not needed." 180 ), 181 test_name="DifferentWatchIdentityPair", 182 test_value_1="false", 183 test_value_2="true", 184 ), 185 DataMember( 186 name="identity_cert_name", 187 type="std::string", 188 special_getter_return_type="const std::string&", 189 setter_comment=( 190 "Sets the name of identity key-cert pairs being watched, if" 191 " |set_watch_identity_pair| is called. If not set, an empty string" 192 " will be used as the name." 193 ), 194 setter_move_semantics=True, 195 test_name="DifferentIdentityCertName", 196 test_value_1='"identity_cert_name_1"', 197 test_value_2='"identity_cert_name_2"', 198 ), 199 DataMember( 200 name="tls_session_key_log_file_path", 201 type="std::string", 202 special_getter_return_type="const std::string&", 203 setter_move_semantics=True, 204 test_name="DifferentTlsSessionKeyLogFilePath", 205 test_value_1='"file_path_1"', 206 test_value_2='"file_path_2"', 207 ), 208 DataMember( 209 name="crl_directory", 210 type="std::string", 211 special_getter_return_type="const std::string&", 212 setter_comment=( 213 " gRPC will enforce CRLs on all handshakes from all hashed CRL" 214 " files inside of the crl_directory. If not set, an empty string" 215 " will be used, which will not enable CRL checking. Only supported" 216 " for OpenSSL version > 1.1." 217 ), 218 setter_move_semantics=True, 219 test_name="DifferentCrlDirectory", 220 test_value_1='"crl_directory_1"', 221 test_value_2='"crl_directory_2"', 222 ), 223 DataMember( 224 name="crl_provider", 225 type="std::shared_ptr<grpc_core::experimental::CrlProvider>", 226 getter_comment=("Returns the CRL Provider"), 227 setter_move_semantics=True, 228 special_comparator=("(crl_provider_ == other.crl_provider_)"), 229 test_name="DifferentCrlProvider", 230 test_value_1=("*experimental::CreateStaticCrlProvider({})"), 231 test_value_2=("*experimental::CreateStaticCrlProvider({})"), 232 ), 233 DataMember( 234 name="send_client_ca_list", 235 type="bool", 236 default_initializer="false", 237 test_name="DifferentSendClientCaListValues", 238 test_value_1="false", 239 test_value_2="true", 240 ), 241] 242 243 244# print copyright notice from this file 245def put_copyright(f, year): 246 print( 247 """// 248// 249// Copyright %s gRPC authors. 250// 251// Licensed under the Apache License, Version 2.0 (the "License"); 252// you may not use this file except in compliance with the License. 253// You may obtain a copy of the License at 254// 255// http://www.apache.org/licenses/LICENSE-2.0 256// 257// Unless required by applicable law or agreed to in writing, software 258// distributed under the License is distributed on an "AS IS" BASIS, 259// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 260// See the License for the specific language governing permissions and 261// limitations under the License. 262// 263// 264""" 265 % (year), 266 file=f, 267 ) 268 269 270# Prints differences between two files 271def get_file_differences(file1, file2): 272 with open(file1) as f1: 273 file1_text = f1.readlines() 274 with open(file2) as f2: 275 file2_text = f2.readlines() 276 return difflib.unified_diff( 277 file1_text, file2_text, fromfile=file1, tofile=file2 278 ) 279 280 281# Is this script executed in test mode? 282test_mode = False 283if len(sys.argv) > 1 and sys.argv[1] == "--test": 284 test_mode = True 285 286HEADER_FILE_NAME = ( 287 "src/core/lib/security/credentials/tls/grpc_tls_credentials_options.h" 288) 289# Generate src/core/lib/security/credentials/tls/grpc_tls_credentials_options.h 290header_file_name = HEADER_FILE_NAME 291if test_mode: 292 header_file_name = tempfile.NamedTemporaryFile(delete=False).name 293H = open(header_file_name, "w") 294 295put_copyright(H, "2018") 296print( 297 "// Generated by tools/codegen/core/gen_grpc_tls_credentials_options.py\n", 298 file=H, 299) 300print( 301 """#ifndef GRPC_SRC_CORE_LIB_SECURITY_CREDENTIALS_TLS_GRPC_TLS_CREDENTIALS_OPTIONS_H 302#define GRPC_SRC_CORE_LIB_SECURITY_CREDENTIALS_TLS_GRPC_TLS_CREDENTIALS_OPTIONS_H 303 304#include <grpc/support/port_platform.h> 305 306#include "absl/container/inlined_vector.h" 307 308#include <grpc/credentials.h> 309#include <grpc/grpc_security.h> 310 311#include "src/core/util/ref_counted.h" 312#include "src/core/lib/security/credentials/tls/grpc_tls_certificate_distributor.h" 313#include "src/core/lib/security/credentials/tls/grpc_tls_certificate_provider.h" 314#include "src/core/lib/security/credentials/tls/grpc_tls_certificate_verifier.h" 315#include "src/core/lib/security/security_connector/ssl_utils.h" 316 317// Contains configurable options specified by callers to configure their certain 318// security features supported in TLS. 319// TODO(ZhenLian): consider making this not ref-counted. 320struct grpc_tls_credentials_options 321 : public grpc_core::RefCounted<grpc_tls_credentials_options> { 322 public: 323 grpc_tls_credentials_options() = default; 324 ~grpc_tls_credentials_options() override = default; 325""", 326 file=H, 327) 328 329# Print out getters for all data members 330print(" // Getters for member fields.", file=H) 331for data_member in _DATA_MEMBERS: 332 if data_member.getter_comment != "": 333 print(" // " + data_member.getter_comment, file=H) 334 if data_member.override_getter: 335 print(" " + data_member.override_getter, file=H) 336 else: 337 print( 338 " %s %s() const { return %s; }" 339 % ( 340 data_member.special_getter_return_type 341 if data_member.special_getter_return_type != "" 342 else data_member.type, 343 data_member.name, 344 data_member.name + "_", 345 ), 346 file=H, 347 ) 348 349# Print out setters for all data members 350print("", file=H) 351print(" // Setters for member fields.", file=H) 352for data_member in _DATA_MEMBERS: 353 if data_member.setter_comment != "": 354 print(" // " + data_member.setter_comment, file=H) 355 if data_member.setter_move_semantics: 356 print( 357 " void set_%s(%s %s) { %s_ = std::move(%s); }" 358 % ( 359 data_member.name, 360 data_member.type, 361 data_member.name, 362 data_member.name, 363 data_member.name, 364 ), 365 file=H, 366 ) 367 else: 368 print( 369 " void set_%s(%s %s) { %s_ = %s; }" 370 % ( 371 data_member.name, 372 data_member.type, 373 data_member.name, 374 data_member.name, 375 data_member.name, 376 ), 377 file=H, 378 ) 379 380# Write out operator== 381print( 382 "\n bool operator==(const grpc_tls_credentials_options& other) const {", 383 file=H, 384) 385operator_equal_content = " return " 386for i in range(len(_DATA_MEMBERS)): 387 if i != 0: 388 operator_equal_content += " " 389 if _DATA_MEMBERS[i].special_comparator != "": 390 operator_equal_content += _DATA_MEMBERS[i].special_comparator 391 else: 392 operator_equal_content += ( 393 _DATA_MEMBERS[i].name + "_ == other." + _DATA_MEMBERS[i].name + "_" 394 ) 395 if i != len(_DATA_MEMBERS) - 1: 396 operator_equal_content += " &&\n" 397print(operator_equal_content + ";\n }", file=H) 398 399# Write out copy constructor 400print( 401 "\n grpc_tls_credentials_options(grpc_tls_credentials_options& other) :", 402 file=H, 403) 404operator_equal_content = " " 405for i in range(len(_DATA_MEMBERS)): 406 if i != 0: 407 operator_equal_content += " " 408 if i == len(_DATA_MEMBERS) - 1: 409 operator_equal_content += ( 410 _DATA_MEMBERS[i].name + "_(other." + _DATA_MEMBERS[i].name + "_)" 411 ) 412 else: 413 operator_equal_content += ( 414 _DATA_MEMBERS[i].name + "_(other." + _DATA_MEMBERS[i].name + "_),\n" 415 ) 416print(operator_equal_content + " {}", file=H) 417 418# Print out data member declarations 419print("\n private:", file=H) 420for data_member in _DATA_MEMBERS: 421 if data_member.default_initializer == "": 422 print( 423 " %s %s_;" 424 % ( 425 data_member.type, 426 data_member.name, 427 ), 428 file=H, 429 ) 430 else: 431 print( 432 " %s %s_ = %s;" 433 % ( 434 data_member.type, 435 data_member.name, 436 data_member.default_initializer, 437 ), 438 file=H, 439 ) 440 441# Print out file ending 442print( 443 """}; 444 445#endif // GRPC_SRC_CORE_LIB_SECURITY_CREDENTIALS_TLS_GRPC_TLS_CREDENTIALS_OPTIONS_H""", 446 file=H, 447) 448 449H.close() 450 451# Generate test/core/security/grpc_tls_credentials_options_comparator_test.cc 452TEST_FILE_NAME = ( 453 "test/core/security/grpc_tls_credentials_options_comparator_test.cc" 454) 455test_file_name = TEST_FILE_NAME 456if test_mode: 457 test_file_name = tempfile.NamedTemporaryFile(delete=False).name 458T = open(test_file_name, "w") 459 460put_copyright(T, "2022") 461print( 462 "// Generated by tools/codegen/core/gen_grpc_tls_credentials_options.py", 463 file=T, 464) 465print( 466 """ 467#include <grpc/support/port_platform.h> 468 469#include <string> 470 471#include <gmock/gmock.h> 472 473#include <grpc/credentials.h> 474 475#include "src/core/lib/security/credentials/xds/xds_credentials.h" 476#include "src/core/lib/security/credentials/tls/grpc_tls_credentials_options.h" 477#include "test/core/test_util/test_config.h" 478 479namespace grpc_core { 480namespace { 481""", 482 file=T, 483) 484 485# Generate negative test for each negative member 486for data_member in _DATA_MEMBERS: 487 print( 488 """TEST(TlsCredentialsOptionsComparatorTest, %s) { 489 auto* options_1 = grpc_tls_credentials_options_create(); 490 auto* options_2 = grpc_tls_credentials_options_create(); 491 options_1->set_%s(%s); 492 options_2->set_%s(%s); 493 EXPECT_FALSE(*options_1 == *options_2); 494 EXPECT_FALSE(*options_2 == *options_1); 495 delete options_1; 496 delete options_2; 497}""" 498 % ( 499 data_member.test_name, 500 data_member.name, 501 data_member.test_value_1, 502 data_member.name, 503 data_member.test_value_2, 504 ), 505 file=T, 506 ) 507 508# Print out file ending 509print( 510 """ 511} // namespace 512} // namespace grpc_core 513 514int main(int argc, char** argv) { 515 testing::InitGoogleTest(&argc, argv); 516 grpc::testing::TestEnvironment env(&argc, argv); 517 grpc_init(); 518 auto result = RUN_ALL_TESTS(); 519 grpc_shutdown(); 520 return result; 521}""", 522 file=T, 523) 524T.close() 525 526if test_mode: 527 header_diff = get_file_differences(header_file_name, HEADER_FILE_NAME) 528 test_diff = get_file_differences(test_file_name, TEST_FILE_NAME) 529 os.unlink(header_file_name) 530 os.unlink(test_file_name) 531 header_error = False 532 for line in header_diff: 533 print(line) 534 header_error = True 535 if header_error: 536 print( 537 HEADER_FILE_NAME 538 + " should not be manually modified. Please make changes to" 539 " tools/distrib/gen_grpc_tls_credentials_options.py instead." 540 ) 541 test_error = False 542 for line in test_diff: 543 print(line) 544 test_error = True 545 if test_error: 546 print( 547 TEST_FILE_NAME 548 + " should not be manually modified. Please make changes to" 549 " tools/distrib/gen_grpc_tls_credentials_options.py instead." 550 ) 551 if header_error or test_error: 552 sys.exit(1) 553