YugabyteDB (2.13.0.0-b42, bfc6a6643e7399ac8a0e81d06a3ee6d6571b33ab)

Coverage Report

Created: 2022-03-09 17:30

/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
843
                                 const TableName& table) {
41
843
  switch (object_type) {
42
245
    case ql::ObjectType::TABLE:
43
245
      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
474
          "User $0 has no $1 permission on <keyspace $2> or any of its parents",
54
474
          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
843
                               to_underlying(object_type));
67
843
  }
68
843
}
69
70
} // namespace
71
72
YBMetaDataCache::YBMetaDataCache(client::YBClient* client,
73
43.3k
                                 bool create_roles_permissions_cache) : client_(client)  {
74
43.3k
  if (create_roles_permissions_cache) {
75
1.45k
    permissions_cache_ = std::make_shared<client::internal::PermissionsCache>(client);
76
41.8k
  } else {
77
41.8k
    LOG(INFO) << "Creating a metadata cache without a permissions cache";
78
41.8k
  }
79
43.3k
}
80
81
39.4k
YBMetaDataCache::~YBMetaDataCache() = default;
82
83
Status YBMetaDataCache::GetTable(const YBTableName& table_name,
84
                                 std::shared_ptr<YBTable>* table,
85
532k
                                 bool* cache_used) {
86
532k
  {
87
532k
    std::lock_guard<std::mutex> lock(cached_tables_mutex_);
88
532k
    auto itr = cached_tables_by_name_.find(table_name);
89
532k
    if (itr != cached_tables_by_name_.end()) {
90
487k
      *table = itr->second;
91
487k
      *cache_used = true;
92
487k
      return Status::OK();
93
487k
    }
94
44.7k
  }
95
96
44.7k
  RETURN_NOT_OK(client_->OpenTable(table_name, table));
97
42.4k
  {
98
42.4k
    std::lock_guard<std::mutex> lock(cached_tables_mutex_);
99
42.4k
    cached_tables_by_name_[(*table)->name()] = *table;
100
42.4k
    cached_tables_by_id_[(*table)->id()] = *table;
101
42.4k
  }
102
42.4k
  *cache_used = false;
103
42.4k
  return Status::OK();
104
44.7k
}
105
106
Status YBMetaDataCache::GetTable(const TableId& table_id,
107
                                 std::shared_ptr<YBTable>* table,
108
49.4k
                                 bool* cache_used) {
109
49.4k
  {
110
49.4k
    std::lock_guard<std::mutex> lock(cached_tables_mutex_);
111
49.4k
    auto itr = cached_tables_by_id_.find(table_id);
112
49.4k
    if (itr != cached_tables_by_id_.end()) {
113
47.4k
      *table = itr->second;
114
47.4k
      *cache_used = true;
115
47.4k
      return Status::OK();
116
47.4k
    }
117
1.94k
  }
118
119
1.94k
  RETURN_NOT_OK(client_->OpenTable(table_id, table));
120
1.94k
  {
121
1.94k
    std::lock_guard<std::mutex> lock(cached_tables_mutex_);
122
1.94k
    cached_tables_by_name_[(*table)->name()] = *table;
123
1.94k
    cached_tables_by_id_[table_id] = *table;
124
1.94k
  }
125
1.94k
  *cache_used = false;
126
1.94k
  return Status::OK();
127
1.94k
}
128
129
9.24k
void YBMetaDataCache::RemoveCachedTable(const YBTableName& table_name) {
130
9.24k
  std::lock_guard<std::mutex> lock(cached_tables_mutex_);
131
9.24k
  const auto itr = cached_tables_by_name_.find(table_name);
132
9.24k
  if (itr != cached_tables_by_name_.end()) {
133
2.88k
    const auto table_id = itr->second->id();
134
2.88k
    cached_tables_by_name_.erase(itr);
135
2.88k
    cached_tables_by_id_.erase(table_id);
136
2.88k
  }
137
9.24k
}
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
81
                                  bool *cache_used) {
153
81
  auto type_path = std::make_pair(keyspace_name, type_name);
154
81
  {
155
81
    std::lock_guard<std::mutex> lock(cached_types_mutex_);
156
81
    auto itr = cached_types_.find(type_path);
157
81
    if (itr != cached_types_.end()) {
158
26
      *type = itr->second;
159
26
      *cache_used = true;
160
26
      return Status::OK();
161
26
    }
162
55
  }
163
164
55
  RETURN_NOT_OK(client_->GetUDType(keyspace_name, type_name, type));
165
48
  {
166
48
    std::lock_guard<std::mutex> lock(cached_types_mutex_);
167
48
    cached_types_[type_path] = *type;
168
48
  }
169
48
  *cache_used = false;
170
48
  return Status::OK();
171
55
}
172
173
void YBMetaDataCache::RemoveCachedUDType(const string& keyspace_name,
174
59
                                         const string& type_name) {
175
59
  std::lock_guard<std::mutex> lock(cached_types_mutex_);
176
59
  cached_types_.erase(std::make_pair(keyspace_name, type_name));
177
59
}
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
917
  }
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
4.11k
      object_type != ql::ObjectType::TABLE &&
218
3.70k
      object_type != ql::ObjectType::ROLE) {
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
13.6k
  }
229
230
13.6k
  if (!permissions_cache_->HasCanonicalResourcePermission(canonical_resource, object_type,
231
1.06k
                                                          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
538
      RETURN_NOT_OK(client_->GetPermissions(permissions_cache_.get()));
236
538
      if (permissions_cache_->HasCanonicalResourcePermission(canonical_resource, object_type,
237
222
                                                             role_name, permission)) {
238
222
        return Status::OK();
239
222
      }
240
843
    }
241
843
    return GenerateUnauthorizedError(
242
843
        canonical_resource, object_type, role_name, permission, keyspace, table);
243
843
  }
244
245
  // Found.
246
12.5k
  return Status::OK();
247
12.5k
}
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.00k
                            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()) {
271
133
    s = HasTablePermission(keyspace_name, table_name, role_name, permission,
272
133
                           CacheCheckMode::NO_RETRY);
273
133
  }
274
415
  return s;
275
415
}
276
277
} // namespace client
278
} // namespace yb