Skip to content

Commit

Permalink
refactoring and added manual testing
Browse files Browse the repository at this point in the history
  • Loading branch information
sfc-gh-ext-simba-jy committed Jan 15, 2025
1 parent 9e0f4a6 commit 7fdb8ef
Show file tree
Hide file tree
Showing 6 changed files with 54 additions and 71 deletions.
35 changes: 25 additions & 10 deletions cpp/lib/Authenticator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@
#include <chrono>
#include <regex>
#include <functional>
#ifdef _WIN32
#include <WS2tcpip.h>
#endif

#include "Authenticator.hpp"
#include "../logger/SFLogger.hpp"
Expand All @@ -26,13 +28,17 @@
#include "memory.h"
#include "../include/snowflake/platform.h"


#include <fstream>

#include "snowflake/SF_CRTFunctionSafe.h"

#ifdef __APPLE__
#include <CoreFoundation/CFBundle.h>
#include <ApplicationServices/ApplicationServices.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#endif

#ifdef _WIN32
Expand Down Expand Up @@ -411,7 +417,8 @@ namespace Client
IAuthenticatorOKTA::authenticate();
if ((m_connection->error).error_code == SF_STATUS_SUCCESS && (isError() || m_idp->isError()))
{
SET_SNOWFLAKE_ERROR(&m_connection->error, SF_STATUS_ERROR_GENERAL, IAuthenticatorOKTA::getErrorMessage().c_str(), SF_SQLSTATE_GENERAL_ERROR);
const char* err = isError() ? getErrorMessage() : m_idp->getErrorMessage();
SET_SNOWFLAKE_ERROR(&m_connection->error, SF_STATUS_ERROR_GENERAL, err, SF_SQLSTATE_GENERAL_ERROR);
}
}

Expand Down Expand Up @@ -612,6 +619,7 @@ namespace Client
"Failed to start web server. Could not create a socket. err: %s",
"");
m_errMsg = "SFAuthWebBrowserFailed: Failed to start web server. Could not create a socket.";
return;
}

struct sockaddr_in recv_server;
Expand All @@ -625,14 +633,16 @@ namespace Client
CXX_LOG_ERROR(
"sf", "AuthWebServer", "start",
"Failed to start web server. Could not bind a port. err: %s", "");
m_errMsg = "SFAuthWebBrowserFailed: Failed to start web server. Could not bind a port";
m_errMsg = "SFAuthWebBrowserFailed: Failed to start web server. Could not bind a port";
return;
}
if (listen(m_socket_descriptor, 0) < 0)
{
CXX_LOG_ERROR(
"sf", "AuthWebServer", "start",
"Failed to start web server. Could not listen a port. err: %s", "");
m_errMsg = "SFAuthWebBrowserFailed: Failed to start web server. Could not listen a port";
return;
}
socklen_t len = sizeof(recv_server);
if (getsockname(m_socket_descriptor,
Expand All @@ -642,6 +652,7 @@ namespace Client
"sf", "AuthWebServer", "start",
"Failed to start web server. Could not get the port. err: %s", "");
m_errMsg = "SFAuthWebBrowserFailed: Failed to start web server. Could not get the port.";
return;
}
m_port = ntohs(recv_server.sin_port);
CXX_LOG_INFO("sf", "AuthWebServer", "start",
Expand Down Expand Up @@ -1081,26 +1092,31 @@ namespace Client
{
#ifdef _WIN32
AuthWinSock authWinSock;
if (authWinSock.isError())
{
SET_SNOWFLAKE_ERROR(&m_connection->error, SF_STATUS_ERROR_GENERAL, authWinSock.getErrorMessage(), SF_SQLSTATE_GENERAL_ERROR);
return;
}
#endif
m_authWebServer->start();
if (m_authWebServer->isError())
{
SET_SNOWFLAKE_ERROR(&m_connection->error, SF_STATUS_ERROR_GENERAL, m_authWebServer->getErrorMessage().c_str(), SF_SQLSTATE_GENERAL_ERROR);
SET_SNOWFLAKE_ERROR(&m_connection->error, SF_STATUS_ERROR_GENERAL, m_authWebServer->getErrorMessage(), SF_SQLSTATE_GENERAL_ERROR);
return;
}

std::map<std::string, std::string> out;
getLoginUrl(out, m_authWebServer->getPort());
if (isError())
{
SET_SNOWFLAKE_ERROR(&m_connection->error, SF_STATUS_ERROR_GENERAL, getErrorMessage().c_str(), SF_SQLSTATE_GENERAL_ERROR);
SET_SNOWFLAKE_ERROR(&m_connection->error, SF_STATUS_ERROR_GENERAL, getErrorMessage(), SF_SQLSTATE_GENERAL_ERROR);
return;
}

startWebBrowser(out[std::string("LOGIN_URL")]);
if (isError())
{
SET_SNOWFLAKE_ERROR(&m_connection->error, SF_STATUS_ERROR_GENERAL, m_errMsg.c_str(), SF_SQLSTATE_GENERAL_ERROR);
SET_SNOWFLAKE_ERROR(&m_connection->error, SF_STATUS_ERROR_GENERAL, getErrorMessage(), SF_SQLSTATE_GENERAL_ERROR);
return;
}
m_proofKey = out[std::string("PROOF_KEY")];
Expand All @@ -1109,7 +1125,7 @@ namespace Client
m_authWebServer->startAccept();
if (m_authWebServer->isError())
{
SET_SNOWFLAKE_ERROR(&m_connection->error, SF_STATUS_ERROR_GENERAL, m_authWebServer->getErrorMessage().c_str(), SF_SQLSTATE_GENERAL_ERROR);
SET_SNOWFLAKE_ERROR(&m_connection->error, SF_STATUS_ERROR_GENERAL, m_authWebServer->getErrorMessage(), SF_SQLSTATE_GENERAL_ERROR);
return;
}
// accept SAML token
Expand All @@ -1120,7 +1136,7 @@ namespace Client
m_authWebServer->stop();
if (m_authWebServer->isError())
{
SET_SNOWFLAKE_ERROR(&m_connection->error, SF_STATUS_ERROR_GENERAL, m_authWebServer->getErrorMessage().c_str(), SF_SQLSTATE_GENERAL_ERROR);
SET_SNOWFLAKE_ERROR(&m_connection->error, SF_STATUS_ERROR_GENERAL, m_authWebServer->getErrorMessage(), SF_SQLSTATE_GENERAL_ERROR);
return;
}
m_token = m_authWebServer->getSAMLToken();
Expand Down Expand Up @@ -1256,7 +1272,6 @@ namespace Client
CXX_LOG_ERROR("sf", "AuthenticatorExternalBrowser", "startWebBrowser",
"Failed to start web browser. xdg-open returned %d", es);
m_errMsg = "SFAuthWebBrowserFailed: Failed to start web browser. xdg-open returned";

}
}
}
Expand Down Expand Up @@ -1290,7 +1305,7 @@ namespace Client
CXX_LOG_ERROR(
"sf", "AuthWinSock", "constructor",
"Failed to call WSAStartup: %d", err);
//SF_THROWGEN1("SFAuthWebBrowserFailed", std::to_string(err));
m_errMsg = "SFAuthWebBrowserFailed: Failed to call WSAStartup";
}
if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)
{
Expand All @@ -1300,7 +1315,7 @@ namespace Client
CXX_LOG_ERROR(
"sf", "AuthWinSock", "constructor",
"Could not find a usable version of Winsock.dll.%s", "");
//SF_THROWGEN1("SFAuthWebBrowserFailed", std::to_string(err));
m_errMsg = "SFAuthWebBrowserFailed: Could not find a usable version of Winsock.dll";
}

CXX_LOG_INFO("sf", "AuthWinSock",
Expand Down
10 changes: 1 addition & 9 deletions cpp/lib/Authenticator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,6 @@ namespace Client
bool m_consent_cache_id_token;
std::string m_origin;
int m_timeout;
std::string m_errMsg;

void parseAndRespondOptionsRequest(std::string response);
void parseAndRespondPostRequest(std::string response);
Expand Down Expand Up @@ -158,13 +157,6 @@ namespace Client
* Set the timeout for the web server.
*/
void setTimeout(int timeout);

bool hasrror()
{
return !m_errMsg.empty();
}

std::string getErrorMessage();
};

class AuthenticatorExternalBrowser : public IAuthenticator
Expand Down Expand Up @@ -223,7 +215,7 @@ namespace Client
/**
* Winsock start and cleanup
*/
class AuthWinSock
class AuthWinSock : public AuthErrorHandler
{
public:
AuthWinSock();
Expand Down
38 changes: 8 additions & 30 deletions cpp/lib/IAuth.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,31 +17,20 @@ namespace Client
{
using namespace picojson;

void IAuthenticator::renewDataMap(jsonObject_t& dataMap)
{
authenticate();
updateDataMap(dataMap);
}

std::string IAuthenticator::getErrorMessage()
const char* AuthErrorHandler::getErrorMessage()
{
return m_errMsg;
return m_errMsg.c_str();
}

bool IAuthenticator::isError()
bool AuthErrorHandler::isError()
{
return !m_errMsg.empty();
}


std::string IDPAuthenticator::getErrorMessage()
{
return m_errMsg;
}

bool IDPAuthenticator::isError()
void IAuthenticator::renewDataMap(jsonObject_t& dataMap)
{
return !m_errMsg.empty();
authenticate();
updateDataMap(dataMap);
}

bool IDPAuthenticator::getIDPInfo(jsonObject_t& dataMap)
Expand Down Expand Up @@ -88,7 +77,8 @@ namespace Client
return url;
}

void IAuthenticatorOKTA::authenticate() {
void IAuthenticatorOKTA::authenticate()
{
// 1. get authenticator info
jsonObject_t dataMap;
dataMap["CLIENT_APP_ID"] = value(m_appID);
Expand Down Expand Up @@ -188,18 +178,6 @@ namespace Client
"extractPostBackUrlFromSamlResponse",
"Post back url after unescape: %s", unescaped_url);
return std::string(unescaped_url);

}


std::string IAuthWebServer::getErrorMessage()
{
return m_errMsg;
}

bool IAuthWebServer::isError()
{
return !m_errMsg.empty();
}
}// namespace IAuth
} // namespace Client
Expand Down
31 changes: 14 additions & 17 deletions include/snowflake/IAuth.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,17 @@ namespace Client
namespace IAuth
{

class IAuthWebServer
class AuthErrorHandler
{
public:
const char* getErrorMessage();
bool isError();

protected:
std::string m_errMsg;
};

class IAuthWebServer : public AuthErrorHandler
{
public:
IAuthWebServer()
Expand All @@ -33,16 +43,11 @@ namespace IAuth
virtual std::string getSAMLToken() = 0;
virtual bool isConsentCacheIdToken() = 0;
virtual void setTimeout(int timeout) = 0;
std::string getErrorMessage();
bool isError();

protected:
std::string m_errMsg;
};
/**
* Authenticator
*/
class IAuthenticator
class IAuthenticator : public AuthErrorHandler
{
public:

Expand All @@ -67,18 +72,14 @@ namespace IAuth

// Renew the autentication and update datamap.
// The default behavior is to call authenticate() and updateDataMap().
virtual void renewDataMap(jsonObject_t& dataMap);

std::string getErrorMessage();
bool isError();
virtual void renewDataMap(jsonObject_t& dataMap);;

protected:
int64 m_renewTimeout;
std::string m_errMsg;
};


class IDPAuthenticator
class IDPAuthenticator : public AuthErrorHandler
{
public:
IDPAuthenticator()
Expand All @@ -95,9 +96,6 @@ namespace IAuth
*/
virtual bool curlPostCall(SFURL& url, const jsonObject_t& body, jsonObject_t& resp) = 0;
virtual bool curlGetCall(SFURL& url, jsonObject_t& resp, bool parseJSON, std::string& raw_data, bool& isRetry) = 0;

std::string getErrorMessage();
bool isError();

std::string tokenURLStr;
std::string ssoURLStr;
Expand All @@ -112,7 +110,6 @@ namespace IAuth
std::string m_protocol;
int8 m_retriedCount;
int64 m_retryTimeout;
std::string m_errMsg;
};

class IAuthenticatorOKTA : public IAuthenticator
Expand Down
1 change: 1 addition & 0 deletions tests/test_manual_connect.c
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ void test_okta_connect(void** unused)
SF_CONNECT* sf = snowflake_init();
snowflake_set_attribute(sf, SF_CON_ACCOUNT,
getenv("SNOWFLAKE_TEST_ACCOUNT"));
//TODO: Change OKTA to External browser
snowflake_set_attribute(sf, SF_CON_USER, getenv("SNOWFLAKE_TEST_OKTA_USERNAME"));
snowflake_set_attribute(sf, SF_CON_PASSWORD,
getenv("SNOWFLAKE_TEST_OKTA_PASSWORD"));
Expand Down
10 changes: 5 additions & 5 deletions tests/test_unit_okta.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ void test_okta_authenticator_succeed(void**)

MockOkta okta = MockOkta(sf);
okta.authenticate();
assert_true(okta.getErrorMessage() == "");
assert_true(!okta.isError());

jsonObject_t data;
okta.updateDataMap(data);
Expand All @@ -220,25 +220,25 @@ void test_okta_authenticator_fail(void**)

MockOkta okta = MockOkta(sf);
okta.authenticate();
assert_string_equal(okta.getErrorMessage().c_str(), "SFSamlResponseVerificationFailed");
assert_string_equal(okta.getErrorMessage(), "SFSamlResponseVerificationFailed");

okta.setCurlGetRequestFailed(true);
okta.authenticate();
assert_string_equal(okta.m_idp->m_errMsg.c_str(), "SFConnectionFailed:curlGetCall");
assert_string_equal(okta.m_idp->getErrorMessage(), "SFConnectionFailed:curlGetCall");

okta.setCurlGetRequestFailed(false);
okta.setCurrentCallFailed(false);
okta.setPostCallFailed(true);

okta.authenticate();
assert_string_equal(okta.m_idp->m_errMsg.c_str(), "SFConnectionFailed:curlPostCall");
assert_string_equal(okta.m_idp->getErrorMessage(), "SFConnectionFailed:curlPostCall");

snowflake_set_attribute(sf, SF_CON_AUTHENTICATOR, "https://wrong.okta.com");


MockOkta okta2 = MockOkta(sf);
okta2.authenticate();
assert_string_equal(okta2.getErrorMessage().c_str(), "SFAuthenticatorVerificationFailed: ssoUrl or tokenUrl does not contains same prefix with the authenticator");
assert_string_equal(okta2.getErrorMessage(), "SFAuthenticatorVerificationFailed: ssoUrl or tokenUrl does not contains same prefix with the authenticator");

snowflake_term(sf);
}
Expand Down

0 comments on commit 7fdb8ef

Please sign in to comment.