OmniSciDB  72c90bc290
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
SysCatalog.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2022 HEAVY.AI, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
23 #include "SysCatalog.h"
24 #include <algorithm>
25 #include <cassert>
26 #include <exception>
27 #include <filesystem>
28 #include <list>
29 #include <memory>
30 #include <random>
31 #include <sstream>
32 #include <string_view>
33 #include "Catalog.h"
34 
35 #include "Catalog/AuthMetadata.h"
37 
38 #include <boost/algorithm/string/predicate.hpp>
39 #include <boost/filesystem.hpp>
40 #include <boost/range/adaptor/map.hpp>
41 #include <boost/version.hpp>
42 
43 #include "MapDRelease.h"
44 #include "Parser/ParserNode.h"
45 #include "RWLocks.h"
46 #include "Shared/File.h"
47 #include "Shared/StringTransform.h"
48 #include "Shared/SysDefinitions.h"
49 #include "Shared/measure.h"
50 #include "Shared/misc.h"
51 #include "include/bcrypt.h"
52 
53 using std::list;
54 using std::map;
55 using std::pair;
56 using std::runtime_error;
57 using std::string;
58 using std::vector;
59 
60 using namespace std::string_literals;
61 
62 std::string g_base_path;
65 
66 extern bool g_enable_fsi;
67 extern bool g_read_only;
68 
69 namespace {
70 
71 std::string hash_with_bcrypt(const std::string& pwd) {
72  char salt[BCRYPT_HASHSIZE], hash[BCRYPT_HASHSIZE];
73  CHECK(bcrypt_gensalt(-1, salt) == 0);
74  CHECK(bcrypt_hashpw(pwd.c_str(), salt, hash) == 0);
75  return std::string(hash, BCRYPT_HASHSIZE);
76 }
77 
78 // This catalog copy must take place before any other catalog accesses.
79 std::filesystem::path copy_catalog_if_read_only(std::filesystem::path base_data_path) {
80  std::filesystem::path catalog_base_data_path;
81 
82  // For a read-only server, make a temporary copy of the catalog directory.
83  // This catalog copy must take place before any other catalog accesses.
84  if (!g_read_only) {
85  // Catalog directory will be in the normal location.
86  catalog_base_data_path = base_data_path;
87  } else {
88  // Catalog directory will be in a temporary location.
89  catalog_base_data_path = base_data_path / "temporary";
90 
91  // Delete the temporary directory if it exists.
92  // The name "temporary" is hardcoded so nobody should object to its deletion!
93  CHECK_NE(catalog_base_data_path.string().find("temporary"), std::string::npos);
94  CHECK_NE(catalog_base_data_path, base_data_path);
95  if (std::filesystem::exists(catalog_base_data_path)) {
96  std::filesystem::remove_all(catalog_base_data_path);
97  }
98  std::filesystem::create_directories(catalog_base_data_path);
99 
100  // Make the temporary copy of the catalog.
101  const auto normal_catalog_path = base_data_path / shared::kCatalogDirectoryName;
102  const auto temporary_catalog_path =
103  catalog_base_data_path / shared::kCatalogDirectoryName;
104  LOG(INFO) << "copying catalog from " << normal_catalog_path << " to "
105  << temporary_catalog_path << " for read-only server";
106  std::filesystem::copy(normal_catalog_path,
107  temporary_catalog_path,
108  std::filesystem::copy_options::recursive);
109 
110  // Create a temporary empty directory structure similar to how initheavy would.
111  // Not expected to be used. Created just in case any code tries to access them.
112  try {
113  std::filesystem::create_directories(catalog_base_data_path /
115  } catch (...) {
116  }
117  try {
118  std::filesystem::create_directories(catalog_base_data_path /
120  } catch (...) {
121  }
122  try {
123  std::filesystem::create_directories(catalog_base_data_path /
125  } catch (...) {
126  }
127  try {
128  std::filesystem::create_directories(catalog_base_data_path /
131  } catch (...) {
132  }
133  try {
134  std::filesystem::create_directories(catalog_base_data_path /
137  } catch (...) {
138  }
139  }
140 
141  return catalog_base_data_path;
142 }
143 
144 } // namespace
145 
146 namespace Catalog_Namespace {
147 
148 thread_local bool SysCatalog::thread_holds_read_lock = false;
149 std::mutex SysCatalog::instance_mutex_;
150 std::unique_ptr<SysCatalog> SysCatalog::instance_;
151 
152 using sys_read_lock = read_lock<SysCatalog>;
155 
156 bool g_log_user_id{false}; // --log-user-id
157 
158 std::string UserMetadata::userLoggable() const {
159  return g_log_user_id ? std::to_string(userId) : userName;
160 }
161 
162 auto CommonFileOperations::assembleCatalogName(std::string const& name) {
163  return base_path_ + "/" + shared::kCatalogDirectoryName + "/" + name;
164 };
165 
166 void CommonFileOperations::removeCatalogByFullPath(std::string const& full_path) {
167  boost::filesystem::remove(full_path);
168 }
169 
170 void CommonFileOperations::removeCatalogByName(std::string const& name) {
171  boost::filesystem::remove(assembleCatalogName(name));
172 };
173 
174 auto CommonFileOperations::duplicateAndRenameCatalog(std::string const& current_name,
175  std::string const& new_name) {
176  auto full_current_path = assembleCatalogName(current_name);
177  auto full_new_path = assembleCatalogName(new_name);
178 
179  try {
180  boost::filesystem::copy_file(full_current_path, full_new_path);
181  } catch (std::exception& e) {
182  std::string err_message{"Could not copy file " + full_current_path + " to " +
183  full_new_path + " exception was " + e.what()};
184  LOG(ERROR) << err_message;
185  throw std::runtime_error(err_message);
186  }
187 
188  return std::make_pair(full_current_path, full_new_path);
189 };
190 
191 void SysCatalog::init(const std::string& basePath,
192  std::shared_ptr<Data_Namespace::DataMgr> dataMgr,
193  const AuthMetadata& authMetadata,
194  std::shared_ptr<Calcite> calcite,
195  bool is_new_db,
196  bool aggregator,
197  const std::vector<LeafHostInfo>& string_dict_hosts) {
198  basePath_ = !g_multi_instance ? copy_catalog_if_read_only(basePath).string() : basePath;
199  sqliteConnector_.reset(new SqliteConnector(
201  dcatalogMutex_ = std::make_unique<heavyai::DistributedSharedMutex>(
202  std::filesystem::path(basePath_) / shared::kLockfilesDirectoryName /
204  [this](size_t) {
206  *dsqliteMutex_);
207  buildMapsUnlocked();
208  });
209  dsqliteMutex_ = std::make_unique<heavyai::DistributedSharedMutex>(
210  std::filesystem::path(basePath_) / shared::kLockfilesDirectoryName /
214  dataMgr_ = dataMgr;
215  authMetadata_ = &authMetadata;
216  pki_server_.reset(new PkiServer(*authMetadata_));
217  calciteMgr_ = calcite;
218  string_dict_hosts_ = string_dict_hosts;
219  aggregator_ = aggregator;
221  if (is_new_db) {
222  initDB();
223  } else {
224  bool db_exists =
225  boost::filesystem::exists(basePath_ + "/" + shared::kCatalogDirectoryName + "/" +
227  if (!db_exists) {
228  importDataFromOldMapdDB();
229  }
231  checkAndExecuteMigrations();
233  }
234  }
235  buildMaps(is_new_db);
236  is_initialized_ = true;
237 }
238 
239 bool SysCatalog::isInitialized() const {
240  return is_initialized_;
241 };
242 
243 void SysCatalog::buildMaps(bool is_new_db) {
246 
247  buildMapsUnlocked(is_new_db);
248 }
249 
250 void SysCatalog::buildMapsUnlocked(bool is_new_db) {
251  VLOG(2) << "reloading catalog caches for: " << shared::kSystemCatalogName;
252 
253  // Store permissions for temporary users.
254  std::map<std::string, std::vector<std::string>> tu_map;
255  for (auto& pair : temporary_users_by_name_) {
256  CHECK(pair.second);
257  UserMetadata& user = *pair.second;
258  auto it = granteeMap_.find(to_upper(user.userName));
259  CHECK(it != granteeMap_.end()) << to_upper(user.userName) << " not found";
260 
261  auto user_rl = dynamic_cast<User*>(it->second.get());
262  CHECK(user_rl);
263  std::vector<std::string> current_roles = user_rl->getRoles();
264  auto result = tu_map.emplace(user.userName, std::move(current_roles));
265  CHECK(result.second);
266  }
267 
268  // Forget permissions and reload them from file storage.
269  buildRoleMapUnlocked();
270  buildUserRoleMapUnlocked();
271  buildObjectDescriptorMapUnlocked();
272  if (!is_new_db) {
273  // We don't want to create the information schema db during database initialization
274  // because we don't have the appropriate context to intialize the tables. For
275  // instance if the server is intended to run in distributed mode, initializing the
276  // table as part of initdb will be missing information such as the location of the
277  // string dictionary server.
278  initializeInformationSchemaDb();
279  }
280 
281  // Restore permissions for temporary users that were stored above.
282  for (auto& pair : temporary_users_by_name_) {
283  CHECK(pair.second);
284  UserMetadata& user = *pair.second;
285 
286  createRole_unsafe(user.userName, /*user_private_role*/ true, /*is_temporary*/ true);
287 
288  auto it = tu_map.find(user.userName);
289  CHECK(it != tu_map.end()) << user.userName << " not found";
290  for (const auto& r : it->second) {
291  grantRole_unsafe(r, user.userName, /*is_temporary*/ true);
292  }
293  }
294 }
295 
296 SysCatalog::SysCatalog()
298  , aggregator_{false}
299  , sqliteMutex_{}
300  , sharedMutex_{}
301  , thread_holding_sqlite_lock{std::thread::id()}
302  , thread_holding_write_lock{std::thread::id()}
303  , dummyCatalog_{std::make_shared<Catalog>()} {}
304 
306  // TODO(sy): Need to lock here to wait for other threads to complete before pulling out
307  // the rug from under them. Unfortunately this lock was seen to deadlock because the
308  // HeavyDB shutdown sequence needs cleanup. Do we even need these clear()'s anymore?
309  // sys_write_lock write_lock(this);
310  granteeMap_.clear();
311  objectDescriptorMap_.clear();
312  cat_map_.clear();
313 }
314 
316  if (g_read_only) {
317  throw std::runtime_error("can't init a new database in read-only mode");
318  }
320  sqliteConnector_->query("BEGIN TRANSACTION");
321  try {
322  sqliteConnector_->query(
323  "CREATE TABLE mapd_users (userid integer primary key, name text unique, "
324  "passwd_hash text, issuper boolean, default_db integer references "
325  "mapd_databases, can_login boolean)");
326  sqliteConnector_->query_with_text_params(
327  "INSERT INTO mapd_users VALUES (?, ?, ?, 1, NULL, 1)",
328  std::vector<std::string>{shared::kRootUserIdStr,
331  sqliteConnector_->query(
332  "CREATE TABLE mapd_databases (dbid integer primary key, name text unique, owner "
333  "integer references mapd_users)");
334  sqliteConnector_->query(
335  "CREATE TABLE mapd_roles(roleName text, userName text, UNIQUE(roleName, "
336  "userName))");
337  sqliteConnector_->query(
338  "CREATE TABLE mapd_object_permissions ("
339  "roleName text, "
340  "roleType bool, "
341  "dbId integer references mapd_databases, "
342  "objectName text, "
343  "objectId integer, "
344  "objectPermissionsType integer, "
345  "objectPermissions integer, "
346  "objectOwnerId integer, UNIQUE(roleName, objectPermissionsType, dbId, "
347  "objectId))");
348  } catch (const std::exception&) {
349  sqliteConnector_->query("ROLLBACK TRANSACTION");
350  throw;
351  }
352  sqliteConnector_->query("END TRANSACTION");
355  shared::kRootUsername, /*userPrivateRole=*/true, /*is_temporary=*/false);
356 }
357 
360  createRoles();
364  updateUserSchema(); // must come before updatePasswordsToHashes()
366  updateBlankPasswordsToRandom(); // must come after updatePasswordsToHashes()
370 }
371 
374 
375  // check to see if the new column already exists
376  sqliteConnector_->query("PRAGMA TABLE_INFO(mapd_users)");
377  for (size_t i = 0; i < sqliteConnector_->getNumRows(); i++) {
378  const auto& col_name = sqliteConnector_->getData<std::string>(i, 1);
379  if (col_name == "default_db") {
380  return; // new column already exists
381  }
382  }
383 
384  // create the new column
385  sqliteConnector_->query("BEGIN TRANSACTION");
386  try {
387  sqliteConnector_->query(
388  "ALTER TABLE mapd_users ADD COLUMN default_db INTEGER REFERENCES mapd_databases");
389  } catch (const std::exception& e) {
390  sqliteConnector_->query("ROLLBACK TRANSACTION");
391  throw;
392  }
393  sqliteConnector_->query("END TRANSACTION");
394 }
395 
398  std::string mapd_db_path = basePath_ + "/" + shared::kCatalogDirectoryName + "/mapd";
399  sqliteConnector_->query("ATTACH DATABASE `" + mapd_db_path + "` as old_cat");
400  sqliteConnector_->query("BEGIN TRANSACTION");
401  try {
402  LOG(INFO) << "Moving global metadata into a separate catalog";
403  auto moveTableIfExists = [conn = sqliteConnector_.get()](const std::string& tableName,
404  bool deleteOld = true) {
405  conn->query("SELECT sql FROM old_cat.sqlite_master WHERE type='table' AND name='" +
406  tableName + "'");
407  if (conn->getNumRows() != 0) {
408  conn->query(conn->getData<string>(0, 0));
409  conn->query("INSERT INTO " + tableName + " SELECT * FROM old_cat." + tableName);
410  if (deleteOld) {
411  conn->query("DROP TABLE old_cat." + tableName);
412  }
413  }
414  };
415  moveTableIfExists("mapd_users");
416  moveTableIfExists("mapd_databases");
417  moveTableIfExists("mapd_roles");
418  moveTableIfExists("mapd_object_permissions");
419  moveTableIfExists("mapd_privileges");
420  moveTableIfExists("mapd_version_history", false);
421  } catch (const std::exception& e) {
422  sqliteConnector_->query("ROLLBACK TRANSACTION");
423  LOG(ERROR) << "Failed to move global metadata into a separate catalog: " << e.what();
424  try {
425  sqliteConnector_->query("DETACH DATABASE old_cat");
426  } catch (const std::exception&) {
427  // nothing to do here
428  }
429  throw;
430  }
431  sqliteConnector_->query("END TRANSACTION");
432  const std::string sys_catalog_path =
433  basePath_ + "/" + shared::kCatalogDirectoryName + "/" + shared::kSystemCatalogName;
434  LOG(INFO) << "Global metadata has been successfully moved into a separate catalog: "
435  << sys_catalog_path
436  << ". Using this database with an older version of heavydb "
437  "is now impossible.";
438  try {
439  sqliteConnector_->query("DETACH DATABASE old_cat");
440  } catch (const std::exception&) {
441  // nothing to do here
442  }
443 }
444 
447  sqliteConnector_->query("BEGIN TRANSACTION");
448  try {
449  sqliteConnector_->query(
450  "SELECT name FROM sqlite_master WHERE type='table' AND name='mapd_roles'");
451  if (sqliteConnector_->getNumRows() != 0) {
452  // already done
453  sqliteConnector_->query("END TRANSACTION");
454  return;
455  }
456  sqliteConnector_->query(
457  "CREATE TABLE mapd_roles(roleName text, userName text, UNIQUE(roleName, "
458  "userName))");
459  } catch (const std::exception&) {
460  sqliteConnector_->query("ROLLBACK TRANSACTION");
461  throw;
462  }
463  sqliteConnector_->query("END TRANSACTION");
464 }
465 
466 /*
467  There was an error in how we migrated users from pre-4.0 versions where we would copy
468  all user names into the mapd_roles table. This table should never have usernames in it
469  (the correct migration was to copy users into the mapd_object_permissions table instead)
470  so this migration function prunes such cases out.
471  */
474  sqliteConnector_->query("BEGIN TRANSACTION");
475  try {
476  sqliteConnector_->query("SELECT name FROM mapd_users");
477  auto num_rows = sqliteConnector_->getNumRows();
478  std::vector<std::string> user_names;
479  for (size_t i = 0; i < num_rows; ++i) {
480  user_names.push_back(sqliteConnector_->getData<std::string>(i, 0));
481  }
482  for (const auto& user_name : user_names) {
483  sqliteConnector_->query_with_text_param("DELETE FROM mapd_roles WHERE roleName = ?",
484  user_name);
485  }
486  } catch (const std::exception&) {
487  sqliteConnector_->query("ROLLBACK TRANSACTION");
488  throw;
489  }
490  sqliteConnector_->query("END TRANSACTION");
491 }
492 
493 namespace {
494 
495 void deleteObjectPrivileges(std::unique_ptr<SqliteConnector>& sqliteConnector,
496  std::string roleName,
497  bool userRole,
498  DBObject& object) {
499  DBObjectKey key = object.getObjectKey();
500 
501  sqliteConnector->query_with_text_params(
502  "DELETE FROM mapd_object_permissions WHERE roleName = ?1 and roleType = ?2 and "
503  "objectPermissionsType = ?3 and "
504  "dbId = "
505  "?4 "
506  "and objectId = ?5",
507  std::vector<std::string>{roleName,
508  std::to_string(userRole),
510  std::to_string(key.dbId),
511  std::to_string(key.objectId)});
512 }
513 
514 void insertOrUpdateObjectPrivileges(std::unique_ptr<SqliteConnector>& sqliteConnector,
515  std::string roleName,
516  bool userRole,
517  const DBObject& object) {
518  CHECK(object.valid());
519  DBObjectKey key = object.getObjectKey();
520 
521  sqliteConnector->query_with_text_params(
522  "INSERT OR REPLACE INTO mapd_object_permissions("
523  "roleName, "
524  "roleType, "
525  "objectPermissionsType, "
526  "dbId, "
527  "objectId, "
528  "objectPermissions, "
529  "objectOwnerId,"
530  "objectName) "
531  "VALUES (?1, ?2, ?3, "
532  "?4, ?5, ?6, ?7, ?8)",
533  std::vector<std::string>{
534  roleName, // roleName
535  userRole ? "1" : "0", // roleType
536  std::to_string(key.permissionType), // permissionType
537  std::to_string(key.dbId), // dbId
538  std::to_string(key.objectId), // objectId
539  std::to_string(object.getPrivileges().privileges), // objectPrivileges
540  std::to_string(object.getOwner()), // objectOwnerId
541  object.getName() // name
542  });
543 }
544 
545 } // namespace
546 
549  sqliteConnector_->query("BEGIN TRANSACTION");
550  try {
551  sqliteConnector_->query(
552  "SELECT name FROM sqlite_master WHERE type='table' AND "
553  "name='mapd_object_permissions'");
554  if (sqliteConnector_->getNumRows() != 0) {
555  // already done
556  sqliteConnector_->query("END TRANSACTION");
557  return;
558  }
559 
560  sqliteConnector_->query(
561  "CREATE TABLE IF NOT EXISTS mapd_object_permissions ("
562  "roleName text, "
563  "roleType bool, "
564  "dbId integer references mapd_databases, "
565  "objectName text, "
566  "objectId integer, "
567  "objectPermissionsType integer, "
568  "objectPermissions integer, "
569  "objectOwnerId integer, UNIQUE(roleName, objectPermissionsType, dbId, "
570  "objectId))");
571 
572  // get the list of databases and their grantees
573  sqliteConnector_->query(
574  "SELECT userid, dbid FROM mapd_privileges WHERE select_priv = 1 and insert_priv "
575  "= 1");
576  size_t numRows = sqliteConnector_->getNumRows();
577  vector<pair<int, int>> db_grantees(numRows);
578  for (size_t i = 0; i < numRows; ++i) {
579  db_grantees[i].first = sqliteConnector_->getData<int>(i, 0);
580  db_grantees[i].second = sqliteConnector_->getData<int>(i, 1);
581  }
582  // map user names to user ids
583  sqliteConnector_->query("select userid, name from mapd_users");
584  numRows = sqliteConnector_->getNumRows();
585  std::unordered_map<int, string> users_by_id;
586  std::unordered_map<int, bool> user_has_privs;
587  for (size_t i = 0; i < numRows; ++i) {
588  users_by_id[sqliteConnector_->getData<int>(i, 0)] =
589  sqliteConnector_->getData<string>(i, 1);
590  user_has_privs[sqliteConnector_->getData<int>(i, 0)] = false;
591  }
592  // map db names to db ids
593  sqliteConnector_->query("select dbid, name from mapd_databases");
594  numRows = sqliteConnector_->getNumRows();
595  std::unordered_map<int, string> dbs_by_id;
596  for (size_t i = 0; i < numRows; ++i) {
597  dbs_by_id[sqliteConnector_->getData<int>(i, 0)] =
598  sqliteConnector_->getData<string>(i, 1);
599  }
600  // migrate old privileges to new privileges: if user had insert access to database, he
601  // was a grantee
602  for (const auto& grantee : db_grantees) {
603  user_has_privs[grantee.first] = true;
604  auto dbName = dbs_by_id[grantee.second];
605  {
606  // table level permissions
608  DBObjectKey key{type, grantee.second};
609  DBObject object(
612  sqliteConnector_, users_by_id[grantee.first], true, object);
613  }
614 
615  {
616  // dashboard level permissions
618  DBObjectKey key{type, grantee.second};
619  DBObject object(dbName,
620  type,
621  key,
625  sqliteConnector_, users_by_id[grantee.first], true, object);
626  }
627 
628  {
629  // view level permissions
631  DBObjectKey key{type, grantee.second};
632  DBObject object(
635  sqliteConnector_, users_by_id[grantee.first], true, object);
636  }
637  }
638  for (auto user : user_has_privs) {
639  auto dbName = dbs_by_id[0];
640  if (user.second == false && user.first != shared::kRootUserId) {
641  {
643  DBObjectKey key{type, 0};
646  sqliteConnector_, users_by_id[user.first], true, object);
647  }
648  }
649  }
650  } catch (const std::exception&) {
651  sqliteConnector_->query("ROLLBACK TRANSACTION");
652  throw;
653  }
654  sqliteConnector_->query("END TRANSACTION");
655 }
656 
659  sqliteConnector_->query("BEGIN TRANSACTION");
660  try {
661  sqliteConnector_->query(
662  "SELECT roleName FROM mapd_object_permissions WHERE roleName = \'" +
663  shared::kRootUsername + "\'");
664  if (sqliteConnector_->getNumRows() != 0) {
665  // already done
666  sqliteConnector_->query("END TRANSACTION");
667  return;
668  }
669 
671  shared::kRootUsername, /*userPrivateRole=*/true, /*is_temporary=*/false);
672  } catch (const std::exception&) {
673  sqliteConnector_->query("ROLLBACK TRANSACTION");
674  throw;
675  }
676  sqliteConnector_->query("END TRANSACTION");
677 }
678 
681  sqliteConnector_->query("BEGIN TRANSACTION");
682  try {
683  sqliteConnector_->query(
684  "SELECT name FROM sqlite_master WHERE type='table' AND name='mapd_users'");
685  if (sqliteConnector_->getNumRows() == 0) {
686  // Nothing to update
687  sqliteConnector_->query("END TRANSACTION");
688  return;
689  }
690  sqliteConnector_->query("PRAGMA TABLE_INFO(mapd_users)");
691  for (size_t i = 0; i < sqliteConnector_->getNumRows(); i++) {
692  const auto& col_name = sqliteConnector_->getData<std::string>(i, 1);
693  if (col_name == "passwd_hash") {
694  sqliteConnector_->query("END TRANSACTION");
695  return;
696  }
697  }
698  // Alas, SQLite can't drop columns so we have to recreate the table
699  // (or, optionally, add the new column and reset the old one to a bunch of nulls)
700  sqliteConnector_->query("SELECT userid, passwd FROM mapd_users");
701  auto numRows = sqliteConnector_->getNumRows();
702  vector<std::string> users, passwords;
703  for (size_t i = 0; i < numRows; i++) {
704  users.push_back(sqliteConnector_->getData<std::string>(i, 0));
705  passwords.push_back(sqliteConnector_->getData<std::string>(i, 1));
706  }
707  sqliteConnector_->query(
708  "CREATE TABLE mapd_users_tmp (userid integer primary key, name text unique, "
709  "passwd_hash text, issuper boolean, default_db integer references "
710  "mapd_databases)");
711  sqliteConnector_->query(
712  "INSERT INTO mapd_users_tmp(userid, name, passwd_hash, issuper, default_db) "
713  "SELECT userid, name, null, issuper, default_db FROM mapd_users");
714  for (size_t i = 0; i < users.size(); ++i) {
715  sqliteConnector_->query_with_text_params(
716  "UPDATE mapd_users_tmp SET passwd_hash = ? WHERE userid = ?",
717  std::vector<std::string>{hash_with_bcrypt(passwords[i]), users[i]});
718  }
719  sqliteConnector_->query("DROP TABLE mapd_users");
720  sqliteConnector_->query("ALTER TABLE mapd_users_tmp RENAME TO mapd_users");
721  } catch (const std::exception& e) {
722  sqliteConnector_->query("ROLLBACK TRANSACTION");
723  LOG(ERROR) << "Failed to hash passwords: " << e.what();
724  throw;
725  }
726  sqliteConnector_->query("END TRANSACTION");
727  sqliteConnector_->query("VACUUM"); // physically delete plain text passwords
728  LOG(INFO) << "Passwords were successfully hashed";
729 }
730 
732  const std::string UPDATE_BLANK_PASSWORDS_TO_RANDOM = "update_blank_passwords_to_random";
733  sqliteConnector_->query_with_text_params(
734  "SELECT migration_history FROM mapd_version_history WHERE migration_history = ?",
735  std::vector<std::string>{UPDATE_BLANK_PASSWORDS_TO_RANDOM});
736  if (sqliteConnector_->getNumRows()) {
737  return;
738  }
739 
741  sqliteConnector_->query("BEGIN TRANSACTION");
742  try {
743  sqliteConnector_->query(
744  "SELECT userid, passwd_hash, name FROM mapd_users WHERE name <> 'mapd'");
745  auto numRows = sqliteConnector_->getNumRows();
746  vector<std::string> users, passwords, names;
747  for (size_t i = 0; i < numRows; i++) {
748  users.push_back(sqliteConnector_->getData<std::string>(i, 0));
749  passwords.push_back(sqliteConnector_->getData<std::string>(i, 1));
750  names.push_back(sqliteConnector_->getData<std::string>(i, 2));
751  }
752  for (size_t i = 0; i < users.size(); ++i) {
753  int pwd_check_result = bcrypt_checkpw("", passwords[i].c_str());
754  // if the check fails there is a good chance that data on disc is broken
755  CHECK(pwd_check_result >= 0);
756  if (pwd_check_result != 0) {
757  continue;
758  }
759  LOG(WARNING) << "resetting blank password for user " << names[i] << " (" << users[i]
760  << ") to a random password";
761  sqliteConnector_->query_with_text_params(
762  "UPDATE mapd_users SET passwd_hash = ? WHERE userid = ?",
763  std::vector<std::string>{hash_with_bcrypt(generate_random_string(72)),
764  users[i]});
765  }
766  sqliteConnector_->query_with_text_params(
767  "INSERT INTO mapd_version_history(version, migration_history) values(?,?)",
768  std::vector<std::string>{std::to_string(MAPD_VERSION),
769  UPDATE_BLANK_PASSWORDS_TO_RANDOM});
770  } catch (const std::exception& e) {
771  sqliteConnector_->query("ROLLBACK TRANSACTION");
772  LOG(ERROR) << "Failed to fix blank passwords: " << e.what();
773  throw;
774  }
775  sqliteConnector_->query("END TRANSACTION");
776 }
777 
779  const std::string UPDATE_SUPPORT_USER_DEACTIVATION = "update_support_user_deactivation";
781  // check to see if the new column already exists
782  sqliteConnector_->query("PRAGMA TABLE_INFO(mapd_users)");
783  for (size_t i = 0; i < sqliteConnector_->getNumRows(); i++) {
784  const auto& col_name = sqliteConnector_->getData<std::string>(i, 1);
785  if (col_name == "can_login") {
786  return; // new column already exists
787  }
788  }
789  sqliteConnector_->query("BEGIN TRANSACTION");
790  try {
791  sqliteConnector_->query("ALTER TABLE mapd_users ADD COLUMN can_login BOOLEAN");
792  sqliteConnector_->query("UPDATE mapd_users SET can_login = true");
793  sqliteConnector_->query_with_text_params(
794  "INSERT INTO mapd_version_history(version, migration_history) values(?,?)",
795  std::vector<std::string>{std::to_string(MAPD_VERSION),
796  UPDATE_SUPPORT_USER_DEACTIVATION});
797  } catch (const std::exception& e) {
798  sqliteConnector_->query("ROLLBACK TRANSACTION");
799  LOG(ERROR) << "Failed to add support for user deactivation: " << e.what();
800  throw;
801  }
802  sqliteConnector_->query("END TRANSACTION");
803 }
804 
807  sqliteConnector_->query("BEGIN TRANSACTION");
808  try {
809  sqliteConnector_->query(
810  "select name from sqlite_master WHERE type='table' AND "
811  "name='mapd_version_history'");
812  if (sqliteConnector_->getNumRows() == 0) {
813  sqliteConnector_->query(
814  "CREATE TABLE mapd_version_history(version integer, migration_history text "
815  "unique)");
816  } else {
817  sqliteConnector_->query(
818  "select * from mapd_version_history where migration_history = "
819  "'db_access_privileges'");
820  if (sqliteConnector_->getNumRows() != 0) {
821  // both privileges migrated
822  // no need for further execution
823  sqliteConnector_->query("END TRANSACTION");
824  return;
825  }
826  }
827  // Insert check for migration
828  sqliteConnector_->query_with_text_params(
829  "INSERT INTO mapd_version_history(version, migration_history) values(?,?)",
830  std::vector<std::string>{std::to_string(MAPD_VERSION), "db_access_privileges"});
831 
832  sqliteConnector_->query("select dbid, name from mapd_databases");
833  std::unordered_map<int, string> databases;
834  for (size_t i = 0; i < sqliteConnector_->getNumRows(); ++i) {
835  databases[sqliteConnector_->getData<int>(i, 0)] =
836  sqliteConnector_->getData<string>(i, 1);
837  }
838 
839  sqliteConnector_->query("select userid, name from mapd_users");
840  std::unordered_map<int, string> users;
841  for (size_t i = 0; i < sqliteConnector_->getNumRows(); ++i) {
842  users[sqliteConnector_->getData<int>(i, 0)] =
843  sqliteConnector_->getData<string>(i, 1);
844  }
845 
846  // All existing users by default will be granted DB Access permissions
847  // and view sql editor privileges
848  DBMetadata dbmeta;
849  for (auto db_ : databases) {
850  CHECK(getMetadataForDB(db_.second, dbmeta));
851  for (auto user : users) {
852  if (user.first != shared::kRootUserId) {
853  {
854  DBObjectKey key;
856  key.dbId = dbmeta.dbId;
857 
858  // access permission;
859  DBObject object_access(key, AccessPrivileges::ACCESS, dbmeta.dbOwner);
861  object_access.setName(dbmeta.dbName);
862  // sql_editor permission
863  DBObject object_editor(
866  object_editor.setName(dbmeta.dbName);
867  object_editor.updatePrivileges(object_access);
869  sqliteConnector_, user.second, true, object_editor);
870  }
871  }
872  }
873  }
874  } catch (const std::exception& e) {
875  sqliteConnector_->query("ROLLBACK TRANSACTION");
876  LOG(ERROR) << "Failed to migrate db access privileges: " << e.what();
877  throw;
878  }
879  sqliteConnector_->query("END TRANSACTION");
880  LOG(INFO) << "Successfully migrated db access privileges";
881 }
882 
885 
886  sqliteConnector_->query("BEGIN TRANSACTION");
887  try {
888  sqliteConnector_->query(
889  "CREATE TABLE IF NOT EXISTS mapd_privileges (userid integer references "
890  "mapd_users, dbid integer references "
891  "mapd_databases, select_priv boolean, insert_priv boolean, UNIQUE(userid, "
892  "dbid))");
893  } catch (const std::exception& e) {
894  sqliteConnector_->query("ROLLBACK TRANSACTION");
895  throw;
896  }
897  sqliteConnector_->query("END TRANSACTION");
898 }
899 
901  static const string duplicate_check_migration{
902  "check_duplicate_case_insensitive_db_names"};
903  if (hasExecutedMigration(duplicate_check_migration)) {
904  return;
905  }
907  sqliteConnector_->query(
908  "SELECT UPPER(name) AS db_name, COUNT(*) AS name_count "
909  "FROM mapd_databases GROUP BY db_name HAVING name_count > 1");
910  auto num_rows = sqliteConnector_->getNumRows();
911  if (num_rows > 0) {
912  std::stringstream error_message;
913  error_message << "Duplicate case insensitive database names encountered:\n";
914  for (size_t row = 0; row < num_rows; row++) {
915  error_message << sqliteConnector_->getData<string>(row, 0) << " ("
916  << sqliteConnector_->getData<int>(row, 1) << ")\n";
917  }
918  throw std::runtime_error{error_message.str()};
919  }
920  recordExecutedMigration(duplicate_check_migration);
921 }
922 
923 std::shared_ptr<Catalog> SysCatalog::login(std::string& dbname,
924  std::string& username,
925  const std::string& password,
926  UserMetadata& user_meta,
927  bool check_password) {
928  // NOTE(sy): The dbname isn't const because getMetadataWithDefaultDB()
929  // can reset it. The username isn't const because SamlServer's
930  // login()/authenticate_user() can reset it.
931 
932  if (check_password) {
933  loginImpl(username, password, user_meta);
934  } else { // not checking for password so user must exist
935  if (!getMetadataForUser(username, user_meta)) {
936  throw std::runtime_error("Invalid credentials.");
937  }
938  }
939  // we should have a user and user_meta by now
940  if (!user_meta.can_login) {
941  throw std::runtime_error("Unauthorized Access: User " + username + " is deactivated");
942  }
944  getMetadataWithDefaultDB(dbname, username, db_meta, user_meta);
945  return getCatalog(db_meta, false);
946 }
947 
948 // loginImpl() with no EE code and no SAML code
949 void SysCatalog::loginImpl(std::string& username,
950  const std::string& password,
951  UserMetadata& user_meta) {
952  if (!checkPasswordForUser(password, username, user_meta)) {
953  throw std::runtime_error("Authentication failure");
954  }
955 }
956 
957 std::shared_ptr<Catalog> SysCatalog::switchDatabase(std::string& dbname,
958  const std::string& username) {
959  DBMetadata db_meta;
960  UserMetadata user_meta;
961 
962  getMetadataWithDefaultDB(dbname, username, db_meta, user_meta);
963 
964  // NOTE(max): register database in Catalog that early to allow ldap
965  // and saml create default user and role privileges on databases
966  auto cat = getCatalog(db_meta, false);
967 
968  DBObject dbObject(dbname, DatabaseDBObjectType);
969  dbObject.loadKey();
971  if (!checkPrivileges(user_meta, std::vector<DBObject>{dbObject})) {
972  throw std::runtime_error("Unauthorized Access: user " + user_meta.userLoggable() +
973  " is not allowed to access database " + dbname + ".");
974  }
975 
976  return cat;
977 }
978 
979 void SysCatalog::check_for_session_encryption(const std::string& pki_cert,
980  std::string& session) {
981  if (!pki_server_->inUse()) {
982  return;
983  }
984  pki_server_->encrypt_session(pki_cert, session);
985 }
986 
988  UserAlterations alts,
989  bool is_temporary) {
992 
993  if (!alts.passwd) {
994  alts.passwd = "";
995  }
996  if (!alts.is_super) {
997  alts.is_super = false;
998  }
999  if (!alts.default_db) {
1000  alts.default_db = "";
1001  }
1002  if (!alts.can_login) {
1003  alts.can_login = true;
1004  }
1005 
1006  UserMetadata user;
1007  if (getMetadataForUser(name, user)) {
1008  throw runtime_error("User " + user.userLoggable() + " already exists.");
1009  }
1010  if (getGrantee(name)) {
1011  std::string const loggable = g_log_user_id ? std::string("") : name + ' ';
1012  throw runtime_error(
1013  "User " + loggable +
1014  "is same as one of existing grantees. User and role names should be unique.");
1015  }
1016  DBMetadata db;
1017  if (!alts.default_db->empty()) {
1018  if (!getMetadataForDB(*alts.default_db, db)) {
1019  throw runtime_error("DEFAULT_DB " + *alts.default_db + " not found.");
1020  }
1021  }
1022 
1023  // Temporary user.
1024  if (is_temporary) {
1025  if (!g_read_only) {
1026  throw std::runtime_error("Temporary users require read-only mode.");
1027  // NOTE(sy): We can remove this restriction when we're confident that
1028  // nothing permanent can depend on a temporary user.
1029  }
1030  auto user2 = std::make_shared<UserMetadata>(next_temporary_user_id_++,
1031  name,
1032  hash_with_bcrypt(*alts.passwd),
1033  *alts.is_super,
1034  !alts.default_db->empty() ? db.dbId : -1,
1035  *alts.can_login,
1036  true);
1037  temporary_users_by_name_[name] = user2;
1038  temporary_users_by_id_[user2->userId] = user2;
1039  createRole_unsafe(name, /*userPrivateRole=*/true, /*is_temporary=*/true);
1040  VLOG(1) << "Created temporary user: " << user2->userLoggable();
1041  return *user2;
1042  }
1043 
1044  // Normal user.
1045  sqliteConnector_->query("BEGIN TRANSACTION");
1046  try {
1047  std::vector<std::string> vals;
1048  if (!alts.default_db->empty()) {
1049  vals = {name,
1050  hash_with_bcrypt(*alts.passwd),
1051  std::to_string(*alts.is_super),
1052  std::to_string(db.dbId),
1053  std::to_string(*alts.can_login)};
1054  sqliteConnector_->query_with_text_params(
1055  "INSERT INTO mapd_users (name, passwd_hash, issuper, default_db, can_login) "
1056  "VALUES (?, ?, ?, ?, ?)",
1057  vals);
1058  } else {
1059  vals = {name,
1060  hash_with_bcrypt(*alts.passwd),
1061  std::to_string(*alts.is_super),
1062  std::to_string(*alts.can_login)};
1063  sqliteConnector_->query_with_text_params(
1064  "INSERT INTO mapd_users (name, passwd_hash, issuper, can_login) "
1065  "VALUES (?, ?, ?, ?)",
1066  vals);
1067  }
1068  createRole_unsafe(name, /*userPrivateRole=*/true, /*is_temporary=*/false);
1069  } catch (const std::exception& e) {
1070  sqliteConnector_->query("ROLLBACK TRANSACTION");
1071  throw;
1072  }
1073  sqliteConnector_->query("END TRANSACTION");
1074  auto u = getUser(name);
1075  CHECK(u);
1076  VLOG(1) << "Created user: " << u->userLoggable();
1077  return *u;
1078 }
1079 
1080 // Can be invoked directly to drop users without sanitization for testing.
1081 void SysCatalog::dropUserUnchecked(const std::string& name, const UserMetadata& user) {
1082  sys_write_lock write_lock(this);
1084 
1085  // Temporary user.
1086  if (user.is_temporary) {
1087  auto it1 = temporary_users_by_name_.find(name);
1088  CHECK(it1 != temporary_users_by_name_.end());
1089  auto it2 = temporary_users_by_id_.find(it1->second->userId);
1090  CHECK(it2 != temporary_users_by_id_.end());
1091  dropRole_unsafe(name, /*is_temporary=*/true);
1093  temporary_users_by_name_.erase(it1);
1094  temporary_users_by_id_.erase(it2);
1095  return;
1096  }
1097 
1098  // Normal user.
1099 
1100  sqliteConnector_->query("BEGIN TRANSACTION");
1101  try {
1102  dropRole_unsafe(name, /*is_temporary=*/false);
1104  const std::string& roleName(name);
1105  sqliteConnector_->query_with_text_param("DELETE FROM mapd_roles WHERE userName = ?",
1106  roleName);
1107  sqliteConnector_->query("DELETE FROM mapd_users WHERE userid = " +
1108  std::to_string(user.userId));
1109  sqliteConnector_->query("DELETE FROM mapd_privileges WHERE userid = " +
1110  std::to_string(user.userId));
1111  } catch (const std::exception& e) {
1112  sqliteConnector_->query("ROLLBACK TRANSACTION");
1113  throw;
1114  }
1115  sqliteConnector_->query("END TRANSACTION");
1116 }
1117 
1118 void SysCatalog::dropUser(const string& name, bool if_exists) {
1119  sys_write_lock write_lock(this);
1121 
1122  std::string const loggable = g_log_user_id ? std::string("") : name + ' ';
1123 
1124  UserMetadata user;
1125  if (!getMetadataForUser(name, user)) {
1126  if (if_exists) {
1127  return;
1128  } else {
1129  throw runtime_error("Cannot drop user. User " + loggable + "does not exist.");
1130  }
1131  }
1132 
1133  if (user.userId == shared::kRootUserId) {
1134  throw runtime_error("Cannot drop user. User " + loggable + "is required to exist.");
1135  }
1136 
1137  auto dbs = getAllDBMetadata();
1138  for (const auto& db : dbs) {
1139  if (db.dbOwner == user.userId) {
1140  throw runtime_error("Cannot drop user. User " + loggable + "owns database " +
1141  db.dbName);
1142  }
1143  }
1144 
1145  dropUserUnchecked(name, user);
1146 }
1147 
1148 std::vector<Catalog*> SysCatalog::getCatalogsForAllDbs() {
1149  std::vector<Catalog*> catalogs{};
1150  const auto& db_metadata_list = getAllDBMetadata();
1151  for (const auto& db_metadata : db_metadata_list) {
1152  catalogs.emplace_back(getCatalog(db_metadata, false).get());
1153  }
1154  return catalogs;
1155 }
1156 
1157 namespace { // anonymous namespace
1158 
1159 auto append_with_commas = [](string& s, const string& t) {
1160  if (!s.empty()) {
1161  s += ", ";
1162  }
1163  s += t;
1164 };
1165 
1166 } // anonymous namespace
1167 
1169  if (passwd && hash_with_bcrypt(*passwd) != user.passwd_hash) {
1170  return true;
1171  }
1172  if (is_super && *is_super != user.isSuper) {
1173  return true;
1174  }
1175  if (default_db) {
1176  DBMetadata db;
1177  if (!default_db->empty()) {
1179  throw std::runtime_error(string("DEFAULT_DB ") + *default_db + " not found.");
1180  }
1181  } else {
1182  db.dbId = -1;
1183  }
1184  if (db.dbId != user.defaultDbId) {
1185  return true;
1186  }
1187  }
1188  if (can_login && *can_login != user.can_login) {
1189  return true;
1190  }
1191  return false;
1192 }
1193 
1194 std::string UserAlterations::toString(bool hide_password) const {
1195  std::stringstream ss;
1196  if (passwd) {
1197  if (hide_password) {
1198  ss << "PASSWORD='XXXXXXXX'";
1199  } else {
1200  ss << "PASSWORD='" << *passwd << "'";
1201  }
1202  }
1203  if (is_super) {
1204  if (!ss.str().empty()) {
1205  ss << ", ";
1206  }
1207  ss << "IS_SUPER='" << (*is_super ? "TRUE" : "FALSE") << "'";
1208  }
1209  if (default_db) {
1210  if (!ss.str().empty()) {
1211  ss << ", ";
1212  }
1213  ss << "DEFAULT_DB='" << *default_db << "'";
1214  }
1215  if (can_login) {
1216  if (!ss.str().empty()) {
1217  ss << ", ";
1218  }
1219  ss << "CAN_LOGIN='" << *can_login << "'";
1220  }
1221  return ss.str();
1222 }
1223 
1225  sys_write_lock write_lock(this);
1227 
1228  UserMetadata user;
1229  if (!getMetadataForUser(name, user)) {
1230  std::string const loggable = g_log_user_id ? std::string("") : name + ' ';
1231  throw runtime_error("Cannot alter user. User " + loggable + "does not exist.");
1232  }
1233  if (!alts.wouldChange(user)) {
1234  return user;
1235  }
1236 
1237  // Temporary user.
1238  if (user.is_temporary) {
1239  if (alts.passwd) {
1240  user.passwd_hash = hash_with_bcrypt(*alts.passwd);
1241  }
1242  if (alts.is_super) {
1243  user.isSuper = *alts.is_super;
1244  }
1245  if (alts.default_db) {
1246  if (!alts.default_db->empty()) {
1247  DBMetadata db;
1248  if (!getMetadataForDB(*alts.default_db, db)) {
1249  throw runtime_error(string("DEFAULT_DB ") + *alts.default_db + " not found.");
1250  }
1251  user.defaultDbId = db.dbId;
1252  } else {
1253  user.defaultDbId = -1;
1254  }
1255  }
1256  if (alts.can_login) {
1257  user.can_login = *alts.can_login;
1258  }
1259  *temporary_users_by_name_[name] = user;
1260  return user;
1261  }
1262 
1263  // Normal user.
1264  sqliteConnector_->query("BEGIN TRANSACTION");
1265  try {
1266  string sql;
1267  std::vector<std::string> values;
1268  if (alts.passwd) {
1269  append_with_commas(sql, "passwd_hash = ?");
1270  values.push_back(hash_with_bcrypt(*alts.passwd));
1271  }
1272  if (alts.is_super) {
1273  append_with_commas(sql, "issuper = ?");
1274  values.push_back(std::to_string(*alts.is_super));
1275  }
1276  if (alts.default_db) {
1277  if (!alts.default_db->empty()) {
1278  append_with_commas(sql, "default_db = ?");
1279  DBMetadata db;
1280  if (!getMetadataForDB(*alts.default_db, db)) {
1281  throw runtime_error(string("DEFAULT_DB ") + *alts.default_db + " not found.");
1282  }
1283  values.push_back(std::to_string(db.dbId));
1284  } else {
1285  append_with_commas(sql, "default_db = NULL");
1286  }
1287  }
1288  if (alts.can_login) {
1289  append_with_commas(sql, "can_login = ?");
1290  values.push_back(std::to_string(*alts.can_login));
1291  }
1292 
1293  sql = "UPDATE mapd_users SET " + sql + " WHERE userid = ?";
1294  values.push_back(std::to_string(user.userId));
1295 
1296  sqliteConnector_->query_with_text_params(sql, values);
1297  } catch (const std::exception& e) {
1298  sqliteConnector_->query("ROLLBACK TRANSACTION");
1299  throw;
1300  }
1301  sqliteConnector_->query("END TRANSACTION");
1302  auto u = getUser(name);
1303  CHECK(u);
1304  VLOG(1) << "Altered user: " << u->userLoggable();
1305  return *u;
1306 }
1307 
1309  return
1310  [](auto& db_connector, auto on_success, auto on_failure, auto&&... query_requests) {
1311  auto query_runner = [&db_connector](auto&&... query_reqs) {
1312  [[gnu::unused]] int throw_away[] = {
1313  (db_connector->query_with_text_params(
1314  std::forward<decltype(query_reqs)>(query_reqs)),
1315  0)...};
1316  };
1317 
1318  db_connector->query("BEGIN TRANSACTION");
1319  try {
1320  query_runner(std::forward<decltype(query_requests)>(query_requests)...);
1321  on_success();
1322  } catch (std::exception&) {
1323  db_connector->query("ROLLBACK TRANSACTION");
1324  on_failure();
1325  throw;
1326  }
1327  db_connector->query("END TRANSACTION");
1328  };
1329 }
1330 
1331 void SysCatalog::updateUserRoleName(const std::string& roleName,
1332  const std::string& newName) {
1333  sys_write_lock write_lock(this);
1334 
1335  auto it = granteeMap_.find(to_upper(roleName));
1336  if (it != granteeMap_.end()) {
1337  it->second->setName(newName);
1338  std::swap(granteeMap_[to_upper(newName)], it->second);
1339  granteeMap_.erase(it);
1340  }
1341 
1342  // Also rename in objectDescriptorMap_
1343  for (auto d = objectDescriptorMap_.begin(); d != objectDescriptorMap_.end(); ++d) {
1344  if (d->second->roleName == roleName) {
1345  d->second->roleName = newName;
1346  }
1347  }
1348 }
1349 
1350 void SysCatalog::renameUser(std::string const& old_name, std::string const& new_name) {
1351  using namespace std::string_literals;
1352  sys_write_lock write_lock(this);
1354 
1355  UserMetadata old_user;
1356  if (!getMetadataForUser(old_name, old_user)) {
1357  std::string const loggable = g_log_user_id ? std::string("") : old_name + ' ';
1358  throw std::runtime_error("User " + loggable + "doesn't exist.");
1359  }
1360 
1361  UserMetadata new_user;
1362  if (getMetadataForUser(new_name, new_user)) {
1363  throw std::runtime_error("User " + new_user.userLoggable() + " already exists.");
1364  }
1365 
1366  if (getGrantee(new_name)) {
1367  std::string const loggable = g_log_user_id ? std::string("") : new_name + ' ';
1368  throw runtime_error(
1369  "Username " + loggable +
1370  "is same as one of existing grantees. User and role names should be unique.");
1371  }
1372 
1373  // Temporary user.
1374  if (old_user.is_temporary) {
1375  auto userit = temporary_users_by_name_.find(old_name);
1376  CHECK(userit != temporary_users_by_name_.end());
1377  auto node = temporary_users_by_name_.extract(userit);
1378  node.key() = new_name;
1379  temporary_users_by_name_.insert(std::move(node));
1380  userit->second->userName = new_name;
1381  updateUserRoleName(old_name, new_name);
1382  return;
1383  }
1384 
1385  // Normal user.
1386  auto transaction_streamer = yieldTransactionStreamer();
1387  auto failure_handler = [] {};
1388  auto success_handler = [this, &old_name, &new_name] {
1389  updateUserRoleName(old_name, new_name);
1390  };
1391  auto q1 = {"UPDATE mapd_users SET name=?1 where name=?2;"s, new_name, old_name};
1392  auto q2 = {"UPDATE mapd_object_permissions set roleName=?1 WHERE roleName=?2;"s,
1393  new_name,
1394  old_name};
1395  auto q3 = {"UPDATE mapd_roles set userName=?1 WHERE userName=?2;"s, new_name, old_name};
1396  transaction_streamer(sqliteConnector_, success_handler, failure_handler, q1, q2, q3);
1397 }
1398 
1399 void SysCatalog::changeDatabaseOwner(std::string const& dbname,
1400  const std::string& new_owner) {
1401  using namespace std::string_literals;
1402  sys_write_lock write_lock(this);
1404 
1405  DBMetadata db;
1406  if (!getMetadataForDB(dbname, db)) {
1407  throw std::runtime_error("Database " + dbname + " does not exists.");
1408  }
1409 
1410  Catalog_Namespace::UserMetadata user, original_owner;
1411  if (!getMetadataForUser(new_owner, user)) {
1412  throw std::runtime_error("User with username \"" + new_owner + "\" does not exist. " +
1413  "Database with name \"" + dbname +
1414  "\" can not have owner changed.");
1415  }
1416 
1417  bool original_owner_exists = getMetadataForUserById(db.dbOwner, original_owner);
1418  auto cat = getCatalog(db, true);
1419  DBObject db_object(db.dbName, DBObjectType::DatabaseDBObjectType);
1421  user,
1422  original_owner,
1423  db_object,
1424  *cat,
1425  UpdateQueries{{"UPDATE mapd_databases SET owner=?1 WHERE name=?2;",
1426  {std::to_string(user.userId), db.dbName}}},
1427  original_owner_exists);
1428 }
1429 
1430 void SysCatalog::renameDatabase(std::string const& old_name,
1431  std::string const& new_name) {
1432  using namespace std::string_literals;
1433  sys_write_lock write_lock(this);
1435 
1436  DBMetadata new_db;
1437  if (getMetadataForDB(new_name, new_db)) {
1438  throw std::runtime_error("Database " + new_name + " already exists.");
1439  }
1440  if (to_upper(new_name) == to_upper(shared::kSystemCatalogName)) {
1441  throw std::runtime_error("Database name " + new_name + "is reserved.");
1442  }
1443 
1444  DBMetadata old_db;
1445  if (!getMetadataForDB(old_name, old_db)) {
1446  throw std::runtime_error("Database " + old_name + " does not exists.");
1447  }
1448 
1449  removeCatalog(old_db.dbName);
1450 
1451  std::string old_catalog_path, new_catalog_path;
1452  std::tie(old_catalog_path, new_catalog_path) =
1453  duplicateAndRenameCatalog(old_db.dbName, new_name);
1454 
1455  auto transaction_streamer = yieldTransactionStreamer();
1456  auto failure_handler = [this, new_catalog_path] {
1457  removeCatalogByFullPath(new_catalog_path);
1458  };
1459  auto success_handler = [this, old_catalog_path] {
1460  removeCatalogByFullPath(old_catalog_path);
1461  };
1462 
1463  auto q1 = {
1464  "UPDATE mapd_databases SET name=?1 WHERE name=?2;"s, new_name, old_db.dbName};
1465  auto q2 = {
1466  "UPDATE mapd_object_permissions SET objectName=?1 WHERE objectNAME=?2 and (objectPermissionsType=?3 or objectId = -1) and dbId=?4;"s,
1467  new_name,
1468  old_db.dbName,
1470  std::to_string(old_db.dbId)};
1471 
1472  transaction_streamer(sqliteConnector_, success_handler, failure_handler, q1, q2);
1473 }
1474 
1475 void SysCatalog::createDatabase(const string& name, int owner) {
1476  sys_write_lock write_lock(this);
1478 
1479  DBMetadata db;
1480  if (getMetadataForDB(name, db)) {
1481  throw runtime_error("Database " + name + " already exists.");
1482  }
1484  throw runtime_error("Database name " + name + " is reserved.");
1485  }
1486 
1487  std::unique_ptr<SqliteConnector> dbConn(
1488  new SqliteConnector(name, basePath_ + "/" + shared::kCatalogDirectoryName + "/"));
1489  // NOTE(max): it's okay to run this in a separate transaction. If we fail later
1490  // we delete the database anyways.
1491  // If we run it in the same transaction as SysCatalog functions, then Catalog
1492  // constructor won't find the tables we have just created.
1493  dbConn->query("BEGIN TRANSACTION");
1494  try {
1495  dbConn->query(
1496  "CREATE TABLE mapd_tables (tableid integer primary key, name text unique, userid "
1497  "integer, ncolumns integer, "
1498  "isview boolean, "
1499  "fragments text, frag_type integer, max_frag_rows integer, max_chunk_size "
1500  "bigint, "
1501  "frag_page_size integer, "
1502  "max_rows bigint, partitions text, shard_column_id integer, shard integer, "
1503  "sort_column_id integer default 0, storage_type text default '', "
1504  "max_rollback_epochs integer default -1, "
1505  "is_system_table boolean default 0, "
1506  "num_shards integer, key_metainfo TEXT, version_num "
1507  "BIGINT DEFAULT 1) ");
1508  dbConn->query(
1509  "CREATE TABLE mapd_columns (tableid integer references mapd_tables, columnid "
1510  "integer, name text, coltype "
1511  "integer, colsubtype integer, coldim integer, colscale integer, is_notnull "
1512  "boolean, compression integer, "
1513  "comp_param integer, size integer, chunks text, is_systemcol boolean, "
1514  "is_virtualcol boolean, virtual_expr "
1515  "text, is_deletedcol boolean, version_num BIGINT, default_value text, "
1516  "primary key(tableid, columnid), unique(tableid, name))");
1517  dbConn->query(
1518  "CREATE TABLE mapd_views (tableid integer references mapd_tables, sql text)");
1519  dbConn->query(
1520  "CREATE TABLE mapd_dashboards (id integer primary key autoincrement, name text , "
1521  "userid integer references mapd_users, state text, image_hash text, update_time "
1522  "timestamp, "
1523  "metadata text, UNIQUE(userid, name) )");
1524  dbConn->query(
1525  "CREATE TABLE mapd_links (linkid integer primary key, userid integer references "
1526  "mapd_users, "
1527  "link text unique, view_state text, update_time timestamp, view_metadata text)");
1528  dbConn->query(
1529  "CREATE TABLE mapd_dictionaries (dictid integer primary key, name text unique, "
1530  "nbits int, is_shared boolean, "
1531  "refcount int, version_num BIGINT DEFAULT 1)");
1532  dbConn->query(
1533  "CREATE TABLE mapd_logical_to_physical(logical_table_id integer, "
1534  "physical_table_id "
1535  "integer)");
1536  dbConn->query("CREATE TABLE mapd_record_ownership_marker (dummy integer)");
1537  dbConn->query_with_text_params(
1538  "INSERT INTO mapd_record_ownership_marker (dummy) VALUES (?1)",
1539  std::vector<std::string>{std::to_string(owner)});
1540 
1541  if (g_enable_fsi) {
1542  dbConn->query(Catalog::getForeignServerSchema());
1543  dbConn->query(Catalog::getForeignTableSchema());
1544  }
1545  dbConn->query(Catalog::getCustomExpressionsSchema());
1546  } catch (const std::exception&) {
1547  dbConn->query("ROLLBACK TRANSACTION");
1548  boost::filesystem::remove(basePath_ + "/" + shared::kCatalogDirectoryName + "/" +
1549  name);
1550  throw;
1551  }
1552  dbConn->query("END TRANSACTION");
1553 
1554  std::shared_ptr<Catalog> cat;
1555  // Now update SysCatalog with privileges and the new database
1556  sqliteConnector_->query("BEGIN TRANSACTION");
1557  try {
1558  sqliteConnector_->query_with_text_param(
1559  "INSERT INTO mapd_databases (name, owner) VALUES (?, " + std::to_string(owner) +
1560  ")",
1561  name);
1562  CHECK(getMetadataForDB(name, db));
1563 
1564  cat = getCatalog(db, true);
1565 
1566  if (owner != shared::kRootUserId) {
1568  object.loadKey(*cat);
1569  UserMetadata user;
1570  CHECK(getMetadataForUserById(owner, user));
1571  grantAllOnDatabase_unsafe(user.userName, object, *cat);
1572  }
1573  } catch (const std::exception&) {
1574  sqliteConnector_->query("ROLLBACK TRANSACTION");
1575  boost::filesystem::remove(basePath_ + "/" + shared::kCatalogDirectoryName + "/" +
1576  name);
1577  throw;
1578  }
1579  sqliteConnector_->query("END TRANSACTION");
1580 
1581  // force a migration on the new database
1582  removeCatalog(name);
1583  cat = getCatalog(db, false);
1584 
1585  if (g_enable_fsi) {
1586  try {
1587  cat->createDefaultServersIfNotExists();
1588  } catch (...) {
1589  boost::filesystem::remove(basePath_ + "/" + shared::kCatalogDirectoryName + "/" +
1590  name);
1591  throw;
1592  }
1593  }
1594 }
1595 
1597  auto cat = getCatalog(db, false);
1598  cat->eraseDbPhysicalData();
1599  sys_write_lock write_lock(this);
1601  sqliteConnector_->query("BEGIN TRANSACTION");
1602  try {
1603  // remove this database ID from any users that have it set as their default database
1604  sqliteConnector_->query_with_text_param(
1605  "UPDATE mapd_users SET default_db = NULL WHERE default_db = ?",
1606  std::to_string(db.dbId));
1607  /* revoke object privileges to all tables of the database being dropped */
1608  const auto tables = cat->getAllTableMetadata();
1609  for (const auto table : tables) {
1610  if (table->shard >= 0) {
1611  // skip shards, they're not standalone tables
1612  continue;
1613  }
1615  DBObject(table->tableName, TableDBObjectType), cat.get());
1616  }
1617  const auto dashboards = cat->getAllDashboardsMetadata();
1618  for (const auto dashboard : dashboards) {
1620  DBObject(dashboard->dashboardId, DashboardDBObjectType), cat.get());
1621  }
1622  /* revoke object privileges to the database being dropped */
1623  for (const auto& grantee : granteeMap_) {
1624  if (grantee.second->hasAnyPrivilegesOnDb(db.dbId, true)) {
1626  grantee.second->getName(), db.dbId, grantee.second.get());
1627  }
1628  }
1629  sqliteConnector_->query_with_text_param("DELETE FROM mapd_databases WHERE dbid = ?",
1630  std::to_string(db.dbId));
1631  cat->eraseDbMetadata();
1632  removeCatalog(db.dbName);
1633  } catch (const std::exception&) {
1634  sqliteConnector_->query("ROLLBACK TRANSACTION");
1635  throw;
1636  }
1637  sqliteConnector_->query("END TRANSACTION");
1638 }
1639 
1640 // checkPasswordForUser() with no EE code
1641 bool SysCatalog::checkPasswordForUser(const std::string& passwd,
1642  std::string& name,
1643  UserMetadata& user) {
1644  return checkPasswordForUserImpl(passwd, name, user);
1645 }
1646 
1647 bool SysCatalog::checkPasswordForUserImpl(const std::string& passwd,
1648  std::string& name,
1649  UserMetadata& user) {
1650  sys_read_lock read_lock(this);
1651  if (!getMetadataForUser(name, user)) {
1652  // Check password against some fake hash just to waste time so that response times
1653  // for invalid password and invalid user are similar and a caller can't say the
1654  // difference
1655  char fake_hash[BCRYPT_HASHSIZE];
1656  CHECK(bcrypt_gensalt(-1, fake_hash) == 0);
1657  bcrypt_checkpw(passwd.c_str(), fake_hash);
1658  LOG(WARNING) << "Local login failed";
1659  return false;
1660  }
1661  int pwd_check_result = bcrypt_checkpw(passwd.c_str(), user.passwd_hash.c_str());
1662  // if the check fails there is a good chance that data on disc is broken
1663  CHECK(pwd_check_result >= 0);
1664  return pwd_check_result == 0;
1665 }
1666 
1667 static bool parseUserMetadataFromSQLite(const std::unique_ptr<SqliteConnector>& conn,
1668  UserMetadata& user,
1669  int row) {
1670  int numRows = conn->getNumRows();
1671  if (numRows == 0) {
1672  return false;
1673  }
1674  user.userId = conn->getData<int>(row, 0);
1675  user.userName = conn->getData<string>(row, 1);
1676  user.passwd_hash = conn->getData<string>(row, 2);
1677  user.isSuper = conn->getData<bool>(row, 3);
1678  user.defaultDbId = conn->isNull(row, 4) ? -1 : conn->getData<int>(row, 4);
1679  if (conn->isNull(row, 5)) {
1680  LOG(WARNING)
1681  << "User property 'can_login' not set for user " << user.userLoggable()
1682  << ". Disabling login ability. Set the users login ability with \"ALTER USER "
1683  << (g_log_user_id ? std::string("[username]") : user.userName)
1684  << " (can_login='true');\".";
1685  }
1686  user.can_login = conn->isNull(row, 5) ? false : conn->getData<bool>(row, 5);
1687  return true;
1688 }
1689 
1691  sys_read_lock read_lock(this);
1693  sqliteConnector_->query_with_text_param(
1694  "SELECT userid, name, passwd_hash, issuper, default_db, can_login FROM mapd_users "
1695  "WHERE name = ?",
1696  name);
1697  int numRows = sqliteConnector_->getNumRows();
1698  if (numRows == 0) {
1699  auto userit = temporary_users_by_name_.find(name);
1700  if (userit != temporary_users_by_name_.end()) {
1701  user = *userit->second;
1702  return true;
1703  } else {
1704  return false;
1705  }
1706  }
1708 }
1709 
1710 bool SysCatalog::getMetadataForUserById(const int32_t idIn, UserMetadata& user) {
1712  sqliteConnector_->query_with_text_param(
1713  "SELECT userid, name, passwd_hash, issuper, default_db, can_login FROM mapd_users "
1714  "WHERE userid = ?",
1715  std::to_string(idIn));
1716  int numRows = sqliteConnector_->getNumRows();
1717  if (numRows == 0) {
1718  auto userit = temporary_users_by_id_.find(idIn);
1719  if (userit != temporary_users_by_id_.end()) {
1720  user = *userit->second;
1721  return true;
1722  } else {
1723  return false;
1724  }
1725  }
1727 }
1728 
1729 list<DBMetadata> SysCatalog::getAllDBMetadata() {
1731  sqliteConnector_->query("SELECT dbid, name, owner FROM mapd_databases");
1732  int numRows = sqliteConnector_->getNumRows();
1733  list<DBMetadata> db_list;
1734  for (int r = 0; r < numRows; ++r) {
1735  DBMetadata db;
1736  db.dbId = sqliteConnector_->getData<int>(r, 0);
1737  db.dbName = sqliteConnector_->getData<string>(r, 1);
1738  db.dbOwner = sqliteConnector_->getData<int>(r, 2);
1739  db_list.push_back(db);
1740  }
1741  return db_list;
1742 }
1743 
1744 namespace {
1745 
1746 auto get_users(SysCatalog& syscat,
1747  std::unique_ptr<SqliteConnector>& sqliteConnector,
1748  const int32_t dbId = -1) {
1749  // Normal users.
1750  sqliteConnector->query(
1751  "SELECT userid, name, passwd_hash, issuper, default_db, can_login FROM mapd_users");
1752  int numRows = sqliteConnector->getNumRows();
1753  list<UserMetadata> user_list;
1754  const bool return_all_users = dbId == -1;
1755  auto has_any_privilege = [&return_all_users, &dbId, &syscat](const std::string& name) {
1756  if (!return_all_users) {
1757  const auto grantee = syscat.getUserGrantee(name);
1758  return grantee ? grantee->hasAnyPrivilegesOnDb(dbId, false) : false;
1759  }
1760  return true;
1761  };
1762  for (int r = 0; r < numRows; ++r) {
1764  parseUserMetadataFromSQLite(sqliteConnector, user, r);
1765  if (has_any_privilege(user.userName)) {
1766  user_list.emplace_back(std::move(user));
1767  }
1768  }
1769 
1770  // Temporary users.
1771  for (const auto& [id, userptr] : syscat.temporary_users_by_id_) {
1772  if (has_any_privilege(userptr->userName)) {
1773  user_list.emplace_back(*userptr);
1774  }
1775  }
1776 
1777  return user_list;
1778 }
1779 
1780 } // namespace
1781 
1782 list<UserMetadata> SysCatalog::getAllUserMetadata(const int64_t dbId) {
1783  // this call is to return users that have some form of permissions to objects in the db
1784  // sadly mapd_object_permissions table is also misused to manage user roles.
1786  return get_users(*this, sqliteConnector_, dbId);
1787 }
1788 
1789 list<UserMetadata> SysCatalog::getAllUserMetadata() {
1791  return get_users(*this, sqliteConnector_);
1792 }
1793 
1794 void SysCatalog::getMetadataWithDefaultDB(std::string& dbname,
1795  const std::string& username,
1797  UserMetadata& user_meta) {
1798  sys_read_lock read_lock(this);
1799  if (!getMetadataForUser(username, user_meta)) {
1800  throw std::runtime_error("Invalid credentials.");
1801  }
1802 
1803  if (!dbname.empty()) {
1804  if (!getMetadataForDB(dbname, db_meta)) {
1805  throw std::runtime_error("Database name " + dbname + " does not exist.");
1806  }
1807  // loaded the requested database
1808  } else {
1809  if (user_meta.defaultDbId != -1) {
1810  if (!getMetadataForDBById(user_meta.defaultDbId, db_meta)) {
1811  std::string loggable = g_log_user_id ? std::string("") : ' ' + user_meta.userName;
1812  throw std::runtime_error(
1813  "Server error: User #" + std::to_string(user_meta.userId) + loggable +
1814  " has invalid default_db #" + std::to_string(user_meta.defaultDbId) +
1815  " which does not exist.");
1816  }
1817  dbname = db_meta.dbName;
1818  // loaded the user's default database
1819  } else {
1820  if (!getMetadataForDB(shared::kDefaultDbName, db_meta)) {
1821  throw std::runtime_error(std::string("Database ") + shared::kDefaultDbName +
1822  " does not exist.");
1823  }
1824  dbname = shared::kDefaultDbName;
1825  // loaded the mapd database by default
1826  }
1827  }
1828 }
1829 
1831  sys_read_lock read_lock(this);
1833  sqliteConnector_->query_with_text_param(
1834  "SELECT dbid, name, owner FROM mapd_databases WHERE UPPER(name) = ?",
1835  to_upper(name));
1836  int numRows = sqliteConnector_->getNumRows();
1837  if (numRows == 0) {
1838  return false;
1839  }
1840  db.dbId = sqliteConnector_->getData<int>(0, 0);
1841  db.dbName = sqliteConnector_->getData<string>(0, 1);
1842  db.dbOwner = sqliteConnector_->getData<int>(0, 2);
1843  return true;
1844 }
1845 
1846 bool SysCatalog::getMetadataForDBById(const int32_t idIn, DBMetadata& db) {
1848  sqliteConnector_->query_with_text_param(
1849  "SELECT dbid, name, owner FROM mapd_databases WHERE dbid = ?",
1850  std::to_string(idIn));
1851  int numRows = sqliteConnector_->getNumRows();
1852  if (numRows == 0) {
1853  return false;
1854  }
1855  db.dbId = sqliteConnector_->getData<int>(0, 0);
1856  db.dbName = sqliteConnector_->getData<string>(0, 1);
1857  db.dbOwner = sqliteConnector_->getData<int>(0, 2);
1858  return true;
1859 }
1860 
1862  DBSummaryList ret;
1863 
1864  std::list<Catalog_Namespace::DBMetadata> db_list = getAllDBMetadata();
1865  std::list<Catalog_Namespace::UserMetadata> user_list = getAllUserMetadata();
1866 
1867  std::map<int32_t, std::string> user_id_to_name_map;
1868  for (const auto& user : user_list) {
1869  user_id_to_name_map.emplace(user.userId, user.userName);
1870  }
1871 
1872  for (auto d : db_list) {
1873  DBObject dbObject(d.dbName, DatabaseDBObjectType);
1874  dbObject.loadKey();
1876  if (!checkPrivileges(user, std::vector<DBObject>{dbObject})) {
1877  continue;
1878  }
1879 
1880  if (auto it = user_id_to_name_map.find(d.dbOwner); it != user_id_to_name_map.end()) {
1881  ret.emplace_back(DBSummary{d.dbName, it->second});
1882  } else {
1883  ret.emplace_back(DBSummary{d.dbName, "<DELETED>"});
1884  }
1885  }
1886 
1887  return ret;
1888 }
1889 
1891  const std::string& objectName,
1893  const Catalog_Namespace::Catalog& catalog,
1894  int32_t objectId) {
1895  sys_write_lock write_lock(this);
1897 
1898  DBObject object =
1899  objectId == -1 ? DBObject(objectName, type) : DBObject(objectId, type);
1900  object.loadKey(catalog);
1901  switch (type) {
1902  case TableDBObjectType:
1903  object.setPrivileges(AccessPrivileges::ALL_TABLE);
1904  break;
1905  case DashboardDBObjectType:
1906  object.setPrivileges(AccessPrivileges::ALL_DASHBOARD);
1907  break;
1908  case ServerDBObjectType:
1909  object.setPrivileges(AccessPrivileges::ALL_SERVER);
1910  break;
1911  default:
1912  object.setPrivileges(AccessPrivileges::ALL_DATABASE);
1913  break;
1914  }
1915  object.setOwner(user.userId);
1916  sqliteConnector_->query("BEGIN TRANSACTION");
1917  try {
1918  if (!user.isSuper) { // no need to grant to suser, has all privs by default
1919  grantDBObjectPrivileges_unsafe(user.userName, object, catalog);
1920  auto* grantee = instance().getUserGrantee(user.userName);
1921  if (!grantee) {
1922  throw runtime_error("Cannot create DBObject. User " + user.userLoggable() +
1923  " does not exist.");
1924  }
1925  grantee->grantPrivileges(object);
1926  }
1927  } catch (std::exception& e) {
1928  sqliteConnector_->query("ROLLBACK TRANSACTION");
1929  throw;
1930  }
1931  sqliteConnector_->query("END TRANSACTION");
1932 }
1933 
1934 void SysCatalog::renameDBObject(const std::string& objectName,
1935  const std::string& newName,
1937  int32_t objectId,
1938  const Catalog_Namespace::Catalog& catalog) {
1939  sys_write_lock write_lock(this);
1940  DBObject new_object(newName, type);
1941  DBObjectKey key;
1942  key.dbId = catalog.getCurrentDB().dbId;
1943  key.objectId = objectId;
1944  key.permissionType = type;
1945  new_object.setObjectKey(key);
1946  auto objdescs =
1947  getMetadataForObject(key.dbId, static_cast<int32_t>(type), key.objectId);
1948  for (auto obj : objdescs) {
1949  Grantee* grnt = getGrantee(obj->roleName);
1950  if (grnt) {
1951  grnt->renameDbObject(new_object);
1952  }
1953  }
1954  renameObjectsInDescriptorMap(new_object, catalog);
1955 }
1956 
1958  const vector<string>& grantees,
1959  const vector<DBObject>& objects,
1960  const Catalog_Namespace::Catalog& catalog) {
1961  for (const auto& grantee : grantees) {
1962  for (const auto& object : objects) {
1963  grantDBObjectPrivileges_unsafe(grantee, object, catalog);
1964  }
1965  }
1966 }
1967 
1968 // GRANT INSERT ON TABLE payroll_table TO payroll_dept_role;
1970  const std::string& granteeName,
1971  DBObject object,
1972  const Catalog_Namespace::Catalog& catalog) {
1973  object.loadKey(catalog);
1974  CHECK(object.valid());
1975  if (object.getPrivileges().hasPermission(DatabasePrivileges::ALL) &&
1976  object.getObjectKey().permissionType == DatabaseDBObjectType) {
1977  return grantAllOnDatabase_unsafe(granteeName, object, catalog);
1978  }
1979 
1980  sys_write_lock write_lock(this);
1981 
1982  UserMetadata user_meta;
1983  bool is_temporary_user{false};
1984  if (instance().getMetadataForUser(granteeName, user_meta)) {
1985  if (user_meta.isSuper) {
1986  // super doesn't have explicit privileges so nothing to do
1987  return;
1988  }
1989  is_temporary_user = user_meta.is_temporary;
1990  }
1991  auto* grantee = instance().getGrantee(granteeName);
1992  if (!grantee) {
1993  throw runtime_error("Request to grant privileges to " + granteeName +
1994  " failed because role or user with this name does not exist.");
1995  }
1996  grantee->grantPrivileges(object);
1997 
1998  /* apply grant privileges statement to sqlite DB */
1999  std::vector<std::string> objectKey = object.toString();
2000  object.resetPrivileges();
2001  grantee->getPrivileges(object, true);
2002 
2003  if (!is_temporary_user) {
2006  sqliteConnector_, granteeName, grantee->isUser(), object);
2007  }
2008  updateObjectDescriptorMap(granteeName, object, grantee->isUser(), catalog);
2009 }
2010 
2011 void SysCatalog::grantAllOnDatabase_unsafe(const std::string& roleName,
2012  DBObject& object,
2013  const Catalog_Namespace::Catalog& catalog) {
2014  // It's a separate use case because it's easier for implementation to convert ALL ON
2015  // DATABASE into ALL ON DASHBOARDS, ALL ON VIEWS and ALL ON TABLES
2016  // Add DB Access privileges
2017  DBObject tmp_object = object;
2020  grantDBObjectPrivileges_unsafe(roleName, tmp_object, catalog);
2023  grantDBObjectPrivileges_unsafe(roleName, tmp_object, catalog);
2026  grantDBObjectPrivileges_unsafe(roleName, tmp_object, catalog);
2028  tmp_object.setPermissionType(ViewDBObjectType);
2029  grantDBObjectPrivileges_unsafe(roleName, tmp_object, catalog);
2030 
2031  if (g_enable_fsi) {
2034  grantDBObjectPrivileges_unsafe(roleName, tmp_object, catalog);
2035  }
2036 
2039  grantDBObjectPrivileges_unsafe(roleName, tmp_object, catalog);
2040  return;
2041 }
2042 
2044  const vector<string>& grantees,
2045  const vector<DBObject>& objects,
2046  const Catalog_Namespace::Catalog& catalog) {
2047  for (const auto& grantee : grantees) {
2048  for (const auto& object : objects) {
2049  revokeDBObjectPrivileges_unsafe(grantee, object, catalog);
2050  }
2051  }
2052 }
2053 
2055  vector<DBObject>& objects,
2056  Catalog_Namespace::Catalog* catalog) {
2057  for (const auto& object : objects) {
2058  revokeDBObjectPrivilegesFromAll_unsafe(object, catalog);
2059  }
2060 }
2061 
2062 // REVOKE INSERT ON TABLE payroll_table FROM payroll_dept_role;
2064  const std::string& granteeName,
2065  DBObject object,
2066  const Catalog_Namespace::Catalog& catalog) {
2067  sys_write_lock write_lock(this);
2068 
2069  UserMetadata user_meta;
2070  bool is_temporary_user{false};
2071  if (instance().getMetadataForUser(granteeName, user_meta)) {
2072  if (user_meta.isSuper) {
2073  // super doesn't have explicit privileges so nothing to do
2074  return;
2075  }
2076  is_temporary_user = user_meta.is_temporary;
2077  }
2078  auto* grantee = getGrantee(granteeName);
2079  if (!grantee) {
2080  throw runtime_error("Request to revoke privileges from " + granteeName +
2081  " failed because role or user with this name does not exist.");
2082  }
2083  object.loadKey(catalog);
2084 
2085  if (object.getPrivileges().hasPermission(DatabasePrivileges::ALL) &&
2086  object.getObjectKey().permissionType == DatabaseDBObjectType) {
2087  return revokeAllOnDatabase_unsafe(granteeName, object.getObjectKey().dbId, grantee);
2088  }
2089 
2090  auto ret_object = grantee->revokePrivileges(object);
2091  if (ret_object) {
2092  if (!is_temporary_user) {
2095  sqliteConnector_, granteeName, grantee->isUser(), *ret_object);
2096  }
2097  updateObjectDescriptorMap(granteeName, *ret_object, grantee->isUser(), catalog);
2098  } else {
2099  if (!is_temporary_user) {
2101  deleteObjectPrivileges(sqliteConnector_, granteeName, grantee->isUser(), object);
2102  }
2103  deleteObjectDescriptorMap(granteeName, object, catalog);
2104  }
2105 }
2106 
2107 void SysCatalog::revokeAllOnDatabase_unsafe(const std::string& roleName,
2108  int32_t dbId,
2109  Grantee* grantee) {
2110  bool is_temporary =
2111  (temporary_users_by_name_.find(roleName) != temporary_users_by_name_.end());
2112  if (!is_temporary) {
2114  sqliteConnector_->query_with_text_params(
2115  "DELETE FROM mapd_object_permissions WHERE roleName = ?1 and dbId = ?2",
2116  std::vector<std::string>{roleName, std::to_string(dbId)});
2117  }
2118  grantee->revokeAllOnDatabase(dbId);
2119  for (auto d = objectDescriptorMap_.begin(); d != objectDescriptorMap_.end();) {
2120  if (d->second->roleName == roleName && d->second->dbId == dbId) {
2121  d = objectDescriptorMap_.erase(d);
2122  } else {
2123  d++;
2124  }
2125  }
2126 }
2127 
2129  Catalog* catalog) {
2130  sys_write_lock write_lock(this);
2131  dbObject.loadKey(*catalog);
2132  auto privs = (dbObject.getObjectKey().permissionType == TableDBObjectType)
2137  dbObject.setPrivileges(privs);
2138  for (const auto& grantee : granteeMap_) {
2139  if (grantee.second->findDbObject(dbObject.getObjectKey(), true)) {
2140  revokeDBObjectPrivileges_unsafe(grantee.second->getName(), dbObject, *catalog);
2141  }
2142  }
2143 }
2144 
2146  DBObject object,
2147  const Catalog_Namespace::Catalog& catalog) {
2148  sys_read_lock read_lock(this);
2149 
2150  auto* grantee = instance().getUserGrantee(user.userName);
2151  if (grantee) {
2152  object.loadKey(catalog);
2153  auto* found_object = grantee->findDbObject(object.getObjectKey(), false);
2154  if (found_object && found_object->getOwner() == user.userId) {
2155  return true;
2156  }
2157  }
2158  return false;
2159 }
2160 
2162  const UserMetadata& new_owner,
2163  const UserMetadata& previous_owner,
2164  DBObject object,
2165  const Catalog_Namespace::Catalog& catalog,
2166  const SysCatalog::UpdateQueries& update_queries,
2167  bool revoke_privileges) {
2168  sys_write_lock write_lock(this);
2169  if (new_owner.is_temporary || previous_owner.is_temporary) {
2170  throw std::runtime_error("ownership change not allowed for temporary user(s)");
2171  }
2173  object.loadKey(catalog);
2174  switch (object.getType()) {
2175  case TableDBObjectType:
2176  object.setPrivileges(AccessPrivileges::ALL_TABLE);
2177  break;
2178  case DashboardDBObjectType:
2179  object.setPrivileges(AccessPrivileges::ALL_DASHBOARD);
2180  break;
2181  case ServerDBObjectType:
2182  object.setPrivileges(AccessPrivileges::ALL_SERVER);
2183  break;
2184  case DatabaseDBObjectType:
2185  object.setPrivileges(AccessPrivileges::ALL_DATABASE);
2186  break;
2187  case ViewDBObjectType:
2188  object.setPrivileges(AccessPrivileges::ALL_VIEW);
2189  break;
2190  default:
2191  UNREACHABLE(); // unkown object type
2192  break;
2193  }
2194  sqliteConnector_->query("BEGIN TRANSACTION");
2195  try {
2196  if (!new_owner.isSuper) { // no need to grant to suser, has all privs by default
2197  grantDBObjectPrivileges_unsafe(new_owner.userName, object, catalog);
2198  }
2199  if (!previous_owner.isSuper && revoke_privileges) { // no need to revoke from suser
2200  revokeDBObjectPrivileges_unsafe(previous_owner.userName, object, catalog);
2201  }
2202 
2203  // run update queries if specified
2204  for (const auto& update_query : update_queries) {
2205  sqliteConnector_->query_with_text_params(update_query.query,
2206  update_query.text_params);
2207  }
2208 
2209  auto object_key = object.getObjectKey();
2210  sqliteConnector_->query_with_text_params(
2211  "UPDATE mapd_object_permissions SET objectOwnerId = ? WHERE dbId = ? AND "
2212  "objectId = ? AND objectPermissionsType = ?",
2213  std::vector<std::string>{std::to_string(new_owner.userId),
2214  std::to_string(object_key.dbId),
2215  std::to_string(object_key.objectId),
2216  std::to_string(object_key.permissionType)});
2217 
2218  for (const auto& [user_or_role, grantee] : granteeMap_) {
2219  grantee->reassignObjectOwner(object_key, new_owner.userId);
2220  }
2221 
2222  for (const auto& [map_object_key, map_object_descriptor] : objectDescriptorMap_) {
2223  if (map_object_descriptor->objectId == object_key.objectId &&
2224  map_object_descriptor->objectType == object_key.permissionType &&
2225  map_object_descriptor->dbId == object_key.dbId) {
2226  map_object_descriptor->objectOwnerId = new_owner.userId;
2227  }
2228  }
2229  } catch (std::exception& e) {
2230  sqliteConnector_->query("ROLLBACK TRANSACTION");
2232  throw;
2233  }
2234  sqliteConnector_->query("END TRANSACTION");
2235 }
2236 
2238  const UserMetadata& previous_owner,
2239  DBObject object,
2240  const Catalog_Namespace::Catalog& catalog,
2241  bool revoke_privileges) {
2243  new_owner, previous_owner, object, catalog, {}, revoke_privileges);
2244 }
2245 
2246 void SysCatalog::getDBObjectPrivileges(const std::string& granteeName,
2247  DBObject& object,
2248  const Catalog_Namespace::Catalog& catalog) const {
2249  sys_read_lock read_lock(this);
2250  UserMetadata user_meta;
2251 
2252  if (instance().getMetadataForUser(granteeName, user_meta)) {
2253  if (user_meta.isSuper) {
2254  throw runtime_error(
2255  "Request to show privileges from " + granteeName +
2256  " failed because user is super user and has all privileges by default.");
2257  }
2258  }
2259  auto* grantee = instance().getGrantee(granteeName);
2260  if (!grantee) {
2261  throw runtime_error("Request to show privileges for " + granteeName +
2262  " failed because role or user with this name does not exist.");
2263  }
2264  object.loadKey(catalog);
2265  grantee->getPrivileges(object, true);
2266 }
2267 
2268 void SysCatalog::createRole_unsafe(const std::string& roleName,
2269  const bool user_private_role,
2270  const bool is_temporary) {
2271  sys_write_lock write_lock(this);
2272 
2273  auto* grantee = getGrantee(roleName);
2274  if (grantee) {
2275  throw std::runtime_error("CREATE ROLE " + roleName +
2276  " failed because grantee with this name already exists.");
2277  }
2278  std::unique_ptr<Grantee> g;
2279  if (user_private_role) {
2280  g.reset(new User(roleName));
2281  } else {
2282  g.reset(new Role(roleName));
2283  }
2284  grantee = g.get();
2285  granteeMap_[to_upper(roleName)] = std::move(g);
2286 
2287  // NOTE (max): Why create an empty privileges record for a role?
2288  /* grant none privileges to this role and add it to sqlite DB */
2290  DBObjectKey objKey;
2291  // 0 is an id that does not exist
2292  objKey.dbId = 0;
2294  dbObject.setObjectKey(objKey);
2295  grantee->grantPrivileges(dbObject);
2296 
2297  if (!is_temporary) {
2300  sqliteConnector_, roleName, user_private_role, dbObject);
2301  }
2302 }
2303 
2304 void SysCatalog::dropRole_unsafe(const std::string& roleName, const bool is_temporary) {
2305  sys_write_lock write_lock(this);
2306 
2307  for (auto d = objectDescriptorMap_.begin(); d != objectDescriptorMap_.end();) {
2308  if (d->second->roleName == roleName) {
2309  d = objectDescriptorMap_.erase(d);
2310  } else {
2311  d++;
2312  }
2313  }
2314  // it may very well be a user "role", so keep it generic
2315  granteeMap_.erase(to_upper(roleName));
2316 
2317  if (!is_temporary) {
2319  sqliteConnector_->query_with_text_param("DELETE FROM mapd_roles WHERE roleName = ?",
2320  roleName);
2321  sqliteConnector_->query_with_text_param(
2322  "DELETE FROM mapd_object_permissions WHERE roleName = ?", roleName);
2323  }
2324 }
2325 
2326 void SysCatalog::grantRoleBatch_unsafe(const std::vector<std::string>& roles,
2327  const std::vector<std::string>& grantees) {
2328  for (const auto& role : roles) {
2329  for (const auto& grantee : grantees) {
2330  bool is_temporary_user{false};
2331  UserMetadata user;
2332  if (getMetadataForUser(grantee, user)) {
2333  is_temporary_user = user.is_temporary;
2334  }
2335  grantRole_unsafe(role, grantee, is_temporary_user);
2336  }
2337  }
2338 }
2339 
2340 // GRANT ROLE payroll_dept_role TO joe;
2341 void SysCatalog::grantRole_unsafe(const std::string& roleName,
2342  const std::string& granteeName,
2343  const bool is_temporary) {
2344  auto* rl = getRoleGrantee(roleName);
2345  if (!rl) {
2346  throw runtime_error("Request to grant role " + roleName +
2347  " failed because role with this name does not exist.");
2348  }
2349  auto* grantee = getGrantee(granteeName);
2350  if (!grantee) {
2351  throw runtime_error("Request to grant role " + roleName + " failed because grantee " +
2352  granteeName + " does not exist.");
2353  }
2354  sys_write_lock write_lock(this);
2355  if (!grantee->hasRole(rl, true)) {
2356  grantee->grantRole(rl);
2357  if (!is_temporary) {
2359  sqliteConnector_->query_with_text_params(
2360  "INSERT INTO mapd_roles(roleName, userName) VALUES (?, ?)",
2361  std::vector<std::string>{rl->getName(), grantee->getName()});
2362  }
2363  }
2364 }
2365 
2366 void SysCatalog::revokeRoleBatch_unsafe(const std::vector<std::string>& roles,
2367  const std::vector<std::string>& grantees) {
2368  for (const auto& role : roles) {
2369  for (const auto& grantee : grantees) {
2370  bool is_temporary_user{false};
2371  UserMetadata user;
2372  if (getMetadataForUser(grantee, user)) {
2373  is_temporary_user = user.is_temporary;
2374  }
2375  revokeRole_unsafe(role, grantee, is_temporary_user);
2376  }
2377  }
2378 }
2379 
2380 // REVOKE ROLE payroll_dept_role FROM joe;
2381 void SysCatalog::revokeRole_unsafe(const std::string& roleName,
2382  const std::string& granteeName,
2383  const bool is_temporary) {
2384  auto* rl = getRoleGrantee(roleName);
2385  if (!rl) {
2386  throw runtime_error("Request to revoke role " + roleName +
2387  " failed because role with this name does not exist.");
2388  }
2389  auto* grantee = getGrantee(granteeName);
2390  if (!grantee) {
2391  throw runtime_error("Request to revoke role from " + granteeName +
2392  " failed because grantee with this name does not exist.");
2393  }
2394  sys_write_lock write_lock(this);
2395  grantee->revokeRole(rl);
2396  if (!is_temporary) {
2398  sqliteConnector_->query_with_text_params(
2399  "DELETE FROM mapd_roles WHERE roleName = ? AND userName = ?",
2400  std::vector<std::string>{rl->getName(), grantee->getName()});
2401  }
2402 }
2403 
2404 // Update or add element in ObjectRoleDescriptorMap
2405 void SysCatalog::updateObjectDescriptorMap(const std::string& roleName,
2406  DBObject& object,
2407  bool roleType,
2409  bool present = false;
2410  auto privs = object.getPrivileges();
2411  sys_write_lock write_lock(this);
2412  auto range = objectDescriptorMap_.equal_range(
2413  std::to_string(cat.getCurrentDB().dbId) + ":" +
2414  std::to_string(object.getObjectKey().permissionType) + ":" +
2415  std::to_string(object.getObjectKey().objectId));
2416  for (auto d = range.first; d != range.second; ++d) {
2417  if (d->second->roleName == roleName) {
2418  // overwrite permissions
2419  d->second->privs = privs;
2420  present = true;
2421  }
2422  }
2423  if (!present) {
2424  auto od = std::make_unique<ObjectRoleDescriptor>();
2425  od->roleName = roleName;
2426  od->roleType = roleType;
2427  od->objectType = object.getObjectKey().permissionType;
2428  od->dbId = object.getObjectKey().dbId;
2429  od->objectId = object.getObjectKey().objectId;
2430  od->privs = object.getPrivileges();
2431  od->objectOwnerId = object.getOwner();
2432  od->objectName = object.getName();
2433  objectDescriptorMap_.insert(ObjectRoleDescriptorMap::value_type(
2434  std::to_string(od->dbId) + ":" + std::to_string(od->objectType) + ":" +
2435  std::to_string(od->objectId),
2436  std::move(od)));
2437  }
2438 }
2439 
2440 // rename object descriptors
2443  sys_write_lock write_lock(this);
2445  auto range = objectDescriptorMap_.equal_range(
2446  std::to_string(cat.getCurrentDB().dbId) + ":" +
2447  std::to_string(object.getObjectKey().permissionType) + ":" +
2448  std::to_string(object.getObjectKey().objectId));
2449  for (auto d = range.first; d != range.second; ++d) {
2450  // rename object
2451  d->second->objectName = object.getName();
2452  }
2453 
2454  sqliteConnector_->query("BEGIN TRANSACTION");
2455  try {
2456  sqliteConnector_->query_with_text_params(
2457  "UPDATE mapd_object_permissions SET objectName = ?1 WHERE "
2458  "dbId = ?2 AND objectId = ?3",
2459  std::vector<std::string>{object.getName(),
2461  std::to_string(object.getObjectKey().objectId)});
2462  } catch (const std::exception& e) {
2463  sqliteConnector_->query("ROLLBACK TRANSACTION");
2464  throw;
2465  }
2466  sqliteConnector_->query("END TRANSACTION");
2467 }
2468 
2469 // Remove user/role from ObjectRoleDescriptorMap
2470 void SysCatalog::deleteObjectDescriptorMap(const std::string& roleName) {
2471  sys_write_lock write_lock(this);
2472 
2473  for (auto d = objectDescriptorMap_.begin(); d != objectDescriptorMap_.end();) {
2474  if (d->second->roleName == roleName) {
2475  d = objectDescriptorMap_.erase(d);
2476  } else {
2477  d++;
2478  }
2479  }
2480 }
2481 
2482 // Remove element from ObjectRoleDescriptorMap
2483 void SysCatalog::deleteObjectDescriptorMap(const std::string& roleName,
2484  DBObject& object,
2485  const Catalog_Namespace::Catalog& cat) {
2486  sys_write_lock write_lock(this);
2487  auto range = objectDescriptorMap_.equal_range(
2488  std::to_string(cat.getCurrentDB().dbId) + ":" +
2489  std::to_string(object.getObjectKey().permissionType) + ":" +
2490  std::to_string(object.getObjectKey().objectId));
2491  for (auto d = range.first; d != range.second;) {
2492  // remove the entry
2493  if (d->second->roleName == roleName) {
2494  d = objectDescriptorMap_.erase(d);
2495  } else {
2496  d++;
2497  }
2498  }
2499 }
2500 
2502  std::vector<DBObject>& privObjects) {
2503  sys_read_lock read_lock(this);
2504  if (user.isSuper) {
2505  return true;
2506  }
2507  auto* user_rl = instance().getUserGrantee(user.userName);
2508  if (!user_rl) {
2509  throw runtime_error("Cannot check privileges. User " + user.userLoggable() +
2510  " does not exist.");
2511  }
2512  for (std::vector<DBObject>::iterator objectIt = privObjects.begin();
2513  objectIt != privObjects.end();
2514  ++objectIt) {
2515  if (!user_rl->hasAnyPrivileges(*objectIt, false)) {
2516  return false;
2517  }
2518  }
2519  return true;
2520 }
2521 
2523  const std::vector<DBObject>& privObjects) const {
2524  sys_read_lock read_lock(this);
2525  if (user.isSuper) {
2526  return true;
2527  }
2528 
2529  auto* user_rl = instance().getUserGrantee(user.userName);
2530  if (!user_rl) {
2531  throw runtime_error("Cannot check privileges. User " + user.userLoggable() +
2532  " does not exist.");
2533  }
2534  for (auto& object : privObjects) {
2535  if (!user_rl->checkPrivileges(object)) {
2536  return false;
2537  }
2538  }
2539  return true;
2540 }
2541 
2542 bool SysCatalog::checkPrivileges(const std::string& userName,
2543  const std::vector<DBObject>& privObjects) const {
2544  UserMetadata user;
2545  if (!instance().getMetadataForUser(userName, user)) {
2546  std::string const loggable = g_log_user_id ? std::string("") : userName + ' ';
2547  throw runtime_error("Request to check privileges for user " + loggable +
2548  "failed because user with this name does not exist.");
2549  }
2550  return (checkPrivileges(user, privObjects));
2551 }
2552 
2553 Grantee* SysCatalog::getGrantee(const std::string& name) const {
2554  sys_read_lock read_lock(this);
2555  auto grantee = granteeMap_.find(to_upper(name));
2556  if (grantee == granteeMap_.end()) { // check to make sure role exists
2557  return nullptr;
2558  }
2559  return grantee->second.get(); // returns pointer to role
2560 }
2561 
2562 Role* SysCatalog::getRoleGrantee(const std::string& name) const {
2563  return dynamic_cast<Role*>(getGrantee(name));
2564 }
2565 
2566 User* SysCatalog::getUserGrantee(const std::string& name) const {
2567  return dynamic_cast<User*>(getGrantee(name));
2568 }
2569 
2570 std::vector<ObjectRoleDescriptor*>
2571 SysCatalog::getMetadataForObject(int32_t dbId, int32_t dbType, int32_t objectId) const {
2572  sys_read_lock read_lock(this);
2573  std::vector<ObjectRoleDescriptor*> objectsList;
2574 
2575  auto range = objectDescriptorMap_.equal_range(std::to_string(dbId) + ":" +
2576  std::to_string(dbType) + ":" +
2577  std::to_string(objectId));
2578  for (auto d = range.first; d != range.second; ++d) {
2579  objectsList.push_back(d->second.get());
2580  }
2581  return objectsList; // return pointers to objects
2582 }
2583 
2584 std::vector<ObjectRoleDescriptor> SysCatalog::getMetadataForAllObjects() const {
2585  sys_read_lock read_lock(this);
2586  std::vector<ObjectRoleDescriptor> objects;
2587  for (const auto& entry : objectDescriptorMap_) {
2588  auto object_role = entry.second.get();
2589  if (object_role->dbId != 0 && !isDashboardSystemRole(object_role->roleName)) {
2590  objects.emplace_back(*object_role);
2591  }
2592  }
2593  return objects;
2594 }
2595 
2596 bool SysCatalog::isRoleGrantedToGrantee(const std::string& granteeName,
2597  const std::string& roleName,
2598  bool only_direct) const {
2599  sys_read_lock read_lock(this);
2600  if (roleName == granteeName) {
2601  return true;
2602  }
2603  bool is_role_granted = false;
2604  auto* target_role = instance().getRoleGrantee(roleName);
2605  auto has_role = [&](auto grantee_rl) {
2606  is_role_granted = target_role && grantee_rl->hasRole(target_role, only_direct);
2607  };
2608  if (auto* user_role = instance().getUserGrantee(granteeName); user_role) {
2609  has_role(user_role);
2610  } else if (auto* role = instance().getRoleGrantee(granteeName); role) {
2611  has_role(role);
2612  } else {
2613  CHECK(false);
2614  }
2615  return is_role_granted;
2616 }
2617 
2618 bool SysCatalog::isDashboardSystemRole(const std::string& roleName) const {
2619  return boost::algorithm::ends_with(roleName, SYSTEM_ROLE_TAG);
2620 }
2621 
2622 std::vector<std::string> SysCatalog::getRoles(const std::string& user_name,
2623  bool effective) {
2624  sys_read_lock read_lock(this);
2625  auto* grantee = getGrantee(user_name);
2626  if (!grantee) {
2627  throw std::runtime_error("user or role not found");
2628  }
2629  return grantee->getRoles(/*only_direct=*/!effective);
2630 }
2631 
2632 std::vector<std::string> SysCatalog::getRoles(const std::string& userName,
2633  const int32_t dbId) {
2634  sys_sqlite_lock sqlite_lock(this);
2635  std::string sql =
2636  "SELECT DISTINCT roleName FROM mapd_object_permissions WHERE "
2637  "objectPermissions<>0 "
2638  "AND roleType=0 AND dbId=" +
2639  std::to_string(dbId);
2640  sqliteConnector_->query(sql);
2641  int numRows = sqliteConnector_->getNumRows();
2642  std::vector<std::string> roles(0);
2643  for (int r = 0; r < numRows; ++r) {
2644  auto roleName = sqliteConnector_->getData<string>(r, 0);
2645  if (isRoleGrantedToGrantee(userName, roleName, false) &&
2646  !isDashboardSystemRole(roleName)) {
2647  roles.push_back(roleName);
2648  }
2649  }
2650  return roles;
2651 }
2652 
2653 std::vector<std::string> SysCatalog::getRoles(bool include_user_private_role,
2654  bool is_super,
2655  const std::string& user_name,
2656  bool ignore_deleted_user) {
2657  sys_read_lock read_lock(this);
2658  if (ignore_deleted_user) {
2659  // In certain cases, it is possible to concurrently call this method while the user is
2660  // being dropped. In such a case, return an empty result.
2661  UserMetadata user;
2662  if (!getMetadataForUser(user_name, user)) {
2663  return {};
2664  }
2665  }
2666  std::vector<std::string> roles;
2667  for (auto& grantee : granteeMap_) {
2668  if (!include_user_private_role && grantee.second->isUser()) {
2669  continue;
2670  }
2671  if (!is_super &&
2672  !isRoleGrantedToGrantee(user_name, grantee.second->getName(), false)) {
2673  continue;
2674  }
2675  if (isDashboardSystemRole(grantee.second->getName())) {
2676  continue;
2677  }
2678  roles.push_back(grantee.second->getName());
2679  }
2680  return roles;
2681 }
2682 
2683 std::set<std::string> SysCatalog::getCreatedRoles() const {
2684  sys_read_lock read_lock(this);
2685  std::set<std::string> roles; // Sorted for human readers.
2686  for (const auto& [key, grantee] : granteeMap_) {
2687  if (!grantee->isUser() && !isDashboardSystemRole(grantee->getName())) {
2688  roles.emplace(grantee->getName());
2689  }
2690  }
2691  return roles;
2692 }
2693 
2695  granteeMap_.clear();
2696  string roleQuery(
2697  "SELECT roleName, roleType, objectPermissionsType, dbId, objectId, "
2698  "objectPermissions, objectOwnerId, objectName "
2699  "from mapd_object_permissions");
2700  sqliteConnector_->query(roleQuery);
2701  size_t numRows = sqliteConnector_->getNumRows();
2702  std::vector<std::string> objectKeyStr(4);
2703  DBObjectKey objectKey;
2704  AccessPrivileges privs;
2705  bool userPrivateRole{false};
2706  for (size_t r = 0; r < numRows; ++r) {
2707  std::string roleName = sqliteConnector_->getData<string>(r, 0);
2708  userPrivateRole = sqliteConnector_->getData<bool>(r, 1);
2709  DBObjectType permissionType =
2710  static_cast<DBObjectType>(sqliteConnector_->getData<int>(r, 2));
2711  objectKeyStr[0] = sqliteConnector_->getData<string>(r, 2);
2712  objectKeyStr[1] = sqliteConnector_->getData<string>(r, 3);
2713  objectKeyStr[2] = sqliteConnector_->getData<string>(r, 4);
2714  objectKey = DBObjectKey::fromString(objectKeyStr, permissionType);
2715  privs.privileges = sqliteConnector_->getData<int>(r, 5);
2716  int32_t owner = sqliteConnector_->getData<int>(r, 6);
2717  std::string name = sqliteConnector_->getData<string>(r, 7);
2718 
2719  DBObject dbObject(objectKey, privs, owner);
2720  dbObject.setName(name);
2721  if (-1 == objectKey.objectId) {
2722  dbObject.setObjectType(DBObjectType::DatabaseDBObjectType);
2723  } else {
2724  dbObject.setObjectType(permissionType);
2725  }
2726 
2727  auto* rl = getGrantee(roleName);
2728  if (!rl) {
2729  std::unique_ptr<Grantee> g;
2730  if (userPrivateRole) {
2731  g.reset(new User(roleName));
2732  } else {
2733  g.reset(new Role(roleName));
2734  }
2735  rl = g.get();
2736  granteeMap_[to_upper(roleName)] = std::move(g);
2737  }
2738  rl->grantPrivileges(dbObject);
2739  }
2740 }
2741 
2742 void SysCatalog::populateRoleDbObjects(const std::vector<DBObject>& objects) {
2743  sys_write_lock write_lock(this);
2744  sys_sqlite_lock sqlite_lock(this);
2745  sqliteConnector_->query("BEGIN TRANSACTION");
2746  try {
2747  for (auto dbobject : objects) {
2748  UserMetadata user;
2749  CHECK(getMetadataForUserById(dbobject.getOwner(), user));
2750  auto* grantee = getUserGrantee(user.userName);
2751  if (grantee) {
2753  sqliteConnector_, grantee->getName(), true, dbobject);
2754  grantee->grantPrivileges(dbobject);
2755  }
2756  }
2757 
2758  } catch (const std::exception& e) {
2759  sqliteConnector_->query("ROLLBACK TRANSACTION");
2760  throw;
2761  }
2762  sqliteConnector_->query("END TRANSACTION");
2763 }
2764 
2766  std::vector<std::pair<std::string, std::string>> granteeRoles;
2767  string userRoleQuery("SELECT roleName, userName from mapd_roles");
2768  sqliteConnector_->query(userRoleQuery);
2769  size_t numRows = sqliteConnector_->getNumRows();
2770  for (size_t r = 0; r < numRows; ++r) {
2771  std::string roleName = sqliteConnector_->getData<string>(r, 0);
2772  std::string userName = sqliteConnector_->getData<string>(r, 1);
2773  // required for declared nomenclature before v4.0.0
2774  if ((boost::equals(roleName, "mapd_default_suser_role") &&
2775  boost::equals(userName, shared::kRootUsername)) ||
2776  (boost::equals(roleName, "mapd_default_user_role") &&
2777  !boost::equals(userName, "mapd_default_user_role"))) {
2778  // grouprole already exists with roleName==userName in mapd_roles table
2779  // ignore duplicate instances of userRole which exists before v4.0.0
2780  continue;
2781  }
2782  auto* rl = getGrantee(roleName);
2783  if (!rl) {
2784  throw runtime_error("Data inconsistency when building role map. Role " + roleName +
2785  " from db not found in the map.");
2786  }
2787  std::pair<std::string, std::string> roleVecElem(roleName, userName);
2788  granteeRoles.push_back(roleVecElem);
2789  }
2790 
2791  for (const auto& [roleName, granteeName] : granteeRoles) {
2792  auto* grantee = getGrantee(granteeName);
2793  if (!grantee) {
2794  throw runtime_error("Data inconsistency when building role map. Grantee " +
2795  granteeName + " not found in the map.");
2796  }
2797  if (granteeName == roleName) {
2798  continue;
2799  }
2800  Role* rl = dynamic_cast<Role*>(getGrantee(roleName));
2801  if (!rl) {
2802  throw runtime_error("Data inconsistency when building role map. Role " + roleName +
2803  " not found in the map.");
2804  }
2805  grantee->grantRole(rl);
2806  }
2807 }
2808 
2810  objectDescriptorMap_.clear();
2811  string objectQuery(
2812  "SELECT roleName, roleType, objectPermissionsType, dbId, objectId, "
2813  "objectPermissions, objectOwnerId, objectName "
2814  "from mapd_object_permissions");
2815  sqliteConnector_->query(objectQuery);
2816  size_t numRows = sqliteConnector_->getNumRows();
2817  for (size_t r = 0; r < numRows; ++r) {
2818  auto od = std::make_unique<ObjectRoleDescriptor>();
2819  od->roleName = sqliteConnector_->getData<string>(r, 0);
2820  od->roleType = sqliteConnector_->getData<bool>(r, 1);
2821  od->objectType = sqliteConnector_->getData<int>(r, 2);
2822  od->dbId = sqliteConnector_->getData<int>(r, 3);
2823  od->objectId = sqliteConnector_->getData<int>(r, 4);
2824  od->privs.privileges = sqliteConnector_->getData<int>(r, 5);
2825  od->objectOwnerId = sqliteConnector_->getData<int>(r, 6);
2826  od->objectName = sqliteConnector_->getData<string>(r, 7);
2827  objectDescriptorMap_.insert(ObjectRoleDescriptorMap::value_type(
2828  std::to_string(od->dbId) + ":" + std::to_string(od->objectType) + ":" +
2829  std::to_string(od->objectId),
2830  std::move(od)));
2831  }
2832 }
2833 
2834 template <typename F, typename... Args>
2835 void SysCatalog::execInTransaction(F&& f, Args&&... args) {
2836  sys_write_lock write_lock(this);
2837  sys_sqlite_lock sqlite_lock(this);
2838  sqliteConnector_->query("BEGIN TRANSACTION");
2839  try {
2840  (this->*f)(std::forward<Args>(args)...);
2841  } catch (std::exception&) {
2842  sqliteConnector_->query("ROLLBACK TRANSACTION");
2843  throw;
2844  }
2845  sqliteConnector_->query("END TRANSACTION");
2846 }
2847 
2848 void SysCatalog::createRole(const std::string& roleName,
2849  const bool user_private_role,
2850  const bool is_temporary) {
2852  &SysCatalog::createRole_unsafe, roleName, user_private_role, is_temporary);
2853 }
2854 
2855 void SysCatalog::dropRole(const std::string& roleName, const bool is_temporary) {
2856  execInTransaction(&SysCatalog::dropRole_unsafe, roleName, is_temporary);
2857 }
2858 
2859 void SysCatalog::grantRoleBatch(const std::vector<std::string>& roles,
2860  const std::vector<std::string>& grantees) {
2862 }
2863 
2864 void SysCatalog::grantRole(const std::string& role,
2865  const std::string& grantee,
2866  const bool is_temporary) {
2867  execInTransaction(&SysCatalog::grantRole_unsafe, role, grantee, is_temporary);
2868 }
2869 
2870 void SysCatalog::revokeRoleBatch(const std::vector<std::string>& roles,
2871  const std::vector<std::string>& grantees) {
2873 }
2874 
2875 void SysCatalog::revokeRole(const std::string& role,
2876  const std::string& grantee,
2877  const bool is_temporary) {
2878  execInTransaction(&SysCatalog::revokeRole_unsafe, role, grantee, is_temporary);
2879 }
2880 
2881 void SysCatalog::grantDBObjectPrivileges(const string& grantee,
2882  const DBObject& object,
2883  const Catalog_Namespace::Catalog& catalog) {
2885  &SysCatalog::grantDBObjectPrivileges_unsafe, grantee, object, catalog);
2886 }
2887 
2888 void SysCatalog::grantDBObjectPrivilegesBatch(const vector<string>& grantees,
2889  const vector<DBObject>& objects,
2890  const Catalog_Namespace::Catalog& catalog) {
2892  &SysCatalog::grantDBObjectPrivilegesBatch_unsafe, grantees, objects, catalog);
2893 }
2894 
2895 void SysCatalog::revokeDBObjectPrivileges(const string& grantee,
2896  const DBObject& object,
2897  const Catalog_Namespace::Catalog& catalog) {
2899  &SysCatalog::revokeDBObjectPrivileges_unsafe, grantee, object, catalog);
2900 }
2901 
2903  const vector<string>& grantees,
2904  const vector<DBObject>& objects,
2905  const Catalog_Namespace::Catalog& catalog) {
2907  &SysCatalog::revokeDBObjectPrivilegesBatch_unsafe, grantees, objects, catalog);
2908 }
2909 
2912 }
2913 
2915  Catalog* catalog) {
2918 }
2919 
2920 void SysCatalog::syncUserWithRemoteProvider(const std::string& user_name,
2921  std::vector<std::string> idp_roles,
2922  UserAlterations alts) {
2923  // need to escalate to a write lock
2924  // need to unlock the read lock
2925  sys_read_lock read_lock(this);
2926  read_lock.unlock();
2927  sys_write_lock write_lock(this);
2928  bool enable_idp_temporary_users{g_enable_idp_temporary_users && g_read_only};
2929  if (auto user = getUser(user_name); !user) {
2930  if (!alts.passwd) {
2931  alts.passwd = generate_random_string(72);
2932  }
2933  user = createUser(user_name, alts, /*is_temporary=*/enable_idp_temporary_users);
2934  LOG(INFO) << "Remote identity provider created user [" << user->userLoggable()
2935  << "] with (" << alts.toString() << ")";
2936  } else if (alts.wouldChange(*user)) {
2937  user = alterUser(user->userName, alts);
2938  LOG(INFO) << "Remote identity provider altered user [" << user->userLoggable()
2939  << "] with (" << alts.toString() << ")";
2940  }
2941  std::vector<std::string> current_roles = {};
2942  auto* user_rl = getUserGrantee(user_name);
2943  if (user_rl) {
2944  current_roles = user_rl->getRoles();
2945  }
2947  current_roles.begin(), current_roles.end(), current_roles.begin(), to_upper);
2948  std::transform(idp_roles.begin(), idp_roles.end(), idp_roles.begin(), to_upper);
2949  std::list<std::string> roles_revoked, roles_granted;
2950  // first remove obsolete ones
2951  for (auto& current_role_name : current_roles) {
2952  if (std::find(idp_roles.begin(), idp_roles.end(), current_role_name) ==
2953  idp_roles.end()) {
2954  revokeRole(current_role_name,
2955  user_name,
2956  /*is_temporary=*/enable_idp_temporary_users);
2957  roles_revoked.push_back(current_role_name);
2958  }
2959  }
2960  for (auto& role_name : idp_roles) {
2961  if (std::find(current_roles.begin(), current_roles.end(), role_name) ==
2962  current_roles.end()) {
2963  auto* rl = getRoleGrantee(role_name);
2964  if (rl) {
2965  grantRole(role_name,
2966  user_name,
2967  /*is_temporary=*/enable_idp_temporary_users);
2968  roles_granted.push_back(role_name);
2969  } else {
2970  LOG(WARNING) << "Error synchronizing roles for user " << user_name << ": role "
2971  << role_name << " does not exist";
2972  }
2973  }
2974  }
2975  if (roles_granted.empty() && roles_revoked.empty()) {
2976  LOG(INFO) << "Roles for user " << user_name
2977  << " are up to date with remote identity provider";
2978  } else {
2979  if (!roles_revoked.empty()) {
2980  LOG(INFO) << "Roles revoked during synchronization with identity provider for user "
2981  << user_name << ": " << join(roles_revoked, " ");
2982  }
2983  if (!roles_granted.empty()) {
2984  LOG(INFO) << "Roles granted during synchronization with identity provider for user "
2985  << user_name << ": " << join(roles_granted, " ");
2986  }
2987  }
2988 }
2989 
2990 std::unordered_map<std::string, std::vector<std::string>>
2991 SysCatalog::getGranteesOfSharedDashboards(const std::vector<std::string>& dashboard_ids) {
2992  sys_sqlite_lock sqlite_lock(this);
2993  std::unordered_map<std::string, std::vector<std::string>> active_grantees;
2994  sqliteConnector_->query("BEGIN TRANSACTION");
2995  try {
2996  for (auto dash : dashboard_ids) {
2997  std::vector<std::string> grantees = {};
2998  sqliteConnector_->query_with_text_params(
2999  "SELECT roleName FROM mapd_object_permissions WHERE objectPermissions NOT IN "
3000  "(0,1) AND objectPermissionsType = ? AND objectId = ?",
3001  std::vector<std::string>{
3002  std::to_string(static_cast<int32_t>(DashboardDBObjectType)), dash});
3003  int num_rows = sqliteConnector_->getNumRows();
3004  if (num_rows == 0) {
3005  // no grantees
3006  continue;
3007  } else {
3008  for (size_t i = 0; i < sqliteConnector_->getNumRows(); ++i) {
3009  grantees.push_back(sqliteConnector_->getData<string>(i, 0));
3010  }
3011  active_grantees[dash] = grantees;
3012  }
3013  }
3014  } catch (const std::exception& e) {
3015  sqliteConnector_->query("ROLLBACK TRANSACTION");
3016  throw;
3017  }
3018  sqliteConnector_->query("END TRANSACTION");
3019  return active_grantees;
3020 }
3021 
3022 std::shared_ptr<Catalog> SysCatalog::getCatalog(const std::string& dbName) {
3023  dbid_to_cat_map::const_accessor cata;
3024  if (cat_map_.find(cata, to_upper(dbName))) {
3025  return cata->second;
3026  } else {
3028  if (getMetadataForDB(dbName, db_meta)) {
3029  return getCatalog(db_meta, false);
3030  } else {
3031  return nullptr;
3032  }
3033  }
3034 }
3035 
3036 std::shared_ptr<Catalog> SysCatalog::getCatalog(const int32_t db_id) {
3037  dbid_to_cat_map::const_accessor cata;
3038  for (dbid_to_cat_map::iterator cat_it = cat_map_.begin(); cat_it != cat_map_.end();
3039  ++cat_it) {
3040  if (cat_it->second->getDatabaseId() == db_id) {
3041  return cat_it->second;
3042  }
3043  }
3044  return nullptr;
3045 }
3046 
3047 std::shared_ptr<Catalog> SysCatalog::getCatalog(const DBMetadata& curDB, bool is_new_db) {
3048  const auto key = to_upper(curDB.dbName);
3049  {
3050  dbid_to_cat_map::const_accessor cata;
3051  if (cat_map_.find(cata, key)) {
3052  return cata->second;
3053  }
3054  }
3055 
3056  // Catalog doesnt exist
3057  // has to be made outside of lock as migration uses cat
3058  auto cat = std::make_shared<Catalog>(
3059  basePath_, curDB, dataMgr_, string_dict_hosts_, calciteMgr_, is_new_db);
3060 
3061  dbid_to_cat_map::accessor cata;
3062 
3063  if (cat_map_.find(cata, key)) {
3064  return cata->second;
3065  }
3066 
3067  cat_map_.insert(cata, key);
3068  cata->second = cat;
3069 
3070  return cat;
3071 }
3072 
3073 void SysCatalog::removeCatalog(const std::string& dbName) {
3074  cat_map_.erase(to_upper(dbName));
3075 }
3076 
3078  const std::map<int32_t, std::vector<DBObject>>& old_owner_db_objects,
3079  int32_t new_owner_id,
3080  const Catalog_Namespace::Catalog& catalog) {
3081  sys_write_lock write_lock(this);
3082  sys_sqlite_lock sqlite_lock(this);
3083 
3084  sqliteConnector_->query("BEGIN TRANSACTION");
3085  try {
3086  UserMetadata new_owner;
3087  CHECK(getMetadataForUserById(new_owner_id, new_owner));
3088  for (const auto& [old_owner_id, db_objects] : old_owner_db_objects) {
3089  UserMetadata old_owner;
3090  CHECK(getMetadataForUserById(old_owner_id, old_owner));
3091  if (!old_owner.isSuper) {
3092  revokeDBObjectPrivilegesBatch_unsafe({old_owner.userName}, db_objects, catalog);
3093  }
3094  if (!new_owner.isSuper) {
3095  grantDBObjectPrivilegesBatch_unsafe({new_owner.userName}, db_objects, catalog);
3096  }
3097  }
3098 
3099  std::set<int32_t> old_owner_ids;
3100  for (const auto& [old_owner_id, db_objects] : old_owner_db_objects) {
3101  old_owner_ids.emplace(old_owner_id);
3102  }
3103 
3104  auto db_id = catalog.getDatabaseId();
3105  for (const auto old_user_id : old_owner_ids) {
3106  sqliteConnector_->query_with_text_params(
3107  "UPDATE mapd_object_permissions SET objectOwnerId = ? WHERE objectOwnerId = ? "
3108  "AND dbId = ? AND objectId != -1",
3109  std::vector<std::string>{std::to_string(new_owner_id),
3110  std::to_string(old_user_id),
3111  std::to_string(db_id)});
3112  }
3113 
3114  for (const auto& [user_or_role, grantee] : granteeMap_) {
3115  grantee->reassignObjectOwners(old_owner_ids, new_owner_id, db_id);
3116  }
3117 
3118  for (const auto& [object_key, object_descriptor] : objectDescriptorMap_) {
3119  if (object_descriptor->objectId != -1 && object_descriptor->dbId == db_id &&
3120  shared::contains(old_owner_ids, object_descriptor->objectOwnerId)) {
3121  object_descriptor->objectOwnerId = new_owner_id;
3122  }
3123  }
3124  } catch (std::exception& e) {
3125  sqliteConnector_->query("ROLLBACK TRANSACTION");
3127  throw;
3128  }
3129  sqliteConnector_->query("END TRANSACTION");
3130 }
3131 
3134  sys_write_lock write_lock(this);
3135  DBMetadata db_metadata;
3136  if (getMetadataForDB(shared::kInfoSchemaDbName, db_metadata)) {
3137  LOG(WARNING) << "A database with name \"" << shared::kInfoSchemaDbName
3138  << "\" already exists. System table creation will be skipped. Rename "
3139  "this database in order to use system tables.";
3140  } else {
3142  try {
3144  } catch (...) {
3146  dropDatabase(db_metadata);
3147  throw;
3148  }
3149  }
3150  }
3151 }
3152 
3153 bool SysCatalog::hasExecutedMigration(const std::string& migration_name) const {
3154  if (hasVersionHistoryTable()) {
3155  sys_sqlite_lock sqlite_lock(this);
3156  sqliteConnector_->query_with_text_params(
3157  "SELECT migration_history FROM mapd_version_history WHERE migration_history = ?",
3158  std::vector<std::string>{migration_name});
3159  return (sqliteConnector_->getNumRows() > 0);
3160  }
3161  return false;
3162 }
3163 
3164 void SysCatalog::recordExecutedMigration(const std::string& migration_name) const {
3165  if (!hasVersionHistoryTable()) {
3167  }
3168  sys_sqlite_lock sqlite_lock(this);
3169  sqliteConnector_->query_with_text_params(
3170  "INSERT INTO mapd_version_history(version, migration_history) values(?, ?)",
3171  std::vector<std::string>{std::to_string(MAPD_VERSION), migration_name});
3172 }
3173 
3175  sys_sqlite_lock sqlite_lock(this);
3176  sqliteConnector_->query(
3177  "select name from sqlite_master WHERE type='table' AND "
3178  "name='mapd_version_history'");
3179  return (sqliteConnector_->getNumRows() > 0);
3180 }
3181 
3183  sys_sqlite_lock sqlite_lock(this);
3184  sqliteConnector_->query(
3185  "CREATE TABLE mapd_version_history(version integer, migration_history text "
3186  "unique)");
3187 }
3188 
3190  // Rebuild updated maps from storage
3191  granteeMap_.clear();
3193  objectDescriptorMap_.clear();
3195 }
3196 
3198  sys_write_lock write_lock(this);
3199  sys_sqlite_lock sqlite_lock(this);
3200  static const std::string drop_render_groups_migration{"drop_render_groups"};
3201  if (!hasExecutedMigration(drop_render_groups_migration)) {
3202  bool all_catalogs_migrated = true;
3203 
3204  LOG(INFO) << "Starting Drop-Render-Group-Columns Migration...";
3206  for (auto& cat : cats) {
3208  all_catalogs_migrated = false;
3209  }
3210  }
3211  LOG(INFO) << "Drop-Render-Group-Columns Migration complete";
3212 
3213  recordExecutedMigration(drop_render_groups_migration);
3214 
3215  if (!all_catalogs_migrated) {
3216  // we have marked the migration as having been attempted, but one or more tables did
3217  // not correctly migrate, so we fail here in order to alert the admin that they need
3218  // to be manually repaired or dropped on next startup
3219  LOG(FATAL) << "One or more tables in one or more databases failed "
3220  "drop-render-group-columns migration. Check INFO and ERROR logs for "
3221  "more details. This message will not be repeated unless the "
3222  "migration record is manually reset.";
3223  }
3224  }
3225 }
3226 
3227 const TableDescriptor* get_metadata_for_table(const ::shared::TableKey& table_key,
3228  bool populate_fragmenter) {
3229  const auto catalog = SysCatalog::instance().getCatalog(table_key.db_id);
3230  CHECK(catalog);
3231  return catalog->getMetadataForTable(table_key.table_id, populate_fragmenter);
3232 }
3233 
3235  const auto catalog = SysCatalog::instance().getCatalog(column_key.db_id);
3236  CHECK(catalog);
3237  return catalog->getMetadataForColumn(column_key.table_id, column_key.column_id);
3238 }
3239 } // namespace Catalog_Namespace
std::optional< std::string > passwd
Definition: SysCatalog.h:117
bool contains(const T &container, const U &element)
Definition: misc.h:195
static const AccessPrivileges VIEW_SQL_EDITOR
Definition: DBObject.h:152
auto get_users(SysCatalog &syscat, std::unique_ptr< SqliteConnector > &sqliteConnector, const int32_t dbId=-1)
void recordExecutedMigration(const std::string &migration_name) const
bool hasAnyPrivilegesOnDb(int32_t dbId, bool only_direct) const
Definition: Grantee.cpp:95
void revokeAllOnDatabase_unsafe(const std::string &roleName, int32_t dbId, Grantee *grantee)
const int kRootUserId
const std::string kDataDirectoryName
void revokeDBObjectPrivilegesBatch_unsafe(const std::vector< std::string > &grantees, const std::vector< DBObject > &objects, const Catalog_Namespace::Catalog &catalog)
std::tuple< int, std::string > ColumnKey
Definition: Types.h:37
void dropUserUnchecked(const std::string &name, const UserMetadata &user)
std::vector< Catalog * > getCatalogsForAllDbs()
bool g_multi_instance
Definition: heavyai_locks.h:22
std::string cat(Ts &&...args)
DBObjectKey getObjectKey() const
Definition: DBObject.h:221
auto duplicateAndRenameCatalog(std::string const &current_name, std::string const &new_name)
Definition: SysCatalog.cpp:174
std::optional< std::string > default_db
Definition: SysCatalog.h:119
class for a per-database catalog. also includes metadata for the current database and the current use...
Definition: Catalog.h:143
void changeDBObjectOwnership(const UserMetadata &new_owner, const UserMetadata &previous_owner, DBObject object, const Catalog_Namespace::Catalog &catalog, bool revoke_privileges=true)
static const AccessPrivileges ALL_DATABASE
Definition: DBObject.h:151
virtual void grantPrivileges(const DBObject &object)
Definition: Grantee.cpp:105
DBObjectType
Definition: DBObject.h:40
std::set< std::string > getCreatedRoles() const
void grantRole(const std::string &role, const std::string &grantee, const bool is_temporary=false)
void updatePrivileges(const DBObject &object)
Definition: DBObject.cpp:152
void revokeRole(const std::string &role, const std::string &grantee, const bool is_temporary=false)
static const AccessPrivileges ALL_TABLE_MIGRATE
Definition: DBObject.h:156
#define LOG(tag)
Definition: Logger.h:285
bool checkPasswordForUser(const std::string &passwd, std::string &name, UserMetadata &user)
static const int32_t ALL
Definition: DBObject.h:77
void revokeDBObjectPrivileges_unsafe(const std::string &granteeName, DBObject object, const Catalog_Namespace::Catalog &catalog)
static void relaxMigrationLock()
std::optional< UserMetadata > getUser(std::string const &uname)
Definition: SysCatalog.h:203
void checkDuplicateCaseInsensitiveDbNames() const
Definition: SysCatalog.cpp:900
void createRole_unsafe(const std::string &roleName, const bool userPrivateRole, const bool is_temporary)
void revokeDBObjectPrivilegesFromAll(DBObject object, Catalog *catalog)
bool getMetadataForUser(const std::string &name, UserMetadata &user)
void revokeDBObjectPrivileges(const std::string &grantee, const DBObject &object, const Catalog_Namespace::Catalog &catalog)
void removeCatalog(const std::string &dbName)
static bool parseUserMetadataFromSQLite(const std::unique_ptr< SqliteConnector > &conn, UserMetadata &user, int row)
std::string name() const
Definition: SysCatalog.h:358
std::string join(T const &container, std::string const &delim)
void checkDropRenderGroupColumnsMigration() const
#define UNREACHABLE()
Definition: Logger.h:338
void createRole(const std::string &roleName, const bool user_private_role, const bool is_temporary=false)
const TableDescriptor * get_metadata_for_table(const ::shared::TableKey &table_key, bool populate_fragmenter)
const std::string kSystemCatalogName
const ColumnDescriptor * get_metadata_for_column(const ::shared::ColumnKey &column_key)
void setObjectKey(const DBObjectKey &objectKey)
Definition: DBObject.h:225
ObjectRoleDescriptorMap objectDescriptorMap_
Definition: SysCatalog.h:508
void changeDatabaseOwner(std::string const &dbname, const std::string &new_owner)
Definition: Grantee.h:75
Grantee * getGrantee(const std::string &name) const
void dropDatabase(const DBMetadata &db)
void loginImpl(std::string &username, const std::string &password, UserMetadata &user_meta)
Definition: SysCatalog.cpp:949
int32_t objectId
Definition: DBObject.h:55
void setName(std::string name)
Definition: DBObject.h:218
Definition: Grantee.h:81
std::vector< ObjectRoleDescriptor > getMetadataForAllObjects() const
const std::string kDefaultExportDirName
bool getMetadataForUserById(const int32_t idIn, UserMetadata &user)
void setPrivileges(const AccessPrivileges &privs)
Definition: DBObject.h:227
void insertOrUpdateObjectPrivileges(std::unique_ptr< SqliteConnector > &sqliteConnector, std::string roleName, bool userRole, const DBObject &object)
Definition: SysCatalog.cpp:514
const std::string kInfoSchemaDbName
void reassignObjectOwners(const std::map< int32_t, std::vector< DBObject >> &old_owner_db_objects, int32_t new_owner_id, const Catalog_Namespace::Catalog &catalog)
dsqliteMutex_(std::make_unique< heavyai::DistributedSharedMutex >(std::filesystem::path(basePath_)/shared::kLockfilesDirectoryName/shared::kCatalogDirectoryName/(currentDB_.dbName+".sqlite.lockfile")))
std::string toString(bool hide_password=true) const
std::list< UpdateQuery > UpdateQueries
Definition: SysCatalog.h:435
std::optional< bool > is_super
Definition: SysCatalog.h:118
void createDBObject(const UserMetadata &user, const std::string &objectName, DBObjectType type, const Catalog_Namespace::Catalog &catalog, int32_t objectId=-1)
void dropUser(const std::string &name, bool if_exists=false)
DBObject * findDbObject(const DBObjectKey &objectKey, bool only_direct) const
Definition: Grantee.cpp:85
std::string to_string(char const *&&v)
void getDBObjectPrivileges(const std::string &granteeName, DBObject &object, const Catalog_Namespace::Catalog &catalog) const
void revokeRole_unsafe(const std::string &roleName, const std::string &granteeName, const bool is_temporary)
bool hasVersionHistoryTable() const
void grantDBObjectPrivileges_unsafe(const std::string &granteeName, const DBObject object, const Catalog_Namespace::Catalog &catalog)
static const AccessPrivileges ALL_VIEW
Definition: DBObject.h:177
void grantRoleBatch(const std::vector< std::string > &roles, const std::vector< std::string > &grantees)
std::unique_ptr< PkiServer > pki_server_
Definition: SysCatalog.h:512
void revokeDBObjectPrivilegesBatch(const std::vector< std::string > &grantees, const std::vector< DBObject > &objects, const Catalog_Namespace::Catalog &catalog)
void grantRoleBatch_unsafe(const std::vector< std::string > &roles, const std::vector< std::string > &grantees)
This file contains the class specification and related data structures for Catalog.
bool checkPrivileges(const UserMetadata &user, const std::vector< DBObject > &privObjects) const
void renameDBObject(const std::string &objectName, const std::string &newName, DBObjectType type, int32_t objectId, const Catalog_Namespace::Catalog &catalog)
static DBObjectKey fromString(const std::vector< std::string > &key, const DBObjectType &type)
Definition: DBObject.cpp:271
static SysCatalog & instance()
Definition: SysCatalog.h:343
This file contains the class specification and related data structures for SysCatalog.
bool g_enable_idp_temporary_users
Definition: SysCatalog.cpp:63
bool wouldChange(UserMetadata const &user_meta) const
Classes representing a parse tree.
void setPermissionType(const DBObjectType &permissionType)
Definition: DBObject.cpp:160
void getMetadataWithDefaultDB(std::string &dbname, const std::string &username, Catalog_Namespace::DBMetadata &db_meta, UserMetadata &user_meta)
const DBMetadata & getCurrentDB() const
Definition: Catalog.h:265
bool g_enable_system_tables
Definition: SysCatalog.cpp:64
const std::string kDefaultDbName
void grantAllOnDatabase_unsafe(const std::string &roleName, DBObject &object, const Catalog_Namespace::Catalog &catalog)
static const std::string getForeignTableSchema(bool if_not_exists=false)
Definition: Catalog.cpp:782
std::string g_base_path
Definition: SysCatalog.cpp:62
void init(LogOptions const &log_opts)
Definition: Logger.cpp:364
std::string generate_random_string(const size_t len)
DEVICE auto copy(ARGS &&...args)
Definition: gpu_enabled.h:51
#define CHECK_NE(x, y)
Definition: Logger.h:302
std::unordered_map< std::string, std::shared_ptr< UserMetadata > > temporary_users_by_name_
Definition: SysCatalog.h:543
virtual void revokeAllOnDatabase(int32_t dbId)
Definition: Grantee.cpp:296
std::string hash_with_bcrypt(const std::string &pwd)
Definition: SysCatalog.cpp:71
void renameObjectsInDescriptorMap(DBObject &object, const Catalog_Namespace::Catalog &cat)
bool checkPasswordForUserImpl(const std::string &passwd, std::string &name, UserMetadata &user)
static bool migrationEnabled()
Definition: MigrationMgr.h:47
std::shared_ptr< Catalog > login(std::string &db, std::string &username, const std::string &password, UserMetadata &user_meta, bool check_password=true)
Definition: SysCatalog.cpp:923
void revokeRoleBatch_unsafe(const std::vector< std::string > &roles, const std::vector< std::string > &grantees)
void grantRole_unsafe(const std::string &roleName, const std::string &granteeName, const bool is_temporary)
void revokeRoleBatch(const std::vector< std::string > &roles, const std::vector< std::string > &grantees)
std::shared_ptr< Data_Namespace::DataMgr > dataMgr_
Definition: SysCatalog.h:511
UserMetadata createUser(std::string const &name, UserAlterations alts, bool is_temporary)
Definition: SysCatalog.cpp:987
std::unique_lock< T > unique_lock
DBSummaryList getDatabaseListForUser(const UserMetadata &user)
static const int32_t MAPD_VERSION
Definition: release.h:32
static const AccessPrivileges ALL_DASHBOARD_MIGRATE
Definition: DBObject.h:168
std::shared_ptr< Catalog > switchDatabase(std::string &dbname, const std::string &username)
Definition: SysCatalog.cpp:957
Role * getRoleGrantee(const std::string &name) const
static const std::string getForeignServerSchema(bool if_not_exists=false)
Definition: Catalog.cpp:775
int getDatabaseId() const
Definition: Catalog.h:326
static const AccessPrivileges ALL_SERVER
Definition: DBObject.h:187
void revokeDBObjectPrivilegesFromAllBatch_unsafe(std::vector< DBObject > &objects, Catalog *catalog)
User * getUserGrantee(const std::string &name) const
void grantDBObjectPrivilegesBatch(const std::vector< std::string > &grantees, const std::vector< DBObject > &objects, const Catalog_Namespace::Catalog &catalog)
void grantDBObjectPrivileges(const std::string &grantee, const DBObject &object, const Catalog_Namespace::Catalog &catalog)
specifies the content in-memory of a row in the column metadata table
OUTPUT transform(INPUT const &input, FUNC const &func)
Definition: misc.h:320
std::unique_ptr< SqliteConnector > sqliteConnector_
Definition: SysCatalog.h:509
static const AccessPrivileges NONE
Definition: DBObject.h:148
void updateUserRoleName(const std::string &roleName, const std::string &newName)
std::list< UserMetadata > getAllUserMetadata()
void grantDBObjectPrivilegesBatch_unsafe(const std::vector< std::string > &grantees, const std::vector< DBObject > &objects, const Catalog_Namespace::Catalog &catalog)
static const std::string SYSTEM_ROLE_TAG("#dash_system_role")
specifies the content in-memory of a row in the dashboard
void execInTransaction(F &&f, Args &&...args)
void dropRole_unsafe(const std::string &roleName, const bool is_temporary)
std::string to_upper(const std::string &str)
void check_for_session_encryption(const std::string &pki_cert, std::string &session)
Definition: SysCatalog.cpp:979
void renameUser(std::string const &old_name, std::string const &new_name)
void loadKey()
Definition: DBObject.cpp:190
std::shared_ptr< Catalog > getCatalog(const std::string &dbName)
bool isRoleGrantedToGrantee(const std::string &granteeName, const std::string &roleName, bool only_direct) const
const std::string kRootUsername
void setObjectType(const DBObjectType &objectType)
Definition: DBObject.cpp:163
bool hasAnyPrivileges(const UserMetadata &user, std::vector< DBObject > &privObjects)
void deleteObjectDescriptorMap(const std::string &roleName)
static const AccessPrivileges ALL_VIEW_MIGRATE
Definition: DBObject.h:176
static void takeMigrationLock(const std::string &base_path)
bool g_read_only
Definition: heavyai_locks.h:21
void updateObjectDescriptorMap(const std::string &roleName, DBObject &object, bool roleType, const Catalog_Namespace::Catalog &cat)
std::unordered_map< int32_t, std::shared_ptr< UserMetadata > > temporary_users_by_id_
Definition: SysCatalog.h:544
void syncUserWithRemoteProvider(const std::string &user_name, std::vector< std::string > idp_roles, UserAlterations alts)
const std::string kDefaultRootPasswd
void dropRole(const std::string &roleName, const bool is_temporary=false)
void createVersionHistoryTable() const
std::list< DBMetadata > getAllDBMetadata()
static const std::string getCustomExpressionsSchema(bool if_not_exists=false)
Definition: Catalog.cpp:790
void renameDatabase(std::string const &old_name, std::string const &new_name)
int32_t dbId
Definition: DBObject.h:54
void revokeDBObjectPrivilegesFromAll_unsafe(DBObject object, Catalog *catalog)
tuple conn
Definition: report.py:41
const std::string kRootUserIdStr
static const AccessPrivileges ALL_DASHBOARD
Definition: DBObject.h:169
static const AccessPrivileges ACCESS
Definition: DBObject.h:153
torch::Tensor f(torch::Tensor x, torch::Tensor W_target, torch::Tensor b_target)
bool checkDropRenderGroupColumnsMigration()
Definition: Catalog.cpp:931
bool verifyDBObjectOwnership(const UserMetadata &user, DBObject object, const Catalog_Namespace::Catalog &catalog)
static const AccessPrivileges ALL_TABLE
Definition: DBObject.h:157
const std::string kCatalogDirectoryName
std::vector< LeafHostInfo > string_dict_hosts_
Definition: SysCatalog.h:515
bool hasRole(Role *role, bool only_direct) const
Definition: Grantee.cpp:55
std::optional< bool > can_login
Definition: SysCatalog.h:120
#define CHECK(condition)
Definition: Logger.h:291
std::shared_ptr< Calcite > calciteMgr_
Definition: SysCatalog.h:514
std::unordered_map< std::string, std::vector< std::string > > getGranteesOfSharedDashboards(const std::vector< std::string > &dashboard_ids)
void runUpdateQueriesAndChangeOwnership(const UserMetadata &new_owner, const UserMetadata &previous_owner, DBObject object, const Catalog_Namespace::Catalog &catalog, const UpdateQueries &update_queries, bool revoke_privileges=true)
const std::string kLockfilesDirectoryName
std::list< DBSummary > DBSummaryList
Definition: SysCatalog.h:145
int32_t permissionType
Definition: DBObject.h:53
void populateRoleDbObjects(const std::vector< DBObject > &objects)
void deleteObjectPrivileges(std::unique_ptr< SqliteConnector > &sqliteConnector, std::string roleName, bool userRole, DBObject &object)
Definition: SysCatalog.cpp:495
int64_t privileges
Definition: DBObject.h:133
bool isDashboardSystemRole(const std::string &roleName) const
string name
Definition: setup.in.py:72
bool hasExecutedMigration(const std::string &migration_name) const
read_lock< SysCatalog > sys_read_lock
Definition: Catalog.cpp:120
bool g_enable_fsi
Definition: Catalog.cpp:96
std::string userLoggable() const
Definition: SysCatalog.cpp:158
bool getMetadataForDBById(const int32_t idIn, DBMetadata &db)
void createDatabase(const std::string &dbname, int owner)
UserMetadata alterUser(std::string const &name, UserAlterations alts)
void removeCatalogByFullPath(std::string const &full_path)
Definition: SysCatalog.cpp:166
DEVICE void swap(ARGS &&...args)
Definition: gpu_enabled.h:114
const std::string kInfoSchemaMigrationName
std::vector< ObjectRoleDescriptor * > getMetadataForObject(int32_t dbId, int32_t dbType, int32_t objectId) const
std::vector< std::string > getRoles(const std::string &user_name, bool effective=true)
virtual void renameDbObject(const DBObject &object)
Definition: Grantee.cpp:121
A selection of helper methods for File I/O.
#define VLOG(n)
Definition: Logger.h:388
std::filesystem::path copy_catalog_if_read_only(std::filesystem::path base_data_path)
Definition: SysCatalog.cpp:79
std::atomic< bool > isSuper
Definition: SysCatalog.h:107
bool getMetadataForDB(const std::string &name, DBMetadata &db)
void revokeDBObjectPrivilegesFromAllBatch(std::vector< DBObject > &objects, Catalog *catalog)