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