1: -module(acl_SUITE).
    2: -compile([export_all, nowarn_export_all]).
    3: 
    4: -include_lib("eunit/include/eunit.hrl").
    5: 
    6: -import(mongoose_config, [set_opt/2, unset_opt/1]).
    7: 
    8: all() ->
    9:     [{group, dynamic_domains},
   10:      {group, static_domains}].
   11: 
   12: groups() ->
   13:     [{dynamic_domains, [], basic_test_cases() ++ host_type_test_cases()},
   14:      {static_domains, [], basic_test_cases()}].
   15: 
   16: basic_test_cases() ->
   17:     [
   18:      all_rule_returns_allow,
   19:      none_rule_returns_deny,
   20:      basic_access_rules,
   21:      compound_access_rules,
   22:      host_specific_access_rules,
   23:      all_and_none_specs,
   24:      match_domain,
   25:      different_specs_matching_the_same_user
   26:     ].
   27: 
   28: host_type_test_cases() ->
   29:     [
   30:      match_host_specific_rule_for_host_type
   31:     ].
   32: 
   33: init_per_suite(Config) ->
   34:     {ok, _} = application:ensure_all_started(jid),
   35:     async_helper:start(Config, mongoose_domain_sup, start_link, [[], []]).
   36: 
   37: end_per_suite(Config) ->
   38:     async_helper:stop_all(Config).
   39: 
   40: init_per_group(dynamic_domains, Config) ->
   41:     [{dynamic_domains, true}|Config];
   42: init_per_group(_Group, Config) ->
   43:     Config.
   44: 
   45: end_per_group(_Group, Config) ->
   46:     Config.
   47: 
   48: init_per_testcase(_TC, Config) ->
   49:     mongoose_config:set_opts(#{}),
   50:     Config.
   51: 
   52: end_per_testcase(_TC, _Config) ->
   53:     mongoose_config:erase_opts().
   54: 
   55: host_type() ->
   56:     <<"test host type">>.
   57: 
   58: all_rule_returns_allow(_Config) ->
   59:     JID = jid:make(<<"pawel">>, <<"phost">>, <<"test">>),
   60:     ?assertEqual(allow, acl:match_rule(global, all, JID)),
   61:     ?assertEqual(allow, acl:match_rule(<<"phost">>, all, JID)),
   62:     ?assertEqual(allow, acl:match_rule(<<"localhost">>, all, JID)),
   63:     ok.
   64: 
   65: none_rule_returns_deny(_Config) ->
   66:     JID = jid:make(<<"gawel">>, <<"phost">>, <<"test">>),
   67:     ?assertEqual(deny, acl:match_rule(global, none, JID)),
   68:     ?assertEqual(deny, acl:match_rule(<<"phost">>, none, JID)),
   69:     ?assertEqual(deny, acl:match_rule(<<"localhost">>, none, JID)),
   70:     ok.
   71: 
   72: basic_access_rules(_Config) ->
   73:     JID = jid:make(<<"pawel">>, <<"phost">>, <<"test">>),
   74: 
   75:     %% rule is not defined deny by default - deny
   76:     ?assertEqual(deny, (acl:match_rule(global, single_rule, JID))),
   77: 
   78:     %% add the rule and recheck
   79:     set_opt({access, global}, #{single_rule => [#{acl => all, value => allow}]}),
   80:     ?assertEqual(allow, (acl:match_rule(global, single_rule, JID))),
   81: 
   82:     %% override it to deny rule
   83:     set_opt({access, global}, #{single_rule => [#{acl => all, value => deny}]}),
   84:     ?assertEqual(deny, (acl:match_rule(global, single_rule, JID))),
   85: 
   86:     %% deny by default
   87:     set_opt({access, global}, #{single_rule => []}),
   88:     ?assertEqual(deny, (acl:match_rule(global, single_rule, JID))),
   89: 
   90:     %% allow nobody
   91:     set_opt({access, global}, #{single_rule => [#{acl => none, value => allow}]}),
   92:     ?assertEqual(deny, (acl:match_rule(global, single_rule, JID))),
   93:     ok.
   94: 
   95: host_specific_access_rules(Config) ->
   96:     given_registered_domains(Config, [<<"poznan">>, <<"wroclaw">>]),
   97: 
   98:     PozAdmin = jid:make(<<"gawel">>, <<"poznan">>, <<"test">>),
   99:     Pawel = jid:make(<<"pawel">>, <<"wroclaw">>, <<"test">>),
  100: 
  101:     set_opt({acl, <<"poznan">>}, #{admin_poz => acl(#{user => <<"gawel">>,
  102:                                                       server => <<"poznan">>})}),
  103:     set_opt({acl, <<"wroclaw">>}, #{admin_poz => acl(#{user => <<"gawel">>,
  104:                                                        server => <<"poznan">>})}),
  105:     set_opt({access, <<"poznan">>}, #{only_poz_admin => [#{acl => admin_poz, value => allow},
  106:                                                          #{acl => all, value => deny}]}),
  107:     set_opt({access, <<"wroclaw">>}, #{only_poz_admin => [#{acl => admin_poz, value => deny},
  108:                                                           #{acl => all, value => allow}]}),
  109: 
  110:     ?assertEqual(allow, acl:match_rule(<<"poznan">>, <<"poznan">>, only_poz_admin, PozAdmin)),
  111:     ?assertEqual(deny, acl:match_rule(<<"poznan">>, <<"wroclaw">>, only_poz_admin, Pawel)),
  112: 
  113:     ?assertEqual(deny, acl:match_rule(<<"wroclaw">>, <<"poznan">>, only_poz_admin, PozAdmin)),
  114:     ?assertEqual(allow, acl:match_rule(<<"wroclaw">>, <<"wroclaw">>, only_poz_admin, Pawel)),
  115:     ok.
  116: 
  117: compound_access_rules(Config) ->
  118:     given_registered_domains(Config, [<<"krakow">>]),
  119: 
  120:     KrkAdmin = jid:make(<<"gawel">>, <<"krakow">>, <<"test">>),
  121:     KrkNormal = jid:make(<<"pawel">>, <<"krakow">>, <<"test">>),
  122: 
  123:     %% add admin user rule
  124:     set_opt({acl, global}, #{admin_wawa => acl(#{user => <<"gawel">>, server => <<"wawa">>}),
  125:                              admin_krakow => acl(#{user => <<"gawel">>, server => <<"krakow">>})}),
  126:     set_opt({access, global}, #{only_krakow_admin => [#{acl => admin_krakow, value => allow},
  127:                                                       #{acl => all, value => deny}],
  128:                                 only_wawa_admin => [#{acl => admin_wawa, value => allow},
  129:                                                     #{acl => all, value => deny}]}),
  130: 
  131:     ?assertEqual(deny, acl:match_rule(global, only_krakow_admin, KrkNormal)),
  132:     ?assertEqual(deny, acl:match_rule(global, only_wawa_admin, KrkNormal)),
  133: 
  134:     ?assertEqual(allow, acl:match_rule(global, only_krakow_admin, KrkAdmin)),
  135:     ?assertEqual(deny,  acl:match_rule(global, only_wawa_admin, KrkAdmin)),
  136:     ok.
  137: 
  138: all_and_none_specs(Config) ->
  139:     given_registered_domains(Config, [<<"zakopane">>]),
  140: 
  141:     User = jid:make(<<"pawel">>, <<"zakopane">>, <<"test">>),
  142:     set_opt({acl, global}, #{a_users => acl(#{match => all}),
  143:                              n_users => acl(#{match => none})}),
  144:     set_opt({access, global}, #{all_users => [#{acl => a_users, value => allow},
  145:                                               #{acl => all, value => deny}],
  146:                                 none_users => [#{acl => n_users, value => allow},
  147:                                                #{acl => all, value => deny}]}),
  148: 
  149:     ?assertEqual(allow, acl:match_rule(global, <<"zakopane">>, all_users, User)),
  150:     ?assertEqual(deny, acl:match_rule(global, <<"zakopane">>, none_users, User)),
  151: 
  152:     %% domain doesn't matter for 'all'
  153:     ?assertEqual(allow, acl:match_rule(global, <<"any domain">>, all_users, User)),
  154:     ?assertEqual(deny, acl:match_rule(global, <<"any domain">>, none_users, User)),
  155:     ok.
  156: 
  157: match_domain(Config) ->
  158:     given_registered_domains(Config, [<<"zakopane">>, <<"gdansk">>]),
  159: 
  160:     UserZa = jid:make(<<"pawel">>, <<"zakopane">>, <<"test">>),
  161:     UserGd = jid:make(<<"pawel">>, <<"gdansk">>, <<"test">>),
  162:     UserTo = jid:make(<<"pawel">>, <<"torun">>, <<"test">>),
  163:     UserZb = jid:make(<<"pawel">>, <<"zakopane">>, <<"best">>),
  164: 
  165:     set_opt({acl, global}, #{a_users => [#{match => current_domain, resource => <<"test">>}],
  166:                              b_users => [#{match => any_hosted_domain, resource => <<"test">>}],
  167:                              c_users => [#{match => all, resource => <<"test">>}]}),
  168:     set_opt({access, global}, #{rule => [#{acl => a_users, value => a},
  169:                                          #{acl => b_users, value => b},
  170:                                          #{acl => c_users, value => c},
  171:                                          #{acl => all, value => d}]}),
  172: 
  173:     ?assertEqual(a, acl:match_rule(global, <<"zakopane">>, rule, UserZa)),
  174:     ?assertEqual(b, acl:match_rule(global, <<"zakopane">>, rule, UserGd)),
  175:     ?assertEqual(c, acl:match_rule(global, <<"zakopane">>, rule, UserTo)),
  176:     ?assertEqual(d, acl:match_rule(global, <<"zakopane">>, rule, UserZb)),
  177:     ok.
  178: 
  179: match_host_specific_rule_for_host_type(Config) ->
  180:     given_registered_domains(Config, [<<"gdansk">>, <<"koszalin">>]),
  181: 
  182:     UserGd = jid:make(<<"pawel">>, <<"gdansk">>,  <<"res">>),
  183: 
  184:     set_opt({acl, <<"test type">>}, #{admin => acl(#{user => <<"pawel">>})}),
  185:     set_opt({access, <<"test type">>}, #{allow_admin => [#{acl => admin, value => allow},
  186:                                                          #{acl => all, value => deny}]}),
  187: 
  188:     %% Check for host type for a specific domain
  189:     ?assertEqual(allow, acl:match_rule(<<"test type">>, <<"gdansk">>, allow_admin, UserGd)),
  190:     ?assertEqual(deny, acl:match_rule(<<"test type">>, <<"koszalin">>, allow_admin, UserGd)),
  191:     ?assertEqual(deny, acl:match_rule(<<"empty type">>, <<"gdansk">>, allow_admin, UserGd)),
  192: 
  193:     %% Check for host type for any domain
  194:     ?assertEqual(allow, acl:match_rule(<<"test type">>,  allow_admin, UserGd)),
  195:     ?assertEqual(deny, acl:match_rule(<<"empty type">>, allow_admin, UserGd)),
  196: 
  197:     %% Check globally for a specific domain
  198:     ?assertEqual(deny, acl:match_rule(global, <<"gdansk">>, allow_admin, UserGd)),
  199:     ?assertEqual(deny, acl:match_rule(global, <<"koszalin">>, allow_admin, UserGd)),
  200: 
  201:     %% Check globally for any domain
  202:     ?assertEqual(deny, acl:match_rule(global, allow_admin, UserGd)).
  203: 
  204: different_specs_matching_the_same_user(Config) ->
  205:     given_registered_domains(Config, [<<"gdansk">>, <<"koszalin">>]),
  206: 
  207:     UserGd = jid:make(<<"pawel">>, <<"gdansk">>,  <<"res">>),
  208:     UserKo = jid:make(<<"pawel">>, <<"koszalin">>,<<"res1">>),
  209: 
  210:     %% invariand we are going to change admin acl only
  211:     set_opt({access, global}, #{allow_admin => [#{acl => admin, value => allow},
  212:                                                 #{acl => all, value => deny}]}),
  213: 
  214:     %% match on pawel
  215:     set_opt({acl, global}, #{admin => acl(#{user => <<"pawel">>})}),
  216:     ?assertEqual(allow, acl:match_rule(global, allow_admin, UserGd)),
  217:     ?assertEqual(allow, acl:match_rule(global, allow_admin, UserKo)),
  218: 
  219:     %% match on pawel@gdansk
  220:     set_opt({acl, global}, #{admin => acl(#{user => <<"pawel">>, server => <<"gdansk">>})}),
  221:     ?assertEqual(allow, acl:match_rule(global, allow_admin, UserGd)),
  222:     ?assertEqual(deny, acl:match_rule(global, allow_admin, UserKo)),
  223: 
  224:     %% match on gdansk
  225:     set_opt({acl, global}, #{admin => acl(#{server => <<"gdansk">>})}),
  226:     ?assertEqual(allow, acl:match_rule(global, allow_admin, UserGd)),
  227:     ?assertEqual(deny, acl:match_rule(global, allow_admin, UserKo)),
  228: 
  229:     %% match on res
  230:     set_opt({acl, global}, #{admin => acl(#{resource => <<"res">>})}),
  231:     ?assertEqual(allow, acl:match_rule(global, allow_admin, UserGd)),
  232:     ?assertEqual(deny, acl:match_rule(global, allow_admin, UserKo)),
  233: 
  234:     %% match on user regex
  235:     set_opt({acl, global}, #{admin => acl(#{user_regexp => <<"^paw.*">>})}),
  236:     ?assertEqual(allow, acl:match_rule(global, allow_admin, UserGd)),
  237:     ?assertEqual(allow, acl:match_rule(global, allow_admin, UserKo)),
  238: 
  239:     %% match on user regex
  240:     set_opt({acl, global}, #{admin => acl(#{user_regexp => <<"^paw.*">>, server => <<"gdansk">>})}),
  241:     ?assertEqual(allow, acl:match_rule(global, allow_admin, UserGd)),
  242:     ?assertEqual(deny, acl:match_rule(global, allow_admin, UserKo)),
  243: 
  244:     %% match on server regex
  245:     set_opt({acl, global}, #{admin => acl(#{server_regexp => <<"^gda.*">>})}),
  246:     ?assertEqual(allow, acl:match_rule(global, allow_admin, UserGd)),
  247:     ?assertEqual(deny, acl:match_rule(global, allow_admin, UserKo)),
  248: 
  249:     %% match on resource regex
  250:     set_opt({acl, global}, #{admin => acl(#{resource_regexp => <<"^res.*">>})}),
  251:     ?assertEqual(allow, acl:match_rule(global, allow_admin, UserGd)),
  252:     ?assertEqual(allow, acl:match_rule(global, allow_admin, UserKo)),
  253: 
  254:     %% match node regex
  255:     set_opt({acl, global}, #{admin => acl(#{user_regexp => <<"^pawe.*">>,
  256:                                             server_regexp => <<"^gda.*">>})}),
  257:     ?assertEqual(allow, acl:match_rule(global, allow_admin, UserGd)),
  258:     ?assertEqual(deny, acl:match_rule(global, allow_admin, UserKo)),
  259: 
  260:     %% match on user glob
  261:     set_opt({acl, global}, #{admin => acl(#{user_glob => <<"paw??">>})}),
  262:     ?assertEqual(allow, acl:match_rule(global, allow_admin, UserGd)),
  263:     ?assertEqual(allow, acl:match_rule(global, allow_admin, UserKo)),
  264: 
  265:     %% match on user glob
  266:     set_opt({acl, global}, #{admin => acl(#{user_glob => <<"paw??">>, server => <<"gdansk">>})}),
  267:     ?assertEqual(allow, acl:match_rule(global, allow_admin, UserGd)),
  268:     ?assertEqual(deny, acl:match_rule(global, allow_admin, UserKo)),
  269: 
  270:     %% match on server glob
  271:     set_opt({acl, global}, #{admin => acl(#{server_glob => <<"gda*">>})}),
  272:     ?assertEqual(allow, acl:match_rule(global, allow_admin, UserGd)),
  273:     ?assertEqual(deny, acl:match_rule(global, allow_admin, UserKo)),
  274: 
  275:     %% match on server glob
  276:     set_opt({acl, global}, #{admin => acl(#{resource_glob => <<"re*">>})}),
  277:     ?assertEqual(allow, acl:match_rule(global, allow_admin, UserGd)),
  278:     ?assertEqual(allow, acl:match_rule(global, allow_admin, UserKo)),
  279: 
  280:     %% match on node glob
  281:     set_opt({acl, global}, #{admin => acl(#{user_glob => <<"pawe?">>, server_glob => <<"gd*">>})}),
  282:     ?assertEqual(allow, acl:match_rule(global, allow_admin, UserGd)),
  283:     ?assertEqual(deny, acl:match_rule(global, allow_admin, UserKo)),
  284: 
  285:     ok.
  286: 
  287: acl(Spec) ->
  288:     [maps:merge(#{match => current_domain}, Spec)].
  289: 
  290: given_registered_domains(Config, DomainsList) ->
  291:     case proplists:get_value(dynamic_domains, Config, false) of
  292:         true ->
  293:             register_dynamic_domains(DomainsList);
  294:         false ->
  295:             register_static_domains(DomainsList)
  296:     end.
  297: 
  298: register_static_domains(DomainsList) ->
  299:     mongoose_config:set_opt(hosts, DomainsList),
  300:     mongoose_config:set_opt(host_types, []),
  301:     mongoose_domain_sup:restart_core([]).
  302: 
  303: register_dynamic_domains(DomainsList) ->
  304:     mongoose_config:set_opt(hosts, []),
  305:     mongoose_config:set_opt(host_types, [<<"test type">>, <<"empty type">>]),
  306:     mongoose_domain_sup:restart_core([]),
  307:     [mongoose_domain_core:insert(Domain, <<"test type">>, test) || Domain <- DomainsList].