internal void renewSession() { RenewSessionRequest postBody = new RenewSessionRequest() { oldSessionToken = this.sessionToken, requestType = "RENEW" }; SFRestRequest renewSessionRequest = new SFRestRequest { jsonBody = postBody, uri = BuildUri(SF_TOKEN_REQUEST_PATH, new Dictionary <string, string> { { SF_QUERY_REQUEST_ID, Guid.NewGuid().ToString() } }), authorizationToken = string.Format(SF_AUTHORIZATION_SNOWFLAKE_FMT, masterToken), sfRestRequestTimeout = Timeout.InfiniteTimeSpan }; var response = restRequest.Post <RenewSessionResponse>(renewSessionRequest); if (!response.success) { SnowflakeDbException e = new SnowflakeDbException("", response.code, response.message, ""); logger.Error("Renew session failed", e); throw e; } else { sessionToken = response.data.sessionToken; } }
public override void Open() { logger.Debug("Open Connection."); SetSession(); try { SfSession.Open(); } catch (Exception e) { // Otherwise when Dispose() is called, the close request would timeout. _connectionState = ConnectionState.Closed; logger.Error("Unable to connect", e); if (!(e.GetType() == typeof(SnowflakeDbException))) { throw new SnowflakeDbException(e.InnerException, SFError.INTERNAL_ERROR, "Unable to connect"); } else { throw; } } OnSessionEstablished(); }
/// <summary> /// Generate the authenticator given the session /// </summary> /// <param name="session">session that requires the authentication</param> /// <returns>authenticator</returns> /// <exception cref="SnowflakeDbException">when authenticator is unknown</exception> internal static IAuthenticator GetAuthenticator(SFSession session) { string type = session.properties[SFSessionProperty.AUTHENTICATOR]; if (type.Equals(BasicAuthenticator.AUTH_NAME, StringComparison.InvariantCultureIgnoreCase)) { return(new BasicAuthenticator(session)); } else if (type.Equals(ExternalBrowserAuthenticator.AUTH_NAME, StringComparison.InvariantCultureIgnoreCase)) { return(new ExternalBrowserAuthenticator(session)); } else if (type.Equals(KeyPairAuthenticator.AUTH_NAME, StringComparison.InvariantCultureIgnoreCase)) { // Get private key path or private key from connection settings if (!session.properties.TryGetValue(SFSessionProperty.PRIVATE_KEY_FILE, out var pkPath) && !session.properties.TryGetValue(SFSessionProperty.PRIVATE_KEY, out var pkContent)) { // There is no PRIVATE_KEY_FILE defined, can't authenticate with key-pair string invalidStringDetail = "Missing required PRIVATE_KEY_FILE or PRIVATE_KEY for key pair authentication"; var error = new SnowflakeDbException( SFError.INVALID_CONNECTION_STRING, new object[] { invalidStringDetail }); logger.Error(error.Message, error); throw error; } return(new KeyPairAuthenticator(session)); } else if (type.Equals(OAuthAuthenticator.AUTH_NAME, StringComparison.InvariantCultureIgnoreCase)) { // Get private key path or private key from connection settings if (!session.properties.TryGetValue(SFSessionProperty.TOKEN, out var pkPath)) { // There is no TOKEN defined, can't authenticate with oauth string invalidStringDetail = "Missing required TOKEN for Oauth authentication"; var error = new SnowflakeDbException( SFError.INVALID_CONNECTION_STRING, new object[] { invalidStringDetail }); logger.Error(error.Message, error); throw error; } return(new OAuthAuthenticator(session)); } // Okta would provide a url of form: https://xxxxxx.okta.com or https://xxxxxx.oktapreview.com else if ((type.EndsWith("okta.com") || type.EndsWith("oktapreview.com")) && type.StartsWith("https://")) { return(new OktaAuthenticator(session, type)); } var e = new SnowflakeDbException(SFError.UNKNOWN_AUTHENTICATOR, type); logger.Error("Unknown authenticator", e); throw e; }
private void VerifyUrls(Uri tokenOrSsoUrl, Uri sessionUrl) { if (tokenOrSsoUrl.Scheme != sessionUrl.Scheme || tokenOrSsoUrl.Host != sessionUrl.Host) { var e = new SnowflakeDbException( SFError.IDP_SSO_TOKEN_URL_MISMATCH, tokenOrSsoUrl.ToString(), oktaUrl.ToString()); logger.Error("Different urls", e); throw e; } }
internal async Task <SFBaseResultSet> ExecuteAsync(int timeout, string sql, Dictionary <string, BindingDTO> bindings, bool describeOnly, CancellationToken cancellationToken) { registerQueryCancellationCallback(timeout, cancellationToken); var queryRequest = BuildQueryRequest(sql, bindings, describeOnly); try { QueryExecResponse response = null; bool receivedFirstQueryResponse = false; while (!receivedFirstQueryResponse) { response = await _restRequester.PostAsync <QueryExecResponse>(queryRequest, cancellationToken).ConfigureAwait(false); if (SessionExpired(response)) { SfSession.renewSession(); queryRequest.authorizationToken = string.Format(SF_AUTHORIZATION_SNOWFLAKE_FMT, SfSession.sessionToken); } else { receivedFirstQueryResponse = true; } } var lastResultUrl = response.data?.getResultUrl; while (RequestInProgress(response) || SessionExpired(response)) { var req = BuildResultRequest(lastResultUrl); response = await _restRequester.GetAsync <QueryExecResponse>(req, cancellationToken).ConfigureAwait(false); if (SessionExpired(response)) { logger.Info("Ping pong request failed with session expired, trying to renew the session."); SfSession.renewSession(); } else { lastResultUrl = response.data?.getResultUrl; } } return(BuildResultSet(response, cancellationToken)); } catch { logger.Error("Query execution failed."); throw; } finally { ClearQueryRequestId(); } }
internal static SFSessionProperties parseConnectionString(String connectionString, SecureString password) { logger.Info("Start parsing connection string."); SFSessionProperties properties = new SFSessionProperties(); string[] propertyEntry = connectionString.Split(';'); foreach (string keyVal in propertyEntry) { if (keyVal.Length > 0) { string[] token = keyVal.Split(new string[] { "=" }, StringSplitOptions.None); if (token.Length == 2) { try { SFSessionProperty p = (SFSessionProperty)Enum.Parse( typeof(SFSessionProperty), token[0].ToUpper()); properties.Add(p, token[1]); logger.Info($"Connection property: {p}, value: {(p == SFSessionProperty.PASSWORD ? "XXXXXXXX" : token[1])}"); } catch (ArgumentException e) { logger.Warn($"Property {token[0]} not found ignored."); } } else { string invalidStringDetail = String.Format("Invalid kay value pair {0}", keyVal); SnowflakeDbException e = new SnowflakeDbException(SFError.INVALID_CONNECTION_STRING, new object[] { invalidStringDetail }); logger.Error("Invalid string.", e); throw e; } } } if (password != null) { properties[SFSessionProperty.PASSWORD] = new NetworkCredential(string.Empty, password).Password; } checkSessionProperties(properties); // compose host value if not specified if (!properties.ContainsKey(SFSessionProperty.HOST)) { string hostName = String.Format("%s.snowflakecomputing.com", properties[SFSessionProperty.ACCOUNT]); properties.Add(SFSessionProperty.HOST, hostName); logger.Info($"Compose host name: {hostName}"); } return(properties); }
/// <summary> /// Generate the authenticator given the session /// </summary> /// <param name="session">session that requires the authentication</param> /// <returns>authenticator</returns> /// <exception cref="SnowflakeDbException">when authenticator is unknown</exception> internal static IAuthenticator GetAuthenticator(SFSession session) { string type = session.properties[SFSessionProperty.AUTHENTICATOR]; if (type.Equals(BasicAuthenticator.AUTH_NAME, StringComparison.InvariantCultureIgnoreCase)) { return(new BasicAuthenticator(session)); } else if (type.Equals(ExternalBrowserAuthenticator.AUTH_NAME, StringComparison.InvariantCultureIgnoreCase)) { return(new ExternalBrowserAuthenticator(session)); } // Okta would provide a url of form: https://xxxxxx.okta.com else if (type.EndsWith("okta.com") && type.StartsWith("https://")) { return(new OktaAuthenticator(session, type)); } else if (type.Equals(OAuthAuthenticator.AUTH_NAME, StringComparison.InvariantCultureIgnoreCase)) { return(new OAuthAuthenticator(session)); } var e = new SnowflakeDbException(SFError.UNKNOWN_AUTHENTICATOR, type); logger.Error("Unknown authenticator", e); throw e; }
internal async Task <SFBaseResultSet> ExecuteAsync(int timeout, string sql, Dictionary <string, BindingDTO> bindings, bool describeOnly, CancellationToken cancellationToken) { registerQueryCancellationCallback(timeout, cancellationToken); var queryRequest = BuildQueryRequest(sql, bindings, describeOnly); try { var response = await _restRequest.PostAsync <QueryExecResponse>(queryRequest, cancellationToken); if (SessionExpired(response)) { SfSession.renewSession(); ClearQueryRequestId(); return(await ExecuteAsync(timeout, sql, bindings, describeOnly, cancellationToken)); } var lastResultUrl = response.data?.getResultUrl; while (RequestInProgress(response) || SessionExpired(response)) { var req = BuildResultRequest(lastResultUrl); response = await _restRequest.GetAsync <QueryExecResponse>(req, cancellationToken); if (SessionExpired(response)) { logger.Info("Ping pong request failed with session expired, trying to renew the session."); SfSession.renewSession(); } else { lastResultUrl = response.data?.getResultUrl; } } return(BuildResultSet(response, cancellationToken)); } catch (Exception ex) { logger.Error("Query execution failed.", ex); throw; } finally { ClearQueryRequestId(); } }
public override void Open() { logger.Debug("Open Connection."); SetMockSession(); try { SfSession.Open(); } catch (Exception e) { // Otherwise when Dispose() is called, the close request would timeout. _connectionState = System.Data.ConnectionState.Closed; logger.Error("Unable to connect", e); throw; } OnSessionEstablished(); }
internal void ProcessLoginResponse(LoginResponse authnResponse) { if (authnResponse.success) { sessionToken = authnResponse.data.token; masterToken = authnResponse.data.masterToken; database = authnResponse.data.authResponseSessionInfo.databaseName; schema = authnResponse.data.authResponseSessionInfo.schemaName; serverVersion = authnResponse.data.serverVersion; UpdateSessionParameterMap(authnResponse.data.nameValueParameter); } else { SnowflakeDbException e = new SnowflakeDbException("", authnResponse.code, authnResponse.message, ""); logger.Error("Authentication failed", e); throw e; } }
/// <summary> /// Register cancel callback. Two factors: either external cancellation token passed down from upper /// layer or timeout reached. Whichever comes first would trigger query cancellation. /// </summary> /// <param name="timeout">query timeout. 0 means no timeout</param> /// <param name="externalCancellationToken">cancellation token from upper layer</param> private void registerQueryCancellationCallback(int timeout, CancellationToken externalCancellationToken) { SetTimeout(timeout); _linkedCancellationTokenSouce = CancellationTokenSource.CreateLinkedTokenSource(_timeoutTokenSource.Token, externalCancellationToken); if (!_linkedCancellationTokenSouce.IsCancellationRequested) { _linkedCancellationTokenSouce.Token.Register(() => { try { Cancel(); } catch (Exception ex) { // Prevent an unhandled exception from being thrown logger.Error("Unable to cancel query.", ex); } }); } }
/// <summary> /// Generate the authenticator given the session /// </summary> /// <param name="session">session that requires the authentication</param> /// <returns>authenticator</returns> /// <exception cref="SnowflakeDbException">when authenticator is unknown</exception> internal static IAuthenticator GetAuthenticator(SFSession session) { string type = session.properties[SFSessionProperty.AUTHENTICATOR]; if (type == "snowflake") { return(new BasicAuthenticator(session)); } // Okta would provide a url of form: https://xxxxxx.okta.com else if (type.EndsWith("okta.com") && type.StartsWith("https://")) { return(new OktaAuthenticator(session, type)); } var e = new SnowflakeDbException(SFError.UNKNOWN_AUTHENTICATOR, type); logger.Error("Unknown authenticator", e); throw e; }
internal static SFSessionProperties parseConnectionString(String connectionString, SecureString password) { logger.Info("Start parsing connection string."); SFSessionProperties properties = new SFSessionProperties(); string[] propertyEntry = connectionString.Split(';'); foreach (string keyVal in propertyEntry) { if (keyVal.Length > 0) { string[] tokens = keyVal.Split(new string[] { "=" }, StringSplitOptions.None); if (tokens.Length != 2) { // https://docs.microsoft.com/en-us/dotnet/api/system.data.oledb.oledbconnection.connectionstring // To include an equal sign (=) in a keyword or value, it must be preceded // by another equal sign. For example, in the hypothetical connection // string "key==word=value" : // the keyword is "key=word" and the value is "value". int currentIndex = 0; int singleEqualIndex = -1; while (currentIndex <= keyVal.Length) { currentIndex = keyVal.IndexOf("=", currentIndex); if (-1 == currentIndex) { // No '=' found break; } if ((currentIndex < (keyVal.Length - 1)) && ('=' != keyVal[currentIndex + 1])) { if (0 > singleEqualIndex) { // First single '=' encountered singleEqualIndex = currentIndex; currentIndex++; } else { // Found another single '=' which is not allowed singleEqualIndex = -1; break; } } else { // skip the doubled one currentIndex += 2; } } if ((singleEqualIndex > 0) && (singleEqualIndex < keyVal.Length - 1)) { // Split the key/value at the right index and deduplicate '==' tokens = new string[2]; tokens[0] = keyVal.Substring(0, singleEqualIndex).Replace("==", "="); tokens[1] = keyVal.Substring( singleEqualIndex + 1, keyVal.Length - (singleEqualIndex + 1)).Replace("==", "=");; } else { // An equal sign was not doubled or something else happened // making the connection invalid string invalidStringDetail = String.Format("Invalid key value pair {0}", keyVal); SnowflakeDbException e = new SnowflakeDbException(SFError.INVALID_CONNECTION_STRING, new object[] { invalidStringDetail }); logger.Error("Invalid string.", e); throw e; } } try { SFSessionProperty p = (SFSessionProperty)Enum.Parse( typeof(SFSessionProperty), tokens[0].ToUpper()); properties.Add(p, tokens[1]); logger.Info($"Connection property: {p}, value: {(secretProps.Contains(p) ? "XXXXXXXX" : tokens[1])}"); } catch (ArgumentException e) { logger.Warn($"Property {tokens[0]} not found ignored.", e); } } } if (password != null) { properties[SFSessionProperty.PASSWORD] = new NetworkCredential(string.Empty, password).Password; } checkSessionProperties(properties); // compose host value if not specified if (!properties.ContainsKey(SFSessionProperty.HOST) || (0 == properties[SFSessionProperty.HOST].Length)) { string hostName = String.Format("{0}.snowflakecomputing.com", properties[SFSessionProperty.ACCOUNT]); // Remove in case it's here but empty properties.Remove(SFSessionProperty.HOST); properties.Add(SFSessionProperty.HOST, hostName); logger.Info($"Compose host name: {hostName}"); } return(properties); }