mirror of
https://github.com/scylladb/scylladb.git
synced 2026-06-06 23:13:15 +00:00
Merge "Generalize data_resource" from Jesse
"Soon we will have resources beyond just keyspaces and table names. There
will be resources for roles, for user-defined functions (UDFs), and
possible resources for REST end-points. This change generalizes the
implementation of a `data_resource` to many different kinds of
resources, though there is still only one kind (`data`).
The most important patch is 2/5 ("auth/resource: Generalize to different
kinds"), which re-writes `auth::data_resource`. The patch message should
sufficiently explain the design decisions involved.
The other patches rename files and identifiers based on the expanded
role of this class, except for 5/5 ("auth/resource.hh: Rename
`resource_ids`"): this patch gives a more appropriate name to a type
alias.
Fixes #3027."
* 'jhk/generalize_resource/v3' of https://github.com/hakuch/scylla:
auth/resource.hh: Rename `resource_ids`
auth: Rename `data_resource` files
cql3/authorization_statement: Fix typo
auth/resource: Generalize to different kinds
auth: Rename `data_resource` to `resource`
This commit is contained in:
@@ -84,9 +84,9 @@ public:
|
||||
return make_ready_future();
|
||||
}
|
||||
|
||||
const resource_ids& protected_resources() const override {
|
||||
static const resource_ids ids;
|
||||
return ids;
|
||||
const resource_set& protected_resources() const override {
|
||||
static const resource_set resources;
|
||||
return resources;
|
||||
}
|
||||
|
||||
::shared_ptr<sasl_challenge> new_sasl_challenge() const override {
|
||||
|
||||
@@ -56,15 +56,15 @@ public:
|
||||
return allow_all_authorizer_name();
|
||||
}
|
||||
|
||||
future<permission_set> authorize(service&, ::shared_ptr<authenticated_user>, data_resource) const override {
|
||||
future<permission_set> authorize(service&, ::shared_ptr<authenticated_user>, resource) const override {
|
||||
return make_ready_future<permission_set>(permissions::ALL);
|
||||
}
|
||||
|
||||
future<> grant(::shared_ptr<authenticated_user>, permission_set, data_resource, sstring) override {
|
||||
future<> grant(::shared_ptr<authenticated_user>, permission_set, resource, sstring) override {
|
||||
throw exceptions::invalid_request_exception("GRANT operation is not supported by AllowAllAuthorizer");
|
||||
}
|
||||
|
||||
future<> revoke(::shared_ptr<authenticated_user>, permission_set, data_resource, sstring) override {
|
||||
future<> revoke(::shared_ptr<authenticated_user>, permission_set, resource, sstring) override {
|
||||
throw exceptions::invalid_request_exception("REVOKE operation is not supported by AllowAllAuthorizer");
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ public:
|
||||
service&,
|
||||
::shared_ptr<authenticated_user> performer,
|
||||
permission_set,
|
||||
stdx::optional<data_resource>,
|
||||
stdx::optional<resource>,
|
||||
stdx::optional<sstring>) const override {
|
||||
throw exceptions::invalid_request_exception("LIST PERMISSIONS operation is not supported by AllowAllAuthorizer");
|
||||
}
|
||||
@@ -81,13 +81,13 @@ public:
|
||||
return make_ready_future();
|
||||
}
|
||||
|
||||
future<> revoke_all(data_resource) override {
|
||||
future<> revoke_all(resource) override {
|
||||
return make_ready_future();
|
||||
}
|
||||
|
||||
const resource_ids& protected_resources() override {
|
||||
static const resource_ids ids;
|
||||
return ids;
|
||||
const resource_set& protected_resources() override {
|
||||
static const resource_set resources;
|
||||
return resources;
|
||||
}
|
||||
|
||||
future<> validate_configuration() const override {
|
||||
|
||||
@@ -53,9 +53,9 @@
|
||||
#include <seastar/core/enum.hh>
|
||||
|
||||
#include "bytes.hh"
|
||||
#include "data_resource.hh"
|
||||
#include "enum_set.hh"
|
||||
#include "exceptions/exceptions.hh"
|
||||
#include "resource.hh"
|
||||
|
||||
namespace db {
|
||||
class config;
|
||||
@@ -160,9 +160,9 @@ public:
|
||||
* Set of resources that should be made inaccessible to users and only accessible internally.
|
||||
*
|
||||
* @return Keyspaces, column families that will be unmodifiable by users; other resources.
|
||||
* @see resource_ids
|
||||
* @see resource_set
|
||||
*/
|
||||
virtual const resource_ids& protected_resources() const = 0;
|
||||
virtual const resource_set& protected_resources() const = 0;
|
||||
|
||||
class sasl_challenge {
|
||||
public:
|
||||
|
||||
@@ -74,27 +74,27 @@ auth::authorizer::setup(const sstring& type) {
|
||||
const sstring& qualified_java_name() const override {
|
||||
return allow_all_authorizer_name();
|
||||
}
|
||||
future<permission_set> authorize(::shared_ptr<authenticated_user>, data_resource) const override {
|
||||
future<permission_set> authorize(::shared_ptr<authenticated_user>, resource) const override {
|
||||
return make_ready_future<permission_set>(permissions::ALL);
|
||||
}
|
||||
future<> grant(::shared_ptr<authenticated_user>, permission_set, data_resource, sstring) override {
|
||||
future<> grant(::shared_ptr<authenticated_user>, permission_set, resource, sstring) override {
|
||||
throw exceptions::invalid_request_exception("GRANT operation is not supported by AllowAllAuthorizer");
|
||||
}
|
||||
future<> revoke(::shared_ptr<authenticated_user>, permission_set, data_resource, sstring) override {
|
||||
future<> revoke(::shared_ptr<authenticated_user>, permission_set, resource, sstring) override {
|
||||
throw exceptions::invalid_request_exception("REVOKE operation is not supported by AllowAllAuthorizer");
|
||||
}
|
||||
future<std::vector<permission_details>> list(::shared_ptr<authenticated_user> performer, permission_set, optional<data_resource>, optional<sstring>) const override {
|
||||
future<std::vector<permission_details>> list(::shared_ptr<authenticated_user> performer, permission_set, optional<resource>, optional<sstring>) const override {
|
||||
throw exceptions::invalid_request_exception("LIST PERMISSIONS operation is not supported by AllowAllAuthorizer");
|
||||
}
|
||||
future<> revoke_all(sstring dropped_user) override {
|
||||
return make_ready_future();
|
||||
}
|
||||
future<> revoke_all(data_resource) override {
|
||||
future<> revoke_all(resource) override {
|
||||
return make_ready_future();
|
||||
}
|
||||
const resource_ids& protected_resources() override {
|
||||
static const resource_ids ids;
|
||||
return ids;
|
||||
const resource_set& protected_resources() override {
|
||||
static const resource_set resources;
|
||||
return resources;
|
||||
}
|
||||
future<> validate_configuration() const override {
|
||||
return make_ready_future();
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
#include <seastar/core/shared_ptr.hh>
|
||||
|
||||
#include "permission.hh"
|
||||
#include "data_resource.hh"
|
||||
#include "resource.hh"
|
||||
|
||||
#include "seastarx.hh"
|
||||
|
||||
@@ -61,7 +61,7 @@ class authenticated_user;
|
||||
|
||||
struct permission_details {
|
||||
sstring user;
|
||||
data_resource resource;
|
||||
::auth::resource resource;
|
||||
permission_set permissions;
|
||||
|
||||
bool operator<(const permission_details& v) const {
|
||||
@@ -88,7 +88,7 @@ public:
|
||||
* @param resource Resource for which the authorization is being requested. @see DataResource.
|
||||
* @return Set of permissions of the user on the resource. Should never return empty. Use permission.NONE instead.
|
||||
*/
|
||||
virtual future<permission_set> authorize(service&, ::shared_ptr<authenticated_user>, data_resource) const = 0;
|
||||
virtual future<permission_set> authorize(service&, ::shared_ptr<authenticated_user>, resource) const = 0;
|
||||
|
||||
/**
|
||||
* Grants a set of permissions on a resource to a user.
|
||||
@@ -102,7 +102,7 @@ public:
|
||||
* @throws RequestValidationException
|
||||
* @throws RequestExecutionException
|
||||
*/
|
||||
virtual future<> grant(::shared_ptr<authenticated_user> performer, permission_set, data_resource, sstring to) = 0;
|
||||
virtual future<> grant(::shared_ptr<authenticated_user> performer, permission_set, resource, sstring to) = 0;
|
||||
|
||||
/**
|
||||
* Revokes a set of permissions on a resource from a user.
|
||||
@@ -116,7 +116,7 @@ public:
|
||||
* @throws RequestValidationException
|
||||
* @throws RequestExecutionException
|
||||
*/
|
||||
virtual future<> revoke(::shared_ptr<authenticated_user> performer, permission_set, data_resource, sstring from) = 0;
|
||||
virtual future<> revoke(::shared_ptr<authenticated_user> performer, permission_set, resource, sstring from) = 0;
|
||||
|
||||
/**
|
||||
* Returns a list of permissions on a resource of a user.
|
||||
@@ -132,7 +132,7 @@ public:
|
||||
* @throws RequestValidationException
|
||||
* @throws RequestExecutionException
|
||||
*/
|
||||
virtual future<std::vector<permission_details>> list(service&, ::shared_ptr<authenticated_user> performer, permission_set, optional<data_resource>, optional<sstring>) const = 0;
|
||||
virtual future<std::vector<permission_details>> list(service&, ::shared_ptr<authenticated_user> performer, permission_set, optional<resource>, optional<sstring>) const = 0;
|
||||
|
||||
/**
|
||||
* This method is called before deleting a user with DROP USER query so that a new user with the same
|
||||
@@ -147,14 +147,14 @@ public:
|
||||
*
|
||||
* @param droppedResource The resource to revoke all permissions on.
|
||||
*/
|
||||
virtual future<> revoke_all(data_resource) = 0;
|
||||
virtual future<> revoke_all(resource) = 0;
|
||||
|
||||
/**
|
||||
* Set of resources that should be made inaccessible to users and only accessible internally.
|
||||
*
|
||||
* @return Keyspaces, column families that will be unmodifiable by users; other resources.
|
||||
*/
|
||||
virtual const resource_ids& protected_resources() = 0;
|
||||
virtual const resource_set& protected_resources() = 0;
|
||||
|
||||
/**
|
||||
* Validates configuration of IAuthorizer implementation (if configurable).
|
||||
|
||||
@@ -1,171 +0,0 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2016 ScyllaDB
|
||||
*
|
||||
* Modified by ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is part of Scylla.
|
||||
*
|
||||
* Scylla is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Scylla is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Scylla. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "data_resource.hh"
|
||||
|
||||
#include <regex>
|
||||
#include "service/storage_proxy.hh"
|
||||
|
||||
const sstring auth::data_resource::ROOT_NAME("data");
|
||||
|
||||
auth::data_resource::data_resource(level l, const sstring& ks, const sstring& cf)
|
||||
: _level(l), _ks(ks), _cf(cf)
|
||||
{
|
||||
}
|
||||
|
||||
auth::data_resource::data_resource()
|
||||
: data_resource(level::ROOT)
|
||||
{}
|
||||
|
||||
auth::data_resource::data_resource(const sstring& ks)
|
||||
: data_resource(level::KEYSPACE, ks)
|
||||
{}
|
||||
|
||||
auth::data_resource::data_resource(const sstring& ks, const sstring& cf)
|
||||
: data_resource(level::COLUMN_FAMILY, ks, cf)
|
||||
{}
|
||||
|
||||
auth::data_resource::level auth::data_resource::get_level() const {
|
||||
return _level;
|
||||
}
|
||||
|
||||
auth::data_resource auth::data_resource::from_name(
|
||||
const sstring& s) {
|
||||
|
||||
static std::regex slash_regex("/");
|
||||
|
||||
auto i = std::regex_token_iterator<sstring::const_iterator>(s.begin(),
|
||||
s.end(), slash_regex, -1);
|
||||
auto e = std::regex_token_iterator<sstring::const_iterator>();
|
||||
auto n = std::distance(i, e);
|
||||
|
||||
if (n > 3 || ROOT_NAME != sstring(*i++)) {
|
||||
throw std::invalid_argument(sprint("%s is not a valid data resource name", s));
|
||||
}
|
||||
|
||||
if (n == 1) {
|
||||
return data_resource();
|
||||
}
|
||||
auto ks = *i++;
|
||||
if (n == 2) {
|
||||
return data_resource(ks.str());
|
||||
}
|
||||
auto cf = *i++;
|
||||
return data_resource(ks.str(), cf.str());
|
||||
}
|
||||
|
||||
sstring auth::data_resource::name() const {
|
||||
switch (get_level()) {
|
||||
case level::ROOT:
|
||||
return ROOT_NAME;
|
||||
case level::KEYSPACE:
|
||||
return sprint("%s/%s", ROOT_NAME, _ks);
|
||||
case level::COLUMN_FAMILY:
|
||||
default:
|
||||
return sprint("%s/%s/%s", ROOT_NAME, _ks, _cf);
|
||||
}
|
||||
}
|
||||
|
||||
auth::data_resource auth::data_resource::get_parent() const {
|
||||
switch (get_level()) {
|
||||
case level::KEYSPACE:
|
||||
return data_resource();
|
||||
case level::COLUMN_FAMILY:
|
||||
return data_resource(_ks);
|
||||
default:
|
||||
throw std::invalid_argument("Root-level resource can't have a parent");
|
||||
}
|
||||
}
|
||||
|
||||
const sstring& auth::data_resource::keyspace() const {
|
||||
if (is_root_level()) {
|
||||
throw std::invalid_argument("ROOT data resource has no keyspace");
|
||||
}
|
||||
return _ks;
|
||||
}
|
||||
|
||||
const sstring& auth::data_resource::column_family() const {
|
||||
if (!is_column_family_level()) {
|
||||
throw std::invalid_argument(sprint("%s data resource has no column family", name()));
|
||||
}
|
||||
return _cf;
|
||||
}
|
||||
|
||||
bool auth::data_resource::has_parent() const {
|
||||
return !is_root_level();
|
||||
}
|
||||
|
||||
bool auth::data_resource::exists() const {
|
||||
switch (get_level()) {
|
||||
case level::ROOT:
|
||||
return true;
|
||||
case level::KEYSPACE:
|
||||
return service::get_local_storage_proxy().get_db().local().has_keyspace(_ks);
|
||||
case level::COLUMN_FAMILY:
|
||||
default:
|
||||
return service::get_local_storage_proxy().get_db().local().has_schema(_ks, _cf);
|
||||
}
|
||||
}
|
||||
|
||||
sstring auth::data_resource::to_string() const {
|
||||
switch (get_level()) {
|
||||
case level::ROOT:
|
||||
return "<all keyspaces>";
|
||||
case level::KEYSPACE:
|
||||
return sprint("<keyspace %s>", _ks);
|
||||
case level::COLUMN_FAMILY:
|
||||
default:
|
||||
return sprint("<table %s.%s>", _ks, _cf);
|
||||
}
|
||||
}
|
||||
|
||||
bool auth::data_resource::operator==(const data_resource& v) const {
|
||||
return _ks == v._ks && _cf == v._cf;
|
||||
}
|
||||
|
||||
bool auth::data_resource::operator<(const data_resource& v) const {
|
||||
return _ks < v._ks ? true : (v._ks < _ks ? false : _cf < v._cf);
|
||||
}
|
||||
|
||||
std::ostream& auth::operator<<(std::ostream& os, const data_resource& r) {
|
||||
return os << r.to_string();
|
||||
}
|
||||
|
||||
@@ -1,159 +0,0 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2016 ScyllaDB
|
||||
*
|
||||
* Modified by ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is part of Scylla.
|
||||
*
|
||||
* Scylla is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Scylla is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Scylla. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "utils/hash.hh"
|
||||
#include <iosfwd>
|
||||
#include <set>
|
||||
#include <seastar/core/sstring.hh>
|
||||
#include "seastarx.hh"
|
||||
|
||||
namespace auth {
|
||||
|
||||
class data_resource {
|
||||
private:
|
||||
enum class level {
|
||||
ROOT, KEYSPACE, COLUMN_FAMILY
|
||||
};
|
||||
|
||||
static const sstring ROOT_NAME;
|
||||
|
||||
level _level;
|
||||
sstring _ks;
|
||||
sstring _cf;
|
||||
|
||||
data_resource(level, const sstring& ks = {}, const sstring& cf = {});
|
||||
|
||||
level get_level() const;
|
||||
public:
|
||||
/**
|
||||
* Creates a DataResource representing the root-level resource.
|
||||
* @return the root-level resource.
|
||||
*/
|
||||
data_resource();
|
||||
/**
|
||||
* Creates a DataResource representing a keyspace.
|
||||
*
|
||||
* @param keyspace Name of the keyspace.
|
||||
*/
|
||||
data_resource(const sstring& ks);
|
||||
/**
|
||||
* Creates a DataResource instance representing a column family.
|
||||
*
|
||||
* @param keyspace Name of the keyspace.
|
||||
* @param columnFamily Name of the column family.
|
||||
*/
|
||||
data_resource(const sstring& ks, const sstring& cf);
|
||||
|
||||
/**
|
||||
* Parses a data resource name into a DataResource instance.
|
||||
*
|
||||
* @param name Name of the data resource.
|
||||
* @return DataResource instance matching the name.
|
||||
*/
|
||||
static data_resource from_name(const sstring&);
|
||||
|
||||
/**
|
||||
* @return Printable name of the resource.
|
||||
*/
|
||||
sstring name() const;
|
||||
|
||||
/**
|
||||
* @return Parent of the resource, if any. Throws IllegalStateException if it's the root-level resource.
|
||||
*/
|
||||
data_resource get_parent() const;
|
||||
|
||||
bool is_root_level() const {
|
||||
return get_level() == level::ROOT;
|
||||
}
|
||||
|
||||
bool is_keyspace_level() const {
|
||||
return get_level() == level::KEYSPACE;
|
||||
}
|
||||
|
||||
bool is_column_family_level() const {
|
||||
return get_level() == level::COLUMN_FAMILY;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return keyspace of the resource.
|
||||
* @throws std::invalid_argument if it's the root-level resource.
|
||||
*/
|
||||
const sstring& keyspace() const;
|
||||
|
||||
/**
|
||||
* @return column family of the resource.
|
||||
* @throws std::invalid_argument if it's not a cf-level resource.
|
||||
*/
|
||||
const sstring& column_family() const;
|
||||
|
||||
/**
|
||||
* @return Whether or not the resource has a parent in the hierarchy.
|
||||
*/
|
||||
bool has_parent() const;
|
||||
|
||||
/**
|
||||
* @return Whether or not the resource exists in scylla.
|
||||
*/
|
||||
bool exists() const;
|
||||
|
||||
sstring to_string() const;
|
||||
|
||||
bool operator==(const data_resource&) const;
|
||||
bool operator<(const data_resource&) const;
|
||||
|
||||
size_t hash_value() const {
|
||||
return utils::tuple_hash()(_ks, _cf);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Resource id mappings, i.e. keyspace and/or column families.
|
||||
*/
|
||||
using resource_ids = std::set<data_resource>;
|
||||
|
||||
std::ostream& operator<<(std::ostream&, const data_resource&);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -106,7 +106,7 @@ future<> auth::default_authorizer::stop() {
|
||||
}
|
||||
|
||||
future<auth::permission_set> auth::default_authorizer::authorize(
|
||||
service& ser, ::shared_ptr<authenticated_user> user, data_resource resource) const {
|
||||
service& ser, ::shared_ptr<authenticated_user> user, resource resource) const {
|
||||
return auth::is_super_user(ser, *user).then([this, user, resource = std::move(resource)](bool is_super) {
|
||||
if (is_super) {
|
||||
return make_ready_future<permission_set>(permissions::ALL);
|
||||
@@ -139,7 +139,7 @@ future<auth::permission_set> auth::default_authorizer::authorize(
|
||||
|
||||
future<> auth::default_authorizer::modify(
|
||||
::shared_ptr<authenticated_user> performer, permission_set set,
|
||||
data_resource resource, sstring user, sstring op) {
|
||||
resource resource, sstring user, sstring op) {
|
||||
// TODO: why does this not check super user?
|
||||
auto query = sprint("UPDATE %s.%s SET %s = %s %s ? WHERE %s = ? AND %s = ?",
|
||||
meta::AUTH_KS, PERMISSIONS_CF, PERMISSIONS_NAME,
|
||||
@@ -151,19 +151,19 @@ future<> auth::default_authorizer::modify(
|
||||
|
||||
future<> auth::default_authorizer::grant(
|
||||
::shared_ptr<authenticated_user> performer, permission_set set,
|
||||
data_resource resource, sstring to) {
|
||||
resource resource, sstring to) {
|
||||
return modify(std::move(performer), std::move(set), std::move(resource), std::move(to), "+");
|
||||
}
|
||||
|
||||
future<> auth::default_authorizer::revoke(
|
||||
::shared_ptr<authenticated_user> performer, permission_set set,
|
||||
data_resource resource, sstring from) {
|
||||
resource resource, sstring from) {
|
||||
return modify(std::move(performer), std::move(set), std::move(resource), std::move(from), "-");
|
||||
}
|
||||
|
||||
future<std::vector<auth::permission_details>> auth::default_authorizer::list(
|
||||
service& ser, ::shared_ptr<authenticated_user> performer, permission_set set,
|
||||
optional<data_resource> resource, optional<sstring> user) const {
|
||||
optional<resource> resource, optional<sstring> user) const {
|
||||
return auth::is_super_user(ser, *performer).then([this, performer, set = std::move(set), resource = std::move(resource), user = std::move(user)](bool is_super) {
|
||||
if (!is_super && (!user || performer->name() != *user)) {
|
||||
throw exceptions::unauthorized_exception(sprint("You are not authorized to view %s's permissions", user ? *user : "everyone"));
|
||||
@@ -194,7 +194,7 @@ future<std::vector<auth::permission_details>> auth::default_authorizer::list(
|
||||
for (auto& row : *res) {
|
||||
if (row.has(PERMISSIONS_NAME)) {
|
||||
auto username = row.get_as<sstring>(USER_NAME);
|
||||
auto resource = data_resource::from_name(row.get_as<sstring>(RESOURCE_NAME));
|
||||
auto resource = resource::from_name(row.get_as<sstring>(RESOURCE_NAME));
|
||||
auto ps = permissions::from_strings(row.get_set<sstring>(PERMISSIONS_NAME));
|
||||
ps = permission_set::from_mask(ps.mask() & set.mask());
|
||||
|
||||
@@ -219,7 +219,7 @@ future<> auth::default_authorizer::revoke_all(sstring dropped_user) {
|
||||
});
|
||||
}
|
||||
|
||||
future<> auth::default_authorizer::revoke_all(data_resource resource) {
|
||||
future<> auth::default_authorizer::revoke_all(resource resource) {
|
||||
auto query = sprint("SELECT %s FROM %s.%s WHERE %s = ? ALLOW FILTERING",
|
||||
USER_NAME, meta::AUTH_KS, PERMISSIONS_CF, RESOURCE_NAME);
|
||||
return _qp.process(query, db::consistency_level::LOCAL_ONE, { resource.name() })
|
||||
@@ -246,10 +246,9 @@ future<> auth::default_authorizer::revoke_all(data_resource resource) {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
const auth::resource_ids& auth::default_authorizer::protected_resources() {
|
||||
static const resource_ids ids({ data_resource(meta::AUTH_KS, PERMISSIONS_CF) });
|
||||
return ids;
|
||||
const auth::resource_set& auth::default_authorizer::protected_resources() {
|
||||
static const resource_set resources({ resource::data(meta::AUTH_KS, PERMISSIONS_CF) });
|
||||
return resources;
|
||||
}
|
||||
|
||||
future<> auth::default_authorizer::validate_configuration() const {
|
||||
|
||||
@@ -68,24 +68,24 @@ public:
|
||||
return default_authorizer_name();
|
||||
}
|
||||
|
||||
future<permission_set> authorize(service&, ::shared_ptr<authenticated_user>, data_resource) const override;
|
||||
future<permission_set> authorize(service&, ::shared_ptr<authenticated_user>, resource) const override;
|
||||
|
||||
future<> grant(::shared_ptr<authenticated_user>, permission_set, data_resource, sstring) override;
|
||||
future<> grant(::shared_ptr<authenticated_user>, permission_set, resource, sstring) override;
|
||||
|
||||
future<> revoke(::shared_ptr<authenticated_user>, permission_set, data_resource, sstring) override;
|
||||
future<> revoke(::shared_ptr<authenticated_user>, permission_set, resource, sstring) override;
|
||||
|
||||
future<std::vector<permission_details>> list(service&, ::shared_ptr<authenticated_user>, permission_set, optional<data_resource>, optional<sstring>) const override;
|
||||
future<std::vector<permission_details>> list(service&, ::shared_ptr<authenticated_user>, permission_set, optional<resource>, optional<sstring>) const override;
|
||||
|
||||
future<> revoke_all(sstring) override;
|
||||
|
||||
future<> revoke_all(data_resource) override;
|
||||
future<> revoke_all(resource) override;
|
||||
|
||||
const resource_ids& protected_resources() override;
|
||||
const resource_set& protected_resources() override;
|
||||
|
||||
future<> validate_configuration() const override;
|
||||
|
||||
private:
|
||||
future<> modify(::shared_ptr<authenticated_user>, permission_set, data_resource, sstring, sstring);
|
||||
future<> modify(::shared_ptr<authenticated_user>, permission_set, resource, sstring, sstring);
|
||||
};
|
||||
|
||||
} /* namespace auth */
|
||||
|
||||
@@ -294,9 +294,9 @@ future<> auth::password_authenticator::drop(sstring username) {
|
||||
}
|
||||
}
|
||||
|
||||
const auth::resource_ids& auth::password_authenticator::protected_resources() const {
|
||||
static const resource_ids ids({ data_resource(meta::AUTH_KS, CREDENTIALS_CF) });
|
||||
return ids;
|
||||
const auth::resource_set& auth::password_authenticator::protected_resources() const {
|
||||
static const resource_set resources({ resource::data(meta::AUTH_KS, CREDENTIALS_CF) });
|
||||
return resources;
|
||||
}
|
||||
|
||||
::shared_ptr<auth::authenticator::sasl_challenge> auth::password_authenticator::new_sasl_challenge() const {
|
||||
|
||||
@@ -76,7 +76,7 @@ public:
|
||||
future<> create(sstring username, const option_map& options) override;
|
||||
future<> alter(sstring username, const option_map& options) override;
|
||||
future<> drop(sstring username) override;
|
||||
const resource_ids& protected_resources() const override;
|
||||
const resource_set& protected_resources() const override;
|
||||
::shared_ptr<sasl_challenge> new_sasl_challenge() const override;
|
||||
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ permissions_cache::permissions_cache(const permissions_cache_config& c, service&
|
||||
}) {
|
||||
}
|
||||
|
||||
future<permission_set> permissions_cache::get(::shared_ptr<authenticated_user> user, data_resource r) {
|
||||
future<permission_set> permissions_cache::get(::shared_ptr<authenticated_user> user, resource r) {
|
||||
return _cache.get(key_type(*user, r));
|
||||
}
|
||||
|
||||
|
||||
@@ -30,20 +30,14 @@
|
||||
#include <seastar/core/shared_ptr.hh>
|
||||
|
||||
#include "auth/authenticated_user.hh"
|
||||
#include "auth/data_resource.hh"
|
||||
#include "auth/permission.hh"
|
||||
#include "auth/resource.hh"
|
||||
#include "log.hh"
|
||||
#include "utils/hash.hh"
|
||||
#include "utils/loading_cache.hh"
|
||||
|
||||
namespace std {
|
||||
|
||||
template <>
|
||||
struct hash<auth::data_resource> final {
|
||||
size_t operator()(const auth::data_resource & v) const {
|
||||
return v.hash_value();
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct hash<auth::authenticated_user> final {
|
||||
size_t operator()(const auth::authenticated_user & v) const {
|
||||
@@ -51,8 +45,8 @@ struct hash<auth::authenticated_user> final {
|
||||
}
|
||||
};
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& os, const std::pair<auth::authenticated_user, auth::data_resource>& p) {
|
||||
os << "{user: " << p.first.name() << ", data_resource: " << p.second << "}";
|
||||
inline std::ostream& operator<<(std::ostream& os, const std::pair<auth::authenticated_user, auth::resource>& p) {
|
||||
os << "{user: " << p.first.name() << ", resource: " << p.second << "}";
|
||||
return os;
|
||||
}
|
||||
|
||||
@@ -76,7 +70,7 @@ struct permissions_cache_config final {
|
||||
|
||||
class permissions_cache final {
|
||||
using cache_type = utils::loading_cache<
|
||||
std::pair<authenticated_user, data_resource>,
|
||||
std::pair<authenticated_user, resource>,
|
||||
permission_set,
|
||||
utils::loading_cache_reload_enabled::yes,
|
||||
utils::simple_entry_size<permission_set>,
|
||||
@@ -97,7 +91,7 @@ public:
|
||||
return _cache.stop();
|
||||
}
|
||||
|
||||
future<permission_set> get(::shared_ptr<authenticated_user>, data_resource);
|
||||
future<permission_set> get(::shared_ptr<authenticated_user>, resource);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
212
auth/resource.cc
Normal file
212
auth/resource.cc
Normal file
@@ -0,0 +1,212 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2016 ScyllaDB
|
||||
*
|
||||
* Modified by ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is part of Scylla.
|
||||
*
|
||||
* Scylla is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Scylla is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Scylla. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "auth/resource.hh"
|
||||
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <boost/algorithm/string/join.hpp>
|
||||
#include <boost/algorithm/string/split.hpp>
|
||||
|
||||
#include "service/storage_proxy.hh"
|
||||
|
||||
namespace auth {
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, resource_kind kind) {
|
||||
switch (kind) {
|
||||
case resource_kind::data: os << "data"; break;
|
||||
}
|
||||
|
||||
return os;
|
||||
}
|
||||
|
||||
static const std::unordered_map<resource_kind, stdx::string_view> roots{
|
||||
{resource_kind::data, "data"},
|
||||
};
|
||||
|
||||
static const std::unordered_map<resource_kind, std::size_t> max_parts{
|
||||
{resource_kind::data, 2},
|
||||
};
|
||||
|
||||
resource::resource(resource_kind kind) : _kind(kind), _parts{sstring(roots.at(kind))} {
|
||||
}
|
||||
|
||||
resource::resource(resource_kind kind, std::vector<sstring> parts) : resource(kind) {
|
||||
_parts.reserve(parts.size() + 1);
|
||||
_parts.insert(_parts.end(), std::make_move_iterator(parts.begin()), std::make_move_iterator(parts.end()));
|
||||
}
|
||||
|
||||
resource resource::data(stdx::string_view keyspace) {
|
||||
return resource(resource_kind::data, std::vector<sstring>{sstring(keyspace)});
|
||||
}
|
||||
|
||||
resource resource::data(stdx::string_view keyspace, stdx::string_view table) {
|
||||
return resource(resource_kind::data, std::vector<sstring>{sstring(keyspace), sstring(table)});
|
||||
}
|
||||
|
||||
resource resource::from_name(stdx::string_view name) {
|
||||
static const std::unordered_map<sstring, resource_kind> reverse_roots = [] {
|
||||
std::unordered_map<sstring, resource_kind> result;
|
||||
|
||||
for (const auto& pair : roots) {
|
||||
result.emplace(pair.second, pair.first);
|
||||
}
|
||||
|
||||
return result;
|
||||
}();
|
||||
|
||||
std::vector<sstring> parts;
|
||||
boost::split(parts, name, [](char ch) { return ch == '/'; });
|
||||
|
||||
if (parts.empty()) {
|
||||
throw invalid_resource_name(name);
|
||||
}
|
||||
|
||||
const auto iter = reverse_roots.find(parts[0]);
|
||||
if (iter == reverse_roots.end()) {
|
||||
throw invalid_resource_name(name);
|
||||
}
|
||||
|
||||
const auto kind = iter->second;
|
||||
parts.erase(parts.begin());
|
||||
|
||||
if (parts.size() > max_parts.at(kind)) {
|
||||
throw invalid_resource_name(name);
|
||||
}
|
||||
|
||||
return resource(kind, std::move(parts));
|
||||
}
|
||||
|
||||
resource resource::root_of(resource_kind kind) {
|
||||
return resource(kind);
|
||||
}
|
||||
|
||||
sstring resource::name() const {
|
||||
return boost::algorithm::join(_parts, "/");
|
||||
}
|
||||
|
||||
stdx::optional<resource> resource::parent() const {
|
||||
if (_parts.size() == 1) {
|
||||
return {};
|
||||
}
|
||||
|
||||
resource copy = *this;
|
||||
copy._parts.pop_back();
|
||||
return copy;
|
||||
}
|
||||
|
||||
bool operator<(const resource& r1, const resource& r2) {
|
||||
if (r1._kind != r2._kind) {
|
||||
return r1._kind < r2._kind;
|
||||
}
|
||||
|
||||
return std::lexicographical_compare(
|
||||
r1._parts.cbegin() + 1,
|
||||
r1._parts.cend(),
|
||||
r2._parts.cbegin() + 1,
|
||||
r2._parts.cend());
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const resource& r) {
|
||||
switch (r.kind()) {
|
||||
case resource_kind::data: return os << data_resource_view(r);
|
||||
}
|
||||
|
||||
return os;
|
||||
}
|
||||
|
||||
data_resource_view::data_resource_view(const resource& r) : _resource(r) {
|
||||
if (r._kind != resource_kind::data) {
|
||||
throw resource_kind_mismatch(resource_kind::data, r._kind);
|
||||
}
|
||||
}
|
||||
|
||||
stdx::optional<stdx::string_view> data_resource_view::keyspace() const {
|
||||
if (_resource._parts.size() == 1) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return _resource._parts[1];
|
||||
}
|
||||
|
||||
stdx::optional<stdx::string_view> data_resource_view::table() const {
|
||||
if (_resource._parts.size() <= 2) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return _resource._parts[2];
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const data_resource_view& v) {
|
||||
const auto keyspace = v.keyspace();
|
||||
const auto table = v.table();
|
||||
|
||||
if (!keyspace) {
|
||||
os << "<all keyspaces>";
|
||||
} else if (!table) {
|
||||
os << "<keyspace " << *keyspace << '>';
|
||||
} else {
|
||||
os << "<table " << *keyspace << '.' << *table << '>';
|
||||
}
|
||||
|
||||
return os;
|
||||
}
|
||||
|
||||
bool resource_exists(const data_resource_view& v) {
|
||||
// TODO(jhaberku) This dependency on global data is a remnant from the previous version, and needs to be fixed in a
|
||||
// dedicated patch.
|
||||
auto& local_db = service::get_local_storage_proxy().get_db().local();
|
||||
|
||||
const auto keyspace = v.keyspace();
|
||||
const auto table = v.table();
|
||||
|
||||
if (table) {
|
||||
return local_db.has_schema(sstring(*keyspace), sstring(*table));
|
||||
} else if (keyspace) {
|
||||
return local_db.has_keyspace(sstring(*keyspace));
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
199
auth/resource.hh
Normal file
199
auth/resource.hh
Normal file
@@ -0,0 +1,199 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2016 ScyllaDB
|
||||
*
|
||||
* Modified by ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is part of Scylla.
|
||||
*
|
||||
* Scylla is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Scylla is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Scylla. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <experimental/optional>
|
||||
#include <experimental/string_view>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
#include <unordered_set>
|
||||
|
||||
#include <seastar/core/print.hh>
|
||||
#include <seastar/core/sstring.hh>
|
||||
|
||||
#include "seastarx.hh"
|
||||
#include "stdx.hh"
|
||||
#include "utils/hash.hh"
|
||||
|
||||
namespace auth {
|
||||
|
||||
//
|
||||
// Resources are entities that users can be granted permissions on.
|
||||
//
|
||||
// Currently, the only known resources are keyspaces and tables. However, we shortly anticipate other kinds of resources
|
||||
// like roles.
|
||||
//
|
||||
// When they are stored as system metadata, resources have the form `root/part_0/part_1/.../part_n`.
|
||||
// Each kind of resource has a specific root prefix, followed by a maximum of `n` parts (where `n` is distinct for each
|
||||
// kind of resource as well). In this code, this form is called the "name".
|
||||
//
|
||||
// Since all resources have this same structure, all the different kinds are stored in instances of the same class:
|
||||
// `resource`. When we wish to query a resource for kind-specific data (like the table of a `data` resource), we create
|
||||
// a kind-specific "view" of the resource.
|
||||
//
|
||||
|
||||
class invalid_resource_name : public std::invalid_argument {
|
||||
std::shared_ptr<sstring> _name;
|
||||
|
||||
public:
|
||||
explicit invalid_resource_name(stdx::string_view name)
|
||||
: std::invalid_argument(sprint("The resource name '%s' is invalid.", name))
|
||||
, _name(std::make_shared<sstring>(name)) {
|
||||
}
|
||||
|
||||
stdx::string_view name() const noexcept {
|
||||
return *_name;
|
||||
}
|
||||
};
|
||||
|
||||
enum class resource_kind {
|
||||
data
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream&, resource_kind);
|
||||
|
||||
class resource final {
|
||||
resource_kind _kind;
|
||||
|
||||
std::vector<sstring> _parts;
|
||||
|
||||
public:
|
||||
static resource root_of(resource_kind);
|
||||
|
||||
static resource data(stdx::string_view keyspace);
|
||||
static resource data(stdx::string_view keyspace, stdx::string_view table);
|
||||
|
||||
// Throws `invalid_resource_name` when the name is malformed.
|
||||
static resource from_name(stdx::string_view);
|
||||
|
||||
resource_kind kind() const noexcept {
|
||||
return _kind;
|
||||
}
|
||||
|
||||
// A machine-friendly identifier unique to each resource.
|
||||
sstring name() const;
|
||||
|
||||
stdx::optional<resource> parent() const;
|
||||
|
||||
private:
|
||||
// A root resource.
|
||||
explicit resource(resource_kind kind);
|
||||
|
||||
resource(resource_kind, std::vector<sstring> parts);
|
||||
|
||||
friend class std::hash<resource>;
|
||||
friend class data_resource_view;
|
||||
|
||||
friend bool operator<(const resource&, const resource&);
|
||||
friend bool operator==(const resource&, const resource&);
|
||||
};
|
||||
|
||||
bool operator<(const resource&, const resource&);
|
||||
|
||||
inline bool operator==(const resource& r1, const resource& r2) {
|
||||
return (r1._kind == r2._kind) && (r1._parts == r2._parts);
|
||||
}
|
||||
|
||||
inline bool operator!=(const resource& r1, const resource& r2) {
|
||||
return !(r1 == r2);
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream&, const resource&);
|
||||
|
||||
class resource_kind_mismatch : public std::invalid_argument {
|
||||
public:
|
||||
explicit resource_kind_mismatch(resource_kind expected, resource_kind actual)
|
||||
: std::invalid_argument(
|
||||
sprint("This resource has kind '%s', but was expected to have kind '%s'.", actual, expected)) {
|
||||
}
|
||||
};
|
||||
|
||||
// A `data` view of `resource`.
|
||||
//
|
||||
// If neither `keyspace` nor `table` is present, this is the root resource.
|
||||
class data_resource_view final {
|
||||
const resource& _resource;
|
||||
|
||||
public:
|
||||
// Throws `resource_kind_mismatch` if the argument is not a `data` resource.
|
||||
explicit data_resource_view(const resource& r);
|
||||
|
||||
stdx::optional<stdx::string_view> keyspace() const;
|
||||
|
||||
stdx::optional<stdx::string_view> table() const;
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream&, const data_resource_view&);
|
||||
|
||||
bool resource_exists(const data_resource_view&);
|
||||
|
||||
}
|
||||
|
||||
namespace std {
|
||||
|
||||
template <>
|
||||
struct hash<auth::resource> {
|
||||
static size_t hash_data(const auth::data_resource_view& dv) {
|
||||
return utils::tuple_hash()(std::make_tuple(auth::resource_kind::data, dv.keyspace(), dv.table()));
|
||||
}
|
||||
|
||||
size_t operator()(const auth::resource& r) const {
|
||||
std::size_t value;
|
||||
|
||||
switch (r._kind) {
|
||||
case auth::resource_kind::data: value = hash_data(auth::data_resource_view(r)); break;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace auth {
|
||||
|
||||
using resource_set = std::unordered_set<resource>;
|
||||
|
||||
}
|
||||
@@ -31,7 +31,7 @@
|
||||
#include <seastar/core/print.hh>
|
||||
#include <seastar/core/sstring.hh>
|
||||
|
||||
#include "auth/data_resource.hh"
|
||||
#include "auth/resource.hh"
|
||||
#include "seastarx.hh"
|
||||
#include "stdx.hh"
|
||||
|
||||
|
||||
@@ -73,11 +73,11 @@ private:
|
||||
void on_update_view(const sstring& ks_name, const sstring& view_name, bool columns_changed) override {}
|
||||
|
||||
void on_drop_keyspace(const sstring& ks_name) override {
|
||||
_authorizer.revoke_all(auth::data_resource(ks_name));
|
||||
_authorizer.revoke_all(auth::resource::data(ks_name));
|
||||
}
|
||||
|
||||
void on_drop_column_family(const sstring& ks_name, const sstring& cf_name) override {
|
||||
_authorizer.revoke_all(auth::data_resource(ks_name, cf_name));
|
||||
_authorizer.revoke_all(auth::resource::data(ks_name, cf_name));
|
||||
}
|
||||
|
||||
void on_drop_user_type(const sstring& ks_name, const sstring& type_name) override {}
|
||||
@@ -343,7 +343,7 @@ future<> service::delete_user(const sstring& name) {
|
||||
{ name }).discard_result();
|
||||
}
|
||||
|
||||
future<permission_set> service::get_permissions(::shared_ptr<authenticated_user> u, data_resource r) const {
|
||||
future<permission_set> service::get_permissions(::shared_ptr<authenticated_user> u, resource r) const {
|
||||
return sharded_permissions_cache.local().get(std::move(u), std::move(r));
|
||||
}
|
||||
|
||||
|
||||
@@ -105,7 +105,7 @@ public:
|
||||
|
||||
future<> delete_user(const sstring& name);
|
||||
|
||||
future<permission_set> get_permissions(::shared_ptr<authenticated_user>, data_resource) const;
|
||||
future<permission_set> get_permissions(::shared_ptr<authenticated_user>, resource) const;
|
||||
|
||||
authenticator& underlying_authenticator() {
|
||||
return *_authenticator;
|
||||
|
||||
@@ -120,7 +120,7 @@ public:
|
||||
future<> drop(sstring username) override {
|
||||
return _authenticator->drop(username);
|
||||
}
|
||||
const resource_ids& protected_resources() const override {
|
||||
const resource_set& protected_resources() const override {
|
||||
return _authenticator->protected_resources();
|
||||
}
|
||||
::shared_ptr<sasl_challenge> new_sasl_challenge() const override {
|
||||
@@ -171,7 +171,7 @@ public:
|
||||
const sstring& qualified_java_name() const override {
|
||||
return transitional_authorizer_name();
|
||||
}
|
||||
future<permission_set> authorize(service& ser, ::shared_ptr<authenticated_user> user, data_resource resource) const override {
|
||||
future<permission_set> authorize(service& ser, ::shared_ptr<authenticated_user> user, resource resource) const override {
|
||||
return is_super_user(ser, *user).then([](bool s) {
|
||||
static const permission_set transitional_permissions =
|
||||
permission_set::of<permission::CREATE,
|
||||
@@ -181,22 +181,22 @@ public:
|
||||
return make_ready_future<permission_set>(s ? permissions::ALL : transitional_permissions);
|
||||
});
|
||||
}
|
||||
future<> grant(::shared_ptr<authenticated_user> user, permission_set ps, data_resource r, sstring s) override {
|
||||
future<> grant(::shared_ptr<authenticated_user> user, permission_set ps, resource r, sstring s) override {
|
||||
return _authorizer->grant(std::move(user), std::move(ps), std::move(r), std::move(s));
|
||||
}
|
||||
future<> revoke(::shared_ptr<authenticated_user> user, permission_set ps, data_resource r, sstring s) override {
|
||||
future<> revoke(::shared_ptr<authenticated_user> user, permission_set ps, resource r, sstring s) override {
|
||||
return _authorizer->revoke(std::move(user), std::move(ps), std::move(r), std::move(s));
|
||||
}
|
||||
future<std::vector<permission_details>> list(service& ser, ::shared_ptr<authenticated_user> user, permission_set ps, optional<data_resource> r, optional<sstring> s) const override {
|
||||
future<std::vector<permission_details>> list(service& ser, ::shared_ptr<authenticated_user> user, permission_set ps, optional<resource> r, optional<sstring> s) const override {
|
||||
return _authorizer->list(ser, std::move(user), std::move(ps), std::move(r), std::move(s));
|
||||
}
|
||||
future<> revoke_all(sstring s) override {
|
||||
return _authorizer->revoke_all(std::move(s));
|
||||
}
|
||||
future<> revoke_all(data_resource r) override {
|
||||
future<> revoke_all(resource r) override {
|
||||
return _authorizer->revoke_all(std::move(r));
|
||||
}
|
||||
const resource_ids& protected_resources() override {
|
||||
const resource_set& protected_resources() override {
|
||||
return _authorizer->protected_resources();
|
||||
}
|
||||
future<> validate_configuration() const override {
|
||||
|
||||
@@ -252,6 +252,7 @@ scylla_tests = [
|
||||
'tests/aggregate_fcts_test',
|
||||
'tests/role_manager_test',
|
||||
'tests/caching_options_test',
|
||||
'tests/auth_resource_test',
|
||||
]
|
||||
|
||||
apps = [
|
||||
@@ -534,7 +535,7 @@ scylla_core = (['database.cc',
|
||||
'auth/authenticator.cc',
|
||||
'auth/common.cc',
|
||||
'auth/default_authorizer.cc',
|
||||
'auth/data_resource.cc',
|
||||
'auth/resource.cc',
|
||||
'auth/password_authenticator.cc',
|
||||
'auth/permission.cc',
|
||||
'auth/permissions_cache.cc',
|
||||
@@ -652,6 +653,7 @@ pure_boost_tests = set([
|
||||
'tests/chunked_vector_test',
|
||||
'tests/big_decimal_test',
|
||||
'tests/caching_options_test',
|
||||
'tests/auth_resource_test',
|
||||
])
|
||||
|
||||
tests_not_using_seastar_test_framework = set([
|
||||
|
||||
12
cql3/Cql.g
12
cql3/Cql.g
@@ -1022,7 +1022,7 @@ revokeRoleStatement returns [::shared_ptr<revoke_role_statement> stmt]
|
||||
|
||||
listPermissionsStatement returns [::shared_ptr<list_permissions_statement> stmt]
|
||||
@init {
|
||||
std::experimental::optional<auth::data_resource> r;
|
||||
std::experimental::optional<auth::resource> r;
|
||||
std::experimental::optional<sstring> u;
|
||||
bool recursive = true;
|
||||
}
|
||||
@@ -1044,15 +1044,15 @@ permissionOrAll returns [auth::permission_set perms]
|
||||
| p=permission ( K_PERMISSION )? { $perms = auth::permission_set::from_mask(auth::permission_set::mask_for($p.perm)); }
|
||||
;
|
||||
|
||||
resource returns [auth::data_resource res]
|
||||
resource returns [uninitialized<auth::resource> res]
|
||||
: r=dataResource { $res = $r.res; }
|
||||
;
|
||||
|
||||
dataResource returns [auth::data_resource res]
|
||||
: K_ALL K_KEYSPACES { $res = auth::data_resource(); }
|
||||
| K_KEYSPACE ks = keyspaceName { $res = auth::data_resource($ks.id); }
|
||||
dataResource returns [uninitialized<auth::resource> res]
|
||||
: K_ALL K_KEYSPACES { $res = auth::resource::root_of(auth::resource_kind::data); }
|
||||
| K_KEYSPACE ks = keyspaceName { $res = auth::resource::data($ks.id); }
|
||||
| ( K_COLUMNFAMILY )? cf = columnFamilyName
|
||||
{ $res = auth::data_resource($cf.name->get_keyspace(), $cf.name->get_column_family()); }
|
||||
{ $res = auth::resource::data($cf.name->get_keyspace(), $cf.name->get_column_family()); }
|
||||
;
|
||||
|
||||
/**
|
||||
|
||||
@@ -82,9 +82,15 @@ future<::shared_ptr<cql_transport::messages::result_message>> cql3::statements::
|
||||
throw std::runtime_error("unsupported operation");
|
||||
}
|
||||
|
||||
void cql3::statements::authorization_statement::mayme_correct_resource(auth::data_resource& resource, const service::client_state& state) {
|
||||
if (resource.is_column_family_level() && resource.keyspace().empty()) {
|
||||
resource = auth::data_resource(state.get_keyspace(), resource.column_family());
|
||||
void cql3::statements::authorization_statement::maybe_correct_resource(auth::resource& resource, const service::client_state& state) {
|
||||
if (resource.kind() == auth::resource_kind::data) {
|
||||
const auto data_view = auth::data_resource_view(resource);
|
||||
const auto keyspace = data_view.keyspace();
|
||||
const auto table = data_view.table();
|
||||
|
||||
if (table && keyspace->empty()) {
|
||||
resource = auth::resource::data(state.get_keyspace(), *table);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
#include "transport/messages_fwd.hh"
|
||||
|
||||
namespace auth {
|
||||
class data_resource;
|
||||
class resource;
|
||||
}
|
||||
|
||||
namespace cql3 {
|
||||
@@ -74,7 +74,7 @@ public:
|
||||
execute_internal(distributed<service::storage_proxy>& proxy, service::query_state& state, const query_options& options) override;
|
||||
|
||||
protected:
|
||||
static void mayme_correct_resource(auth::data_resource&, const service::client_state&);
|
||||
static void maybe_correct_resource(auth::resource&, const service::client_state&);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
|
||||
cql3::statements::list_permissions_statement::list_permissions_statement(
|
||||
auth::permission_set permissions,
|
||||
std::experimental::optional<auth::data_resource> resource,
|
||||
std::experimental::optional<auth::resource> resource,
|
||||
std::experimental::optional<sstring> username, bool recursive)
|
||||
: _permissions(permissions), _resource(std::move(resource)), _username(
|
||||
std::move(username)), _recursive(recursive) {
|
||||
@@ -72,8 +72,10 @@ future<> cql3::statements::list_permissions_statement::check_access(const servic
|
||||
}
|
||||
return f.then([this, &state] {
|
||||
if (_resource) {
|
||||
mayme_correct_resource(*_resource, state);
|
||||
if (!_resource->exists()) {
|
||||
maybe_correct_resource(*_resource, state);
|
||||
|
||||
if ((_resource->kind() == auth::resource_kind::data)
|
||||
&& !auth::resource_exists(auth::data_resource_view(*_resource))) {
|
||||
throw exceptions::invalid_request_exception(sprint("%s doesn't exist", *_resource));
|
||||
}
|
||||
}
|
||||
@@ -90,17 +92,23 @@ cql3::statements::list_permissions_statement::execute(distributed<service::stora
|
||||
make_column("username"), make_column("resource"), make_column("permission")
|
||||
});
|
||||
|
||||
typedef std::experimental::optional<auth::data_resource> opt_resource;
|
||||
typedef std::experimental::optional<auth::resource> opt_resource;
|
||||
|
||||
std::vector<opt_resource> resources;
|
||||
|
||||
auto r = _resource;
|
||||
for (;;) {
|
||||
resources.emplace_back(r);
|
||||
if (!r || !r->has_parent() || !_recursive) {
|
||||
if (!r || !_recursive) {
|
||||
break;
|
||||
}
|
||||
r = r->get_parent();
|
||||
|
||||
auto parent = r->parent();
|
||||
if (!parent) {
|
||||
break;
|
||||
}
|
||||
|
||||
r = std::move(parent);
|
||||
}
|
||||
|
||||
return map_reduce(resources, [&state, this](opt_resource r) {
|
||||
@@ -121,7 +129,7 @@ cql3::statements::list_permissions_statement::execute(distributed<service::stora
|
||||
rs->add_row(
|
||||
std::vector<bytes_opt> { utf8_type->decompose(
|
||||
v.user), utf8_type->decompose(
|
||||
v.resource.to_string()),
|
||||
sstring(sprint("%s", v.resource))),
|
||||
utf8_type->decompose(p), });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
|
||||
#include "authorization_statement.hh"
|
||||
#include "auth/permission.hh"
|
||||
#include "auth/data_resource.hh"
|
||||
#include "auth/resource.hh"
|
||||
|
||||
namespace cql3 {
|
||||
|
||||
@@ -54,12 +54,12 @@ namespace statements {
|
||||
class list_permissions_statement : public authorization_statement {
|
||||
private:
|
||||
auth::permission_set _permissions;
|
||||
std::experimental::optional<auth::data_resource> _resource;
|
||||
std::experimental::optional<auth::resource> _resource;
|
||||
std::experimental::optional<sstring> _username;
|
||||
bool _recursive;
|
||||
|
||||
public:
|
||||
list_permissions_statement(auth::permission_set, std::experimental::optional<auth::data_resource>, std::experimental::optional<sstring>, bool);
|
||||
list_permissions_statement(auth::permission_set, std::experimental::optional<auth::resource>, std::experimental::optional<sstring>, bool);
|
||||
|
||||
void validate(distributed<service::storage_proxy>&, const service::client_state&) override;
|
||||
future<> check_access(const service::client_state&) override;
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
#include "cql3/selection/selection.hh"
|
||||
|
||||
cql3::statements::permission_altering_statement::permission_altering_statement(
|
||||
auth::permission_set permissions, auth::data_resource resource,
|
||||
auth::permission_set permissions, auth::resource resource,
|
||||
sstring username)
|
||||
: _permissions(permissions), _resource(std::move(resource)), _username(
|
||||
std::move(username)) {
|
||||
@@ -64,8 +64,10 @@ future<> cql3::statements::permission_altering_statement::check_access(const ser
|
||||
if (!exists) {
|
||||
throw exceptions::invalid_request_exception(sprint("User %s doesn't exist", _username));
|
||||
}
|
||||
mayme_correct_resource(_resource, state);
|
||||
if (!_resource.exists()) {
|
||||
maybe_correct_resource(_resource, state);
|
||||
|
||||
if ((_resource.kind() == auth::resource_kind::data)
|
||||
&& !auth::resource_exists(auth::data_resource_view(_resource))) {
|
||||
throw exceptions::invalid_request_exception(sprint("%s doesn't exist", _resource));
|
||||
}
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
|
||||
#include "authorization_statement.hh"
|
||||
#include "auth/permission.hh"
|
||||
#include "auth/data_resource.hh"
|
||||
#include "auth/resource.hh"
|
||||
|
||||
namespace cql3 {
|
||||
|
||||
@@ -52,11 +52,11 @@ namespace statements {
|
||||
class permission_altering_statement : public authorization_statement {
|
||||
protected:
|
||||
auth::permission_set _permissions;
|
||||
auth::data_resource _resource;
|
||||
auth::resource _resource;
|
||||
sstring _username;
|
||||
|
||||
public:
|
||||
permission_altering_statement(auth::permission_set, auth::data_resource, sstring);
|
||||
permission_altering_statement(auth::permission_set, auth::resource, sstring);
|
||||
|
||||
void validate(distributed<service::storage_proxy>&, const service::client_state&) override;
|
||||
future<> check_access(const service::client_state&) override;
|
||||
|
||||
@@ -235,12 +235,12 @@ future<::shared_ptr<cql_transport::messages::result_message>>
|
||||
list_roles_statement::execute(distributed<service::storage_proxy>&, service::query_state& state, const query_options&) {
|
||||
unimplemented::warn(unimplemented::cause::ROLES);
|
||||
|
||||
static const auth::data_resource virtual_table(auth::meta::AUTH_KS, "role");
|
||||
static const sstring virtual_table_name("roles");
|
||||
|
||||
static const auto make_column_spec = [](const sstring& name, const ::shared_ptr<const abstract_type>& ty) {
|
||||
return ::make_shared<column_specification>(
|
||||
virtual_table.keyspace(),
|
||||
virtual_table.column_family(),
|
||||
auth::meta::AUTH_KS,
|
||||
virtual_table_name,
|
||||
::make_shared<column_identifier>(name, true),
|
||||
ty);
|
||||
};
|
||||
|
||||
@@ -100,25 +100,25 @@ future<> service::client_state::has_all_keyspaces_access(
|
||||
return make_ready_future();
|
||||
}
|
||||
validate_login();
|
||||
return ensure_has_permission(p, auth::data_resource());
|
||||
return ensure_has_permission(p, auth::resource::root_of(auth::resource_kind::data));
|
||||
}
|
||||
|
||||
future<> service::client_state::has_keyspace_access(const sstring& ks,
|
||||
auth::permission p) const {
|
||||
return has_access(ks, p, auth::data_resource(ks));
|
||||
return has_access(ks, p, auth::resource::data(ks));
|
||||
}
|
||||
|
||||
future<> service::client_state::has_column_family_access(const sstring& ks,
|
||||
const sstring& cf, auth::permission p) const {
|
||||
validation::validate_column_family(ks, cf);
|
||||
return has_access(ks, p, auth::data_resource(ks, cf));
|
||||
return has_access(ks, p, auth::resource::data(ks, cf));
|
||||
}
|
||||
|
||||
future<> service::client_state::has_schema_access(const schema& s, auth::permission p) const {
|
||||
return has_access(s.ks_name(), p, auth::data_resource(s.ks_name(), s.cf_name()));
|
||||
return has_access(s.ks_name(), p, auth::resource::data(s.ks_name(), s.cf_name()));
|
||||
}
|
||||
|
||||
future<> service::client_state::has_access(const sstring& ks, auth::permission p, auth::data_resource resource) const {
|
||||
future<> service::client_state::has_access(const sstring& ks, auth::permission p, auth::resource resource) const {
|
||||
if (ks.empty()) {
|
||||
throw exceptions::invalid_request_exception("You have not set a keyspace for this session");
|
||||
}
|
||||
@@ -145,13 +145,13 @@ future<> service::client_state::has_access(const sstring& ks, auth::permission p
|
||||
}
|
||||
}
|
||||
|
||||
static thread_local std::set<auth::data_resource> readable_system_resources = [] {
|
||||
std::set<auth::data_resource> tmp;
|
||||
static thread_local std::set<auth::resource> readable_system_resources = [] {
|
||||
std::set<auth::resource> tmp;
|
||||
for (auto cf : { db::system_keyspace::LOCAL, db::system_keyspace::PEERS }) {
|
||||
tmp.emplace(db::system_keyspace::NAME, cf);
|
||||
tmp.insert(auth::resource::data(db::system_keyspace::NAME, cf));
|
||||
}
|
||||
for (auto cf : db::schema_tables::ALL) {
|
||||
tmp.emplace(db::schema_tables::NAME, cf);
|
||||
tmp.insert(auth::resource::data(db::schema_tables::NAME, cf));
|
||||
}
|
||||
return tmp;
|
||||
}();
|
||||
@@ -173,15 +173,12 @@ future<> service::client_state::has_access(const sstring& ks, auth::permission p
|
||||
return ensure_has_permission(p, std::move(resource));
|
||||
}
|
||||
|
||||
future<bool> service::client_state::check_has_permission(auth::permission p, auth::data_resource resource) const {
|
||||
future<bool> service::client_state::check_has_permission(auth::permission p, auth::resource resource) const {
|
||||
if (_is_internal) {
|
||||
return make_ready_future<bool>(true);
|
||||
}
|
||||
|
||||
std::experimental::optional<auth::data_resource> parent;
|
||||
if (resource.has_parent()) {
|
||||
parent = resource.get_parent();
|
||||
}
|
||||
std::experimental::optional<auth::resource> parent = resource.parent();
|
||||
|
||||
return _auth_service->get_permissions(_user, resource).then([this, p, parent = std::move(parent)](auth::permission_set set) {
|
||||
if (set.contains(p)) {
|
||||
@@ -194,7 +191,7 @@ future<bool> service::client_state::check_has_permission(auth::permission p, aut
|
||||
});
|
||||
}
|
||||
|
||||
future<> service::client_state::ensure_has_permission(auth::permission p, auth::data_resource resource) const {
|
||||
future<> service::client_state::ensure_has_permission(auth::permission p, auth::resource resource) const {
|
||||
return check_has_permission(p, resource).then([this, p, resource](bool ok) {
|
||||
if (!ok) {
|
||||
throw exceptions::unauthorized_exception(sprint("User %s has no %s permission on %s or any of its parents",
|
||||
|
||||
@@ -253,10 +253,10 @@ public:
|
||||
future<> has_schema_access(const schema& s, auth::permission p) const;
|
||||
|
||||
private:
|
||||
future<> has_access(const sstring&, auth::permission, auth::data_resource) const;
|
||||
future<bool> check_has_permission(auth::permission, auth::data_resource) const;
|
||||
future<> has_access(const sstring&, auth::permission, auth::resource) const;
|
||||
future<bool> check_has_permission(auth::permission, auth::resource) const;
|
||||
public:
|
||||
future<> ensure_has_permission(auth::permission, auth::data_resource) const;
|
||||
future<> ensure_has_permission(auth::permission, auth::resource) const;
|
||||
|
||||
void validate_login() const;
|
||||
void ensure_not_anonymous() const; // unauthorized_exception on error
|
||||
|
||||
1
test.py
1
test.py
@@ -95,6 +95,7 @@ boost_tests = [
|
||||
'aggregate_fcts_test',
|
||||
'role_manager_test',
|
||||
'caching_options_test',
|
||||
'auth_resource_test',
|
||||
]
|
||||
|
||||
other_tests = [
|
||||
|
||||
95
tests/auth_resource_test.cc
Normal file
95
tests/auth_resource_test.cc
Normal file
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright (C) 2017 ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is part of Scylla.
|
||||
*
|
||||
* Scylla is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Scylla is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Scylla. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#define BOOST_TEST_MODULE core
|
||||
|
||||
#include "auth/resource.hh"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
BOOST_AUTO_TEST_CASE(root_of) {
|
||||
const auto r = auth::resource::root_of(auth::resource_kind::data);
|
||||
BOOST_REQUIRE_EQUAL(r.kind(), auth::resource_kind::data);
|
||||
|
||||
const auto v = auth::data_resource_view(r);
|
||||
BOOST_REQUIRE(!v.keyspace());
|
||||
BOOST_REQUIRE(!v.table());
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(data) {
|
||||
const auto r1 = auth::resource::data("my_keyspace");
|
||||
BOOST_REQUIRE_EQUAL(r1.kind(), auth::resource_kind::data);
|
||||
const auto v1 = auth::data_resource_view(r1);
|
||||
|
||||
BOOST_REQUIRE_EQUAL(*v1.keyspace(), "my_keyspace");
|
||||
BOOST_REQUIRE(!v1.table());
|
||||
|
||||
const auto r2 = auth::resource::data("my_keyspace", "my_table");
|
||||
BOOST_REQUIRE_EQUAL(r2.kind(), auth::resource_kind::data);
|
||||
const auto v2 = auth::data_resource_view(r2);
|
||||
|
||||
BOOST_REQUIRE_EQUAL(*v2.keyspace(), "my_keyspace");
|
||||
BOOST_REQUIRE_EQUAL(*v2.table(), "my_table");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(from_name) {
|
||||
const auto r1 = auth::resource::from_name("data");
|
||||
BOOST_REQUIRE_EQUAL(r1, auth::resource::root_of(auth::resource_kind::data));
|
||||
|
||||
const auto r2 = auth::resource::from_name("data/my_keyspace");
|
||||
BOOST_REQUIRE_EQUAL(r2, auth::resource::data("my_keyspace"));
|
||||
|
||||
const auto r3 = auth::resource::from_name("data/my_keyspace/my_table");
|
||||
BOOST_REQUIRE_EQUAL(r3, auth::resource::data("my_keyspace", "my_table"));
|
||||
|
||||
BOOST_REQUIRE_THROW(auth::resource::from_name("animal/horse"), auth::invalid_resource_name);
|
||||
BOOST_REQUIRE_THROW(auth::resource::from_name(""), auth::invalid_resource_name);
|
||||
BOOST_REQUIRE_THROW(auth::resource::from_name("data/foo/bar/baz"), auth::invalid_resource_name);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(name) {
|
||||
BOOST_REQUIRE_EQUAL(auth::resource::root_of(auth::resource_kind::data).name(), "data");
|
||||
BOOST_REQUIRE_EQUAL(auth::resource::data("my_keyspace").name(), "data/my_keyspace");
|
||||
BOOST_REQUIRE_EQUAL(auth::resource::data("my_keyspace", "my_table").name(), "data/my_keyspace/my_table");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(parent) {
|
||||
const auto r1 = auth::resource::data("my_keyspace", "my_table");
|
||||
|
||||
const auto r2 = r1.parent();
|
||||
BOOST_REQUIRE(r2);
|
||||
BOOST_REQUIRE_EQUAL(*r2, auth::resource::data("my_keyspace"));
|
||||
|
||||
const auto r3 = r2->parent();
|
||||
BOOST_REQUIRE(r3);
|
||||
BOOST_REQUIRE_EQUAL(*r3, auth::resource::root_of(auth::resource_kind::data));
|
||||
|
||||
const auto r4 = r3->parent();
|
||||
BOOST_REQUIRE(!r4);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(output) {
|
||||
BOOST_REQUIRE_EQUAL(sprint("%s", auth::resource::root_of(auth::resource_kind::data)), "<all keyspaces>");
|
||||
BOOST_REQUIRE_EQUAL(sprint("%s", auth::resource::data("my_keyspace")), "<keyspace my_keyspace>");
|
||||
BOOST_REQUIRE_EQUAL(sprint("%s", auth::resource::data("my_keyspace", "my_table")), "<table my_keyspace.my_table>");
|
||||
}
|
||||
@@ -35,39 +35,15 @@
|
||||
#include "tests/cql_assertions.hh"
|
||||
|
||||
#include "auth/allow_all_authenticator.hh"
|
||||
#include "auth/data_resource.hh"
|
||||
#include "auth/authenticator.hh"
|
||||
#include "auth/password_authenticator.hh"
|
||||
#include "auth/service.hh"
|
||||
#include "auth/authenticated_user.hh"
|
||||
#include "auth/resource.hh"
|
||||
|
||||
#include "db/config.hh"
|
||||
#include "cql3/query_processor.hh"
|
||||
|
||||
SEASTAR_TEST_CASE(test_data_resource) {
|
||||
auth::data_resource root, keyspace("fisk"), column_family("fisk", "notter");
|
||||
|
||||
BOOST_REQUIRE_EQUAL(root.is_root_level(), true);
|
||||
BOOST_REQUIRE_EQUAL(keyspace.is_keyspace_level(), true);
|
||||
BOOST_REQUIRE_EQUAL(column_family.is_column_family_level(), true);
|
||||
|
||||
BOOST_REQUIRE_EQUAL(root.has_parent(), false);
|
||||
BOOST_REQUIRE_EQUAL(keyspace.has_parent(), true);
|
||||
BOOST_REQUIRE_EQUAL(column_family.has_parent(), true);
|
||||
|
||||
try {
|
||||
root.get_parent();
|
||||
BOOST_FAIL("Should not reach");
|
||||
} catch (...) {
|
||||
// ok
|
||||
}
|
||||
|
||||
BOOST_REQUIRE_EQUAL(keyspace.get_parent(), root);
|
||||
BOOST_REQUIRE_EQUAL(column_family.get_parent(), keyspace);
|
||||
|
||||
return make_ready_future();
|
||||
}
|
||||
|
||||
SEASTAR_TEST_CASE(test_default_authenticator) {
|
||||
return do_with_cql_env([](cql_test_env& env) {
|
||||
auto& a = env.local_auth_service().underlying_authenticator();
|
||||
|
||||
Reference in New Issue
Block a user