1: %%============================================================================== 2: %% Copyright 2020 Erlang Solutions Ltd. 3: %% 4: %% Licensed under the Apache License, Version 2.0 (the "License"); 5: %% you may not use this file except in compliance with the License. 6: %% You may obtain a copy of the License at 7: %% 8: %% http://www.apache.org/licenses/LICENSE-2.0 9: %% 10: %% Unless required by applicable law or agreed to in writing, software 11: %% distributed under the License is distributed on an "AS IS" BASIS, 12: %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13: %% See the License for the specific language governing permissions and 14: %% limitations under the License. 15: %%============================================================================== 16: -module(users_api_SUITE). 17: -compile([export_all, nowarn_export_all]). 18: 19: -include_lib("eunit/include/eunit.hrl"). 20: 21: -import(distributed_helper, [mim/0, 22: require_rpc_nodes/1, 23: rpc/4]). 24: -import(rest_helper, [assert_status/2, simple_request/2, simple_request/3, simple_request/4]). 25: -import(domain_helper, [domain/0]). 26: -import(mongoose_helper, [auth_modules/0]). 27: 28: -define(DOMAIN, (domain())). 29: -define(PORT, (ct:get_config({hosts, mim, metrics_rest_port}))). 30: -define(USERNAME, "http_guy"). 31: 32: %%-------------------------------------------------------------------- 33: %% Suite configuration 34: %%-------------------------------------------------------------------- 35: 36: all() -> 37: [{group, transaction}, 38: {group, negative}]. 39: 40: groups() -> 41: G = [{transaction, [{repeat_until_any_fail, 10}], [user_transaction]}, 42: {negative, [], negative_calls_test_cases()} 43: ], 44: ct_helper:repeat_all_until_all_ok(G). 45: 46: negative_calls_test_cases() -> 47: [ 48: add_malformed_user, 49: add_user_without_proper_fields, 50: delete_non_existent_user 51: ]. 52: 53: init_per_suite(_Config) -> 54: case is_external_auth() of 55: true -> 56: {skip, "users api not compatible with external authentication"}; 57: false -> 58: [{riak_auth, is_riak_auth()}] 59: end. 60: 61: end_per_suite(_Config) -> 62: ok. 63: 64: suite() -> 65: require_rpc_nodes([mim]). 66: 67: %%-------------------------------------------------------------------- 68: %% users_api tests 69: %%-------------------------------------------------------------------- 70: 71: user_transaction(Config) -> 72: Count1 = fetch_list_of_users(Config), 73: add_user(?USERNAME, <<"my_http_password">>), 74: Count2 = fetch_list_of_users(Config), 75: % add user again = change their password 76: % check idempotence 77: add_user(?USERNAME, <<"some_other_password">>), 78: Count2 = fetch_list_of_users(Config), 79: add_user("http_guy2", <<"my_http_password">>), 80: Count3 = fetch_list_of_users(Config), 81: delete_user(?USERNAME), 82: delete_user("http_guy2"), 83: Count1 = fetch_list_of_users(Config), 84: 85: ?assertEqual(Count2, Count1+1), 86: ?assertEqual(Count3, Count2+1), 87: 88: wait_for_user_removal(proplists:get_value(riak_auth, Config)). 89: 90: add_malformed_user(_Config) -> 91: Path = unicode:characters_to_list(["/users/host/", ?DOMAIN, "/username/" ?USERNAME]), 92: % cannot use jiffy here, because the JSON is malformed 93: Res = simple_request(<<"PUT">>, Path, ?PORT, 94: <<"{ 95: \"user\": { 96: \"password\": \"my_http_password\" 97: }">>), 98: assert_status(400, Res). 99: 100: add_user_without_proper_fields(_Config) -> 101: Path = unicode:characters_to_list(["/users/host/", ?DOMAIN, "/username/" ?USERNAME]), 102: Body = jiffy:encode(#{<<"user">> => #{<<"pazzwourd">> => <<"my_http_password">>}}), 103: Res = simple_request(<<"PUT">>, Path, ?PORT, Body), 104: assert_status(422, Res). 105: 106: delete_non_existent_user(_Config) -> 107: Path = unicode:characters_to_list(["/users/host/", ?DOMAIN, "/username/i_don_exist"]), 108: Res = simple_request(<<"DELETE">>, Path, ?PORT), 109: assert_status(404, Res). 110: 111: %%-------------------------------------------------------------------- 112: %% internal functions 113: %%-------------------------------------------------------------------- 114: 115: fetch_list_of_users(_Config) -> 116: Result = simple_request(<<"GET">>, unicode:characters_to_list(["/users/host/", ?DOMAIN]), ?PORT), 117: assert_status(200, Result), 118: {_S, H, B} = Result, 119: ?assertEqual(<<"application/json">>, proplists:get_value(<<"content-type">>, H)), 120: #{<<"count">> := Count, <<"users">> := _} = B, 121: ?assertEqual(2, maps:size(B)), 122: Count. 123: 124: add_user(UserName, Password) -> 125: Path = unicode:characters_to_list(["/users/host/", ?DOMAIN, "/username/", UserName]), 126: Body = jiffy:encode(#{<<"user">> => #{<<"password">> => Password}}), 127: Res = simple_request(<<"PUT">>, Path, ?PORT, Body), 128: assert_status(204, Res), 129: Res. 130: 131: delete_user(UserName) -> 132: Path = unicode:characters_to_list(["/users/host/", ?DOMAIN, "/username/", UserName]), 133: Res = simple_request(<<"DELETE">>, Path, ?PORT), 134: assert_status(204, Res), 135: Res. 136: 137: is_external_auth() -> 138: lists:member(ejabberd_auth_external, auth_modules()). 139: 140: is_riak_auth() -> 141: lists:member(ejabberd_auth_riak, auth_modules()). 142: 143: wait_for_user_removal(false) -> 144: ok; 145: wait_for_user_removal(_) -> 146: Domain = domain(), 147: try mongoose_helper:wait_until( 148: fun() -> 149: rpc(mim(), ejabberd_auth_riak, get_vh_registered_users_number, [Domain]) 150: end, 151: 0, 152: #{ time_sleep => 500, time_left => 5000, name => rpc}) 153: of 154: {ok, 0} -> 155: ok 156: catch 157: _Error:Reason -> 158: ct:pal("~p", [Reason]), 159: ok 160: end.