def set_iam_properties( info: RedshiftProperty, user: str, host: str, database: str, port: int, password: str, source_address: typing.Optional[str], unix_sock: typing.Optional[str], ssl: bool, sslmode: str, timeout: typing.Optional[int], max_prepared_statements: int, tcp_keepalive: bool, application_name: typing.Optional[str], replication: typing.Optional[str], idp_host: typing.Optional[str], db_user: typing.Optional[str], iam: bool, app_id: typing.Optional[str], app_name: str, preferred_role: typing.Optional[str], principal_arn: typing.Optional[str], credentials_provider: typing.Optional[str], region: typing.Optional[str], cluster_identifier: typing.Optional[str], client_id: typing.Optional[str], idp_tenant: typing.Optional[str], client_secret: typing.Optional[str], partner_sp_id: typing.Optional[str], idp_response_timeout: int, listen_port: int, login_url: typing.Optional[str], auto_create: bool, db_groups: typing.Optional[typing.List[str]], force_lowercase: bool, allow_db_user_override: bool, ) -> None: if info is None: raise InterfaceError( "Invalid connection property setting. info must be specified") # IAM requires an SSL connection to work. # Make sure that is set to SSL level VERIFY_CA or higher. info.ssl = ssl if info.ssl is True: if sslmode == SSLMode.VERIFY_CA.value: info.sslmode = SSLMode.VERIFY_CA.value elif sslmode == SSLMode.VERIFY_FULL.value: info.sslmode = SSLMode.VERIFY_FULL.value else: info.sslmode = SSLMode.VERIFY_CA.value else: info.sslmode = "" if (info.ssl is False) and (iam is True): raise InterfaceError( "Invalid connection property setting. SSL must be enabled when using IAM" ) else: info.iam = iam if (info.iam is False) and (credentials_provider is not None): raise InterfaceError( "Invalid connection property setting. IAM must be enabled when using credentials " "via identity provider") elif (info.iam is True) and (credentials_provider is None): raise InterfaceError( "Invalid connection property setting. " "Credentials provider cannot be None when IAM is enabled") else: info.credentials_provider = credentials_provider if user is None: raise InterfaceError( "Invalid connection property setting. user must be specified") if host is None: raise InterfaceError( "Invalid connection property setting. host must be specified") if database is None: raise InterfaceError( "Invalid connection property setting. database must be specified") if port is None: raise InterfaceError( "Invalid connection property setting. port must be specified") if password is None: raise InterfaceError( "Invalid connection property setting. password must be specified") # basic driver parameters info.user_name = user info.host = host info.db_name = database info.port = port info.password = password info.source_address = source_address info.unix_sock = unix_sock info.timeout = timeout info.max_prepared_statements = max_prepared_statements info.tcp_keepalive = tcp_keepalive info.application_name = application_name info.replication = replication # Idp parameters info.idp_host = idp_host info.db_user = db_user info.app_id = app_id info.app_name = app_name info.preferred_role = preferred_role info.principal = principal_arn # Regions.fromName(string) requires the string to be lower case and in this format: # E.g. "us-west-2" info.region = region # cluster_identifier parameter is required info.cluster_identifier = cluster_identifier info.auto_create = auto_create info.db_groups = db_groups info.force_lowercase = force_lowercase info.allow_db_user_override = allow_db_user_override # Azure specified parameters info.client_id = client_id info.idp_tenant = idp_tenant info.client_secret = client_secret # Browser idp parameters info.idp_response_timeout = idp_response_timeout info.listen_port = listen_port info.login_url = login_url info.partner_sp_id = partner_sp_id if info.iam is True: set_iam_credentials(info) else: return
def set_iam_properties( info: RedshiftProperty, user: str, host: str, database: str, port: int, password: str, source_address: typing.Optional[str], unix_sock: typing.Optional[str], ssl: bool, sslmode: str, timeout: typing.Optional[int], max_prepared_statements: int, tcp_keepalive: bool, application_name: typing.Optional[str], replication: typing.Optional[str], idp_host: typing.Optional[str], db_user: typing.Optional[str], iam: bool, app_id: typing.Optional[str], app_name: str, preferred_role: typing.Optional[str], principal_arn: typing.Optional[str], access_key_id: typing.Optional[str], secret_access_key: typing.Optional[str], session_token: typing.Optional[str], profile: typing.Optional[str], credentials_provider: typing.Optional[str], region: typing.Optional[str], cluster_identifier: typing.Optional[str], client_id: typing.Optional[str], idp_tenant: typing.Optional[str], client_secret: typing.Optional[str], partner_sp_id: typing.Optional[str], idp_response_timeout: int, listen_port: int, login_url: typing.Optional[str], auto_create: bool, db_groups: typing.List[str], force_lowercase: bool, allow_db_user_override: bool, client_protocol_version: int, database_metadata_current_db_only: bool, ssl_insecure: typing.Optional[bool], web_identity_token: typing.Optional[str], role_session_name: typing.Optional[str], role_arn: typing.Optional[str], ) -> None: """ Helper function to handle IAM connection properties and ensure required parameters are specified. Parameters """ if info is None: raise InterfaceError( "Invalid connection property setting. info must be specified") # IAM requires an SSL connection to work. # Make sure that is set to SSL level VERIFY_CA or higher. info.ssl = ssl if info.ssl is True: if sslmode not in SupportedSSLMode.list(): info.sslmode = SupportedSSLMode.default() _logger.debug( "A non-supported value: {} was provides for sslmode. Falling back to default value: {}" .format(sslmode, SupportedSSLMode.default())) else: info.sslmode = sslmode else: info.sslmode = "" if (info.ssl is False) and (iam is True): raise InterfaceError( "Invalid connection property setting. SSL must be enabled when using IAM" ) else: info.iam = iam if (info.iam is False) and (ssl_insecure is not None): raise InterfaceError( "Invalid connection property setting. IAM must be enabled when using ssl_insecure" ) elif (info.iam is False) and any( (credentials_provider, access_key_id, secret_access_key, session_token, profile)): raise InterfaceError( "Invalid connection property setting. IAM must be enabled when using credential_provider, " "AWS credentials, or AWS profile") elif info.iam is True: if cluster_identifier is None: raise InterfaceError( "Invalid connection property setting. cluster_identifier must be provided when IAM is enabled" ) if not any((credentials_provider, access_key_id, secret_access_key, session_token, profile)): raise InterfaceError( "Invalid connection property setting. Credentials provider, AWS credentials, or AWS profile must be" " provided when IAM is enabled") elif credentials_provider is not None: if any((access_key_id, secret_access_key, session_token, profile)): raise InterfaceError( "Invalid connection property setting. It is not valid to provide both Credentials provider and " "AWS credentials or AWS profile") else: info.credentials_provider = credentials_provider elif profile is not None: if any((access_key_id, secret_access_key, session_token)): raise InterfaceError( "Invalid connection property setting. It is not valid to provide any of access_key_id, " "secret_access_key, or session_token when profile is provided" ) else: info.profile = profile elif access_key_id is not None: info.access_key_id = access_key_id if secret_access_key is not None: info.secret_access_key = secret_access_key # secret_access_key can be provided in the password field so it is hidden from applications such as # SQL Workbench. elif password != "": info.secret_access_key = password else: raise InterfaceError( "Invalid connection property setting. " "secret access key must be provided in either secret_access_key or password field" ) if session_token is not None: info.session_token = session_token elif secret_access_key is not None: raise InterfaceError( "Invalid connection property setting. access_key_id is required when secret_access_key is " "provided") elif session_token is not None: raise InterfaceError( "Invalid connection property setting. access_key_id and secret_access_key are required when " "session_token is provided") if not all((user, host, database, port, password)): if (profile is None) and (access_key_id is None): raise InterfaceError( "Invalid connection property setting. " "user, password, host, database and port are required " "when not using AWS credentials and AWS profile") if client_protocol_version not in ClientProtocolVersion.list(): raise InterfaceError( "Invalid connection property setting. client_protocol_version must be in: {}" .format(ClientProtocolVersion.list())) # basic driver parameters info.user_name = user info.host = host info.db_name = database info.port = port info.password = password info.source_address = source_address info.unix_sock = unix_sock info.timeout = timeout info.max_prepared_statements = max_prepared_statements info.tcp_keepalive = tcp_keepalive info.application_name = application_name info.replication = replication info.client_protocol_version = client_protocol_version info.database_metadata_current_db_only = database_metadata_current_db_only # Idp parameters info.idp_host = idp_host info.db_user = db_user info.app_id = app_id info.app_name = app_name info.preferred_role = preferred_role info.principal = principal_arn # Regions.fromName(string) requires the string to be lower case and in this format: # E.g. "us-west-2" info.region = region # cluster_identifier parameter is required info.cluster_identifier = cluster_identifier info.auto_create = auto_create info.db_groups = db_groups info.force_lowercase = force_lowercase info.allow_db_user_override = allow_db_user_override if ssl_insecure is not None: info.sslInsecure = ssl_insecure # Azure specified parameters info.client_id = client_id info.idp_tenant = idp_tenant info.client_secret = client_secret # Browser idp parameters info.idp_response_timeout = idp_response_timeout info.listen_port = listen_port info.login_url = login_url info.partner_sp_id = partner_sp_id # Jwt idp parameters info.web_identity_token = web_identity_token info.role_session_name = role_session_name info.role_arn = role_arn if info.iam is True: IamHelper.set_iam_credentials(info) else: return
def set_iam_credentials(info: RedshiftProperty) -> None: provider: typing.Optional[SamlCredentialsProvider] = None # case insensitive comparison if info.credentials_provider is None: return elif info.credentials_provider.lower() == "OktaCredentialsProvider".lower( ): provider = OktaCredentialsProvider() provider.add_parameter(info) elif info.credentials_provider.lower() == "AzureCredentialsProvider".lower( ): provider = AzureCredentialsProvider() provider.add_parameter(info) elif info.credentials_provider.lower( ) == "BrowserAzureCredentialsProvider".lower(): provider = BrowserAzureCredentialsProvider() provider.add_parameter(info) elif info.credentials_provider.lower() == "PingCredentialsProvider".lower( ): provider = PingCredentialsProvider() provider.add_parameter(info) elif info.credentials_provider.lower( ) == "BrowserSamlCredentialsProvider".lower(): provider = BrowserSamlCredentialsProvider() provider.add_parameter(info) elif info.credentials_provider.lower() == "AdfsCredentialsProvider".lower( ): provider = AdfsCredentialsProvider() provider.add_parameter(info) else: raise InterfaceError("Invalid credentials provider" + info.credentials_provider) credentials: CredentialsHolder = provider.get_credentials() metadata: CredentialsHolder.IamMetadata = credentials.get_metadata() if metadata is not None: auto_create: bool = metadata.get_auto_create() db_user: typing.Optional[str] = metadata.get_db_user() saml_db_user: typing.Optional[str] = metadata.get_saml_db_user() profile_db_user: typing.Optional[str] = metadata.get_profile_db_user() db_groups: typing.Optional[str] = metadata.get_db_groups() force_lowercase: bool = metadata.get_force_lowercase() allow_db_user_override: bool = metadata.get_allow_db_user_override() if auto_create is True: info.auto_create = auto_create if force_lowercase is True: info.force_lowercase = force_lowercase if allow_db_user_override is True: if saml_db_user is not None: info.db_user = saml_db_user if db_user is not None: info.db_user = db_user if profile_db_user is not None: info.db_user = profile_db_user else: if db_user is not None: info.db_user = db_user if profile_db_user is not None: info.db_user = profile_db_user if saml_db_user is not None: info.db_user = saml_db_user if (info.db_groups is None) and (db_groups is not None): tmp: typing.List[str] = db_groups.split(",") info.db_groups = [group.lower() for group in tmp] set_cluster_credentials(provider, info)
def set_iam_credentials(info: RedshiftProperty) -> None: """ Helper function to create the appropriate credential providers. """ klass: typing.Optional[SamlCredentialsProvider] = None provider: typing.Union[SamlCredentialsProvider, AWSCredentialsProvider] if info.credentials_provider is not None: try: klass = dynamic_plugin_import(info.credentials_provider) provider = klass() # type: ignore provider.add_parameter(info) # type: ignore except (AttributeError, ModuleNotFoundError): _logger.debug("Failed to load user defined plugin: {}".format( info.credentials_provider)) try: klass = dynamic_plugin_import( "redshift_connector.plugin.{}".format( info.credentials_provider)) provider = klass() # type: ignore provider.add_parameter(info) # type: ignore except (AttributeError, ModuleNotFoundError): _logger.debug( "Failed to load pre-defined IdP plugin from redshift_connector.plugin: {}" .format(info.credentials_provider)) raise InterfaceError("Invalid credentials provider " + info.credentials_provider) else: # indicates AWS Credentials will be used provider = AWSCredentialsProvider() provider.add_parameter(info) if isinstance(provider, SamlCredentialsProvider): credentials: CredentialsHolder = provider.get_credentials() metadata: CredentialsHolder.IamMetadata = credentials.get_metadata( ) if metadata is not None: auto_create: bool = metadata.get_auto_create() db_user: typing.Optional[str] = metadata.get_db_user() saml_db_user: typing.Optional[str] = metadata.get_saml_db_user( ) profile_db_user: typing.Optional[ str] = metadata.get_profile_db_user() db_groups: typing.List[str] = metadata.get_db_groups() force_lowercase: bool = metadata.get_force_lowercase() allow_db_user_override: bool = metadata.get_allow_db_user_override( ) if auto_create is True: info.auto_create = auto_create if force_lowercase is True: info.force_lowercase = force_lowercase if allow_db_user_override is True: if saml_db_user is not None: info.db_user = saml_db_user if db_user is not None: info.db_user = db_user if profile_db_user is not None: info.db_user = profile_db_user else: if db_user is not None: info.db_user = db_user if profile_db_user is not None: info.db_user = profile_db_user if saml_db_user is not None: info.db_user = saml_db_user if (len(info.db_groups) == 0) and (len(db_groups) > 0): info.db_groups = db_groups IamHelper.set_cluster_credentials(provider, info)