1: -module(graphql_account_SUITE). 2: 3: -include_lib("common_test/include/ct.hrl"). 4: -include_lib("eunit/include/eunit.hrl"). 5: 6: -compile([export_all, nowarn_export_all]). 7: 8: -import(distributed_helper, [mim/0, require_rpc_nodes/1, rpc/4]). 9: -import(graphql_helper, [execute/3, execute_auth/2, get_listener_port/1, get_listener_config/1]). 10: 11: -define(NOT_EXISTING_JID, <<"unknown987@unknown">>). 12: 13: suite() -> 14: require_rpc_nodes([mim]) ++ escalus:suite(). 15: 16: all() -> 17: [{group, user_account_handler}, 18: {group, admin_account_handler}]. 19: 20: groups() -> 21: [{user_account_handler, [parallel], user_account_handler()}, 22: {admin_account_handler, [], admin_account_handler()}]. 23: 24: user_account_handler() -> 25: [user_unregister, 26: user_change_password]. 27: 28: admin_account_handler() -> 29: [admin_list_users, 30: admin_count_users, 31: admin_get_active_users_number, 32: admin_check_password, 33: admin_check_password_hash, 34: admin_check_plain_password_hash, 35: admin_check_user, 36: admin_register_user, 37: admin_register_random_user, 38: admin_remove_non_existing_user, 39: admin_remove_existing_user, 40: admin_ban_user, 41: admin_change_user_password, 42: admin_list_old_users_domain, 43: admin_list_old_users_all, 44: admin_remove_old_users_domain, 45: admin_remove_old_users_all]. 46: 47: init_per_suite(Config) -> 48: Config1 = [{ctl_auth_mods, mongoose_helper:auth_modules()} | Config], 49: Config2 = escalus:init_per_suite(Config1), 50: dynamic_modules:save_modules(domain_helper:host_type(), Config2). 51: 52: end_per_suite(Config) -> 53: dynamic_modules:restore_modules(Config), 54: escalus:end_per_suite(Config). 55: 56: init_per_group(admin_account_handler, Config) -> 57: dynamic_modules:ensure_modules(domain_helper:host_type(), [{mod_last, []}]), 58: dynamic_modules:ensure_modules(domain_helper:secondary_host_type(), [{mod_last, []}]), 59: Config1 = escalus:create_users(Config, escalus:get_users([alice])), 60: graphql_helper:init_admin_handler(Config1); 61: init_per_group(user_account_handler, Config) -> 62: [{schema_endpoint, user} | Config]; 63: init_per_group(_, Config) -> 64: Config. 65: 66: end_per_group(admin_account_handler, Config) -> 67: escalus_fresh:clean(), 68: escalus:delete_users(Config, escalus:get_users([alice])), 69: dynamic_modules:restore_modules(Config); 70: end_per_group(user_account_handler, _Config) -> 71: escalus_fresh:clean(); 72: end_per_group(_, _Config) -> 73: ok. 74: 75: init_per_testcase(C, Config) when C =:= admin_list_old_users_all; 76: C =:= admin_list_old_users_domain; 77: C =:= admin_remove_old_users_all; 78: C =:= admin_remove_old_users_domain -> 79: {_, AuthMods} = lists:keyfind(ctl_auth_mods, 1, Config), 80: case lists:member(ejabberd_auth_ldap, AuthMods) of 81: true -> {skip, not_fully_supported_with_ldap}; 82: false -> 83: Config1 = escalus:create_users(Config, escalus:get_users([alice, bob, alice_bis])), 84: escalus:init_per_testcase(C, Config1) 85: end; 86: init_per_testcase(admin_register_user = C, Config) -> 87: Config1 = [{user, {<<"gql_admin_registration_test">>, domain_helper:domain()}} | Config], 88: escalus:init_per_testcase(C, Config1); 89: init_per_testcase(admin_check_plain_password_hash = C, Config) -> 90: {_, AuthMods} = lists:keyfind(ctl_auth_mods, 1, Config), 91: case lists:member(ejabberd_auth_ldap, AuthMods) of 92: true -> 93: {skip, not_fully_supported_with_ldap}; 94: false -> 95: AuthOpts = mongoose_helper:auth_opts_with_password_format(plain), 96: Config1 = mongoose_helper:backup_and_set_config_option( 97: Config, {auth, domain_helper:host_type()}, AuthOpts), 98: Config2 = escalus:create_users(Config1, escalus:get_users([carol])), 99: escalus:init_per_testcase(C, Config2) 100: end; 101: init_per_testcase(CaseName, Config) -> 102: escalus:init_per_testcase(CaseName, Config). 103: 104: end_per_testcase(C, Config) when C =:= admin_list_old_users_all; 105: C =:= admin_list_old_users_domain; 106: C =:= admin_remove_old_users_all; 107: C =:= admin_remove_old_users_domain -> 108: escalus:delete_users(Config, escalus:get_users([alice, bob, alice_bis])); 109: end_per_testcase(admin_register_user = C, Config) -> 110: {Username, Domain} = proplists:get_value(user, Config), 111: rpc(mim(), mongoose_account_api, unregister_user, [Username, Domain]), 112: escalus:end_per_testcase(C, Config); 113: end_per_testcase(admin_check_plain_password_hash, Config) -> 114: mongoose_helper:restore_config(Config), 115: escalus:delete_users(Config, escalus:get_users([carol])); 116: end_per_testcase(CaseName, Config) -> 117: escalus:end_per_testcase(CaseName, Config). 118: 119: user_unregister(Config) -> 120: escalus:fresh_story_with_config(Config, [{alice, 1}], fun user_unregister_story/2). 121: 122: user_unregister_story(Config, Alice) -> 123: Ep = ?config(schema_endpoint, Config), 124: Password = lists:last(escalus_users:get_usp(Config, alice)), 125: BinJID = escalus_client:full_jid(Alice), 126: Creds = {BinJID, Password}, 127: Query = <<"mutation { account { unregister } }">>, 128: 129: Path = [data, account, unregister], 130: Resp = execute(Ep, #{query => Query}, Creds), 131: ?assertNotEqual(nomatch, binary:match(get_ok_value(Path, Resp), <<"successfully unregistered">>)), 132: % Ensure the user is removed 133: AllUsers = rpc(mim(), mongoose_account_api, list_users, [domain_helper:domain()]), 134: LAliceJID = jid:to_binary(jid:to_lower((jid:binary_to_bare(BinJID)))), 135: ?assertNot(lists:member(LAliceJID, AllUsers)). 136: 137: user_change_password(Config) -> 138: escalus:fresh_story_with_config(Config, [{alice, 1}], fun user_change_password_story/2). 139: 140: user_change_password_story(Config, Alice) -> 141: Ep = ?config(schema_endpoint, Config), 142: Password = lists:last(escalus_users:get_usp(Config, alice)), 143: Creds = {escalus_client:full_jid(Alice), Password}, 144: % Set an empty password 145: Resp1 = execute(Ep, user_change_password_body(<<>>), Creds), 146: ?assertNotEqual(nomatch, binary:match(get_err_msg(Resp1), <<"Empty password">>)), 147: % Set a correct password 148: Path = [data, account, changePassword], 149: Resp2 = execute(Ep, user_change_password_body(<<"kaczka">>), Creds), 150: ?assertNotEqual(nomatch, binary:match(get_ok_value(Path, Resp2), <<"Password changed">>)). 151: 152: admin_list_users(Config) -> 153: % An unknown domain 154: Resp = execute_auth(list_users_body(<<"unknown-domain">>), Config), 155: ?assertEqual([], get_ok_value([data, account, listUsers], Resp)), 156: % A domain with users 157: Domain = domain_helper:domain(), 158: Username = jid:nameprep(escalus_users:get_username(Config, alice)), 159: JID = <<Username/binary, "@", Domain/binary>>, 160: Resp2 = execute_auth(list_users_body(Domain), Config), 161: Users = get_ok_value([data, account, listUsers], Resp2), 162: ?assert(lists:member(JID, Users)). 163: 164: admin_count_users(Config) -> 165: % An unknown domain 166: Resp = execute_auth(count_users_body(<<"unknown-domain">>), Config), 167: ?assertEqual(0, get_ok_value([data, account, countUsers], Resp)), 168: % A domain with at least one user 169: Domain = domain_helper:domain(), 170: Resp2 = execute_auth(count_users_body(Domain), Config), 171: ?assert(0 < get_ok_value([data, account, countUsers], Resp2)). 172: 173: admin_get_active_users_number(Config) -> 174: % Check non-existing domain 175: Resp = execute_auth(get_active_users_number_body(<<"unknown-domain">>, 5), Config), 176: ?assertNotEqual(nomatch, binary:match(get_err_msg(Resp), <<"Cannot count">>)), 177: % Check an existing domain without active users 178: Resp2 = execute_auth(get_active_users_number_body(domain_helper:domain(), 5), Config), 179: ?assertEqual(0, get_ok_value([data, account, countActiveUsers], Resp2)). 180: 181: admin_check_password(Config) -> 182: Password = lists:last(escalus_users:get_usp(Config, alice)), 183: BinJID = escalus_users:get_jid(Config, alice), 184: Path = [data, account, checkPassword], 185: % A correct password 186: Resp1 = execute_auth(check_password_body(BinJID, Password), Config), 187: ?assertMatch(#{<<"correct">> := true, <<"message">> := _}, get_ok_value(Path, Resp1)), 188: % An incorrect password 189: Resp2 = execute_auth(check_password_body(BinJID, <<"incorrect_pw">>), Config), 190: ?assertMatch(#{<<"correct">> := false, <<"message">> := _}, get_ok_value(Path, Resp2)), 191: % A non-existing user 192: Resp3 = execute_auth(check_password_body(?NOT_EXISTING_JID, Password), Config), 193: ?assertEqual(null, get_ok_value(Path, Resp3)). 194: 195: admin_check_password_hash(Config) -> 196: UserSCRAM = escalus_users:get_jid(Config, alice), 197: EmptyHash = list_to_binary(get_md5(<<>>)), 198: Method = <<"md5">>, 199: % SCRAM password user 200: Resp1 = execute_auth(check_password_hash_body(UserSCRAM, EmptyHash, Method), Config), 201: ?assertNotEqual(nomatch, binary:match(get_err_msg(Resp1), <<"SCRAM password">>)), 202: % A non-existing user 203: Resp2 = execute_auth(check_password_hash_body(?NOT_EXISTING_JID, EmptyHash, Method), Config), 204: ?assertNotEqual(nomatch, binary:match(get_err_msg(Resp2), <<"not exist">>)). 205: 206: admin_check_plain_password_hash(Config) -> 207: UserJID = escalus_users:get_jid(Config, carol), 208: Password = lists:last(escalus_users:get_usp(Config, carol)), 209: Method = <<"md5">>, 210: Hash = list_to_binary(get_md5(Password)), 211: WrongHash = list_to_binary(get_md5(<<"wrong password">>)), 212: Path = [data, account, checkPasswordHash], 213: % A correct hash 214: Resp = execute_auth(check_password_hash_body(UserJID, Hash, Method), Config), 215: ?assertMatch(#{<<"correct">> := true, <<"message">> := _}, get_ok_value(Path, Resp)), 216: % An incorrect hash 217: Resp2 = execute_auth(check_password_hash_body(UserJID, WrongHash, Method), Config), 218: ?assertMatch(#{<<"correct">> := false, <<"message">> := _}, get_ok_value(Path, Resp2)), 219: % A not-supported hash method 220: Resp3 = execute_auth(check_password_hash_body(UserJID, Hash, <<"a">>), Config), 221: ?assertNotEqual(nomatch, binary:match(get_err_msg(Resp3), <<"not supported">>)). 222: 223: admin_check_user(Config) -> 224: BinJID = escalus_users:get_jid(Config, alice), 225: Path = [data, account, checkUser], 226: % An existing user 227: Resp1 = execute_auth(check_user_body(BinJID), Config), 228: ?assertMatch(#{<<"exist">> := true, <<"message">> := _}, get_ok_value(Path, Resp1)), 229: % A non-existing user 230: Resp2 = execute_auth(check_user_body(?NOT_EXISTING_JID), Config), 231: ?assertMatch(#{<<"exist">> := false, <<"message">> := _}, get_ok_value(Path, Resp2)). 232: 233: admin_register_user(Config) -> 234: Password = <<"my_password">>, 235: {Username, Domain} = proplists:get_value(user, Config), 236: Path = [data, account, registerUser, message], 237: % Register a new user 238: Resp1 = execute_auth(register_user_body(Domain, Username, Password), Config), 239: ?assertNotEqual(nomatch, binary:match(get_ok_value(Path, Resp1), <<"successfully registered">>)), 240: % Try to register a user with existing name 241: Resp2 = execute_auth(register_user_body(Domain, Username, Password), Config), 242: ?assertNotEqual(nomatch, binary:match(get_err_msg(Resp2), <<"already registered">>)). 243: 244: admin_register_random_user(Config) -> 245: Password = <<"my_password">>, 246: Domain = domain_helper:domain(), 247: Path = [data, account, registerUser], 248: % Register a new user 249: Resp1 = execute_auth(register_user_body(Domain, null, Password), Config), 250: #{<<"message">> := Msg, <<"jid">> := JID} = get_ok_value(Path, Resp1), 251: {Username, Server} = jid:to_lus(jid:from_binary(JID)), 252: 253: ?assertNotEqual(nomatch, binary:match(Msg, <<"successfully registered">>)), 254: {ok, _} = rpc(mim(), mongoose_account_api, unregister_user, [Username, Server]). 255: 256: admin_remove_non_existing_user(Config) -> 257: Resp = execute_auth(remove_user_body(?NOT_EXISTING_JID), Config), 258: ?assertNotEqual(nomatch, binary:match(get_err_msg(Resp), <<"not exist">>)). 259: 260: admin_remove_existing_user(Config) -> 261: escalus:fresh_story(Config, [{alice, 1}], fun(Alice) -> 262: Path = [data, account, removeUser, message], 263: BinJID = escalus_client:full_jid(Alice), 264: Resp4 = execute_auth(remove_user_body(BinJID), Config), 265: ?assertNotEqual(nomatch, binary:match(get_ok_value(Path, Resp4), 266: <<"successfully unregister">>)) 267: end). 268: 269: admin_ban_user(Config) -> 270: Path = [data, account, banUser, message], 271: Reason = <<"annoying">>, 272: % Ban not existing user 273: Resp1 = execute_auth(ban_user_body(?NOT_EXISTING_JID, Reason), Config), 274: ?assertNotEqual(nomatch, binary:match(get_err_msg(Resp1), <<"not allowed">>)), 275: % Ban an existing user 276: escalus:fresh_story(Config, [{alice, 1}], fun(Alice) -> 277: BinJID = escalus_client:full_jid(Alice), 278: Resp2 = execute_auth(ban_user_body(BinJID, Reason), Config), 279: ?assertNotEqual(nomatch, binary:match(get_ok_value(Path, Resp2), <<"successfully banned">>)) 280: end). 281: 282: admin_change_user_password(Config) -> 283: Path = [data, account, changeUserPassword, message], 284: NewPassword = <<"new password">>, 285: % Change password of not existing user 286: Resp1 = execute_auth(change_user_password_body(?NOT_EXISTING_JID, NewPassword), Config), 287: ?assertNotEqual(nomatch, binary:match(get_err_msg(Resp1), <<"not allowed">>)), 288: % Set an empty password 289: Resp2 = execute_auth(change_user_password_body(?NOT_EXISTING_JID, <<>>), Config), 290: ?assertNotEqual(nomatch, binary:match(get_err_msg(Resp2), <<"Empty password">>)), 291: % Change password of an existing user 292: escalus:fresh_story(Config, [{alice, 1}], fun(Alice) -> 293: BinJID = escalus_client:full_jid(Alice), 294: Resp3 = execute_auth(change_user_password_body(BinJID, NewPassword), Config), 295: ?assertNotEqual(nomatch, binary:match(get_ok_value(Path, Resp3), <<"Password changed">>)) 296: end). 297: 298: admin_remove_old_users_domain(Config) -> 299: [AliceName, Domain, _] = escalus_users:get_usp(Config, alice), 300: [BobName, Domain, _] = escalus_users:get_usp(Config, bob), 301: [AliceBisName, BisDomain, _] = escalus_users:get_usp(Config, alice_bis), 302: 303: Now = erlang:system_time(seconds), 304: set_last(AliceName, Domain, Now), 305: set_last(BobName, Domain, Now - 86400 * 30), 306: 307: Path = [data, account, removeOldUsers, users], 308: Resp = execute_auth(remove_old_users_body(Domain, 10), Config), 309: ?assertEqual(1, length(get_ok_value(Path, Resp))), 310: ?assertMatch({user_does_not_exist, _}, check_account(BobName, Domain)), 311: ?assertMatch({ok, _}, check_account(AliceName, Domain)), 312: ?assertMatch({ok, _}, check_account(AliceBisName, BisDomain)). 313: 314: admin_remove_old_users_all(Config) -> 315: [AliceName, Domain, _] = escalus_users:get_usp(Config, alice), 316: [BobName, Domain, _] = escalus_users:get_usp(Config, bob), 317: [AliceBisName, BisDomain, _] = escalus_users:get_usp(Config, alice_bis), 318: 319: Now = erlang:system_time(seconds), 320: OldTime = Now - 86400 * 30, 321: set_last(AliceName, Domain, Now), 322: set_last(BobName, Domain, OldTime), 323: set_last(AliceBisName, BisDomain, OldTime), 324: 325: Path = [data, account, removeOldUsers, users], 326: Resp = execute_auth(remove_old_users_body(null, 10), Config), 327: ?assertEqual(2, length(get_ok_value(Path, Resp))), 328: ?assertMatch({user_does_not_exist, _}, check_account(BobName, Domain)), 329: ?assertMatch({ok, _}, check_account(AliceName, Domain)), 330: ?assertMatch({user_does_not_exist, _}, check_account(AliceBisName, BisDomain)). 331: 332: admin_list_old_users_domain(Config) -> 333: [AliceName, Domain, _] = escalus_users:get_usp(Config, alice), 334: [BobName, Domain, _] = escalus_users:get_usp(Config, bob), 335: 336: Now = erlang:system_time(seconds), 337: set_last(AliceName, Domain, Now), 338: set_last(BobName, Domain, Now - 86400 * 30), 339: 340: LBob = escalus_utils:jid_to_lower(escalus_users:get_jid(Config, bob)), 341: 342: Path = [data, account, listOldUsers], 343: Resp = execute_auth(list_old_users_body(Domain, 10), Config), 344: Users = get_ok_value(Path, Resp), 345: ?assertEqual(1, length(Users)), 346: ?assert(lists:member(LBob, Users)). 347: 348: admin_list_old_users_all(Config) -> 349: [AliceName, Domain, _] = escalus_users:get_usp(Config, alice), 350: [BobName, Domain, _] = escalus_users:get_usp(Config, bob), 351: [AliceBisName, BisDomain, _] = escalus_users:get_usp(Config, alice_bis), 352: 353: Now = erlang:system_time(seconds), 354: OldTime = Now - 86400 * 30, 355: set_last(AliceName, Domain, Now), 356: set_last(BobName, Domain, OldTime), 357: set_last(AliceBisName, BisDomain, OldTime), 358: 359: LBob = escalus_utils:jid_to_lower(escalus_users:get_jid(Config, bob)), 360: LAliceBis = escalus_utils:jid_to_lower(escalus_users:get_jid(Config, alice_bis)), 361: 362: Path = [data, account, listOldUsers], 363: Resp = execute_auth(list_old_users_body(null, 10), Config), 364: Users = get_ok_value(Path, Resp), 365: ?assertEqual(2, length(Users)), 366: ?assert(lists:member(LBob, Users)), 367: ?assert(lists:member(LAliceBis, Users)). 368: 369: %% Helpers 370: 371: get_md5(AccountPass) -> 372: lists:flatten([io_lib:format("~.16B", [X]) 373: || X <- binary_to_list(crypto:hash(md5, AccountPass))]). 374: 375: set_last(User, Domain, TStamp) -> 376: rpc(mim(), mod_last, store_last_info, 377: [domain_helper:host_type(), escalus_utils:jid_to_lower(User), Domain, TStamp, <<>>]). 378: 379: check_account(Username, Domain) -> 380: rpc(mim(), mongoose_account_api, check_account, [Username, Domain]). 381: 382: get_err_msg(Resp) -> 383: get_ok_value([errors, message], Resp). 384: 385: get_account_field(Field, Resp) -> 386: get_ok_value([data, account, Field], Resp). 387: 388: get_ok_value([errors | Path], {{<<"200">>, <<"OK">>}, #{<<"errors">> := [Error]}}) -> 389: get_value(Path, Error); 390: get_ok_value(Path, {{<<"200">>, <<"OK">>}, Data}) -> 391: get_value(Path, Data). 392: 393: get_err_value([errors | Path], {{<<"400">>, <<"Bad Request">>}, #{<<"errors">> := [Error]}}) -> 394: get_value(Path, Error). 395: 396: % Gets a nested value given a path 397: get_value([], Data) -> Data; 398: get_value([Field | Fields], Data) -> 399: BinField = atom_to_binary(Field), 400: Data2 = maps:get(BinField, Data), 401: get_value(Fields, Data2). 402: 403: %% Request bodies 404: 405: list_users_body(Domain) -> 406: Query = <<"query Q1($domain: String!) { account { listUsers(domain: $domain) } }">>, 407: OpName = <<"Q1">>, 408: Vars = #{<<"domain">> => Domain}, 409: #{query => Query, operationName => OpName, variables => Vars}. 410: 411: count_users_body(Domain) -> 412: Query = <<"query Q1($domain: String!) { account { countUsers(domain: $domain) } }">>, 413: OpName = <<"Q1">>, 414: Vars = #{<<"domain">> => Domain}, 415: #{query => Query, operationName => OpName, variables => Vars}. 416: 417: get_active_users_number_body(Domain, Days) -> 418: Query = <<"query Q1($domain: String!, $days: Int!) 419: { account { countActiveUsers(domain: $domain, days: $days) } }">>, 420: OpName = <<"Q1">>, 421: Vars = #{<<"domain">> => Domain, <<"days">> => Days}, 422: #{query => Query, operationName => OpName, variables => Vars}. 423: 424: check_password_body(User, Password) -> 425: Query = <<"query Q1($user: JID!, $password: String!) 426: { account { checkPassword(user: $user, password: $password) {correct message} } }">>, 427: OpName = <<"Q1">>, 428: Vars = #{<<"user">> => User, <<"password">> => Password}, 429: #{query => Query, operationName => OpName, variables => Vars}. 430: 431: check_password_hash_body(User, PasswordHash, HashMethod) -> 432: Query = <<"query Q1($user: JID!, $hash: String!, $method: String!) 433: { account { checkPasswordHash(user: $user, passwordHash: $hash, hashMethod: $method) 434: {correct message} } }">>, 435: OpName = <<"Q1">>, 436: Vars = #{<<"user">> => User, <<"hash">> => PasswordHash, <<"method">> => HashMethod}, 437: #{query => Query, operationName => OpName, variables => Vars}. 438: 439: check_user_body(User) -> 440: Query = <<"query Q1($user: JID!) 441: { account { checkUser(user: $user) {exist message} } }">>, 442: OpName = <<"Q1">>, 443: Vars = #{<<"user">> => User}, 444: #{query => Query, operationName => OpName, variables => Vars}. 445: 446: register_user_body(Domain, Username, Password) -> 447: Query = <<"mutation M1($domain: String!, $username: String, $password: String!) 448: { account { registerUser(domain: $domain, username: $username, password: $password) 449: { jid message } } }">>, 450: OpName = <<"M1">>, 451: Vars = #{<<"domain">> => Domain, <<"username">> => Username, <<"password">> => Password}, 452: #{query => Query, operationName => OpName, variables => Vars}. 453: 454: remove_user_body(User) -> 455: Query = <<"mutation M1($user: JID!) 456: { account { removeUser(user: $user) { jid message } } }">>, 457: OpName = <<"M1">>, 458: Vars = #{<<"user">> => User}, 459: #{query => Query, operationName => OpName, variables => Vars}. 460: 461: remove_old_users_body(Domain, Days) -> 462: Query = <<"mutation M1($domain: String, $days: Int!) 463: { account { removeOldUsers(domain: $domain, days: $days) { message users } } }">>, 464: OpName = <<"M1">>, 465: Vars = #{<<"domain">> => Domain, <<"days">> => Days}, 466: #{query => Query, operationName => OpName, variables => Vars}. 467: 468: list_old_users_body(Domain, Days) -> 469: Query = <<"query Q1($domain: String, $days: Int!) 470: { account { listOldUsers(domain: $domain, days: $days) } }">>, 471: OpName = <<"Q1">>, 472: Vars = #{<<"domain">> => Domain, <<"days">> => Days}, 473: #{query => Query, operationName => OpName, variables => Vars}. 474: 475: ban_user_body(JID, Reason) -> 476: Query = <<"mutation M1($user: JID!, $reason: String!) 477: { account { banUser(user: $user, reason: $reason) { jid message } } }">>, 478: OpName = <<"M1">>, 479: Vars = #{<<"user">> => JID, <<"reason">> => Reason}, 480: #{query => Query, operationName => OpName, variables => Vars}. 481: 482: change_user_password_body(JID, NewPassword) -> 483: Query = <<"mutation M1($user: JID!, $newPassword: String!) 484: { account { changeUserPassword(user: $user, newPassword: $newPassword) { jid message } } }">>, 485: OpName = <<"M1">>, 486: Vars = #{<<"user">> => JID, <<"newPassword">> => NewPassword}, 487: #{query => Query, operationName => OpName, variables => Vars}. 488: 489: user_change_password_body(NewPassword) -> 490: Query = <<"mutation M1($newPassword: String!) 491: { account { changePassword(newPassword: $newPassword) } }">>, 492: OpName = <<"M1">>, 493: Vars = #{<<"newPassword">> => NewPassword}, 494: #{query => Query, operationName => OpName, variables => Vars}.