Skip to content

File authenticate_migration.cpp

File List > cubrid > src > object > authenticate_migration.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_migration.cpp -
 */
#include "authenticate.h"

#include "db.h"
#include "dbi.h"
#include "dbtype.h"

#include "authenticate_grant.hpp"
#include "object_accessor.h"
#include "object_print.h"
#include "set_object.h"
#include "schema_manager.h" /* sm_get_ch_name () */
#include "extract_schema.hpp" /* extract_context */
#include "printer.hpp" /* print_output */
#include "jsp_cl.h" /* jsp_get_owner () */

/*
 * CLASS_GRANT
 *
 * Maintains information about a desired grant request.
 */
typedef struct class_grant CLASS_GRANT;
struct class_grant
{
  struct class_grant *next;

  struct class_user *user;
  int cache;
};

/*
 * CLASS_USER
 *
 * Maintains information about a desired grant subject user.
 */
typedef struct class_user CLASS_USER;
struct class_user
{
  struct class_user *next;

  MOP obj;

  CLASS_GRANT *grants;
  int available_auth;
};

/*
 * CLASS_AUTH
 *
 * Maintains information about the grants on a particular class.
 */
typedef struct class_auth CLASS_AUTH;
struct class_auth
{

  MOP class_mop;
  MOP owner;
  CLASS_USER *users;
};

static CLASS_GRANT *make_class_grant (CLASS_USER *user, int cache);
static CLASS_USER *make_class_user (MOP user_obj);
static void free_class_grants (CLASS_GRANT *grants);
static void free_class_users (CLASS_USER *users);
static CLASS_USER *find_or_add_user (CLASS_AUTH *auth, MOP user_obj);
static int add_class_grant (CLASS_AUTH *auth, MOP source, MOP user, int cache);
static int build_class_grant_list (CLASS_AUTH *cl_auth, MOP class_mop);

static void issue_grant_statement (extract_context &ctxt, print_output &output_ctx, CLASS_AUTH *auth,
                   CLASS_GRANT *grant, int authbits, DB_OBJECT_TYPE obj_type);
static int class_grant_loop (extract_context &ctxt, print_output &output_ctx, CLASS_AUTH *auth,
                 DB_OBJECT_TYPE obj_type);

/*
 * MIGRATION SUPPORT
 *
 * These functions provide a way to dump the authorization catalog
 * as a sequence of CSQL statements.  When the statements are evaluated
 * by the interpreter, it will reconstruct the authorization catalog.
 */

/*
 * au_export_users - Generates a sequence of add_user and add_member method
 *                   calls that when evaluated, will re-create the current
 *                   user/group hierarchy.
 *   return: error code
 *   output_ctx(in/out): print context
 */
int
au_export_users (extract_context &ctxt, print_output &output_ctx)
{
  int error = NO_ERROR;
  DB_SET *direct_groups = NULL;
  DB_VALUE value, gvalue;
  MOP user = NULL, pwd = NULL;
  int g, gcard;
  const char *uname = NULL, *str = NULL, *gname = NULL, *comment = NULL;
  char passbuf[AU_MAX_PASSWORD_BUF] = { '\0' };
  char *query = NULL;
  size_t query_size;
  DB_QUERY_RESULT *query_result = NULL;
  DB_QUERY_ERROR query_error;
  DB_VALUE user_val;
  DB_VALUE user_group[2] = { 0, };
  const char *dba_query = "select [%s] from [%s];";
  const char *user_query = "select [%s] from [%s] where name='%s';";
  const char *group_query =
      "select u.name, [t].[g].name from [db_user] [u], TABLE([u].[groups]) [t]([g]) where [t].[g].name = '%s';";
  char encrypt_mode = ENCODE_PREFIX_DEFAULT;
  char *upper_case_name = NULL;
  size_t upper_case_name_size = 0;

  if (ctxt.is_dba_user || ctxt.is_dba_group_member)
    {
      query_size = strlen (dba_query) + strlen (AU_USER_CLASS_NAME) * 2;
      query = (char *) malloc (query_size);
      if (query == NULL)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, query_size);
      return ER_OUT_OF_VIRTUAL_MEMORY;
    }
      sprintf (query, dba_query, AU_USER_CLASS_NAME, AU_USER_CLASS_NAME);
    }
  else
    {
      upper_case_name_size = intl_identifier_upper_string_size (ctxt.login_user);
      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 ER_OUT_OF_VIRTUAL_MEMORY;
    }

      intl_identifier_upper (ctxt.login_user, upper_case_name);

      query_size = strlen (user_query) + strlen (AU_USER_CLASS_NAME) * 2 + strlen (upper_case_name);
      query = (char *) malloc (query_size);
      if (query == NULL)
    {
      if (upper_case_name != NULL)
        {
          free_and_init (upper_case_name);
        }

      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, query_size);
      return ER_OUT_OF_VIRTUAL_MEMORY;
    }

      sprintf (query, user_query, AU_USER_CLASS_NAME, AU_USER_CLASS_NAME, upper_case_name);
    }

  error = db_compile_and_execute_local (query, &query_result, &query_error);
  /* error is row count if not negative. */
  if (error < NO_ERROR)
    {
      if (upper_case_name != NULL)
    {
      free_and_init (upper_case_name);
    }

      if (query != NULL)
    {
      free_and_init (query);
    }

      if (query_result != NULL)
    {
      db_query_end (query_result);
      query_result = NULL;
    }
      return error;
    }

  while (db_query_next_tuple (query_result) == DB_CURSOR_SUCCESS)
    {
      if (db_query_get_tuple_value (query_result, 0, &user_val) != NO_ERROR)
    {
      continue;
    }

      if (DB_IS_NULL (&user_val))
    {
      user = NULL;
    }
      else
    {
      user = db_get_object (&user_val);
    }

      uname = au_get_user_name (user);
      strcpy (passbuf, "");
      encrypt_mode = ENCODE_PREFIX_DEFAULT;

      /* retrieve password */
      error = obj_get (user, "password", &value);
      if (error == NO_ERROR)
    {
      if (DB_IS_NULL (&value))
        {
          pwd = NULL;
        }
      else
        {
          pwd = db_get_object (&value);
        }

      if (pwd != NULL)
        {
          error = obj_get (pwd, "password", &value);
          if (error == NO_ERROR)
        {
          if (!DB_IS_NULL (&value) && DB_IS_STRING (&value))
            {
              /*
               * copy password string using malloc
               * to be consistent with encrypt_password
               */
              str = db_get_string (&value);
              if (IS_ENCODED_DES (str))
            {
              /* strip off the prefix so its readable */
              snprintf (passbuf, AU_MAX_PASSWORD_BUF - 1, "%s", str + 1);
              encrypt_mode = ENCODE_PREFIX_DES;
            }
              else if (IS_ENCODED_SHA1 (str))
            {
              /* strip off the prefix so its readable */
              snprintf (passbuf, AU_MAX_PASSWORD_BUF - 1, "%s", str + 1);
              encrypt_mode = ENCODE_PREFIX_SHA1;
            }
              else if (IS_ENCODED_SHA2_512 (str))
            {
              /* not strip off the prefix */
              snprintf (passbuf, AU_MAX_PASSWORD_BUF - 1, "%s", str);
              encrypt_mode = ENCODE_PREFIX_SHA2_512;
            }
              else if (strlen (str))
            {
              /* sha2 hashing with prefix */
              encrypt_password_sha2_512 (str, passbuf);
            }
              ws_free_string (str);
            }
        }
        }
    }

      /* retrieve comment */
      error = obj_get (user, "comment", &value);
      if (error == NO_ERROR)
    {
      if (DB_IS_NULL (&value))
        {
          comment = NULL;
        }
      else
        {
          comment = db_get_string (&value);
        }
    }

      if (error == NO_ERROR)
    {
      if (!ws_is_same_object (user, Au_dba_user) && !ws_is_same_object (user, Au_public_user))
        {
          if (!strlen (passbuf))
        {
          if (ctxt.is_dba_user || ctxt.is_dba_group_member)
            {
              output_ctx ("call [add_user]('%s', '') on class [db_root];\n", uname);
            }
        }
          else
        {
          if (ctxt.is_dba_user || ctxt.is_dba_group_member)
            {
              output_ctx ("call [add_user]('%s', '') on class [db_root] to [auser];\n", uname);
              if (encrypt_mode == ENCODE_PREFIX_DES)
            {
              output_ctx ("call [set_password_encoded]('%s') on [auser];\n", passbuf);
            }
              else
            {
              output_ctx ("call [set_password_encoded_sha1]('%s') on [auser];\n", passbuf);
            }
            }
        }
        }
      else
        {
          if (strlen (passbuf))
        {
          output_ctx ("call [find_user]('%s') on class [db_user] to [auser];\n", uname);
          if (encrypt_mode == ENCODE_PREFIX_DES)
            {
              output_ctx ("call [set_password_encoded]('%s') on [auser];\n", passbuf);
            }
          else
            {
              output_ctx ("call [set_password_encoded_sha1]('%s') on [auser];\n", passbuf);
            }
        }
        }

      /* export comment */
      if (comment != NULL && comment[0] != '\0')
        {
          output_ctx ("ALTER USER [%s] ", uname);
          help_print_describe_comment (output_ctx, comment);
          output_ctx (";\n");
        }
    }

      /* remember, these were allocated in the workspace */
      if (uname != NULL)
    {
      ws_free_string_and_init (uname);
    }
      if (comment != NULL)
    {
      ws_free_string_and_init (comment);
    }
    }

  /* group hierarchy */
  if (ctxt.is_dba_user || ctxt.is_dba_group_member)
    {
      if (db_query_first_tuple (query_result) == DB_CURSOR_SUCCESS)
    {
      do
        {
          if (db_query_get_tuple_value (query_result, 0, &user_val) != NO_ERROR)
        {
          continue;
        }

          if (DB_IS_NULL (&user_val))
        {
          user = NULL;
        }
          else
        {
          user = db_get_object (&user_val);
        }

          uname = au_get_user_name (user);
          if (uname == NULL)
        {
          continue;
        }

          if (au_get_set (user, "direct_groups", &direct_groups) != NO_ERROR)
        {
          ws_free_string_and_init (uname);
          continue;
        }

          gcard = set_cardinality (direct_groups);
          for (g = 0; g < gcard && !error; g++)
        {
          if (set_get_element (direct_groups, g, &gvalue) != NO_ERROR)
            {
              continue;
            }

          if (ws_is_same_object (db_get_object (&gvalue), Au_public_user))
            {
              continue;
            }

          error = obj_get (db_get_object (&gvalue), "name", &value);
          if (error != NO_ERROR)
            {
              continue;
            }

          if (DB_IS_NULL (&value))
            {
              gname = NULL;
            }
          else
            {
              gname = db_get_string (&value);
            }

          if (gname != NULL)
            {
              output_ctx ("call [find_user]('%s') on class [db_user] to [g_%s];\n", gname, gname);
              output_ctx ("call [add_member]('%s') on [g_%s];\n", uname, gname);
            }
        }

          set_free (direct_groups);
          if (uname != NULL)
        {
          ws_free_string_and_init (uname);
        }

          if (gname != NULL)
        {
          ws_free_string_and_init (gname);
        }
        }
      while (db_query_next_tuple (query_result) == DB_CURSOR_SUCCESS);
    }

      if (query_result != NULL)
    {
      db_query_end (query_result);
      query_result = NULL;
    }

      if (upper_case_name != NULL)
    {
      free_and_init (upper_case_name);
    }

      if (query != NULL)
    {
      free_and_init (query);
    }

    }
  else
    {
      // Initializing memory used by user query ("select [%s] from [%s] where name='%s';")
      if (query_result != NULL)
    {
      db_query_end (query_result);
      query_result = NULL;
    }

      if (upper_case_name != NULL)
    {
      free_and_init (upper_case_name);
    }

      if (query != NULL)
    {
      free_and_init (query);
    }

      upper_case_name_size = intl_identifier_upper_string_size (ctxt.login_user);
      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 ER_OUT_OF_VIRTUAL_MEMORY;
    }

      intl_identifier_upper (ctxt.login_user, upper_case_name);

      query_size = strlen (group_query) + strlen (upper_case_name);
      query = (char *) malloc (query_size);
      if (query == NULL)
    {
      if (upper_case_name != NULL)
        {
          free_and_init (upper_case_name);
        }

      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, query_size);
      return ER_OUT_OF_VIRTUAL_MEMORY;
    }

      sprintf (query, group_query, upper_case_name);

      error = db_compile_and_execute_local (query, &query_result, &query_error);

      /* error is row count if not negative. */
      if (error < NO_ERROR)
    {
      if (query_result != NULL)
        {
          db_query_end (query_result);
          query_result = NULL;
        }

      if (upper_case_name != NULL)
        {
          free_and_init (upper_case_name);
        }

      if (query != NULL)
        {
          free_and_init (query);
        }
      return error;
    }

      while (db_query_next_tuple (query_result) == DB_CURSOR_SUCCESS)
    {
      if (db_query_get_tuple_value (query_result, 0, &user_group[0]) != NO_ERROR)
        {
          continue;
        }

      if (db_query_get_tuple_value (query_result, 1, &user_group[1]) != NO_ERROR)
        {
          continue;
        }

      if (DB_IS_NULL (&user_group[0]) == false)
        {
          uname = db_get_string (&user_group[0]);
        }

      if (DB_IS_NULL (&user_group[1]) == false)
        {
          gname = db_get_string (&user_group[1]);
        }

      if (uname != NULL && gname != NULL)
        {
          output_ctx ("call [find_user]('%s') on class [db_user] to [g_%s];\n", gname, gname);
          output_ctx ("call [add_member]('%s') on [g_%s];\n", uname, gname);
        }

      if (uname != NULL)
        {
          ws_free_string_and_init (uname);
        }

      if (gname != NULL)
        {
          ws_free_string_and_init (gname);
        }
    }

      if (query_result != NULL)
    {
      db_query_end (query_result);
      query_result = NULL;
    }

      if (upper_case_name != NULL)
    {
      free_and_init (upper_case_name);
    }

      if (query != NULL)
    {
      free_and_init (query);
    }
    }

  return (error);
}



/*
 * au_export_grants() - Issues a sequence of CSQL grant statements related
 *                      to the given class.
 *   return: error code
 *   output_ctx(in): output context
 *   class_mop(in): class of interest
 *   quoted_id_flag(in):
 */
int
au_export_grants (extract_context &ctxt, print_output &output_ctx, MOP class_mop, DB_OBJECT_TYPE obj_type)
{
  int error = NO_ERROR;
  CLASS_AUTH cl_auth;
  CLASS_USER *u;
  int statements, ecount;
  char *uname;

  switch (obj_type)
    {
    case DB_OBJECT_CLASS:
      cl_auth.class_mop = class_mop;
      cl_auth.owner = au_get_class_owner (class_mop);
      cl_auth.users = NULL;
      break;
    case DB_OBJECT_PROCEDURE:
      cl_auth.class_mop = class_mop;
      cl_auth.owner = jsp_get_owner (class_mop);
      cl_auth.users = NULL;
      break;
    default:
      assert (false);
      error = ER_FAILED;
      return (error);
    }

  /* make an entry for the owner with complete authorization */
  u = find_or_add_user (&cl_auth, cl_auth.owner);
  u->available_auth = AU_FULL_AUTHORIZATION;

  /* add entries for the other users with authorization on this class */
  error = build_class_grant_list (&cl_auth, class_mop);
  if (error == NO_ERROR)
    {
      /* loop through the grant list, issuing grant statements */
      while ((statements = class_grant_loop (ctxt, output_ctx, &cl_auth, obj_type)))
    ;

      for (u = cl_auth.users, ecount = 0; u != NULL; u = u->next)
    {
      if (u->grants != NULL)
        {
          uname = au_get_user_name (u->obj);

          /*
           * should this be setting an error condition ?
           * for now, leave a comment in the output file
           */
          output_ctx ("/*");
          output_ctx (msgcat_message (MSGCAT_CATALOG_CUBRID, MSGCAT_SET_AUTHORIZATION,
                      MSGCAT_AUTH_GRANT_DUMP_ERROR), uname);
          output_ctx ("*/\n");
          ws_free_string (uname);
          ecount++;
        }
    }
      if (ecount)
    {
      error = ER_GENERIC_ERROR;
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
    }
    }

  free_class_users (cl_auth.users);

  return (error);
}


/*
 * GRANT EXPORT
 *
 * This is in support of the authorization migration utilities.  We build
 * hierarchy of grant information and then generate a sequence of
 * CSQL statemenets to recreate the grants.  Note that the grants have
 * to be done in order of dependencies.
 */

/*
 * make_class_grant - Create a temporary class grant structure.
 *   return: new class grant
 *   user(in): subject user
 *   cache(in): authorization bits to grant
 */
static CLASS_GRANT *
make_class_grant (CLASS_USER *user, int cache)
{
  CLASS_GRANT *grant;

  if ((grant = (CLASS_GRANT *) malloc (sizeof (CLASS_GRANT))) != NULL)
    {
      grant->next = NULL;
      grant->user = user;
      grant->cache = cache;
    }
  return (grant);
}

/*
 * make_class_user - Create a temporary class user structure.
 *   return: new class user structure
 *   user_obj(in): pointer to actual database object for this user
 */
static CLASS_USER *
make_class_user (MOP user_obj)
{
  CLASS_USER *u;

  if ((u = (CLASS_USER *) malloc (sizeof (CLASS_USER))) != NULL)
    {
      u->next = NULL;
      u->obj = user_obj;
      u->grants = NULL;

      /*
       * This authorization of this user class structure would normally
       * be filled in by examining authorizations granted by other users.
       * The DBA user is special in that it should have full authorization
       * without being granted it by any users.  Therefore we need to set
       * the authorization explicitly before any code checks it.
       */
      if (ws_is_same_object (user_obj, Au_dba_user))
    {
      u->available_auth = AU_FULL_AUTHORIZATION;
    }
      else
    {
      u->available_auth = 0;
    }
    }
  return (u);
}

/*
 * free_class_grants - Frees list of temporary class grant structures.
 *   return: none
 *   grants(in): list of class grant structures
 */
static void
free_class_grants (CLASS_GRANT *grants)
{
  CLASS_GRANT *g, *next;

  for (g = grants, next = NULL; g != NULL; g = next)
    {
      next = g->next;
      free_and_init (g);
    }
}

/*
 * find_or_add_user - Adds an entry in the user list of a class authorization
 *                    structure for the user object.
 *   return: class user structures
 *   auth(in):class authorization state
 *   user_obj(in):database user object to add
 *
 * Note: If there is already an entry in the list, it returns the found entry
 */
static CLASS_USER *
find_or_add_user (CLASS_AUTH *auth, MOP user_obj)
{
  CLASS_USER *u, *last;

  for (u = auth->users, last = NULL; u != NULL && !ws_is_same_object (u->obj, user_obj); u = u->next)
    {
      last = u;
    }

  if (u == NULL)
    {
      u = make_class_user (user_obj);
      if (last == NULL)
    {
      auth->users = u;
    }
      else
    {
      last->next = u;
    }
    }
  return (u);
}

/*
 * add_class_grant - Makes an entry in the class authorization state
 *                   for a desired grant.
 *   return: error code
 *   auth(in): class authorization state
 *   source(in): source user object
 *   user(in): subject user object
 *   cache(in): authorization cache bits
 */
static int
add_class_grant (CLASS_AUTH *auth, MOP source, MOP user, int cache)
{
  CLASS_USER *u, *gu;
  CLASS_GRANT *g;

  u = find_or_add_user (auth, source);

  for (g = u->grants; g != NULL && !ws_is_same_object (g->user->obj, user); g = g->next)
    ;

  if (g == NULL)
    {
      if (!ws_is_same_object (source, user))
    {
      gu = find_or_add_user (auth, user);
      g = make_class_grant (gu, cache);
      if (g == NULL)
        {
          assert (er_errid () != NO_ERROR);
          return er_errid ();
        }
      g->next = u->grants;
      u->grants = g;
    }
    }
  else
    {
      /*
       * this shouldn't happen, multiple grants from source should already have
       * been combined
       */
      g->cache |= cache;
    }
  return NO_ERROR;
}

/*
 * free_class_users - Frees list of class user objects.
 *   return: none
 *   users(in): class user list
 */
static void
free_class_users (CLASS_USER *users)
{
  CLASS_USER *u, *next;

  for (u = users, next = NULL; u != NULL; u = next)
    {
      next = u->next;
      free_class_grants (u->grants);
      free_and_init (u);
    }
}


/*
 * build_class_grant_list - Adds grant entries in cl_auth for every grant entry
 *                          found in the authorization catalog for
 *                          the class "class".
 *   return: error code
 *   cl_auth(in):  class authorization state
 *   class(in): class object
 *
 * Note: 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.  A query on the db_user class is
 *       now used to find all users.
 */
static int
build_class_grant_list (CLASS_AUTH *cl_auth, MOP class_mop)
{
  int error;
  MOP user, auth, source;
  DB_SET *grants;
  DB_VALUE value;
  int j, gsize, cache;
  char *query;
  size_t query_size;
  DB_QUERY_RESULT *query_result;
  DB_QUERY_ERROR query_error;
  DB_VALUE user_val;
  const char *qp1 = "select [%s] from [%s];";

  query_size = strlen (qp1) + strlen (AU_USER_CLASS_NAME) * 2;
  query = (char *) malloc (query_size);
  if (query == NULL)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, query_size);
      return ER_OUT_OF_VIRTUAL_MEMORY;
    }

  sprintf (query, qp1, AU_USER_CLASS_NAME, AU_USER_CLASS_NAME);

  error = db_compile_and_execute_local (query, &query_result, &query_error);
  if (error < 0)
    /* error is row count if not negative. */
    {
      free_and_init (query);
      return error;
    }

  while (db_query_next_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))
        {
          user = NULL;
        }
      else
        {
          user = db_get_object (&user_val);
        }

      error = au_get_object (user, "authorization", &auth);
      /* ignore the deleted object errors */
      if (error != NO_ERROR)
        {
          if (error == ER_HEAP_UNKNOWN_OBJECT)
        {
          error = NO_ERROR;
        }
        }
      else
        {
          if ((error = get_grants (auth, &grants, 1)) == NO_ERROR)
        {
          gsize = set_size (grants);
          for (j = 0; j < gsize; j += GRANT_ENTRY_LENGTH)
            {
              error = set_get_element (grants, GRANT_ENTRY_CLASS (j), &value);
              if (error == NO_ERROR && DB_VALUE_TYPE (&value) == DB_TYPE_OBJECT
              && db_get_object (&value) == class_mop)
            {
              error = set_get_element (grants, GRANT_ENTRY_SOURCE (j), &value);
              if (error == NO_ERROR && DB_VALUE_TYPE (&value) == DB_TYPE_OBJECT && !DB_IS_NULL (&value)
                  && (source = db_get_object (&value)) != NULL)
                {
                  error = set_get_element (grants, GRANT_ENTRY_CACHE (j), &value);
                  if (error == NO_ERROR)
                {
                  cache = db_get_int (&value);
                  error = add_class_grant (cl_auth, source, user, cache);
                }
                }
            }
            }
          set_free (grants);
        }
        }
    }           /* if */
    }               /* while */

  db_query_end (query_result);
  free_and_init (query);

  return (error);
}

/*
 * name is user_specified_name.
 * owner_name must be a char array of size DB_MAX_IDENTIFIER_LENGTH to copy user_specified_name.
 * class_name refers to class_name after dot(.).
 */
#define SPLIT_USER_SPECIFIED_NAME(name, owner_name, class_name) \
    do \
      { \
        assert (strlen ((name)) < STATIC_CAST (int, sizeof ((owner_name)))); \
        strcpy ((owner_name), (name)); \
        (class_name) = strchr ((owner_name), '.'); \
        *(class_name)++ = '\0'; \
      } \
    while (0)

/*
 * issue_grant_statement - Generates an CSQL "grant" statement.
 *   return: none
 *   output_ctx(in/out): output context
 *   auth(in): class authorization state
 *   grant(in): desired grant
 *   authbits(in): specific authorization to grant
 *   quoted_id_flag(in):
 */
static void
issue_grant_statement (extract_context &ctxt, print_output &output_ctx, CLASS_AUTH *auth, CLASS_GRANT *grant,
               int authbits, DB_OBJECT_TYPE obj_type)
{
  const char *gtype;
  char owner_name[DB_MAX_IDENTIFIER_LENGTH] = { '\0' };
  char unique_name[DB_MAX_IDENTIFIER_LENGTH + 1];
  char *class_name = NULL;
  char *username = NULL;
  int typebit;

  typebit = authbits & AU_TYPE_MASK;
  switch (typebit)
    {
    case AU_SELECT:
      gtype = "SELECT";
      break;
    case AU_INSERT:
      gtype = "INSERT";
      break;
    case AU_UPDATE:
      gtype = "UPDATE";
      break;
    case AU_DELETE:
      gtype = "DELETE";
      break;
    case AU_ALTER:
      gtype = "ALTER";
      break;
    case AU_INDEX:
      gtype = "INDEX";
      break;
    case AU_EXECUTE:
      gtype = "EXECUTE";
      break;
    default:
      gtype = "???";
      break;
    }

  output_ctx ("\n");

  switch (obj_type)
    {
    case DB_OBJECT_CLASS:
      SPLIT_USER_SPECIFIED_NAME (sm_get_ch_name (auth->class_mop), owner_name, class_name);
      username = au_get_user_name (grant->user->obj);

      output_ctx ("GRANT %s ON ", gtype);
      break;
    case DB_OBJECT_PROCEDURE:
      unique_name[0] = '\0';
      jsp_get_unique_name (auth->class_mop, unique_name, DB_MAX_IDENTIFIER_LENGTH + 1);
      SPLIT_USER_SPECIFIED_NAME (unique_name, owner_name, class_name);
      username = au_get_user_name (grant->user->obj);

      output_ctx ("GRANT %s ON PROCEDURE ", gtype);
      break;
    default:
      output_ctx ("GRANT %s ON ", gtype);
      break;
    }

  if (ctxt.is_dba_user || ctxt.is_dba_group_member)
    {
      output_ctx ("[%s].[%s]", owner_name, class_name);
    }
  else
    {
      output_ctx ("[%s]", class_name);
    }

  if (username != NULL)
    {
      output_ctx (" TO [%s]", username);
    }
  else
    {
      output_ctx (" TO %s", "???");
    }

  if (authbits & (typebit << AU_GRANT_SHIFT))
    {
      output_ctx (" WITH GRANT OPTION");
    }
  output_ctx (";\n");

  ws_free_string (username);
}


/*
 * class_grant_loop - Makes a pass on the authorization user list and
 *                    issues grant statements for any users that are able.
 *                    Returns the number of statements issued
 *   return: number of statements issued
 *   output_ctx(in): output context
 *   auth(in): class authorization state
 *   quoted_id_flag(in):
 *
 * Note:
 * If this resturns zero and the user list is not empty, it indicates
 * that there are illegal grants in the hierarchy that were not rooted
 * in the class owner object.
 *
 * It would likely be more efficient if rather than making a full pass
 * on the list we evaluate the first node in the list and then recursively
 * evaluate every mode affected by the first evaluation.  If the first
 * node results in no evaluations, we move to the next node in the list.
 *
 * This will tend to get grants to come out "depth first" which may be
 * more logical when examining the resulting statements.  It will probably
 * result in fewer traversals of the user list as well ?
 *
 * TODO : LP64
 */
static int
class_grant_loop (extract_context &ctxt, print_output &output_ctx, CLASS_AUTH *auth, DB_OBJECT_TYPE obj_type)
{
#define AU_MIN_BIT 1        /* AU_SELECT */
#define AU_MAX_BIT 0x40     /* AU_EXECUTE */

  CLASS_USER *user;
  CLASS_GRANT *grant, *prev_grant, *next_grant;
  int statements = 0;
  int mask, authbits;

  for (user = auth->users; user != NULL; user = user->next)
    {
      for (grant = user->grants, prev_grant = NULL, next_grant = NULL; grant != NULL; grant = next_grant)
    {
      next_grant = grant->next;
      mask = AU_SELECT;
      for (mask = AU_MIN_BIT; mask <= AU_MAX_BIT; mask = mask << 1)
        {
          if (grant->cache & mask)
        {
          /* combine auth type & grant option bit */
          authbits = mask | (grant->cache & (mask << AU_GRANT_SHIFT));
          /*
           * if the user has these same bits available,
           * issue the grant
           */
          if ((user->available_auth & authbits) == authbits)
            {
              if (!ws_is_same_object (auth->users->obj, grant->user->obj))
            {
              issue_grant_statement (ctxt, output_ctx, auth, grant, authbits, obj_type);
            }

              /* turn on grant bits in the granted user */
              grant->user->available_auth |= authbits;
              /* turn off the pending grant bits in granting user */
              grant->cache &= ~authbits;
              statements++;
            }
        }
        }
      if (grant->cache == 0)
        {
          /* no more grants, remove it from the list */
          if (prev_grant == NULL)
        {
          user->grants = grant->next;
        }
          else
        {
          prev_grant->next = grant->next;
        }
          grant->next = NULL;
          free_class_grants (grant);
        }
      else
        {
          prev_grant = grant;
        }
    }
      /*
       * could remove user from the list but can't free it because
       * structure may be referenced by a grant inside another user
       */
    }
  return (statements);
}