mirror of
https://github.com/valitydev/riak_test.git
synced 2024-11-06 08:35:22 +00:00
beb20830ec
If we leave it up to the Cuttlefish defaults, we end up with search 1.0 being disabled, which causes this test to fail.
821 lines
42 KiB
Erlang
821 lines
42 KiB
Erlang
-module(pb_security).
|
|
|
|
-behavior(riak_test).
|
|
-export([confirm/0]).
|
|
|
|
-export([map_object_value/3, reduce_set_union/2, mapred_modfun_input/3]).
|
|
|
|
-include_lib("eunit/include/eunit.hrl").
|
|
-include_lib("riakc/include/riakc.hrl").
|
|
|
|
-define(assertDenied(Op), ?assertMatch({error, <<"Permission",_/binary>>}, Op)).
|
|
|
|
confirm() ->
|
|
application:start(crypto),
|
|
application:start(asn1),
|
|
application:start(public_key),
|
|
application:start(ssl),
|
|
application:start(inets),
|
|
|
|
CertDir = rt_config:get(rt_scratch_dir) ++ "/pb_security_certs",
|
|
|
|
%% make a bunch of crypto keys
|
|
make_certs:rootCA(CertDir, "rootCA"),
|
|
make_certs:intermediateCA(CertDir, "intCA", "rootCA"),
|
|
make_certs:intermediateCA(CertDir, "revokedCA", "rootCA"),
|
|
make_certs:endusers(CertDir, "intCA", ["site1.basho.com", "site2.basho.com"]),
|
|
make_certs:endusers(CertDir, "rootCA", ["site3.basho.com", "site4.basho.com", "site5.basho.com"]),
|
|
make_certs:enduser(CertDir, "revokedCA", "site6.basho.com"),
|
|
make_certs:revoke(CertDir, "rootCA", "site5.basho.com"),
|
|
make_certs:revoke(CertDir, "rootCA", "revokedCA"),
|
|
|
|
%% use a leaf certificate as a CA certificate and make a totally bogus new leaf certificate
|
|
make_certs:create_ca_dir(CertDir, "site1.basho.com", make_certs:ca_cnf("site1.basho.com")),
|
|
file:copy(filename:join(CertDir, "site1.basho.com/key.pem"), filename:join(CertDir, "site1.basho.com/private/key.pem")),
|
|
make_certs:enduser(CertDir, "site1.basho.com", "site7.basho.com"),
|
|
file:copy(filename:join([CertDir, "site1.basho.com", "cacerts.pem"]), filename:join(CertDir, "site7.basho.com/cacerts.pem")),
|
|
{ok, Bin} = file:read_file(filename:join(CertDir, "site1.basho.com/cert.pem")),
|
|
{ok, FD} = file:open(filename:join(CertDir, "site7.basho.com/cacerts.pem"), [append]),
|
|
file:write(FD, ["\n", Bin]),
|
|
file:close(FD),
|
|
make_certs:gencrl(CertDir, "site1.basho.com"),
|
|
|
|
%% start a HTTP server to serve the CRLs
|
|
%%
|
|
%% NB: we use the 'stand_alone' option to link the server to the
|
|
%% test process, so it exits when the test process exits.
|
|
{ok, _HTTPPid} = inets:start(httpd, [{port, 8000}, {server_name, "localhost"},
|
|
{server_root, "/tmp"},
|
|
{document_root, CertDir},
|
|
{modules, [mod_get]}], stand_alone),
|
|
|
|
lager:info("Deploy some nodes"),
|
|
PrivDir = rt:priv_dir(),
|
|
Conf = [
|
|
{riak_core, [
|
|
{default_bucket_props, [{allow_mult, true}, {dvv_enabled, true}]},
|
|
{ssl, [
|
|
{certfile, filename:join([CertDir,"site3.basho.com/cert.pem"])},
|
|
{keyfile, filename:join([CertDir, "site3.basho.com/key.pem"])},
|
|
{cacertfile, filename:join([CertDir, "site3.basho.com/cacerts.pem"])}
|
|
]},
|
|
{job_accept_class, undefined}
|
|
]},
|
|
{riak_search, [
|
|
{enabled, true}
|
|
]}
|
|
],
|
|
|
|
MD = riak_test_runner:metadata(),
|
|
HaveIndexes = case proplists:get_value(backend, MD) of
|
|
undefined -> false; %% default is da 'cask
|
|
bitcask -> false;
|
|
_ -> true
|
|
end,
|
|
|
|
Nodes = rt:build_cluster(4, Conf),
|
|
Node = hd(Nodes),
|
|
%% enable security on the cluster
|
|
ok = rpc:call(Node, riak_core_console, security_enable, [[]]),
|
|
|
|
[_, {pb, {"127.0.0.1", Port}}] = rt:connection_info(Node),
|
|
|
|
lager:info("Checking non-SSL results in error"),
|
|
%% can connect without credentials, but not do anything
|
|
{ok, PB0} = riakc_pb_socket:start("127.0.0.1", Port,
|
|
[]),
|
|
?assertEqual({error, <<"Security is enabled, please STARTTLS first">>},
|
|
riakc_pb_socket:ping(PB0)),
|
|
|
|
riakc_pb_socket:stop(PB0),
|
|
|
|
%% Hindi in Devanagari : हिन्दी
|
|
Username = [2361,2367,2344,2381,2342,2368],
|
|
UsernameBin = unicode:characters_to_binary(Username, utf8, utf8),
|
|
|
|
lager:info("Checking SSL requires peer cert validation"),
|
|
%% can't connect without specifying cacert to validate the server
|
|
?assertMatch({error, _}, riakc_pb_socket:start("127.0.0.1", Port,
|
|
[{credentials, UsernameBin,
|
|
"pass"}])),
|
|
|
|
lager:info("Checking that authentication is required"),
|
|
%% invalid credentials should be invalid
|
|
?assertEqual({error, {tcp, <<"Authentication failed">>}}, riakc_pb_socket:start("127.0.0.1", Port,
|
|
[{credentials, UsernameBin,
|
|
"pass"}, {cacertfile,
|
|
filename:join([CertDir, "rootCA/cert.pem"])}])),
|
|
|
|
lager:info("Creating user"),
|
|
%% grant the user credentials
|
|
ok = rpc:call(Node, riak_core_console, add_user, [[Username, "password=password"]]),
|
|
|
|
lager:info("Setting trust mode on user"),
|
|
%% trust 'user' on localhost
|
|
ok = rpc:call(Node, riak_core_console, add_source, [[Username, "127.0.0.1/32",
|
|
"trust"]]),
|
|
|
|
lager:info("Checking that credentials are ignored in trust mode"),
|
|
%% invalid credentials should be ignored in trust mode
|
|
{ok, PB1} = riakc_pb_socket:start("127.0.0.1", Port,
|
|
[{credentials, UsernameBin,
|
|
"pass"}, {cacertfile,
|
|
filename:join([CertDir, "rootCA/cert.pem"])}]),
|
|
?assertEqual(pong, riakc_pb_socket:ping(PB1)),
|
|
riakc_pb_socket:stop(PB1),
|
|
|
|
lager:info("Setting password mode on user"),
|
|
%% require password on localhost
|
|
ok = rpc:call(Node, riak_core_console, add_source, [[Username, "127.0.0.1/32",
|
|
"password"]]),
|
|
|
|
lager:info("Checking that incorrect password fails auth"),
|
|
%% invalid credentials should be invalid
|
|
?assertEqual({error, {tcp, <<"Authentication failed">>}}, riakc_pb_socket:start("127.0.0.1", Port,
|
|
[{credentials, UsernameBin,
|
|
"pass"}, {cacertfile,
|
|
filename:join([CertDir, "rootCA/cert.pem"])}])),
|
|
|
|
lager:info("Checking that correct password is successful"),
|
|
%% valid credentials should be valid
|
|
{ok, PB2} = riakc_pb_socket:start("127.0.0.1", Port,
|
|
[{credentials, UsernameBin,
|
|
"password"}, {cacertfile,
|
|
filename:join([CertDir, "rootCA/cert.pem"])}]),
|
|
?assertEqual(pong, riakc_pb_socket:ping(PB2)),
|
|
riakc_pb_socket:stop(PB2),
|
|
|
|
lager:info("Creating a certificate-authenticated user"),
|
|
%% grant the user credential
|
|
ok = rpc:call(Node, riak_core_console, add_user, [["site4.basho.com"]]),
|
|
|
|
%% require certificate auth on localhost
|
|
ok = rpc:call(Node, riak_core_console, add_source, [["site4.basho.com",
|
|
"127.0.0.1/32",
|
|
"certificate"]]),
|
|
|
|
lager:info("Checking certificate authentication"),
|
|
%% valid credentials should be valid
|
|
{ok, PB3} = riakc_pb_socket:start("127.0.0.1", Port,
|
|
[{credentials, "site4.basho.com",
|
|
"password"},
|
|
{cacertfile, filename:join([CertDir, "site4.basho.com/cacerts.pem"])},
|
|
{certfile, filename:join([CertDir, "site4.basho.com/cert.pem"])},
|
|
{keyfile, filename:join([CertDir, "site4.basho.com/key.pem"])}
|
|
]),
|
|
?assertEqual(pong, riakc_pb_socket:ping(PB3)),
|
|
riakc_pb_socket:stop(PB3),
|
|
|
|
lager:info("Creating another cert-auth user"),
|
|
%% grant the user credential
|
|
ok = rpc:call(Node, riak_core_console, add_user, [["site5.basho.com"]]),
|
|
|
|
%% require certificate auth on localhost
|
|
ok = rpc:call(Node, riak_core_console, add_source, [["site5.basho.com",
|
|
"127.0.0.1/32",
|
|
"certificate"]]),
|
|
|
|
lager:info("Checking auth with mismatched user/cert fails"),
|
|
%% authing with mismatched user should fail
|
|
?assertEqual({error, {tcp, <<"Authentication failed">>}}, riakc_pb_socket:start("127.0.0.1", Port,
|
|
[{credentials, "site5.basho.com",
|
|
"password"},
|
|
{cacertfile, filename:join([CertDir, "rootCA/cert.pem"])},
|
|
{certfile, filename:join([CertDir, "site4.basho.com/cert.pem"])},
|
|
{keyfile, filename:join([CertDir, "site4.basho.com/key.pem"])}
|
|
])),
|
|
|
|
lager:info("Checking revoked certificates are denied"),
|
|
?assertMatch({error, {tcp, _Reason}}, riakc_pb_socket:start("127.0.0.1", Port,
|
|
[{credentials, "site5.basho.com",
|
|
"password"},
|
|
{cacertfile, filename:join([CertDir, "rootCA/cert.pem"])},
|
|
{certfile, filename:join([CertDir, "site5.basho.com/cert.pem"])},
|
|
{keyfile, filename:join([CertDir, "site5.basho.com/key.pem"])}
|
|
])),
|
|
|
|
lager:info("Checking auth with non-peer certificate fails"),
|
|
%% authing with non-peer certificate should fail
|
|
?assertMatch({error, {tcp, _Reason}}, riakc_pb_socket:start("127.0.0.1", Port,
|
|
[{credentials, "site5.basho.com",
|
|
"password"},
|
|
{cacertfile, filename:join([PrivDir,
|
|
"certs/CA/rootCA/cert.pem"])},
|
|
{certfile, filename:join([PrivDir,
|
|
"certs/cacert.org/ca-cert.pem"])},
|
|
{keyfile, filename:join([PrivDir,
|
|
"certs/cacert.org/ca-key.pem"])}
|
|
])),
|
|
|
|
lager:info("cert from intermediate CA should work"),
|
|
%% grant the user credential
|
|
ok = rpc:call(Node, riak_core_console, add_user, [["site1.basho.com"]]),
|
|
|
|
%% require certificate auth on localhost
|
|
ok = rpc:call(Node, riak_core_console, add_source, [["site1.basho.com",
|
|
"127.0.0.1/32",
|
|
"certificate"]]),
|
|
|
|
{ok, PB4} = riakc_pb_socket:start("127.0.0.1", Port,
|
|
[{credentials, "site1.basho.com", "password"},
|
|
{cacertfile, filename:join([CertDir, "site1.basho.com/cacerts.pem"])},
|
|
{certfile, filename:join([CertDir, "site1.basho.com/cert.pem"])},
|
|
{keyfile, filename:join([CertDir, "site1.basho.com/key.pem"])}
|
|
]),
|
|
|
|
?assertEqual(pong, riakc_pb_socket:ping(PB4)),
|
|
riakc_pb_socket:stop(PB4),
|
|
|
|
lager:info("checking certificates from a revoked CA are denied"),
|
|
%% grant the user credential
|
|
ok = rpc:call(Node, riak_core_console, add_user, [["site6.basho.com"]]),
|
|
|
|
%% require certificate auth on localhost
|
|
ok = rpc:call(Node, riak_core_console, add_source, [["site6.basho.com",
|
|
"127.0.0.1/32",
|
|
"certificate"]]),
|
|
|
|
?assertMatch({error, {tcp, _Reason}}, riakc_pb_socket:start("127.0.0.1", Port,
|
|
[{credentials, "site6.basho.com", "password"},
|
|
{cacertfile, filename:join([CertDir, "site6.basho.com/cacerts.pem"])},
|
|
{certfile, filename:join([CertDir, "site6.basho.com/cert.pem"])},
|
|
{keyfile, filename:join([CertDir, "site6.basho.com/key.pem"])}
|
|
])),
|
|
|
|
lager:info("checking a certificate signed by a leaf CA is not honored"),
|
|
%% grant the user credential
|
|
ok = rpc:call(Node, riak_core_console, add_user, [["site7.basho.com"]]),
|
|
|
|
%% require certificate auth on localhost
|
|
ok = rpc:call(Node, riak_core_console, add_source, [["site7.basho.com",
|
|
"127.0.0.1/32",
|
|
"certificate"]]),
|
|
|
|
?assertMatch({error, {tcp, _Reason}}, riakc_pb_socket:start("127.0.0.1", Port,
|
|
[{credentials, "site7.basho.com", "password"},
|
|
{cacertfile, filename:join([CertDir, "site7.basho.com/cacerts.pem"])},
|
|
{certfile, filename:join([CertDir, "site7.basho.com/cert.pem"])},
|
|
{keyfile, filename:join([CertDir, "site7.basho.com/key.pem"])}
|
|
])),
|
|
|
|
%% time to actually do some stuff
|
|
{ok, PB} = riakc_pb_socket:start("127.0.0.1", Port,
|
|
[{credentials, UsernameBin, "password"},
|
|
{cacertfile,
|
|
filename:join([CertDir, "rootCA/cert.pem"])}]),
|
|
?assertEqual(pong, riakc_pb_socket:ping(PB)),
|
|
|
|
lager:info("verifying that user cannot get/put without grants"),
|
|
?assertMatch({error, <<"Permission", _/binary>>}, riakc_pb_socket:get(PB,
|
|
<<"hello">>,
|
|
<<"world">>)),
|
|
|
|
lager:info("Granting riak_kv.get, checking get works but put doesn't"),
|
|
grant(Node, ["riak_kv.get", "on", "default", "hello", "to", Username]),
|
|
|
|
?assertMatch({error, notfound}, riakc_pb_socket:get(PB, <<"hello">>,
|
|
<<"world">>)),
|
|
|
|
?assertMatch({error, <<"Permission", _/binary>>},
|
|
riakc_pb_socket:put(PB,
|
|
riakc_obj:new(<<"hello">>, <<"world">>,
|
|
<<"howareyou">>))),
|
|
|
|
lager:info("Granting riak_kv.put, checking put works and roundtrips with get"),
|
|
grant(Node, ["riak_kv.put", "on", "default", "hello", "to", Username]),
|
|
|
|
?assertEqual(ok,
|
|
riakc_pb_socket:put(PB,
|
|
riakc_obj:new(<<"hello">>, <<"world">>,
|
|
<<"1">>, "application/json"))),
|
|
|
|
?assertMatch({ok, _Obj}, riakc_pb_socket:get(PB, <<"hello">>,
|
|
<<"world">>)),
|
|
|
|
%% 1.4 counters
|
|
%%
|
|
grant(Node, ["riak_kv.put,riak_kv.get", "on", "default", "counters", "to", Username]),
|
|
%% ok = rpc:call(Node, riak_core_console, grant, [["riak_kv.put,riak_kv.get", "on",
|
|
%% "default", "counters", "to", Username]]),
|
|
|
|
|
|
lager:info("Checking that counters work on resources that have get/put permitted"),
|
|
?assertEqual({error, notfound}, riakc_pb_socket:counter_val(PB,
|
|
<<"counters">>,
|
|
<<"numberofpies">>)),
|
|
ok = riakc_pb_socket:counter_incr(PB, <<"counters">>,
|
|
<<"numberofpies">>, 5),
|
|
?assertEqual({ok, 5}, riakc_pb_socket:counter_val(PB, <<"counters">>,
|
|
<<"numberofpies">>)),
|
|
|
|
lager:info("Revoking get, checking that counter_val fails"),
|
|
%% revoke get
|
|
ok = rpc:call(Node, riak_core_console, revoke,
|
|
[["riak_kv.get", "on", "default", "counters", "from", Username]]),
|
|
|
|
?assertMatch({error, <<"Permission", _/binary>>},
|
|
riakc_pb_socket:counter_val(PB, <<"counters">>,
|
|
<<"numberofpies">>)),
|
|
ok = riakc_pb_socket:counter_incr(PB, <<"counters">>,
|
|
<<"numberofpies">>, 5),
|
|
|
|
lager:info("Revoking put, checking that counter_incr fails"),
|
|
%% revoke put
|
|
ok = rpc:call(Node, riak_core_console, revoke,
|
|
[["riak_kv.put", "on", "default", "counters", "from", Username]]),
|
|
|
|
?assertMatch({error, <<"Permission", _/binary>>},
|
|
riakc_pb_socket:counter_incr(PB, <<"counters">>,
|
|
<<"numberofpies">>, 5)),
|
|
|
|
|
|
lager:info("Revoking get/put, checking that get/put are disallowed"),
|
|
ok = rpc:call(Node, riak_core_console, revoke, [["riak_kv.get,riak_kv.put", "on",
|
|
"default", "hello", "from", Username]]),
|
|
|
|
?assertMatch({error, <<"Permission", _/binary>>}, riakc_pb_socket:get(PB,
|
|
<<"hello">>,
|
|
<<"world">>)),
|
|
|
|
?assertMatch({error, <<"Permission", _/binary>>},
|
|
riakc_pb_socket:put(PB,
|
|
riakc_obj:new(<<"hello">>, <<"world">>,
|
|
<<"howareyou">>))),
|
|
|
|
%% try the 'any' grant
|
|
lager:info("Granting get on ANY, checking user can fetch any bucket/key"),
|
|
grant(Node, ["riak_kv.get", "on", "any", "to", Username]),
|
|
|
|
?assertMatch({ok, _Obj}, riakc_pb_socket:get(PB, <<"hello">>,
|
|
<<"world">>)),
|
|
|
|
lager:info("Revoking ANY permission, checking fetch fails"),
|
|
ok = rpc:call(Node, riak_core_console, revoke, [["riak_kv.get", "on",
|
|
"any", "from", Username]]),
|
|
|
|
?assertMatch({error, <<"Permission", _/binary>>}, riakc_pb_socket:get(PB,
|
|
<<"hello">>,
|
|
<<"world">>)),
|
|
|
|
%% list keys
|
|
lager:info("Checking that list keys is disallowed"),
|
|
?assertMatch({error, <<"Permission", _/binary>>}, riakc_pb_socket:list_keys(PB, <<"hello">>)),
|
|
|
|
lager:info("Granting riak_kv.list_keys, checking that list_keys succeeds"),
|
|
ok = rpc:call(Node, riak_core_console, grant, [["riak_kv.list_keys", "on",
|
|
"default", "hello", "to", Username]]),
|
|
|
|
?assertMatch({ok, [<<"world">>]}, riakc_pb_socket:list_keys(PB, <<"hello">>)),
|
|
|
|
lager:info("Checking that list buckets is disallowed"),
|
|
?assertMatch({error, <<"Permission", _/binary>>},
|
|
riakc_pb_socket:list_buckets(PB)),
|
|
|
|
lager:info("Granting riak_kv.list_buckets, checking that list_buckets succeeds"),
|
|
ok = rpc:call(Node, riak_core_console, grant, [["riak_kv.list_buckets", "on",
|
|
"default", "to", Username]]),
|
|
|
|
{ok, BList} = riakc_pb_socket:list_buckets(PB),
|
|
?assertEqual([<<"counters">>, <<"hello">>], lists:sort(BList)),
|
|
|
|
%% still need mapreduce permission
|
|
lager:info("Checking that full-bucket mapred is disallowed"),
|
|
?assertMatch({error, <<"Permission", _/binary>>},
|
|
riakc_pb_socket:mapred_bucket(PB, <<"hello">>,
|
|
[{map, {jsfun, <<"Riak.mapValuesJson">>}, undefined, false},
|
|
{reduce, {jsfun,
|
|
<<"Riak.reduceSum">>},
|
|
undefined, true}])),
|
|
|
|
lager:info("Granting mapreduce, checking that job succeeds"),
|
|
ok = rpc:call(Node, riak_core_console, grant, [["riak_kv.mapreduce", "on",
|
|
"default", "to", Username]]),
|
|
|
|
?assertEqual({ok, [{1, [1]}]},
|
|
riakc_pb_socket:mapred_bucket(PB, <<"hello">>,
|
|
[{map, {jsfun, <<"Riak.mapValuesJson">>}, undefined, false},
|
|
{reduce, {jsfun,
|
|
<<"Riak.reduceSum">>},
|
|
undefined, true}])),
|
|
|
|
lager:info("checking mapreduce with a whitelisted modfun works"),
|
|
?assertEqual({ok, [{1, [<<"1">>]}]},
|
|
riakc_pb_socket:mapred_bucket(PB, <<"hello">>,
|
|
[{map, {modfun, riak_kv_mapreduce,
|
|
map_object_value}, undefined, false},
|
|
{reduce, {modfun,
|
|
riak_kv_mapreduce,
|
|
reduce_set_union},
|
|
undefined, true}])),
|
|
|
|
%% load this module on all the nodes
|
|
ok = rt:load_modules_on_nodes([?MODULE], Nodes),
|
|
|
|
lager:info("checking mapreduce with a insecure modfun input fails"),
|
|
?assertMatch({error, <<"{inputs,{insecure_module_path",_/binary>>},
|
|
riakc_pb_socket:mapred_bucket(PB, {modfun, ?MODULE,
|
|
mapred_modfun_input, []},
|
|
[{map, {modfun, riak_kv_mapreduce,
|
|
map_object_value}, undefined, false},
|
|
{reduce, {modfun,
|
|
riak_kv_mapreduce,
|
|
reduce_set_union},
|
|
undefined, true}])),
|
|
|
|
lager:info("checking mapreduce with a insecure modfun phase fails"),
|
|
?assertMatch({error, <<"{query,{insecure_module_path",_/binary>>},
|
|
riakc_pb_socket:mapred_bucket(PB, <<"hello">>,
|
|
[{map, {modfun, ?MODULE,
|
|
map_object_value}, undefined, false},
|
|
{reduce, {modfun,
|
|
?MODULE,
|
|
reduce_set_union},
|
|
undefined, true}])),
|
|
|
|
lager:info("whitelisting module path"),
|
|
{?MODULE, _ModBin, ModFile} = code:get_object_code(?MODULE),
|
|
ok = rpc:call(Node, application, set_env, [riak_kv, add_paths, [filename:dirname(ModFile)]]),
|
|
|
|
lager:info("checking mapreduce with a insecure modfun input fails when"
|
|
" whitelisted but lacking permissions"),
|
|
?assertMatch({error, <<"Permission",_/binary>>},
|
|
riakc_pb_socket:mapred_bucket(PB, {modfun, ?MODULE,
|
|
mapred_modfun_input, []},
|
|
[{map, {modfun, riak_kv_mapreduce,
|
|
map_object_value}, undefined, false},
|
|
{reduce, {modfun,
|
|
riak_kv_mapreduce,
|
|
reduce_set_union},
|
|
undefined, true}])),
|
|
|
|
ok = rpc:call(Node, riak_core_console, grant, [["riak_kv.mapreduce", "on",
|
|
"any", "to", Username]]),
|
|
?assertEqual({ok, [{1, [<<"1">>]}]},
|
|
riakc_pb_socket:mapred_bucket(PB, {modfun, ?MODULE,
|
|
mapred_modfun_input, []},
|
|
[{map, {modfun, riak_kv_mapreduce,
|
|
map_object_value}, undefined, false},
|
|
{reduce, {modfun,
|
|
riak_kv_mapreduce,
|
|
reduce_set_union},
|
|
undefined, true}])),
|
|
|
|
ok = rpc:call(Node, riak_core_console, revoke, [["riak_kv.mapreduce", "on",
|
|
"any", "from", Username]]),
|
|
|
|
lager:info("checking mapreduce with a insecure modfun phase works when"
|
|
" whitelisted"),
|
|
?assertEqual({ok, [{1, [<<"1">>]}]},
|
|
riakc_pb_socket:mapred_bucket(PB, <<"hello">>,
|
|
[{map, {modfun, ?MODULE,
|
|
map_object_value}, undefined, false},
|
|
{reduce, {modfun,
|
|
?MODULE,
|
|
reduce_set_union},
|
|
undefined, true}])),
|
|
|
|
|
|
|
|
lager:info("link walking should fail with a deprecation error"),
|
|
?assertMatch({error, _}, riakc_pb_socket:mapred(PB, [{<<"lists">>, <<"mine">>}],
|
|
[{link, <<"items">>, '_', true}])),
|
|
|
|
%% revoke only the list_keys permission
|
|
lager:info("Revoking list-keys, checking that full-bucket mapred fails"),
|
|
ok = rpc:call(Node, riak_core_console, revoke, [["riak_kv.list_keys", "on",
|
|
"default", "hello", "from", Username]]),
|
|
|
|
?assertMatch({error, <<"Permission", _/binary>>},
|
|
riakc_pb_socket:mapred_bucket(PB, <<"hello">>,
|
|
[{map, {jsfun, <<"Riak.mapValuesJson">>}, undefined, false},
|
|
{reduce, {jsfun,
|
|
<<"Riak.reduceSum">>},
|
|
undefined, true}])),
|
|
|
|
case HaveIndexes of
|
|
false -> ok;
|
|
true ->
|
|
%% 2i permission test
|
|
lager:info("Checking 2i is disallowed"),
|
|
?assertMatch({error, <<"Permission", _/binary>>},
|
|
riakc_pb_socket:get_index(PB, <<"hello">>,
|
|
{binary_index,
|
|
"name"},
|
|
<<"John">>)),
|
|
|
|
lager:info("Granting 2i permissions, checking that results come back"),
|
|
ok = rpc:call(Node, riak_core_console, grant, [["riak_kv.index", "on",
|
|
"default", "to", Username]]),
|
|
|
|
%% don't actually have any indexes
|
|
?assertMatch({ok, ?INDEX_RESULTS{keys=[]}},
|
|
riakc_pb_socket:get_index(PB, <<"hello">>,
|
|
{binary_index,
|
|
"name"},
|
|
<<"John">>)),
|
|
ok
|
|
end,
|
|
|
|
%% get/set bprops
|
|
lager:info("Checking that get_bucket is disallowed"),
|
|
?assertMatch({error, <<"Permission", _/binary>>},
|
|
riakc_pb_socket:get_bucket(PB, <<"mybucket">>)),
|
|
|
|
lager:info("Granting riak_core.get_bucket, checking that get_bucket succeeds"),
|
|
ok = rpc:call(Node, riak_core_console, grant, [["riak_core.get_bucket", "on",
|
|
"default", "mybucket", "to", Username]]),
|
|
|
|
?assertEqual(3, proplists:get_value(n_val, element(2,
|
|
riakc_pb_socket:get_bucket(PB,
|
|
<<"mybucket">>)))),
|
|
|
|
lager:info("Checking that set_bucket is disallowed"),
|
|
?assertMatch({error, <<"Permission", _/binary>>},
|
|
riakc_pb_socket:set_bucket(PB, <<"mybucket">>, [{n_val, 5}])),
|
|
|
|
lager:info("Granting set_bucket, checking that set_bucket succeeds"),
|
|
ok = rpc:call(Node, riak_core_console, grant, [["riak_core.set_bucket", "on",
|
|
"default", "mybucket", "to", Username]]),
|
|
?assertEqual(ok,
|
|
riakc_pb_socket:set_bucket(PB, <<"mybucket">>, [{n_val, 5}])),
|
|
|
|
?assertEqual(5, proplists:get_value(n_val, element(2,
|
|
riakc_pb_socket:get_bucket(PB,
|
|
<<"mybucket">>)))),
|
|
|
|
%%%%%%%%%%%%
|
|
%%% bucket type tests
|
|
%%%%%%%%%%%%
|
|
|
|
%% create a new type
|
|
rt:create_and_activate_bucket_type(Node, <<"mytype">>, [{n_val, 3}]),
|
|
rt:wait_until_bucket_type_status(<<"mytype">>, active, Nodes),
|
|
rt:wait_until_bucket_type_visible(Nodes, <<"mytype">>),
|
|
|
|
lager:info("Checking that get on a new bucket type is disallowed"),
|
|
?assertMatch({error, <<"Permission", _/binary>>}, riakc_pb_socket:get(PB,
|
|
{<<"mytype">>,
|
|
<<"hello">>},
|
|
<<"world">>)),
|
|
|
|
lager:info("Granting get on the new bucket type, checking that it succeeds"),
|
|
ok = rpc:call(Node, riak_core_console, grant, [["riak_kv.get", "on",
|
|
"mytype", "hello", "to", Username]]),
|
|
|
|
?assertMatch({error, notfound}, riakc_pb_socket:get(PB, {<<"mytype">>,
|
|
<<"hello">>},
|
|
<<"world">>)),
|
|
|
|
lager:info("Checking that permisisons are unchanged on the default bucket type"),
|
|
%% can't read from the default bucket, though
|
|
?assertMatch({error, <<"Permission", _/binary>>}, riakc_pb_socket:get(PB,
|
|
<<"hello">>,
|
|
<<"world">>)),
|
|
|
|
lager:info("Checking that put on the new bucket type is disallowed"),
|
|
?assertMatch({error, <<"Permission", _/binary>>},
|
|
riakc_pb_socket:put(PB,
|
|
riakc_obj:new({<<"mytype">>, <<"hello">>}, <<"world">>,
|
|
<<"howareyou">>))),
|
|
|
|
lager:info("Granting put on a bucket in the new bucket type, checking that it succeeds"),
|
|
ok = rpc:call(Node, riak_core_console, grant, [["riak_kv.put", "on",
|
|
"mytype", "hello", "to", Username]]),
|
|
|
|
?assertEqual(ok,
|
|
riakc_pb_socket:put(PB,
|
|
riakc_obj:new({<<"mytype">>, <<"hello">>}, <<"world">>,
|
|
<<"howareyou">>))),
|
|
|
|
?assertEqual(ok,
|
|
riakc_pb_socket:put(PB,
|
|
riakc_obj:new({<<"mytype">>,
|
|
<<"hello">>}, <<"drnick">>,
|
|
<<"Hi, everybody">>))),
|
|
|
|
?assertMatch({ok, _Obj}, riakc_pb_socket:get(PB, {<<"mytype">>,
|
|
<<"hello">>},
|
|
<<"world">>)),
|
|
|
|
lager:info("Revoking get/put on the new bucket type, checking that they fail"),
|
|
ok = rpc:call(Node, riak_core_console, revoke, [["riak_kv.get,riak_kv.put", "on",
|
|
"mytype", "hello", "from", Username]]),
|
|
|
|
?assertMatch({error, <<"Permission", _/binary>>}, riakc_pb_socket:get(PB,
|
|
{<<"mytype">>,
|
|
<<"hello">>},
|
|
<<"world">>)),
|
|
|
|
?assertMatch({error, <<"Permission", _/binary>>},
|
|
riakc_pb_socket:put(PB,
|
|
riakc_obj:new({<<"mytype">>, <<"hello">>}, <<"world">>,
|
|
<<"howareyou">>))),
|
|
|
|
lager:info("Checking that list keys is disallowed on the new bucket type"),
|
|
?assertMatch({error, <<"Permission", _/binary>>},
|
|
riakc_pb_socket:list_keys(PB, {<<"mytype">>, <<"hello">>})),
|
|
|
|
lager:info("Granting list keys on a bucket in the new type, checking that it works"),
|
|
ok = rpc:call(Node, riak_core_console, grant, [["riak_kv.list_keys", "on",
|
|
"mytype", "hello", "to", Username]]),
|
|
|
|
?assertEqual([<<"drnick">>, <<"world">>], lists:sort(element(2, riakc_pb_socket:list_keys(PB,
|
|
{<<"mytype">>,
|
|
<<"hello">>})))),
|
|
|
|
lager:info("Creating another bucket type"),
|
|
%% create a new type
|
|
rt:create_and_activate_bucket_type(Node, <<"mytype2">>, [{allow_mult, true}]),
|
|
rt:wait_until_bucket_type_status(<<"mytype2">>, active, Nodes),
|
|
rt:wait_until_bucket_type_visible(Nodes, <<"mytype2">>),
|
|
|
|
lager:info("Checking that get on the new type is disallowed"),
|
|
?assertMatch({error, <<"Permission", _/binary>>}, riakc_pb_socket:get(PB,
|
|
{<<"mytype2">>,
|
|
<<"hello">>},
|
|
<<"world">>)),
|
|
|
|
lager:info("Granting get/put on all buckets in the new type, checking that get/put works"),
|
|
%% do a wildcard grant
|
|
ok = rpc:call(Node, riak_core_console, grant,
|
|
[["riak_kv.get,riak_kv.put", "on",
|
|
"mytype2", "to", Username]]),
|
|
|
|
?assertMatch({error, notfound}, riakc_pb_socket:get(PB, {<<"mytype2">>,
|
|
<<"hello">>},
|
|
<<"world">>)),
|
|
|
|
?assertEqual(ok,
|
|
riakc_pb_socket:put(PB,
|
|
riakc_obj:new({<<"mytype2">>,
|
|
<<"embiggen">>},
|
|
<<"the noblest heart">>,
|
|
<<"true">>))),
|
|
|
|
?assertEqual(ok,
|
|
riakc_pb_socket:put(PB,
|
|
riakc_obj:new({<<"mytype2">>,
|
|
<<"cromulent">>},
|
|
<<"perfectly">>,
|
|
<<"true">>))),
|
|
|
|
lager:info("Checking that list buckets is disallowed on the new type"),
|
|
?assertMatch({error, <<"Permission", _/binary>>},
|
|
riakc_pb_socket:list_buckets(PB, <<"mytype2">>)),
|
|
|
|
lager:info("Granting list buckets on the new type, checking that it succeeds"),
|
|
ok = rpc:call(Node, riak_core_console, grant, [["riak_kv.list_buckets", "on",
|
|
"mytype2", "to", Username]]),
|
|
|
|
?assertMatch([<<"cromulent">>, <<"embiggen">>], lists:sort(element(2,
|
|
riakc_pb_socket:list_buckets(PB,
|
|
<<"mytype2">>)))),
|
|
|
|
|
|
%% get/set bucket type props
|
|
|
|
lager:info("Checking that get/set bucket-type properties are disallowed"),
|
|
?assertMatch({error, <<"Permission", _/binary>>}, riakc_pb_socket:set_bucket_type(PB, <<"mytype2">>,
|
|
[{n_val, 5}])),
|
|
|
|
?assertMatch({error, <<"Permission", _/binary>>},
|
|
riakc_pb_socket:get_bucket_type(PB, <<"mytype2">>)),
|
|
|
|
lager:info("Granting get on bucket type props, checking it succeeds and put still fails"),
|
|
ok = rpc:call(Node, riak_core_console, grant,
|
|
[["riak_core.get_bucket_type", "on", "mytype2", "to", Username]]),
|
|
|
|
?assertEqual(3, proplists:get_value(n_val,
|
|
element(2, riakc_pb_socket:get_bucket_type(PB,
|
|
<<"mytype2">>)))),
|
|
|
|
?assertMatch({error, <<"Permission", _/binary>>}, riakc_pb_socket:set_bucket_type(PB, <<"mytype2">>,
|
|
[{n_val, 5}])),
|
|
|
|
lager:info("Granting set on bucket type props, checking it succeeds"),
|
|
ok = rpc:call(Node, riak_core_console, grant,
|
|
[["riak_core.set_bucket_type", "on", "mytype2", "to", Username]]),
|
|
|
|
riakc_pb_socket:set_bucket_type(PB, <<"mytype2">>, [{n_val, 5}]),
|
|
|
|
?assertEqual(5, proplists:get_value(n_val,
|
|
element(2, riakc_pb_socket:get_bucket_type(PB,
|
|
<<"mytype2">>)))),
|
|
|
|
riakc_pb_socket:set_bucket_type(PB, <<"mytype2">>, [{n_val, 3}]),
|
|
|
|
crdt_tests(Nodes, PB),
|
|
|
|
riakc_pb_socket:stop(PB),
|
|
|
|
group_test(Node, Port, CertDir).
|
|
|
|
group_test(Node, Port, CertDir) ->
|
|
%%%%%%%%%%%%%%%%
|
|
%% test groups
|
|
%%%%%%%%%%%%%%%%
|
|
|
|
lager:info("Creating a new group"),
|
|
%% create a new group
|
|
ok = rpc:call(Node, riak_core_console, add_group, [["group"]]),
|
|
|
|
lager:info("Creating a user in the group"),
|
|
%% create a new user in that group
|
|
ok = rpc:call(Node, riak_core_console, add_user, [["myuser", "groups=group"]]),
|
|
|
|
|
|
lager:info("Granting get/put/delete on a bucket type to the group, checking those requests work"),
|
|
|
|
%% do a wildcard grant
|
|
grant(Node,["riak_kv.get,riak_kv.put,riak_kv.delete", "on", "mytype2",
|
|
"to", "group"]),
|
|
|
|
%% trust 'myuser' on localhost
|
|
ok = rpc:call(Node, riak_core_console, add_source, [["myuser", "127.0.0.1/32",
|
|
"trust"]]),
|
|
|
|
{ok, PB} = riakc_pb_socket:start("127.0.0.1", Port,
|
|
[{credentials, "myuser", "password"},
|
|
{cacertfile,
|
|
filename:join([CertDir, "rootCA/cert.pem"])}]),
|
|
|
|
?assertMatch({error, notfound}, (riakc_pb_socket:get(PB, {<<"mytype2">>,
|
|
<<"hello">>},
|
|
<<"world">>))),
|
|
?assertEqual(ok,
|
|
(riakc_pb_socket:put(PB,
|
|
riakc_obj:new({<<"mytype2">>, <<"hello">>}, <<"world">>,
|
|
<<"howareyou">>)))),
|
|
|
|
{ok, Obj} = riakc_pb_socket:get(PB, {<<"mytype2">>,
|
|
<<"hello">>},
|
|
<<"world">>),
|
|
riakc_pb_socket:delete_obj(PB, Obj),
|
|
|
|
?assertMatch({error, notfound}, (riakc_pb_socket:get(PB, {<<"mytype2">>,
|
|
<<"hello">>},
|
|
<<"world">>))),
|
|
|
|
lager:info("riak search should not be running with security enabled"),
|
|
?assertMatch({error, <<"Riak Search 1.0 is deprecated", _/binary>>},
|
|
riakc_pb_socket:search(PB, <<"index">>, <<"foo:bar">>)),
|
|
|
|
riakc_pb_socket:stop(PB),
|
|
pass.
|
|
|
|
grant(Node, Args) ->
|
|
ok = rpc:call(Node, riak_core_console, grant, [Args]).
|
|
|
|
crdt_tests([Node|_]=Nodes, PB) ->
|
|
Username = [2361,2367,2344,2381,2342,2368],
|
|
|
|
%% rt:create_and_activate
|
|
lager:info("Creating bucket types for CRDTs"),
|
|
|
|
Types = [{<<"counters">>, counter, riakc_counter:to_op(riakc_counter:increment(5, riakc_counter:new()))},
|
|
{<<"sets">>, set, riakc_set:to_op(riakc_set:add_element(<<"foo">>, riakc_set:new()))},
|
|
{<<"maps">>, map, riakc_map:to_op(riakc_map:update({<<"bar">>, counter}, fun(In) -> riakc_counter:increment(In) end, riakc_map:new()))}],
|
|
[ begin
|
|
rt:create_and_activate_bucket_type(Node, BType, [{allow_mult, true}, {datatype, DType}]),
|
|
rt:wait_until_bucket_type_status(BType, active, Nodes),
|
|
rt:wait_until_bucket_type_visible(Nodes, BType)
|
|
end || {BType, DType, _Op} <- Types ],
|
|
|
|
lager:info("Checking that CRDT fetch is denied"),
|
|
|
|
[ ?assertDenied(riakc_pb_socket:fetch_type(PB, {BType, <<"bucket">>}, <<"key">>))
|
|
|| {BType, _, _} <- Types ],
|
|
|
|
lager:info("Granting CRDT riak_kv.get, checking that fetches succeed"),
|
|
|
|
[ grant(Node, ["riak_kv.get", "on", binary_to_list(Type), "to", Username]) || {Type, _, _} <- Types ],
|
|
|
|
[ ?assertEqual({error, {notfound, DType}},
|
|
riakc_pb_socket:fetch_type(PB, {BType, <<"bucket">>}, <<"key">>)) ||
|
|
{BType, DType, _} <- Types ],
|
|
|
|
lager:info("Checking that CRDT update is denied"),
|
|
|
|
[ ?assertDenied(riakc_pb_socket:update_type(PB, {BType, <<"bucket">>}, <<"key">>, Op))
|
|
|| {BType, _, Op} <- Types ],
|
|
|
|
|
|
lager:info("Granting CRDT riak_kv.put, checking that updates succeed"),
|
|
|
|
[ grant(Node, ["riak_kv.put", "on", binary_to_list(Type), "to", Username]) || {Type, _, _} <- Types ],
|
|
|
|
[ ?assertEqual(ok, riakc_pb_socket:update_type(PB, {BType, <<"bucket">>}, <<"key">>, Op))
|
|
|| {BType, _, Op} <- Types ],
|
|
|
|
ok.
|
|
|
|
map_object_value(RiakObject, A, B) ->
|
|
riak_kv_mapreduce:map_object_value(RiakObject, A, B).
|
|
|
|
reduce_set_union(List, A) ->
|
|
riak_kv_mapreduce:reduce_set_union(List, A).
|
|
|
|
mapred_modfun_input(Pipe, _Args, _Timeout) ->
|
|
riak_pipe:queue_work(Pipe, {{<<"hello">>, <<"world">>}, {struct, []}}),
|
|
riak_pipe:eoi(Pipe).
|