1: -module(acl_SUITE).
    2: -compile([export_all, nowarn_export_all]).
    3: 
    4: -include_lib("common_test/include/ct.hrl").
    5: -include_lib("proper/include/proper.hrl").
    6: -include_lib("eunit/include/eunit.hrl").
    7: 
    8: 
    9: all() ->
   10:     [{group, dynamic_domains},
   11:      {group, static_domains}].
   12: 
   13: groups() ->
   14:     [{dynamic_domains, [], basic_test_cases() ++ host_type_test_cases()},
   15:      {static_domains, [], basic_test_cases()}].
   16: 
   17: basic_test_cases() ->
   18:     [
   19:      all_rule_returns_allow,
   20:      none_rule_returns_deny,
   21:      basic_access_rules,
   22:      compound_access_rules,
   23:      host_specific_access_rules,
   24:      global_host_priority,
   25:      all_and_none_specs,
   26:      invalid_spec,
   27:      different_specs_matching_the_same_user
   28:     ].
   29: 
   30: host_type_test_cases() ->
   31:     [
   32:      match_host_specific_rule_for_host_type,
   33:      match_global_rule_for_host_type
   34:     ].
   35: 
   36: init_per_suite(Config) ->
   37:     {ok, _} = application:ensure_all_started(jid),
   38:     Config.
   39: 
   40: end_per_suite(_Config) ->
   41:     mongoose_domain_api:stop(),
   42:     meck:unload(),
   43:     ok.
   44: 
   45: init_per_group(dynamic_domains, Config) ->
   46:     [{dynamic_domains, true}|Config];
   47: init_per_group(_Group, Config) ->
   48:     Config.
   49: 
   50: end_per_group(_Group, Config) ->
   51:     Config.
   52: 
   53: init_per_testcase(_TC, Config) ->
   54:     given_clean_config(),
   55:     Config.
   56: 
   57: end_per_testcase(_TC, _Config) ->
   58:     ok.
   59: 
   60: all_rule_returns_allow(_Config) ->
   61:     JID = jid:make(<<"pawel">>, <<"phost">>, <<"test">>),
   62:     ?assertEqual(allow, acl:match_rule(global, all, JID)),
   63:     ?assertEqual(allow, acl:match_rule(<<"phost">>, all, JID)),
   64:     ?assertEqual(allow, acl:match_rule(<<"localhost">>, all, JID)),
   65:     ok.
   66: 
   67: none_rule_returns_deny(_Config) ->
   68:     JID = jid:make(<<"gawel">>, <<"phost">>, <<"test">>),
   69:     ?assertEqual(deny, acl:match_rule(global, none, JID)),
   70:     ?assertEqual(deny, acl:match_rule(<<"phosty">>, none, JID)),
   71:     ?assertEqual(deny, acl:match_rule(<<"localhosty">>, none, JID)),
   72:     ok.
   73: 
   74: basic_access_rules(_Config) ->
   75:     JID = jid:make(<<"pawel">>, <<"phost">>, <<"test">>),
   76: 
   77:     %% rule is not defined deny by default - deny
   78:     ?assertEqual(deny, (acl:match_rule(global, single_rule, JID))),
   79: 
   80:     %% add the rule and recheck
   81:     set_global_rule(single_rule, [{allow, all}]),
   82:     ?assertEqual(allow, (acl:match_rule(global, single_rule, JID))),
   83: 
   84:     %% override it to deny rule
   85:     set_global_rule(single_rule, [{deny, all}]),
   86:     ?assertEqual(deny, (acl:match_rule(global, single_rule, JID))),
   87: 
   88:     %% deny by default
   89:     set_global_rule(single_rule, []),
   90:     ?assertEqual(deny, (acl:match_rule(global, single_rule, JID))),
   91: 
   92:     %% allow nobody
   93:     set_global_rule(single_rule, [{allow, none}]),
   94:     ?assertEqual(deny, (acl:match_rule(global, single_rule, JID))),
   95:     ok.
   96: 
   97: host_specific_access_rules(Config) ->
   98:     given_registered_domains(Config, [<<"poznan">>, <<"wroclaw">>]),
   99: 
  100:     PozAdmin = jid:make(<<"gawel">>, <<"poznan">>, <<"test">>),
  101:     Pawel = jid:make(<<"pawel">>, <<"wroclaw">>, <<"test">>),
  102: 
  103:     set_acl(global, admin_poz, {user, <<"gawel">>, <<"poznan">>}),
  104: 
  105:     set_host_rule(only_poz_admin, <<"poznan">>, [{allow, admin_poz}, {deny, all}]),
  106:     set_host_rule(only_poz_admin, <<"wroclaw">>, [{deny, admin_poz}, {allow, all}]),
  107: 
  108:     ?assertEqual(allow, acl:match_rule(<<"poznan">>, only_poz_admin, PozAdmin)),
  109:     ?assertEqual(deny, acl:match_rule(<<"poznan">>, only_poz_admin, Pawel)),
  110: 
  111:     ?assertEqual(deny, acl:match_rule(<<"wroclaw">>, only_poz_admin, PozAdmin)),
  112:     ?assertEqual(allow, acl:match_rule(<<"wroclaw">>, only_poz_admin, Pawel)),
  113:     ok.
  114: 
  115: compound_access_rules(Config) ->
  116:     given_registered_domains(Config, [<<"krakow">>]),
  117: 
  118:     KrkAdmin = jid:make(<<"gawel">>, <<"krakow">>, <<"test">>),
  119:     KrkNormal = jid:make(<<"pawel">>, <<"krakow">>, <<"test">>),
  120: 
  121:     %% add admin user rule
  122:     set_acl(global, admin_wawa, {user, <<"gawel">>, <<"wawa">>}),
  123:     set_acl(global, admin_krakow, {user, <<"gawel">>, <<"krakow">>}),
  124: 
  125:     set_global_rule(only_krakow_admin, [{allow, admin_krakow}, {deny, all}]),
  126:     set_global_rule(only_wawa_admin, [{allow, admin_wawa}, {deny, all}]),
  127: 
  128:     ?assertEqual(deny, acl:match_rule(global, only_krakow_admin, KrkNormal)),
  129:     ?assertEqual(deny, acl:match_rule(global, only_wawa_admin, KrkNormal)),
  130: 
  131:     ?assertEqual(allow, acl:match_rule(global, only_krakow_admin, KrkAdmin)),
  132:     ?assertEqual(deny,  acl:match_rule(global, only_wawa_admin, KrkAdmin)),
  133:     ok.
  134: 
  135: global_host_priority(Config) ->
  136:     given_registered_domains(Config, [<<"rzeszow">>, <<"lublin">>]),
  137: 
  138:     RzeAdmin = jid:make(<<"pawel">>, <<"rzeszow">>, <<"test">>),
  139: 
  140:     %% add admin user rule
  141:     set_acl(<<"rzeszow">>, admin, {user, <<"pawel">>, <<"rzeszow">>}),
  142:     set_acl(global, admin, {user, <<"pawel">>, <<"lublin">>}),
  143: 
  144:     %% allow only admin
  145:     set_global_rule(only_admin, [{allow, admin}, {deny, all}]),
  146:     %% deny all
  147:     set_host_rule(only_admin, <<"rzeszow">>, [{deny, admin}, {deny, all}]),
  148: 
  149:     set_global_rule(ban_admin, [{allow, all}]),
  150:     set_host_rule(ban_admin, <<"rzeszow">>, [{deny, admin}, {allow, all}]),
  151: 
  152:     %% host rule is more important than the host one
  153:     ?assertEqual(allow, acl:match_rule(<<"rzeszow">>, only_admin, RzeAdmin)),
  154: 
  155:     %% host rule applies when global doesn't match and ends up with {allow, all}
  156:     %% ...
  157:     ?assertEqual(deny, acl:match_rule(<<"rzeszow">>, ban_admin, RzeAdmin)),
  158:     ok.
  159: 
  160: all_and_none_specs(Config) ->
  161:     given_registered_domains(Config, [<<"zakopane">>]),
  162: 
  163:     User = jid:make(<<"pawel">>, <<"zakopane">>, <<"test">>),
  164:     set_acl(global, a_users, all),
  165:     set_acl(global, n_users, none),
  166: 
  167:     set_global_rule(all_users, [{allow, a_users}, {deny, all}]),
  168:     set_global_rule(none_users, [{allow, n_users}, {deny, all}]),
  169: 
  170:     ?assertEqual(allow, acl:match_rule(global, all_users, User)),
  171:     ?assertEqual(deny, acl:match_rule(global, none_users, User)),
  172:     ok.
  173: 
  174: invalid_spec(Config) ->
  175:     given_registered_domains(Config, [<<"bialystok">>]),
  176: 
  177:     User = jid:make(<<"pawel">>, <<"bialystok">>, <<"test">>),
  178:     set_acl(global, invalid, {non_existing_spec, "lalala"}),
  179: 
  180:     set_global_rule(invalid, [{allow, invalid}, {deny, all}]),
  181:     ?assertEqual(deny, acl:match_rule(global, invalid, User)),
  182:     ok.
  183: 
  184: match_host_specific_rule_for_host_type(Config) ->
  185:     given_registered_domains(Config, [<<"gdansk">>, <<"koszalin">>]),
  186: 
  187:     UserGd = jid:make(<<"pawel">>, <<"gdansk">>,  <<"res">>),
  188: 
  189:     set_host_rule(allow_admin, <<"test type">>, [{allow, admin}, {deny, all}]),
  190:     set_acl(<<"test type">>, admin, {user, <<"pawel">>}),
  191: 
  192:     %% Check for host type for a specific domain
  193:     ?assertEqual(allow, acl:match_rule_for_host_type(<<"test type">>, <<"gdansk">>,
  194:                                                      allow_admin, UserGd)),
  195:     ?assertEqual(deny, acl:match_rule_for_host_type(<<"test type">>, <<"koszalin">>,
  196:                                                     allow_admin, UserGd)),
  197:     ?assertEqual(deny, acl:match_rule_for_host_type(<<"empty type">>, <<"gdansk">>,
  198:                                                     allow_admin, UserGd)),
  199: 
  200:     %% Check for host type for any domain
  201:     ?assertEqual(allow, acl:match_rule_for_host_type(<<"test type">>, global, allow_admin, UserGd)),
  202:     ?assertEqual(deny, acl:match_rule_for_host_type(<<"empty type">>, global, allow_admin, UserGd)),
  203: 
  204:     %% Check globally for a specific domain
  205:     ?assertEqual(deny, acl:match_rule_for_host_type(global, <<"gdansk">>, allow_admin, UserGd)),
  206:     ?assertEqual(deny, acl:match_rule_for_host_type(global, <<"koszalin">>, allow_admin, UserGd)),
  207: 
  208:     %% Check globally for any domain
  209:     ?assertEqual(deny, acl:match_rule_for_host_type(global, global, allow_admin, UserGd)).
  210: 
  211: match_global_rule_for_host_type(Config) ->
  212:     given_registered_domains(Config, [<<"gdansk">>, <<"koszalin">>]),
  213: 
  214:     UserGd = jid:make(<<"pawel">>, <<"gdansk">>,  <<"res">>),
  215: 
  216:     set_global_rule(allow_admin, [{allow, admin}, {deny, all}]),
  217:     set_acl(global, admin, {user, <<"pawel">>}),
  218: 
  219:     %% Check for host type for a specific domain
  220:     ?assertEqual(allow, acl:match_rule_for_host_type(<<"test type">>, <<"gdansk">>,
  221:                                                      allow_admin, UserGd)),
  222:     ?assertEqual(deny, acl:match_rule_for_host_type(<<"test type">>, <<"koszalin">>,
  223:                                                     allow_admin, UserGd)),
  224: 
  225:     %% Check for host type for any domain
  226:     ?assertEqual(allow, acl:match_rule_for_host_type(<<"test type">>, global, allow_admin, UserGd)),
  227: 
  228:     %% Check globally for a specific domain
  229:     ?assertEqual(allow, acl:match_rule_for_host_type(global, <<"gdansk">>, allow_admin, UserGd)),
  230:     ?assertEqual(deny, acl:match_rule_for_host_type(global, <<"koszalin">>, allow_admin, UserGd)),
  231: 
  232:     %% Check globally for any domain
  233:     ?assertEqual(allow, acl:match_rule_for_host_type(global, global, allow_admin, UserGd)).
  234: 
  235: different_specs_matching_the_same_user(Config) ->
  236:     given_registered_domains(Config, [<<"gdansk">>, <<"koszalin">>]),
  237: 
  238:     UserGd = jid:make(<<"pawel">>, <<"gdansk">>,  <<"res">>),
  239:     UserKo = jid:make(<<"pawel">>, <<"koszalin">>,<<"res1">>),
  240: 
  241:     %% invariand we are going to change admin acl only
  242:     set_global_rule(allow_admin, [{allow, admin}, {deny, all}]),
  243: 
  244:     %% match on pawel
  245:     set_acl(global, admin, {user, <<"pawel">>}),
  246:     ?assertEqual(allow, acl:match_rule(global, allow_admin, UserGd)),
  247:     ?assertEqual(allow, acl:match_rule(global, allow_admin, UserKo)),
  248: 
  249:     %% match on pawel@gdansk
  250:     set_acl(global, admin, {user, <<"pawel">>, <<"gdansk">>}),
  251:     ?assertEqual(allow, acl:match_rule(global, allow_admin, UserGd)),
  252:     ?assertEqual(deny, acl:match_rule(global, allow_admin, UserKo)),
  253: 
  254:     %% match on gdansk
  255:     set_acl(global, admin, {server, <<"gdansk">>}),
  256:     ?assertEqual(allow, acl:match_rule(global, allow_admin, UserGd)),
  257:     ?assertEqual(deny, acl:match_rule(global, allow_admin, UserKo)),
  258: 
  259:     %% match on res
  260:     set_acl(global, admin, {resource, <<"res">>}),
  261:     ?assertEqual(allow, acl:match_rule(global, allow_admin, UserGd)),
  262:     ?assertEqual(deny, acl:match_rule(global, allow_admin, UserKo)),
  263: 
  264:     %% match on user regex
  265:     set_acl(global, admin, {user_regexp, <<"^paw.*">>}),
  266:     ?assertEqual(allow, acl:match_rule(global, allow_admin, UserGd)),
  267:     ?assertEqual(allow, acl:match_rule(global, allow_admin, UserKo)),
  268: 
  269:     %% match on user regex
  270:     set_acl(global, admin, {user_regexp, <<"^paw.*">>, <<"gdansk">>}),
  271:     ?assertEqual(allow, acl:match_rule(global, allow_admin, UserGd)),
  272:     ?assertEqual(deny, acl:match_rule(global, allow_admin, UserKo)),
  273: 
  274:     %% match on server regex
  275:     set_acl(global, admin, {server_regexp, <<"^gda.*">>}),
  276:     ?assertEqual(allow, acl:match_rule(global, allow_admin, UserGd)),
  277:     ?assertEqual(deny, acl:match_rule(global, allow_admin, UserKo)),
  278: 
  279:     %% match on resource regex
  280:     set_acl(global, admin, {resource_regexp, <<"^res.*">>}),
  281:     ?assertEqual(allow, acl:match_rule(global, allow_admin, UserGd)),
  282:     ?assertEqual(allow, acl:match_rule(global, allow_admin, UserKo)),
  283: 
  284:     %% match node regex
  285:     set_acl(global, admin, {node_regexp, <<"^pawe.*">>, <<"^gda.*">>}),
  286:     ?assertEqual(allow, acl:match_rule(global, allow_admin, UserGd)),
  287:     ?assertEqual(deny, acl:match_rule(global, allow_admin, UserKo)),
  288: 
  289:     %% match on user glob
  290:     set_acl(global, admin, {user_glob, <<"paw??">>}),
  291:     ?assertEqual(allow, acl:match_rule(global, allow_admin, UserGd)),
  292:     ?assertEqual(allow, acl:match_rule(global, allow_admin, UserKo)),
  293: 
  294:     %% match on user glob
  295:     set_acl(global, admin, {user_glob, <<"paw??">>, <<"gdansk">>}),
  296:     ?assertEqual(allow, acl:match_rule(global, allow_admin, UserGd)),
  297:     ?assertEqual(deny, acl:match_rule(global, allow_admin, UserKo)),
  298: 
  299:     %% match on server glob
  300:     set_acl(global, admin, {server_glob, <<"gda*">>}),
  301:     ?assertEqual(allow, acl:match_rule(global, allow_admin, UserGd)),
  302:     ?assertEqual(deny, acl:match_rule(global, allow_admin, UserKo)),
  303: 
  304:     %% match on server glob
  305:     set_acl(global, admin, {resource_glob, <<"re*">>}),
  306:     ?assertEqual(allow, acl:match_rule(global, allow_admin, UserGd)),
  307:     ?assertEqual(allow, acl:match_rule(global, allow_admin, UserKo)),
  308: 
  309:     %% match on node glob
  310:     set_acl(global, admin, {node_glob, <<"pawe?">>, <<"gd*">>}),
  311:     ?assertEqual(allow, acl:match_rule(global, allow_admin, UserGd)),
  312:     ?assertEqual(deny, acl:match_rule(global, allow_admin, UserKo)),
  313: 
  314:     ok.
  315: 
  316: set_acl(HostType, ACLName, ACLSpec) ->
  317:     mongoose_config:set_opt({acl, ACLName, HostType}, [ACLSpec]).
  318: 
  319: given_clean_config() ->
  320:     [persistent_term:erase(Key) || {Key = {mongoose_config, _}, _Value} <- persistent_term:get()],
  321:     ok.
  322: 
  323: given_registered_domains(Config, DomainsList) ->
  324:     case proplists:get_value(dynamic_domains, Config, false) of
  325:         true ->
  326:             register_dynamic_domains(DomainsList);
  327:         false ->
  328:             register_static_domains(DomainsList)
  329:     end.
  330: 
  331: register_static_domains(DomainsList) ->
  332:     mongoose_config:set_opt(hosts, DomainsList),
  333:     mongoose_config:set_opt(host_types, []),
  334:     mongoose_domain_api:stop(),
  335:     mongoose_domain_api:init().
  336: 
  337: register_dynamic_domains(DomainsList) ->
  338:     mongoose_config:set_opt(hosts, []),
  339:     mongoose_config:set_opt(host_types, [<<"test type">>, <<"empty type">>]),
  340:     mongoose_domain_api:stop(),
  341:     mongoose_domain_api:init(),
  342:     [mongoose_domain_core:insert(Domain, <<"test type">>, test) || Domain <- DomainsList].
  343: 
  344: %% ACLs might be an empty list
  345: set_host_rule(Rule, Host, ACLs) ->
  346:     mongoose_config:set_opt({access, Rule, Host}, ACLs),
  347:     ok.
  348: 
  349: %% ACLs might be an empty list
  350: set_global_rule(Rule, ACLs) ->
  351:     mongoose_config:set_opt({access, Rule, global}, ACLs),
  352:     ok.