blob: f5d3f70f34becaa2111b21a148e425039cd0f382 [file] [log] [blame]
/*****************************************************************************\
* users.c - Slurm REST API acct user http operations handlers
*****************************************************************************
* Copyright (C) SchedMD LLC.
*
* This file is part of Slurm, a resource management program.
* For details, see <https://slurm.schedmd.com/>.
* Please also read the included file: DISCLAIMER.
*
* Slurm is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* In addition, as a special exception, the copyright holders give permission
* to link the code of portions of this program with the OpenSSL library under
* certain conditions as described in each individual source file, and
* distribute linked combinations including the two. You must obey the GNU
* General Public License in all respects for all of the code used other than
* OpenSSL. If you modify file(s) with this exception, you may extend this
* exception to your version of the file(s), but you are not obligated to do
* so. If you do not wish to do so, delete this exception statement from your
* version. If you delete this exception statement from all source files in
* the program, then also delete it here.
*
* Slurm is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along
* with Slurm; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
\*****************************************************************************/
#include "slurm/slurmdb.h"
#include "src/common/list.h"
#include "src/common/log.h"
#include "src/common/read_config.h"
#include "src/common/xassert.h"
#include "src/common/xmalloc.h"
#include "src/common/xstring.h"
#include "src/slurmrestd/openapi.h"
#include "src/slurmrestd/operations.h"
#include "api.h"
static void _dump_users(ctxt_t *ctxt, slurmdb_user_cond_t *user_cond)
{
list_t *user_list = NULL;
if (!db_query_list(ctxt, &user_list, slurmdb_users_get, user_cond))
DUMP_OPENAPI_RESP_SINGLE(OPENAPI_USERS_RESP, user_list, ctxt);
FREE_NULL_LIST(user_list);
}
static int _foreach_wckey_set_def(void *x, void *key)
{
slurmdb_wckey_rec_t *wckey = x;
xassert(wckey->name && wckey->name[0]);
wckey->is_def = (wckey == key);
return SLURM_SUCCESS;
}
static int _match_wckey_name(void *x, void *key)
{
slurmdb_wckey_rec_t *wckey = x;
char *name = key;
xassert(name && name[0]);
xassert(wckey->name && wckey->name[0]);
return !xstrcmp(wckey->name, name);
}
static int _foreach_update_user(void *x, void *arg)
{
slurmdb_user_rec_t *user = x;
ctxt_t *ctxt = arg;
list_t *user_list = NULL;
bool modify;
slurmdb_assoc_cond_t assoc_cond = {};
slurmdb_user_cond_t user_cond = {
.assoc_cond = &assoc_cond,
.with_deleted = true,
};
assoc_cond.user_list = list_create(NULL);
if (user->old_name && !user->old_name[0]) {
/*
* Ignore empty string since the client is not asking to change
* user from nothing.
*/
xfree(user->old_name);
}
if (user->old_name) {
list_append(assoc_cond.user_list, user->old_name);
if (db_query_list_xempty(ctxt, &user_list, slurmdb_users_get,
&user_cond))
goto cleanup;
if (!user_list || list_is_empty(user_list)) {
resp_error(ctxt, ESLURM_USER_ID_MISSING, __func__,
"Unable to rename non-existent user %s to %s",
user->old_name, user->name);
goto cleanup;
}
list_flush(assoc_cond.user_list);
FREE_NULL_LIST(user_list);
list_append(assoc_cond.user_list, user->name);
if (db_query_list_xempty(ctxt, &user_list, slurmdb_users_get,
&user_cond))
goto cleanup;
if (user_list && !list_is_empty(user_list)) {
resp_error(ctxt, ESLURM_DATA_AMBIGUOUS_MODIFY, __func__,
"Unable to rename user %s to existing %s",
user->old_name, user->name);
goto cleanup;
}
list_append(assoc_cond.user_list, user->old_name);
modify = true;
} else {
list_append(assoc_cond.user_list, user->name);
if (db_query_list_xempty(ctxt, &user_list, slurmdb_users_get,
&user_cond))
goto cleanup;
if (!user_list || list_is_empty(user_list)) {
modify = false;
} else if (list_count(user_list) > 1) {
resp_error(ctxt, ESLURM_DATA_AMBIGUOUS_MODIFY, __func__,
"ambiguous user modify request");
goto cleanup;
} else {
modify = true;
}
}
if (user->assoc_list && list_count(user->assoc_list)) {
resp_warn(ctxt, __func__, "User %s associations list ignored. They must be set via the associations end point.",
user->name);
FREE_NULL_LIST(user->assoc_list);
}
if (user->coord_accts && list_count(user->coord_accts)) {
resp_warn(ctxt, __func__, "User %s coordinators list ignored. They must be set via the coordinators or accounts end point.",
user->name);
FREE_NULL_LIST(user->coord_accts);
}
if (user->default_wckey && user->default_wckey[0]) {
/*
* User may provide wckey as default but not in the list of
* wckeys. Automatically add it.
*/
slurmdb_wckey_rec_t *key = NULL;
if (user->wckey_list) {
key = list_find_first(user->wckey_list,
_match_wckey_name,
user->default_wckey);
/* Avoid any conflicting defaults */
list_for_each(user->wckey_list, _foreach_wckey_set_def,
key);
}
if (!key) {
if (!user->wckey_list)
user->wckey_list =
list_create(slurmdb_destroy_wckey_rec);
key = xmalloc(sizeof(*key));
slurmdb_init_wckey_rec(key, false);
key->name = xstrdup(user->default_wckey);
key->user = xstrdup(user->name);
key->cluster = xstrdup(slurm_conf.cluster_name);
key->is_def = true;
list_append(user->wckey_list, key);
}
}
if (user->flags & SLURMDB_USER_FLAG_DELETED) {
resp_warn(ctxt, __func__,
"Ignoring request to set flag: DELETED");
user->flags &= ~SLURMDB_USER_FLAG_DELETED;
}
if (!modify) {
debug("%s: [%s] add user request: user=%s",
__func__, ctxt->id, user->name);
if (!user_list)
user_list = list_create(NULL);
list_append(user_list, user);
db_query_rc(ctxt, user_list, slurmdb_users_add);
} else {
debug("%s: [%s] modifying user request: user=%s%s%s",
__func__,
ctxt->id, (user->old_name ? user->old_name : ""),
(user->old_name ? "->" : ""), user->name);
db_modify_rc(ctxt, &user_cond, user, slurmdb_users_modify);
}
cleanup:
FREE_NULL_LIST(assoc_cond.user_list);
FREE_NULL_LIST(user_list);
return ctxt->rc ? SLURM_ERROR : SLURM_SUCCESS;
}
extern int update_users(ctxt_t *ctxt, bool commit, list_t *user_list)
{
if (!(list_for_each(user_list, _foreach_update_user, ctxt) < 0) &&
!ctxt->rc && commit)
db_query_commit(ctxt);
return ctxt->rc;
}
static void _add_users_association(ctxt_t *ctxt,
slurmdb_add_assoc_cond_t *add_assoc,
slurmdb_user_rec_t *user)
{
int rc = SLURM_SUCCESS;
char *ret_str = NULL;
/* Mimic sacctmgr/user_functions.c _set_add_cond() */
if (user->default_acct) {
if (!add_assoc->acct_list)
add_assoc->acct_list = list_create(xfree_ptr);
if (list_is_empty(add_assoc->acct_list))
slurm_addto_char_list(add_assoc->acct_list,
user->default_acct);
}
/* Mimic sacctmgr/user_functions.c _set_add_cond() */
if (user->default_wckey) {
if (!add_assoc->wckey_list)
add_assoc->wckey_list = list_create(xfree_ptr);
if (list_is_empty(add_assoc->wckey_list))
slurm_addto_char_list(add_assoc->wckey_list,
user->default_wckey);
}
/*
* Mimic sacctmgr/user_functions.c sacctmgr_add_user().
*
* The add_assoc.user_list is a requirement by itself, already handled
* by the add_parse_req() in the USERS_ADD_COND parser array.
*
* But add_assoc.[acct|wckey]_list are required at least one or the
* other. Since there's no way to do it at present via data_parser
* functionality, checking the requirement here, mimicking sacctmgr.
*
* These checks should arguably happen inside the API call.
*/
if (!list_count(add_assoc->acct_list) &&
!list_count(add_assoc->wckey_list)) {
rc = ESLURM_DATA_AMBIGUOUS_QUERY;
resp_error(ctxt, rc, __func__,
"Need name of account(s) or wckey(s) to add");
goto cleanup;
}
/*
* sacctmgr performs a uid_to_string() check on the requested user list
* and emits a warning letting user decide to proceed if any fails,
* but there's no such commit check mechanism for rest POST.
*/
errno = 0;
ret_str = slurmdb_users_add_cond(ctxt->db_conn, add_assoc, user);
if ((rc = errno))
resp_error(ctxt, rc, "slurmdb_users_add_cond", "%s", ret_str);
else
db_query_commit(ctxt);
cleanup:
DUMP_OPENAPI_RESP_SINGLE(OPENAPI_USERS_ADD_COND_RESP_STR, ret_str,
ctxt);
xfree(ret_str);
}
static void _update_users(ctxt_t *ctxt)
{
openapi_resp_single_t resp = {0};
openapi_resp_single_t *resp_ptr = &resp;
if (!DATA_PARSE(ctxt->parser, OPENAPI_USERS_RESP, resp, ctxt->query,
ctxt->parent_path)) {
list_t *user_list = resp.response;
update_users(ctxt, true, user_list);
FREE_NULL_LIST(user_list);
}
FREE_OPENAPI_RESP_COMMON_CONTENTS(resp_ptr);
}
static void _delete_user(ctxt_t *ctxt, char *user_name)
{
slurmdb_assoc_cond_t assoc_cond = {0};
slurmdb_user_cond_t user_cond = {
.assoc_cond = &assoc_cond,
};
list_t *user_list = NULL;
assoc_cond.user_list = list_create(NULL);
list_append(assoc_cond.user_list, user_name);
if (!db_query_list(ctxt, &user_list, slurmdb_users_remove, &user_cond))
DUMP_OPENAPI_RESP_SINGLE(OPENAPI_USERS_REMOVED_RESP, user_list,
ctxt);
if (!ctxt->rc)
db_query_commit(ctxt);
FREE_NULL_LIST(user_list);
FREE_NULL_LIST(assoc_cond.user_list);
}
static void _parse_add_users_assoc(ctxt_t *ctxt)
{
openapi_resp_users_add_cond_t resp = {0};
openapi_resp_users_add_cond_t *resp_ptr = &resp;
if (!DATA_PARSE(ctxt->parser, OPENAPI_USERS_ADD_COND_RESP, resp,
ctxt->query, ctxt->parent_path)) {
_add_users_association(ctxt, resp_ptr->add_assoc,
resp_ptr->user);
slurmdb_destroy_add_assoc_cond(resp_ptr->add_assoc);
slurmdb_destroy_user_rec(resp_ptr->user);
}
FREE_OPENAPI_RESP_COMMON_CONTENTS(resp_ptr);
}
/* based on sacctmgr_list_user() */
extern int op_handler_users(ctxt_t *ctxt)
{
if (ctxt->method == HTTP_REQUEST_GET) {
slurmdb_user_cond_t *user_cond = NULL;
if (!DATA_PARSE(ctxt->parser, USER_CONDITION_PTR, user_cond,
ctxt->query, ctxt->parent_path))
_dump_users(ctxt, user_cond);
slurmdb_destroy_user_cond(user_cond);
} else if (ctxt->method == HTTP_REQUEST_POST) {
_update_users(ctxt);
} else {
resp_error(ctxt, ESLURM_REST_INVALID_QUERY, __func__,
"Unsupported HTTP method requested: %s",
get_http_method_string(ctxt->method));
}
return SLURM_SUCCESS;
}
extern int op_handler_user(ctxt_t *ctxt)
{
openapi_user_param_t params = {0};
if (DATA_PARSE(ctxt->parser, OPENAPI_USER_PARAM, params,
ctxt->parameters, ctxt->parent_path))
goto cleanup;
if (!params.name || !params.name[0]) {
resp_error(ctxt, ESLURM_USER_ID_MISSING, __func__,
"User name must be provided for singular query");
} else if (ctxt->method == HTTP_REQUEST_GET) {
openapi_user_query_t query = {0};
slurmdb_assoc_cond_t assoc_cond = {0};
slurmdb_user_cond_t user_cond = {
.assoc_cond = &assoc_cond,
};
if (DATA_PARSE(ctxt->parser, OPENAPI_USER_QUERY, query,
ctxt->query, ctxt->parent_path))
goto cleanup;
user_cond.with_deleted = query.with_deleted;
user_cond.with_assocs = query.with_assocs;
user_cond.with_coords = query.with_coords;
user_cond.with_wckeys = query.with_wckeys;
assoc_cond.user_list = list_create(NULL);
list_append(assoc_cond.user_list, params.name);
_dump_users(ctxt, &user_cond);
FREE_NULL_LIST(assoc_cond.user_list);
} else if (ctxt->method == HTTP_REQUEST_DELETE) {
_delete_user(ctxt, params.name);
} else {
resp_error(ctxt, ESLURM_REST_INVALID_QUERY, __func__,
"Unsupported HTTP method requested: %s",
get_http_method_string(ctxt->method));
}
cleanup:
xfree(params.name);
return SLURM_SUCCESS;
}
extern int op_handler_users_association(ctxt_t *ctxt)
{
if (ctxt->method == HTTP_REQUEST_POST)
_parse_add_users_assoc(ctxt);
else
resp_error(ctxt, ESLURM_REST_INVALID_QUERY, __func__,
"Unsupported HTTP method requested: %s",
get_http_method_string(ctxt->method));
return SLURM_SUCCESS;
}