diff --git a/src/mod/applications/mod_http_cache/Makefile.am b/src/mod/applications/mod_http_cache/Makefile.am index 5f6f5c4bbb..7e482895bb 100644 --- a/src/mod/applications/mod_http_cache/Makefile.am +++ b/src/mod/applications/mod_http_cache/Makefile.am @@ -2,7 +2,7 @@ include $(top_srcdir)/build/modmake.rulesam MODNAME=mod_http_cache noinst_LTLIBRARIES = libhttpcachemod.la -libhttpcachemod_la_SOURCES = mod_http_cache.c common.c aws.c azure.c +libhttpcachemod_la_SOURCES = mod_http_cache.c common.c aws.c azure.c gcs.c mod_LTLIBRARIES = mod_http_cache.la mod_http_cache_la_SOURCES = @@ -11,12 +11,16 @@ mod_http_cache_la_CPPFLAGS = $(CURL_CFLAGS) $(AM_CPPFLAGS) mod_http_cache_la_LIBADD = $(switch_builddir)/libfreeswitch.la libhttpcachemod.la mod_http_cache_la_LDFLAGS = $(CURL_LIBS) -avoid-version -module -no-undefined -shared -noinst_PROGRAMS = test/test_aws +noinst_PROGRAMS = test/test_aws test/test_gcs test_test_aws_SOURCES = test/test_aws.c test_test_aws_CFLAGS = $(AM_CFLAGS) -I. -DSWITCH_TEST_BASE_DIR_FOR_CONF=\"${abs_builddir}/test\" -DSWITCH_TEST_BASE_DIR_OVERRIDE=\"${abs_builddir}/test\" test_test_aws_LDFLAGS = $(AM_LDFLAGS) -avoid-version -no-undefined $(freeswitch_LDFLAGS) $(switch_builddir)/libfreeswitch.la $(CORE_LIBS) $(APR_LIBS) test_test_aws_LDADD = libhttpcachemod.la -TESTS = $(noinst_PROGRAMS) +test_test_gcs_SOURCES = test/test_gcs.c +test_test_gcs_CFLAGS = $(AM_CFLAGS) -I. -DSWITCH_TEST_BASE_DIR_FOR_CONF=\"${abs_builddir}/test\" -DSWITCH_TEST_BASE_DIR_OVERRIDE=\"${abs_builddir}/test\" +test_test_gcs_LDFLAGS = $(AM_LDFLAGS) -avoid-version -no-undefined $(freeswitch_LDFLAGS) $(switch_builddir)/libfreeswitch.la $(CORE_LIBS) $(APR_LIBS) -lcurl +test_test_gcs_LDADD = libhttpcachemod.la +TESTS = $(noinst_PROGRAMS) diff --git a/src/mod/applications/mod_http_cache/common.h b/src/mod/applications/mod_http_cache/common.h index f2406983c5..5670d0642b 100644 --- a/src/mod/applications/mod_http_cache/common.h +++ b/src/mod/applications/mod_http_cache/common.h @@ -42,8 +42,11 @@ struct http_profile { char *aws_s3_access_key_id; char *secret_access_key; char *base_domain; - char *region; // AWS region. Used by AWS S3 + char *region; // AWS region. Used by AWS S3 // Used by gcs as token uri + char *gcs_email; // GCS service account email + char *gcs_credentials; // GCS credencial token switch_time_t expires; // Expiration time in seconds for URL signature. Default is 604800 seconds. Used by AWS S3 + // GCS Expiration used for when the gcs_credentials expires switch_size_t bytes_per_block; int header_count; char** header_names; diff --git a/src/mod/applications/mod_http_cache/gcs.c b/src/mod/applications/mod_http_cache/gcs.c new file mode 100644 index 0000000000..871d054e43 --- /dev/null +++ b/src/mod/applications/mod_http_cache/gcs.c @@ -0,0 +1,368 @@ +/* + * gcs.c for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2020 + * + * Version: MPL 1.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is gcs.c for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * + * gcs.c -- Some GCS Blob Service helper functions + * + */ +#include "gcs.h" +#include +#include + +#if defined(HAVE_OPENSSL) +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +struct http_data { + switch_stream_handle_t stream; + switch_size_t bytes; + switch_size_t max_bytes; + int err; +}; + +#if defined(HAVE_OPENSSL) +char *encoded_token(const char *token_uri, const char *client_email, const char *private_key_id, int *token_length, time_t now) { + time_t then = now + 3600; + int tlength = 1 + snprintf(NULL, 0, "{\"typ\":\"JWT\",\"alg\":\"RS256\",\"kid\":\"%s\"}", private_key_id); + int payload_length = 1 + snprintf(NULL, 0, "{\"iat\":\"%ld\",\"exp\":\"%ld\",\"iss\":\"%s\",\"aud\":\"%s\",\"scope\":\"https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_only https://www.googleapis.com/auth/devstorage.read_write\"}", now, then, client_email,token_uri); + char token[tlength]; + char payload[payload_length]; + int encoded_tlength = tlength * 4 / 3 + (tlength % 3 ? 1 : 0); + int encoded_playload_length = payload_length * 4 / 3 + (payload_length % 3 ? 1 : 0); + char *tokenb64 = malloc(encoded_tlength * sizeof(char)); + char *payloadb64 = malloc(encoded_playload_length* sizeof(char)); + int signee_length = encoded_tlength + encoded_playload_length; + char *signee = malloc((signee_length) * sizeof(char)); + sprintf(token,"{\"typ\":\"JWT\",\"alg\":\"RS256\",\"kid\":\"%s\"}", private_key_id); + sprintf(payload, "{\"iat\":\"%ld\",\"exp\":\"%ld\",\"iss\":\"%s\",\"aud\":\"%s\",\"scope\":\"https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/devstorage.read_only https://www.googleapis.com/auth/devstorage.read_write\"}", now, then, client_email,token_uri); + *token_length = signee_length - 1; + switch_b64_encode((unsigned char *) token,sizeof(token), (unsigned char *) tokenb64, encoded_tlength); + switch_b64_encode((unsigned char *) payload,sizeof(payload), (unsigned char *) payloadb64, encoded_playload_length); + sprintf(signee, "%s.%s", tokenb64, payloadb64); + free(tokenb64); + free(payloadb64); + return signee; +} + +switch_size_t file_size_to_alloc(switch_size_t in) { + switch_size_t last = 4096; + for (int i = 0; i < 20; i++) { + if (! (in & last)) { + return last; + } + last *= 2; + } + return last; +} + +void signtoken(char *token, int tokenlen,char *pkey, char *out) { + unsigned char *sig = NULL; + BIO *b = NULL; + RSA *r = NULL; + unsigned int sig_len; + unsigned char *md = malloc(SHA256_DIGEST_LENGTH * sizeof(unsigned char)); + unsigned char *digest = SHA256((const unsigned char *) token, tokenlen, md); + b = BIO_new_mem_buf(pkey, -1); + r = PEM_read_bio_RSAPrivateKey(b, NULL, NULL, NULL); + BIO_set_close(b, BIO_CLOSE); + BIO_free(b); + sig = malloc(RSA_size(r)); + RSA_sign(NID_sha256, digest, sizeof(char) * SHA256_DIGEST_LENGTH, sig, &sig_len, r); + switch_b64_encode(sig,(switch_size_t) sizeof(char) * sig_len,(unsigned char *) out, 343 * sizeof(char)); + free(sig); + free(md); + RSA_free(r); +} + +char *gcs_auth_request(char *content, char *url); +switch_status_t gcs_refresh_authorization (http_profile_t *profile) +{ + int token_length; + char *token = NULL; + char *encoded = NULL; + char *assertion = NULL; + char *auth = NULL; + char content[GCS_SIGNATURE_LENGTH_MAX]; + char *signature_url_encoded = NULL; + time_t exp; + time_t now = time(NULL); + token = encoded_token(profile->region, profile->gcs_email, profile->aws_s3_access_key_id, &token_length, now); + encoded = malloc(sizeof(char) * 343); + signtoken(token, token_length, profile->secret_access_key, encoded); + assertion = malloc(sizeof(char) * (1 + token_length + 343)); + sprintf(assertion, "%s.%s", token, encoded); + free(token); + free(encoded); + signature_url_encoded = switch_string_replace(assertion, "+", "%2B"); + sprintf(content,"%s%s", "grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=", signature_url_encoded); + free(signature_url_encoded); + auth = gcs_auth_request(content, profile->region); + profile->gcs_credentials = auth; + free(auth); + exp = now + 3540; + profile->expires = exp; + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Credentials Expries Unix Time: %ld", exp); + switch_safe_free(assertion); + return SWITCH_STATUS_SUCCESS; +} +#endif + +/** + * Append the specific GCS Blob Service headers + * @param http_profile_t the provile + * @param headers the list of headers to append to + */ + +switch_curl_slist_t *gcs_append_headers(http_profile_t *profile, switch_curl_slist_t *headers, const char *verb,unsigned int content_length, const char *content_type, const char *url, const unsigned int block_num, char **query_string) +{ + char header[1024]; +#if defined(HAVE_OPENSSL) + switch_time_t now = time(NULL); + if (profile->expires < now) { + gcs_refresh_authorization(profile); + } + switch_snprintf(header, sizeof(header), "Authorization: Bearer %s", profile->gcs_credentials); + //switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Credecials Token: %s", profile->gcs_credentials); + headers = switch_curl_slist_append(headers, header); +#endif + return headers; +} + +/** + * Read the GCS Blob Service profile + * @param name the name of the profile + * @param xml the portion of the XML document containing the profile + * @param access_key_id returned value of access_key_id in the configuration + * @param secret_access_key returned value of secret_access_key in the configuration + * @param base_domain returned value of base_domain in the configuration + * @param bytes_per_block returned value of bytes_per_block in the configuration + * @return SWITCH_STATUS_SUCCESS on success + */ +switch_status_t gcs_config_profile(switch_xml_t xml, http_profile_t *profile,switch_memory_pool_t *pool) +{ + switch_status_t status = SWITCH_STATUS_SUCCESS; +#if defined(HAVE_OPENSSL) + char *file = NULL; + char *envfile = getenv("GOOGLE_APPLICATION_CREDENTIALS"); + switch_xml_t base_domain_xml = switch_xml_child(xml, "base-domain"); + profile->append_headers_ptr = gcs_append_headers; + + /* check if environment variables set the keys */ + if (!zstr(envfile)) { + file = strdup(envfile); + //switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, + // "Using GOOGLE_APPLICATION_CREDENTIALS environment variables for GCS access on profile \"%s\"\n", profile->name); + } else { + /* use configuration for keys */ + switch_xml_t creds = switch_xml_child(xml, "credential_file"); + if (creds) { + file = switch_strip_whitespace(switch_xml_txt(creds)); + } + } + if (switch_file_exists(file, pool) == SWITCH_STATUS_SUCCESS) { + char *contents = NULL; + char *jsonstr = NULL; + switch_file_t *fd; + switch_status_t status; + switch_size_t size; + cJSON *json = {0}; + + status = switch_file_open(&fd, file, SWITCH_FOPEN_READ, SWITCH_FPROT_UREAD, pool); + if (status != SWITCH_STATUS_SUCCESS) { + //switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERR, "Could not open credencial file\n", profile->bytes_per_block); + switch_safe_free(file); + return status; + } + + size = switch_file_get_size(fd); + if (size) { + contents = malloc(file_size_to_alloc(size * 2) * sizeof(char)); + switch_file_read(fd, (void *) contents, &size); + } else { + switch_safe_free(file); + status = switch_file_close(fd); + return SWITCH_STATUS_FALSE; + } + status = switch_file_close(fd); + if (status != SWITCH_STATUS_SUCCESS) { + //switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERR, "Could not close credencial file\n", profile->bytes_per_block); + switch_safe_free(file); + free(contents); + return status; + } + json = cJSON_Parse(contents); + if (cJSON_GetObjectItem(json,"private_key_id") != NULL) { + jsonstr = cJSON_GetObjectItem(json,"private_key_id")->valuestring; + profile->aws_s3_access_key_id = malloc(sizeof(char) * (1+ strlen(jsonstr))); + strcpy(profile->aws_s3_access_key_id, jsonstr); + } + if (cJSON_GetObjectItem(json,"private_key") != NULL) { + jsonstr = cJSON_GetObjectItem(json,"private_key")->valuestring; + profile->secret_access_key = malloc(sizeof(char) * (1+ strlen(jsonstr))); + strcpy(profile->secret_access_key, jsonstr); + } + if (cJSON_GetObjectItem(json,"client_email") != NULL) { + jsonstr = cJSON_GetObjectItem(json,"client_email")->valuestring; + profile->gcs_email = malloc(sizeof(char) * (1+ strlen(jsonstr))); + strcpy(profile->gcs_email, jsonstr); + } + if (cJSON_GetObjectItem(json,"token_uri") != NULL) { + jsonstr = cJSON_GetObjectItem(json,"token_uri")->valuestring; + profile->region = malloc(sizeof(char) * (1+ strlen(jsonstr))); + strcpy(profile->region, jsonstr); + } + cJSON_Delete(json); + switch_safe_free(contents); + } else { + switch_xml_t private_key = switch_xml_child(xml, "private_key"); + switch_xml_t private_key_id = switch_xml_child(xml, "private_key_id"); + switch_xml_t client_email = switch_xml_child(xml, "client_email"); + switch_xml_t token_uri = switch_xml_child(xml, "token_uri"); + if (private_key_id) { + profile->aws_s3_access_key_id = switch_strip_whitespace(switch_xml_txt(private_key_id)); + } else { + //switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Missing key private_key_id\n"); + switch_safe_free(file); + return SWITCH_STATUS_FALSE; + } + if (private_key) { + profile->secret_access_key = switch_xml_txt(private_key); + } else { + //switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Missing key private_key\n"); + switch_safe_free(file); + return SWITCH_STATUS_FALSE; + } + if (client_email) { + profile->gcs_email = switch_strip_whitespace(switch_xml_txt(client_email)); + } else { + //switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Missing key client_email\n"); + switch_safe_free(file); + return SWITCH_STATUS_FALSE; + } + if (token_uri) { + profile->region = switch_strip_whitespace(switch_xml_txt(token_uri)); + } else { + //switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Missing key token_uri\n"); + switch_safe_free(file); + return SWITCH_STATUS_FALSE; + } + } + switch_safe_free(file); + profile->bytes_per_block = 4e6; + //switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set number of bytes per block to %zu\n", profile->bytes_per_block); + status = gcs_refresh_authorization(profile); + if (status != SWITCH_STATUS_SUCCESS){ + return status; + } + if (base_domain_xml) { + profile->base_domain = switch_strip_whitespace(switch_xml_txt(base_domain_xml)); + if (zstr(profile->base_domain)) { + switch_safe_free(profile->base_domain); + } + } +#endif + return status; +} + +#if defined(HAVE_OPENSSL) +static size_t gcs_auth_callback(void *ptr, size_t size, size_t nmemb, void *data) +{ + register unsigned int realsize = (unsigned int) (size * nmemb); + struct http_data *http_data = data; + + http_data->bytes += realsize; + + if (http_data->bytes > http_data->max_bytes) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Oversized file detected [%d bytes]\n", (int) http_data->bytes); + http_data->err = 1; + return 0; + } + + http_data->stream.write_function(&http_data->stream, "%.*s", realsize, ptr); + return realsize; +} + +char *gcs_auth_request(char *content, char *url) { + switch_CURL *curl_handle = NULL; + long httpRes = 0; + char *response = NULL; + switch_curl_slist_t *headers = NULL; + char *ct = "Content-Type: application/x-www-form-urlencoded"; + struct http_data http_data; + CURLcode res; + memset(&http_data, 0, sizeof(http_data)); + http_data.max_bytes = 10240; + SWITCH_STANDARD_STREAM(http_data.stream); + + curl_handle = switch_curl_easy_init(); + switch_curl_easy_setopt(curl_handle, CURLOPT_URL, url); + switch_curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDS, content); + switch_curl_easy_setopt(curl_handle, CURLOPT_CONNECTTIMEOUT, 5); + switch_curl_easy_setopt(curl_handle, CURLOPT_TIMEOUT, 10); + + headers = switch_curl_slist_append(headers, ct); + switch_curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "freeswitch-curl/1.0"); + switch_curl_easy_getinfo(curl_handle, CURLINFO_RESPONSE_CODE, &httpRes); + switch_curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, headers); + switch_curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, gcs_auth_callback); + switch_curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)&http_data); + + res = switch_curl_easy_perform(curl_handle); + switch_curl_easy_cleanup(curl_handle); + + if(res != CURLE_OK) + fprintf(stderr, "curl_easy_perform() failed: %s\n", + switch_curl_easy_strerror(res)); + + if (http_data.stream.data && !zstr((char *) http_data.stream.data) && strcmp(" ", http_data.stream.data)) { + cJSON *json = {0}; + json = cJSON_Parse(http_data.stream.data); + + if (cJSON_GetObjectItem(json,"access_token") != NULL) { + char *jsonstr; + jsonstr = cJSON_GetObjectItem(json,"access_token")->valuestring; + response = malloc(sizeof(char) * (1+strlen(jsonstr))); + strcpy(response, jsonstr); + } + cJSON_Delete(json); + } + switch_safe_free(http_data.stream.data); + if (headers) { + switch_curl_slist_free_all(headers); + } + return response; +} +#endif + + +/* For Emacs: + * Local Variables: + * mode:c + * indent-tabs-mode:t + * tab-width:4 + * c-basic-offset:4 + * End: + * For VIM: + * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet + */ diff --git a/src/mod/applications/mod_http_cache/gcs.h b/src/mod/applications/mod_http_cache/gcs.h new file mode 100644 index 0000000000..3ae15e3e4c --- /dev/null +++ b/src/mod/applications/mod_http_cache/gcs.h @@ -0,0 +1,53 @@ +/* + * gcs.h for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2013-2014, Grasshopper + * + * Version: MPL 1.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is gcs.h for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * + * The Initial Developer of the Original Code is Baroukh Ovadia + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Baroukh Ovadia + * + * gcs.h - Some Google Cloud Storage helper functions + * + */ +#ifndef GCS_H +#define GCS_H + +#include +#include +#include + +/* (SHA1_LENGTH * 1.37 base64 bytes per byte * 3 url-encoded bytes per byte) */ +#define GCS_SIGNATURE_LENGTH_MAX 1024 + +SWITCH_MOD_DECLARE(switch_curl_slist_t*) gcs_append_headers(http_profile_t *profile, switch_curl_slist_t *headers, + const char *verb, unsigned int content_length, const char *content_type, const char *url, const unsigned int block_num, char **query_string); +SWITCH_MOD_DECLARE(switch_status_t) gcs_config_profile(switch_xml_t xml, http_profile_t *profile,switch_memory_pool_t *pool); + +#endif +/* For Emacs: + * Local Variables: + * mode:c + * indent-tabs-mode:t + * tab-width:4 + * c-basic-offset:4 + * End: + * For VIM: + * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet + */ diff --git a/src/mod/applications/mod_http_cache/mod_http_cache.c b/src/mod/applications/mod_http_cache/mod_http_cache.c index 365ba27425..2fb7afe63d 100644 --- a/src/mod/applications/mod_http_cache/mod_http_cache.c +++ b/src/mod/applications/mod_http_cache/mod_http_cache.c @@ -36,6 +36,7 @@ #include #include "aws.h" #include "azure.h" +#include "gcs.h" #include @@ -1698,14 +1699,21 @@ static switch_status_t do_config(url_cache_t *cache) continue; } } else { - profile_xml = switch_xml_child(profile, "default"); + profile_xml = switch_xml_child(profile, "gcs"); if (profile_xml) { - if (default_config_profile(profile_xml, profile_obj, cache->pool) == SWITCH_STATUS_FALSE) { + if (gcs_config_profile(profile_xml, profile_obj, cache->pool) == SWITCH_STATUS_FALSE) { continue; } + } else { + profile_xml = switch_xml_child(profile, "default"); + if (profile_xml) { + if (default_config_profile(profile_xml, profile_obj, cache->pool) == SWITCH_STATUS_FALSE) { + continue; + } + } } } - } + } switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Adding profile \"%s\" to cache\n", name); switch_core_hash_insert(cache->profiles, profile_obj->name, profile_obj); diff --git a/src/mod/applications/mod_http_cache/test/test_gcs.c b/src/mod/applications/mod_http_cache/test/test_gcs.c new file mode 100644 index 0000000000..f2cc0c66ac --- /dev/null +++ b/src/mod/applications/mod_http_cache/test/test_gcs.c @@ -0,0 +1,153 @@ +/* + * aws.h for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2013-2014, Grasshopper + * + * Version: MPL 1.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is aws.h for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * + * The Initial Developer of the Original Code is Grasshopper + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Chris Rienzo + * Quoc-Bao Nguyen + * + * test_aws.c - Unit tests for functions in aws.c + * + */ + +#include +#include +#include "../gcs.c" +#include + +// Run test +// make && libtool --mode=execute valgrind --leak-check=full --log-file=vg.log ./test/test_aws && cat vg.log + +FST_BEGIN() +{ +FST_SUITE_BEGIN() +{ + +FST_SETUP_BEGIN() +{ +} +FST_SETUP_END() + +FST_TEARDOWN_BEGIN() +{ +} +FST_TEARDOWN_END() + +#if defined(HAVE_OPENSSL) +FST_TEST_BEGIN(encoded_token) +{ +//char *encoded_token(const char *token_uri, const char *client_email, const char *private_key_id, int *token_length, time_t now) { + time_t now = 1615402513; + char *token = NULL; + int token_length; + + /* + char *tkn = "TESTER"; + int encoded_tlength = 8; + unsigned char *tknb64 = malloc(encoded_tlength * sizeof(char)); + switch_b64_encode((unsigned char *) tkn,sizeof(tkn), tknb64, encoded_tlength); + */ + + token = encoded_token("https://accounts.google.com/o/oauth2/token", "gcs@freeswitch.com", "667265657377697463682D676373", &token_length, now); + fst_check_string_equals("eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6IjY2NzI2NTY1NzM3NzY5NzQ2MzY4MkQ2NzYzNzMifQ.eyJpYXQiOiIxNjE1NDAyNTEzIiwiZXhwIjoiMTYxNTQwNjExMyIsImlzcyI6Imdjc0BmcmVlc3dpdGNoLmNvbSIsImF1ZCI6Imh0dHBzOi8vYWNjb3VudHMuZ29vZ2xlLmNvbS9vL29hdXRoMi90b2tlbiIsInNjb3BlIjoiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vYXV0aC9kZXZzdG9yYWdlLmZ1bGxfY29udHJvbCBodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9hdXRoL2RldnN0b3JhZ2UucmVhZF9vbmx5IGh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL2F1dGgvZGV2c3RvcmFnZS5yZWFkX3dyaXRlIn0", token); + free(token); +} +FST_TEST_END() + +FST_TEST_BEGIN(signtoken) +{ +//void signtoken(char *token, int tokenlen,char *pkey, char *out) { + char *encoded = NULL; + char *pkey = "-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCXiVOV3h61llym\nnpHamUHsuVjrdDiEQnNX1KA4k/kcfP+gqRjL9m3YAxXfUA9GFCIC2OYvdciW3Ggm\nt4CSYmSltljKjbN2JHs7iNQw4CcFfiAXhxL0TYNhNE9wBDOZRsC1Uusv38RPwqwd\n922pAGzF+PqNE2j+zSxlNOnlJfyJDMKrqCGV8CclS+j/u41MaT6cpOlHP6KgaS01\nJJVyaqLpnMLWAx9/5G6Y/YWah+obEyn7yoDva/1Yhlnq20CMHlh3ifDfYYrS9rtp\nhdutz4fESKFPKymlG9aGfFuCME3GP8Rn8ZdPbsAWZ2cf388CLjlxEid1EU8klr2X\n+G1+3di/AgMBAAECggEAHL6UZ9+/5IMWpRZ8JUKgAjbwWo1rsQ7n0TfIgqLzBIfj\nd4bL6NigYnLHYdpOY2UrRG3/T+5gM9mwOfPiBCJ85AAwXI+/hIAMDjF4yqKiVETl\n8oCRRF01uCkTjnSFkyQcJukJKsYf918+hdqq5v1pJK6DXGJbrsWdj78XRPvNKPPD\naEEvSNwdet12Jz9wkmj2g7zg8KMX18v3IUyf7HKjb/cjomB0WuIDfJchDRYlxrfJ\n4DShcIfMi+04C/FFN+vbP77tXQM7O3Z81uqDQAO3k3NoXTTNBZGLP+SOyDUSRZQS\nCNb3J6cwTC737e0M6K+zVP1f03ynuU2u+dHiVVMTtQKBgQDNw/19HSxoJ/5nGHbD\nLW1g33Jm4Nr05lYnetEPS0wkruzEbTy3B6prl34KUslreuUTAuhtFxCLsEgT/sMO\nrxX/OM7RaNUILrpLrzen/eVNeiquM1wLEI52VNkRU5GlTZEJohGE+a3YP+kQTLe5\n8xmzKfJUllyfpXGxDNkryjaeSwKBgQC8iBqDJs6h9tkdxC/XC8+qSJ+WBk5tr3PM\nyx/x1NGKO5TpgdhRr98GYYUhoph+TIp0/8/+d2lVDzO4SAOzms3xnANPEzJcLi7c\nCa8ECOW3S4HhWE61QsbVY5xA83hGAO2WQN22vu9KwhyFU145aSQH0tJrQoevkdBl\ndpqtP15W3QKBgQCsL8gePJ1+g4k2SJiJd6hCGnoncR6JNX7/Bp2PiNktEVx8e1UF\nbNrFsj388Y4v7OVo5VQOhfCIlHmckeI0lXt42dboEivC7ydiUjvmzmZmUUcKA1yQ\nvcgZaaNEBoSoqaInR4IVnsJFZiXoR+qvJqlo7j8lXbYgulfLaw8Iv+y4xQKBgGK6\no6eq2urWajy8UJE9DjMOdQQLqWanSu0kMkZiPJk3OnROGwosH48n4qAKlfEOBDPh\nAvsvbWmt3FfU3ptfphmwqcrvMqAzTzbLm2txfVrPn+RyakViAt4cm+cnmQSP19un\nfHQG6SktHeJ0FhPai5PNQ4QIAyZeJdP8mGPBm5XBAoGBAJWnXiapFMcHi3DgsVb4\n+ni8Qvs293OvsdjHlo2eent/Kwbrdytw/V8uhq9awJb1npdgVd54RrbZ+Jq4x19K\nt06Jz9/EAoLLfL+tqzpEiKvSLjdKpedPm1Cgfj0KTM+MqoSU0bv4gMssnM428luJ\nv5ptWjeaHoYpJzvGfGBouVCI\n-----END PRIVATE KEY-----\n"; + int tlength = snprintf(NULL, 0, "%s", pkey); + encoded = malloc(sizeof(char) * 343); + signtoken("eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6IjY2NzI2NTY1NzM3NzY5NzQ2MzY4MkQ2NzYzNzMifQ.eyJpYXQiOiIxNjE1NDAyNTEzIiwiZXhwIjoiMTYxNTQwNjExMyIsImlzcyI6Imdjc0BmcmVlc3dpdGNoLmNvbSIsImF1ZCI6Imh0dHBzOi8vYWNjb3VudHMuZ29vZ2xlLmNvbS9vL29hdXRoMi90b2tlbiIsInNjb3BlIjoiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vYXV0aC9kZXZzdG9yYWdlLmZ1bGxfY29udHJvbCBodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9hdXRoL2RldnN0b3JhZ2UucmVhZF9vbmx5IGh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL2F1dGgvZGV2c3RvcmFnZS5yZWFkX3dyaXRlIn0", tlength, pkey, encoded); + fst_check(!zstr(encoded)); + //fst_check_string_equals("D38uUWBI1nwFQ30Ha7ZqwYJrIIRGsgCfMcBadukv9wPhu3qiCvJxpvCVVdhqfXicB0iWyOQ+6BTZkHlbC2tTzqe1dxnTM2xvYaOePAfrsou02qQlOatbzX+kwL1ZsuUrxCNEX3NmkGLIJD9zo7LH+/d0wYdFZ7c2iLncv/9+mNjnEQ8a22x00T58hVIoapbYYJsXg4uTnWgruwk39YFh5OghspXYDd+dCG076vOqbFa9D5s/da/2bBp6Rw83zCcM+l8ZEWUZFRNnaGT0mT3NvQcPVsXSeGlLV0onwQw4x9NK3ZMHCDCoDUUPZy6O8Tg6Hn+usDgp3/07RqAUER7nDA", encoded); + free(encoded); +} +FST_TEST_END() + +FST_TEST_BEGIN(parse_xml_config_with_gcs) +{ + switch_xml_t cfg, profiles, profile; + http_profile_t http_profile; + int fd; + int i = 0; + + + fd = open("test_gcs_http_cache.conf.xml", O_RDONLY); + if (fd < 0) { + //printf("Open in test dir\n"); + fd = open("test/test_gcs_http_cache.conf.xml", O_RDONLY); + } + fst_check(fd > 0); + + cfg = switch_xml_parse_fd(fd); + fst_check(cfg != NULL); + + profiles = switch_xml_child(cfg, "profiles"); + fst_check(profiles); + + for (profile = switch_xml_child(profiles, "profile"); profile; profile = profile->next) { + const char *name = NULL; + switch_memory_pool_t *pool; + switch_core_new_memory_pool(&pool); + i++; + + fst_check(profile); + + name = switch_xml_attr_soft(profile, "name"); + fst_check(name); + + http_profile.name = name; + http_profile.aws_s3_access_key_id = NULL; + http_profile.secret_access_key = NULL; + http_profile.base_domain = NULL; + http_profile.region = NULL; + + gcs_config_profile(profile, &http_profile, pool); + +//aws_s3_access_key_id, secret_access_key, gcs_email, region + fst_check(!zstr(http_profile.region)); + fst_check(!zstr(http_profile.aws_s3_access_key_id)); + fst_check(!zstr(http_profile.secret_access_key)); + switch_safe_free(http_profile.region); + switch_safe_free(http_profile.aws_s3_access_key_id); + switch_safe_free(http_profile.secret_access_key); + switch_safe_free(http_profile.base_domain); + switch_safe_free(http_profile.gcs_email); + switch_core_destroy_memory_pool(&pool); + } + + fst_check(i == 1); // test data contain two config + + switch_xml_free(cfg); +} +FST_TEST_END() + +#endif + +} +FST_SUITE_END() + +} +FST_END() diff --git a/src/mod/applications/mod_http_cache/test/test_gcs.json b/src/mod/applications/mod_http_cache/test/test_gcs.json new file mode 100644 index 0000000000..a9bbcab4db --- /dev/null +++ b/src/mod/applications/mod_http_cache/test/test_gcs.json @@ -0,0 +1,12 @@ +{ + "type": "service_account", + "project_id": "freeswitch-gcs", + "private_key_id": "667265657377697463682D676373", + "private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCXiVOV3h61llym\nnpHamUHsuVjrdDiEQnNX1KA4k/kcfP+gqRjL9m3YAxXfUA9GFCIC2OYvdciW3Ggm\nt4CSYmSltljKjbN2JHs7iNQw4CcFfiAXhxL0TYNhNE9wBDOZRsC1Uusv38RPwqwd\n922pAGzF+PqNE2j+zSxlNOnlJfyJDMKrqCGV8CclS+j/u41MaT6cpOlHP6KgaS01\nJJVyaqLpnMLWAx9/5G6Y/YWah+obEyn7yoDva/1Yhlnq20CMHlh3ifDfYYrS9rtp\nhdutz4fESKFPKymlG9aGfFuCME3GP8Rn8ZdPbsAWZ2cf388CLjlxEid1EU8klr2X\n+G1+3di/AgMBAAECggEAHL6UZ9+/5IMWpRZ8JUKgAjbwWo1rsQ7n0TfIgqLzBIfj\nd4bL6NigYnLHYdpOY2UrRG3/T+5gM9mwOfPiBCJ85AAwXI+/hIAMDjF4yqKiVETl\n8oCRRF01uCkTjnSFkyQcJukJKsYf918+hdqq5v1pJK6DXGJbrsWdj78XRPvNKPPD\naEEvSNwdet12Jz9wkmj2g7zg8KMX18v3IUyf7HKjb/cjomB0WuIDfJchDRYlxrfJ\n4DShcIfMi+04C/FFN+vbP77tXQM7O3Z81uqDQAO3k3NoXTTNBZGLP+SOyDUSRZQS\nCNb3J6cwTC737e0M6K+zVP1f03ynuU2u+dHiVVMTtQKBgQDNw/19HSxoJ/5nGHbD\nLW1g33Jm4Nr05lYnetEPS0wkruzEbTy3B6prl34KUslreuUTAuhtFxCLsEgT/sMO\nrxX/OM7RaNUILrpLrzen/eVNeiquM1wLEI52VNkRU5GlTZEJohGE+a3YP+kQTLe5\n8xmzKfJUllyfpXGxDNkryjaeSwKBgQC8iBqDJs6h9tkdxC/XC8+qSJ+WBk5tr3PM\nyx/x1NGKO5TpgdhRr98GYYUhoph+TIp0/8/+d2lVDzO4SAOzms3xnANPEzJcLi7c\nCa8ECOW3S4HhWE61QsbVY5xA83hGAO2WQN22vu9KwhyFU145aSQH0tJrQoevkdBl\ndpqtP15W3QKBgQCsL8gePJ1+g4k2SJiJd6hCGnoncR6JNX7/Bp2PiNktEVx8e1UF\nbNrFsj388Y4v7OVo5VQOhfCIlHmckeI0lXt42dboEivC7ydiUjvmzmZmUUcKA1yQ\nvcgZaaNEBoSoqaInR4IVnsJFZiXoR+qvJqlo7j8lXbYgulfLaw8Iv+y4xQKBgGK6\no6eq2urWajy8UJE9DjMOdQQLqWanSu0kMkZiPJk3OnROGwosH48n4qAKlfEOBDPh\nAvsvbWmt3FfU3ptfphmwqcrvMqAzTzbLm2txfVrPn+RyakViAt4cm+cnmQSP19un\nfHQG6SktHeJ0FhPai5PNQ4QIAyZeJdP8mGPBm5XBAoGBAJWnXiapFMcHi3DgsVb4\n+ni8Qvs293OvsdjHlo2eent/Kwbrdytw/V8uhq9awJb1npdgVd54RrbZ+Jq4x19K\nt06Jz9/EAoLLfL+tqzpEiKvSLjdKpedPm1Cgfj0KTM+MqoSU0bv4gMssnM428luJ\nv5ptWjeaHoYpJzvGfGBouVCI\n-----END PRIVATE KEY-----\n", + "client_email": "gcs@freeswitch.com", + "client_id": "105862293685176461395", + "auth_uri": "https://accounts.google.com/o/oauth2/auth", + "token_uri": "https://accounts.google.com/o/oauth2/token", + "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", + "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/gcs%40freeswitch.com" +} diff --git a/src/mod/applications/mod_http_cache/test/test_gcs_http_cache.conf.xml b/src/mod/applications/mod_http_cache/test/test_gcs_http_cache.conf.xml new file mode 100644 index 0000000000..7f004677df --- /dev/null +++ b/src/mod/applications/mod_http_cache/test/test_gcs_http_cache.conf.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + test_gcs.json + + +