Skip to content

Commit

Permalink
DAOS-2061 tools: Add container overwrite-acl command (daos-stack#1803)
Browse files Browse the repository at this point in the history
This command allows a user with a RW container handle to
completely replace the container ACL with a new one. On
success, the tool fetches the new ACL and displays it for
confirmation.

- Add the daos cont overwrite-acl command.
- Update the daos tool help to cover overwrite-acl, as
  well as get-acl, which was previously missed.
- Add daos_cont_overwrite_acl() to client API. This is
  just a wrapper around daos_cont_set_prop().

Signed-off-by: Kris Jacque <[email protected]>
  • Loading branch information
kjacque authored Feb 10, 2020
1 parent 39a7118 commit 3968e74
Show file tree
Hide file tree
Showing 6 changed files with 312 additions and 36 deletions.
25 changes: 25 additions & 0 deletions src/client/api/container.c
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,31 @@ daos_cont_set_prop(daos_handle_t coh, daos_prop_t *prop, daos_event_t *ev)
return dc_task_schedule(task, true);
}

int
daos_cont_overwrite_acl(daos_handle_t coh, struct daos_acl *acl,
daos_event_t *ev)
{
daos_prop_t *prop;
int rc;

if (daos_acl_cont_validate(acl) != 0) {
D_ERROR("invalid acl parameter\n");
return -DER_INVAL;
}

prop = daos_prop_alloc(1);
if (prop == NULL)
return -DER_NOMEM;

prop->dpp_entries[0].dpe_type = DAOS_PROP_CO_ACL;
prop->dpp_entries[0].dpe_val_ptr = daos_acl_dup(acl);

rc = daos_cont_set_prop(coh, prop, ev);

daos_prop_free(prop);
return rc;
}

int
daos_cont_aggregate(daos_handle_t coh, daos_epoch_t epoch, daos_event_t *ev)
{
Expand Down
22 changes: 22 additions & 0 deletions src/include/daos_cont.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
extern "C" {
#endif

#include <daos_security.h>

/**
* DAOS_COO_RO opens the container for reading only. This flag conflicts with
* DAOS_COO_RW.
Expand Down Expand Up @@ -299,6 +301,26 @@ daos_cont_get_acl(daos_handle_t container, daos_prop_t **acl_prop,
int
daos_cont_set_prop(daos_handle_t coh, daos_prop_t *prop, daos_event_t *ev);

/**
* Overwrites the container ACL with a new one.
*
* \param[in] coh Container handle
* \param[in] acl New ACL to write
* \param[in] ev Completion event, it is optional and can be NULL.
* The function will run in blocking mode if \a ev is NULL.
*
* \return These values will be returned by \a ev::ev_error in
* non-blocking mode:
* 0 Success
* -DER_INVAL Invalid parameter
* -DER_NO_PERM Permission denied
* -DER_UNREACH Network is unreachable
* -DER_NO_HDL Invalid container handle
*/
int
daos_cont_overwrite_acl(daos_handle_t coh, struct daos_acl *acl,
daos_event_t *ev);

/**
* List the names of all user-defined container attributes.
*
Expand Down
148 changes: 113 additions & 35 deletions src/tests/suite/daos_container.c
Original file line number Diff line number Diff line change
Expand Up @@ -465,28 +465,111 @@ co_op_retry(void **state)
print_message("success\n");
}

static void
co_acl_get(test_arg_t *arg, struct daos_acl *exp_acl,
const char *exp_owner, const char *exp_owner_grp)
{
int rc;
daos_prop_t *acl_prop = NULL;
struct daos_prop_entry *entry;
struct daos_acl *actual_acl;

print_message("Getting the container ACL\n");
rc = daos_cont_get_acl(arg->coh, &acl_prop, NULL);
assert_int_equal(rc, 0);

assert_non_null(acl_prop);
assert_int_equal(acl_prop->dpp_nr, 3);

print_message("Checking ACL\n");
entry = daos_prop_entry_get(acl_prop, DAOS_PROP_CO_ACL);
if (entry == NULL || entry->dpe_val_ptr == NULL) {
print_message("ACL prop wasn't returned.\n");
assert_false(true); /* fail the test */
}
actual_acl = entry->dpe_val_ptr;
assert_int_equal(actual_acl->dal_ver, exp_acl->dal_ver);
assert_int_equal(actual_acl->dal_len, exp_acl->dal_len);
assert_memory_equal(actual_acl->dal_ace, exp_acl->dal_ace,
exp_acl->dal_len);

print_message("Checking owner\n");
entry = daos_prop_entry_get(acl_prop, DAOS_PROP_CO_OWNER);
if (entry == NULL || entry->dpe_str == NULL ||
strncmp(entry->dpe_str, exp_owner,
DAOS_ACL_MAX_PRINCIPAL_LEN)) {
print_message("Owner prop verification failed.\n");
assert_false(true); /* fail the test */
}

print_message("Checking owner-group\n");
entry = daos_prop_entry_get(acl_prop, DAOS_PROP_CO_OWNER_GROUP);
if (entry == NULL || entry->dpe_str == NULL ||
strncmp(entry->dpe_str, exp_owner_grp,
DAOS_ACL_MAX_PRINCIPAL_LEN)) {
print_message("Owner-group prop verification failed.\n");
assert_false(true); /* fail the test */
}

daos_prop_free(acl_prop);
}

static void
add_ace_with_perms(struct daos_acl **acl, enum daos_acl_principal_type type,
const char *name, uint64_t perms)
{
struct daos_ace *ace;
int rc;

ace = daos_ace_create(type, name);
assert_non_null(ace);
ace->dae_access_types = DAOS_ACL_ACCESS_ALLOW;
ace->dae_allow_perms = perms;

rc = daos_acl_add_ace(acl, ace);
assert_int_equal(rc, 0);

daos_ace_free(ace);
}

static void
co_acl(void **state)
{
test_arg_t *arg0 = *state;
test_arg_t *arg = NULL;
daos_prop_t *prop_in;
daos_prop_t *prop_out = NULL;
struct daos_prop_entry *entry;
daos_pool_info_t info = {0};
int rc;
const char *exp_owner = "fictionaluser@";
const char *exp_owner_grp = "admins@";
struct daos_acl *exp_acl, *actual_acl;
struct daos_acl *exp_acl;
struct daos_ace *ace;
uid_t uid;
char *user;

print_message("create container with access props, and verify.\n");
rc = test_setup((void **)&arg, SETUP_POOL_CONNECT, arg0->multi_rank,
DEFAULT_POOL_SIZE, NULL);
assert_int_equal(rc, 0);

/* Empty ACL is a weird case, but valid */
print_message("Case 1: initial non-default ACL/ownership\n");
/*
* Want to set up with a non-default ACL and owner/group.
* This ACL gives the effective user permissions to interact
* with the ACL. This is the bare minimum required to run the tests.
*/
uid = geteuid();
rc = daos_acl_uid_to_principal(uid, &user);
assert_int_equal(rc, 0);
assert_non_null(user);

exp_acl = daos_acl_create(NULL, 0);
assert_non_null(exp_acl);

add_ace_with_perms(&exp_acl, DAOS_ACL_USER, user,
DAOS_ACL_PERM_GET_ACL | DAOS_ACL_PERM_SET_ACL);
add_ace_with_perms(&exp_acl, DAOS_ACL_EVERYONE, NULL,
DAOS_ACL_PERM_READ);
assert_int_equal(daos_acl_cont_validate(exp_acl), 0);

/*
Expand Down Expand Up @@ -516,49 +599,44 @@ co_acl(void **state)
}
MPI_Barrier(MPI_COMM_WORLD);

print_message("Getting the container ACL\n");
rc = daos_cont_get_acl(arg->coh, &prop_out, NULL);
co_acl_get(arg, exp_acl, exp_owner, exp_owner_grp);

print_message("Case 2: overwrite ACL\n");
/*
* Modify the existing ACL - don't want to clobber the user entry
* though.
*/
rc = daos_acl_remove_ace(&exp_acl, DAOS_ACL_EVERYONE, NULL);
assert_int_equal(rc, 0);

assert_non_null(prop_out);
assert_int_equal(prop_out->dpp_nr, 3);
add_ace_with_perms(&exp_acl, DAOS_ACL_OWNER, NULL,
DAOS_ACL_PERM_GET_PROP | DAOS_ACL_PERM_SET_PROP |
DAOS_ACL_PERM_DEL_CONT);
add_ace_with_perms(&exp_acl, DAOS_ACL_GROUP, "testgroup@",
DAOS_ACL_PERM_GET_PROP | DAOS_ACL_PERM_READ |
DAOS_ACL_PERM_WRITE | DAOS_ACL_PERM_DEL_CONT);
add_ace_with_perms(&exp_acl, DAOS_ACL_GROUP, "testgroup2@",
DAOS_ACL_PERM_READ | DAOS_ACL_PERM_WRITE);

print_message("Checking ACL\n");
entry = daos_prop_entry_get(prop_out, DAOS_PROP_CO_ACL);
if (entry == NULL || entry->dpe_val_ptr == NULL) {
print_message("ACL prop wasn't returned.\n");
assert_int_equal(rc, 1); /* fail the test */
}
actual_acl = entry->dpe_val_ptr;
assert_int_equal(actual_acl->dal_ver, exp_acl->dal_ver);
assert_int_equal(actual_acl->dal_len, exp_acl->dal_len);
rc = daos_acl_get_ace_for_principal(exp_acl, DAOS_ACL_USER, user, &ace);
assert_int_equal(rc, 0);
ace->dae_allow_perms |= DAOS_ACL_PERM_SET_OWNER;

print_message("Checking owner\n");
entry = daos_prop_entry_get(prop_out, DAOS_PROP_CO_OWNER);
if (entry == NULL || entry->dpe_str == NULL ||
strncmp(entry->dpe_str, exp_owner,
DAOS_ACL_MAX_PRINCIPAL_LEN)) {
print_message("Owner prop verification failed.\n");
assert_int_equal(rc, 1); /* fail the test */
}
assert_int_equal(daos_acl_cont_validate(exp_acl), 0);

print_message("Checking owner-group\n");
entry = daos_prop_entry_get(prop_out, DAOS_PROP_CO_OWNER_GROUP);
if (entry == NULL || entry->dpe_str == NULL ||
strncmp(entry->dpe_str, exp_owner_grp,
DAOS_ACL_MAX_PRINCIPAL_LEN)) {
print_message("Owner-group prop verification failed.\n");
assert_int_equal(rc, 1); /* fail the test */
}
rc = daos_cont_overwrite_acl(arg->coh, exp_acl, NULL);
assert_int_equal(rc, 0);

co_acl_get(arg, exp_acl, exp_owner, exp_owner_grp);

if (arg->myrank == 0)
daos_mgmt_set_params(arg->group, -1, DMG_KEY_FAIL_LOC, 0,
0, NULL);
MPI_Barrier(MPI_COMM_WORLD);

daos_prop_free(prop_in);
daos_prop_free(prop_out);
daos_acl_free(exp_acl);
D_FREE(user);
test_teardown((void **)&arg);
}

Expand Down Expand Up @@ -673,7 +751,7 @@ static const struct CMUnitTest co_tests[] = {
co_properties, NULL, test_case_teardown},
{ "CONT7: retry CONT_{CLOSE,DESTROY,QUERY}",
co_op_retry, NULL, test_case_teardown},
{ "CONT8: get container ACL",
{ "CONT8: get/set container ACL",
co_acl, NULL, test_case_teardown},
{ "CONT9: container set prop",
co_set_prop, NULL, test_case_teardown},
Expand Down
21 changes: 20 additions & 1 deletion src/utils/daos.c
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ cont_op_parse(const char *str)
return CONT_ROLLBACK;
else if (strcmp(str, "get-acl") == 0)
return CONT_GET_ACL;
else if (strcmp(str, "overwrite-acl") == 0)
return CONT_OVERWRITE_ACL;
return -1;
}

Expand Down Expand Up @@ -465,6 +467,7 @@ common_op_parse_hdlr(int argc, char *argv[], struct cmd_args_s *ap)
{"properties", required_argument, NULL, DAOS_PROPERTIES_OPTION},
{"outfile", required_argument, NULL, 'O'},
{"verbose", no_argument, NULL, 'V'},
{"acl-file", required_argument, NULL, 'A'},
{NULL, 0, NULL, 0}
};
int rc;
Expand Down Expand Up @@ -639,6 +642,11 @@ common_op_parse_hdlr(int argc, char *argv[], struct cmd_args_s *ap)
case 'V':
ap->verbose = true;
break;
case 'A':
D_STRNDUP(ap->aclfile, optarg, strlen(optarg));
if (ap->aclfile == NULL)
D_GOTO(out_free, rc = RC_NO_HELP);
break;
case DAOS_PROPERTIES_OPTION:
/* parse properties to be set at cont create time */
/* alloc max */
Expand Down Expand Up @@ -718,6 +726,8 @@ common_op_parse_hdlr(int argc, char *argv[], struct cmd_args_s *ap)
}
if (ap->outfile != NULL)
D_FREE(ap->outfile);
if (ap->aclfile != NULL)
D_FREE(ap->aclfile);
D_FREE(cmdname);
return rc;
}
Expand Down Expand Up @@ -891,6 +901,9 @@ cont_op_hdlr(struct cmd_args_s *ap)
case CONT_GET_ACL:
rc = cont_get_acl_hdlr(ap);
break;
case CONT_OVERWRITE_ACL:
rc = cont_overwrite_acl_hdlr(ap);
break;
default:
break;
}
Expand Down Expand Up @@ -1055,6 +1068,8 @@ help_hdlr(struct cmd_args_s *ap)
" list-objects list all objects in container\n"
" list-obj\n"
" query query a container\n"
" get-acl get a container's ACL\n"
" overwrite-acl replace a container's ACL\n"
" stat get container statistics\n"
" list-attrs list container user-defined attributes\n"
" del-attr delete container user-defined attribute\n"
Expand Down Expand Up @@ -1122,7 +1137,11 @@ help_hdlr(struct cmd_args_s *ap)
"container options (snapshot and rollback-related):\n"
" --snap=NAME container snapshot (create/destroy-snap, rollback)\n"
" --epc=EPOCHNUM container epoch (destroy-snap, rollback)\n"
" --eprange=B-E container epoch range (destroy-snap)\n");
" --eprange=B-E container epoch range (destroy-snap)\n"
"container options (ACL-related):\n"
" --acl-file=PATH input file containing ACL (overwrite-acl)\n"
" --verbose verbose mode (get-acl)\n"
" --outfile=PATH write ACL to file (get-acl)\n");

fprintf(stream, "\n"
"object (obj) commands:\n"
Expand Down
Loading

0 comments on commit 3968e74

Please sign in to comment.