github.com/treeverse/lakefs@v1.24.1-0.20240520134607-95648127bfb0/clients/python/lakefs_sdk/configuration.py (about) 1 # coding: utf-8 2 3 """ 4 lakeFS API 5 6 lakeFS HTTP API 7 8 The version of the OpenAPI document: 1.0.0 9 Contact: services@treeverse.io 10 Generated by OpenAPI Generator (https://openapi-generator.tech) 11 12 Do not edit the class manually. 13 """ # noqa: E501 14 15 16 import copy 17 import logging 18 import multiprocessing 19 import sys 20 import urllib3 21 22 import http.client as httplib 23 from lakefs_sdk.exceptions import ApiValueError 24 25 JSON_SCHEMA_VALIDATION_KEYWORDS = { 26 'multipleOf', 'maximum', 'exclusiveMaximum', 27 'minimum', 'exclusiveMinimum', 'maxLength', 28 'minLength', 'pattern', 'maxItems', 'minItems' 29 } 30 31 class Configuration(object): 32 """This class contains various settings of the API client. 33 34 :param host: Base url. 35 :param api_key: Dict to store API key(s). 36 Each entry in the dict specifies an API key. 37 The dict key is the name of the security scheme in the OAS specification. 38 The dict value is the API key secret. 39 :param api_key_prefix: Dict to store API prefix (e.g. Bearer). 40 The dict key is the name of the security scheme in the OAS specification. 41 The dict value is an API key prefix when generating the auth data. 42 :param username: Username for HTTP basic authentication. 43 :param password: Password for HTTP basic authentication. 44 :param access_token: Access token. 45 :param server_index: Index to servers configuration. 46 :param server_variables: Mapping with string values to replace variables in 47 templated server configuration. The validation of enums is performed for 48 variables with defined enum values before. 49 :param server_operation_index: Mapping from operation ID to an index to server 50 configuration. 51 :param server_operation_variables: Mapping from operation ID to a mapping with 52 string values to replace variables in templated server configuration. 53 The validation of enums is performed for variables with defined enum values before. 54 :param ssl_ca_cert: str - the path to a file of concatenated CA certificates 55 in PEM format. 56 57 :Example: 58 59 API Key Authentication Example. 60 Given the following security scheme in the OpenAPI specification: 61 components: 62 securitySchemes: 63 cookieAuth: # name for the security scheme 64 type: apiKey 65 in: cookie 66 name: JSESSIONID # cookie name 67 68 You can programmatically set the cookie: 69 70 conf = lakefs_sdk.Configuration( 71 api_key={'cookieAuth': 'abc123'} 72 api_key_prefix={'cookieAuth': 'JSESSIONID'} 73 ) 74 75 The following cookie will be added to the HTTP request: 76 Cookie: JSESSIONID abc123 77 78 HTTP Basic Authentication Example. 79 Given the following security scheme in the OpenAPI specification: 80 components: 81 securitySchemes: 82 http_basic_auth: 83 type: http 84 scheme: basic 85 86 Configure API client with HTTP basic authentication: 87 88 conf = lakefs_sdk.Configuration( 89 username='the-user', 90 password='the-password', 91 ) 92 93 """ 94 95 _default = None 96 97 def __init__(self, host=None, 98 api_key=None, api_key_prefix=None, 99 username=None, password=None, 100 access_token=None, 101 server_index=None, server_variables=None, 102 server_operation_index=None, server_operation_variables=None, 103 ssl_ca_cert=None, 104 ): 105 """Constructor 106 """ 107 self._base_path = "/api/v1" if host is None else host 108 """Default Base url 109 """ 110 self.server_index = 0 if server_index is None and host is None else server_index 111 self.server_operation_index = server_operation_index or {} 112 """Default server index 113 """ 114 self.server_variables = server_variables or {} 115 self.server_operation_variables = server_operation_variables or {} 116 """Default server variables 117 """ 118 self.temp_folder_path = None 119 """Temp file folder for downloading files 120 """ 121 # Authentication Settings 122 self.api_key = {} 123 if api_key: 124 self.api_key = api_key 125 """dict to store API key(s) 126 """ 127 self.api_key_prefix = {} 128 if api_key_prefix: 129 self.api_key_prefix = api_key_prefix 130 """dict to store API prefix (e.g. Bearer) 131 """ 132 self.refresh_api_key_hook = None 133 """function hook to refresh API key if expired 134 """ 135 self.username = username 136 """Username for HTTP basic authentication 137 """ 138 self.password = password 139 """Password for HTTP basic authentication 140 """ 141 self.access_token = access_token 142 """Access token 143 """ 144 self.logger = {} 145 """Logging Settings 146 """ 147 self.logger["package_logger"] = logging.getLogger("lakefs_sdk") 148 self.logger["urllib3_logger"] = logging.getLogger("urllib3") 149 self.logger_format = '%(asctime)s %(levelname)s %(message)s' 150 """Log format 151 """ 152 self.logger_stream_handler = None 153 """Log stream handler 154 """ 155 self.logger_file_handler = None 156 """Log file handler 157 """ 158 self.logger_file = None 159 """Debug file location 160 """ 161 self.debug = False 162 """Debug switch 163 """ 164 165 self.verify_ssl = True 166 """SSL/TLS verification 167 Set this to false to skip verifying SSL certificate when calling API 168 from https server. 169 """ 170 self.ssl_ca_cert = ssl_ca_cert 171 """Set this to customize the certificate file to verify the peer. 172 """ 173 self.cert_file = None 174 """client certificate file 175 """ 176 self.key_file = None 177 """client key file 178 """ 179 self.assert_hostname = None 180 """Set this to True/False to enable/disable SSL hostname verification. 181 """ 182 self.tls_server_name = None 183 """SSL/TLS Server Name Indication (SNI) 184 Set this to the SNI value expected by the server. 185 """ 186 187 self.connection_pool_maxsize = multiprocessing.cpu_count() * 5 188 """urllib3 connection pool's maximum number of connections saved 189 per pool. urllib3 uses 1 connection as default value, but this is 190 not the best value when you are making a lot of possibly parallel 191 requests to the same host, which is often the case here. 192 cpu_count * 5 is used as default value to increase performance. 193 """ 194 195 self.proxy = None 196 """Proxy URL 197 """ 198 self.proxy_headers = None 199 """Proxy headers 200 """ 201 self.safe_chars_for_path_param = '' 202 """Safe chars for path_param 203 """ 204 self.retries = None 205 """Adding retries to override urllib3 default value 3 206 """ 207 # Enable client side validation 208 self.client_side_validation = True 209 210 self.socket_options = None 211 """Options to pass down to the underlying urllib3 socket 212 """ 213 214 self.datetime_format = "%Y-%m-%dT%H:%M:%S.%f%z" 215 """datetime format 216 """ 217 218 self.date_format = "%Y-%m-%d" 219 """date format 220 """ 221 222 def __deepcopy__(self, memo): 223 cls = self.__class__ 224 result = cls.__new__(cls) 225 memo[id(self)] = result 226 for k, v in self.__dict__.items(): 227 if k not in ('logger', 'logger_file_handler'): 228 setattr(result, k, copy.deepcopy(v, memo)) 229 # shallow copy of loggers 230 result.logger = copy.copy(self.logger) 231 # use setters to configure loggers 232 result.logger_file = self.logger_file 233 result.debug = self.debug 234 return result 235 236 def __setattr__(self, name, value): 237 object.__setattr__(self, name, value) 238 239 @classmethod 240 def set_default(cls, default): 241 """Set default instance of configuration. 242 243 It stores default configuration, which can be 244 returned by get_default_copy method. 245 246 :param default: object of Configuration 247 """ 248 cls._default = default 249 250 @classmethod 251 def get_default_copy(cls): 252 """Deprecated. Please use `get_default` instead. 253 254 Deprecated. Please use `get_default` instead. 255 256 :return: The configuration object. 257 """ 258 return cls.get_default() 259 260 @classmethod 261 def get_default(cls): 262 """Return the default configuration. 263 264 This method returns newly created, based on default constructor, 265 object of Configuration class or returns a copy of default 266 configuration. 267 268 :return: The configuration object. 269 """ 270 if cls._default is None: 271 cls._default = Configuration() 272 return cls._default 273 274 @property 275 def logger_file(self): 276 """The logger file. 277 278 If the logger_file is None, then add stream handler and remove file 279 handler. Otherwise, add file handler and remove stream handler. 280 281 :param value: The logger_file path. 282 :type: str 283 """ 284 return self.__logger_file 285 286 @logger_file.setter 287 def logger_file(self, value): 288 """The logger file. 289 290 If the logger_file is None, then add stream handler and remove file 291 handler. Otherwise, add file handler and remove stream handler. 292 293 :param value: The logger_file path. 294 :type: str 295 """ 296 self.__logger_file = value 297 if self.__logger_file: 298 # If set logging file, 299 # then add file handler and remove stream handler. 300 self.logger_file_handler = logging.FileHandler(self.__logger_file) 301 self.logger_file_handler.setFormatter(self.logger_formatter) 302 for _, logger in self.logger.items(): 303 logger.addHandler(self.logger_file_handler) 304 305 @property 306 def debug(self): 307 """Debug status 308 309 :param value: The debug status, True or False. 310 :type: bool 311 """ 312 return self.__debug 313 314 @debug.setter 315 def debug(self, value): 316 """Debug status 317 318 :param value: The debug status, True or False. 319 :type: bool 320 """ 321 self.__debug = value 322 if self.__debug: 323 # if debug status is True, turn on debug logging 324 for _, logger in self.logger.items(): 325 logger.setLevel(logging.DEBUG) 326 # turn on httplib debug 327 httplib.HTTPConnection.debuglevel = 1 328 else: 329 # if debug status is False, turn off debug logging, 330 # setting log level to default `logging.WARNING` 331 for _, logger in self.logger.items(): 332 logger.setLevel(logging.WARNING) 333 # turn off httplib debug 334 httplib.HTTPConnection.debuglevel = 0 335 336 @property 337 def logger_format(self): 338 """The logger format. 339 340 The logger_formatter will be updated when sets logger_format. 341 342 :param value: The format string. 343 :type: str 344 """ 345 return self.__logger_format 346 347 @logger_format.setter 348 def logger_format(self, value): 349 """The logger format. 350 351 The logger_formatter will be updated when sets logger_format. 352 353 :param value: The format string. 354 :type: str 355 """ 356 self.__logger_format = value 357 self.logger_formatter = logging.Formatter(self.__logger_format) 358 359 def get_api_key_with_prefix(self, identifier, alias=None): 360 """Gets API key (with prefix if set). 361 362 :param identifier: The identifier of apiKey. 363 :param alias: The alternative identifier of apiKey. 364 :return: The token for api key authentication. 365 """ 366 if self.refresh_api_key_hook is not None: 367 self.refresh_api_key_hook(self) 368 key = self.api_key.get(identifier, self.api_key.get(alias) if alias is not None else None) 369 if key: 370 prefix = self.api_key_prefix.get(identifier) 371 if prefix: 372 return "%s %s" % (prefix, key) 373 else: 374 return key 375 376 def get_basic_auth_token(self): 377 """Gets HTTP basic authentication header (string). 378 379 :return: The token for basic HTTP authentication. 380 """ 381 username = "" 382 if self.username is not None: 383 username = self.username 384 password = "" 385 if self.password is not None: 386 password = self.password 387 return urllib3.util.make_headers( 388 basic_auth=username + ':' + password 389 ).get('authorization') 390 391 def auth_settings(self): 392 """Gets Auth Settings dict for api client. 393 394 :return: The Auth Settings information dict. 395 """ 396 auth = {} 397 if self.username is not None and self.password is not None: 398 auth['basic_auth'] = { 399 'type': 'basic', 400 'in': 'header', 401 'key': 'Authorization', 402 'value': self.get_basic_auth_token() 403 } 404 if self.access_token is not None: 405 auth['jwt_token'] = { 406 'type': 'bearer', 407 'in': 'header', 408 'format': 'JWT', 409 'key': 'Authorization', 410 'value': 'Bearer ' + self.access_token 411 } 412 if 'cookie_auth' in self.api_key: 413 auth['cookie_auth'] = { 414 'type': 'api_key', 415 'in': 'cookie', 416 'key': 'internal_auth_session', 417 'value': self.get_api_key_with_prefix( 418 'cookie_auth', 419 ), 420 } 421 if 'oidc_auth' in self.api_key: 422 auth['oidc_auth'] = { 423 'type': 'api_key', 424 'in': 'cookie', 425 'key': 'oidc_auth_session', 426 'value': self.get_api_key_with_prefix( 427 'oidc_auth', 428 ), 429 } 430 if 'saml_auth' in self.api_key: 431 auth['saml_auth'] = { 432 'type': 'api_key', 433 'in': 'cookie', 434 'key': 'saml_auth_session', 435 'value': self.get_api_key_with_prefix( 436 'saml_auth', 437 ), 438 } 439 return auth 440 441 def to_debug_report(self): 442 """Gets the essential information for debugging. 443 444 :return: The report for debugging. 445 """ 446 return "Python SDK Debug Report:\n"\ 447 "OS: {env}\n"\ 448 "Python Version: {pyversion}\n"\ 449 "Version of the API: 1.0.0\n"\ 450 "SDK Package Version: 0.1.0-SNAPSHOT".\ 451 format(env=sys.platform, pyversion=sys.version) 452 453 def get_host_settings(self): 454 """Gets an array of host settings 455 456 :return: An array of host settings 457 """ 458 return [ 459 { 460 'url': "/api/v1", 461 'description': "lakeFS server endpoint", 462 } 463 ] 464 465 def get_host_from_settings(self, index, variables=None, servers=None): 466 """Gets host URL based on the index and variables 467 :param index: array index of the host settings 468 :param variables: hash of variable and the corresponding value 469 :param servers: an array of host settings or None 470 :return: URL based on host settings 471 """ 472 if index is None: 473 return self._base_path 474 475 variables = {} if variables is None else variables 476 servers = self.get_host_settings() if servers is None else servers 477 478 try: 479 server = servers[index] 480 except IndexError: 481 raise ValueError( 482 "Invalid index {0} when selecting the host settings. " 483 "Must be less than {1}".format(index, len(servers))) 484 485 url = server['url'] 486 487 # go through variables and replace placeholders 488 for variable_name, variable in server.get('variables', {}).items(): 489 used_value = variables.get( 490 variable_name, variable['default_value']) 491 492 if 'enum_values' in variable \ 493 and used_value not in variable['enum_values']: 494 raise ValueError( 495 "The variable `{0}` in the host URL has invalid value " 496 "{1}. Must be {2}.".format( 497 variable_name, variables[variable_name], 498 variable['enum_values'])) 499 500 url = url.replace("{" + variable_name + "}", used_value) 501 502 return url 503 504 @property 505 def host(self): 506 """Return generated host.""" 507 return self.get_host_from_settings(self.server_index, variables=self.server_variables) 508 509 @host.setter 510 def host(self, value): 511 """Fix base path.""" 512 self._base_path = value 513 self.server_index = None