File authenticate_access_user.cpp¶
File List > cubrid > src > object > authenticate_access_user.cpp
Go to the documentation of this file
/*
*
* Copyright 2016 CUBRID Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
/*
* authenticate_access_user.cpp -
*/
#include "authenticate.h"
#include "authenticate_cache.hpp"
#include "authenticate_access_auth.hpp"
#include "set_object.h"
#include "dbtype.h"
#include "error_manager.h"
#include "object_accessor.h"
#include "object_primitive.h"
#include "network_interface_cl.h"
#include "transaction_cl.h" /* TM_TRAN_READ_FETCH_VERSION */
#include "db.h"
#include "dbi.h"
#include "schema_manager.h"
#include "schema_system_catalog_constants.h"
/*
* USER/GROUP ACCESS
*/
static MOP au_make_user (const char *name);
static int au_add_direct_groups (DB_SET *new_groups, DB_VALUE *value);
static int au_compute_groups (MOP member, const char *name);
static int au_add_member_internal (MOP group, MOP member, int new_user);
/*
* au_find_user - Find a user object by name.
* return: user object
* user_name(in): name
*
* Note: The db_root class used to have a users attribute which was a set
* containing the object-id for all users.
* The users attribute has been eliminated for performance reasons.
* A query is now used to find the user. Since the user name is not
* case insensitive, it is set to upper case in the query. This forces
* user names to be set to upper case when users are added.
*/
MOP
au_find_user (const char *user_name)
{
MOP obj, user = NULL;
int save;
char *query;
DB_QUERY_RESULT *query_result;
DB_QUERY_ERROR query_error;
int error = NO_ERROR;
DB_VALUE user_val;
const char *qp1 = "select [%s] from [%s] where [name] = '%s' using index none";
MOP user_class;
char *upper_case_name;
size_t upper_case_name_size;
DB_VALUE user_name_string;
AU_USER_CACHE *user_cache = nullptr;
if (user_name == NULL)
{
return NULL;
}
{
/*
* To reduce unnecessary code execution,
* the current schema name can be used instead of the current user name.
*
* Returns the current user object when the user name is the same as the current schema name.
*
* Au_user_name cannot be used because it does not always store the current user name.
* When au_login_method () is called, Au_user_name is not changed.
*/
const char *sc_name = sc_current_schema_name ();
if (Au_user && sc_name && sc_name[0] != '\0' && intl_identifier_casecmp (sc_name, user_name) == 0)
{
return Au_user;
}
}
user = NULL;
upper_case_name_size = intl_identifier_upper_string_size (user_name);
upper_case_name = (char *) malloc (upper_case_name_size + 1);
if (upper_case_name == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, upper_case_name_size);
return NULL;
}
intl_identifier_upper (user_name, upper_case_name);
user_cache = Au_cache.find_user_cache_by_name (upper_case_name);
if (user_cache)
{
user = user_cache->user;
assert (user != NULL);
AU_SAVE_AND_ENABLE (save);
goto exit;
}
/* disable checking of internal authorization object access */
AU_DISABLE (save);
/*
* first try to find the user id by index. This is faster than
* a query, and will not get blocked out as a server request
* if the query processing resources are all used up at the moment.
* This is primarily of importance during logging in.
*/
user_class = db_find_class ("db_user");
if (user_class)
{
db_make_string (&user_name_string, upper_case_name);
user = obj_find_unique (user_class, "name", &user_name_string, AU_FETCH_READ);
}
error = er_errid ();
if (error != NO_ERROR)
{
if (error == ER_OBJ_OBJECT_NOT_FOUND)
{
er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_AU_INVALID_USER, 1, user_name);
}
goto exit;
}
if (error == NO_ERROR && !user)
{
/* proceed with the query version of the function */
query = (char *) malloc (strlen (qp1) + (2 * strlen (AU_USER_CLASS_NAME)) + strlen (upper_case_name) + 1);
if (query)
{
sprintf (query, qp1, AU_USER_CLASS_NAME, AU_USER_CLASS_NAME, upper_case_name);
lang_set_parser_use_client_charset (false);
error = db_compile_and_execute_local (query, &query_result, &query_error);
lang_set_parser_use_client_charset (true);
/* error is row count if not negative. */
if (error > 0)
{
if (db_query_first_tuple (query_result) == DB_CURSOR_SUCCESS)
{
if (db_query_get_tuple_value (query_result, 0, &user_val) == NO_ERROR)
{
if (DB_IS_NULL (&user_val))
{
obj = NULL;
}
else
{
obj = db_get_object (&user_val);
}
if (obj)
{
user = obj;
}
}
}
}
if (error >= 0)
{
db_query_end (query_result);
}
free_and_init (query);
}
}
if (user)
{
(void) Au_cache.make_user_cache (user_name, user, false);
}
exit:
AU_ENABLE (save);
if (upper_case_name)
{
free_and_init (upper_case_name);
}
return (user);
}
/*
* au_find_user_to_drop - Find a user object by name for dropping.
*
* return: error code
* user_name(in): name
* user(out): user object
*
* Note: X_Lock will be added on this user_object
We also need check whether ths user is an active user.
*/
int
au_find_user_to_drop (const char *user_name, MOP *user)
{
int error = NO_ERROR;
bool existed;
MOP user_class;
char *upper_case_name = NULL;
size_t upper_case_name_size;
DB_VALUE user_name_string;
*user = NULL;
/* check current user is DBA group */
if (Au_dba_user != NULL && !au_is_dba_group_member (Au_user))
{
error = ER_AU_DBA_ONLY;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 1, "drop_user");
goto exit;
}
upper_case_name_size = intl_identifier_upper_string_size (user_name);
upper_case_name = (char *) malloc (upper_case_name_size + 1);
if (upper_case_name == NULL)
{
error = ER_OUT_OF_VIRTUAL_MEMORY;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 1, upper_case_name_size);
goto exit;
}
intl_identifier_upper (user_name, upper_case_name);
/* find the user object */
user_class = db_find_class (AU_USER_CLASS_NAME);
if (user_class == NULL)
{
error = er_errid ();
assert (error != NO_ERROR);
goto exit;
}
db_make_string (&user_name_string, upper_case_name);
*user = obj_find_unique (user_class, "name", &user_name_string, AU_FETCH_WRITE);
if ((*user) == NULL)
{
error = er_errid ();
assert (error != NO_ERROR);
if (error == ER_OBJ_OBJECT_NOT_FOUND)
{
error = ER_AU_INVALID_USER;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 1, user_name);
}
goto exit;
}
/* check whether this user is an active user */
error = log_does_active_user_exist (upper_case_name, &existed);
if (error == NO_ERROR && existed)
{
error = ER_AU_NOT_ALLOW_TO_DROP_ACTIVE_USER;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 1, user_name);
}
exit:
if (upper_case_name)
{
free_and_init (upper_case_name);
}
if (error != NO_ERROR)
{
*user = NULL;
}
return error;
}
/*
* au_get_user_name - Shorthand function for getting name from user object.
* Must remember to free the string
* return: user name string
* obj(in): user object
*/
char *
au_get_user_name (MOP obj)
{
DB_VALUE value;
db_make_null (&value);
char *name = NULL;
{
MOP sc_owner;
const char *sc_name;
char upper_sc_name[DB_MAX_USER_LENGTH];
/*
* To reduce unnecessary code execution,
* the current schema name can be used instead of the current user name.
*
* Returns the current schema name if the user object is the same as the current user object.
*
* Au_user_name cannot be used because it does not always store the current user name.
* When au_login_method () is called, Au_user_name is not changed.
*/
sc_owner = sc_current_schema_owner ();
if (sc_owner && ws_is_same_object (sc_owner, obj))
{
sc_name = sc_current_schema_name ();
if (sc_name && sc_name[0] != '\0')
{
intl_identifier_upper (sc_name, upper_sc_name);
return ws_copy_string (upper_sc_name);
}
}
}
int error = obj_get (obj, "name", &value);
if (error == NO_ERROR)
{
if (DB_IS_STRING (&value) && !DB_IS_NULL (&value) && db_get_string (&value) != NULL)
{
name = ws_copy_string (db_get_string (&value));
}
}
db_value_clear (&value);
return name;
}
/*
* au_make_user - Create a new user object. Convert the name to upper case
* so that au_find_user can use a query.
* return: new user object
* name(in): user name
*/
static MOP
au_make_user (const char *name)
{
MOP uclass, aclass, user, auth;
DB_VALUE value;
DB_SET *set;
char *lname;
int error;
user = NULL;
uclass = sm_find_class (AU_USER_CLASS_NAME);
if (uclass == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_AU_MISSING_CLASS, 1, AU_USER_CLASS_NAME);
}
else
{
aclass = sm_find_class (AU_AUTH_CLASS_NAME);
if (aclass == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_AU_MISSING_CLASS, 1, AU_AUTH_CLASS_NAME);
}
else
{
int name_size;
user = obj_create (uclass);
name_size = intl_identifier_upper_string_size (name);
lname = (char *) malloc (name_size + 1);
if (lname)
{
intl_identifier_upper (name, lname);
db_make_string (&value, lname);
error = obj_set (user, "name", &value);
free_and_init (lname);
if (error != NO_ERROR)
{
if (!ER_IS_ABORTED_DUE_TO_DEADLOCK (error))
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_AU_ACCESS_ERROR, 2, AU_USER_CLASS_NAME, "name");
obj_delete (user);
}
user = NULL;
}
else
{
/* flattened group list */
set = set_create_basic ();
if (set == NULL)
{
goto memory_error;
}
db_make_set (&value, set);
obj_set (user, "groups", &value);
set_free (set);
/* direct group list */
set = set_create_basic ();
if (set == NULL)
{
goto memory_error;
}
db_make_set (&value, set);
obj_set (user, "direct_groups", &value);
set_free (set);
/* authorization object */
auth = obj_create (aclass);
if (auth == NULL)
{
goto memory_error;
}
db_make_object (&value, user);
/* back pointer to user object */
obj_set (auth, "owner", &value);
set = set_create_sequence (0);
if (set == NULL)
{
goto memory_error;
}
db_make_sequence (&value, set);
obj_set (auth, "grants", &value);
set_free (set);
db_make_object (&value, auth);
obj_set (user, "authorization", &value);
db_make_int (&value, true);
obj_set (user, AU_USER_ATTR_IS_LOGINABLE, &value);
db_make_int (&value, false);
obj_set (user,AU_USER_ATTR_IS_SYSTEM_CREATED, &value);
db_make_null (&value);
obj_set (user, "comment", &value);
au_set_user_timestamps (user);
}
}
else
{
goto memory_error;
}
}
}
return (user);
memory_error:
if (user != NULL)
{
obj_delete (user);
}
return NULL;
}
/*
* au_is_dba_group_member - Determines if a given user is the DBA/a member
* of the DBA group, or not
* return: true or false
* user(in): user object
*/
bool
au_is_dba_group_member (MOP user)
{
DB_SET *groups;
DB_VALUE value;
bool is_member = false;
LC_FETCH_VERSION_TYPE read_fetch_instance_version;
if (!user)
{
return false; /* avoid gratuitous er_set later */
}
if (ws_is_same_object (user, Au_dba_user))
{
return true;
}
/* Set fetch version type to read dirty version. */
read_fetch_instance_version = TM_TRAN_READ_FETCH_VERSION ();
db_set_read_fetch_instance_version (LC_FETCH_DIRTY_VERSION);
if (au_get_set (user, "groups", &groups) == NO_ERROR)
{
db_make_object (&value, Au_dba_user);
is_member = set_ismember (groups, &value);
set_free (groups);
}
/* Restore fetch version type. */
db_set_read_fetch_instance_version (read_fetch_instance_version);
return is_member;
}
bool
au_is_user_group_member (MOP group_user, MOP user)
{
DB_SET *groups;
DB_VALUE group_user_val;
int error = NO_ERROR;
db_make_null (&group_user_val);
if (!group_user || !user)
{
return false;
}
if (ws_is_same_object (group_user, user))
{
return true;
}
db_make_object (&group_user_val, group_user);
if (au_get_set (user, "groups", &groups) == NO_ERROR)
{
if (set_ismember (groups, &group_user_val))
{
set_free (groups);
return true;
}
}
else
{
assert (er_errid () != NO_ERROR);
}
if (groups)
{
set_free (groups);
}
return false;
}
/*
* TODO: return NO_ERROR in the previous implementation
* check_user_name
* return: error code
* name(in): proposed user name
*
* Note: This is made void for ansi compatibility. It previously insured
* that identifiers which were accepted could be parsed in the
* language interface.
*
* ANSI allows any character in an identifier. It also allows reserved
* words. In order to parse identifiers with non-alpha characters
* or that are reserved words, an escape syntax is definned with double
* quotes, "FROM", for example.
*/
#define check_user_name(name) NO_ERROR
/*
* au_add_user - Add a user object if one does not already exist.
* return: new or existing user object
* name(in): user name
* exists(out): flag set if user already existed
*
* Note: If one already exists, return it and set the flag.
* The db_root class used to have a user attribute which was a set
* containing the object-id for all users. The users attribute has been
* eliminated for performance reasons.
*
*/
MOP
au_add_user (const char *name, int *exists)
{
MOP user;
DB_VALUE value;
int save;
user = NULL;
if (Au_dba_user != NULL && !au_is_dba_group_member (Au_user))
{
er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_AU_DBA_ONLY, 1, "add_user");
}
else if (!check_user_name (name))
{
AU_DISABLE (save);
user = NULL;
if (exists != NULL)
{
*exists = 0;
}
if (name == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_AU_INVALID_USER_NAME, 1, "");
}
else
{
user = au_find_user (name);
if (user != NULL)
{
if (exists != NULL)
{
*exists = 1;
}
}
else
{
if (er_errid () != ER_AU_INVALID_USER)
{
AU_ENABLE (save);
return NULL;
}
/* clear error */
er_clear ();
user = au_make_user (name);
if (user != NULL)
{
db_make_object (&value, user);
if (Au_public_user != NULL)
{
/*
* every user is a member of the PUBLIC group,
* must make sure that the exported routines can't
* be used to violate this internal connection
*/
if (au_add_member_internal (Au_public_user, user, 1) != NO_ERROR)
{
er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_AU_CANT_ADD_MEMBER, 2, name, AU_PUBLIC_USER_NAME);
}
}
/*
* do we want to do this ?? - logically it is ok but this
* means we can't have DBA members since this would
* cause user hierarchy cycles.
*/
#if 0
if (Au_dba_user != NULL)
{
if (au_get_set (Au_dba_user, "groups", &dba_groups) == NO_ERROR)
{
db_make_object (&value, user);
if (!set_ismember (dba_groups, &value))
{
db_set_add (dba_groups, &value);
}
set_free (dba_groups);
}
}
#endif /* 0 */
}
}
}
AU_ENABLE (save);
}
if (user != NULL)
{
(void) Au_cache.make_user_cache (name, user, false);
}
return (user);
}
/*
* au_set_user_comment() - Set the comment string for a user.
* return: error code
* user(in): user object
* comment(in): a comment string
*/
int
au_set_user_comment (MOP user, const char *comment)
{
int error = NO_ERROR;
DB_VALUE value;
int len = 0, save;
AU_SAVE_AND_DISABLE (save);
if (!ws_is_same_object (Au_user, user) && !au_is_dba_group_member (Au_user))
{
error = ER_AU_UPDATE_FAILURE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
}
else
{
if (comment != NULL)
{
len = strlen (comment);
}
if (len == 0)
{
comment = NULL;
}
if (len > AU_MAX_COMMENT_CHARS)
{
error = ER_AU_COMMENT_OVERFLOW;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
}
else
{
db_make_string (&value, comment);
error = obj_set (user, "comment", &value);
pr_clear_value (&value);
}
}
AU_RESTORE (save);
return error;
}
int
au_set_user_timestamps (MOP user)
{
DB_VALUE current_datetime;
int save;
int error = NO_ERROR;
if (db_sys_datetime (¤t_datetime) != NO_ERROR)
{
return ER_FAILED;
}
AU_SAVE_AND_DISABLE (save);
if (obj_set (user, "created_time", ¤t_datetime) != NO_ERROR ||
obj_set (user, "updated_time", ¤t_datetime) != NO_ERROR)
{
error = ER_FAILED;
}
AU_RESTORE (save);
return error;
}
int
au_update_user_timestamp (MOP user)
{
DB_VALUE current_datetime;
int save;
int error = NO_ERROR;
if (db_sys_datetime (¤t_datetime) != NO_ERROR)
{
return ER_FAILED;
}
AU_SAVE_AND_DISABLE (save);
if (obj_set (user, "updated_time", ¤t_datetime) != NO_ERROR)
{
error = ER_FAILED;
}
AU_RESTORE (save);
return error;
}
/*
* GROUP HIERARCHY MAINTENANCE
*/
/*
* au_add_direct_groups - Add the group to the new_groups and then add
* the group's groups.
* return: error status
* new_groups(in):the set to add to
* value(in): the group to add
*/
static int
au_add_direct_groups (DB_SET *new_groups, DB_VALUE *value)
{
int error;
MOP group;
DB_SET *direct_groups;
int gcard, g;
DB_VALUE gvalue;
if ((error = db_set_add (new_groups, value)) == NO_ERROR)
{
if (DB_IS_NULL (value))
{
group = NULL;
}
else
{
group = db_get_object (value);
}
if ((error = au_get_set (group, "direct_groups", &direct_groups)) == NO_ERROR)
{
gcard = set_cardinality (direct_groups);
for (g = 0; g < gcard && !error; g++)
{
if ((error = set_get_element (direct_groups, g, &gvalue)) == NO_ERROR)
{
error = au_add_direct_groups (new_groups, &gvalue);
}
}
set_free (direct_groups);
}
}
return error;
}
/*
* au_compute_groups - Compute the groups attribute from the direct_groups
* attribute for those users that have a particular
* user/group in their groups attribute.
* return: error status
* member(in): the new member
* name(in): the new member name
*/
static int
au_compute_groups (MOP member, const char *name)
{
int error = NO_ERROR;
DB_SET *new_groups, *direct_groups;
DB_VALUE value, gvalue, user_val;
MOP user;
int g, gcard;
DB_SESSION *session;
DB_VALUE val[3];
STATEMENT_ID stmt_id;
DB_QUERY_RESULT *result = (DB_QUERY_RESULT *) 0;
const char *qstr = "select [d] from [db_user] [d] where ? in [d].[groups] or [d].[name] = ?;";
db_make_object (&val[0], member);
db_make_string (&val[1], name);
session = db_open_buffer (qstr);
if (!session)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
goto ret;
}
db_push_values (session, 2, val);
stmt_id = db_compile_statement (session);
if (stmt_id != 1)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
goto ret;
}
error = db_execute_statement_local (session, stmt_id, &result);
if (error < 0)
{
goto ret;
}
/* error is row count if not negative. */
if (error > 0)
{
error = NO_ERROR;
while (db_query_next_tuple (result) == DB_CURSOR_SUCCESS)
{
if ((error = db_query_get_tuple_value (result, 0, &user_val)) == NO_ERROR)
{
if (DB_IS_NULL (&user_val))
{
user = NULL;
}
else
{
user = db_get_object (&user_val);
}
new_groups = set_create_basic ();
if (new_groups)
{
if ((error = au_get_set (user, "direct_groups", &direct_groups)) == NO_ERROR)
{
/* compute closure */
gcard = set_cardinality (direct_groups);
for (g = 0; g < gcard && !error; g++)
{
if ((error = set_get_element (direct_groups, g, &gvalue)) == NO_ERROR)
{
error = au_add_direct_groups (new_groups, &gvalue);
}
}
set_free (direct_groups);
}
}
else
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
}
if (error == NO_ERROR)
{
db_make_set (&value, new_groups);
obj_set (user, "groups", &value);
}
if (new_groups)
{
set_free (new_groups);
}
}
}
}
ret:
if (result)
{
db_query_end (result);
}
if (session)
{
db_close_session (session);
}
return error;
}
/*
* au_add_member_internal - Add a member to a group and propagate the member
* to all affected sub-groups. If the call is
* for a new user, then no other user can be part of
* this user(group)
* return: error status
* group(in): group to get new member
* member(in): the new member
* new_user(in): whether the call is for a new user
*
* Note:
* the db_user class used to have a groups and a members attribute. the
* members attribute was eliminated as a performance improvement, but the
* direct_groups attribute has been added. both groups and direct_groups
* are sets. the direct_groups attribute indicates which groups the user/
* group is an immediate member of. the groups attribute indicates which
* groups the user/group is a member of (immediate or otherwise). the
* group attribute is a flattened set. when a user/group is added to a
* new group, the new group is added to both the direct_groups and groups
* attributes for the user/group. then that change is propagated to other
* users/groups.
* for example, if u1 is in g1 and g1 is added to g2, g2 is added to g1's
* direct_groups and groups attributes and g2 is also added to u1's groups
* attributes.
*/
static int
au_add_member_internal (MOP group, MOP member, int new_user)
{
int error = NO_ERROR;
DB_VALUE membervalue, member_name_val, groupvalue;
DB_SET *group_groups = NULL, *member_groups = NULL, *member_direct_groups = NULL;
int save;
const char *member_name = NULL;
AU_DISABLE (save);
db_make_object (&membervalue, member);
db_make_object (&groupvalue, group);
/*
* Skip some checks and processing for a new user/group because it
* can't have any members yet.
*/
if ((!new_user) && (group == member))
{
error = ER_AU_MEMBER_CAUSES_CYCLES;
er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, error, 0);
}
else
{
if ((!new_user) && ((error = au_get_set (group, "groups", &group_groups)) != NO_ERROR))
{
;
}
else
{
if ((!new_user) && (set_ismember (group_groups, &membervalue)))
{
error = ER_AU_MEMBER_CAUSES_CYCLES;
er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, error, 0);
}
else
{
error = obj_inst_lock (member, 1);
if (error == NO_ERROR)
{
error = au_get_set (member, "groups", &member_groups);
}
if (error == NO_ERROR)
{
error = au_get_set (member, "direct_groups", &member_direct_groups);
if (error == NO_ERROR)
{
if (new_user)
{
error = db_set_add (member_groups, &groupvalue);
if (error == NO_ERROR)
{
error = db_set_add (member_direct_groups, &groupvalue);
}
}
else if (!set_ismember (member_direct_groups, &membervalue))
{
error = db_get (member, "name", &member_name_val);
if (error == NO_ERROR)
{
if (DB_IS_NULL (&member_name_val))
{
member_name = NULL;
}
else
{
member_name = db_get_string (&member_name_val);
}
error = db_set_add (member_direct_groups, &groupvalue);
if (error == NO_ERROR)
{
error = au_compute_groups (member, member_name);
}
db_value_clear (&member_name_val);
}
}
set_free (member_direct_groups);
}
set_free (member_groups);
}
if (!new_user)
{
set_free (group_groups);
}
}
}
}
if (error == NO_ERROR)
{
error = au_update_user_timestamp (member);
}
AU_ENABLE (save);
return (error);
}
/*
* au_add_member - Add a member to a group and propagate the member to
* all affected sub-groups.
* return: error status
* group(in): group to get new member
* member(in): the new member
*/
int
au_add_member (MOP group, MOP member)
{
return au_add_member_internal (group, member, 0);
}
/*
* au_drop_member - Remove a member from a group.
* return: error code
* group(in): group with member to drop
* member(in): member to drop
*
* Note:
*
* The db_user class used to have a groups and a members attribute. The
* members attribute was eliminated as a performance improvement, but the
* direct_groups attribute has been added. Both groups and direct_groups
* are sets. The direct_groups attribute indicates which groups the user/
* group is an immediate member of. The groups attribute indicates which
* groups the user/group is a member of (immediate or otherwise). The
* groups attribute is a flattened set. When a user/group is dropped from
* a group, the group is removed from both the direct_groups and groups
* attributes for the user/group. Then that change is propagated to other
* users/groups.
* For example, if U1 is directly in G1 and G1 is directly in G2 and G1
* is dropped from G2, G2 is removed from G1's direct_groups and groups
* attributes and G2 is also removed from U1's groups attribute.
*/
int
au_drop_member (MOP group, MOP member)
{
int syserr = NO_ERROR, error = NO_ERROR;
DB_VALUE groupvalue, member_name_val;
DB_SET *groups = NULL, *member_groups = NULL, *member_direct_groups = NULL;
int save;
const char *member_name = NULL;
AU_DISABLE (save);
db_make_object (&groupvalue, group);
if ((syserr = au_get_set (member, "groups", &member_groups)) == NO_ERROR)
{
if (!set_ismember (member_groups, &groupvalue))
{
error = ER_AU_MEMBER_NOT_FOUND;
er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, error, 0);
}
else if ((error = au_get_set (group, "groups", &groups)) == NO_ERROR)
{
if (set_ismember (groups, &groupvalue))
{
error = ER_AU_MEMBER_NOT_FOUND;
er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, error, 0);
}
else
{
error = obj_inst_lock (member, 1);
if (error == NO_ERROR)
{
error = au_get_set (member, "direct_groups", &member_direct_groups);
}
if (error == NO_ERROR)
{
if ((error = db_get (member, "name", &member_name_val)) == NO_ERROR)
{
if (DB_IS_NULL (&member_name_val))
{
member_name = NULL;
}
else
{
member_name = db_get_string (&member_name_val);
}
if ((error = db_set_drop (member_direct_groups, &groupvalue)) == NO_ERROR)
{
error = au_compute_groups (member, member_name);
}
db_value_clear (&member_name_val);
}
set_free (member_direct_groups);
}
}
set_free (groups);
}
set_free (member_groups);
}
if (error == NO_ERROR)
{
error = au_update_user_timestamp (member);
}
AU_ENABLE (save);
return (error);
}
/*
* AU_OBJECT_CLASS_NAME
*
* This is list of class names that CUBRID manages as database objects
* Their existence is checked when dropping an user
*/
static const char *AU_OBJECT_CLASS_NAME[] =
{
CT_CLASS_NAME, /* AU_OBJECT_CLASS */
CT_TRIGGER_NAME, /* AU_OBJECT_TRIGGER */
CT_SERIAL_NAME, /* AU_OBJECT_SERIAL */
CT_SERVER_NAME, /* AU_OBJECT_SERVER */
CT_SYNONYM_NAME, /* AU_OBJECT_SYNONYM */
CT_STORED_PROC_NAME, /* AU_OBJECT_PROCEDURE */
NULL
};
/*
* au_drop_user - Drop a user from the database.
* return: error code
* user(in): user object
*
* Note:
*
* This should only be called with DBA privilidges.
* The db_user class used to have a groups and a members attribute. The
* members attribute was eliminated as a performance improvement, but the
* direct_groups attribute has been added. Both groups and direct_groups
* are sets. The direct_groups attribute indicates which groups the user/
* group is an immediate member of. The groups attribute indicates which
* groups the user/group is a member of (immediate or otherwise). The
* groups attribute is a flattened set. When a user/group is dropped,
* the user/group is removed from both the direct_groups and groups
* attributes for all users. For example, if U1 is directly in G1 and G1
* is directly in G2 and G1 is dropped, G1 & G2 are removed from U1's
* groups attribute and G1 is also removed from U1's direct_groups
* attribute.
*/
int
au_drop_user (MOP user)
{
int save;
DB_SESSION *session = NULL;
DB_VALUE val[2], user_val, gvalue, value, password_val;
STATEMENT_ID stmt_id;
int error = NO_ERROR;
DB_QUERY_RESULT *result;
MOP auser, password;
DB_SET *new_groups, *direct_groups;
int g, gcard, i;
DB_VALUE name;
char query_buf[1024];
AU_DISABLE (save);
if (Au_dba_user != NULL && !au_is_dba_group_member (Au_user))
{
error = ER_AU_DBA_ONLY;
er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, error, 1, "drop_user");
goto error;
}
/* check if user is dba/public or current user */
if (ws_is_same_object (user, Au_dba_user) || ws_is_same_object (user, Au_public_user)
|| ws_is_same_object (user, Au_user))
{
db_make_null (&name);
error = obj_get (user, "name", &name);
if (error != NO_ERROR)
{
goto error;
}
error = ER_AU_CANT_DROP_USER;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 1, db_get_string (&name));
goto error;
}
/* check if user owns class/vclass/trigger/serial/synonym */
for (i = 0; AU_OBJECT_CLASS_NAME[i] != NULL; i++)
{
sprintf (query_buf, "select count(*) from [%s] where [owner] = ?;", AU_OBJECT_CLASS_NAME[i]);
session = db_open_buffer (query_buf);
if (session == NULL)
{
goto error;
}
db_make_object (&val[0], user);
db_push_values (session, 1, &val[0]);
stmt_id = db_compile_statement (session);
if (stmt_id != 1)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
db_close_session (session);
goto error;
}
error = db_execute_statement_local (session, stmt_id, &result);
if (error < 0)
{
db_close_session (session);
goto error;
}
error = db_query_first_tuple (result);
if (error < 0)
{
db_query_end (result);
db_close_session (session);
goto error;
}
db_make_bigint (&value, 0);
error = db_query_get_tuple_value (result, 0, &value);
if (error != NO_ERROR)
{
db_query_end (result);
db_close_session (session);
goto error;
}
if (db_get_bigint (&value) > 0)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_AU_USER_HAS_DATABASE_OBJECTS, 0);
db_query_end (result);
db_close_session (session);
error = ER_AU_USER_HAS_DATABASE_OBJECTS;
goto error;
}
db_query_end (result);
db_close_session (session);
pr_clear_value (&val[0]);
}
/* propagate user deletion to groups */
db_make_object (&val[1], user);
session =
db_open_buffer ("update [db_user] [d] set "
"[d].[direct_groups] = [d].[direct_groups] - ? where ? in [d].[direct_groups];");
if (session == NULL)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
goto error;
}
new_groups = set_create_basic ();
if (new_groups)
{
error = db_set_add (new_groups, &val[1]);
if (error == NO_ERROR)
{
db_make_set (&val[0], new_groups);
db_push_values (session, 2, val);
stmt_id = db_compile_statement (session);
if (stmt_id == 1)
{
error = db_execute_statement_local (session, stmt_id, &result);
db_query_end (result);
}
else
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
}
}
/*
* We need to clear the host variable here to free the set. set_free()
* is not sufficient since the set referenced by new_groups may have
* be replaced as a result of tp_value_cast().
*/
pr_clear_value (&val[0]);
}
db_close_session (session);
if (error < NO_ERROR)
{
goto error;
}
session = db_open_buffer ("select [d] from [db_user] [d] where ? in [d].[groups];");
if (session == NULL)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
goto error;
}
db_push_values (session, 1, &val[1]);
stmt_id = db_compile_statement (session);
if (stmt_id == 1)
{
error = db_execute_statement_local (session, stmt_id, &result);
if (error > 0)
{
error = NO_ERROR;
while (error == NO_ERROR && db_query_next_tuple (result) == DB_CURSOR_SUCCESS)
{
error = db_query_get_tuple_value (result, 0, &user_val);
if (error == NO_ERROR)
{
if (DB_IS_NULL (&user_val))
{
auser = NULL;
}
else
{
auser = db_get_object (&user_val);
}
new_groups = set_create_basic ();
if (new_groups)
{
error = au_get_set (auser, "direct_groups", &direct_groups);
if (error == NO_ERROR)
{
/* compute closure */
gcard = set_cardinality (direct_groups);
for (g = 0; g < gcard && !error; g++)
{
error = set_get_element (direct_groups, g, &gvalue);
if (error == NO_ERROR)
{
error = au_add_direct_groups (new_groups, &gvalue);
}
}
set_free (direct_groups);
}
}
else
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
}
if (error == NO_ERROR)
{
db_make_set (&value, new_groups);
obj_set (auser, "groups", &value);
error = au_update_user_timestamp (auser);
}
if (new_groups)
{
set_free (new_groups);
}
}
}
}
db_query_end (result);
}
else
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
}
db_close_session (session);
if (error < NO_ERROR)
{
goto error;
}
db_make_null (&password_val);
error = obj_get (user, "password", &password_val);
if (!DB_IS_NULL (&password_val))
{
password = db_get_object (&password_val);
error = obj_delete (password);
if (error == NO_ERROR)
{
db_make_null (&password_val);
error = obj_set (user, "password", &password_val);
}
if (error != NO_ERROR)
{
goto error;
}
}
/* before deleting an user, all permissions are revoked. */
error = au_user_revoke_all_privileges (user);
if (error != NO_ERROR)
{
goto error;
}
error = au_delete_auth_of_dropping_user (user);
if (error != NO_ERROR)
{
goto error;
}
error = au_delete_authorizartion_of_dropping_user (user);
if (error != NO_ERROR)
{
goto error;
}
/*
* could go through classes created by this user and change ownership
* to the DBA ? - do this as the classes are referenced instead
*/
error = obj_delete (user);
if (error == NO_ERROR)
{
Au_cache.remove_user_cache (user);
}
error:
AU_ENABLE (save);
return error;
}