sql-mock icon indicating copy to clipboard operation
sql-mock copied to clipboard

Can we access Snowflake using a private key and passphrase instead of a password?

Open BFASRachelS opened this issue 1 year ago • 2 comments

Your docs say the user and password need to be set as environment variables.

SQL_MOCK_SNOWFLAKE_ACCOUNT: Your snowflake account SQL_MOCK_SNOWFLAKE_USER: The name of your Snowflake user SQL_MOCK_SNOWFLAKE_PASSWORD: The password for your Snowflake user

https://github.com/DeepLcom/sql-mock/blob/main/docs/snowflake.md

I am using key-pair authentication to access Snowflake. Does sql-mock support this?

BFASRachelS avatar Feb 27 '25 22:02 BFASRachelS

@BFASRachelS the Python library seems to support it so it should be doable. Right now, the library only uses the authentication via the env variables you linked above.

It should be pretty straight forward to support it. Without having tested the code, it could be something like this:

(1) Adjust the settings to accept the other auth values (and add some validation).

class SnowflakeSettings(BaseSettings):
    model_config = SettingsConfigDict(env_prefix="SQL_MOCK_SNOWFLAKE_")
    account: str
    user: str
    password: Optional[str]
    private_key_file: Optional[str]
    private_key_file_pwd: Optional[str]

    @root_validator
    def validate_settings(cls, values):
        password_present = values.get('password') is not None
        private_key_file_present = values.get('private_key_file') is not None
        private_key_file_pwd_present = values.get('private_key_file_pwd') is not None
        
        # Check if either password auth or key-pair auth is present
        if (private_key_file_present and private_key_file_pwd_present) or password_present:
            return values
        
        raise ValueError("Either both private_key_file and private_key_file_pwd must be set, or password must be set")
    
    @computed_field
    def auth_args(self):
        if self.password:
            return {
                'user': self.user,
                'password': self.password
            }
        else:
            return {
                'user': self.user,
                'private_key_file': self.private_key_file,
                'private_key_file_pwd': self.private_key_file_pwd
            }

(2) Add some logic here to see which settings are present and choose the auth method accordingly

class SnowflakeTableMock(BaseTableMock):
    _sql_dialect = "snowflake"

    def __init__(
        self,
        *args,
        **kwargs,
    ):
        self.settings = SnowflakeSettings()
        super().__init__(*args, **kwargs)

    def _get_results(self, query: str) -> list[dict]:
        with connect(
            user=self.settings.user,
            **self.settings.auth_args,
        ) as conn:
            with conn.cursor(DictCursor) as cur:
                cur.execute(query)
                return cur.fetchall()

In case you feel curious, feel free to give it a try and create a PR. If you have the key-pair setup running for your Snowflake, then it should be quick to test.

The new env vars would also take the SQL_MOCK_SNOWFLAKE_ prefix:

  • SQL_MOCK_SNOWFLAKE_PRIVATE_KEY_FILE
  • SQL_MOCK_SNOWFLAKE_PRIVATE_KEY_FILE_PWD

Somtom avatar Feb 28 '25 06:02 Somtom

Thanks. I will definitely give that a try.


From: Thomas Schmidt @.> Sent: Thursday, February 27, 2025 10:52 PM To: DeepLcom/sql-mock @.> Cc: Rachel Scanlon @.>; Mention @.> Subject: Re: [DeepLcom/sql-mock] Can we access Snowflake using a private key and passphrase instead of a password? (Issue #59)

@BFASRachelShttps://github.com/BFASRachelS the Python library seems to supporthttps://docs.snowflake.com/en/developer-guide/python-connector/python-connector-connect#label-python-key-pair-authn-rotation it so it should be doable. Right now, the library only uses the authentication via the env variables you linked above.

It should be pretty straight forward to support it. Without having tested the code, it could be something like this:

(1) Adjust the settingshttps://github.com/DeepLcom/sql-mock/blob/main/src/sql_mock/snowflake/settings.py to accept the other auth values (and add some validation).

class SnowflakeSettings(BaseSettings): model_config = SettingsConfigDict(env_prefix="SQL_MOCK_SNOWFLAKE_") account: str user: str password: Optional[str] private_key_file: Optional[str] private_key_file_pwd: Optional[str]

@root_validator
def validate_settings(cls, values):
    password_present = values.get('password') is not None
    private_key_file_present = values.get('private_key_file') is not None
    private_key_file_pwd_present = values.get('private_key_file_pwd') is not None

    # Check if either password auth or key-pair auth is present
    if (private_key_file_present and private_key_file_pwd_present) or password_present:
        return values

    raise ValueError("Either both private_key_file and private_key_file_pwd must be set, or password must be set")

@computed_field
def auth_args(self):
    if self.password:
        return {
            'user': self.user,
            'password': self.password
        }
    else:
        return {
            'user': self.user,
            'private_key_file': self.private_key_file,
            'private_key_file_pwd': self.private_key_file_pwd
        }

(2) Add some logic herehttps://github.com/DeepLcom/sql-mock/blob/main/src/sql_mock/snowflake/table_mocks.py#L19-L23 to see which settings are present and choose the auth method accordingly

class SnowflakeTableMock(BaseTableMock): _sql_dialect = "snowflake"

def __init__(
    self,
    *args,
    **kwargs,
):
    self.settings = SnowflakeSettings()
    super().__init__(*args, **kwargs)

def _get_results(self, query: str) -> list[dict]:
    with connect(
        user=self.settings.user,
        **self.settings.auth_args,
    ) as conn:
        with conn.cursor(DictCursor) as cur:
            cur.execute(query)
            return cur.fetchall()

In case you feel curious, feel free to give it a try and create a PR. If you have the key-pair setup running for your Snowflake, then it should be quick to test.

— Reply to this email directly, view it on GitHubhttps://github.com/DeepLcom/sql-mock/issues/59#issuecomment-2689874163, or unsubscribehttps://github.com/notifications/unsubscribe-auth/AKJQCFDD7ECGPPGGZXIKJJD2SABS5AVCNFSM6AAAAABYA6U5AKVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDMOBZHA3TIMJWGM. You are receiving this because you were mentioned.Message ID: @.***>

[Somtom]Somtom left a comment (DeepLcom/sql-mock#59)https://github.com/DeepLcom/sql-mock/issues/59#issuecomment-2689874163

@BFASRachelShttps://github.com/BFASRachelS the Python library seems to supporthttps://docs.snowflake.com/en/developer-guide/python-connector/python-connector-connect#label-python-key-pair-authn-rotation it so it should be doable. Right now, the library only uses the authentication via the env variables you linked above.

It should be pretty straight forward to support it. Without having tested the code, it could be something like this:

(1) Adjust the settingshttps://github.com/DeepLcom/sql-mock/blob/main/src/sql_mock/snowflake/settings.py to accept the other auth values (and add some validation).

class SnowflakeSettings(BaseSettings): model_config = SettingsConfigDict(env_prefix="SQL_MOCK_SNOWFLAKE_") account: str user: str password: Optional[str] private_key_file: Optional[str] private_key_file_pwd: Optional[str]

@root_validator
def validate_settings(cls, values):
    password_present = values.get('password') is not None
    private_key_file_present = values.get('private_key_file') is not None
    private_key_file_pwd_present = values.get('private_key_file_pwd') is not None

    # Check if either password auth or key-pair auth is present
    if (private_key_file_present and private_key_file_pwd_present) or password_present:
        return values

    raise ValueError("Either both private_key_file and private_key_file_pwd must be set, or password must be set")

@computed_field
def auth_args(self):
    if self.password:
        return {
            'user': self.user,
            'password': self.password
        }
    else:
        return {
            'user': self.user,
            'private_key_file': self.private_key_file,
            'private_key_file_pwd': self.private_key_file_pwd
        }

(2) Add some logic herehttps://github.com/DeepLcom/sql-mock/blob/main/src/sql_mock/snowflake/table_mocks.py#L19-L23 to see which settings are present and choose the auth method accordingly

class SnowflakeTableMock(BaseTableMock): _sql_dialect = "snowflake"

def __init__(
    self,
    *args,
    **kwargs,
):
    self.settings = SnowflakeSettings()
    super().__init__(*args, **kwargs)

def _get_results(self, query: str) -> list[dict]:
    with connect(
        user=self.settings.user,
        **self.settings.auth_args,
    ) as conn:
        with conn.cursor(DictCursor) as cur:
            cur.execute(query)
            return cur.fetchall()

In case you feel curious, feel free to give it a try and create a PR. If you have the key-pair setup running for your Snowflake, then it should be quick to test.

— Reply to this email directly, view it on GitHubhttps://github.com/DeepLcom/sql-mock/issues/59#issuecomment-2689874163, or unsubscribehttps://github.com/notifications/unsubscribe-auth/AKJQCFDD7ECGPPGGZXIKJJD2SABS5AVCNFSM6AAAAABYA6U5AKVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDMOBZHA3TIMJWGM. You are receiving this because you were mentioned.Message ID: @.***>

BFASRachelS avatar Feb 28 '25 15:02 BFASRachelS