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 getter_comment: str = '' # Comment to add before the getter for this field 40 special_getter_return_type: str = '' # Override for the return type of getter (eg. const std::string&) 41 override_getter: str = '' # Override for the entire getter method. Relevant for certificate_verifier and certificate_provider 42 setter_comment: str = '' # Commend to add before the setter for this field 43 setter_move_semantics: bool = False # Should the setter use move-semantics 44 special_comparator: str = '' # If non-empty, this will be used in `operator==` 45 46 47_DATA_MEMBERS = [ 48 DataMember(name='cert_request_type', 49 type='grpc_ssl_client_certificate_request_type', 50 default_initializer='GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE', 51 test_name="DifferentCertRequestType", 52 test_value_1="GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE", 53 test_value_2="GRPC_SSL_REQUEST_CLIENT_CERTIFICATE_AND_VERIFY"), 54 DataMember(name='verify_server_cert', 55 type='bool', 56 default_initializer='true', 57 test_name="DifferentVerifyServerCert", 58 test_value_1="false", 59 test_value_2="true"), 60 DataMember(name='min_tls_version', 61 type='grpc_tls_version', 62 default_initializer='grpc_tls_version::TLS1_2', 63 test_name="DifferentMinTlsVersion", 64 test_value_1="grpc_tls_version::TLS1_2", 65 test_value_2="grpc_tls_version::TLS1_3"), 66 DataMember(name='max_tls_version', 67 type='grpc_tls_version', 68 default_initializer='grpc_tls_version::TLS1_3', 69 test_name="DifferentMaxTlsVersion", 70 test_value_1="grpc_tls_version::TLS1_2", 71 test_value_2="grpc_tls_version::TLS1_3"), 72 DataMember( 73 name='certificate_verifier', 74 type='grpc_core::RefCountedPtr<grpc_tls_certificate_verifier>', 75 override_getter="""grpc_tls_certificate_verifier* certificate_verifier() { 76 return certificate_verifier_.get(); 77 }""", 78 setter_move_semantics=True, 79 special_comparator= 80 '(certificate_verifier_ == other.certificate_verifier_ || (certificate_verifier_ != nullptr && other.certificate_verifier_ != nullptr && certificate_verifier_->Compare(other.certificate_verifier_.get()) == 0))', 81 test_name="DifferentCertificateVerifier", 82 test_value_1="MakeRefCounted<HostNameCertificateVerifier>()", 83 test_value_2="MakeRefCounted<XdsCertificateVerifier>(nullptr, \"\")"), 84 DataMember(name='check_call_host', 85 type='bool', 86 default_initializer='true', 87 test_name="DifferentCheckCallHost", 88 test_value_1="false", 89 test_value_2="true"), 90 DataMember( 91 name='certificate_provider', 92 type='grpc_core::RefCountedPtr<grpc_tls_certificate_provider>', 93 getter_comment= 94 'Returns the distributor from certificate_provider_ if it is set, nullptr otherwise.', 95 override_getter= 96 """grpc_tls_certificate_distributor* certificate_distributor() { 97 if (certificate_provider_ != nullptr) { return certificate_provider_->distributor().get(); } 98 return nullptr; 99 }""", 100 setter_move_semantics=True, 101 special_comparator= 102 '(certificate_provider_ == other.certificate_provider_ || (certificate_provider_ != nullptr && other.certificate_provider_ != nullptr && certificate_provider_->Compare(other.certificate_provider_.get()) == 0))', 103 test_name="DifferentCertificateProvider", 104 test_value_1= 105 "MakeRefCounted<StaticDataCertificateProvider>(\"root_cert_1\", PemKeyCertPairList())", 106 test_value_2= 107 "MakeRefCounted<StaticDataCertificateProvider>(\"root_cert_2\", PemKeyCertPairList())" 108 ), 109 DataMember( 110 name='watch_root_cert', 111 type='bool', 112 default_initializer='false', 113 setter_comment= 114 'If need to watch the updates of root certificates with name |root_cert_name|. The default value is false. If used in tls_credentials, it should always be set to true unless the root certificates are not needed.', 115 test_name="DifferentWatchRootCert", 116 test_value_1="false", 117 test_value_2="true"), 118 DataMember( 119 name='root_cert_name', 120 type='std::string', 121 special_getter_return_type='const std::string&', 122 setter_comment= 123 'Sets the name of root certificates being watched, if |set_watch_root_cert| is called. If not set, an empty string will be used as the name.', 124 setter_move_semantics=True, 125 test_name="DifferentRootCertName", 126 test_value_1="\"root_cert_name_1\"", 127 test_value_2="\"root_cert_name_2\""), 128 DataMember( 129 name='watch_identity_pair', 130 type='bool', 131 default_initializer='false', 132 setter_comment= 133 'If need to watch the updates of identity certificates with name |identity_cert_name|. The default value is false. If used in tls_credentials, it should always be set to true unless the identity key-cert pairs are not needed.', 134 test_name="DifferentWatchIdentityPair", 135 test_value_1="false", 136 test_value_2="true"), 137 DataMember( 138 name='identity_cert_name', 139 type='std::string', 140 special_getter_return_type='const std::string&', 141 setter_comment= 142 'Sets the name of identity key-cert pairs being watched, if |set_watch_identity_pair| is called. If not set, an empty string will be used as the name.', 143 setter_move_semantics=True, 144 test_name="DifferentIdentityCertName", 145 test_value_1="\"identity_cert_name_1\"", 146 test_value_2="\"identity_cert_name_2\""), 147 DataMember(name='tls_session_key_log_file_path', 148 type='std::string', 149 special_getter_return_type='const std::string&', 150 setter_move_semantics=True, 151 test_name="DifferentTlsSessionKeyLogFilePath", 152 test_value_1="\"file_path_1\"", 153 test_value_2="\"file_path_2\""), 154 DataMember( 155 name='crl_directory', 156 type='std::string', 157 special_getter_return_type='const std::string&', 158 setter_comment= 159 ' gRPC will enforce CRLs on all handshakes from all hashed CRL files inside of the crl_directory. If not set, an empty string will be used, which will not enable CRL checking. Only supported for OpenSSL version > 1.1.', 160 setter_move_semantics=True, 161 test_name="DifferentCrlDirectory", 162 test_value_1="\"crl_directory_1\"", 163 test_value_2="\"crl_directory_2\""), 164 DataMember( 165 name="send_client_ca_list", 166 type="bool", 167 default_initializer="false", 168 test_name="DifferentSendClientCaListValues", 169 test_value_1="false", 170 test_value_2="true", 171 ), 172] 173 174 175# print copyright notice from this file 176def put_copyright(f, year): 177 print("""// 178// 179// Copyright %s gRPC authors. 180// 181// Licensed under the Apache License, Version 2.0 (the "License"); 182// you may not use this file except in compliance with the License. 183// You may obtain a copy of the License at 184// 185// http://www.apache.org/licenses/LICENSE-2.0 186// 187// Unless required by applicable law or agreed to in writing, software 188// distributed under the License is distributed on an "AS IS" BASIS, 189// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 190// See the License for the specific language governing permissions and 191// limitations under the License. 192// 193// 194""" % (year), 195 file=f) 196 197 198# Prints differences between two files 199def get_file_differences(file1, file2): 200 with open(file1) as f1: 201 file1_text = f1.readlines() 202 with open(file2) as f2: 203 file2_text = f2.readlines() 204 return difflib.unified_diff(file1_text, 205 file2_text, 206 fromfile=file1, 207 tofile=file2) 208 209 210# Is this script executed in test mode? 211test_mode = False 212if len(sys.argv) > 1 and sys.argv[1] == "--test": 213 test_mode = True 214 215HEADER_FILE_NAME = 'src/core/lib/security/credentials/tls/grpc_tls_credentials_options.h' 216# Generate src/core/lib/security/credentials/tls/grpc_tls_credentials_options.h 217header_file_name = HEADER_FILE_NAME 218if (test_mode): 219 header_file_name = tempfile.NamedTemporaryFile(delete=False).name 220H = open(header_file_name, 'w') 221 222put_copyright(H, '2018') 223print( 224 '// Generated by tools/codegen/core/gen_grpc_tls_credentials_options.py\n', 225 file=H) 226print( 227 """#ifndef GRPC_SRC_CORE_LIB_SECURITY_CREDENTIALS_TLS_GRPC_TLS_CREDENTIALS_OPTIONS_H 228#define GRPC_SRC_CORE_LIB_SECURITY_CREDENTIALS_TLS_GRPC_TLS_CREDENTIALS_OPTIONS_H 229 230#include <grpc/support/port_platform.h> 231 232#include "absl/container/inlined_vector.h" 233 234#include <grpc/grpc_security.h> 235 236#include "src/core/lib/gprpp/ref_counted.h" 237#include "src/core/lib/security/credentials/tls/grpc_tls_certificate_distributor.h" 238#include "src/core/lib/security/credentials/tls/grpc_tls_certificate_provider.h" 239#include "src/core/lib/security/credentials/tls/grpc_tls_certificate_verifier.h" 240#include "src/core/lib/security/security_connector/ssl_utils.h" 241 242// Contains configurable options specified by callers to configure their certain 243// security features supported in TLS. 244// TODO(ZhenLian): consider making this not ref-counted. 245struct grpc_tls_credentials_options 246 : public grpc_core::RefCounted<grpc_tls_credentials_options> { 247 public: 248 ~grpc_tls_credentials_options() override = default; 249""", 250 file=H) 251 252# Print out getters for all data members 253print(" // Getters for member fields.", file=H) 254for data_member in _DATA_MEMBERS: 255 if data_member.getter_comment != '': 256 print(" // " + data_member.getter_comment, file=H) 257 if data_member.override_getter: 258 print(" " + data_member.override_getter, file=H) 259 else: 260 print( 261 " %s %s() const { return %s; }" % 262 (data_member.special_getter_return_type if 263 data_member.special_getter_return_type != '' else data_member.type, 264 data_member.name, data_member.name + '_'), 265 file=H) 266 267# Print out setters for all data members 268print("", file=H) 269print(" // Setters for member fields.", file=H) 270for data_member in _DATA_MEMBERS: 271 if data_member.setter_comment != '': 272 print(" // " + data_member.setter_comment, file=H) 273 if (data_member.setter_move_semantics): 274 print(" void set_%s(%s %s) { %s_ = std::move(%s); }" % 275 (data_member.name, data_member.type, data_member.name, 276 data_member.name, data_member.name), 277 file=H) 278 else: 279 print(" void set_%s(%s %s) { %s_ = %s; }" % 280 (data_member.name, data_member.type, data_member.name, 281 data_member.name, data_member.name), 282 file=H) 283 284# Write out operator== 285print("\n bool operator==(const grpc_tls_credentials_options& other) const {", 286 file=H) 287operator_equal_content = " return " 288for i in range(len(_DATA_MEMBERS)): 289 if (i != 0): 290 operator_equal_content += " " 291 if (_DATA_MEMBERS[i].special_comparator != ''): 292 operator_equal_content += _DATA_MEMBERS[i].special_comparator 293 else: 294 operator_equal_content += _DATA_MEMBERS[ 295 i].name + "_ == other." + _DATA_MEMBERS[i].name + "_" 296 if (i != len(_DATA_MEMBERS) - 1): 297 operator_equal_content += ' &&\n' 298print(operator_equal_content + ";\n }", file=H) 299 300#Print out data member declarations 301print("\n private:", file=H) 302for data_member in _DATA_MEMBERS: 303 if data_member.default_initializer == '': 304 print(" %s %s_;" % ( 305 data_member.type, 306 data_member.name, 307 ), file=H) 308 else: 309 print(" %s %s_ = %s;" % (data_member.type, data_member.name, 310 data_member.default_initializer), 311 file=H) 312 313# Print out file ending 314print("""}; 315 316#endif // GRPC_SRC_CORE_LIB_SECURITY_CREDENTIALS_TLS_GRPC_TLS_CREDENTIALS_OPTIONS_H""", 317 file=H) 318 319H.close() 320 321# Generate test/core/security/grpc_tls_credentials_options_comparator_test.cc 322TEST_FILE_NAME = 'test/core/security/grpc_tls_credentials_options_comparator_test.cc' 323test_file_name = TEST_FILE_NAME 324if (test_mode): 325 test_file_name = tempfile.NamedTemporaryFile(delete=False).name 326T = open(test_file_name, 'w') 327 328put_copyright(T, '2022') 329print('// Generated by tools/codegen/core/gen_grpc_tls_credentials_options.py', 330 file=T) 331print(""" 332#include <grpc/support/port_platform.h> 333 334#include <string> 335 336#include <gmock/gmock.h> 337 338#include "src/core/lib/security/credentials/xds/xds_credentials.h" 339#include "src/core/lib/security/credentials/tls/grpc_tls_credentials_options.h" 340#include "test/core/util/test_config.h" 341 342namespace grpc_core { 343namespace { 344""", 345 file=T) 346 347# Generate negative test for each negative member 348for data_member in _DATA_MEMBERS: 349 print("""TEST(TlsCredentialsOptionsComparatorTest, %s) { 350 auto* options_1 = grpc_tls_credentials_options_create(); 351 auto* options_2 = grpc_tls_credentials_options_create(); 352 options_1->set_%s(%s); 353 options_2->set_%s(%s); 354 EXPECT_FALSE(*options_1 == *options_2); 355 EXPECT_FALSE(*options_2 == *options_1); 356 delete options_1; 357 delete options_2; 358}""" % (data_member.test_name, data_member.name, data_member.test_value_1, 359 data_member.name, data_member.test_value_2), 360 file=T) 361 362# Print out file ending 363print(""" 364} // namespace 365} // namespace grpc_core 366 367int main(int argc, char** argv) { 368 testing::InitGoogleTest(&argc, argv); 369 grpc::testing::TestEnvironment env(&argc, argv); 370 grpc_init(); 371 auto result = RUN_ALL_TESTS(); 372 grpc_shutdown(); 373 return result; 374}""", 375 file=T) 376T.close() 377 378if (test_mode): 379 header_diff = get_file_differences(header_file_name, HEADER_FILE_NAME) 380 test_diff = get_file_differences(test_file_name, TEST_FILE_NAME) 381 os.unlink(header_file_name) 382 os.unlink(test_file_name) 383 header_error = False 384 for line in header_diff: 385 print(line) 386 header_error = True 387 if header_error: 388 print( 389 HEADER_FILE_NAME + 390 ' should not be manually modified. Please make changes to tools/distrib/gen_grpc_tls_credentials_options.py instead.' 391 ) 392 test_error = False 393 for line in test_diff: 394 print(line) 395 test_error = True 396 if test_error: 397 print( 398 TEST_FILE_NAME + 399 ' should not be manually modified. Please make changes to tools/distrib/gen_grpc_tls_credentials_options.py instead.' 400 ) 401 if (header_error or test_error): 402 sys.exit(1) 403