1: -module(graphql_SUITE).
    2: 
    3: -include_lib("common_test/include/ct.hrl").
    4: -include_lib("eunit/include/eunit.hrl").
    5: -include_lib("exml/include/exml.hrl").
    6: 
    7: -compile([export_all, nowarn_export_all]).
    8: 
    9: -import(distributed_helper, [mim/0, require_rpc_nodes/1, rpc/4]).
   10: -import(graphql_helper, [execute/3, execute_auth/2, execute_user/3,
   11:                          get_value/2, get_bad_request/1]).
   12: 
   13: -define(assertAdminAuth(Domain, Type, Auth, Data),
   14:         assert_auth(#{<<"domain">> => Domain,
   15:                       <<"authStatus">> => atom_to_binary(Auth),
   16:                       <<"authType">> => maybe_atom_to_bin(Type)}, Data)).
   17: -define(assertUserAuth(Username, Auth, Data),
   18:         assert_auth(#{<<"username">> => Username,
   19:                       <<"authStatus">> => atom_to_binary(Auth)}, Data)).
   20: 
   21: suite() ->
   22:     require_rpc_nodes([mim]) ++ escalus:suite().
   23: 
   24: all() ->
   25:     [{group, cowboy_handler},
   26:      {group, admin_handler},
   27:      {group, domain_admin_handler},
   28:      {group, user_handler},
   29:      {group, categories_disabled}].
   30: 
   31: groups() ->
   32:     [{cowboy_handler, [parallel], cowboy_handler()},
   33:      {user_handler, [parallel], user_handler()},
   34:      {domain_admin_handler, [parallel], domain_admin_handler()},
   35:      {admin_handler, [parallel], admin_handler()},
   36:      {categories_disabled, [parallel], categories_disabled_tests()}].
   37: 
   38: cowboy_handler() ->
   39:     [can_connect_to_admin,
   40:      can_connect_to_domain_admin,
   41:      can_connect_to_user].
   42: 
   43: user_handler() ->
   44:     [user_checks_auth,
   45:      auth_user_checks_auth | common_tests()].
   46: admin_handler() ->
   47:     [admin_checks_auth,
   48:      auth_admin_checks_auth | common_tests()].
   49: domain_admin_handler() ->
   50:     [domain_admin_checks_auth,
   51:      auth_domain_admin_checks_auth | common_tests()].
   52: 
   53: common_tests() ->
   54:     [can_load_graphiql].
   55: 
   56: categories_disabled_tests() ->
   57:     [category_disabled_error_test,
   58:      admin_checks_auth,
   59:      category_does_not_exist_error,
   60:      listener_reply_with_validation_error,
   61:      multiple_categories_query_test].
   62: 
   63: init_per_suite(Config) ->
   64:     Config1 = escalus:init_per_suite(Config),
   65:     dynamic_modules:save_modules(domain_helper:host_type(), Config1).
   66: 
   67: end_per_suite(Config) ->
   68:     dynamic_modules:restore_modules(Config),
   69:     escalus_fresh:clean(),
   70:     escalus:end_per_suite(Config).
   71: 
   72: init_per_group(admin_handler, Config) ->
   73:     graphql_helper:init_admin_handler(Config);
   74: init_per_group(domain_admin_handler, Config) ->
   75:     case mongoose_helper:is_rdbms_enabled(domain_helper:host_type()) of
   76:         true ->
   77:             graphql_helper:init_domain_admin_handler(Config);
   78:         false ->
   79:             {skip, require_rdbms}
   80:     end;
   81: init_per_group(user_handler, Config) ->
   82:     Config1 = escalus:create_users(Config, escalus:get_users([alice])),
   83:     [{schema_endpoint, user} | Config1];
   84: init_per_group(categories_disabled, Config) ->
   85:     #{node := Node} = mim(),
   86:     CowboyGraphqlListenerConfig = graphql_helper:get_listener_config(Node, admin),
   87:     #{handlers := [SchemaConfig]} = CowboyGraphqlListenerConfig,
   88:     UpdatedSchemaConfig = maps:put(allowed_categories, [<<"vcard">>, <<"checkAuth">>], SchemaConfig),
   89:     UpdatedListenerConfig = maps:put(handlers, [UpdatedSchemaConfig], CowboyGraphqlListenerConfig),
   90:     mongoose_helper:restart_listener(mim(), UpdatedListenerConfig),
   91:     Config1 = [{admin_listener_config, CowboyGraphqlListenerConfig} | Config],
   92:     graphql_helper:init_admin_handler(Config1);
   93: init_per_group(cowboy_handler, Config) ->
   94:     Config.
   95: 
   96: end_per_group(user_handler, Config) ->
   97:     escalus:delete_users(Config, escalus:get_users([alice]));
   98: end_per_group(domain_admin_handler, Config) ->
   99:     graphql_helper:end_domain_admin_handler(Config);
  100: end_per_group(categories_disabled, Config) ->
  101:     ListenerConfig = ?config(admin_listener_config, Config),
  102:     mongoose_helper:restart_listener(mim(), ListenerConfig),
  103:     Config;
  104: end_per_group(_, _Config) ->
  105:     ok.
  106: 
  107: init_per_testcase(CaseName, Config) ->
  108:     escalus:init_per_testcase(CaseName, Config).
  109: 
  110: end_per_testcase(CaseName, Config) ->
  111:     escalus:end_per_testcase(CaseName, Config).
  112: 
  113: can_connect_to_admin(_Config) ->
  114:     ?assertMatch({{<<"400">>, <<"Bad Request">>}, _}, execute(admin, #{}, undefined)).
  115: 
  116: can_connect_to_domain_admin(_Config) ->
  117:     ?assertMatch({{<<"400">>, <<"Bad Request">>}, _}, execute(domain_admin, #{}, undefined)).
  118: 
  119: can_connect_to_user(_Config) ->
  120:     ?assertMatch({{<<"400">>, <<"Bad Request">>}, _}, execute(user, #{}, undefined)).
  121: 
  122: can_load_graphiql(Config) ->
  123:     Ep = ?config(schema_endpoint, Config),
  124:     {Status, Html} = get_graphiql_website(Ep),
  125:     ?assertEqual({<<"200">>, <<"OK">>}, Status),
  126:     ?assertNotEqual(nomatch, binary:match(Html, <<"Loading...">>)).
  127: 
  128: user_checks_auth(Config) ->
  129:     Ep = ?config(schema_endpoint, Config),
  130:     StatusData = execute(Ep, user_check_auth_body(), undefined),
  131:     ?assertUserAuth(null, 'UNAUTHORIZED', StatusData).
  132: 
  133: auth_user_checks_auth(Config) ->
  134:     escalus:fresh_story(
  135:         Config, [{alice, 1}],
  136:         fun(Alice) ->
  137:             AliceJID = escalus_utils:jid_to_lower(escalus_client:short_jid(Alice)),
  138:             StatusData = execute_user(user_check_auth_body(), Alice, Config),
  139:             ?assertUserAuth(AliceJID, 'AUTHORIZED', StatusData)
  140:         end).
  141: 
  142: admin_checks_auth(Config) ->
  143:     Ep = ?config(schema_endpoint, Config),
  144:     StatusData = execute(Ep, admin_check_auth_body(), undefined),
  145:     ?assertAdminAuth(null, null, 'UNAUTHORIZED', StatusData).
  146: 
  147: auth_admin_checks_auth(Config) ->
  148:     StatusData = execute_auth(admin_check_auth_body(), Config),
  149:     ?assertAdminAuth(null, 'ADMIN', 'AUTHORIZED', StatusData).
  150: 
  151: domain_admin_checks_auth(Config) ->
  152:     Ep = ?config(schema_endpoint, Config),
  153:     Res = execute(Ep, admin_check_auth_body(), undefined),
  154:     ?assertAdminAuth(null, null, 'UNAUTHORIZED', Res).
  155: 
  156: auth_domain_admin_checks_auth(Config) ->
  157:     {Username, _} = ?config(domain_admin, Config),
  158:     Domain = escalus_utils:get_server(Username),
  159:     Res = execute_auth(admin_check_auth_body(), Config),
  160:     ?assertAdminAuth(Domain, 'DOMAIN_ADMIN', 'AUTHORIZED', Res).
  161: 
  162: category_disabled_error_test(Config) ->
  163:     Status = execute_auth(admin_server_get_loglevel_body(), Config),
  164:     {_Code, #{<<"errors">> := [Msg]}} = Status,
  165:     ?assertEqual(<<"category_disabled">>, get_value([extensions, code], Msg)),
  166:     ?assertEqual([<<"server">>], get_value([path], Msg)).
  167: 
  168: category_does_not_exist_error(Config) ->
  169:     Ep = ?config(schema_endpoint, Config),
  170:     Status = execute(Ep, #{<<"query">> => <<"{ field ">>}, undefined),
  171:     get_bad_request(Status),
  172:     {_Code, #{<<"errors">> := [Msg]}} = Status,
  173:     ?assertEqual(<<"parser_error">>, get_value([extensions, code], Msg)).
  174: 
  175: listener_reply_with_validation_error(Config) ->
  176:     Ep = ?config(schema_endpoint, Config),
  177:     Body = #{<<"query">> => <<"query Q1 { field } query Q1 { field }">>,
  178:              <<"operationName">> => <<"Q1">>},
  179:     {Status, Data} = execute(Ep, Body, undefined).
  180: 
  181: multiple_categories_query_test(Config) ->
  182:     Status = execute_auth(user_check_auth_multiple(), Config),
  183:     {_Code, #{<<"errors">> := [ErrorMsg], <<"data">> := DataMsg}} = Status,
  184:     ?assertEqual(<<"category_disabled">>, get_value([extensions, code], ErrorMsg)),
  185:     ?assertEqual([<<"server">>], get_value([path], ErrorMsg)),
  186:     ?assertEqual(<<"AUTHORIZED">>, get_value([checkAuth, authStatus], DataMsg)).
  187: 
  188: %% Helpers
  189: 
  190: assert_auth(Auth, {Status, Data}) ->
  191:     ?assertEqual({<<"200">>, <<"OK">>}, Status),
  192:     ?assertMatch(#{<<"data">> := #{<<"checkAuth">> := Auth}}, Data).
  193: 
  194: get_graphiql_website(EpName) ->
  195:     Request =
  196:       #{port => graphql_helper:get_listener_port(EpName),
  197:         role => {graphql, atom_to_binary(EpName)},
  198:         method => <<"GET">>,
  199:         headers => [{<<"Accept">>, <<"text/html">>}],
  200:         return_maps => true,
  201:         path => "/graphql"},
  202:     rest_helper:make_request(Request).
  203: 
  204: maybe_atom_to_bin(null) -> null;
  205: maybe_atom_to_bin(X) -> atom_to_binary(X).
  206: 
  207: admin_check_auth_body() ->
  208:     #{query => "{ checkAuth { domain authType authStatus } }"}.
  209: 
  210: admin_server_get_loglevel_body() ->
  211:     #{query => "{ server { getLoglevel } }"}.
  212: 
  213: user_check_auth_body() ->
  214:     #{query => "{ checkAuth { username authStatus } }"}.
  215: 
  216: user_check_auth_multiple() ->
  217:     #{query => "{ checkAuth { authStatus } server { getLoglevel } }"}.