mirror of
https://github.com/ceph/ceph
synced 2026-08-02 07:03:18 +00:00
rgw/policy: Add rgw-policy-test command line tool
Annotated evaluation of a policy. Signed-off-by: Adam C. Emerson <aemerson@redhat.com>
This commit is contained in:
parent
3a1686cf29
commit
5a176d3f83
@ -2620,8 +2620,10 @@ fi
|
|||||||
%{_bindir}/radosgw-es
|
%{_bindir}/radosgw-es
|
||||||
%{_bindir}/radosgw-object-expirer
|
%{_bindir}/radosgw-object-expirer
|
||||||
%{_bindir}/rgw-policy-check
|
%{_bindir}/rgw-policy-check
|
||||||
|
%{_bindir}/rgw-policy-test
|
||||||
%{_mandir}/man8/radosgw.8*
|
%{_mandir}/man8/radosgw.8*
|
||||||
%{_mandir}/man8/rgw-policy-check.8*
|
%{_mandir}/man8/rgw-policy-check.8*
|
||||||
|
%{_mandir}/man8/rgw-policy-test.8*
|
||||||
%dir %{_localstatedir}/lib/ceph/radosgw
|
%dir %{_localstatedir}/lib/ceph/radosgw
|
||||||
%{_unitdir}/ceph-radosgw@.service
|
%{_unitdir}/ceph-radosgw@.service
|
||||||
%{_unitdir}/ceph-radosgw.target
|
%{_unitdir}/ceph-radosgw.target
|
||||||
|
|||||||
2
debian/ceph-common.install
vendored
2
debian/ceph-common.install
vendored
@ -23,6 +23,7 @@ usr/bin/rgw-gap-list
|
|||||||
usr/bin/rgw-gap-list-comparator
|
usr/bin/rgw-gap-list-comparator
|
||||||
usr/bin/rgw-orphan-list
|
usr/bin/rgw-orphan-list
|
||||||
usr/bin/rgw-policy-check
|
usr/bin/rgw-policy-check
|
||||||
|
usr/bin/rgw-policy-test
|
||||||
usr/bin/rgw-restore-bucket-index
|
usr/bin/rgw-restore-bucket-index
|
||||||
usr/bin/rbd
|
usr/bin/rbd
|
||||||
usr/bin/rbdmap
|
usr/bin/rbdmap
|
||||||
@ -44,6 +45,7 @@ usr/share/man/man8/mount.ceph.8
|
|||||||
usr/share/man/man8/rados.8
|
usr/share/man/man8/rados.8
|
||||||
usr/share/man/man8/radosgw-admin.8
|
usr/share/man/man8/radosgw-admin.8
|
||||||
usr/share/man/man8/rgw-policy-check.8
|
usr/share/man/man8/rgw-policy-check.8
|
||||||
|
usr/share/man/man8/rgw-policy-test.8
|
||||||
usr/share/man/man8/rgw-gap-list.8
|
usr/share/man/man8/rgw-gap-list.8
|
||||||
usr/share/man/man8/rbd.8
|
usr/share/man/man8/rbd.8
|
||||||
usr/share/man/man8/rbdmap.8
|
usr/share/man/man8/rbdmap.8
|
||||||
|
|||||||
@ -61,6 +61,7 @@ if(WITH_RADOSGW)
|
|||||||
rgw-orphan-list.rst
|
rgw-orphan-list.rst
|
||||||
rgw-gap-list.rst
|
rgw-gap-list.rst
|
||||||
rgw-policy-check.rst
|
rgw-policy-check.rst
|
||||||
|
rgw-policy-test.rst
|
||||||
ceph-diff-sorted.rst
|
ceph-diff-sorted.rst
|
||||||
rgw-restore-bucket-index.rst)
|
rgw-restore-bucket-index.rst)
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
65
doc/man/8/rgw-policy-test.rst
Normal file
65
doc/man/8/rgw-policy-test.rst
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
:orphan:
|
||||||
|
|
||||||
|
===================================================
|
||||||
|
rgw-policy-test -- test evaluation of bucket policy
|
||||||
|
===================================================
|
||||||
|
|
||||||
|
.. program:: rgw-policy-test
|
||||||
|
|
||||||
|
Synopsis
|
||||||
|
========
|
||||||
|
|
||||||
|
| **rgw-policy-test**
|
||||||
|
[-e *key*=*value*]... [-t *tenant*] [-R resource] [-I identity] *action* *filename*
|
||||||
|
|
||||||
|
|
||||||
|
Description
|
||||||
|
===========
|
||||||
|
|
||||||
|
This utility reads a policy from a file or stdin and evaluates it
|
||||||
|
against the supplied identity, resource, action, and request context
|
||||||
|
(the key/value pairs evaluated by conditions.) The utility will print
|
||||||
|
a trace of evaluating the policy and the effect of its evaluation.
|
||||||
|
|
||||||
|
On error it will print a message and return non-zero.
|
||||||
|
|
||||||
|
Options
|
||||||
|
=======
|
||||||
|
|
||||||
|
.. option:: -t *tenant*, --tenant *tenant*
|
||||||
|
|
||||||
|
Specify *tenant* as the tenant. If not provided, the default
|
||||||
|
(empty) tenant is used.
|
||||||
|
|
||||||
|
.. option:: -e *key*=*value*, --environment *key*=*value*
|
||||||
|
|
||||||
|
Add (*key*, *value*) to the environment for evaluation. May be
|
||||||
|
specified multiple times.
|
||||||
|
|
||||||
|
.. option:: -I *SCHEMA:STRING*, --identity *SCHEMA:STRING*
|
||||||
|
|
||||||
|
Specify *identity* as the identity whose access is to be checked.
|
||||||
|
|
||||||
|
.. option:: -R *ARN*, --resource *ARN*
|
||||||
|
|
||||||
|
Specify *ARN* as the resource for which to check access.
|
||||||
|
|
||||||
|
.. option:: *action*
|
||||||
|
|
||||||
|
Use *action* as the action to check.
|
||||||
|
|
||||||
|
.. option:: *filename*
|
||||||
|
|
||||||
|
Read the policy from *filename*, or - for standard input.
|
||||||
|
|
||||||
|
Availability
|
||||||
|
============
|
||||||
|
|
||||||
|
**rgw-policy-test** is part of Ceph, a massively scalable, open-source,
|
||||||
|
distributed storage system. Please refer to the Ceph documentation at
|
||||||
|
https://docs.ceph.com/ for more information.
|
||||||
|
|
||||||
|
See also
|
||||||
|
========
|
||||||
|
|
||||||
|
:doc:`radosgw <radosgw>`\(8)
|
||||||
@ -48,4 +48,5 @@
|
|||||||
man/8/ceph-immutable-object-cache
|
man/8/ceph-immutable-object-cache
|
||||||
man/8/ceph-diff-sorted
|
man/8/ceph-diff-sorted
|
||||||
man/8/rgw-policy-check
|
man/8/rgw-policy-check
|
||||||
|
man/8/rgw-policy-test
|
||||||
man/8/rgw-restore-bucket-index
|
man/8/rgw-restore-bucket-index
|
||||||
|
|||||||
@ -668,6 +668,12 @@ add_executable(rgw-policy-check ${radosgw_polparser_srcs})
|
|||||||
target_link_libraries(rgw-policy-check ${rgw_libs})
|
target_link_libraries(rgw-policy-check ${rgw_libs})
|
||||||
install(TARGETS rgw-policy-check DESTINATION bin)
|
install(TARGETS rgw-policy-check DESTINATION bin)
|
||||||
|
|
||||||
|
set(radosgw_poltester_srcs
|
||||||
|
rgw_poltester.cc)
|
||||||
|
add_executable(rgw-policy-test ${radosgw_poltester_srcs})
|
||||||
|
target_link_libraries(rgw-policy-test ${rgw_libs})
|
||||||
|
install(TARGETS rgw-policy-test DESTINATION bin)
|
||||||
|
|
||||||
set(librgw_srcs
|
set(librgw_srcs
|
||||||
librgw.cc)
|
librgw.cc)
|
||||||
add_library(rgw SHARED ${librgw_srcs})
|
add_library(rgw SHARED ${librgw_srcs})
|
||||||
|
|||||||
@ -10,6 +10,7 @@
|
|||||||
#include <fmt/ranges.h>
|
#include <fmt/ranges.h>
|
||||||
#include <fmt/std.h>
|
#include <fmt/std.h>
|
||||||
|
|
||||||
|
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
#include <experimental/iterator>
|
#include <experimental/iterator>
|
||||||
@ -344,6 +345,79 @@ const Keyword top[1]{{"<Top>", TokenKind::pseudo, TokenID::Top, 0, false,
|
|||||||
const Keyword cond_key[1]{{"<Condition Key>", TokenKind::cond_key,
|
const Keyword cond_key[1]{{"<Condition Key>", TokenKind::cond_key,
|
||||||
TokenID::CondKey, 0, true, false}};
|
TokenID::CondKey, 0, true, false}};
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
boost::optional<Principal>
|
||||||
|
parse_principal_(const struct Keyword* w, std::string&& s,
|
||||||
|
string* errmsg) {
|
||||||
|
if ((w->id == TokenID::AWS) && (s == "*")) {
|
||||||
|
// Wildcard!
|
||||||
|
return Principal::wildcard();
|
||||||
|
} else if (w->id == TokenID::CanonicalUser) {
|
||||||
|
// Do nothing for now.
|
||||||
|
if (errmsg)
|
||||||
|
*errmsg = "RGW does not support canonical users.";
|
||||||
|
return boost::none;
|
||||||
|
} else if (w->id == TokenID::AWS || w->id == TokenID::Federated) {
|
||||||
|
// AWS and Federated ARNs
|
||||||
|
if (auto a = ARN::parse(s)) {
|
||||||
|
if (a->resource == "root") {
|
||||||
|
return Principal::account(std::move(a->account));
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char rx_str[] = "([^/]*)/(.*)";
|
||||||
|
static const regex rx(rx_str, sizeof(rx_str) - 1,
|
||||||
|
std::regex_constants::ECMAScript |
|
||||||
|
std::regex_constants::optimize);
|
||||||
|
smatch match;
|
||||||
|
if (regex_match(a->resource, match, rx) && match.size() == 3) {
|
||||||
|
if (match[1] == "user") {
|
||||||
|
return Principal::user(std::move(a->account),
|
||||||
|
match[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (match[1] == "role") {
|
||||||
|
return Principal::role(std::move(a->account),
|
||||||
|
match[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (match[1] == "oidc-provider") {
|
||||||
|
return Principal::oidc_provider(std::move(match[2]));
|
||||||
|
}
|
||||||
|
if (match[1] == "assumed-role") {
|
||||||
|
return Principal::assumed_role(std::move(a->account), match[2]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (std::none_of(s.begin(), s.end(),
|
||||||
|
[](const char& c) {
|
||||||
|
return (c == ':') || (c == '/');
|
||||||
|
})) {
|
||||||
|
// Since tenants are simply prefixes, there's no really good
|
||||||
|
// way to see if one exists or not. So we return the thing and
|
||||||
|
// let them try to match against it.
|
||||||
|
return Principal::account(std::move(s));
|
||||||
|
}
|
||||||
|
if (errmsg)
|
||||||
|
*errmsg =
|
||||||
|
fmt::format(
|
||||||
|
"`{}` is not a supported AWS or Federated ARN. Supported ARNs are "
|
||||||
|
"forms like: "
|
||||||
|
"`arn:aws:iam::tenant:root` or a bare tenant name for a tenant, "
|
||||||
|
"`arn:aws:iam::tenant:role/role-name` for a role, "
|
||||||
|
"`arn:aws:sts::tenant:assumed-role/role-name/role-session-name` "
|
||||||
|
"for an assumed role, "
|
||||||
|
"`arn:aws:iam::tenant:user/user-name` for a user, "
|
||||||
|
"`arn:aws:iam::tenant:oidc-provider/idp-url` for OIDC.", s);
|
||||||
|
} else if (w->id == TokenID::Service) {
|
||||||
|
return Principal::service(std::move(s));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errmsg)
|
||||||
|
*errmsg = fmt::format("RGW does not support principals of type `{}`.",
|
||||||
|
w->name);
|
||||||
|
return boost::none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct ParseState {
|
struct ParseState {
|
||||||
PolicyParser* pp;
|
PolicyParser* pp;
|
||||||
const Keyword* w;
|
const Keyword* w;
|
||||||
@ -643,72 +717,7 @@ bool ParseState::key(const char* s, size_t l) {
|
|||||||
// which will make all of this ever so much nicer.
|
// which will make all of this ever so much nicer.
|
||||||
boost::optional<Principal> ParseState::parse_principal(string&& s,
|
boost::optional<Principal> ParseState::parse_principal(string&& s,
|
||||||
string* errmsg) {
|
string* errmsg) {
|
||||||
if ((w->id == TokenID::AWS) && (s == "*")) {
|
return ::rgw::IAM::parse_principal_(w, std::move(s), errmsg);
|
||||||
// Wildcard!
|
|
||||||
return Principal::wildcard();
|
|
||||||
} else if (w->id == TokenID::CanonicalUser) {
|
|
||||||
// Do nothing for now.
|
|
||||||
if (errmsg)
|
|
||||||
*errmsg = "RGW does not support canonical users.";
|
|
||||||
return boost::none;
|
|
||||||
} else if (w->id == TokenID::AWS || w->id == TokenID::Federated) {
|
|
||||||
// AWS and Federated ARNs
|
|
||||||
if (auto a = ARN::parse(s)) {
|
|
||||||
if (a->resource == "root") {
|
|
||||||
return Principal::account(std::move(a->account));
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char rx_str[] = "([^/]*)/(.*)";
|
|
||||||
static const regex rx(rx_str, sizeof(rx_str) - 1,
|
|
||||||
std::regex_constants::ECMAScript |
|
|
||||||
std::regex_constants::optimize);
|
|
||||||
smatch match;
|
|
||||||
if (regex_match(a->resource, match, rx) && match.size() == 3) {
|
|
||||||
if (match[1] == "user") {
|
|
||||||
return Principal::user(std::move(a->account),
|
|
||||||
match[2]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (match[1] == "role") {
|
|
||||||
return Principal::role(std::move(a->account),
|
|
||||||
match[2]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (match[1] == "oidc-provider") {
|
|
||||||
return Principal::oidc_provider(std::move(match[2]));
|
|
||||||
}
|
|
||||||
if (match[1] == "assumed-role") {
|
|
||||||
return Principal::assumed_role(std::move(a->account), match[2]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (std::none_of(s.begin(), s.end(),
|
|
||||||
[](const char& c) {
|
|
||||||
return (c == ':') || (c == '/');
|
|
||||||
})) {
|
|
||||||
// Since tenants are simply prefixes, there's no really good
|
|
||||||
// way to see if one exists or not. So we return the thing and
|
|
||||||
// let them try to match against it.
|
|
||||||
return Principal::account(std::move(s));
|
|
||||||
}
|
|
||||||
if (errmsg)
|
|
||||||
*errmsg =
|
|
||||||
fmt::format(
|
|
||||||
"`{}` is not a supported AWS or Federated ARN. Supported ARNs are "
|
|
||||||
"forms like: "
|
|
||||||
"`arn:aws:iam::tenant:root` or a bare tenant name for a tenant, "
|
|
||||||
"`arn:aws:iam::tenant:role/role-name` for a role, "
|
|
||||||
"`arn:aws:sts::tenant:assumed-role/role-name/role-session-name` "
|
|
||||||
"for an assumed role, "
|
|
||||||
"`arn:aws:iam::tenant:user/user-name` for a user, "
|
|
||||||
"`arn:aws:iam::tenant:oidc-provider/idp-url` for OIDC.", s);
|
|
||||||
} else if (w->id == TokenID::Service) {
|
|
||||||
return Principal::service(std::move(s));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (errmsg)
|
|
||||||
*errmsg = fmt::format("RGW does not support principals of type `{}`.",
|
|
||||||
w->name);
|
|
||||||
return boost::none;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ParseState::do_string(CephContext* cct, const char* s, size_t l) {
|
bool ParseState::do_string(CephContext* cct, const char* s, size_t l) {
|
||||||
@ -2144,5 +2153,39 @@ bool is_public(const Policy& p, const LogOut& eval_log)
|
|||||||
return std::any_of(p.statements.begin(), p.statements.end(),
|
return std::any_of(p.statements.begin(), p.statements.end(),
|
||||||
IsPublicStatement(eval_log));
|
IsPublicStatement(eval_log));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boost::optional<Principal> parse_principal(std::string&& s, string* errmsg) {
|
||||||
|
keyword_hash tokens;
|
||||||
|
auto colon = s.find(':') ;
|
||||||
|
if (colon == s.npos) {
|
||||||
|
if (errmsg) {
|
||||||
|
*errmsg = "Identities are of the form SCHEMA:STRING";
|
||||||
|
}
|
||||||
|
return boost::none;
|
||||||
|
}
|
||||||
|
const auto w = tokens.lookup(s.data(), colon);
|
||||||
|
if (!w || w->kind != TokenKind::princ_type) {
|
||||||
|
if (errmsg) {
|
||||||
|
*errmsg = fmt::format("`{}` is not a valid identity schema",
|
||||||
|
std::string_view{s.data(), colon});
|
||||||
|
}
|
||||||
|
return boost::none;
|
||||||
|
}
|
||||||
|
s.erase(0, colon + 1);
|
||||||
|
return parse_principal_(w, std::move(s), errmsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function is only use from the policy testing tool, so it makes
|
||||||
|
// no sense for it to handle wildcards.
|
||||||
|
boost::optional<action_t>
|
||||||
|
parse_action(std::string_view s)
|
||||||
|
{
|
||||||
|
for (const auto& [key, n] : actpairs) {
|
||||||
|
if (boost::iequals(key, s)) {
|
||||||
|
return rgw::IAM::action_t(n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return boost::none;
|
||||||
|
}
|
||||||
} // namespace IAM
|
} // namespace IAM
|
||||||
} // namespace rgw
|
} // namespace rgw
|
||||||
|
|||||||
@ -222,6 +222,9 @@ enum action_t {
|
|||||||
allCount
|
allCount
|
||||||
};
|
};
|
||||||
|
|
||||||
|
boost::optional<rgw::auth::Principal>
|
||||||
|
parse_principal(std::string&& s, std::string* errmsg);
|
||||||
|
|
||||||
using Action_t = std::bitset<allCount>;
|
using Action_t = std::bitset<allCount>;
|
||||||
using NotAction_t = Action_t;
|
using NotAction_t = Action_t;
|
||||||
|
|
||||||
@ -840,6 +843,8 @@ inline bool is_public(const DoutPrefixProvider* dpp, const Policy& p)
|
|||||||
}
|
}
|
||||||
return b;
|
return b;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boost::optional<action_t> parse_action(std::string_view s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
167
src/rgw/rgw_poltester.cc
Normal file
167
src/rgw/rgw_poltester.cc
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:nil -*-
|
||||||
|
// vim: ts=8 sw=2 sts=2 expandtab
|
||||||
|
|
||||||
|
#include <exception>
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
#include <boost/optional.hpp>
|
||||||
|
|
||||||
|
#include "include/buffer.h"
|
||||||
|
|
||||||
|
#include "common/ceph_argparse.h"
|
||||||
|
#include "common/common_init.h"
|
||||||
|
|
||||||
|
#include "global/global_init.h"
|
||||||
|
|
||||||
|
#include "rgw/rgw_auth.h"
|
||||||
|
#include "rgw/rgw_iam_policy.h"
|
||||||
|
|
||||||
|
using namespace std::literals;
|
||||||
|
|
||||||
|
namespace buffer = ceph::buffer;
|
||||||
|
|
||||||
|
using rgw::auth::Identity;
|
||||||
|
|
||||||
|
void
|
||||||
|
evaluate(CephContext* cct, const std::string* tenant,
|
||||||
|
const std::string& fname, std::istream& in,
|
||||||
|
const std::unordered_multimap<std::string, std::string>& environment,
|
||||||
|
boost::optional<const rgw::auth::Identity&> ida, uint64_t action,
|
||||||
|
boost::optional<const rgw::ARN&> resource)
|
||||||
|
{
|
||||||
|
buffer::list bl;
|
||||||
|
bl.append(in);
|
||||||
|
try {
|
||||||
|
auto p = rgw::IAM::Policy(
|
||||||
|
cct, tenant, bl.to_str(),
|
||||||
|
cct->_conf.get_val<bool>("rgw_policy_reject_invalid_principals"));
|
||||||
|
auto effect = p.eval(std::cout, environment, ida, action, resource);
|
||||||
|
std::cout << effect << std::endl;
|
||||||
|
} catch (const rgw::IAM::PolicyParseException& e) {
|
||||||
|
std::cerr << fname << ": " << e.what() << std::endl;
|
||||||
|
throw;
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
std::cerr << fname << ": caught exception: " << e.what() << std::endl;
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int helpful_exit(std::string_view cmdname)
|
||||||
|
{
|
||||||
|
std::cerr << cmdname << " -h for usage" << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void usage(std::string_view cmdname)
|
||||||
|
{
|
||||||
|
std::cout << "usage: " << cmdname << " [options...] ACTION POLICY" << std::endl;
|
||||||
|
std::cout << "options:" << std::endl;
|
||||||
|
std::cout <<
|
||||||
|
" -t | --tenant=TENANT\ttenant owning the resource the policy governs\n";
|
||||||
|
std::cout <<
|
||||||
|
" -e | --environment=KEY=VALUE\tPair to set in the environment\n";
|
||||||
|
std::cout << " -I | --identity=IDENTITY\tIdentity against which to test\n";
|
||||||
|
std::cout << " -R | --resource=ARN\tResource to test access to\n\n";
|
||||||
|
std::cout << " ACTION\tAction to test against, e.g. s3:GetObject\n";
|
||||||
|
std::cout << " POLICY\tFilename of the policy or - for standard input\n";
|
||||||
|
std::cout.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
// This has an uncaught exception. Even if the exception is caught, the program
|
||||||
|
// would need to be terminated, so the warning is simply suppressed.
|
||||||
|
// coverity[root_function:SUPPRESS]
|
||||||
|
int main(int argc, const char* argv[])
|
||||||
|
{
|
||||||
|
std::string_view cmdname = argv[0];
|
||||||
|
std::string tenant{}; // Explicitly defaulted to the empty tenant if none is provided.
|
||||||
|
std::unordered_multimap<std::string, std::string> environment;
|
||||||
|
boost::optional<rgw::auth::PrincipalIdentity> identity_;
|
||||||
|
boost::optional<const rgw::auth::Identity&> identity;
|
||||||
|
uint64_t action = 0;
|
||||||
|
boost::optional<rgw::ARN> resource_;
|
||||||
|
boost::optional<const rgw::ARN&> resource;
|
||||||
|
|
||||||
|
auto args = argv_to_vec(argc, argv);
|
||||||
|
if (ceph_argparse_need_usage(args)) {
|
||||||
|
usage(cmdname);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto cct = global_init(nullptr, args, CEPH_ENTITY_TYPE_CLIENT,
|
||||||
|
CODE_ENVIRONMENT_UTILITY,
|
||||||
|
CINIT_FLAG_NO_DAEMON_ACTIONS |
|
||||||
|
CINIT_FLAG_NO_MON_CONFIG);
|
||||||
|
common_init_finish(cct.get());
|
||||||
|
std::string val;
|
||||||
|
for (std::vector<const char*>::iterator i = args.begin(); i != args.end(); ) {
|
||||||
|
if (ceph_argparse_double_dash(args, i)) {
|
||||||
|
break;
|
||||||
|
} else if (ceph_argparse_witharg(args, i, &val, "--tenant", "-t",
|
||||||
|
(char*)nullptr)) {
|
||||||
|
tenant = std::move(val);
|
||||||
|
} else if (ceph_argparse_witharg(args, i, &val, "--environment", "-e",
|
||||||
|
(char*)nullptr)) {
|
||||||
|
auto equal = val.find('=');
|
||||||
|
if (equal == val.npos || equal == 0) {
|
||||||
|
return helpful_exit(cmdname);
|
||||||
|
}
|
||||||
|
std::string k{val.data(), equal};
|
||||||
|
std::string v{val.data() + equal + 1};
|
||||||
|
environment.insert({std::move(k), std::move(v)});
|
||||||
|
} else if (ceph_argparse_witharg(args, i, &val, "--resource", "-R",
|
||||||
|
(char*)nullptr)) {
|
||||||
|
resource_ = rgw::ARN::parse(val);
|
||||||
|
if (!resource_) {
|
||||||
|
return helpful_exit(cmdname);
|
||||||
|
}
|
||||||
|
resource = *resource_;
|
||||||
|
} else if (ceph_argparse_witharg(args, i, &val, "--identity", "-I",
|
||||||
|
(char*)nullptr)) {
|
||||||
|
std::string errmsg;
|
||||||
|
auto principal = rgw::IAM::parse_principal(std::move(val), &errmsg);
|
||||||
|
if (!principal) {
|
||||||
|
std::cerr << errmsg << std::endl;
|
||||||
|
return helpful_exit(cmdname);
|
||||||
|
}
|
||||||
|
identity_.emplace(std::move(*principal));
|
||||||
|
identity = *identity_;
|
||||||
|
} else {
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.size() != 2) {
|
||||||
|
return helpful_exit(cmdname);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auto act = rgw::IAM::parse_action(args[0]); act) {
|
||||||
|
action = *act;
|
||||||
|
} else {
|
||||||
|
std::cerr << args[0] << " is not a valid action." << std::endl;
|
||||||
|
return helpful_exit(cmdname);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (args[1] == "-"s) {
|
||||||
|
evaluate(cct.get(), &tenant, "(stdin)", std::cin,
|
||||||
|
environment, identity, action, resource);
|
||||||
|
} else {
|
||||||
|
std::ifstream in;
|
||||||
|
in.open(args[1], std::ifstream::in);
|
||||||
|
if (!in.is_open()) {
|
||||||
|
std::cerr << "Can't read " << args[1] << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
evaluate(cct.get(), &tenant, args[1], in, environment,
|
||||||
|
identity, action, resource);
|
||||||
|
}
|
||||||
|
} catch (const std::exception&) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user