Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SNOW-715524: Add SSO token cache #921

Open
wants to merge 98 commits into
base: master
Choose a base branch
from

Conversation

sfc-gh-ext-simba-lf
Copy link
Collaborator

Description

Add SSO token cache

Checklist

  • Code compiles correctly
  • Code is formatted according to Coding Conventions
  • Created tests which fail without the change (if possible)
  • All tests passing (dotnet test)
  • Extended the README / documentation, if necessary
  • Provide JIRA issue id (if possible) or GitHub issue id in PR name

Copy link

codecov bot commented Apr 18, 2024

Codecov Report

Attention: Patch coverage is 92.53333% with 28 lines in your changes missing coverage. Please review.

Project coverage is 87.23%. Comparing base (c3cb344) to head (126e337).

Files with missing lines Patch % Lines
Snowflake.Data/Core/Tools/BrowserOperations.cs 5.55% 17 Missing ⚠️
...astructure/SFCredentialManagerWindowsNativeImpl.cs 92.15% 2 Missing and 2 partials ⚠️
...Core/Authenticator/ExternalBrowserAuthenticator.cs 96.96% 3 Missing ⚠️
...ager/Infrastructure/SFCredentialManagerFileImpl.cs 98.09% 0 Missing and 2 partials ⚠️
...re/CredentialManager/SFCredentialManagerFactory.cs 95.83% 0 Missing and 1 partial ⚠️
Snowflake.Data/Core/Session/SFSession.cs 96.15% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #921      +/-   ##
==========================================
+ Coverage   85.70%   87.23%   +1.53%     
==========================================
  Files         117      122       +5     
  Lines       11331    11614     +283     
  Branches     1130     1158      +28     
==========================================
+ Hits         9711    10132     +421     
+ Misses       1342     1196     -146     
- Partials      278      286       +8     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@sfc-gh-ext-simba-lf sfc-gh-ext-simba-lf marked this pull request as ready for review April 20, 2024 00:42
@sfc-gh-ext-simba-lf sfc-gh-ext-simba-lf requested a review from a team as a code owner April 20, 2024 00:42
@rafael-sotelo
Copy link

Any updates on this PR ? Can I help ? I been waiting for this SSO Token Cache for long time

@sfc-gh-ext-simba-lf
Copy link
Collaborator Author

Any updates on this PR ? Can I help ? I been waiting for this SSO Token Cache for long time

Currently we're trying out different libraries/packages for the credential manager. Then it'll be reviewed if it meets the security requirements

public void SaveCredentials(string key, string token)
{
s_logger.Debug($"Saving credentials into memory for key: {key}");
s_credentials[key] = SnowflakeCredentialEncryption.EncryptToken(token);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we encrypt tokens when storing them in memory whereas in file implementation we do not do it?
I think encryption here it is not necessary... Maybe only we should make sure that ToString() operation on this class does not reveal any credentials.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I saw JDBC do some encryption so I tried to follow it

The encryption is now removed. I also checked ToString() and it only shows the class name so no sensitive info. The dictionary itself is private so it shouldn't be possible to call ToString() on the dictionary itself

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe, you are right some kind of obfuscating the string would be good. Maybe let's use SecureString to store the tokens. You could use Snowflake.Data.Core.Tools.SecureStringHelper

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, the tokens are now stored with SecureStringHelper


namespace Snowflake.Data.Client
{
public class SnowflakeCredentialManagerFactory
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this class should not be public since it is not meant to be used by the user. You should move it into another package because in Snowflake.Data.Client should be only classes we want to share with the user.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Understood, the class is no longer public and has been moved

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But you moved all the classes from Client to Core - I meant something else...

The Idea is that all classes which could be used by the client should be publicly accessible and located in Client package so the client has all his classes in one place. Classes in Client package start with Snowflake - so it would be good to follow this convention.

In other than Client package we have our internal classes. These should not be public. These classes do not prefix with Snowflake. Sometimes they prefix with SF, but I don't think we should prefix every our internal class with SF, because we do not gain anything from this. So for our internal classes we are free to use any name we want.

The factory class is internal so it is good that you moved it to Core. But for instance ISFCredentialManager is public so it would better fit to Client package. And its previous name ISnowflakeCredentialManager was better fitting our conventions. However the class TokenType which is defined in the same file as ISFCredentialManager is internal so it should be extracted to separate file and placed in Core. Sometimes if we would not like to put too many implementation details into the Client classes we can use a pattern of creating a wrapper class in API. Such a pattern was used here: https://github.com/snowflakedb/snowflake-connector-net/blob/master/Snowflake.Data/Client/SnowflakeDbSessionPool.cs is a public class in Client package which is wrapping the internal class in Core package: https://github.com/snowflakedb/snowflake-connector-net/blob/master/Snowflake.Data/Core/Session/SessionPool.cs
So following this pattern we could have internal implementations in Core.CredentialManager package but then we would need a publicly accessible wrappers in Client package with names starting from Snowflake. Or you can don't create such wrappers but move all public classes to Client package - it is up to you.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My mistake, I thought I was supposed to move the other classes as well

I've reverted some of the changes and did some refactoring:

  • Rename ISFCredentialManager back to ISnowflakeCredentialManager
  • Move ISnowflakeCredentialManager back to Client
  • Extract TokenType from ISnowflakeCredentialManager

Does that look alright to you?


namespace Snowflake.Data.Client
{
public class SnowflakeCredentialManagerNativeImpl : ISnowflakeCredentialManager
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could move all the implementations into subpackage Infrastructure

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@@ -252,6 +258,8 @@ private SFRestRequest BuildAuthenticatorRestRequest(int port)
AccountName = session.properties[SFSessionProperty.ACCOUNT],
Authenticator = AUTH_NAME,
BrowserModeRedirectPort = port.ToString(),
DriverName = SFEnvironment.DriverName,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this improvement is not related to the feature itself?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not directly related, but I saw that other connectors like JDBC include the driver name and version in the request so I thought to include it as well in .NET for parity

sfc-gh-ext-simba-lf and others added 30 commits July 8, 2024 15:44
…nector-net into SNOW-715524-SSO-Token-Cache

# Conflicts:
#	Snowflake.Data.Tests/UnitTests/SFSessionPropertyTest.cs
…nector-net into SNOW-715524-SSO-Token-Cache

# Conflicts:
#	Snowflake.Data.Tests/IntegrationTests/SFConnectionIT.cs
…nector-net into SNOW-715524-SSO-Token-Cache

# Conflicts:
#	Snowflake.Data/Core/SFError.cs
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants