Skip to content

Commit 3aaf735

Browse files
authored
Add support for API Key authentication (#313)
This adds support for authenticating using API Keys. The DSN editor has been updated to take a new "API Key" field. This field is enabled iif no Username and Password are provided. The field is stored in the DSN string and then used in every request for respective configured connection.
1 parent 0d8b656 commit 3aaf735

File tree

9 files changed

+423
-273
lines changed

9 files changed

+423
-273
lines changed

driver/connect.c

Lines changed: 87 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@
3535
#define HTTP_CONTENT_TYPE_JSON "Content-Type: " HTTP_APP_JSON \
3636
"; charset=utf-8"
3737

38+
/* HTTP header API Key authentication-specific */
39+
#define HTTP_AUTH_API_KEY "Authorization: ApiKey "
40+
#define APIKEY_BUFF_SIZE 512
41+
3842
/* Elasticsearch/SQL data types */
3943
/* 2 */
4044
#define TYPE_IP "IP"
@@ -382,6 +386,10 @@ static void cleanup_curl(esodbc_dbc_st *dbc)
382386
return;
383387
}
384388
DBGH(dbc, "libcurl: handle 0x%p cleanup.", dbc->curl);
389+
if (dbc->curl_hdrs) {
390+
curl_slist_free_all(dbc->curl_hdrs);
391+
dbc->curl_hdrs = NULL;
392+
}
385393
dbc->curl_err = CURLE_OK;
386394
dbc->curl_err_buff[0] = '\0';
387395

@@ -442,11 +450,34 @@ SQLRETURN dbc_curl_set_url(esodbc_dbc_st *dbc, int url_type)
442450
return SQL_ERROR;
443451
}
444452

453+
/* copy of unexported Curl_slist_duplicate() libcurl function */
454+
static struct curl_slist *curl_slist_duplicate(struct curl_slist *inlist)
455+
{
456+
struct curl_slist *outlist = NULL;
457+
struct curl_slist *tmp;
458+
459+
while(inlist) {
460+
tmp = curl_slist_append(outlist, inlist->data);
461+
462+
if(!tmp) {
463+
curl_slist_free_all(outlist);
464+
return NULL;
465+
}
466+
467+
outlist = tmp;
468+
inlist = inlist->next;
469+
}
470+
return outlist;
471+
}
472+
445473
static SQLRETURN dbc_curl_init(esodbc_dbc_st *dbc)
446474
{
447475
CURL *curl;
448476
SQLRETURN ret;
449477
BOOL compress;
478+
char apikey_buff[APIKEY_BUFF_SIZE], *apikey_ptr = NULL;
479+
struct curl_slist *curl_hdrs;
480+
450481

451482
assert(! dbc->curl);
452483

@@ -467,14 +498,6 @@ static SQLRETURN dbc_curl_init(esodbc_dbc_st *dbc)
467498
goto err;
468499
}
469500

470-
/* set the HTTP headers: Content-Type, Accept */
471-
dbc->curl_err = curl_easy_setopt(curl, CURLOPT_HTTPHEADER,
472-
dbc->pack_json ? json_headers : cbor_headers);
473-
if (dbc->curl_err != CURLE_OK) {
474-
ERRH(dbc, "libcurl: failed to set HTTP headers list.");
475-
goto err;
476-
}
477-
478501
/* set the behavior for redirection */
479502
dbc->curl_err = curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION,
480503
dbc->follow);
@@ -582,8 +605,55 @@ static SQLRETURN dbc_curl_init(esodbc_dbc_st *dbc)
582605
goto err;
583606
}
584607
}
608+
} else if (dbc->api_key.cnt) {
609+
if (sizeof(HTTP_AUTH_API_KEY) + dbc->api_key.cnt <
610+
sizeof(apikey_buff)) {
611+
apikey_ptr = apikey_buff;
612+
} else {
613+
DBGH(dbc, "static buffer size %zuB less than required %zuB, "
614+
"allocating.", sizeof(apikey_buff), sizeof(HTTP_AUTH_API_KEY) +
615+
dbc->api_key.cnt);
616+
apikey_ptr = malloc(sizeof(HTTP_AUTH_API_KEY) + dbc->api_key.cnt);
617+
if (! apikey_ptr) {
618+
ERRNH(dbc, "OOM for %zuB.", sizeof(HTTP_AUTH_API_KEY) +
619+
dbc->api_key.cnt);
620+
goto err;
621+
}
622+
}
623+
memcpy(apikey_ptr, HTTP_AUTH_API_KEY, sizeof(HTTP_AUTH_API_KEY) - 1);
624+
memcpy(apikey_ptr + sizeof(HTTP_AUTH_API_KEY) - 1, dbc->api_key.str,
625+
dbc->api_key.cnt);
626+
apikey_ptr[sizeof(HTTP_AUTH_API_KEY) - 1 + dbc->api_key.cnt] = '\0';
627+
628+
dbc->curl_hdrs = curl_slist_append(NULL, apikey_ptr);
629+
if (apikey_ptr != apikey_buff) {
630+
free(apikey_ptr);
631+
apikey_ptr = NULL;
632+
}
633+
if (! dbc->curl_hdrs) {
634+
ERRH(dbc, "libcurl: failed to init API key Auth header.");
635+
goto err;
636+
}
585637
} else {
586-
INFOH(dbc, "no username provided: auth disabled.");
638+
INFOH(dbc, "no username or API key provided: auth disabled.");
639+
}
640+
641+
/* set the HTTP headers: Content-Type, Accept, Authorization(?) */
642+
curl_hdrs = dbc->pack_json ? json_headers : cbor_headers;
643+
/* is there already an Autorization header set? then chain the pack hdrs */
644+
if (dbc->curl_hdrs) {
645+
if (! (curl_hdrs = curl_slist_duplicate(curl_hdrs))) {
646+
ERRNH(dbc, "failed duplicating packing format headers.");
647+
goto err;
648+
}
649+
dbc->curl_hdrs->next = curl_hdrs;
650+
/* if there's no Authz header, the pack headers won't be dup'd */
651+
curl_hdrs = dbc->curl_hdrs;
652+
}
653+
dbc->curl_err = curl_easy_setopt(curl, CURLOPT_HTTPHEADER, curl_hdrs);
654+
if (dbc->curl_err != CURLE_OK) {
655+
ERRH(dbc, "libcurl: failed to set HTTP headers list.");
656+
goto err;
587657
}
588658

589659
/* proxy parameters */
@@ -1322,6 +1392,14 @@ SQLRETURN config_dbc(esodbc_dbc_st *dbc, esodbc_dsn_attrs_st *attrs)
13221392
/* indicates the presence of a non-empty password */
13231393
INFOH(dbc, "connection PWD: " ESODBC_PWD_VAL_SUBST ".");
13241394
}
1395+
} else if (attrs->api_key.cnt) {
1396+
if (! wstr_to_utf8(&attrs->api_key, &dbc->api_key)) {
1397+
ERRH(dbc, "failed to convert API key [%zu] `" LWPDL "` to UTF8.",
1398+
attrs->api_key.cnt, LWSTR(&attrs->api_key));
1399+
SET_HDIAG(dbc, SQL_STATE_HY000, "API key UTF8 conversion failed",
1400+
0);
1401+
goto err;
1402+
}
13251403
}
13261404

13271405
/* "follow location" param for liburl */

driver/dsn.c

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ void TEST_API init_dsn_attrs(esodbc_dsn_attrs_st *attrs)
3333
static inline wstr_st *mask_pwd(wstr_st *attr, wstr_st *val)
3434
{
3535
static wstr_st subst = WSTR_INIT(ESODBC_PWD_VAL_SUBST);
36-
return EQ_CASE_WSTR(attr, &MK_WSTR(ESODBC_DSN_PWD)) ? &subst : val;
36+
return EQ_CASE_WSTR(attr, &MK_WSTR(ESODBC_DSN_PWD)) ||
37+
EQ_CASE_WSTR(attr, &MK_WSTR(ESODBC_DSN_API_KEY)) ? &subst : val;
3738
}
3839

3940
#define DSN_NOT_MATCHED 0
@@ -61,6 +62,7 @@ int assign_dsn_attr(esodbc_dsn_attrs_st *attrs,
6162
{&MK_WSTR(ESODBC_DSN_DSN), &attrs->dsn},
6263
{&MK_WSTR(ESODBC_DSN_PWD), &attrs->pwd},
6364
{&MK_WSTR(ESODBC_DSN_UID), &attrs->uid},
65+
{&MK_WSTR(ESODBC_DSN_API_KEY), &attrs->api_key},
6466
{&MK_WSTR(ESODBC_DSN_SAVEFILE), &attrs->savefile},
6567
{&MK_WSTR(ESODBC_DSN_FILEDSN), &attrs->filedsn},
6668
{&MK_WSTR(ESODBC_DSN_CLOUD_ID), &attrs->cloud_id},
@@ -404,6 +406,7 @@ long TEST_API write_00_list(esodbc_dsn_attrs_st *attrs,
404406
{&MK_WSTR(ESODBC_DSN_DSN), &attrs->dsn},
405407
{&MK_WSTR(ESODBC_DSN_PWD), &attrs->pwd},
406408
{&MK_WSTR(ESODBC_DSN_UID), &attrs->uid},
409+
{&MK_WSTR(ESODBC_DSN_API_KEY), &attrs->api_key},
407410
{&MK_WSTR(ESODBC_DSN_SAVEFILE), &attrs->savefile},
408411
{&MK_WSTR(ESODBC_DSN_FILEDSN), &attrs->filedsn},
409412
{&MK_WSTR(ESODBC_DSN_CLOUD_ID), &attrs->cloud_id},
@@ -637,6 +640,10 @@ BOOL write_system_dsn(esodbc_dsn_attrs_st *new_attrs,
637640
&MK_WSTR(ESODBC_DSN_UID), &new_attrs->uid,
638641
old_attrs ? &old_attrs->uid : NULL
639642
},
643+
{
644+
&MK_WSTR(ESODBC_DSN_API_KEY), &new_attrs->api_key,
645+
old_attrs ? &old_attrs->api_key : NULL
646+
},
640647
/* SAVEILE */
641648
/* FILEDSN */
642649
{
@@ -819,6 +826,7 @@ long TEST_API write_connection_string(esodbc_dsn_attrs_st *attrs,
819826
{&attrs->dsn, &MK_WSTR(ESODBC_DSN_DSN)},
820827
{&attrs->pwd, &MK_WSTR(ESODBC_DSN_PWD)},
821828
{&attrs->uid, &MK_WSTR(ESODBC_DSN_UID)},
829+
{&attrs->api_key, &MK_WSTR(ESODBC_DSN_API_KEY)},
822830
{&attrs->savefile, &MK_WSTR(ESODBC_DSN_SAVEFILE)},
823831
{&attrs->filedsn, &MK_WSTR(ESODBC_DSN_FILEDSN)},
824832
{&attrs->cloud_id, &MK_WSTR(ESODBC_DSN_CLOUD_ID)},

driver/dsn.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#define ESODBC_DSN_DSN "DSN"
2121
#define ESODBC_DSN_PWD "PWD"
2222
#define ESODBC_DSN_UID "UID"
23+
#define ESODBC_DSN_API_KEY "APIKey"
2324
#define ESODBC_DSN_SAVEFILE "SAVEFILE"
2425
#define ESODBC_DSN_FILEDSN "FILEDSN"
2526
#define ESODBC_DSN_CLOUD_ID "CloudID"
@@ -71,6 +72,7 @@ typedef struct {
7172
wstr_st dsn;
7273
wstr_st pwd;
7374
wstr_st uid;
75+
wstr_st api_key;
7476
wstr_st savefile;
7577
wstr_st filedsn;
7678
wstr_st cloud_id;
@@ -102,7 +104,7 @@ typedef struct {
102104
wstr_st trace_enabled;
103105
wstr_st trace_file;
104106
wstr_st trace_level;
105-
#define ESODBC_DSN_ATTRS_COUNT 36
107+
#define ESODBC_DSN_ATTRS_COUNT 37
106108

107109
SQLWCHAR buff[ESODBC_DSN_ATTRS_COUNT * ESODBC_DSN_MAX_ATTR_LEN];
108110
/* DSN reading/writing functions are passed a SQLSMALLINT length param */

driver/handles.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,10 @@ typedef struct struct_dbc {
150150
cstr_st ca_path;
151151

152152
cstr_st uid;
153-
cstr_st pwd;
153+
union {
154+
cstr_st pwd; /* when dbc configuring, pwd is only set if uid is */
155+
cstr_st api_key;
156+
};
154157
SQLUINTEGER timeout;
155158
BOOL follow;
156159
struct {
@@ -204,6 +207,7 @@ typedef struct struct_dbc {
204207
size_t apos; /* current write position in the abuff */
205208
size_t amax; /* maximum length (bytes) that abuff can grow to */
206209
esodbc_mutex_lt curl_mux; /* mutex for above 'networking' members */
210+
struct curl_slist *curl_hdrs; /* HTTP headers list */
207211

208212
/* window handler */
209213
HWND hwin;

0 commit comments

Comments
 (0)