mirror of
https://github.com/ceph/ceph
synced 2025-02-20 17:37:29 +00:00
rgw: Keystone implementation can support multiple instances now.
Signed-off-by: Radoslaw Zarzynski <rzarzynski@mirantis.com>
This commit is contained in:
parent
f82af9ca95
commit
9ddb0494c5
@ -297,7 +297,8 @@ RGWKeystoneAuthEngine::decode_pki_token(const std::string& token) const
|
||||
}
|
||||
|
||||
rgw::keystone::TokenEnvelope token_body;
|
||||
ret = token_body.parse(cct, token, token_body_bl);
|
||||
ret = token_body.parse(cct, token, token_body_bl,
|
||||
rgw::keystone::CephCtxConfig::get_instance().get_api_version());
|
||||
if (ret < 0) {
|
||||
throw ret;
|
||||
}
|
||||
@ -314,12 +315,13 @@ RGWKeystoneAuthEngine::get_from_keystone(const std::string& token) const
|
||||
bufferlist token_body_bl;
|
||||
RGWValidateKeystoneToken validate(cct, &token_body_bl);
|
||||
|
||||
std::string url;
|
||||
if (rgw::keystone::Service::get_keystone_url(cct, url) < 0) {
|
||||
std::string url
|
||||
= rgw::keystone::CephCtxConfig::get_instance().get_endpoint_url();
|
||||
if (url.empty()) {
|
||||
throw -EINVAL;
|
||||
}
|
||||
|
||||
const auto keystone_version = rgw::keystone::Service::get_api_version();
|
||||
const auto keystone_version = rgw::keystone::CephCtxConfig::get_instance().get_api_version();
|
||||
if (keystone_version == rgw::keystone::ApiVersion::VER_2) {
|
||||
url.append("v2.0/tokens/" + token);
|
||||
} else if (keystone_version == rgw::keystone::ApiVersion::VER_3) {
|
||||
@ -328,7 +330,11 @@ RGWKeystoneAuthEngine::get_from_keystone(const std::string& token) const
|
||||
}
|
||||
|
||||
std::string admin_token;
|
||||
if (rgw::keystone::Service::get_keystone_admin_token(cct, admin_token) < 0) {
|
||||
if (rgw::keystone::Service::get_admin_token(
|
||||
cct,
|
||||
rgw::keystone::TokenCache::get_instance<rgw::keystone::CephCtxConfig>(),
|
||||
rgw::keystone::CephCtxConfig::get_instance(),
|
||||
admin_token) < 0) {
|
||||
throw -EINVAL;
|
||||
}
|
||||
|
||||
@ -359,7 +365,7 @@ RGWKeystoneAuthEngine::get_from_keystone(const std::string& token) const
|
||||
}
|
||||
|
||||
rgw::keystone::TokenEnvelope token_body;
|
||||
ret = token_body.parse(cct, token, token_body_bl);
|
||||
ret = token_body.parse(cct, token, token_body_bl, keystone_version);
|
||||
if (ret < 0) {
|
||||
throw ret;
|
||||
}
|
||||
@ -465,7 +471,7 @@ RGWAuthApplier::aplptr_t RGWKeystoneAuthEngine::authenticate() const
|
||||
ldout(cct, 20) << "token_id=" << token_id << dendl;
|
||||
|
||||
/* Check cache first. */
|
||||
if (rgw::keystone::TokenCache::get_instance().find(token_id, t)) {
|
||||
if (rgw::keystone::TokenCache::get_instance<rgw::keystone::CephCtxConfig>().find(token_id, t)) {
|
||||
ldout(cct, 20) << "cached token.project.id=" << t.get_project_id()
|
||||
<< dendl;
|
||||
return apl_factory->create_apl_remote(cct,
|
||||
@ -500,7 +506,7 @@ RGWAuthApplier::aplptr_t RGWKeystoneAuthEngine::authenticate() const
|
||||
ldout(cct, 0) << "validated token: " << t.get_project_name()
|
||||
<< ":" << t.get_user_name()
|
||||
<< " expires: " << t.get_expires() << dendl;
|
||||
rgw::keystone::TokenCache::get_instance().add(token_id, t);
|
||||
rgw::keystone::TokenCache::get_instance<rgw::keystone::CephCtxConfig>().add(token_id, t);
|
||||
return apl_factory->create_apl_remote(cct,
|
||||
get_acl_strategy(t),
|
||||
get_creds_info(t, roles.admin));
|
||||
|
@ -1204,57 +1204,32 @@ void rgw::keystone::TokenEnvelope::User::decode_json(JSONObj *obj)
|
||||
JSONDecoder::decode_json("roles", roles_v2, obj);
|
||||
}
|
||||
|
||||
void rgw::keystone::TokenEnvelope::decode_json(JSONObj *root_obj)
|
||||
void rgw::keystone::TokenEnvelope::decode_v3(JSONObj* const root_obj)
|
||||
{
|
||||
std::string expires_iso8601;
|
||||
|
||||
JSONDecoder::decode_json("user", user, root_obj, true);
|
||||
JSONDecoder::decode_json("expires_at", expires_iso8601, root_obj, true);
|
||||
JSONDecoder::decode_json("roles", roles, root_obj, true);
|
||||
JSONDecoder::decode_json("project", project, root_obj, true);
|
||||
|
||||
struct tm t;
|
||||
if (parse_iso8601(expires_iso8601.c_str(), &t)) {
|
||||
token.expires = timegm(&t);
|
||||
} else {
|
||||
token.expires = 0;
|
||||
throw JSONDecoder::err("Failed to parse ISO8601 expiration date"
|
||||
"from Keystone response.");
|
||||
}
|
||||
}
|
||||
|
||||
void rgw::keystone::TokenEnvelope::decode_v2(JSONObj* const root_obj)
|
||||
{
|
||||
JSONDecoder::decode_json("user", user, root_obj, true);
|
||||
JSONDecoder::decode_json("token", token, root_obj, true);
|
||||
|
||||
const auto version = rgw::keystone::Service::get_api_version();
|
||||
|
||||
if (version == rgw::keystone::ApiVersion::VER_3) {
|
||||
string expires_iso8601;
|
||||
if (JSONDecoder::decode_json("expires_at", expires_iso8601, root_obj)) {
|
||||
/* VER_3 */
|
||||
/* Presence of "expires_at" suggests we are dealing with OpenStack
|
||||
* Identity API v3 (aka Keystone API v3) token. */
|
||||
struct tm t;
|
||||
|
||||
if (parse_iso8601(expires_iso8601.c_str(), &t)) {
|
||||
token.expires = timegm(&t);
|
||||
} else {
|
||||
token.expires = 0;
|
||||
throw JSONDecoder::err("Failed to parse ISO8601 expiration date"
|
||||
"from Keystone response.");
|
||||
}
|
||||
JSONDecoder::decode_json("roles", roles, root_obj, true);
|
||||
JSONDecoder::decode_json("project", project, root_obj, true);
|
||||
} else {
|
||||
/* fallback: VER_2 */
|
||||
JSONDecoder::decode_json("token", token, root_obj, true);
|
||||
roles = user.roles_v2;
|
||||
project = token.tenant_v2;
|
||||
}
|
||||
} else if (version == rgw::keystone::ApiVersion::VER_2) {
|
||||
if (JSONDecoder::decode_json("token", token, root_obj)) {
|
||||
/* VER_2 */
|
||||
roles = user.roles_v2;
|
||||
project = token.tenant_v2;
|
||||
} else {
|
||||
/* fallback: VER_3 */
|
||||
string expires_iso8601;
|
||||
JSONDecoder::decode_json("expires_at", expires_iso8601, root_obj, true);
|
||||
|
||||
struct tm t;
|
||||
if (parse_iso8601(expires_iso8601.c_str(), &t)) {
|
||||
token.expires = timegm(&t);
|
||||
} else {
|
||||
token.expires = 0;
|
||||
throw JSONDecoder::err("Failed to parse ISO8601 expiration date"
|
||||
"from Keystone response.");
|
||||
}
|
||||
JSONDecoder::decode_json("roles", roles, root_obj, true);
|
||||
JSONDecoder::decode_json("project", project, root_obj, true);
|
||||
}
|
||||
}
|
||||
roles = user.roles_v2;
|
||||
project = token.tenant_v2;
|
||||
}
|
||||
|
||||
void rgw_slo_entry::decode_json(JSONObj *obj)
|
||||
@ -1344,15 +1319,24 @@ void rgw_sync_error_info::dump(Formatter *f) const {
|
||||
encode_json("message", message, f);
|
||||
}
|
||||
|
||||
/* This utility function shouldn't conflict with the overload of std::to_string
|
||||
* provided by string_ref since Boost 1.54 as it's defined outside of the std
|
||||
* namespace. I hope we'll remove it soon - just after merging the Matt's PR
|
||||
* for bundled Boost. It would allow us to forget that CentOS 7 has Boost 1.53. */
|
||||
static inline std::string to_string(const boost::string_ref& s)
|
||||
{
|
||||
return std::string(s.data(), s.length());
|
||||
}
|
||||
|
||||
void rgw::keystone::AdminTokenRequestVer2::dump(Formatter* const f) const
|
||||
{
|
||||
f->open_object_section("token_request");
|
||||
f->open_object_section("auth");
|
||||
f->open_object_section("passwordCredentials");
|
||||
encode_json("username", cct->_conf->rgw_keystone_admin_user, f);
|
||||
encode_json("password", cct->_conf->rgw_keystone_admin_password, f);
|
||||
encode_json("username", to_string(conf.get_admin_user()), f);
|
||||
encode_json("password", to_string(conf.get_admin_password()), f);
|
||||
f->close_section();
|
||||
encode_json("tenantName", cct->_conf->rgw_keystone_admin_tenant, f);
|
||||
encode_json("tenantName", to_string(conf.get_admin_tenant()), f);
|
||||
f->close_section();
|
||||
f->close_section();
|
||||
}
|
||||
@ -1368,22 +1352,22 @@ void rgw::keystone::AdminTokenRequestVer3::dump(Formatter* const f) const
|
||||
f->open_object_section("password");
|
||||
f->open_object_section("user");
|
||||
f->open_object_section("domain");
|
||||
encode_json("name", cct->_conf->rgw_keystone_admin_domain, f);
|
||||
encode_json("name", to_string(conf.get_admin_domain()), f);
|
||||
f->close_section();
|
||||
encode_json("name", cct->_conf->rgw_keystone_admin_user, f);
|
||||
encode_json("password", cct->_conf->rgw_keystone_admin_password, f);
|
||||
encode_json("name", to_string(conf.get_admin_user()), f);
|
||||
encode_json("password", to_string(conf.get_admin_password()), f);
|
||||
f->close_section();
|
||||
f->close_section();
|
||||
f->close_section();
|
||||
f->open_object_section("scope");
|
||||
f->open_object_section("project");
|
||||
if (!cct->_conf->rgw_keystone_admin_project.empty()) {
|
||||
encode_json("name", cct->_conf->rgw_keystone_admin_project, f);
|
||||
if (! conf.get_admin_project().empty()) {
|
||||
encode_json("name", to_string(conf.get_admin_project()), f);
|
||||
} else {
|
||||
encode_json("name", cct->_conf->rgw_keystone_admin_tenant, f);
|
||||
encode_json("name", to_string(conf.get_admin_tenant()), f);
|
||||
}
|
||||
f->open_object_section("domain");
|
||||
encode_json("name", cct->_conf->rgw_keystone_admin_domain, f);
|
||||
encode_json("name", to_string(conf.get_admin_domain()), f);
|
||||
f->close_section();
|
||||
f->close_section();
|
||||
f->close_section();
|
||||
|
@ -4,6 +4,8 @@
|
||||
#include <errno.h>
|
||||
#include <fnmatch.h>
|
||||
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
|
||||
#include "common/errno.h"
|
||||
#include "common/ceph_json.h"
|
||||
#include "include/types.h"
|
||||
@ -141,68 +143,82 @@ bool rgw_decode_pki_token(CephContext * const cct,
|
||||
namespace rgw {
|
||||
namespace keystone {
|
||||
|
||||
ApiVersion Service::get_api_version()
|
||||
ApiVersion CephCtxConfig::get_api_version() const noexcept
|
||||
{
|
||||
const int keystone_version = g_ceph_context->_conf->rgw_keystone_api_version;
|
||||
|
||||
if (keystone_version == 3) {
|
||||
switch (g_ceph_context->_conf->rgw_keystone_api_version) {
|
||||
case 3:
|
||||
return ApiVersion::VER_3;
|
||||
} else if (keystone_version == 2) {
|
||||
case 2:
|
||||
return ApiVersion::VER_2;
|
||||
} else {
|
||||
dout(0) << "ERROR: wrong Keystone API version: " << keystone_version
|
||||
default:
|
||||
dout(0) << "ERROR: wrong Keystone API version: "
|
||||
<< g_ceph_context->_conf->rgw_keystone_api_version
|
||||
<< "; falling back to v2" << dendl;
|
||||
return ApiVersion::VER_2;
|
||||
}
|
||||
}
|
||||
|
||||
int Service::get_keystone_url(CephContext* const cct,
|
||||
std::string& url)
|
||||
std::string CephCtxConfig::get_endpoint_url() const noexcept
|
||||
{
|
||||
url = cct->_conf->rgw_keystone_url;
|
||||
if (url.empty()) {
|
||||
ldout(cct, 0) << "ERROR: keystone url is not configured" << dendl;
|
||||
return -EINVAL;
|
||||
}
|
||||
static const std::string url = g_ceph_context->_conf->rgw_keystone_url;
|
||||
|
||||
if (url[url.size() - 1] != '/') {
|
||||
url.append("/");
|
||||
if (url.empty() || boost::algorithm::ends_with(url, "/")) {
|
||||
return url;
|
||||
} else {
|
||||
static const std::string url_normalised = url + '/';
|
||||
return url_normalised;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Service::get_keystone_admin_token(CephContext* const cct,
|
||||
std::string& token)
|
||||
int Service::get_admin_token(CephContext* const cct,
|
||||
TokenCache& token_cache,
|
||||
const Config& config,
|
||||
std::string& token)
|
||||
{
|
||||
std::string token_url;
|
||||
|
||||
if (get_keystone_url(cct, token_url) < 0) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!cct->_conf->rgw_keystone_admin_token.empty()) {
|
||||
token = cct->_conf->rgw_keystone_admin_token;
|
||||
/* Let's check whether someone uses the deprecated "admin token" feauture
|
||||
* based on a shared secret from keystone.conf file. */
|
||||
const auto& admin_token = config.get_admin_token();
|
||||
if (! admin_token.empty()) {
|
||||
token = std::string(admin_token.data(), admin_token.length());
|
||||
return 0;
|
||||
}
|
||||
|
||||
TokenEnvelope t;
|
||||
|
||||
/* Try cache first. */
|
||||
if (TokenCache::get_instance<rgw::keystone::LegacyConfig>().find_admin(t)) {
|
||||
/* Try cache first before calling Keystone for a new admin token. */
|
||||
if (token_cache.find_admin(t)) {
|
||||
ldout(cct, 20) << "found cached admin token" << dendl;
|
||||
token = t.token.id;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Call Keystone now. */
|
||||
const auto ret = issue_admin_token_request(cct, config, t);
|
||||
if (! ret) {
|
||||
token_cache.add_admin(t);
|
||||
token = t.token.id;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int Service::issue_admin_token_request(CephContext* const cct,
|
||||
const Config& config,
|
||||
TokenEnvelope& t)
|
||||
{
|
||||
std::string token_url = config.get_endpoint_url();
|
||||
if (token_url.empty()) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
bufferlist token_bl;
|
||||
RGWGetKeystoneAdminToken token_req(cct, &token_bl);
|
||||
token_req.append_header("Content-Type", "application/json");
|
||||
JSONFormatter jf;
|
||||
|
||||
const auto keystone_version = Service::get_api_version();
|
||||
const auto keystone_version = config.get_api_version();
|
||||
if (keystone_version == ApiVersion::VER_2) {
|
||||
AdminTokenRequestVer2 req_serializer(cct);
|
||||
AdminTokenRequestVer2 req_serializer(config);
|
||||
req_serializer.dump(&jf);
|
||||
|
||||
std::stringstream ss;
|
||||
@ -212,7 +228,7 @@ int Service::get_keystone_admin_token(CephContext* const cct,
|
||||
token_url.append("v2.0/tokens");
|
||||
|
||||
} else if (keystone_version == ApiVersion::VER_3) {
|
||||
AdminTokenRequestVer3 req_serializer(cct);
|
||||
AdminTokenRequestVer3 req_serializer(config);
|
||||
req_serializer.dump(&jf);
|
||||
|
||||
std::stringstream ss;
|
||||
@ -235,12 +251,11 @@ int Service::get_keystone_admin_token(CephContext* const cct,
|
||||
return -EACCES;
|
||||
}
|
||||
|
||||
if (t.parse(cct, token_req.get_subject_token(), token_bl) != 0) {
|
||||
if (t.parse(cct, token_req.get_subject_token(), token_bl,
|
||||
keystone_version) != 0) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
TokenCache::get_instance().add_admin(t);
|
||||
token = t.token.id;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -258,33 +273,47 @@ bool TokenEnvelope::has_role(const std::string& r) const
|
||||
|
||||
int TokenEnvelope::parse(CephContext* const cct,
|
||||
const std::string& token_str,
|
||||
ceph::bufferlist& bl)
|
||||
ceph::bufferlist& bl,
|
||||
const ApiVersion version)
|
||||
{
|
||||
JSONParser parser;
|
||||
if (!parser.parse(bl.c_str(), bl.length())) {
|
||||
if (! parser.parse(bl.c_str(), bl.length())) {
|
||||
ldout(cct, 0) << "Keystone token parse error: malformed json" << dendl;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
try {
|
||||
const auto version = rgw::keystone::Service::get_api_version();
|
||||
JSONObjIter token_iter = parser.find_first("token");
|
||||
JSONObjIter access_iter = parser.find_first("access");
|
||||
|
||||
try {
|
||||
if (version == rgw::keystone::ApiVersion::VER_2) {
|
||||
if (!JSONDecoder::decode_json("access", *this, &parser)) {
|
||||
/* TokenEnvelope structure doesn't follow Identity API v2, so the token
|
||||
* must be in v3. Otherwise we can assume it's wrongly formatted. */
|
||||
JSONDecoder::decode_json("token", *this, &parser, true);
|
||||
if (! access_iter.end()) {
|
||||
decode_v2(*access_iter);
|
||||
} else if (! token_iter.end()) {
|
||||
/* TokenEnvelope structure doesn't follow Identity API v2, so let's
|
||||
* fallback to v3. Otherwise we can assume it's wrongly formatted.
|
||||
* The whole mechanism is a workaround for s3_token middleware that
|
||||
* speaks in v2 disregarding the promise to go with v3. */
|
||||
decode_v3(*token_iter);
|
||||
|
||||
/* Identity v3 conveys the token inforamtion not as a part of JSON but
|
||||
* in the X-Subject-Token HTTP header we're getting from caller. */
|
||||
token.id = token_str;
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
} else if (version == rgw::keystone::ApiVersion::VER_3) {
|
||||
if (!JSONDecoder::decode_json("token", *this, &parser)) {
|
||||
/* If the token cannot be parsed according to V3, try V2. */
|
||||
JSONDecoder::decode_json("access", *this, &parser, true);
|
||||
} else {
|
||||
if (! token_iter.end()) {
|
||||
decode_v3(*token_iter);
|
||||
/* v3 suceeded. We have to fill token.id from external input as it
|
||||
* isn't a part of the JSON response anymore. It has been moved
|
||||
* to X-Subject-Token HTTP header instead. */
|
||||
token.id = token_str;
|
||||
} else if (! access_iter.end()) {
|
||||
/* If the token cannot be parsed according to V3, try V2. */
|
||||
decode_v2(*access_iter);
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
} else {
|
||||
return -ENOTSUP;
|
||||
@ -297,13 +326,6 @@ int TokenEnvelope::parse(CephContext* const cct,
|
||||
return 0;
|
||||
}
|
||||
|
||||
TokenCache& TokenCache::get_instance()
|
||||
{
|
||||
/* In C++11 this is thread safe. */
|
||||
static TokenCache instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
bool TokenCache::find(const std::string& token_id,
|
||||
rgw::keystone::TokenEnvelope& token)
|
||||
{
|
||||
@ -406,15 +428,18 @@ int TokenCache::RevokeThread::check_revoked()
|
||||
bufferlist bl;
|
||||
RGWGetRevokedTokens req(cct, &bl);
|
||||
|
||||
if (rgw::keystone::Service::get_keystone_admin_token(cct, token) < 0) {
|
||||
if (rgw::keystone::Service::get_admin_token(cct, *cache, config, token) < 0) {
|
||||
return -EINVAL;
|
||||
}
|
||||
if (rgw::keystone::Service::get_keystone_url(cct, url) < 0) {
|
||||
|
||||
url = config.get_endpoint_url();
|
||||
if (url.empty()) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
req.append_header("X-Auth-Token", token);
|
||||
|
||||
const auto keystone_version = rgw::keystone::Service::get_api_version();
|
||||
const auto keystone_version = config.get_api_version();
|
||||
if (keystone_version == rgw::keystone::ApiVersion::VER_2) {
|
||||
url.append("v2.0/tokens/revoked");
|
||||
} else if (keystone_version == rgw::keystone::ApiVersion::VER_3) {
|
||||
|
@ -4,6 +4,10 @@
|
||||
#ifndef CEPH_RGW_KEYSTONE_H
|
||||
#define CEPH_RGW_KEYSTONE_H
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#include <boost/utility/string_ref.hpp>
|
||||
|
||||
#include "rgw_common.h"
|
||||
#include "rgw_http_client.h"
|
||||
#include "common/Cond.h"
|
||||
@ -35,6 +39,67 @@ enum class ApiVersion {
|
||||
VER_3
|
||||
};
|
||||
|
||||
|
||||
class Config {
|
||||
protected:
|
||||
Config() = default;
|
||||
virtual ~Config() = default;
|
||||
|
||||
public:
|
||||
virtual std::string get_endpoint_url() const noexcept = 0;
|
||||
virtual ApiVersion get_api_version() const noexcept = 0;
|
||||
|
||||
virtual boost::string_ref get_admin_token() const noexcept = 0;
|
||||
virtual boost::string_ref get_admin_user() const noexcept = 0;
|
||||
virtual boost::string_ref get_admin_password() const noexcept = 0;
|
||||
virtual boost::string_ref get_admin_tenant() const noexcept = 0;
|
||||
virtual boost::string_ref get_admin_project() const noexcept = 0;
|
||||
virtual boost::string_ref get_admin_domain() const noexcept = 0;
|
||||
};
|
||||
|
||||
class CephCtxConfig : public Config {
|
||||
protected:
|
||||
CephCtxConfig() = default;
|
||||
virtual ~CephCtxConfig() = default;
|
||||
|
||||
public:
|
||||
static CephCtxConfig& get_instance() {
|
||||
static CephCtxConfig instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
std::string get_endpoint_url() const noexcept override;
|
||||
ApiVersion get_api_version() const noexcept override;
|
||||
|
||||
boost::string_ref get_admin_token() const noexcept override {
|
||||
return g_ceph_context->_conf->rgw_keystone_admin_token;
|
||||
}
|
||||
|
||||
boost::string_ref get_admin_user() const noexcept override {
|
||||
return g_ceph_context->_conf->rgw_keystone_admin_user;
|
||||
}
|
||||
|
||||
boost::string_ref get_admin_password() const noexcept override {
|
||||
return g_ceph_context->_conf->rgw_keystone_admin_password;
|
||||
}
|
||||
|
||||
boost::string_ref get_admin_tenant() const noexcept override {
|
||||
return g_ceph_context->_conf->rgw_keystone_admin_tenant;
|
||||
}
|
||||
|
||||
boost::string_ref get_admin_project() const noexcept override {
|
||||
return g_ceph_context->_conf->rgw_keystone_admin_project;
|
||||
}
|
||||
|
||||
boost::string_ref get_admin_domain() const noexcept override {
|
||||
return g_ceph_context->_conf->rgw_keystone_admin_domain;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class TokenEnvelope;
|
||||
class TokenCache;
|
||||
|
||||
class Service {
|
||||
public:
|
||||
class RGWKeystoneHTTPTransceiver : public RGWHTTPTransceiver {
|
||||
@ -60,12 +125,13 @@ public:
|
||||
typedef RGWKeystoneHTTPTransceiver RGWGetKeystoneAdminToken;
|
||||
typedef RGWKeystoneHTTPTransceiver RGWGetRevokedTokens;
|
||||
|
||||
static ApiVersion get_api_version();
|
||||
|
||||
static int get_keystone_url(CephContext * const cct,
|
||||
std::string& url);
|
||||
static int get_keystone_admin_token(CephContext * const cct,
|
||||
std::string& token);
|
||||
static int get_admin_token(CephContext* const cct,
|
||||
TokenCache& token_cache,
|
||||
const Config& config,
|
||||
std::string& token);
|
||||
static int issue_admin_token_request(CephContext* const cct,
|
||||
const Config& config,
|
||||
TokenEnvelope& token);
|
||||
};
|
||||
|
||||
|
||||
@ -115,9 +181,13 @@ public:
|
||||
User user;
|
||||
list<Role> roles;
|
||||
|
||||
void decode_v3(JSONObj* obj);
|
||||
void decode_v2(JSONObj* obj);
|
||||
|
||||
public:
|
||||
// FIXME: default ctor needs to be eradicated here
|
||||
/* We really need the default ctor because of the internals of TokenCache. */
|
||||
TokenEnvelope() = default;
|
||||
|
||||
time_t get_expires() const { return token.expires; }
|
||||
const std::string& get_domain_id() const {return project.domain.id;};
|
||||
const std::string& get_domain_name() const {return project.domain.name;};
|
||||
@ -130,10 +200,10 @@ public:
|
||||
uint64_t now = ceph_clock_now().sec();
|
||||
return (now >= (uint64_t)get_expires());
|
||||
}
|
||||
int parse(CephContext *cct,
|
||||
const string& token_str,
|
||||
bufferlist& bl /* in */);
|
||||
void decode_json(JSONObj *access_obj);
|
||||
int parse(CephContext* cct,
|
||||
const std::string& token_str,
|
||||
ceph::buffer::list& bl /* in */,
|
||||
ApiVersion version);
|
||||
};
|
||||
|
||||
|
||||
@ -143,7 +213,6 @@ class TokenCache {
|
||||
list<string>::iterator lru_iter;
|
||||
};
|
||||
|
||||
const rgw::keystone::Config& config;
|
||||
atomic_t down_flag;
|
||||
|
||||
class RevokeThread : public Thread {
|
||||
@ -152,13 +221,17 @@ class TokenCache {
|
||||
|
||||
CephContext * const cct;
|
||||
TokenCache* const cache;
|
||||
const rgw::keystone::Config& config;
|
||||
|
||||
Mutex lock;
|
||||
Cond cond;
|
||||
|
||||
RevokeThread(CephContext* const cct,
|
||||
TokenCache* const cache)
|
||||
TokenCache* const cache,
|
||||
const rgw::keystone::Config& config)
|
||||
: cct(cct),
|
||||
cache(cache),
|
||||
config(config),
|
||||
lock("rgw::keystone::TokenCache::RevokeThread") {
|
||||
}
|
||||
void *entry() override;
|
||||
@ -176,8 +249,8 @@ class TokenCache {
|
||||
|
||||
const size_t max;
|
||||
|
||||
TokenCache()
|
||||
: revocator(g_ceph_context, this),
|
||||
TokenCache(const rgw::keystone::Config& config)
|
||||
: revocator(g_ceph_context, this, config),
|
||||
cct(g_ceph_context),
|
||||
lock("rgw::keystone::TokenCache"),
|
||||
max(cct->_conf->rgw_keystone_token_cache_size) {
|
||||
@ -196,7 +269,10 @@ public:
|
||||
TokenCache(const TokenCache&) = delete;
|
||||
void operator=(const TokenCache&) = delete;
|
||||
|
||||
static TokenCache& get_instance();
|
||||
template<class ConfigT>
|
||||
static TokenCache& get_instance() {
|
||||
static_assert(std::is_base_of<rgw::keystone::Config, ConfigT>::value,
|
||||
"ConfigT must be a subclass of rgw::keystone::Config");
|
||||
|
||||
/* In C++11 this is thread safe. */
|
||||
static TokenCache instance(ConfigT::get_instance());
|
||||
@ -223,21 +299,21 @@ public:
|
||||
};
|
||||
|
||||
class AdminTokenRequestVer2 : public AdminTokenRequest {
|
||||
CephContext* cct;
|
||||
const Config& conf;
|
||||
|
||||
public:
|
||||
AdminTokenRequestVer2(CephContext* const cct)
|
||||
: cct(cct) {
|
||||
AdminTokenRequestVer2(const Config& conf)
|
||||
: conf(conf) {
|
||||
}
|
||||
void dump(Formatter *f) const override;
|
||||
};
|
||||
|
||||
class AdminTokenRequestVer3 : public AdminTokenRequest {
|
||||
CephContext* cct;
|
||||
const Config& conf;
|
||||
|
||||
public:
|
||||
AdminTokenRequestVer3(CephContext* const cct)
|
||||
: cct(cct) {
|
||||
AdminTokenRequestVer3(const Config& conf)
|
||||
: conf(conf) {
|
||||
}
|
||||
void dump(Formatter *f) const override;
|
||||
};
|
||||
|
@ -3302,7 +3302,8 @@ int RGW_Auth_S3_Keystone_ValidateToken::validate_s3token(
|
||||
keystone_url.append("/");
|
||||
}
|
||||
|
||||
if (rgw::keystone::Service::get_api_version() == rgw::keystone::ApiVersion::VER_3) {
|
||||
if (rgw::keystone::CephCtxConfig::get_instance().get_api_version()
|
||||
== rgw::keystone::ApiVersion::VER_3) {
|
||||
keystone_url.append("v3/s3tokens");
|
||||
} else {
|
||||
keystone_url.append("v2.0/s3tokens");
|
||||
@ -3310,7 +3311,12 @@ int RGW_Auth_S3_Keystone_ValidateToken::validate_s3token(
|
||||
|
||||
/* get authentication token for Keystone. */
|
||||
string admin_token_id;
|
||||
int r = rgw::keystone::Service::get_keystone_admin_token(cct, admin_token_id);
|
||||
int r = rgw::keystone::Service::get_admin_token(
|
||||
cct,
|
||||
rgw::keystone::TokenCache::get_instance<rgw::keystone::CephCtxConfig>(),
|
||||
rgw::keystone::CephCtxConfig::get_instance(),
|
||||
admin_token_id);
|
||||
|
||||
if (r < 0) {
|
||||
ldout(cct, 2) << "s3 keystone: cannot get token for keystone access" << dendl;
|
||||
return r;
|
||||
@ -3358,7 +3364,8 @@ int RGW_Auth_S3_Keystone_ValidateToken::validate_s3token(
|
||||
}
|
||||
|
||||
/* now parse response */
|
||||
if (response.parse(cct, string(), rx_buffer) < 0) {
|
||||
if (response.parse(cct, string(), rx_buffer,
|
||||
rgw::keystone::CephCtxConfig::get_instance().get_api_version()) < 0) {
|
||||
dout(2) << "s3 keystone: token parsing failed" << dendl;
|
||||
return -EPERM;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user