YugabyteDB (2.13.1.0-b60, 21121d69985fbf76aa6958d8f04a9bfa936293b5)

Coverage Report

Created: 2022-03-22 16:43

/Users/deen/code/yugabyte-db/src/yb/client/meta_data_cache.cc
Line
Count
Source (jump to first uncovered line)
1
// Copyright (c) YugaByte, Inc.
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
4
// in compliance with the License.  You may obtain a copy of the License at
5
//
6
// http://www.apache.org/licenses/LICENSE-2.0
7
//
8
// Unless required by applicable law or agreed to in writing, software distributed under the License
9
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
10
// or implied.  See the License for the specific language governing permissions and limitations
11
// under the License.
12
//
13
14
#include "yb/client/meta_data_cache.h"
15
16
#include "yb/client/client.h"
17
#include "yb/client/permissions.h"
18
#include "yb/client/table.h"
19
#include "yb/client/yb_table_name.h"
20
21
#include "yb/common/roles_permissions.h"
22
23
#include "yb/util/result.h"
24
#include "yb/util/status_format.h"
25
#include "yb/util/status_log.h"
26
27
DEFINE_int32(update_permissions_cache_msecs, 2000,
28
             "How often the roles' permissions cache should be updated. 0 means never update it");
29
30
namespace yb {
31
namespace client {
32
33
namespace {
34
35
Status GenerateUnauthorizedError(const std::string& canonical_resource,
36
                                 const ql::ObjectType& object_type,
37
                                 const RoleName& role_name,
38
                                 const PermissionType& permission,
39
                                 const NamespaceName& keyspace,
40
844
                                 const TableName& table) {
41
844
  switch (object_type) {
42
246
    case ql::ObjectType::TABLE:
43
246
      return STATUS_SUBSTITUTE(NotAuthorized,
44
0
          "User $0 has no $1 permission on <table $2.$3> or any of its parents",
45
0
          role_name, PermissionName(permission), keyspace, table);
46
562
    case ql::ObjectType::SCHEMA:
47
562
      if (canonical_resource == "data") {
48
88
        return STATUS_SUBSTITUTE(NotAuthorized,
49
88
            "User $0 has no $1 permission on <all keyspaces> or any of its parents",
50
88
            role_name, PermissionName(permission));
51
88
      }
52
474
      return STATUS_SUBSTITUTE(NotAuthorized,
53
562
          "User $0 has no $1 permission on <keyspace $2> or any of its parents",
54
562
          role_name, PermissionName(permission), keyspace);
55
36
    case ql::ObjectType::ROLE:
56
36
      if (canonical_resource == "role") {
57
0
        return STATUS_SUBSTITUTE(NotAuthorized,
58
0
            "User $0 has no $1 permission on <all roles> or any of its parents",
59
0
            role_name, PermissionName(permission));
60
0
      }
61
36
      return STATUS_SUBSTITUTE(NotAuthorized,
62
36
          "User $0 does not have sufficient privileges to perform the requested operation",
63
36
          role_name);
64
0
    default:
65
0
      return STATUS_SUBSTITUTE(IllegalState, "Unable to find permissions for object $0",
66
844
                               to_underlying(object_type));
67
844
  }
68
844
}
69
70
} // namespace
71
72
YBMetaDataCache::YBMetaDataCache(client::YBClient* client,
73
57.4k
                                 bool create_roles_permissions_cache) : client_(client)  {
74
57.4k
  if (create_roles_permissions_cache) {
75
1.45k
    permissions_cache_ = std::make_shared<client::internal::PermissionsCache>(client);
76
55.9k
  } else {
77
55.9k
    LOG(INFO) << "Creating a metadata cache without a permissions cache";
78
55.9k
  }
79
57.4k
}
80
81
51.6k
YBMetaDataCache::~YBMetaDataCache() = default;
82
83
Status YBMetaDataCache::GetTable(const YBTableName& table_name,
84
                                 std::shared_ptr<YBTable>* table,
85
745k
                                 bool* cache_used) {
86
745k
  {
87
745k
    std::lock_guard<std::mutex> lock(cached_tables_mutex_);
88
745k
    auto itr = cached_tables_by_name_.find(table_name);
89
745k
    if (itr != cached_tables_by_name_.end()) {
90
698k
      *table = itr->second;
91
698k
      *cache_used = true;
92
698k
      return Status::OK();
93
698k
    }
94
745k
  }
95
96
46.9k
  RETURN_NOT_OK(client_->OpenTable(table_name, table));
97
44.6k
  {
98
44.6k
    std::lock_guard<std::mutex> lock(cached_tables_mutex_);
99
44.6k
    cached_tables_by_name_[(*table)->name()] = *table;
100
44.6k
    cached_tables_by_id_[(*table)->id()] = *table;
101
44.6k
  }
102
44.6k
  *cache_used = false;
103
44.6k
  return Status::OK();
104
46.9k
}
105
106
Status YBMetaDataCache::GetTable(const TableId& table_id,
107
                                 std::shared_ptr<YBTable>* table,
108
42.6k
                                 bool* cache_used) {
109
42.6k
  {
110
42.6k
    std::lock_guard<std::mutex> lock(cached_tables_mutex_);
111
42.6k
    auto itr = cached_tables_by_id_.find(table_id);
112
42.6k
    if (itr != cached_tables_by_id_.end()) {
113
40.5k
      *table = itr->second;
114
40.5k
      *cache_used = true;
115
40.5k
      return Status::OK();
116
40.5k
    }
117
42.6k
  }
118
119
2.03k
  RETURN_NOT_OK(client_->OpenTable(table_id, table));
120
2.03k
  {
121
2.03k
    std::lock_guard<std::mutex> lock(cached_tables_mutex_);
122
2.03k
    cached_tables_by_name_[(*table)->name()] = *table;
123
2.03k
    cached_tables_by_id_[table_id] = *table;
124
2.03k
  }
125
2.03k
  *cache_used = false;
126
2.03k
  return Status::OK();
127
2.03k
}
128
129
9.52k
void YBMetaDataCache::RemoveCachedTable(const YBTableName& table_name) {
130
9.52k
  std::lock_guard<std::mutex> lock(cached_tables_mutex_);
131
9.52k
  const auto itr = cached_tables_by_name_.find(table_name);
132
9.52k
  if (itr != cached_tables_by_name_.end()) {
133
2.95k
    const auto table_id = itr->second->id();
134
2.95k
    cached_tables_by_name_.erase(itr);
135
2.95k
    cached_tables_by_id_.erase(table_id);
136
2.95k
  }
137
9.52k
}
138
139
0
void YBMetaDataCache::RemoveCachedTable(const TableId& table_id) {
140
0
  std::lock_guard<std::mutex> lock(cached_tables_mutex_);
141
0
  const auto itr = cached_tables_by_id_.find(table_id);
142
0
  if (itr != cached_tables_by_id_.end()) {
143
0
    const auto table_name = itr->second->name();
144
0
    cached_tables_by_name_.erase(table_name);
145
0
    cached_tables_by_id_.erase(itr);
146
0
  }
147
0
}
148
149
Status YBMetaDataCache::GetUDType(const string& keyspace_name,
150
                                  const string& type_name,
151
                                  std::shared_ptr<QLType> *type,
152
82
                                  bool *cache_used) {
153
82
  auto type_path = std::make_pair(keyspace_name, type_name);
154
82
  {
155
82
    std::lock_guard<std::mutex> lock(cached_types_mutex_);
156
82
    auto itr = cached_types_.find(type_path);
157
82
    if (itr != cached_types_.end()) {
158
26
      *type = itr->second;
159
26
      *cache_used = true;
160
26
      return Status::OK();
161
26
    }
162
82
  }
163
164
56
  RETURN_NOT_OK(client_->GetUDType(keyspace_name, type_name, type));
165
49
  {
166
49
    std::lock_guard<std::mutex> lock(cached_types_mutex_);
167
49
    cached_types_[type_path] = *type;
168
49
  }
169
49
  *cache_used = false;
170
49
  return Status::OK();
171
56
}
172
173
void YBMetaDataCache::RemoveCachedUDType(const string& keyspace_name,
174
60
                                         const string& type_name) {
175
60
  std::lock_guard<std::mutex> lock(cached_types_mutex_);
176
60
  cached_types_.erase(std::make_pair(keyspace_name, type_name));
177
60
}
178
179
917
Status YBMetaDataCache::WaitForPermissionCache() {
180
917
  if (!permissions_cache_) {
181
0
    LOG(WARNING) << "Permissions cache disabled. This only should be used in unit tests";
182
0
    return STATUS(TimedOut, "Permissions cache unavailable");
183
0
  }
184
185
917
  if (!permissions_cache_->ready()) {
186
0
    if (!permissions_cache_->WaitUntilReady(
187
0
            MonoDelta::FromMilliseconds(FLAGS_update_permissions_cache_msecs))) {
188
0
      return STATUS(TimedOut, "Permissions cache unavailable");
189
0
    }
190
0
  }
191
917
  return Status::OK();
192
917
}
193
194
519
Result<std::string> YBMetaDataCache::RoleSaltedHash(const RoleName& role_name) {
195
519
  RETURN_NOT_OK(WaitForPermissionCache());
196
519
  return permissions_cache_->salted_hash(role_name);
197
519
}
198
199
398
Result<bool> YBMetaDataCache::RoleCanLogin(const RoleName& role_name) {
200
398
  RETURN_NOT_OK(WaitForPermissionCache());
201
398
  return permissions_cache_->can_login(role_name);
202
398
}
203
204
Status YBMetaDataCache::HasResourcePermission(const std::string& canonical_resource,
205
                                              const ql::ObjectType& object_type,
206
                                              const RoleName& role_name,
207
                                              const PermissionType& permission,
208
                                              const NamespaceName& keyspace,
209
                                              const TableName& table,
210
13.6k
                                              const CacheCheckMode check_mode) {
211
13.6k
  if (!permissions_cache_) {
212
0
    LOG(WARNING) << "Permissions cache disabled. This only should be used in unit tests";
213
0
    return Status::OK();
214
0
  }
215
216
13.6k
  if (object_type != ql::ObjectType::SCHEMA &&
217
13.6k
      
object_type != ql::ObjectType::TABLE4.11k
&&
218
13.6k
      
object_type != ql::ObjectType::ROLE3.70k
) {
219
0
    DFATAL_OR_RETURN_NOT_OK(STATUS_SUBSTITUTE(InvalidArgument, "Invalid ObjectType $0",
220
0
                                              to_underlying(object_type)));
221
0
  }
222
223
13.6k
  if (!permissions_cache_->ready()) {
224
0
    if (!permissions_cache_->WaitUntilReady(
225
0
            MonoDelta::FromMilliseconds(FLAGS_update_permissions_cache_msecs))) {
226
0
      return STATUS(TimedOut, "Permissions cache unavailable");
227
0
    }
228
0
  }
229
230
13.6k
  if (!permissions_cache_->HasCanonicalResourcePermission(canonical_resource, object_type,
231
13.6k
                                                          role_name, permission)) {
232
1.06k
    if (check_mode == CacheCheckMode::RETRY) {
233
      // We could have failed to find the permission because our cache is stale. If we are asked
234
      // to retry, we update the cache and try again.
235
537
      RETURN_NOT_OK(client_->GetPermissions(permissions_cache_.get()));
236
537
      if (permissions_cache_->HasCanonicalResourcePermission(canonical_resource, object_type,
237
537
                                                             role_name, permission)) {
238
220
        return Status::OK();
239
220
      }
240
537
    }
241
844
    return GenerateUnauthorizedError(
242
844
        canonical_resource, object_type, role_name, permission, keyspace, table);
243
1.06k
  }
244
245
  // Found.
246
12.5k
  return Status::OK();
247
13.6k
}
248
249
Status YBMetaDataCache::HasTablePermission(const NamespaceName& keyspace_name,
250
                                           const TableName& table_name,
251
                                           const RoleName& role_name,
252
                                           const PermissionType permission,
253
4.42k
                                           const CacheCheckMode check_mode) {
254
255
  // Check wihtout retry. In case our cache is stale, we will check again by issuing a recursive
256
  // call to this method.
257
4.42k
  if (HasResourcePermission(get_canonical_keyspace(keyspace_name),
258
4.42k
                            ql::ObjectType::SCHEMA, role_name, permission,
259
4.42k
                            keyspace_name, "", CacheCheckMode::NO_RETRY).ok()) {
260
4.00k
    return Status::OK();
261
4.00k
  }
262
263
  // By default the first call asks to retry. If we decide to retry, we will issue a recursive
264
  // call with NO_RETRY mode.
265
415
  Status s = HasResourcePermission(get_canonical_table(keyspace_name, table_name),
266
415
                                   ql::ObjectType::TABLE, role_name, permission,
267
415
                                   keyspace_name, table_name,
268
415
                                   check_mode);
269
270
415
  if (check_mode == CacheCheckMode::RETRY && 
s.IsNotAuthorized()303
) {
271
134
    s = HasTablePermission(keyspace_name, table_name, role_name, permission,
272
134
                           CacheCheckMode::NO_RETRY);
273
134
  }
274
415
  return s;
275
4.42k
}
276
277
} // namespace client
278
} // namespace yb