From fa018b5fb0b87f4f7af7e262b9822aeb3c6893bc Mon Sep 17 00:00:00 2001 From: Marcos Tenrero Date: Mon, 4 Jan 2021 23:09:14 +0100 Subject: [PATCH 1/6] allow escaped equal characters in config keys --- src/config.erl | 50 ++++++++++++------------- test/config_tests.erl | 13 ++++++- test/fixtures/config_tests_escaping.ini | 23 ++++++++++++ 3 files changed, 59 insertions(+), 27 deletions(-) create mode 100644 test/fixtures/config_tests_escaping.ini diff --git a/src/config.erl b/src/config.erl index e8f7533..6aa191b 100644 --- a/src/config.erl +++ b/src/config.erl @@ -244,30 +244,30 @@ terminate(_Reason, _State) -> handle_call(all, _From, Config) -> Resp = lists:sort((ets:tab2list(?MODULE))), {reply, Resp, Config}; -handle_call({set, Sec, Key, Val, Opts}, _From, Config) -> - Persist = maps:get(persist, Opts, true), - Reason = maps:get(reason, Opts, nil), - IsSensitive = maps:get(sensitive, Opts, false), - case validate_config_update(Sec, Key, Val) of - {error, ValidationError} when IsSensitive -> - couch_log:error("~p: [~s] ~s = '****' rejected for reason ~p", - [?MODULE, Sec, Key, Reason]), - {reply, {error, ValidationError}, Config}; + handle_call({set, Sec, Key, Val, Opts}, _From, Config) -> + Persist = maps:get(persist, Opts, true), + Reason = maps:get(reason, Opts, nil), + IsSensitive = maps:get(sensitive, Opts, false), + case validate_config_update(Sec, Key, Val) of + {error, ValidationError} when IsSensitive -> + couch_log:error("~p: [~s] ~s = '****' rejected for reason ~p", + [?MODULE, Sec, Key, Reason]), + {reply, {error, ValidationError}, Config}; {error, ValidationError} -> couch_log:error("~p: [~s] ~s = '~s' rejected for reason ~p", [?MODULE, Sec, Key, Val, Reason]), {reply, {error, ValidationError}, Config}; ok -> - true = ets:insert(?MODULE, {{Sec, Key}, Val}), + true = ets:insert(?MODULE, {{Sec, Key}, Val}), case IsSensitive of - false -> - couch_log:notice("~p: [~s] ~s set to ~s for reason ~p", - [?MODULE, Sec, Key, Val, Reason]); - true -> - couch_log:notice("~p: [~s] ~s set to '****' for reason ~p", - [?MODULE, Sec, Key, Reason]) - end, - ConfigWriteReturn = case {Persist, Config#config.write_filename} of + false -> + couch_log:notice("~p: [~s] ~s set to ~s for reason ~p", + [?MODULE, Sec, Key, Val, Reason]); + true -> + couch_log:notice("~p: [~s] ~s set to '****' for reason ~p", + [?MODULE, Sec, Key, Reason]) + end, + ConfigWriteReturn = case {Persist, Config#config.write_filename} of {true, undefined} -> ok; {true, FileName} -> @@ -383,8 +383,9 @@ parse_ini_file(IniFile) -> ";" ++ _Comment -> {AccSectionName, AccValues}; Line2 -> - case re:split(Line2, "\s?=\s?", [{return, list}]) of - [Value] -> + MatchResult = re:run(Line2,<<"(.*(\\\\=)?\\S)(\\s?=\\s?)(.*)">>, [{capture,[1,4],list}]), + case MatchResult of + {match,[""|Value]} -> MultiLineValuePart = case re:run(Line, "^ \\S", []) of {match, _} -> true; @@ -406,19 +407,18 @@ parse_ini_file(IniFile) -> _ -> {AccSectionName, AccValues} end; - [""|_LineValues] -> % line begins with "=", ignore + nomatch -> % line begins with "=", ignore {AccSectionName, AccValues}; - [ValueName|LineValues] -> % yeehaw, got a line! - RemainingLine = config_util:implode(LineValues, "="), + {match,[ValueName|LineValues]} -> % yeehaw, got a line % removes comments - case re:split(RemainingLine, " ;|\t;", [{return, list}]) of + case re:split(LineValues, " ;|\t;", [{return, list}]) of [[]] -> % empty line means delete this key ets:delete(?MODULE, {AccSectionName, ValueName}), {AccSectionName, AccValues}; [LineValue | _Rest] -> {AccSectionName, - [{{AccSectionName, ValueName}, LineValue} | AccValues]} + [{{AccSectionName, re:replace(ValueName, "\\\\=", "=", [{return,list}])}, LineValue} | AccValues]} end end end diff --git a/test/config_tests.erl b/test/config_tests.erl index fae0d43..db23d2d 100644 --- a/test/config_tests.erl +++ b/test/config_tests.erl @@ -36,6 +36,9 @@ -define(CONFIG_FIXTURE_2, filename:join([?CONFIG_FIXTURESDIR, "config_tests_2.ini"])). +-define(CONFIG_FIXTURE_ESCAPING, + filename:join([?CONFIG_FIXTURESDIR, "config_tests_escaping.ini"])). + -define(CONFIG_DEFAULT_D, filename:join([?CONFIG_FIXTURESDIR, "default.d"])). @@ -89,6 +92,8 @@ setup_config_notifier(Subscription) -> Pid = spawn_config_notifier(Subscription), {Apps, Pid}. +setup_with_escaped_chars() -> + setup(?CONFIG_CHAIN ++ [?CONFIG_FIXTURE_ESCAPING]). teardown({Apps, Pid}) when is_pid(Pid) -> catch exit(Pid, kill), @@ -130,7 +135,7 @@ config_get_test_() -> "Config get tests", { foreach, - fun setup/0, + fun setup_with_escaped_chars/0, fun teardown/1, [ fun should_load_all_configs/0, @@ -139,7 +144,8 @@ config_get_test_() -> fun should_return_custom_default_value_on_missed_option/0, fun should_only_return_default_on_missed_option/0, fun should_fail_to_get_binary_value/0, - fun should_return_any_supported_default/0 + fun should_return_any_supported_default/0, + fun should_return_values_from_escaped_keys/0 ] } }. @@ -374,6 +380,9 @@ should_return_undefined_atom_on_missed_option() -> should_return_custom_default_value_on_missed_option() -> ?assertEqual("bar", config:get("httpd", "foo", "bar")). +should_return_values_from_escaped_keys() -> + ?assertEqual("somepass", config:get("admins", "sample=user")), + ?assertEqual("somevalue", config:get("jwt_tokens","kid:somekey=")). should_only_return_default_on_missed_option() -> ?assertEqual("0", config:get("httpd", "port", "bar")). diff --git a/test/fixtures/config_tests_escaping.ini b/test/fixtures/config_tests_escaping.ini new file mode 100644 index 0000000..4feae50 --- /dev/null +++ b/test/fixtures/config_tests_escaping.ini @@ -0,0 +1,23 @@ +; Licensed to the Apache Software Foundation (ASF) under one +; or more contributor license agreements. See the NOTICE file +; distributed with this work for additional information +; regarding copyright ownership. The ASF licenses this file +; to you under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 +; +; Unless required by applicable law or agreed to in writing, +; software distributed under the License is distributed on an +; "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +; KIND, either express or implied. See the License for the +; specific language governing permissions and limitations +; under the License. + +[admins] +test=value +sample\=user = somepass + +[jwt_tokens] +kid:somekey\= = somevalue \ No newline at end of file From b571214d4eec1e28661a9356cf698cba5da8c18c Mon Sep 17 00:00:00 2001 From: Marcos Tenrero Date: Tue, 5 Jan 2021 22:57:50 +0100 Subject: [PATCH 2/6] restored indentation --- src/config.erl | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/config.erl b/src/config.erl index 6aa191b..21310e8 100644 --- a/src/config.erl +++ b/src/config.erl @@ -244,30 +244,30 @@ terminate(_Reason, _State) -> handle_call(all, _From, Config) -> Resp = lists:sort((ets:tab2list(?MODULE))), {reply, Resp, Config}; - handle_call({set, Sec, Key, Val, Opts}, _From, Config) -> - Persist = maps:get(persist, Opts, true), - Reason = maps:get(reason, Opts, nil), - IsSensitive = maps:get(sensitive, Opts, false), - case validate_config_update(Sec, Key, Val) of - {error, ValidationError} when IsSensitive -> - couch_log:error("~p: [~s] ~s = '****' rejected for reason ~p", - [?MODULE, Sec, Key, Reason]), - {reply, {error, ValidationError}, Config}; +handle_call({set, Sec, Key, Val, Opts}, _From, Config) -> + Persist = maps:get(persist, Opts, true), + Reason = maps:get(reason, Opts, nil), + IsSensitive = maps:get(sensitive, Opts, false), + case validate_config_update(Sec, Key, Val) of + {error, ValidationError} when IsSensitive -> + couch_log:error("~p: [~s] ~s = '****' rejected for reason ~p", + [?MODULE, Sec, Key, Reason]), + {reply, {error, ValidationError}, Config}; {error, ValidationError} -> couch_log:error("~p: [~s] ~s = '~s' rejected for reason ~p", [?MODULE, Sec, Key, Val, Reason]), {reply, {error, ValidationError}, Config}; ok -> - true = ets:insert(?MODULE, {{Sec, Key}, Val}), + true = ets:insert(?MODULE, {{Sec, Key}, Val}), case IsSensitive of - false -> - couch_log:notice("~p: [~s] ~s set to ~s for reason ~p", - [?MODULE, Sec, Key, Val, Reason]); - true -> - couch_log:notice("~p: [~s] ~s set to '****' for reason ~p", - [?MODULE, Sec, Key, Reason]) - end, - ConfigWriteReturn = case {Persist, Config#config.write_filename} of + false -> + couch_log:notice("~p: [~s] ~s set to ~s for reason ~p", + [?MODULE, Sec, Key, Val, Reason]); + true -> + couch_log:notice("~p: [~s] ~s set to '****' for reason ~p", + [?MODULE, Sec, Key, Reason]) + end, + ConfigWriteReturn = case {Persist, Config#config.write_filename} of {true, undefined} -> ok; {true, FileName} -> From a456af664f8dff731fedcb04896355e13c381078 Mon Sep 17 00:00:00 2001 From: Marcos Tenrero Date: Tue, 5 Jan 2021 22:58:47 +0100 Subject: [PATCH 3/6] removed bad indentation --- src/config.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config.erl b/src/config.erl index 21310e8..46b74ea 100644 --- a/src/config.erl +++ b/src/config.erl @@ -251,7 +251,7 @@ handle_call({set, Sec, Key, Val, Opts}, _From, Config) -> case validate_config_update(Sec, Key, Val) of {error, ValidationError} when IsSensitive -> couch_log:error("~p: [~s] ~s = '****' rejected for reason ~p", - [?MODULE, Sec, Key, Reason]), + [?MODULE, Sec, Key, Reason]), {reply, {error, ValidationError}, Config}; {error, ValidationError} -> couch_log:error("~p: [~s] ~s = '~s' rejected for reason ~p", From 8a9be2c0bb30f6022482134cd5326b2b7feba36f Mon Sep 17 00:00:00 2001 From: Marcos Tenrero Date: Tue, 5 Jan 2021 22:59:25 +0100 Subject: [PATCH 4/6] removed extra space --- src/config.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config.erl b/src/config.erl index 46b74ea..bc4c357 100644 --- a/src/config.erl +++ b/src/config.erl @@ -251,7 +251,7 @@ handle_call({set, Sec, Key, Val, Opts}, _From, Config) -> case validate_config_update(Sec, Key, Val) of {error, ValidationError} when IsSensitive -> couch_log:error("~p: [~s] ~s = '****' rejected for reason ~p", - [?MODULE, Sec, Key, Reason]), + [?MODULE, Sec, Key, Reason]), {reply, {error, ValidationError}, Config}; {error, ValidationError} -> couch_log:error("~p: [~s] ~s = '~s' rejected for reason ~p", From 7675b55e2a34b22a4c927c02d7a378e304cb6e87 Mon Sep 17 00:00:00 2001 From: Marcos Tenrero Date: Tue, 5 Jan 2021 23:00:09 +0100 Subject: [PATCH 5/6] restore original status of unwanted changes --- src/config.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config.erl b/src/config.erl index bc4c357..fc1b398 100644 --- a/src/config.erl +++ b/src/config.erl @@ -251,7 +251,7 @@ handle_call({set, Sec, Key, Val, Opts}, _From, Config) -> case validate_config_update(Sec, Key, Val) of {error, ValidationError} when IsSensitive -> couch_log:error("~p: [~s] ~s = '****' rejected for reason ~p", - [?MODULE, Sec, Key, Reason]), + [?MODULE, Sec, Key, Reason]), {reply, {error, ValidationError}, Config}; {error, ValidationError} -> couch_log:error("~p: [~s] ~s = '~s' rejected for reason ~p", From a4c1d8e71c6a3eac7127a901f0922c5aec3dd114 Mon Sep 17 00:00:00 2001 From: Marcos Tenrero Date: Tue, 5 Jan 2021 23:00:44 +0100 Subject: [PATCH 6/6] trailing space removal --- src/config.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config.erl b/src/config.erl index fc1b398..e54f967 100644 --- a/src/config.erl +++ b/src/config.erl @@ -251,7 +251,7 @@ handle_call({set, Sec, Key, Val, Opts}, _From, Config) -> case validate_config_update(Sec, Key, Val) of {error, ValidationError} when IsSensitive -> couch_log:error("~p: [~s] ~s = '****' rejected for reason ~p", - [?MODULE, Sec, Key, Reason]), + [?MODULE, Sec, Key, Reason]), {reply, {error, ValidationError}, Config}; {error, ValidationError} -> couch_log:error("~p: [~s] ~s = '~s' rejected for reason ~p",