@@ -246,8 +246,6 @@ path(BasePath, RawParams, Version, Options) ->
246246qs_list (v1 ) ->
247247 [
248248 {" db" , database },
249- {" u" , username },
250- {" p" , password },
251249 {" precision" , precision }
252250 ];
253251qs_list (v2 ) ->
@@ -265,20 +263,47 @@ write_path(v1) -> "/write";
265263write_path (v2 ) -> " /api/v2/write" ;
266264write_path (v3 ) -> " /api/v3/write_lp" .
267265
268- header (v1 , _ ) ->
269- [{<<" Content-type" >>, <<" text/plain; charset=utf-8" >>}];
266+ header (v1 , Options ) ->
267+ maybe_add_basic_auth_header (
268+ [{<<" Content-type" >>, <<" text/plain; charset=utf-8" >>}],
269+ Options
270+ );
270271header (v2 , Options ) ->
271272 Token = proplists :get_value (token , Options , <<" " >>),
272- [{<<" Authorization" >>, <<" Token " , Token /binary >>} | header ( v1 , Options ) ];
273+ [{<<" Authorization" >>, <<" Token " , Token /binary >>}, {<< " Content-type " >>, << " text/plain; charset=utf-8 " >>} ];
273274header (v3 , Options ) ->
274275 Token = proplists :get_value (token , Options , <<" " >>),
275- [{<<" Authorization" >>, <<" Bearer " , Token /binary >>} | header ( v1 , Options ) ].
276+ [{<<" Authorization" >>, <<" Bearer " , Token /binary >>}, {<< " Content-type " >>, << " text/plain; charset=utf-8 " >>} ].
276277
277278
278279str (A ) when is_atom (A ) -> atom_to_list (A );
279280str (B ) when is_binary (B ) -> binary_to_list (B );
280281str (L ) when is_list (L ) -> L .
281282
283+ maybe_add_basic_auth_header (Headers , Options ) ->
284+ case basic_auth_value (Options ) of
285+ undefined -> Headers ;
286+ Authorization -> [{<<" Authorization" >>, Authorization } | Headers ]
287+ end .
288+
289+ basic_auth_value (Options ) ->
290+ case {proplists :get_value (username , Options ), proplists :get_value (password , Options )} of
291+ {undefined , undefined } ->
292+ undefined ;
293+ {Username , Password } ->
294+ Encoded = base64 :encode (
295+ iolist_to_binary ([to_binary_or_empty (Username ), <<" :" >>, to_binary_or_empty (Password )])
296+ ),
297+ <<" Basic " , Encoded /binary >>
298+ end .
299+
300+ to_binary (Value ) when is_binary (Value ) -> Value ;
301+ to_binary (Value ) when is_list (Value ) -> unicode :characters_to_binary (Value );
302+ to_binary (Value ) when is_atom (Value ) -> atom_to_binary (Value , utf8 ).
303+
304+ to_binary_or_empty (undefined ) -> <<>>;
305+ to_binary_or_empty (Value ) -> to_binary (Value ).
306+
282307% %===================================================================
283308% % eunit tests
284309% %===================================================================
@@ -294,9 +319,9 @@ auth_path_v1_no_show_databases_test() ->
294319 ? assertEqual (nomatch , string :find (Path , " show" )),
295320 ? assertEqual (nomatch , string :find (Path , " SHOW" )),
296321 ? assertEqual (nomatch , string :find (Path , " q=" )),
297- % % but must contain credential params
298- ? assertNotEqual (nomatch , string :find (Path , " u=user" )),
299- ? assertNotEqual (nomatch , string :find (Path , " p=pass" )).
322+ % % and must not leak credentials into the query string
323+ ? assertEqual (nomatch , string :find (Path , " u=user" )),
324+ ? assertEqual (nomatch , string :find (Path , " p=pass" )).
300325
301326auth_path_v2_undefined_test () ->
302327 Options = [{token , <<" mytoken" >>}, {org , " myorg" }, {bucket , " mybucket" }],
@@ -313,36 +338,54 @@ http_clients_options_v1_no_show_databases_test() ->
313338 ? assertEqual (nomatch , string :find (AuthPath , " show" )),
314339 ? assertEqual (nomatch , string :find (AuthPath , " q=" )).
315340
316- v1_ping_auth_params_default_disabled_test () ->
341+ v1_paths_do_not_include_ping_auth_params_test () ->
317342 Options = [{version , v1 }, {database , " mydb" }, {username , " user" }, {password , " pass" }],
318- PingParams = influxdb_http :ping_auth_params (Options ),
319343 #{path := WritePath , auth_path := AuthPath } = http_clients_options (Options ),
320- ? assertEqual ([], PingParams ),
321344 ? assertNotEqual (nomatch , string :find (WritePath , " /write" )),
322345 ? assertNotEqual (nomatch , string :find (WritePath , " db=mydb" )),
323- ? assertNotEqual (nomatch , string :find (AuthPath , " /query" )).
324-
325- v1_ping_auth_params_enabled_test () ->
326- Options =
327- [{version , v1 }, {database , " mydb" }, {username , " user" }, {password , " pass" }, {ping_with_auth , true }],
328- PingParams = influxdb_http :ping_auth_params (Options ),
329- ? assertNotEqual (nomatch , string :find (PingParams , " verbose=true" )),
330- ? assertNotEqual (nomatch , string :find (PingParams , " u=user" )),
331- ? assertNotEqual (nomatch , string :find (PingParams , " p=pass" )),
332- ? assertEqual (nomatch , string :find (PingParams , " db=mydb" )),
333- ? assertEqual (nomatch , string :find (PingParams , " precision=" )).
334-
335- v2_ping_auth_params_ignored_test () ->
336- Options = [{version , v2 }, {token , <<" tok" >>}],
337- ? assertEqual ([], influxdb_http :ping_auth_params (Options )).
338-
339- v3_ping_auth_params_ignored_test () ->
340- Options = [{version , v3 }, {token , <<" tok" >>}, {database , " mydb" }],
341- ? assertEqual ([], influxdb_http :ping_auth_params (Options )).
346+ ? assertEqual (nomatch , string :find (WritePath , " u=" )),
347+ ? assertEqual (nomatch , string :find (WritePath , " p=" )),
348+ ? assertNotEqual (nomatch , string :find (AuthPath , " /query" )),
349+ ? assertEqual (nomatch , string :find (AuthPath , " u=" )),
350+ ? assertEqual (nomatch , string :find (AuthPath , " p=" )).
351+
352+ v1_header_uses_basic_auth_test () ->
353+ Options = [{username , <<" user" >>}, {password , <<" pass" >>}],
354+ Headers = header (v1 , Options ),
355+ ? assert (lists :member (
356+ {<<" Authorization" >>, <<" Basic dXNlcjpwYXNz" >>},
357+ Headers
358+ )).
359+
360+ v1_header_supports_username_without_password_test () ->
361+ Headers = header (v1 , [{username , <<" user" >>}]),
362+ ? assert (lists :member (
363+ {<<" Authorization" >>, <<" Basic dXNlcjo=" >>},
364+ Headers
365+ )).
366+
367+ v1_header_supports_password_without_username_test () ->
368+ Headers = header (v1 , [{password , <<" pass" >>}]),
369+ ? assert (lists :member (
370+ {<<" Authorization" >>, <<" Basic OnBhc3M=" >>},
371+ Headers
372+ )).
373+
374+ v2_header_does_not_include_basic_auth_test () ->
375+ Options = [{token , <<" tok" >>}, {username , <<" user" >>}, {password , <<" pass" >>}],
376+ Headers = header (v2 , Options ),
377+ AuthorizationHeaders = [Value || {Key , Value } <- Headers , Key =:= <<" Authorization" >>],
378+ ? assertEqual ([<<" Token tok" >>], AuthorizationHeaders ).
379+
380+ v3_header_does_not_include_basic_auth_test () ->
381+ Options = [{token , <<" tok" >>}, {username , <<" user" >>}, {password , <<" pass" >>}],
382+ Headers = header (v3 , Options ),
383+ AuthorizationHeaders = [Value || {Key , Value } <- Headers , Key =:= <<" Authorization" >>],
384+ ? assertEqual ([<<" Bearer tok" >>], AuthorizationHeaders ).
342385
343386v1_is_alive_with_ping_auth_enabled_test () ->
344387 application :ensure_all_started (influxdb ),
345- ListenSocket = ping_auth_server_start (<<" user " >>, << " pass " >>),
388+ ListenSocket = ping_auth_server_start (<<" Authorization: Basic dXNlcjpwYXNz \r\n " >>),
346389 {ok , {_Addr , Port }} = inet :sockname (ListenSocket ),
347390 Options =
348391 [ {host , " 127.0.0.1" }
@@ -367,7 +410,7 @@ v1_is_alive_with_ping_auth_enabled_test() ->
367410
368411v1_is_alive_accepts_verbose_ping_auth_response_test () ->
369412 application :ensure_all_started (influxdb ),
370- ListenSocket = ping_auth_server_start (<<" user " >>, << " pass " >>, <<" HTTP/1.1 200 OK\r\n " >>),
413+ ListenSocket = ping_auth_server_start (<<" Authorization: Basic dXNlcjpwYXNz \r\n " >>, <<" HTTP/1.1 200 OK\r\n " >>),
371414 {ok , {_Addr , Port }} = inet :sockname (ListenSocket ),
372415 Options =
373416 [ {host , " 127.0.0.1" }
@@ -392,7 +435,7 @@ v1_is_alive_accepts_verbose_ping_auth_response_test() ->
392435
393436v1_is_alive_defaults_to_legacy_ping_test () ->
394437 application :ensure_all_started (influxdb ),
395- ListenSocket = ping_auth_server_start ( undefined , undefined ),
438+ ListenSocket = auth_header_ping_server_start ( undefined ),
396439 {ok , {_Addr , Port }} = inet :sockname (ListenSocket ),
397440 Options =
398441 [ {host , " 127.0.0.1" }
@@ -416,7 +459,7 @@ v1_is_alive_defaults_to_legacy_ping_test() ->
416459
417460v1_is_alive_rejects_bad_ping_auth_when_enabled_test () ->
418461 application :ensure_all_started (influxdb ),
419- ListenSocket = ping_auth_server_start (<<" user " >>, << " pass " >>),
462+ ListenSocket = ping_auth_server_start (<<" Authorization: Basic dXNlcjpwYXNz \r\n " >>),
420463 {ok , {_Addr , Port }} = inet :sockname (ListenSocket ),
421464 Options =
422465 [ {host , " 127.0.0.1" }
@@ -542,28 +585,28 @@ http_clients_options_v3_auth_path_undefined_test() ->
542585 #{auth_path := AuthPath } = http_clients_options (Options ),
543586 ? assertEqual (undefined , AuthPath ).
544587
545- ping_auth_server_start (ExpectedUser , ExpectedPassword ) ->
546- ping_auth_server_start (ExpectedUser , ExpectedPassword , <<" HTTP/1.1 204 No Content\r\n " >>).
588+ ping_auth_server_start (ExpectedAuthorization ) ->
589+ ping_auth_server_start (ExpectedAuthorization , <<" HTTP/1.1 204 No Content\r\n " >>).
547590
548- ping_auth_server_start (ExpectedUser , ExpectedPassword , SuccessStatusLine ) ->
591+ ping_auth_server_start (ExpectedAuthorization , SuccessStatusLine ) ->
549592 {ok , ListenSocket } = gen_tcp :listen (0 , [binary , {active , false }, {reuseaddr , true }]),
550- spawn_link (fun () -> ping_auth_server_serve (ListenSocket , ExpectedUser , ExpectedPassword , SuccessStatusLine ) end ),
593+ spawn_link (fun () -> ping_auth_server_serve (ListenSocket , ExpectedAuthorization , SuccessStatusLine ) end ),
551594 ListenSocket .
552595
553596auth_header_ping_server_start (ExpectedHeader ) ->
554597 {ok , ListenSocket } = gen_tcp :listen (0 , [binary , {active , false }, {reuseaddr , true }]),
555598 spawn_link (fun () -> auth_header_ping_server_serve (ListenSocket , ExpectedHeader ) end ),
556599 ListenSocket .
557600
558- ping_auth_server_serve (ListenSocket , ExpectedUser , ExpectedPassword , SuccessStatusLine ) ->
601+ ping_auth_server_serve (ListenSocket , ExpectedAuthorization , SuccessStatusLine ) ->
559602 {ok , Socket } = gen_tcp :accept (ListenSocket ),
560603 {ok , Request } = gen_tcp :recv (Socket , 0 , 5000 ),
561604 StatusLine =
562- case ping_auth_request_result (Request , ExpectedUser , ExpectedPassword ) of
605+ case ping_auth_request_result (Request , ExpectedAuthorization ) of
563606 true -> SuccessStatusLine ;
564607 false -> <<" HTTP/1.1 401 Unauthorized\r\n " >>
565608 end ,
566- ok = gen_tcp :send (Socket , [StatusLine , <<" Content-Length: 0\r\n Connection: close\r\n\r\n " >>]),
609+ _ = gen_tcp :send (Socket , [StatusLine , <<" Content-Length: 0\r\n Connection: close\r\n\r\n " >>]),
567610 ok = gen_tcp :close (Socket ),
568611 ok = gen_tcp :close (ListenSocket ).
569612
@@ -575,7 +618,7 @@ auth_header_ping_server_serve(ListenSocket, ExpectedHeader) ->
575618 true -> <<" HTTP/1.1 204 No Content\r\n " >>;
576619 false -> <<" HTTP/1.1 401 Unauthorized\r\n " >>
577620 end ,
578- ok = gen_tcp :send (Socket , [StatusLine , <<" Content-Length: 0\r\n Connection: close\r\n\r\n " >>]),
621+ _ = gen_tcp :send (Socket , [StatusLine , <<" Content-Length: 0\r\n Connection: close\r\n\r\n " >>]),
579622 ok = gen_tcp :close (Socket ),
580623 ok = gen_tcp :close (ListenSocket ).
581624
@@ -585,13 +628,14 @@ auth_header_ping_request_result(Request, undefined) ->
585628auth_header_ping_request_result (Request , ExpectedHeader ) ->
586629 contains (Request , <<" GET /ping HTTP/" >>) andalso contains_ci (Request , ExpectedHeader ).
587630
588- ping_auth_request_result (Request , undefined , undefined ) ->
631+ ping_auth_request_result (Request , undefined ) ->
589632 contains (Request , <<" GET /ping HTTP/" >>);
590- ping_auth_request_result (Request , ExpectedUser , ExpectedPassword ) ->
591- contains (Request , <<" GET /ping?" >>) andalso
592- contains (Request , <<" verbose=true" >>) andalso
593- contains (Request , <<" u=" , ExpectedUser /binary >>) andalso
594- contains (Request , <<" p=" , ExpectedPassword /binary >>).
633+ ping_auth_request_result (Request , ExpectedAuthorization ) ->
634+ contains (Request , <<" GET /ping HTTP/" >>) andalso
635+ contains_ci (Request , ExpectedAuthorization ) andalso
636+ not contains (Request , <<" GET /ping?" >>) andalso
637+ not contains_ci (Request , <<" u=" >>) andalso
638+ not contains_ci (Request , <<" p=" >>).
595639
596640contains (Binary , Pattern ) ->
597641 binary :match (Binary , Pattern ) =/= nomatch .
0 commit comments