set_oauthhelper_dll(){ // Called by get_pJWTAssertion() interations: int retVal = 0; // used for return from oauth_load_privatekey(). // *** No need to change the rest of this file: lr_param_sprintf("pTimeExpire","%d",time(0)); // create initial seed where (now 10-digit) 1379510581 is for Sep. 18, 2013 at 7:24AM MT. lr_load_dll("oauthhelper.dll"); // Written by [email protected]. // TODO: 20. In Run-time Settings > Miscellaneous, set to run as "Process" rather than "Thread". // TODO: 21. Specify in vugen.dat file in "C:\Program Files (x86)\HP\LoadRunner\dat" to load DLL globally for use by all Vuser scripts. // file mdrv.dat // [HTTP] section // WINNT_DLLS=kernel32.dll,vtclient.dll,user32.dll // TODO: 22. If loading from the script’s directory, specify the DLL is listed in the Controller’s Design View -> Details -> More -> Files. // If errors are only from Controller, copy the DLL to the same directory on all Load Generator machines. // Using values obtained from Google APIs Console for Project X Client ID associated with {GoogleAPIServiceEmail}: retVal = oauth_load_privatekey(lr_eval_string("{pServicePrivateKeyFile}"),"notasecret"); // Done only once. if( retVal != 0 ){ if( retVal == -1 ){ lr_error_message(">> oauth_load_privatekey Failed to open p12 file, file not found, path incorrect."); }else if( retVal == -2 ){ lr_error_message(">> oauth_load_privatekey Error reading the PKCS#12 file, file permissions."); }else if( retVal == -3 ){ lr_error_message(">> oauth_load_privatekey Error parsing the PKCS#12 file, invalid certificate or invalid password."); }else if( retVal == -4 ){ lr_error_message(">> oauth_load_privatekey Key not set."); }else{ lr_error_message(">> oauth_load_privatekey retVal=%d.",retVal); } } return retVal; } // set_oauthhelper_dll()
/*! \brief Save a substring of a parameter into a new parameter. Search for a specific substring inside a parameter using left and right boundaries and save that into a new parameter. \param [in] original_parameter The parameter to search. \param [in] result_parameter The name of the parameter to store the result in. \param [in] left The left boundary - the text immediately preceding the substring in question. \param [in] right The right boundary. \b Example: \code char* str = "LorumIpsumLipsum"; lr_save_string(str, "param"); y_substr("param", "param", "Lorum", "Lipsum"); lr_log_message(lr_eval_string("{param}")); // Prints "Ipsum". \endcode \author André Luyer, Marcel Jepma & Floris Kraak */ void y_substr(const char *original_parameter, const char *result_parameter, const char *left, const char *right) { char *p1; char *str = y_get_parameter_or_null(original_parameter); if( str == NULL ) { lr_error_message("y_substr(): Error: Parameter %s does not exist!", original_parameter); lr_abort(); } // zoek start if (left) { p1 = strstr(str, left); if (p1) str = p1 + strlen(left); // else start is positie 0... } // zoek eind if (right) { p1 = strstr(str, right); if (p1) { lr_param_sprintf(result_parameter, "%.*s", p1 - str, str); // of sprintf return; } } lr_save_string(str, result_parameter); }
get_pJWTAssertion(){ int rc=0; char * signature = 0; // Before getting here, example: lr_save_string("*****@*****.**","GoogleAPIServiceEmail"); // Each vuser gets its own tokens. // Google will return "Invalid Request' if the current time sent is off more than a just a few seconds from Google's server, so: // TODO: 14. Wherever machine this runs on needs to be synced to NTP or time.gov for UTC (GMT) timezone. // Google returns "invalid_grant" 400 error response if the previous token has not expired yet. // So loop is needed to re-use tokens until its expiration: lr_param_sprintf("pTimeNow","%d",time(0)); // where time(0) generates 10-digits. if( strcmp( lr_eval_string("{pTimeNow}"), lr_eval_string("{pTimeExpire}")) < 0 ){ wi_startPrintingTrace(); // Re-use assertion saved for use instead of running get_pJWTAssertion() again now. lr_output_message(">> JWT assertion token reuse: pTimeNow=%s, pTimeExpire=%s" ,lr_eval_string("{pTimeNow}") ,lr_eval_string("{pTimeExpire}") ); wi_stopPrinting(); } else { // First iteration or generated one expired: lr_param_sprintf("pTimeExpire","%d",time(0)+1800); // Google allows up to 3600 (one hour). } // The spec this should follow is http://tools.ietf.org/id/draft-jones-json-web-token-08.html#rfc.section.3.1 lr_param_sprintf("JWTBody", "{\"iss\":\"%s\"," "\"scope\":\"%s\"," "\"aud\":\"https://accounts.google.com/o/oauth2/token\"," "\"exp\":%s,\"iat\":%s}", lr_eval_string("{GoogleAPIServiceEmail}"), lr_eval_string("{pServiceScope}"), lr_eval_string("{pTimeExpire}"), lr_eval_string("{pTimeNow}")); // "aud" (audit) without back-slashes is: https://accounts.google.com/o/oauth2/token // "prn" (principal) is not set because it's not applicable for Gmail accounts, only Google Apps accounts granted domain-wide delegation access by an administrator. // Encode Header dot Claim set: lr_param_sprintf("pEncoded","%s.%s", oauth_url_escape(oauth_encode_base64(0,"{\"alg\":\"RS256\",\"typ\":\"JWT\"}")), oauth_url_escape(oauth_encode_base64(0,lr_eval_string("{JWTBody}")))); // "alg:RS256" = algorithm RSA P-256 SHA-256 used by Google APIs // "alg:HS256" = used by Google Wallet and OpenID // "alg:ES256" = algorithm ECDSA P-256 SHA-256 used by Salesforce // Microsoft Live Connect API signature = (char*)oauth_sign_rsa_sha256( lr_eval_string("{pEncoded}") ); lr_param_sprintf("pJWTAssertion","%s.%s", lr_eval_string("{pEncoded}"), oauth_url_escape(signature)); // Verify assertion output on-line using https://developers.google.com/commerce/wallet/digital/docs/jwtdecoder // https://developers.google.com/wallet/digital/docs/jwtdecoder wi_startPrintingTrace(); lr_output_message(">> JWTBody=%s." ,lr_eval_string("{JWTBody}") ); lr_output_message(">> pEncoded=%s." ,lr_eval_string("{pEncoded}") ); lr_output_message(">> pJWTAssertion=%s." ,lr_eval_string("{pJWTAssertion}") ); lr_output_message(">> signature=%s." ,signature ); wi_stopPrinting(); return rc; } // get_pJWTAssertion()