1: -module(keystore_SUITE). 2: -include_lib("eunit/include/eunit.hrl"). 3: -compile([export_all, nowarn_export_all]). 4: 5: -define(ae(Expected, Actual), ?assertEqual(Expected, Actual)). 6: 7: all() -> 8: [ 9: module_startup_no_opts, 10: module_startup_read_key_from_file, 11: module_startup_create_ram_key, 12: module_startup_create_ram_key_of_given_size, 13: module_startup_for_multiple_domains, 14: module_startup_non_unique_key_ids, 15: multiple_domains_one_stopped 16: ]. 17: 18: init_per_suite(C) -> 19: {ok, _} = application:ensure_all_started(jid), 20: ok = mnesia:create_schema([node()]), 21: ok = mnesia:start(), 22: C. 23: 24: end_per_suite(C) -> 25: mnesia:stop(), 26: mnesia:delete_schema([node()]), 27: C. 28: 29: init_per_testcase(_, Config) -> 30: mock_mongoose_metrics(), 31: async_helper:start(Config, gen_hook, start_link, []). 32: 33: end_per_testcase(module_startup_non_unique_key_ids, C) -> 34: clean_after_testcase(C); 35: end_per_testcase(module_startup_for_multiple_domains, C) -> 36: mod_keystore:stop(<<"first.com">>), 37: mod_keystore:stop(<<"second.com">>), 38: clean_after_testcase(C); 39: end_per_testcase(multiple_domains_one_stopped, C) -> 40: mod_keystore:stop(<<"second.com">>), 41: clean_after_testcase(C); 42: end_per_testcase(_CaseName, C) -> 43: mod_keystore:stop(<<"localhost">>), 44: clean_after_testcase(C). 45: 46: clean_after_testcase(C) -> 47: meck:unload(mongoose_metrics), 48: async_helper:stop_all(C), 49: mnesia:delete_table(key), 50: C. 51: 52: %% 53: %% Tests 54: %% 55: 56: module_startup_no_opts(_) -> 57: ok = mod_keystore:start(<<"localhost">>, []). 58: 59: module_startup_read_key_from_file(_) -> 60: %% given 61: RawKey = <<"qwe123">>, 62: {ok, KeyFile} = key_at("/tmp/key-from-file", RawKey), 63: %% when 64: ok = mod_keystore:start(<<"localhost">>, key_from_file(KeyFile)), 65: %% then 66: ?ae([{{key_from_file, <<"localhost">>}, RawKey}], 67: get_key(<<"localhost">>, key_from_file)). 68: 69: module_startup_create_ram_key(Config) -> 70: module_startup_create_ram_key(Config, ram_key()), 71: %% then we can access the key 72: [{{ram_key, <<"localhost">>}, Key}] = get_key(<<"localhost">>, ram_key), 73: true = is_binary(Key). 74: 75: module_startup_create_ram_key_of_given_size(Config) -> 76: KeySize = 4, 77: module_startup_create_ram_key(Config, sized_ram_key(KeySize)), 78: %% then 79: [{{ram_key, <<"localhost">>}, Key}] = get_key(<<"localhost">>, ram_key), 80: true = is_binary(Key), 81: KeySize = byte_size(Key). 82: 83: module_startup_create_ram_key(_, ModKeystoreOpts) -> 84: %% given no key 85: [] = get_key(<<"localhost">>, ram_key), 86: %% when keystore starts with config to generate a memory-only key 87: ok = mod_keystore:start(<<"localhost">>, ModKeystoreOpts). 88: 89: module_startup_for_multiple_domains(_Config) -> 90: %% given 91: [] = get_key(<<"first.com">>, key_from_file), 92: [] = get_key(<<"second.com">>, key_from_file), 93: FirstKey = <<"random-first.com-key-content">>, 94: SecondKey = <<"random-second.com-key-content">>, 95: {ok, FirstKeyFile} = key_at("/tmp/first.com", FirstKey), 96: {ok, SecondKeyFile} = key_at("/tmp/second.com", SecondKey), 97: %% when 98: ok = mod_keystore:start(<<"first.com">>, key_from_file(FirstKeyFile)), 99: ok = mod_keystore:start(<<"second.com">>, key_from_file(SecondKeyFile)), 100: %% then 101: ?ae([{{key_from_file, <<"first.com">>}, FirstKey}], 102: get_key(<<"first.com">>, key_from_file)), 103: ?ae([{{key_from_file, <<"second.com">>}, SecondKey}], 104: get_key(<<"second.com">>, key_from_file)). 105: 106: module_startup_non_unique_key_ids(_) -> 107: %% given 108: NonUniqueKeyIDsOpts = [{keys, [{some_key, ram}, 109: {some_key, {file, "some_key.dat"}}]}], 110: %% when 111: try 112: mod_keystore:start(<<"localhost">>, NonUniqueKeyIDsOpts) 113: %% then 114: catch 115: error:non_unique_key_ids -> ok 116: end. 117: 118: multiple_domains_one_stopped(_Config) -> 119: % given 120: [] = get_key(<<"first.com">>, key_from_file), 121: [] = get_key(<<"second.com">>, key_from_file), 122: FirstKey = <<"random-first.com-key-content">>, 123: SecondKey = <<"random-second.com-key-content">>, 124: {ok, FirstKeyFile} = key_at("/tmp/first.com", FirstKey), 125: {ok, SecondKeyFile} = key_at("/tmp/second.com", SecondKey), 126: % when 127: ok = mod_keystore:start(<<"first.com">>, key_from_file(FirstKeyFile)), 128: ok = mod_keystore:start(<<"second.com">>, key_from_file(SecondKeyFile)), 129: ok = mod_keystore:stop(<<"first.com">>), 130: % then 131: ?ae([{{key_from_file, <<"second.com">>}, SecondKey}], 132: get_key(<<"second.com">>, key_from_file)). 133: 134: %% 135: %% Helpers 136: %% 137: 138: start_async(M, F, A) -> 139: Self = self(), 140: P = spawn(fun () -> 141: erlang:apply(M, F, A), 142: Self ! started, 143: helper_loop() 144: end), 145: receive 146: started -> 147: %ct:pal("started", []), 148: {ok, P} 149: after timer:seconds(1) -> 150: ct:fail("async start timeout") 151: end. 152: 153: helper_loop() -> 154: receive 155: stop -> exit(normal); 156: _ -> helper_loop() 157: end. 158: 159: key_at(Path, Data) -> 160: ok = file:write_file(Path, Data), 161: {ok, Path}. 162: 163: key_from_file(KeyFile) -> 164: [{keys, [{key_from_file, {file, KeyFile}}]}]. 165: 166: ram_key() -> 167: [{keys, [{ram_key, ram}]}]. 168: 169: sized_ram_key(Size) -> 170: [{keys, [{ram_key, ram}]}, 171: {ram_key_size, Size}]. 172: 173: mock_mongoose_metrics() -> 174: meck:new(mongoose_metrics, []), 175: meck:expect(mongoose_metrics, create_generic_hook_metric, fun (_, _) -> ok end), 176: meck:expect(mongoose_metrics, increment_generic_hook_metric, fun (_, _) -> ok end), 177: ok. 178: 179: %% Use a function like this in your module which is a client of mod_keystore. 180: -spec get_key(HostType, KeyName) -> Result when 181: HostType :: mongooseim:host_type(), 182: KeyName :: mod_keystore:key_name(), 183: Result :: mod_keystore:key_list(). 184: get_key(HostType, KeyName) -> 185: mongoose_hooks:get_key(HostType, KeyName). 186: 187: %%{mod_keystore, [{keys, [{asdqwe_access_secret, ram}, 188: %% {asdqwe_access_psk, {file, "priv/asdqwe_access_psk"}}, 189: %% {asdqwe_provision_psk, {file, "priv/asdqwe_access_psk"}}]}]},