blob: 3c9939d10a6eeefe4e82fb9b803bcacc18b0db26 [file] [log] [blame]
/**********************************************************************
// @@@ START COPYRIGHT @@@
//
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you 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.
//
// @@@ END COPYRIGHT @@@
**********************************************************************/
// *****************************************************************************
// *
// * File: CmpSeabaseDDLauth.cpp
// * Description: Implements methods for user management
// *
// * Contains methods for classes:
// * CmpSeabaseDDLauth
// * CmpSeabaseDDLuser
// *
// *
// *****************************************************************************
#define SQLPARSERGLOBALS_FLAGS
#include "SqlParserGlobalsCmn.h"
#include "CmpSeabaseDDLauth.h"
#include "CmpSeabaseDDL.h"
#include "StmtDDLRegisterUser.h"
#include "StmtDDLAlterUser.h"
#include "StmtDDLCreateRole.h"
#include "ElemDDLGrantee.h"
#include "CompException.h"
#include "Context.h"
#include "dbUserAuth.h"
#include "ComUser.h"
#include "CmpDDLCatErrorCodes.h"
#include "NAStringDef.h"
#include "ExpHbaseInterface.h"
#include "PrivMgrCommands.h"
#include "PrivMgrRoles.h"
#include "PrivMgrComponentPrivileges.h"
#define RESERVED_AUTH_NAME_PREFIX "DB__"
inline static bool validateExternalUsername(
const char * externalUsername,
DBUserAuth::AuthenticationConfiguration configurationNumber,
DBUserAuth::AuthenticationConfiguration & foundConfigurationNumber);
// ****************************************************************************
// Class CmpSeabaseDDLauth methods
// ****************************************************************************
// ----------------------------------------------------------------------------
// Constructors
// ----------------------------------------------------------------------------
CmpSeabaseDDLauth::CmpSeabaseDDLauth(
const NAString & systemCatalog,
const NAString & MDSchema)
: authID_(NA_UserIdDefault),
authType_(COM_UNKNOWN_ID_CLASS),
authCreator_(NA_UserIdDefault),
authCreateTime_(0),
authRedefTime_(0),
authValid_(true),
systemCatalog_(systemCatalog),
MDSchema_(MDSchema)
{}
CmpSeabaseDDLauth::CmpSeabaseDDLauth()
: authID_(NA_UserIdDefault),
authType_(COM_UNKNOWN_ID_CLASS),
authCreator_(NA_UserIdDefault),
authCreateTime_(0),
authRedefTime_(0),
authValid_(true),
systemCatalog_("TRAFODION"),
MDSchema_("TRAFODION.\"_MD_\"")
{}
// ----------------------------------------------------------------------------
// public method: authExists
//
// Input:
// authName - name to look up
// isExternal -
// true - the auth name is the external name (auth_ext_name)
// false - the auth name is the database name (auth_db_name)
//
// Output:
// Returns true if authorization row exists in the metadata
// Returns false if authorization row does not exist in the metadata or an
// unexpected error occurs
//
// The diags area contains an error if any unexpected errors occurred.
// Callers should check the diags area when false is returned
// ----------------------------------------------------------------------------
bool CmpSeabaseDDLauth::authExists (const NAString &authName, bool isExternal)
{
// Read the auths table to get a count of rows for the authName
Int64 rowCount = 0;
try
{
NAString whereClause ("where ");
whereClause += (isExternal) ? "auth_ext_name = '" : "auth_db_name = '";
whereClause += authName;
whereClause += "'";
rowCount = selectCount(whereClause);
return (rowCount > 0) ? true : false;
}
catch (...)
{
// If there is no error in the diags area, set up an internal error
if (CmpCommon::diags()->getNumber(DgSqlCode::ERROR_) == 0)
SEABASEDDL_INTERNAL_ERROR("CmpSeabaseDDLauth::authExists for authName");
return false;
}
}
// ----------------------------------------------------------------------------
// method: describe
//
// This method is not valid for the base class
//
// Input: none
//
// Output: populates diag area, throws exception.
// ----------------------------------------------------------------------------
bool CmpSeabaseDDLauth::describe(const NAString &authName, NAString &authText)
{
SEABASEDDL_INTERNAL_ERROR("CmpSeabaseDDLauth::describe");
UserException excp(NULL,0);
throw excp;
return 0;
}
// ----------------------------------------------------------------------------
// public method: getAuthDetails
//
// Populates the CmpSeabaseDDLauth class containing auth details for the
// requested authName
//
// Input:
// authName - the database or external auth name
// isExternal -
// true - the auth name is the external name (auth_ext_name)
// false - the auth name is the database name (auth_db_name)
//
// Output:
// Returned parameter (AuthStatus):
// STATUS_GOOD: authorization details are populated:
// STATUS_NOTFOUND: authorization details were not found
// STATUS_WARNING: (not 100) warning was returned, diags area populated
// STATUS_ERROR: error was returned, diags area populated
// ----------------------------------------------------------------------------
CmpSeabaseDDLauth::AuthStatus
CmpSeabaseDDLauth::getAuthDetails(const char *pAuthName, bool isExternal)
{
// If the authname is a special PUBLIC authorization ID, set it up
std::string authName = pAuthName;
if (authName == PUBLIC_AUTH_NAME)
{
setAuthCreator(SUPER_USER);
setAuthCreateTime(0);
setAuthDbName(PUBLIC_AUTH_NAME);
setAuthExtName(PUBLIC_AUTH_NAME);
setAuthID(PUBLIC_USER);
setAuthRedefTime(0);
setAuthType(COM_ROLE_CLASS);
setAuthValid(false);
return STATUS_GOOD;
}
try
{
NAString whereClause ("where ");
whereClause += (isExternal) ? "auth_ext_name = '" : "auth_db_name = '";
whereClause += pAuthName;
whereClause += "'";
return selectExactRow(whereClause);
}
catch (...)
{
// If there is no error in the diags area, set up an internal error
if (CmpCommon::diags()->getNumber(DgSqlCode::ERROR_) == 0)
SEABASEDDL_INTERNAL_ERROR("CmpSeabaseDDLauth::getAuthDetails for authName");
return STATUS_ERROR;
}
}
// ----------------------------------------------------------------------------
// public method: getAuthDetails
//
// Create the CmpSeabaseDDLauth class containing auth details for the
// request authID
//
// Input:
// authID - the database authorization ID to search for
//
// Output:
// A returned parameter:
// STATUS_GOOD: authorization details are populated:
// STATUS_NOTFOUND: authorization details were not found
// STATUS_WARNING: (not 100) warning was returned, diags area populated
// STATUS_ERROR: error was returned, diags area populated
// ----------------------------------------------------------------------------
CmpSeabaseDDLauth::AuthStatus CmpSeabaseDDLauth::getAuthDetails(Int32 authID)
{
try
{
char buf[1000];
str_sprintf(buf, "where auth_id = %d ", authID);
NAString whereClause (buf);
return selectExactRow(whereClause);
}
catch (...)
{
// If there is no error in the diags area, set up an internal error
if (CmpCommon::diags()->getNumber(DgSqlCode::ERROR_) == 0)
SEABASEDDL_INTERNAL_ERROR("CmpSeabaseDDLauth::getAuthDetails for authID");
return STATUS_ERROR;
}
}
CmpSeabaseDDLauth::AuthStatus CmpSeabaseDDLauth::getRoleIDs(
const Int32 authID,
std::vector<int32_t> &roleIDs)
{
NAString privMgrMDLoc;
CONCAT_CATSCH(privMgrMDLoc,systemCatalog_.data(),SEABASE_PRIVMGR_SCHEMA);
PrivMgrRoles role(std::string(MDSchema_.data()),
std::string(privMgrMDLoc.data()),
CmpCommon::diags());
std::vector<std::string> roleNames;
std::vector<int32_t> roleDepths;
if (role.fetchRolesForUser(authID,roleNames,roleIDs,roleDepths) == PrivStatus::STATUS_ERROR)
return STATUS_ERROR;
return STATUS_GOOD;
}
// ----------------------------------------------------------------------------
// public method: getObjectName
//
// Returns the first object name from the list of passed in objectUIDs.
//
// Input:
// objectUIDs - list of objectUIDs
//
// Output:
// returns the fully qualified object name
// returns NULL string if no objects were found
// ----------------------------------------------------------------------------
NAString CmpSeabaseDDLauth::getObjectName (const std::vector <int64_t> objectUIDs)
{
char longBuf [sizeof(int64_t)*8+1];
bool isFirst = true;
NAString objectList;
NAString objectName;
if (objectUIDs.size() == 0)
return objectName;
// convert objectUIDs into an "in" clause list
for (int i = 0; i < objectUIDs.size(); i++)
{
if (isFirst)
objectList = "(";
else
objectList += ", ";
isFirst = false;
sprintf (longBuf, "%ld", objectUIDs[i]);
objectList += longBuf;
}
objectList += ")";
NAString sysCat = CmpSeabaseDDL::getSystemCatalogStatic();
char buf[1000 + objectList.length()];
str_sprintf(buf, "select [first 1] trim(catalog_name) || '.' || "
"trim(schema_name) || '.' || trim(object_name) "
" from %s.\"%s\".%s where object_uid in %s",
sysCat.data(), SEABASE_MD_SCHEMA, SEABASE_OBJECTS, objectList.data());
ExeCliInterface cliInterface(STMTHEAP);
Int32 cliRC = cliInterface.fetchRowsPrologue(buf, true/*no exec*/);
if (cliRC != 0)
{
cliInterface.retrieveSQLDiagnostics(CmpCommon::diags());
UserException excp (NULL, 0);
throw excp;
}
cliRC = cliInterface.clearExecFetchClose(NULL, 0);
if (cliRC < 0)
{
cliInterface.retrieveSQLDiagnostics(CmpCommon::diags());
UserException excp (NULL, 0);
throw excp;
}
// return an empty string
if (cliRC == 100)
return objectName;
// get the objectname
char * ptr = NULL;
Lng32 len = 0;
// object name returned
cliInterface.getPtrAndLen(1,ptr,len);
NAString returnedName(ptr,len);
return returnedName;
}
// ----------------------------------------------------------------------------
// method: getUniqueAuthID
//
// Return an unused auth ID between the requested ranges
// Input:
// minValue - the lowest value
// maxValue - the highest value
//
// Output: unique ID to use
// exception is generated if unable to generate a unique value
// ----------------------------------------------------------------------------
Int32 CmpSeabaseDDLauth::getUniqueAuthID(
const Int32 minValue,
const Int32 maxValue)
{
Int32 newUserID = 0;
char buf[300];
Int32 len = snprintf(buf, 300,
"SELECT [FIRST 1] auth_id FROM (SELECT auth_id, "
"LEAD(auth_id) OVER (ORDER BY auth_id) L FROM %s.%s ) "
"WHERE (L - auth_id > 1 or L is null) and auth_id >= %d ",
MDSchema_.data(),SEABASE_AUTHS, minValue);
assert (len <= 300);
len = 0;
Int64 metadataValue = 0;
bool nullTerminate = false;
ExeCliInterface cliInterface(STMTHEAP);
Lng32 cliRC = cliInterface.executeImmediate(buf, (char *)&metadataValue, &len, nullTerminate);
if (cliRC < 0)
{
cliInterface.retrieveSQLDiagnostics(CmpCommon::diags());
return 0;
}
// We have lots of available ID's. Don't expect to run out of ID's for awhile
if (cliRC == 100 || metadataValue > maxValue)
{
SEABASEDDL_INTERNAL_ERROR("CmpSeabaseDDLauth::getUniqueAuthID failed, ran out of available IDs");
UserException excp (NULL, 0);
throw excp;
}
newUserID = (Int32)metadataValue;
if (newUserID == 0)
newUserID = ROOT_USER_ID + 1;
else
newUserID++;
// There is a bug where grants are not being removed from component privileges
// when a user is dropped. So if this authID still shows up as a component
// privilege grantee go ahead a cleanup the inconsistency.
std::string privMDLoc(CmpSeabaseDDL::getSystemCatalogStatic().data());
privMDLoc += std::string(".\"") +
std::string(SEABASE_PRIVMGR_SCHEMA) +
std::string("\"");
PrivMgrComponentPrivileges componentPrivs(privMDLoc,CmpCommon::diags());
if (componentPrivs.isAuthIDGrantedPrivs(newUserID))
{
if (!componentPrivs.dropAllForGrantee(newUserID))
{
*CmpCommon::diags() << DgSqlCode(CAT_WARN_USED_AUTHID)
<< DgInt0(newUserID);
Int32 newMinValue = newUserID+1;
newUserID = getUniqueAuthID(newUserID + 1, maxValue);
}
}
return newUserID;
}
// ----------------------------------------------------------------------------
// method: isAuthNameReserved
//
// Checks to see if proposed name is reserved
//
// Input: authorization name
//
// Output:
// true - name is reserved
// false - name is not reserved
// ----------------------------------------------------------------------------
bool CmpSeabaseDDLauth::isAuthNameReserved (const NAString &authName)
{
bool result;
result = authName.length() >= strlen(RESERVED_AUTH_NAME_PREFIX)
&&
authName.operator()(0,strlen(RESERVED_AUTH_NAME_PREFIX)) ==
RESERVED_AUTH_NAME_PREFIX
||
authName == SYSTEM_AUTH_NAME
||
authName == PUBLIC_AUTH_NAME
||
authName == "NONE";
return result;
}
// ----------------------------------------------------------------------------
// method: isAuthNameValid
//
// checks to see if the name contains valid character
//
// Input:
// NAString - authName -- name string to check
//
// Returns: true if valid, false otherwise
// ----------------------------------------------------------------------------
bool CmpSeabaseDDLauth::isAuthNameValid(const NAString &authName)
{
string validChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_@./";
string strToScan = authName.data();
size_t found = strToScan.find_first_not_of(validChars);
if (found == string::npos)
return true;
return false;
}
// ----------------------------------------------------------------------------
// method: isRoleID
//
// Determines if an authID is in the role ID range
//
// Input:
// Int32 - authID -- numeric ID to check
//
// Returns: true if authID is a role ID, false otherwise
// ----------------------------------------------------------------------------
bool CmpSeabaseDDLauth::isRoleID(Int32 authID)
{
return (authID >= MIN_ROLEID && authID <= MAX_ROLEID);
}
// ----------------------------------------------------------------------------
// method: isUserID
//
// Determines if an authID is in the user ID range
//
// Input:
// Int32 - authID -- numeric ID to check
//
// Returns: true if authID is a user ID, false otherwise
// ----------------------------------------------------------------------------
bool CmpSeabaseDDLauth::isUserID(Int32 authID)
{
return (authID >= MIN_USERID && authID <= MAX_USERID);
}
// ----------------------------------------------------------------------------
// method: isSystemAuth
//
// Checks the list of authorization IDs to see if the passed in authName is a
// system auth. This replaces checks for reserved names.
//
// isSpecialAuth indicates a system auth but it is not defined in the metadata
//
// Returns:
// true - is a system auth
// false - is not a system auth
// ----------------------------------------------------------------------------
bool CmpSeabaseDDLauth::isSystemAuth(
const ComIdClass authType,
const NAString &authName,
bool &isSpecialAuth)
{
bool isSystem = false;
switch (authType)
{
case COM_ROLE_CLASS:
{
int32_t numberRoles = sizeof(systemRoles)/sizeof(SystemAuthsStruct);
for (int32_t i = 0; i < numberRoles; i++)
{
const SystemAuthsStruct &roleDefinition = systemRoles[i];
if (roleDefinition.authName == authName)
{
isSystem = true;
isSpecialAuth = roleDefinition.isSpecialAuth;
break;
}
}
break;
}
case COM_USER_CLASS:
{
// Verify name is a standard name
std::string authNameStr(authName.data());
size_t prefixLength = strlen(RESERVED_AUTH_NAME_PREFIX);
if (authNameStr.size() <= prefixLength ||
authNameStr.compare(0,prefixLength,RESERVED_AUTH_NAME_PREFIX) == 0)
isSystem = true;
break;
}
default:
{
// should never get here - assert?
isSystem = false;
}
}
return isSystem;
}
// ----------------------------------------------------------------------------
// protected method: createStandardAuth
//
// Inserts a standard user or role in the Trafodion metadata
// The authType needs to be set up before calling
//
// Input:
// authName
// authID
// ----------------------------------------------------------------------------
bool CmpSeabaseDDLauth::createStandardAuth(
const std::string authName,
const int32_t authID)
{
// check to see if authName is a system object
bool isSpecialAuth = false;
bool isSystem = isSystemAuth(getAuthType(), NAString(authName.c_str()), isSpecialAuth);
// since this is being called by internal code, should not be trying to
// create non system object (isSystemAuth) or object that should not be
// registered in the metadata (isSpecialAuth), return internal error
if (!isSystem || isSpecialAuth)
{
NAString errorMsg ("Invalid system authorization identifier for ");
errorMsg += getAuthType() == COM_ROLE_CLASS ? "role " : "user ";
errorMsg += authName.c_str();
SEABASEDDL_INTERNAL_ERROR(errorMsg.data());
return false;
}
setAuthDbName(authName.c_str());
setAuthExtName(authName.c_str());
setAuthValid(true); // assume a valid authorization ID
Int64 createTime = NA_JulianTimestamp();
setAuthCreateTime(createTime);
setAuthRedefTime(createTime); // make redef time the same as create time
// Make sure authorization ID has not already been registered
if (authExists(getAuthDbName(),false))
return false;
try
{
Int32 minAuthID = isRole() ? MIN_ROLEID : MIN_USERID;
Int32 maxAuthID = isRole() ? MAX_ROLEID : MAX_USERID;
Int32 newAuthID = (authID == NA_UserIdDefault) ? getUniqueAuthID(minAuthID, maxAuthID) : authID;
if (isRole())
assert(isRoleID(newAuthID));
else if (isUser())
assert (isUserID(newAuthID));
setAuthID(newAuthID);
setAuthCreator(ComUser::getRootUserID());
// Add the role to AUTHS table
insertRow();
}
catch (...)
{
// At this time, an error should be in the diags area.
// If there is no error, set up an internal error
if (CmpCommon::diags()->getNumber(DgSqlCode::ERROR_) == 0)
SEABASEDDL_INTERNAL_ERROR("Unexpected error in CmpSeabaseDDLuser::createStandardAuth");
}
return true;
}
//-----------------------------------------------------------------------------
// Methods that perform metadata access
//
// All methods return a UserException if an unexpected error occurs
//-----------------------------------------------------------------------------
// Delete a row from the AUTHS table based on the AUTH_ID
void CmpSeabaseDDLauth::deleteRow(const NAString &authName)
{
NAString systemCatalog = CmpSeabaseDDL::getSystemCatalogStatic();
char buf[1000];
ExeCliInterface cliInterface(STMTHEAP);
str_sprintf(buf, "delete from %s.\"%s\".%s where auth_db_name = '%s'",
systemCatalog.data(), SEABASE_MD_SCHEMA, SEABASE_AUTHS, authName.data());
Lng32 cliRC = cliInterface.executeImmediate(buf);
if (cliRC < 0)
{
cliInterface.retrieveSQLDiagnostics(CmpCommon::diags());
UserException excp (NULL, 0);
throw excp;
}
// Existence of the row is already checked, throw an assertion the row does not exist
// With optimistic locking, this might occur if a concurrent session is deleting the
// same row and gets in first
CMPASSERT (cliRC == 100);
// Not sure if it is possible to get a warning from a delete and
// what it means if one is returned for now, it is ignored.
}
// Insert a row into the AUTHS table
void CmpSeabaseDDLauth::insertRow()
{
char buf[1000];
ExeCliInterface cliInterface(STMTHEAP);
NAString authType;
switch (getAuthType())
{
case COM_ROLE_CLASS:
authType = COM_ROLE_CLASS_LIT;
break;
case COM_USER_CLASS:
authType = COM_USER_CLASS_LIT;
break;
default:
SEABASEDDL_INTERNAL_ERROR("Switch statement in CmpSeabaseDDLuser::deleteRow invalid authType");
UserException excp (NULL, 0);
throw excp;
}
NAString authValid = isAuthValid() ? "Y" : "N";
NAString sysCat = CmpSeabaseDDL::getSystemCatalogStatic();
str_sprintf(buf, "insert into %s.\"%s\".%s values (%d, '%s', '%s', '%s', %d, '%s', %ld, %ld, 0)",
sysCat.data(), SEABASE_MD_SCHEMA, SEABASE_AUTHS,
getAuthID(),
getAuthDbName().data(),
getAuthExtName().data(),
authType.data(),
getAuthCreator(),
authValid.data(),
getAuthCreateTime(),
getAuthRedefTime());
Int32 cliRC = cliInterface.executeImmediate(buf);
if (cliRC < 0)
{
cliInterface.retrieveSQLDiagnostics(CmpCommon::diags());
UserException excp (NULL, 0);
throw excp;
}
// Not sure if it is possible to get a warning from an insert and
// what it means if one is returned, for now it is ignored.
}
// update a row in AUTHS table based on the passed in setClause
void CmpSeabaseDDLauth::updateRow(NAString &setClause)
{
char buf[1000];
ExeCliInterface cliInterface(STMTHEAP);
NAString sysCat = CmpSeabaseDDL::getSystemCatalogStatic();
str_sprintf(buf, "update %s.\"%s\".%s %s where auth_id = %d",
sysCat.data(), SEABASE_MD_SCHEMA, SEABASE_AUTHS,
setClause.data(),
getAuthID());
Int32 cliRC = cliInterface.executeImmediate(buf);
if (cliRC < 0)
{
cliInterface.retrieveSQLDiagnostics(CmpCommon::diags());
UserException excp (NULL, 0);
throw excp;
}
// Existence of the row is already checked, throw an assertion the row does not exist
// With optimistic locking, this might occur if a concurrent session is deleting the
// same row and gets in first
CMPASSERT (cliRC == 100);
// Not sure if it is possible to get a warning from an update and
// what it means if one is returned, for now it is ignored.
}
// select exact based on the passed in whereClause
CmpSeabaseDDLauth::AuthStatus
CmpSeabaseDDLauth::selectExactRow(const NAString & whereClause)
{
NAString sysCat = CmpSeabaseDDL::getSystemCatalogStatic();
char buf[1000];
str_sprintf(buf, "select auth_id, auth_db_name, auth_ext_name, auth_type, "
"auth_creator, auth_is_valid, auth_create_time, auth_redef_time"
" from %s.%s %s ",
MDSchema_.data(), SEABASE_AUTHS, whereClause.data());
NAString cmd (buf);
ExeCliInterface cliInterface(STMTHEAP);
Int32 cliRC = cliInterface.fetchRowsPrologue(cmd.data(), true/*no exec*/);
if (cliRC != 0)
{
cliInterface.retrieveSQLDiagnostics(CmpCommon::diags());
DDLException excp (cliRC, NULL, 0);
throw excp;
}
cliRC = cliInterface.clearExecFetchClose(NULL, 0);
if (cliRC < 0)
{
cliInterface.retrieveSQLDiagnostics(CmpCommon::diags());
DDLException excp (cliRC, NULL, 0);
throw excp;
}
if (cliRC == 100) // did not find the row
{
cliInterface.clearGlobalDiags();
return STATUS_NOTFOUND;
}
// Set the return status
CmpSeabaseDDLauth::AuthStatus authStatus =
(cliRC == 0) ? STATUS_GOOD : STATUS_WARNING;
// Populate the class
char * ptr = NULL;
Lng32 len = 0;
char type [6];
// value 1: auth_id (int32)
cliInterface.getPtrAndLen(1, ptr, len);
setAuthID(*(Int32*)ptr);
// value 2: auth_db_name (NAString)
cliInterface.getPtrAndLen(2,ptr,len);
NAString dbName(ptr,len);
setAuthDbName(dbName);
// value 3: auth_ext_name (NAString)
cliInterface.getPtrAndLen(3,ptr,len);
NAString extName(ptr,len);
setAuthExtName(extName);
// value 4: auth_type (char)
// str_cpy_and_null params: *tgt, *src, len, endchar, blank, null term
cliInterface.getPtrAndLen(4,ptr,len);
str_cpy_and_null(type, ptr, len, '\0', ' ', true);
if ( type[0] == 'U')
setAuthType(COM_USER_CLASS);
else if (type[0] == 'R')
setAuthType(COM_ROLE_CLASS);
else
setAuthType(COM_UNKNOWN_ID_CLASS);
// value 5: auth_creator (int32)
cliInterface.getPtrAndLen(5,ptr,len);
setAuthCreator(*(Int32*)ptr);
// value 6: auth_is_valid (char)
cliInterface.getPtrAndLen(6,ptr,len);
str_cpy_and_null(type, ptr, len, '\0', ' ', true);
if (type[0] == 'Y')
setAuthValid(true);
else
setAuthValid(false);
// value 7: auth_create_time (int64)
cliInterface.getPtrAndLen(7,ptr,len);
Int64 intValue = *(Int64*)ptr;
setAuthCreateTime((ComTimestamp) intValue);
// value 8: auth_redef_time (int64)
cliInterface.getPtrAndLen(8,ptr,len);
intValue = *(Int64*)ptr;
setAuthRedefTime((ComTimestamp) intValue);
cliInterface.fetchRowsEpilogue(NULL, true);
return authStatus;
}
// selectCount - returns the number of rows based on the where clause
Int64 CmpSeabaseDDLauth::selectCount(const NAString & whereClause)
{
NAString sysCat = CmpSeabaseDDL::getSystemCatalogStatic();
char buf[1000];
str_sprintf(buf, "select count(*) from %s.%s %s ",
MDSchema_.data(), SEABASE_AUTHS,
whereClause.data());
Lng32 len = 0;
Int64 rowCount = 0;
ExeCliInterface cliInterface(STMTHEAP);
Lng32 cliRC = cliInterface.executeImmediate(buf, (char*)&rowCount, &len, FALSE);
// If unexpected error occurred, return an exception
if (cliRC < 0)
{
cliInterface.retrieveSQLDiagnostics(CmpCommon::diags());
DDLException excp (cliRC, NULL, 0);
throw excp;
}
return rowCount;
}
// selectMaxAuthID - gets the last used auth ID
Int32 CmpSeabaseDDLauth::selectMaxAuthID(const NAString &whereClause)
{
NAString sysCat = CmpSeabaseDDL::getSystemCatalogStatic();
char buf[400];
str_sprintf(buf, "select nvl(max (auth_id),0) from %s.%s %s" ,
MDSchema_.data(),SEABASE_AUTHS,whereClause.data());
Lng32 len = 0;
Int64 maxValue = 0;
bool nullTerminate = false;
ExeCliInterface cliInterface(STMTHEAP);
Lng32 cliRC = cliInterface.executeImmediate(buf, (char *)&maxValue, &len, nullTerminate);
if (cliRC != 0)
{
cliInterface.retrieveSQLDiagnostics(CmpCommon::diags());
UserException excp (NULL, 0);
throw excp;
}
return static_cast<Int32>(maxValue);
}
// ----------------------------------------------------------------------------
// method: verifyAuthority
//
// makes sure user has privilege to perform the operation
//
// Input: none
//
// Output:
// true - authority granted
// false - no authority or unexpected error
// ----------------------------------------------------------------------------
bool CmpSeabaseDDLauth::verifyAuthority(const SQLOperation operation)
{
// If authorization is not enabled, just return with no error
if (!CmpCommon::context()->isAuthorizationEnabled())
return true;
int32_t currentUser = ComUser::getCurrentUser();
// Root user has authority to manage users.
if (currentUser == ComUser::getRootUserID())
return true;
NAString systemCatalog = CmpSeabaseDDL::getSystemCatalogStatic();
std::string privMDLoc(systemCatalog.data());
privMDLoc += std::string(".\"") +
std::string(SEABASE_PRIVMGR_SCHEMA) +
std::string("\"");
PrivMgrComponentPrivileges componentPrivileges(privMDLoc,CmpCommon::diags());
// See if non-root user has authority to manage users.
if (componentPrivileges.hasSQLPriv(currentUser, operation, true))
{
return true;
}
return false;
}
// ****************************************************************************
// Class CmpSeabaseDDLuser methods
// ****************************************************************************
// ----------------------------------------------------------------------------
// Constructor
// ----------------------------------------------------------------------------
CmpSeabaseDDLuser::CmpSeabaseDDLuser(
const NAString & systemCatalog,
const NAString & MDSchema)
: CmpSeabaseDDLauth(systemCatalog,MDSchema)
{}
CmpSeabaseDDLuser::CmpSeabaseDDLuser()
: CmpSeabaseDDLauth()
{}
// ----------------------------------------------------------------------------
// public method: getUserDetails
//
// Create the CmpSeabaseDDLuser class containing user details for the
// requested userID
//
// Input:
// userID - the database authorization ID to search for
//
// Output:
// Returned parameter:
// STATUS_GOOD: authorization details are populated:
// STATUS_NOTFOUND: authorization details were not found
// STATUS_WARNING: (not 100) warning was returned, diags area populated
// STATUS_ERROR: error was returned, diags area populated
// ----------------------------------------------------------------------------
CmpSeabaseDDLauth::AuthStatus CmpSeabaseDDLuser::getUserDetails(Int32 userID)
{
CmpSeabaseDDLauth::AuthStatus retcode = getAuthDetails(userID);
if (retcode == STATUS_GOOD && !isUser())
{
*CmpCommon::diags() << DgSqlCode (-CAT_IS_NOT_A_USER)
<< DgString0(getAuthDbName().data());
retcode = STATUS_ERROR;
}
return retcode;
}
// ----------------------------------------------------------------------------
// public method: getUserDetails
//
// Create the CmpSeabaseDDLuser class containing user details for the
// requested username
//
// Input:
// userName - the database username to retrieve details for
// isExternal -
// true - the username is the external name (auth_ext_name)
// false - the username is the database name (auth_db_name)
//
// Output:
// Returned parameter:
// STATUS_GOOD: authorization details are populated:
// STATUS_NOTFOUND: authorization details were not found
// STATUS_WARNING: (not 100) warning was returned, diags area populated
// STATUS_ERROR: error was returned, diags area populated
// ----------------------------------------------------------------------------
CmpSeabaseDDLauth::AuthStatus
CmpSeabaseDDLuser::getUserDetails(const char *pUserName, bool isExternal)
{
CmpSeabaseDDLauth::AuthStatus retcode = getAuthDetails(pUserName, isExternal);
if (retcode == STATUS_GOOD && !isUser())
{
*CmpCommon::diags() << DgSqlCode (-CAT_IS_NOT_A_USER)
<< DgString0(getAuthDbName().data());
retcode = STATUS_ERROR;
}
return retcode;
}
// ----------------------------------------------------------------------------
// Public method: registerUser
//
// registers a user in the Trafodion metadata
//
// Input: parse tree containing a definition of the user
// Output: the global diags area is set up with the result
// ----------------------------------------------------------------------------
void CmpSeabaseDDLuser::registerUser(StmtDDLRegisterUser * pNode)
{
// Set up a global try/catch loop to catch unexpected errors
try
{
// Verify user is authorized to perform REGISTER USER requests
if (!verifyAuthority(SQLOperation::MANAGE_USERS))
{
// No authority. We're outta here.
*CmpCommon::diags() << DgSqlCode(-CAT_NOT_AUTHORIZED);
return;
}
// Verify that the specified user name is not reserved
setAuthDbName(pNode->getDbUserName());
if (isAuthNameReserved(pNode->getDbUserName()))
{
*CmpCommon::diags() << DgSqlCode (-CAT_AUTH_NAME_RESERVED)
<< DgString0(pNode->getDbUserName().data());
return;
}
// Verify that the name does not include unsupported special characters
if (!isAuthNameValid(getAuthDbName()))
{
*CmpCommon::diags() << DgSqlCode (-CAT_INVALID_CHARS_IN_AUTH_NAME)
<< DgString0(pNode->getDbUserName().data());
return;
}
setAuthExtName(pNode->getExternalUserName());
if (!isAuthNameValid(getAuthExtName()))
{
*CmpCommon::diags() << DgSqlCode (-CAT_INVALID_CHARS_IN_AUTH_NAME)
<< DgString0(pNode->getExternalUserName().data());
return;
}
// set up class members from parse node
setAuthType(COM_USER_CLASS); // we are a user
setAuthValid(true); // assume a valid user
Int64 createTime = NA_JulianTimestamp();
setAuthCreateTime(createTime);
setAuthRedefTime(createTime); // make redef time the same as create time
// Make sure db user has not already been registered
if (authExists(getAuthDbName(), false))
{
*CmpCommon::diags() << DgSqlCode(-CAT_AUTHID_ALREADY_EXISTS)
<< DgString0(getAuthDbName().data());
return;
}
// unexpected error occurred - ?
if (CmpCommon::diags()->getNumber(DgSqlCode::ERROR_) > 0)
return;
// Make sure external user has not already been registered
if (authExists(getAuthExtName(), true))
{
*CmpCommon::diags() << DgSqlCode(-CAT_LDAP_USER_ALREADY_EXISTS)
<< DgString0(getAuthExtName().data());
return;
}
// unexpected error occurred - ?
if (CmpCommon::diags()->getNumber(DgSqlCode::ERROR_) > 0)
return;
DBUserAuth::AuthenticationConfiguration configurationNumber = DBUserAuth::DefaultConfiguration;
DBUserAuth::AuthenticationConfiguration foundConfigurationNumber = DBUserAuth::DefaultConfiguration;
// Verify that the external user exists in configured identity store
if (!validateExternalUsername(pNode->getExternalUserName().data(),
configurationNumber,
foundConfigurationNumber))
return;
// Get a unique auth ID number
Int32 userID = getUniqueAuthID(MIN_USERID, MAX_USERID);
assert(isUserID(userID));
setAuthID (userID);
// get effective user from the Context
Int32 *pUserID = GetCliGlobals()->currContext()->getDatabaseUserID();
setAuthCreator(*pUserID);
// Add the user to AUTHS table
insertRow();
#if 0
if (pNode->isSchemaSpecified())
{
ExeCliInterface cliInterface(STMTHEAP);
char buf [1000];
NAString csStmt;
COM_SCHEMA_CLASS_PRIVATE = 3,
COM_SCHEMA_CLASS_SHARED = 4,
COM_SCHEMA_CLASS_DEFAULT = 5};
csStmt = "CREATE ";
switch (pNode->getSchemaClass())
{
case COM_SCHEMA_CLASS_SHARED:
csStmt += "SHARED ";
break;
case COM_SCHEMA_CLASS_DEFAULT:
case COM_SCHEMA_CLASS_PRIVATE:
csStmt += "PRIVATE ";
break;
default:
{
SEABASEDDL_INTERNAL_ERROR("Unknown schema class in registerUser");
return;
}
}
csStmt += pNode->getSchemaName()->getCatalogName();
csStmt +- ".";
csStmt += pNode->getSchemaName()->getSchemaName();
str_sprintf(buf, "CREATE %s SCHEMA %s \"%s\".\"%s\".\"%s\" cascade",
(char*)catName.data(), (char*)schName.data(), objName);
cliRC = cliInterface.executeImmediate(buf);
}
#endif
}
catch (...)
{
// At this time, an error should be in the diags area.
// If there is no error, set up an internal error
if (CmpCommon::diags()->getNumber(DgSqlCode::ERROR_) == 0)
SEABASEDDL_INTERNAL_ERROR("Unexpected error in CmpSeabaseDDLuser::registerUser");
}
}
// ----------------------------------------------------------------------------
// public method: unregisterUser
//
// This method removes a user from the database
//
// Input: parse tree containing a definition of the user
// Output: the global diags area is set up with the result
// ----------------------------------------------------------------------------
void CmpSeabaseDDLuser::unregisterUser(StmtDDLRegisterUser * pNode)
{
try
{
// CASCADE option not yet supported
if (pNode->getDropBehavior() == COM_CASCADE_DROP_BEHAVIOR)
{
*CmpCommon::diags() << DgSqlCode (-CAT_ONLY_SUPPORTING_RESTRICT_DROP_BEHAVIOR);
return;
}
// Verify that the specified user name is not reserved
if (isAuthNameReserved(pNode->getDbUserName()))
{
*CmpCommon::diags() << DgSqlCode (-CAT_AUTH_NAME_RESERVED)
<< DgString0(pNode->getDbUserName().data());
return;
}
// read user details from the AUTHS table
const NAString dbUserName(pNode->getDbUserName());
CmpSeabaseDDLauth::AuthStatus retcode = getUserDetails(dbUserName);
if (retcode == STATUS_ERROR)
return;
if (retcode == STATUS_NOTFOUND)
{
*CmpCommon::diags() << DgSqlCode(-CAT_USER_NOT_EXIST)
<< DgString0(dbUserName.data());
return;
}
if (ComUser::getCurrentUser() != getAuthCreator() &&
!verifyAuthority(SQLOperation::MANAGE_USERS))
{
*CmpCommon::diags() << DgSqlCode(-CAT_NOT_AUTHORIZED);
return;
}
NAString whereClause(" WHERE AUTH_TYPE = 'R' AND AUTH_CREATOR = ");
char authIDString[20];
sprintf(authIDString,"%d",getAuthID());
whereClause += authIDString;
if (selectCount(whereClause) > 0)
{
*CmpCommon::diags() << DgSqlCode(-CAT_NO_UNREG_USER_OWNS_ROLES);
return;
}
NAString privMgrMDLoc;
CONCAT_CATSCH(privMgrMDLoc,systemCatalog_.data(),SEABASE_PRIVMGR_SCHEMA);
// User does not own any roles, but may have been granted roles.
if (CmpCommon::context()->isAuthorizationEnabled())
{
PrivMgrRoles role(std::string(MDSchema_.data()),
std::string(privMgrMDLoc.data()),
CmpCommon::diags());
if (role.isUserGrantedAnyRole(getAuthID()))
{
*CmpCommon::diags() << DgSqlCode(-CAT_NO_UNREG_USER_GRANTED_ROLES);
return;
}
}
// Does user own any objects?
NAString whereClause2(" WHERE OBJECT_OWNER = ");
NAString sysCat = CmpSeabaseDDL::getSystemCatalogStatic();
char buf[1000];
str_sprintf(buf, "SELECT COUNT(*) FROM %s.\"%s\".%s %s %d",
sysCat.data(), SEABASE_MD_SCHEMA, SEABASE_OBJECTS,
whereClause2.data(),getAuthID());
Int32 len = 0;
Int64 rowCount = 0;
ExeCliInterface cliInterface(STMTHEAP);
Lng32 cliRC = cliInterface.executeImmediate(buf, (char*)&rowCount, &len, FALSE);
if (cliRC != 0)
{
cliInterface.retrieveSQLDiagnostics(CmpCommon::diags());
UserException excp (NULL, 0);
throw excp;
}
if (rowCount > 0)
{
*CmpCommon::diags() << DgSqlCode(-CAT_NO_UNREG_USER_OWNS_OBJECT);
return;
}
// Is user granted any privileges?
if (CmpCommon::context()->isAuthorizationEnabled())
{
PrivMgr privMgr(std::string(privMgrMDLoc),CmpCommon::diags());
std::vector<PrivClass> privClasses;
privClasses.push_back(PrivClass::ALL);
std::vector<int64_t> objectUIDs;
if (privMgr.isAuthIDGrantedPrivs(getAuthID(),privClasses, objectUIDs))
{
NAString objectName = getObjectName(objectUIDs);
if (objectName.length() > 0)
{
*CmpCommon::diags() << DgSqlCode(-CAT_NO_UNREG_USER_HAS_PRIVS)
<< DgString0(dbUserName.data())
<< DgString1(objectName.data());
return;
}
}
}
// remove any component privileges granted to this user
if (CmpCommon::context()->isAuthorizationEnabled())
{
PrivMgrComponentPrivileges componentPrivileges(privMgrMDLoc.data(),CmpCommon::diags());
std::string componentUIDString = "1";
if (!componentPrivileges.dropAllForGrantee(getAuthID()))
{
UserException excp (NULL, 0);
throw excp;
}
}
// delete the row
deleteRow(getAuthDbName());
}
catch (...)
{
// At this time, an error should be in the diags area.
// If there is no error, set up an internal error
if (CmpCommon::diags()->getNumber(DgSqlCode::ERROR_) == 0)
SEABASEDDL_INTERNAL_ERROR("Switch statement in CmpSeabaseDDLuser::unregisterUser");
}
}
// ----------------------------------------------------------------------------
// public method: alterUser
//
// This method changes a user definition
//
// Input: parse tree containing a definition of the user change
// Output: the global diags area is set up with the result
// ----------------------------------------------------------------------------
void CmpSeabaseDDLuser::alterUser (StmtDDLAlterUser * pNode)
{
try
{
// read user details from the AUTHS table
const NAString dbUserName(pNode->getDatabaseUsername());
CmpSeabaseDDLauth::AuthStatus retcode = getUserDetails(dbUserName);
if (retcode == STATUS_ERROR)
return;
if (retcode == STATUS_NOTFOUND)
{
*CmpCommon::diags() << DgSqlCode(-CAT_USER_NOT_EXIST)
<< DgString0(dbUserName.data());
return;
}
if ((ComUser::getCurrentUser() != getAuthCreator()) &&
!verifyAuthority(SQLOperation::MANAGE_USERS))
{
// No authority. We're outta here.
*CmpCommon::diags() << DgSqlCode(-CAT_NOT_AUTHORIZED);
return;
}
// Process the requested operation
NAString setClause("set ");
switch (pNode->getAlterUserCmdSubType())
{
case StmtDDLAlterUser::SET_EXTERNAL_NAME:
{
// If authExtName already set to specified name, we are done
if (getAuthExtName() == pNode->getExternalUsername())
return;
// Make sure external user has not already been registered
if (authExists(pNode->getExternalUsername(), true))
{
*CmpCommon::diags() << DgSqlCode(-CAT_LDAP_USER_ALREADY_EXISTS)
<< DgString0(pNode->getExternalUsername().data());
return;
}
// unexpected error occurred - ?
if (CmpCommon::diags()->getNumber(DgSqlCode::ERROR_) > 0)
return;
DBUserAuth::AuthenticationConfiguration
configurationNumber = DBUserAuth::DefaultConfiguration;
DBUserAuth::AuthenticationConfiguration
foundConfigurationNumber = DBUserAuth::DefaultConfiguration;
// Verify that the external user exists in configured identity store
if (!validateExternalUsername(pNode->getExternalUsername().data(),
configurationNumber,
foundConfigurationNumber))
return;
setAuthExtName(pNode->getExternalUsername());
setClause += "auth_ext_name = '";
setClause += getAuthExtName();
setClause += "'";
break;
}
case StmtDDLAlterUser::SET_IS_VALID_USER:
{
if (isAuthNameReserved(getAuthDbName()))
{
*CmpCommon::diags() << DgSqlCode(-CAT_AUTH_NAME_RESERVED)
<< DgString0(getAuthDbName().data());
return;
}
setAuthValid(pNode->isValidUser());
setClause += (isAuthValid()) ? "auth_is_valid = 'Y'" : "auth_is_valid = 'N'";
break;
}
default:
{
*CmpCommon::diags() << DgSqlCode (-CAT_UNSUPPORTED_COMMAND_ERROR );
return;
}
}
updateRow(setClause);
}
catch (...)
{
// At this time, an error should be in the diags area.
// If there is no error, set up an internal error
if (CmpCommon::diags()->getNumber(DgSqlCode::ERROR_) == 0)
SEABASEDDL_INTERNAL_ERROR("Switch statement in CmpSeabaseDDLuser::alterUser");
}
}
// ----------------------------------------------------------------------------
// method: registerStandardUser
//
// Creates a standard user ie. (DB__ROOT) in the Trafodion metadata
//
// Input:
// authName
// authID
// ----------------------------------------------------------------------------
void CmpSeabaseDDLuser::registerStandardUser(
const std::string authName,
const int32_t authID)
{
setAuthType(COM_USER_CLASS); // we are a user
createStandardAuth(authName, authID);
}
// -----------------------------------------------------------------------------
// * *
// * Function: validateExternalUsername *
// * *
// * Determines if an external username is valid. *
// * *
// -----------------------------------------------------------------------------
// * *
// * Parameters: *
// * *
// * <externalUsername> const char * In *
// * is the username to be validated. *
// * *
// * <configurationNumber> DBUserAuth::AuthenticationConfiguration In *
// * specifies which configuration to use to validate the username; a *
// * configuration designates one or more identity stores and their *
// * parameters. A value of zero indicates the "default" configuration *
// * should be used. *
// * *
// * <foundConfigurationNumber> DBUserAuth::AuthenticationConfiguration & In *
// * passes back the configuration used to validate the username. *
// * *
// -----------------------------------------------------------------------------
inline static bool validateExternalUsername(
const char * externalUsername,
DBUserAuth::AuthenticationConfiguration configurationNumber,
DBUserAuth::AuthenticationConfiguration & foundConfigurationNumber)
{
// During initialization external checking needs to be disabled to setup
// standard database users.
if (Get_SqlParser_Flags(DISABLE_EXTERNAL_USERNAME_CHECK))
return true;
// Verify that the external username is defined in the identity store.
DBUserAuth::CheckUserResult chkUserRslt = DBUserAuth::UserDoesNotExist;
chkUserRslt = DBUserAuth::CheckExternalUsernameDefined(externalUsername,
configurationNumber,
foundConfigurationNumber);
// Username was found!
if (chkUserRslt == DBUserAuth::UserExists)
return true;
// Who?
if (chkUserRslt == DBUserAuth::UserDoesNotExist)
{
*CmpCommon::diags() << DgSqlCode(-CAT_LDAP_USER_NOT_FOUND)
<< DgString0(externalUsername);
return false;
}
// Problem looking up the username. Could be a bad configuration, a
// problem at the identity store, or communicating with the identity store.
if (chkUserRslt == DBUserAuth::ErrorDuringCheck)
{
*CmpCommon::diags() << DgSqlCode(-CAT_LDAP_COMM_ERROR);
return false;
}
return false;
}
//---------------------- End of validateExternalUsername -----------------------
// -----------------------------------------------------------------------------
// public method: describe
//
// This method returns the showddl text for the requested user in string format
//
// Input:
// authName - name of user to describe
//
// Input/Output:
// authText - the REGISTER USER text
//
// returns result:
// true - successful
// false - failed (ComDiags area will be set up with appropriate error)
//-----------------------------------------------------------------------------
bool CmpSeabaseDDLuser::describe (const NAString &authName, NAString &authText)
{
// If current user matches authName, allow request
NAString currentUserName (ComUser::getCurrentUsername());
if ((currentUserName != authName) && !verifyAuthority(SQLOperation::SHOW))
{
// No authority. We're outta here.
*CmpCommon::diags() << DgSqlCode(-CAT_NOT_AUTHORIZED);
return false;
}
CmpSeabaseDDLauth::AuthStatus retcode = getUserDetails(authName.data());
// If the user was not found, set up an error
if (retcode == STATUS_NOTFOUND)
{
*CmpCommon::diags() << DgSqlCode(-CAT_USER_NOT_EXIST)
<< DgString0(authName.data());
return false;
}
// If an error was detected, return
if (retcode == STATUS_ERROR)
return false;
// Generate output text
authText = "REGISTER USER \"";
authText += getAuthExtName();
if (getAuthExtName() != getAuthDbName())
{
authText += "\" AS \"";
authText += getAuthDbName();
}
authText += "\";\n";
if (!isAuthValid())
{
authText += "ALTER USER \"";
authText += getAuthDbName();
authText += "\" SET OFFLINE;\n";
}
return true;
}
//------------------------------ End of describe -------------------------------
// ****************************************************************************
// Class CmpSeabaseDDLrole methods
// ****************************************************************************
// ----------------------------------------------------------------------------
// Constructors
// ----------------------------------------------------------------------------
CmpSeabaseDDLrole::CmpSeabaseDDLrole(
const NAString & systemCatalog,
const NAString & MDSchema)
: CmpSeabaseDDLauth(systemCatalog,MDSchema)
{}
CmpSeabaseDDLrole::CmpSeabaseDDLrole(const NAString & systemCatalog)
: CmpSeabaseDDLauth()
{
systemCatalog_ = systemCatalog;
CONCAT_CATSCH(MDSchema_,systemCatalog.data(),SEABASE_MD_SCHEMA);
}
CmpSeabaseDDLrole::CmpSeabaseDDLrole()
: CmpSeabaseDDLauth()
{}
// ----------------------------------------------------------------------------
// Public method: createRole
//
// Creates a role in the Trafodion metadata
//
// Input: parse tree containing a definition of the role
// Output: the global diags area is set up with the result
// ----------------------------------------------------------------------------
void CmpSeabaseDDLrole::createRole(StmtDDLCreateRole * pNode)
{
// Don't allow roles to be created unless authorization is enabled
if (!CmpCommon::context()->isAuthorizationEnabled())
{
*CmpCommon::diags() << DgSqlCode(-CAT_AUTHORIZATION_NOT_ENABLED);
return;
}
// Verify user is authorized to perform CREATE ROLE requests
if (!verifyAuthority(SQLOperation::MANAGE_ROLES))
{
*CmpCommon::diags() << DgSqlCode(-CAT_NOT_AUTHORIZED);
return;
}
// Set up a global try/catch loop to catch unexpected errors
try
{
// Verify that the specified role name is not reserved
setAuthDbName(pNode->getRoleName());
if (isAuthNameReserved(getAuthDbName()))
{
*CmpCommon::diags() << DgSqlCode(-CAT_AUTH_NAME_RESERVED)
<< DgString0(getAuthDbName().data());
return;
}
// Verify that the name does not include unsupported special characters
if (!isAuthNameValid(getAuthDbName()))
{
*CmpCommon::diags() << DgSqlCode (-CAT_INVALID_CHARS_IN_AUTH_NAME)
<< DgString0(getAuthDbName().data());
return;
}
// set up class members from parse node
setAuthType(COM_ROLE_CLASS); // we are a role
setAuthValid(true); // assume a valid role
Int64 createTime = NA_JulianTimestamp();
setAuthCreateTime(createTime);
setAuthRedefTime(createTime); // make redef time the same as create time
// Make sure role has not already been registered
if (authExists(getAuthDbName(),false))
{
*CmpCommon::diags() << DgSqlCode(-CAT_AUTHID_ALREADY_EXISTS)
<< DgString0(getAuthDbName().data());
return;
}
// unexpected error occurred - ?
if (CmpCommon::diags()->getNumber(DgSqlCode::ERROR_) > 0)
return;
// Get a unique role ID number
Int32 roleID = getUniqueAuthID(MIN_ROLEID, MAX_ROLEID); //TODO: add role support
assert (isRoleID(roleID));
setAuthID(roleID);
std::string creatorUsername;
// If the WITH ADMIN clause was specified, then create the role on behalf of the
// authorization ID specified in this clause.
// Need to translate the creator name to its authID
if (pNode->getOwner() == NULL)
{
// get effective user from the Context
Int32 userID = ComUser::getCurrentUser();
setAuthCreator(userID);
creatorUsername = ComUser::getCurrentUsername();
}
else
{
const NAString creatorName =
pNode->getOwner()->getAuthorizationIdentifier();
Int32 authID = NA_UserIdDefault;
Int32 result = ComUser::getUserIDFromUserName(creatorName.data(),authID);
if (result != 0)
{
*CmpCommon::diags() << DgSqlCode(-CAT_USER_NOT_EXIST)
<< DgString0(creatorName.data());
return;
}
// TODO: verify creator can create roles
setAuthCreator(authID);
creatorUsername = creatorName.data();
}
// For roles, external and database names are the same.
setAuthExtName(getAuthDbName());
// Add the role to AUTHS table
insertRow();
// Grant this role to the creator of the role if authorization is enabled.
NAString privMgrMDLoc;
CONCAT_CATSCH(privMgrMDLoc,systemCatalog_.data(),SEABASE_PRIVMGR_SCHEMA);
PrivMgrRoles roles(std::string(MDSchema_.data()),std::string(privMgrMDLoc),
CmpCommon::diags());
PrivStatus privStatus = roles.grantRoleToCreator(roleID,
getAuthDbName().data(),
getAuthCreator(),
creatorUsername);
if (privStatus != PrivStatus::STATUS_GOOD)
{
SEABASEDDL_INTERNAL_ERROR("Unable to grant role to role administrator");
return;
}
}
catch (...)
{
// At this time, an error should be in the diags area.
// If there is no error, set up an internal error
if (CmpCommon::diags()->getNumber(DgSqlCode::ERROR_) == 0)
SEABASEDDL_INTERNAL_ERROR("Switch statement in CmpSeabaseDDLrole::createRole");
}
}
// ----------------------------------------------------------------------------
// Public method: createStandardRole
//
// Creates a standard role (ie. DB__nameROLE) in the Trafodion metadata
//
// Input:
// role name
// role ID
//
// returns: true - added role,
// false - did not add role
// ----------------------------------------------------------------------------
bool CmpSeabaseDDLrole::createStandardRole(
const std::string roleName,
const int32_t roleID)
{
setAuthType(COM_ROLE_CLASS); // we are a role
return createStandardAuth(roleName, roleID);
}
// -----------------------------------------------------------------------------
// public method: describe
//
// This method returns the showddl text for the requested role in string format
//
// Input:
// roleName - name of role to describe
//
// Input/Output:
// roleText - the CREATE ROLE (and GRANT ROLE) text
//
// returns result:
// true - successful
// false - failed (ComDiags area will be set up with appropriate error)
//-----------------------------------------------------------------------------
bool CmpSeabaseDDLrole::describe(
const NAString & roleName,
NAString &roleText)
{
try
{
// Can current user perform request
Int32 roleID = NA_UserIdDefault;
if (ComUser::getAuthIDFromAuthName(roleName.data(), roleID) != 0)
roleID = NA_UserIdDefault;
if (!ComUser::currentUserHasRole(roleID) && !verifyAuthority(SQLOperation::SHOW))
{
// No authority. We're outta here.
*CmpCommon::diags() << DgSqlCode(-CAT_NOT_AUTHORIZED);
return false;
}
CmpSeabaseDDLauth::AuthStatus retcode = getRoleDetails(roleName.data());
// If the role was not found, set up an error
if (retcode == STATUS_NOTFOUND)
{
*CmpCommon::diags() << DgSqlCode(-CAT_ROLE_NOT_EXIST)
<< DgString0(roleName.data());
return false;
}
// If an error was detected, throw an exception so the catch handler will
// put a value in ComDiags area in case no message exists
if (retcode == STATUS_ERROR)
return false;
// Generate output text
roleText = "CREATE ROLE \"";
roleText += getAuthDbName();
roleText += "\"";
// If the role owner is not DB__ROOT, list the user who administers the role.
if (getAuthCreator() != ComUser::getRootUserID())
{
roleText += " WITH ADMIN \"";
char creatorName[MAX_DBUSERNAME_LEN + 1];
int32_t length = 0;
Int16 retCode = ComUser::getAuthNameFromAuthID(getAuthCreator(),creatorName,
sizeof(creatorName),length);
if (retCode != 0)
{
SEABASEDDL_INTERNAL_ERROR("Role administrator not registered");
UserException excp (NULL, 0);
throw excp;
}
roleText += creatorName;
roleText += "\"";
}
roleText += ";\n";
// See if authorization is enabled. If so, need to list any grants of this
// role. Otherwise, we are outta here.
if (!CmpCommon::context()->isAuthorizationEnabled())
return true;
NAString privMgrMDLoc;
CONCAT_CATSCH(privMgrMDLoc,systemCatalog_.data(),SEABASE_PRIVMGR_SCHEMA);
PrivMgrRoles roles(std::string(MDSchema_.data()),std::string(privMgrMDLoc),
CmpCommon::diags());
std::vector<std::string> granteeNames;
std::vector<int32_t> grantDepths;
std::vector<int32_t> grantorIDs;
PrivStatus privStatus = PrivStatus::STATUS_GOOD;
privStatus = roles.fetchUsersForRole(getAuthID(),granteeNames,
grantorIDs,grantDepths);
// If no users were granted this role, nothing to do.
if (privStatus == PrivStatus::STATUS_NOTFOUND || granteeNames.size() == 0)
return true;
if (privStatus == PrivStatus::STATUS_ERROR)
SEABASEDDL_INTERNAL_ERROR("Could not fetch users granted role.");
// If CQD to display privilege grants is off, return now
if ((CmpCommon::getDefault(SHOWDDL_DISPLAY_PRIVILEGE_GRANTS) == DF_OFF) ||
((CmpCommon::getDefault(SHOWDDL_DISPLAY_PRIVILEGE_GRANTS) == DF_SYSTEM)
&& getenv("SQLMX_REGRESS")))
return true;
// Report on each grantee.
for (size_t r = 0; r < granteeNames.size(); r++)
{
// If the grantor is system, we want to show the grant, but exclude
// it from executing in a playback script.
if (grantorIDs[r] == ComUser::getSystemUserID())
roleText += "-- ";
roleText += "GRANT ROLE \"";
roleText += getAuthDbName();
roleText += "\" TO \"";
roleText += granteeNames[r].c_str();
roleText += "\"";
// Grant depth is either zero or non-zero for now. If non-zero,
// report WITH ADMIN OPTION.
if (grantDepths[r] != 0)
roleText += " WITH ADMIN OPTION";
// If the grantor is not DB__ROOT or _SYSTEM, list the grantor.
if (grantorIDs[r] != ComUser::getRootUserID() &&
grantorIDs[r] != ComUser::getSystemUserID())
{
roleText += " GRANTED BY \"";
char grantorName[MAX_DBUSERNAME_LEN + 1];
int32_t length = 0;
Int16 retCode = ComUser::getAuthNameFromAuthID(grantorIDs[r],
grantorName,
sizeof(grantorName),
length);
if (retCode != 0)
{
SEABASEDDL_INTERNAL_ERROR("Role grantor not registered");
UserException excp(NULL,0);
throw excp;
}
roleText += grantorName;
roleText += "\"";
}
roleText += ";\n";
}
}
catch (...)
{
// At this time, an error should be in the diags area.
// If there is no error, set up an internal error
if (CmpCommon::diags()->getNumber(DgSqlCode::ERROR_) == 0)
SEABASEDDL_INTERNAL_ERROR("Switch statement in CmpSeabaseDDLrole::describe");
return false;
}
return true;
}
//------------------------------ End of describe -------------------------------
// ----------------------------------------------------------------------------
// Public method: dropRole
//
// Drops a role from the Trafodion metadata
//
// Input: parse tree containing a definition of the role
// Output: the global diags area is set up with the result
// ----------------------------------------------------------------------------
void CmpSeabaseDDLrole::dropRole(StmtDDLCreateRole * pNode)
{
// Set up a global try/catch loop to catch unexpected errors
try
{
const NAString roleName(pNode->getRoleName());
// Verify that the specified user name is not reserved
setAuthDbName(roleName);
if (isAuthNameReserved(getAuthDbName()))
{
*CmpCommon::diags() << DgSqlCode(-CAT_AUTH_NAME_RESERVED)
<< DgString0(roleName.data());
return;
}
// read role details from the AUTHS table
CmpSeabaseDDLauth::AuthStatus retcode = getRoleDetails(roleName);
if (retcode == STATUS_ERROR)
return;
if (retcode == STATUS_NOTFOUND)
{
*CmpCommon::diags() << DgSqlCode(-CAT_ROLE_NOT_EXIST)
<< DgString0(roleName.data());
return;
}
if (ComUser::getCurrentUser() != getAuthCreator())
{
// If the user does not have privilege, allow the drop if
// the user has been granted an admin role and
// the role being dropped is not an admin role and
// the authCreator of the role being dropped matches the admin role
if (verifyAuthority(SQLOperation::MANAGE_ROLES) == false)
{
*CmpCommon::diags() << DgSqlCode(-CAT_NOT_AUTHORIZED);
return;
}
}
NAString privMgrMDLoc;
CONCAT_CATSCH(privMgrMDLoc,systemCatalog_.data(),SEABASE_PRIVMGR_SCHEMA);
PrivMgrRoles role(std::string(MDSchema_.data()),std::string(privMgrMDLoc),
CmpCommon::diags());
// If authorization is not enabled and a role has been defined, skip
// looking for dependencies and just remove the role from auths.
if (CmpCommon::context()->isAuthorizationEnabled())
{
//TODO: Could support a CASCADE option that would clean up
// grants and dependent objects.
// First, see if role has been granted
bool roleIsGranted = role.isGranted(getAuthID(),true);
if (roleIsGranted)
{
*CmpCommon::diags() << DgSqlCode(-CAT_ROLE_IS_GRANTED_NO_DROP);
return;
}
// Now see if the role has been granted any privileges.
// TODO: could allow priv grants if no dependent objects.
PrivMgr privMgr(std::string(privMgrMDLoc),CmpCommon::diags());
std::vector<PrivClass> privClasses;
privClasses.push_back(PrivClass::ALL);
std::vector<int64_t> objectUIDs;
if (privMgr.isAuthIDGrantedPrivs(getAuthID(),privClasses, objectUIDs))
{
NAString objectName = getObjectName(objectUIDs);
if (objectName.length() > 0)
{
*CmpCommon::diags() << DgSqlCode(-CAT_ROLE_HAS_PRIVS_NO_DROP)
<< DgString0(roleName.data())
<< DgString1(objectName.data());
return;
}
}
// Role has not been granted and no privileges have been granted to
// the role. Remove the system grant.
PrivStatus privStatus = role.revokeRoleFromCreator(getAuthID(),
getAuthCreator());
if (privStatus != PrivStatus::STATUS_GOOD)
{
SEABASEDDL_INTERNAL_ERROR("Unable to remove grant to role administrator");
return;
}
}
// delete the row
deleteRow(roleName);
}
catch (...)
{
// At this time, an error should be in the diags area.
// If there is no error, set up an internal error
if (CmpCommon::diags()->getNumber(DgSqlCode::ERROR_) == 0)
SEABASEDDL_INTERNAL_ERROR("Switch statement in CmpSeabaseDDLrole::dropRole");
}
}
// ----------------------------------------------------------------------------
// Public method: dropStandardRole
//
// Drops a standard role (ie. DB__nameROLE) from the Trafodion metadata
//
// Input: role name
// ----------------------------------------------------------------------------
void CmpSeabaseDDLrole::dropStandardRole(const std::string roleName)
{
// Verify name is a standard name
size_t prefixLength = strlen(RESERVED_AUTH_NAME_PREFIX);
if (roleName.size() <= prefixLength ||
roleName.compare(0,prefixLength,RESERVED_AUTH_NAME_PREFIX) != 0)
{
*CmpCommon::diags() << DgSqlCode(-CAT_ROLE_NOT_EXIST)
<< DgString0(roleName.c_str());
return;
}
// delete the row
deleteRow(roleName.c_str());
}
// ----------------------------------------------------------------------------
// public method: getRoleDetails
//
// Create the CmpSeabaseDDLuser class containing user details for the
// requested username
//
// Input:
// userName - the database username to retrieve details for
// isExternal -
// true - the username is the external name (auth_ext_name)
// false - the username is the database name (auth_db_name)
//
// Output:
// Returned parameter:
// STATUS_GOOD: authorization details are populated:
// STATUS_NOTFOUND: authorization details were not found
// STATUS_WARNING: (not 100) warning was returned, diags area populated
// STATUS_ERROR: error was returned, diags area populated
// ----------------------------------------------------------------------------
CmpSeabaseDDLauth::AuthStatus
CmpSeabaseDDLrole::getRoleDetails(const char *pRoleName)
{
CmpSeabaseDDLauth::AuthStatus retcode = getAuthDetails(pRoleName,false);
if (retcode == STATUS_GOOD && !isRole())
{
*CmpCommon::diags() << DgSqlCode (-CAT_IS_NOT_A_ROLE)
<< DgString0(getAuthDbName().data());
retcode = STATUS_ERROR;
}
return retcode;
}
// ----------------------------------------------------------------------------
// Public method: getRoleIDFromRoleName
//
// Lookup a role name in the Trafodion metadata
//
// Input: Role name to lookup
// Output: Role ID if role was found
// true returned if role found
// false returned if role not found
// ----------------------------------------------------------------------------
bool CmpSeabaseDDLrole::getRoleIDFromRoleName(
const char * roleName,
Int32 & roleID)
{
CmpSeabaseDDLauth::AuthStatus authStatus = getAuthDetails(roleName,false);
if (authStatus != STATUS_GOOD && authStatus != STATUS_WARNING)
return false;
if (getAuthType() != COM_ROLE_CLASS)
return false;
roleID = getAuthID();
return true;
}