}
boost::optional<TokenEngine::token_envelope_t>
-TokenEngine::get_from_keystone(const DoutPrefixProvider* dpp, const std::string& token) const
+TokenEngine::get_from_keystone(const DoutPrefixProvider* dpp, const std::string& token, const bool& allow_expired) const
{
/* Unfortunately, we can't use the short form of "using" here. It's because
* we're aliasing a class' member, not namespace. */
url.append("v2.0/tokens/" + token);
} else if (keystone_version == rgw::keystone::ApiVersion::VER_3) {
url.append("v3/auth/tokens");
+
+ if (allow_expired) {
+ url.append("?allow_expired=1");
+ }
+
validate.append_header("X-Subject-Token", token);
}
TokenEngine::result_t
TokenEngine::authenticate(const DoutPrefixProvider* dpp,
const std::string& token,
+ const std::string& service_token,
const req_state* const s) const
{
+ bool allow_expired = false;
boost::optional<TokenEngine::token_envelope_t> t;
/* This will be initialized on the first call to this method. In C++11 it's
return result_t::grant(std::move(apl));
}
- /* Not in cache. Go to the Keystone for validation. This happens even
+ /* TODO(tobias-urdin): Add config option for enabling service token support */
+
+ /* We have a service token and a token so we verify the service
+ * token and if it's invalid the request is invalid. If it's valid
+ * we allow an expired token to be used when doing lookup in Keystone.
+ * We never get to this if the token is in the cache. */
+ if (! service_token.empty()) {
+ boost::optional<TokenEngine::token_envelope_t> st;
+
+ const auto& service_token_id = rgw_get_token_id(service_token);
+ ldpp_dout(dpp, 20) << "service_token_id=" << service_token_id << dendl;
+
+ /* Check cache for service token first. */
+ st = token_cache.find(service_token_id);
+ if (st) {
+ ldpp_dout(dpp, 20) << "cached service_token.project.id=" << st->get_project_id()
+ << dendl;
+
+ /* We found the service token in the cache so we allow using an expired
+ * token for this request. */
+ allow_expired = true;
+ ldpp_dout(dpp, 20) << "allowing expired tokens because service_token_id="
+ << service_token_id
+ << " was found in cache" << dendl;
+ } else {
+ /* Service token was not found in cache. Go to Keystone for validating
+ * the token. The allow_expired here must always be false. */
+ st = get_from_keystone(dpp, service_token, allow_expired);
+
+ if (! st) {
+ return result_t::deny(-EACCES);
+ }
+
+ /* Verify expiration of service token. */
+ if (st->expired()) {
+ ldpp_dout(dpp, 0) << "got expired service token: " << st->get_project_name()
+ << ":" << st->get_user_name()
+ << " expired " << st->get_expires() << dendl;
+ return result_t::deny(-EPERM);
+ }
+
+ /* TODO(tobias-urdin): Verify role on service user with corresponding config option */
+
+ /* Service token is valid so we allow using an expired token for
+ * this request. */
+ ldpp_dout(dpp, 20) << "allowing expired tokens because service_token_id="
+ << service_token_id
+ << " is valid" << dendl;
+ allow_expired = true;
+ token_cache.add(service_token_id, *st);
+ }
+ }
+
+ /* Token not in cache. Go to the Keystone for validation. This happens even
* for the legacy PKI/PKIz token types. That's it, after the PKI/PKIz
* RadosGW-side validation has been removed, we always ask Keystone. */
- t = get_from_keystone(dpp, token);
+ t = get_from_keystone(dpp, token, allow_expired);
if (! t) {
return result_t::deny(-EACCES);
/* Verify expiration. */
if (t->expired()) {
- ldpp_dout(dpp, 0) << "got expired token: " << t->get_project_name()
- << ":" << t->get_user_name()
- << " expired: " << t->get_expires() << dendl;
- return result_t::deny(-EPERM);
+ if (allow_expired) {
+ ldpp_dout(dpp, 20) << "allowing expired token: " << t->get_project_name()
+ << ":" << t->get_user_name()
+ << " expired: " << t->get_expires()
+ << " because of valid service token" << dendl;
+ } else {
+ ldpp_dout(dpp, 0) << "got expired token: " << t->get_project_name()
+ << ":" << t->get_user_name()
+ << " expired: " << t->get_expires() << dendl;
+ return result_t::deny(-EPERM);
+ }
}
/* Check for necessary roles. */
bool is_applicable(const std::string& token) const noexcept;
boost::optional<token_envelope_t>
- get_from_keystone(const DoutPrefixProvider* dpp, const std::string& token) const;
+ get_from_keystone(const DoutPrefixProvider* dpp, const std::string& token, const bool& allow_expired) const;
acl_strategy_t get_acl_strategy(const token_envelope_t& token) const;
auth_info_t get_creds_info(const token_envelope_t& token,
) const noexcept;
result_t authenticate(const DoutPrefixProvider* dpp,
const std::string& token,
+ const std::string& service_token,
const req_state* s) const;
public:
result_t authenticate(const DoutPrefixProvider* dpp, const req_state* const s,
optional_yield y) const override {
- return authenticate(dpp, extractor->get_token(s), s);
+ return authenticate(dpp, extractor->get_token(s), extractor->get_service_token(s), s);
}
}; /* class TokenEngine */