Skip to content

Commit

Permalink
Wip
Browse files Browse the repository at this point in the history
  • Loading branch information
Premwoik committed Apr 26, 2022
1 parent e85c138 commit 995f125
Show file tree
Hide file tree
Showing 29 changed files with 505 additions and 60 deletions.
8 changes: 8 additions & 0 deletions big_tests/tests/domain_helper.erl
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
delete_configured_domains/0,
insert_domain/3,
delete_domain/2,
set_domain_password/3,
delete_domain_password/2,
make_metrics_prefix/1,
host_types/0,
host_types/1,
Expand Down Expand Up @@ -66,6 +68,12 @@ insert_persistent_domain(Node, Domain, HostType) ->
delete_persistent_domain(Node, Domain, HostType) ->
ok = rpc(Node, mongoose_domain_api, delete_domain, [Domain, HostType]).

set_domain_password(Node, Domain, Password) ->
ok = rpc(Node, mongoose_domain_api, set_domain_password, [Domain, Password]).

delete_domain_password(Node, Domain) ->
ok = rpc(Node, mongoose_domain_api, delete_domain_password, [Domain]).

for_each_configured_domain(F) ->
[for_each_configured_domain(F, Opts) || {_, Opts} <- ct:get_config(hosts)],
ok.
Expand Down
69 changes: 45 additions & 24 deletions big_tests/tests/graphql_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -7,28 +7,33 @@
-compile([export_all, nowarn_export_all]).

-import(distributed_helper, [mim/0, require_rpc_nodes/1, rpc/4]).
-import(graphql_helper, [execute/3]).
-import(graphql_helper, [execute/3, execute_auth/2, execute_domain_auth/2, execute_user/3]).

-define(assertAdminAuth(Auth, Data), assert_auth(atom_to_binary(Auth), Data)).
-define(assertAdminAuth(Domain, Type, Auth, Data),
assert_auth(#{<<"domain">> => Domain,
<<"authStatus">> => atom_to_binary(Auth),
<<"authType">> => maybe_atom_to_bin(Type)}, Data)).
-define(assertUserAuth(Username, Auth, Data),
assert_auth(#{<<"username">> => Username,
<<"authStatus">> => atom_to_binary(Auth)}, Data)).

suite() ->
require_rpc_nodes([mim]) ++ escalus:suite().

all() ->
[{group, cowboy_handler},
{group, admin_handler},
{group, domain_admin_handler},
{group, user_handler}].

groups() ->
[{cowboy_handler, [parallel], cowboy_handler()},
{user_handler, [parallel], user_handler()},
{domain_admin_handler, [parallel], domain_admin_handler()},
{admin_handler, [parallel], admin_handler()}].

cowboy_handler() ->
[can_connect_to_admin,
can_connect_to_domain_admin,
can_connect_to_user].

user_handler() ->
Expand All @@ -37,6 +42,9 @@ user_handler() ->
admin_handler() ->
[admin_checks_auth,
auth_admin_checks_auth | common_tests()].
domain_admin_handler() ->
[domain_admin_checks_auth,
auth_domain_admin_checks_auth | common_tests()].

common_tests() ->
[can_load_graphiql].
Expand All @@ -52,14 +60,18 @@ end_per_suite(Config) ->

init_per_group(admin_handler, Config) ->
graphql_helper:init_admin_handler(Config);
init_per_group(domain_admin_handler, Config) ->
graphql_helper:init_domain_admin_handler(Config);
init_per_group(user_handler, Config) ->
Config1 = escalus:create_users(Config, escalus:get_users([alice])),
[{schema_endpoint, user} | Config1];
init_per_group(_, Config) ->
init_per_group(cowboy_handler, Config) ->
Config.

end_per_group(user_handler, Config) ->
escalus:delete_users(Config, escalus:get_users([alice]));
end_per_group(domain_admin_handler, Config) ->
graphql_helper:end_domain_admin_handler(Config);
end_per_group(_, _Config) ->
ok.

Expand All @@ -72,6 +84,9 @@ end_per_testcase(CaseName, Config) ->
can_connect_to_admin(_Config) ->
?assertMatch({{<<"400">>, <<"Bad Request">>}, _}, execute(admin, #{}, undefined)).

can_connect_to_domain_admin(_Config) ->
?assertMatch({{<<"400">>, <<"Bad Request">>}, _}, execute(domain_admin, #{}, undefined)).

can_connect_to_user(_Config) ->
?assertMatch({{<<"400">>, <<"Bad Request">>}, _}, execute(user, #{}, undefined)).

Expand All @@ -83,47 +98,44 @@ can_load_graphiql(Config) ->

user_checks_auth(Config) ->
Ep = ?config(schema_endpoint, Config),
Body = #{query => "{ checkAuth { username authStatus } }"},
StatusData = execute(Ep, Body, undefined),
StatusData = execute(Ep, user_check_auth_body(), undefined),
?assertUserAuth(null, 'UNAUTHORIZED', StatusData).

auth_user_checks_auth(Config) ->
escalus:fresh_story(
Config, [{alice, 1}],
fun(Alice) ->
Password = user_password(alice),
AliceJID = escalus_client:short_jid(Alice),
Ep = ?config(schema_endpoint, Config),
Body = #{query => "{ checkAuth { username authStatus } }"},
StatusData = execute(Ep, Body, {AliceJID, Password}),
AliceJID = escalus_utils:jid_to_lower(escalus_client:short_jid(Alice)),
StatusData = execute_user(user_check_auth_body(), Alice, Config),
?assertUserAuth(AliceJID, 'AUTHORIZED', StatusData)
end).

admin_checks_auth(Config) ->
Ep = ?config(schema_endpoint, Config),
Body = #{query => "{ checkAuth }"},
StatusData = execute(Ep, Body, undefined),
?assertAdminAuth('UNAUTHORIZED', StatusData).
StatusData = execute(Ep, admin_check_auth_body(), undefined),
?assertAdminAuth(null, null, 'UNAUTHORIZED', StatusData).

auth_admin_checks_auth(Config) ->
StatusData = execute_auth(admin_check_auth_body(), Config),
?assertAdminAuth(null, 'ADMIN', 'AUTHORIZED', StatusData).

domain_admin_checks_auth(Config) ->
Ep = ?config(schema_endpoint, Config),
Opts = ?config(listener_opts, Config),
User = proplists:get_value(username, Opts),
Password = proplists:get_value(password, Opts),
Body = #{query => "{ checkAuth }"},
StatusData = execute(Ep, Body, {User, Password}),
?assertAdminAuth('AUTHORIZED', StatusData).
Res = execute(Ep, admin_check_auth_body(), undefined),
?assertAdminAuth(null, null, 'UNAUTHORIZED', Res).

auth_domain_admin_checks_auth(Config) ->
{Username, _} = ?config(domain_admin, Config),
Domain = escalus_utils:get_server(Username),
Res = execute_domain_auth(admin_check_auth_body(), Config),
?assertAdminAuth(Domain, 'DOMAIN_ADMIN', 'AUTHORIZED', Res).

%% Helpers

assert_auth(Auth, {Status, Data}) ->
?assertEqual({<<"200">>, <<"OK">>}, Status),
?assertMatch(#{<<"data">> := #{<<"checkAuth">> := Auth}}, Data).

user_password(User) ->
[{User, Props}] = escalus:get_users([User]),
proplists:get_value(password, Props).

get_graphiql_website(EpName) ->
Request =
#{port => graphql_helper:get_listener_port(EpName),
Expand All @@ -133,3 +145,12 @@ get_graphiql_website(EpName) ->
return_maps => true,
path => "/graphql"},
rest_helper:make_request(Request).

maybe_atom_to_bin(null) -> null;
maybe_atom_to_bin(X) -> atom_to_binary(X).

admin_check_auth_body() ->
#{query => "{ checkAuth { domain authType authStatus } }"}.

user_check_auth_body() ->
#{query => "{ checkAuth { username authStatus } }"}.
22 changes: 20 additions & 2 deletions big_tests/tests/graphql_helper.erl
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@

-import(distributed_helper, [mim/0, rpc/4]).

-export([execute/3, execute_auth/2, execute_user/3, get_listener_port/1, get_listener_config/1]).
-export([init_admin_handler/1]).
-export([execute/3, execute_auth/2, execute_domain_auth/2, execute_user/3]).
-export([init_admin_handler/1, init_domain_admin_handler/1, end_domain_admin_handler/1]).
-export([get_listener_port/1, get_listener_config/1]).
-export([get_ok_value/2, get_err_msg/1, get_err_msg/2, make_creds/1,
user_to_bin/1, user_to_jid/1, user_to_full_bin/1]).

Expand All @@ -30,6 +31,11 @@ execute_auth(Body, Config) ->
Password = proplists:get_value(password, Opts),
execute(Ep, Body, {User, Password}).

execute_domain_auth(Body, Config) ->
Ep = ?config(schema_endpoint, Config),
Creds = ?config(domain_admin, Config),
execute(Ep, Body, Creds).

execute_user(Body, User, Config) ->
Ep = ?config(schema_endpoint, Config),
Creds = make_creds(User),
Expand Down Expand Up @@ -57,6 +63,18 @@ init_admin_handler(Config) ->
ct:fail(<<"Admin credentials are not defined in config">>)
end.

init_domain_admin_handler(Config) ->
Domain = domain_helper:domain(),
Password = base16:encode(crypto:strong_rand_bytes(8)),
Creds = {<<"admin@", Domain/binary>>, Password},
ok = domain_helper:set_domain_password(mim(), Domain, Password),
[{domain_admin, Creds}, {schema_endpoint, domain_admin} | Config].

end_domain_admin_handler(Config) ->
{JID, _} = ?config(domain_admin, Config),
Domain = escalus_utils:get_server(JID),
domain_helper:delete_domain_password(mim(), Domain).

get_listener_opts(EpName) ->
#{handlers := Handlers} = get_listener_config(EpName),
[Opts2] = lists:filtermap(
Expand Down
2 changes: 1 addition & 1 deletion big_tests/tests/mongooseimctl_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -1120,7 +1120,7 @@ stats_host(Config) ->
%%--------------------------------------------------------------------

can_execute_admin_queries_with_permissions(Config) ->
Query = "query { checkAuth }",
Query = "query { checkAuth { authStatus } }",
Res = mongooseimctl("graphql", [Query], Config),
?assertMatch({_, 0}, Res),
Data = element(1, Res),
Expand Down
2 changes: 1 addition & 1 deletion priv/graphql/schemas/admin/account.gql
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ Allow admin to get information about accounts.
"""
type AccountAdminQuery @protected{
"List users per domain"
listUsers(domain: String!): [String!]!
listUsers(domain: String!): [String!]! @protected(type: DOMAIN, args: ["domain"])
"List users that didn't log in the last days or have never logged in. Globally or for a specified domain"
listOldUsers(domain: String, days: Int!): [String!]!
"Get number of users per domain"
Expand Down
18 changes: 18 additions & 0 deletions priv/graphql/schemas/admin/admin_auth_status.gql
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
"Inforamtion about user request authorization"
type AdminAuthInfo{
"Authorized for a domain"
domain: String
"Authorization status"
authStatus: AuthStatus!
"Authorization as a "
authType: AuthType
}

enum AuthType{
""
DOMAIN_ADMIN
""
ADMIN
""
UNAUTHORIZED
}
2 changes: 1 addition & 1 deletion priv/graphql/schemas/admin/admin_schema.gql
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Only an authenticated admin can execute these queries.
"""
type AdminQuery{
"Check authorization status"
checkAuth: AuthStatus
checkAuth: AdminAuthInfo
"Domain management"
domains: DomainAdminQuery
"Account management"
Expand Down
4 changes: 4 additions & 0 deletions priv/graphql/schemas/admin/domain.gql
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ type DomainAdminMutation @protected{
enableDomain(domain: String!): Domain
"Disable domain"
disableDomain(domain: String!): Domain
""
setDomainAdmin(domain: String!, password: String!): String
""
deleteDomainAdmin(domain: String!): String
}

"A result of domain removal"
Expand Down
7 changes: 6 additions & 1 deletion priv/graphql/schemas/global/protected_dir.gql
Original file line number Diff line number Diff line change
@@ -1,2 +1,7 @@
"Marks the resource to be accessed only by authorized requests"
directive @protected on FIELD_DEFINITION | OBJECT | INTERFACE
directive @protected (type: String = ALL, args: [String!] = []) on FIELD_DEFINITION | OBJECT | INTERFACE

enum ProtectedType{
DOMAIN
ALL
}
6 changes: 6 additions & 0 deletions priv/pg.sql
Original file line number Diff line number Diff line change
Expand Up @@ -478,6 +478,12 @@ CREATE TABLE offline_markers (

CREATE INDEX i_offline_markers ON offline_markers(jid);

CREATE TABLE domain_admin(
domain VARCHAR(250) NOT NULL,
password VARCHAR(250) NOT NULL,
PRIMARY KEY(domain)
);

-- Mapping from domain hostname to host_type.
-- Column id is used for ordering only.
CREATE TABLE domain_settings (
Expand Down
2 changes: 1 addition & 1 deletion rebar.config
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@
{worker_pool, "6.0.0"},

%%% HTTP tools
{graphql, {git, "https://github.com/Premwoik/graphql-erlang.git", {branch, "mim"}}},
{graphql, {git, "https://github.com/Premwoik/graphql-erlang.git", {branch, "fix-graphql-internal-types"}}},
{cowboy, "2.9.0"},
{gun, "1.3.3"},
{fusco, "0.1.1"},
Expand Down
7 changes: 5 additions & 2 deletions rel/fed1.vars-toml.config
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
{http_api_endpoint_port, 5294}.
{http_api_old_endpoint_port, 5293}.
{http_api_client_endpoint_port, 8095}.
{http_qraphql_api_admin_endpoint_port, 5556}.
{http_graphql_api_admin_endpoint_port, 5556}.
{http_graphql_api_domain_admin_endpoint_port, 5546}.
{http_graphql_api_user_endpoint_port, 5566}.

%% This node is for s2s testing.
Expand Down Expand Up @@ -50,7 +51,9 @@
port = {{ http_api_endpoint_port }}"}.
{http_api_client_endpoint, "port = {{ http_api_client_endpoint_port }}"}.
{http_graphql_api_admin_endpoint, "ip_address = \"127.0.0.1\"
port = {{http_qraphql_api_admin_endpoint_port}}"}.
port = {{http_graphql_api_admin_endpoint_port}}"}.
{http_graphql_api_domain_admin_endpoint, "ip_address = \"0.0.0.0\"
port = {{http_graphql_api_domain_admin_endpoint_port}}"}.
{http_graphql_api_user_endpoint, "ip_address = \"0.0.0.0\"
port = {{http_graphql_api_user_endpoint_port}}"}.

Expand Down
12 changes: 12 additions & 0 deletions rel/files/mongooseim.toml
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,18 @@
username = "admin"
password = "secret"

[[listen.http]]
{{#http_graphql_api_domain_admin_endpoint}}
{{{http_graphql_api_domain_admin_endpoint}}}
{{/http_graphql_api_domain_admin_endpoint}}
transport.num_acceptors = 10
transport.max_connections = 1024

[[listen.http.handlers.mongoose_graphql_cowboy_handler]]
host = "_"
path = "/api/graphql"
schema_endpoint = "domain_admin"

[[listen.http]]
{{#http_graphql_api_user_endpoint}}
{{{http_graphql_api_user_endpoint}}}
Expand Down
7 changes: 5 additions & 2 deletions rel/mim2.vars-toml.config
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
{http_api_endpoint_port, 8090}.
{http_api_client_endpoint_port, 8091}.
{service_port, 8899}.
{http_qraphql_api_admin_endpoint_port, 5552}.
{http_graphql_api_admin_endpoint_port, 5552}.
{http_graphql_api_domain_admin_endpoint_port, 5542}.
{http_graphql_api_user_endpoint_port, 5562}.

{hosts, "\"localhost\", \"anonymous.localhost\", \"localhost.bis\""}.
Expand All @@ -22,7 +23,9 @@
{highload_vm_args, ""}.

{http_graphql_api_admin_endpoint, "ip_address = \"127.0.0.1\"
port = {{http_qraphql_api_admin_endpoint_port}}"}.
port = {{http_graphql_api_admin_endpoint_port}}"}.
{http_graphql_api_domain_admin_endpoint, "ip_address = \"0.0.0.0\"
port = {{http_graphql_api_domain_admin_endpoint_port}}"}.
{http_graphql_api_user_endpoint, "ip_address = \"0.0.0.0\"
port = {{http_graphql_api_user_endpoint_port}}"}.
{http_api_old_endpoint, "ip_address = \"127.0.0.1\"
Expand Down
7 changes: 5 additions & 2 deletions rel/mim3.vars-toml.config
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
{http_api_old_endpoint_port, 5292}.
{http_api_endpoint_port, 8092}.
{http_api_client_endpoint_port, 8193}.
{http_qraphql_api_admin_endpoint_port, 5553}.
{http_graphql_api_admin_endpoint_port, 5553}.
{http_graphql_api_domain_admin_endpoint_port, 5543}.
{http_graphql_api_user_endpoint_port, 5563}.

{hosts, "\"localhost\", \"anonymous.localhost\", \"localhost.bis\""}.
Expand Down Expand Up @@ -39,7 +40,9 @@
tls.ciphers = \"ECDHE-RSA-AES256-GCM-SHA384\""}.

{http_graphql_api_admin_endpoint, "ip_address = \"127.0.0.1\"
port = {{http_qraphql_api_admin_endpoint_port}}"}.
port = {{http_graphql_api_admin_endpoint_port}}"}.
{http_graphql_api_domain_admin_endpoint, "ip_address = \"0.0.0.0\"
port = {{http_graphql_api_domain_admin_endpoint_port}}"}.
{http_graphql_api_user_endpoint, "ip_address = \"0.0.0.0\"
port = {{http_graphql_api_user_endpoint_port}}"}.
{http_api_old_endpoint, "ip_address = \"127.0.0.1\"
Expand Down
Loading

0 comments on commit 995f125

Please sign in to comment.