Skip to content

File authenticate_owner.cpp

File List > cubrid > src > object > authenticate_owner.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_owner.cpp - changing owner opertion
 */

#include "authenticate.h"

#include "db.h"
#include "dbtype.h"
#include "transform.h"
#include "object_accessor.h"
#include "execute_statement.h" /* do_get_serial_obj_id */
#include "execute_schema.h"
#include "jsp_cl.h"
#include "trigger_manager.h"
#include "transaction_cl.h"
#include "schema_manager.h" /* sm_downcase_name */
#include "object_primitive.h" /* pr_clear_value */
#include "authenticate_access_auth.hpp" /* au_object_revoke_all_privileges */

int
au_check_owner (DB_VALUE *creator_val)
{
  MOP creator;
  DB_SET *groups;
  int ret_val = ER_FAILED;

  creator = db_get_object (creator_val);

  if (ws_is_same_object (creator, Au_user) || au_is_dba_group_member (Au_user))
    {
      ret_val = NO_ERROR;
    }
  else if (au_get_set (Au_user, "groups", &groups) == NO_ERROR)
    {
      if (set_ismember (groups, creator_val))
    {
      ret_val = NO_ERROR;
    }
      set_free (groups);
    }

  return ret_val;
}

/*
 * au_change_serial_owner() - Change serial object's owner
 *   return: error code
 *   object(in/out): serial object whose owner is to be changed
 *   new_owner(in): new owner
 *   is_auto_increment(in): check if auto increment serial name change is necessary
 */
int
au_change_serial_owner (MOP serial_mop, MOP owner_mop, bool by_class_owner_change)
{
  DB_OBJECT *serial_owner_obj = NULL;
  DB_OBJECT *serial_class_mop = NULL;
  DB_OBJECT *serial_obj = NULL;
  DB_IDENTIFIER serial_obj_id;
  DB_OTMPL *obj_tmpl = NULL;
  DB_VALUE value;
  const char *serial_name = NULL;
  char serial_new_name[DB_MAX_SERIAL_NAME_LENGTH] = { '\0' };
  const char *att_name = NULL;
  char *owner_name = NULL;
  char downcase_owner_name[DB_MAX_USER_LENGTH] = { '\0' };
  bool is_abort = false;
  int save = 0;
  int error = NO_ERROR;

  if (!serial_mop || !owner_mop)
    {
      ERROR_SET_WARNING (error, ER_OBJ_INVALID_ARGUMENTS);
      return error;
    }

  OID_SET_NULL (&serial_obj_id);

  if (!au_is_dba_group_member (Au_user))
    {
      ERROR_SET_WARNING_1ARG (error, ER_AU_DBA_ONLY, "change_serial_owner");
      return error;
    }

  AU_DISABLE (save);

  /*
   * class, serial, and trigger distinguish user schema by unique_name (user_specified_name).
   * so if the owner of class, serial, trigger changes, the unique_name must also change.
   */

  /*
   * after serial.next_value, the currect value maybe changed, but cub_cas
   * still hold the old value. To get the new value. we need decache it
   * then refetch it from server again.
   */
  assert (WS_ISDIRTY (serial_mop) == false);

  ws_decache (serial_mop);

  /* no need to get last version for serial - actually, the purpose is AU_FETCH_WRITE, so fetch type is not relevant;
   * the last version will be locked and it will be considered visibile only if delid is not set */
  error = au_fetch_instance_force (serial_mop, NULL, AU_FETCH_WRITE, LC_FETCH_MVCC_VERSION);
  if (error != NO_ERROR)
    {
      ASSERT_ERROR ();
      goto end;
    }

  if (!by_class_owner_change)
    {
      /* It can be checked as one of unique_name, class_name, and att_name. */
      error = obj_get (serial_mop, SERIAL_ATTR_ATTR_NAME, &value);
      if (error != NO_ERROR)
    {
      ASSERT_ERROR ();
      goto end;
    }

      if (!DB_IS_NULL (&value))
    {
      ERROR_SET_WARNING (error, ER_AU_CANT_ALTER_OWNER_OF_AUTO_INCREMENT);
      goto end;
    }
    }

  /* Check if the owner to be changed is the same. */
  error = obj_get (serial_mop, SERIAL_ATTR_OWNER, &value);
  if (error != NO_ERROR)
    {
      ASSERT_ERROR ();
      goto end;
    }

  if (DB_VALUE_DOMAIN_TYPE (&value) != DB_TYPE_OBJECT || (serial_owner_obj = db_get_object (&value)) == NULL)
    {
      /* Unable to get attribute value. */
      ERROR_SET_WARNING (error, ER_OBJ_INVALID_ARGUMENTS);
      goto end;
    }

  if (ws_is_same_object (serial_owner_obj, owner_mop))
    {
      goto end;
    }

  error = obj_get (serial_mop, SERIAL_ATTR_NAME, &value);
  if (error != NO_ERROR)
    {
      ASSERT_ERROR ();
      goto end;
    }

  if (!DB_IS_STRING (&value) || (serial_name = db_get_string (&value)) == NULL)
    {
      /* Unable to get attribute value. */
      ERROR_SET_WARNING (error, ER_OBJ_INVALID_ARGUMENTS);
      goto end;
    }

  owner_name = au_get_user_name (owner_mop);
  if (!owner_name)
    {
      ASSERT_ERROR_AND_SET (error);
      goto end;
    }
  sm_downcase_name (owner_name, downcase_owner_name, DB_MAX_USER_LENGTH);
  db_ws_free_and_init (owner_name);

  sprintf (serial_new_name, "%s.%s", downcase_owner_name, serial_name);

  serial_class_mop = sm_find_class (CT_SERIAL_NAME);
  if (serial_class_mop == NULL)
    {
      ERROR_SET_ERROR (error, ER_QPROC_DB_SERIAL_NOT_FOUND);
      goto end;
    }

  if (do_get_serial_obj_id (&serial_obj_id, serial_class_mop, serial_new_name) != NULL)
    {
      ERROR_SET_ERROR_1ARG (error, ER_QPROC_SERIAL_ALREADY_EXIST, serial_new_name);
      goto end;
    }

  obj_tmpl = dbt_edit_object (serial_mop);
  if (!obj_tmpl)
    {
      ASSERT_ERROR ();
      goto end;
    }

  /* unique_name */
  db_make_string (&value, serial_new_name);
  error = dbt_put_internal (obj_tmpl, SERIAL_ATTR_UNIQUE_NAME, &value);
  if (error != NO_ERROR)
    {
      ASSERT_ERROR ();
      is_abort = true;
      goto end;
    }

  /* owner */
  db_make_object (&value, owner_mop);
  error = dbt_put_internal (obj_tmpl, SERIAL_ATTR_OWNER, &value);
  if (error != NO_ERROR)
    {
      ASSERT_ERROR ();
      is_abort = true;
      goto end;
    }

  /* updated_time */
  error = db_update_otmpl_timestamp (obj_tmpl);
  if (error != NO_ERROR)
    {
      ASSERT_ERROR ();
      is_abort = true;
      goto end;
    }

  serial_obj = dbt_finish_object (obj_tmpl);
  if (!serial_obj)
    {
      ASSERT_ERROR_AND_SET (error);
      is_abort = true;
      goto end;
    }

end:
  if (is_abort && obj_tmpl)
    {
      dbt_abort_object (obj_tmpl);
    }

  AU_ENABLE (save);

  return error;
}

/*
 * au_change_trigger_owner - This changes the owning user of a trigger.
 *                           This should be called only by the DBA.
 *   return: error code
 *   trigger(in): trigger whose owner is to change
 *   owner(in):new owner
 */
int
au_change_trigger_owner (MOP trigger_mop, MOP owner_mop)
{
  DB_OBJECT *trigger_owner_obj = NULL;
  DB_OBJECT *target_class_obj = NULL;
  SM_CLASS *target_class = NULL;
  DB_VALUE value;
  const char *trigger_old_name = NULL;
  char trigger_new_name[DB_MAX_IDENTIFIER_LENGTH] = { '\0' };
  char *owner_name = NULL;
  char downcase_owner_name[DB_MAX_USER_LENGTH] = { '\0' };
  int save = 0;
  int error = NO_ERROR;

  if (!trigger_mop || !owner_mop)
    {
      ERROR_SET_WARNING (error, ER_OBJ_INVALID_ARGUMENTS);
      return error;
    }

  if (!au_is_dba_group_member (Au_user))
    {
      ERROR_SET_WARNING_1ARG (error, ER_AU_DBA_ONLY, "change_trigger_owner");
      return error;
    }

  AU_DISABLE (save);

  /*
   * class, serial, and trigger distinguish user schema by unique_name (user_specified_name).
   * so if the owner of class, serial, trigger changes, the unique_name must also change.
   */

  error = obj_get (trigger_mop, TR_ATT_UNIQUE_NAME, &value);
  if (error != NO_ERROR)
    {
      ASSERT_ERROR ();
      goto end;
    }

  if (!DB_IS_STRING (&value) || (trigger_old_name = db_get_string (&value)) == NULL)
    {
      ERROR_SET_WARNING_1ARG (error, ER_TR_TRIGGER_NOT_FOUND, "");
      goto end;
    }

  /* Check if the owner to be changed is the same. */
  error = obj_get (trigger_mop, TR_ATT_OWNER, &value);
  if (error != NO_ERROR)
    {
      ASSERT_ERROR ();
      goto end;
    }

  if (DB_VALUE_DOMAIN_TYPE (&value) != DB_TYPE_OBJECT || (trigger_owner_obj = db_get_object (&value)) == NULL)
    {
      /* Unable to get attribute value. */
      ERROR_SET_WARNING (error, ER_OBJ_INVALID_ARGUMENTS);
      goto end;
    }

  if (ws_is_same_object (trigger_owner_obj, owner_mop))
    {
      goto end;
    }

  /* TO BE: It is necessary to check the permission of the target class of the owner to be changed. */
#if 0
  error = obj_get (target_class_obj, TR_ATT_CLASS, &value);
  if (error != NO_ERROR)
    {
      ASSERT_ERROR ();
      goto end;
    }

  error = au_fetch_class (target_class_obj, &target_class, AU_FETCH_READ, AU_SELECT);
  if (error != NO_ERROR)
    {
      ASSERT_ERROR ();
      goto end;
    }
#endif

  owner_name = au_get_user_name (owner_mop);
  if (!owner_name)
    {
      ASSERT_ERROR_AND_SET (error);
      goto end;
    }
  sm_downcase_name (owner_name, downcase_owner_name, DB_MAX_USER_LENGTH);
  db_ws_free_and_init (owner_name);

  snprintf (trigger_new_name, SM_MAX_IDENTIFIER_LENGTH, "%s.%s", downcase_owner_name,
        sm_remove_qualifier_name (trigger_old_name));

  error = tr_rename_trigger (trigger_mop, trigger_new_name, false, true);
  if (error != NO_ERROR)
    {
      ASSERT_ERROR ();
      goto end;
    }

  /* owner */
  db_make_object (&value, owner_mop);
  error = obj_set (trigger_mop, TR_ATT_OWNER, &value);
  if (error != NO_ERROR)
    {
      ASSERT_ERROR ();

      /* abort on error */
      if (tr_rename_trigger (trigger_mop, trigger_old_name, false, true) != NO_ERROR)
    {
      assert (false);
    }
    }

end:
  AU_ENABLE (save);

  return error;
}

/*
 * au_get_class_owner - This access the user object that is the owner of
 *                      the class.
 *   return: user object (owner of class_)
 *   classmop(in): class object
 */
MOP
au_get_class_owner (MOP classmop)
{
  MOP owner = NULL;
  SM_CLASS *class_;

  /* should we disable authorization here ? */
  /*
   * should we allow the class owner to be known if the active user doesn't
   * have select authorization ?
   */

  if (au_fetch_class_force (classmop, &class_, AU_FETCH_READ) == NO_ERROR)
    {
      owner = class_->owner;
      if (owner == NULL)
    {
      /* shouln't we try to update the class if the owner wasn't set ? */
      owner = Au_dba_user;
    }
    }

  return (owner);
}

int
au_change_class_owner (MOP class_mop, MOP owner_mop)
{
  int error = NO_ERROR;
  int i;
  MOP *sub_partitions = NULL, owner = NULL;
  int is_partition = DB_NOT_PARTITIONED_CLASS;
  bool has_savepoint = false;
  const char *table_name;

  /* change the owner of a partition */
  error = sm_partitioned_class_type (class_mop, &is_partition, NULL, &sub_partitions);
  if (error != NO_ERROR)
    {
      ASSERT_ERROR ();
      return error;
    }

  if (is_partition == DB_PARTITION_CLASS)   /* if partition; error */
    {
      ERROR_SET_ERROR_1ARG (error, ER_NOT_ALLOWED_ACCESS_TO_PARTITION, "");
      goto end;
    }

  if (is_partition == DB_PARTITIONED_CLASS) /* if partitioned class; do actions to all partitions */
    {
      error = tran_system_savepoint (UNIQUE_PARTITION_SAVEPOINT_OWNER);
      if (error != NO_ERROR)
    {
      ASSERT_ERROR ();
      goto end;
    }

      has_savepoint = true;

      for (i = 0; sub_partitions[i]; i++)
    {
      owner = au_get_class_owner (sub_partitions[i]);
      if (owner == NULL)
        {
          ASSERT_ERROR_AND_SET (error);
          goto end;
        }

      table_name = sm_get_ch_name (sub_partitions[i]);
      if (table_name == NULL)
        {
          ASSERT_ERROR_AND_SET (error);
          goto end;
        }

      error = au_object_owner_change_privileges (DB_OBJECT_CLASS, sub_partitions[i], owner, owner_mop, table_name);
      if (error != NO_ERROR)
        {
          ASSERT_ERROR_AND_SET (error);
          goto end;
        }

      error = au_change_class_owner_including_partitions (sub_partitions[i], owner_mop);
      if (error != NO_ERROR)
        {
          ASSERT_ERROR ();
          goto end;
        }
    }
    }

  /* when changing the owner,  all rights are transferred to the new owner. */
  owner = au_get_class_owner (class_mop);
  if (owner == NULL)
    {
      ASSERT_ERROR_AND_SET (error);
      goto end;
    }

  table_name = sm_get_ch_name (class_mop);
  if (table_name == NULL)
    {
      ASSERT_ERROR_AND_SET (error);
      goto end;
    }

  error = au_object_owner_change_privileges (DB_OBJECT_CLASS, class_mop, owner, owner_mop, table_name);
  if (error != NO_ERROR)
    {
      ASSERT_ERROR_AND_SET (error);
      goto end;
    }

  /* change the owner of a class */
  error = au_change_class_owner_including_partitions (class_mop, owner_mop);
  if (error != NO_ERROR)
    {
      ASSERT_ERROR ();
    }

end:
  if (sub_partitions)
    {
      free_and_init (sub_partitions);
    }

  if (has_savepoint && error != NO_ERROR && error != ER_LK_UNILATERALLY_ABORTED)
    {
      tran_abort_upto_system_savepoint (UNIQUE_PARTITION_SAVEPOINT_OWNER);
    }

  return error;
}

/*
 * au_change_sp_owner -
 *   return: error code
 *   parser(in):
 *   sp(in):
 *   owner(in):
 */
int
au_change_sp_owner (PARSER_CONTEXT *parser, MOP sp, MOP owner)
{
  int error = NO_ERROR;
  int save, lang;
  const char *name_str = NULL, *owner_str = NULL, *target_cls = NULL;
  char new_name_str[DB_MAX_IDENTIFIER_LENGTH];
  new_name_str[0]= '\0';
  char downcase_owner_name[DB_MAX_USER_LENGTH];
  downcase_owner_name[0] = '\0';
  DB_VALUE value, name_value, owner_value, sp_lang_val, target_cls_val;

  db_make_null (&value);
  db_make_null (&name_value);
  db_make_null (&owner_value);
  db_make_null (&sp_lang_val);
  db_make_null (&target_cls_val);

  assert (sp != NULL && owner != NULL);

  AU_DISABLE (save);

  /* change _db_stored_procedure */
  error = obj_get (sp, SP_ATTR_SP_NAME, &name_value);
  if (error != NO_ERROR)
    {
      goto end;
    }
  error = obj_get (owner, "name", &owner_value);
  if (error != NO_ERROR)
    {
      goto end;
    }

  name_str = db_get_string (&name_value);
  owner_str = db_get_string (&owner_value);

  sm_downcase_name (owner_str, downcase_owner_name, DB_MAX_USER_LENGTH);
  sprintf (new_name_str, "%s.%s", downcase_owner_name, name_str);

  /* change the unique_name */
  db_make_string (&value, new_name_str);
  error = obj_set (sp, SP_ATTR_UNIQUE_NAME, &value);
  if (error < 0)
    {
      goto end;
    }

  /* change the owner */
  db_make_object (&value, owner);
  error = obj_set (sp, SP_ATTR_OWNER, &value);
  if (error < 0)
    {
      goto end;
    }

  /* check lang */
  error = db_get (sp, SP_ATTR_LANG, &sp_lang_val);
  if (error != NO_ERROR)
    {
      goto end;
    }

  lang = db_get_int (&sp_lang_val);
  if (lang == SP_LANG_PLCSQL)
    {
      error = db_get (sp, SP_ATTR_TARGET_CLASS, &target_cls_val);
      if (error != NO_ERROR)
    {
      goto end;
    }
      target_cls = db_get_string (&target_cls_val);

      /* change _db_stored_procedure_code */
      error = alter_stored_procedure_code (parser, sp, target_cls, owner_str, 0);
      if (error != NO_ERROR)
    {
      goto end;
    }
    }

  error = db_update_obj_timestamp (sp);
  if (error != NO_ERROR)
    {
      goto end;
    }

end:

  AU_ENABLE (save);
  pr_clear_value (&value);
  pr_clear_value (&name_value);
  pr_clear_value (&owner_value);
  pr_clear_value (&sp_lang_val);
  pr_clear_value (&target_cls_val);

  return (error);
}

/*
 * au_change_sp_owner_with_transfer_privileges -
 *   return: error code
 *   parser(in):
 *   sp_mop(in):
 *   owner_mop(in):
 */
int
au_change_sp_owner_with_transfer_privileges (PARSER_CONTEXT *parser, MOP sp_mop, MOP new_owner_mop)
{
  int error = NO_ERROR;
  char unique_name[DB_MAX_IDENTIFIER_LENGTH + 1];
  unique_name[0] = '\0';
  PARSER_CONTEXT *dummy_parser = NULL;
  MOP owner = NULL;

  assert (sp_mop != NULL && new_owner_mop != NULL);

  if (!au_is_dba_group_member (Au_user))
    {
      error = ER_AU_DBA_ONLY;
      er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, error, 1, "change_sp_owner");
      goto end;
    }

  /* when changing the owner,  all rights are transferred to the new owner. */
  owner = jsp_get_owner (sp_mop);
  if (owner == NULL)
    {
      error = ER_FAILED;
      ASSERT_ERROR_AND_SET (error);
      goto end;
    }

  if (jsp_get_unique_name (sp_mop, unique_name, DB_MAX_IDENTIFIER_LENGTH) == NULL)
    {
      ASSERT_ERROR_AND_SET (error);
      goto end;
    }

  error = au_object_owner_change_privileges (DB_OBJECT_PROCEDURE, sp_mop, owner, new_owner_mop, unique_name);
  if (error != NO_ERROR)
    {
      ASSERT_ERROR_AND_SET (error);
      return error;
    }

  /* create dummy parser of change_sp_owner_method */
  if (parser == NULL)
    {
      dummy_parser = parser_create_parser ();
      if (dummy_parser == NULL)
    {
      error = ER_FAILED;
      goto end;
    }

      parser = dummy_parser;
    }

  /* change the owner of a sp */
  error = au_change_sp_owner (parser, sp_mop, new_owner_mop);

end:

  if (dummy_parser != NULL)
    {
      parser_free_parser (dummy_parser);
      parser = NULL;
    }

  return error;
}