]> git.proxmox.com Git - ceph.git/blame - ceph/src/rgw/rgw_auth_keystone.cc
import quincy 17.2.0
[ceph.git] / ceph / src / rgw / rgw_auth_keystone.cc
CommitLineData
7c673cae 1// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
9f95a23c 2// vim: ts=8 sw=2 smarttab ft=cpp
7c673cae
FG
3
4#include <string>
5#include <vector>
6
7#include <errno.h>
8#include <fnmatch.h>
9
10#include "rgw_b64.h"
11
12#include "common/errno.h"
13#include "common/ceph_json.h"
14#include "include/types.h"
15#include "include/str_list.h"
16
17#include "rgw_common.h"
18#include "rgw_keystone.h"
19#include "rgw_auth_keystone.h"
7c673cae
FG
20#include "rgw_rest_s3.h"
21#include "rgw_auth_s3.h"
22
9f95a23c 23#include "common/ceph_crypto.h"
7c673cae
FG
24#include "common/Cond.h"
25
26#define dout_subsys ceph_subsys_rgw
27
20effc67 28using namespace std;
7c673cae
FG
29
30namespace rgw {
31namespace auth {
32namespace keystone {
33
34bool
35TokenEngine::is_applicable(const std::string& token) const noexcept
36{
37 return ! token.empty() && ! cct->_conf->rgw_keystone_url.empty();
38}
39
7c673cae 40boost::optional<TokenEngine::token_envelope_t>
11fdf7f2 41TokenEngine::get_from_keystone(const DoutPrefixProvider* dpp, const std::string& token) const
7c673cae
FG
42{
43 /* Unfortunately, we can't use the short form of "using" here. It's because
44 * we're aliasing a class' member, not namespace. */
45 using RGWValidateKeystoneToken = \
46 rgw::keystone::Service::RGWValidateKeystoneToken;
47
48 /* The container for plain response obtained from Keystone. It will be
49 * parsed token_envelope_t::parse method. */
50 ceph::bufferlist token_body_bl;
11fdf7f2 51 RGWValidateKeystoneToken validate(cct, "GET", "", &token_body_bl);
7c673cae
FG
52
53 std::string url = config.get_endpoint_url();
54 if (url.empty()) {
55 throw -EINVAL;
56 }
57
58 const auto keystone_version = config.get_api_version();
59 if (keystone_version == rgw::keystone::ApiVersion::VER_2) {
60 url.append("v2.0/tokens/" + token);
61 } else if (keystone_version == rgw::keystone::ApiVersion::VER_3) {
62 url.append("v3/auth/tokens");
63 validate.append_header("X-Subject-Token", token);
64 }
65
66 std::string admin_token;
20effc67 67 if (rgw::keystone::Service::get_admin_token(dpp, cct, token_cache, config,
7c673cae
FG
68 admin_token) < 0) {
69 throw -EINVAL;
70 }
71
72 validate.append_header("X-Auth-Token", admin_token);
73 validate.set_send_length(0);
74
11fdf7f2
TL
75 validate.set_url(url);
76
9f95a23c 77 int ret = validate.process(null_yield);
7c673cae
FG
78 if (ret < 0) {
79 throw ret;
80 }
81
82 /* NULL terminate for debug output. */
83 token_body_bl.append(static_cast<char>(0));
7c673cae
FG
84
85 /* Detect Keystone rejection earlier than during the token parsing.
86 * Although failure at the parsing phase doesn't impose a threat,
87 * this allows to return proper error code (EACCESS instead of EINVAL
88 * or similar) and thus improves logging. */
89 if (validate.get_http_status() ==
90 /* Most likely: wrong admin credentials or admin token. */
91 RGWValidateKeystoneToken::HTTP_STATUS_UNAUTHORIZED ||
92 validate.get_http_status() ==
93 /* Most likely: non-existent token supplied by the client. */
94 RGWValidateKeystoneToken::HTTP_STATUS_NOTFOUND) {
11fdf7f2 95 ldpp_dout(dpp, 5) << "Failed keystone auth from " << url << " with "
b32b8144 96 << validate.get_http_status() << dendl;
7c673cae
FG
97 return boost::none;
98 }
99
11fdf7f2 100 ldpp_dout(dpp, 20) << "received response status=" << validate.get_http_status()
b32b8144
FG
101 << ", body=" << token_body_bl.c_str() << dendl;
102
7c673cae 103 TokenEngine::token_envelope_t token_body;
20effc67 104 ret = token_body.parse(dpp, cct, token, token_body_bl, config.get_api_version());
7c673cae
FG
105 if (ret < 0) {
106 throw ret;
107 }
108
109 return token_body;
110}
111
112TokenEngine::auth_info_t
113TokenEngine::get_creds_info(const TokenEngine::token_envelope_t& token,
114 const std::vector<std::string>& admin_roles
115 ) const noexcept
116{
117 using acct_privilege_t = rgw::auth::RemoteApplier::AuthInfo::acct_privilege_t;
118
119 /* Check whether the user has an admin status. */
120 acct_privilege_t level = acct_privilege_t::IS_PLAIN_ACCT;
121 for (const auto& admin_role : admin_roles) {
122 if (token.has_role(admin_role)) {
123 level = acct_privilege_t::IS_ADMIN_ACCT;
124 break;
125 }
126 }
127
128 return auth_info_t {
129 /* Suggested account name for the authenticated user. */
130 rgw_user(token.get_project_id()),
131 /* User's display name (aka real name). */
132 token.get_project_name(),
133 /* Keystone doesn't support RGW's subuser concept, so we cannot cut down
134 * the access rights through the perm_mask. At least at this layer. */
135 RGW_PERM_FULL_CONTROL,
136 level,
137 TYPE_KEYSTONE,
138 };
139}
140
141static inline const std::string
142make_spec_item(const std::string& tenant, const std::string& id)
143{
144 return tenant + ":" + id;
145}
146
147TokenEngine::acl_strategy_t
148TokenEngine::get_acl_strategy(const TokenEngine::token_envelope_t& token) const
149{
150 /* The primary identity is constructed upon UUIDs. */
151 const auto& tenant_uuid = token.get_project_id();
152 const auto& user_uuid = token.get_user_id();
153
154 /* For Keystone v2 an alias may be also used. */
155 const auto& tenant_name = token.get_project_name();
156 const auto& user_name = token.get_user_name();
157
158 /* Construct all possible combinations including Swift's wildcards. */
159 const std::array<std::string, 6> allowed_items = {
160 make_spec_item(tenant_uuid, user_uuid),
161 make_spec_item(tenant_name, user_name),
162
163 /* Wildcards. */
164 make_spec_item(tenant_uuid, "*"),
165 make_spec_item(tenant_name, "*"),
166 make_spec_item("*", user_uuid),
167 make_spec_item("*", user_name),
168 };
169
170 /* Lambda will obtain a copy of (not a reference to!) allowed_items. */
171 return [allowed_items](const rgw::auth::Identity::aclspec_t& aclspec) {
172 uint32_t perm = 0;
173
174 for (const auto& allowed_item : allowed_items) {
175 const auto iter = aclspec.find(allowed_item);
176
177 if (std::end(aclspec) != iter) {
178 perm |= iter->second;
179 }
180 }
181
182 return perm;
183 };
184}
185
186TokenEngine::result_t
11fdf7f2
TL
187TokenEngine::authenticate(const DoutPrefixProvider* dpp,
188 const std::string& token,
7c673cae
FG
189 const req_state* const s) const
190{
191 boost::optional<TokenEngine::token_envelope_t> t;
192
193 /* This will be initialized on the first call to this method. In C++11 it's
194 * also thread-safe. */
195 static const struct RolesCacher {
11fdf7f2 196 explicit RolesCacher(CephContext* const cct) {
7c673cae
FG
197 get_str_vec(cct->_conf->rgw_keystone_accepted_roles, plain);
198 get_str_vec(cct->_conf->rgw_keystone_accepted_admin_roles, admin);
199
200 /* Let's suppose that having an admin role implies also a regular one. */
201 plain.insert(std::end(plain), std::begin(admin), std::end(admin));
202 }
203
204 std::vector<std::string> plain;
205 std::vector<std::string> admin;
206 } roles(cct);
207
208 if (! is_applicable(token)) {
209 return result_t::deny();
210 }
211
9f95a23c
TL
212 /* Token ID is a legacy of supporting the service-side validation
213 * of PKI/PKIz token type which are already-removed-in-OpenStack.
214 * The idea was to bury in cache only a short hash instead of few
215 * kilobytes. RadosGW doesn't do the local validation anymore. */
7c673cae 216 const auto& token_id = rgw_get_token_id(token);
11fdf7f2 217 ldpp_dout(dpp, 20) << "token_id=" << token_id << dendl;
7c673cae
FG
218
219 /* Check cache first. */
220 t = token_cache.find(token_id);
221 if (t) {
11fdf7f2 222 ldpp_dout(dpp, 20) << "cached token.project.id=" << t->get_project_id()
7c673cae
FG
223 << dendl;
224 auto apl = apl_factory->create_apl_remote(cct, s, get_acl_strategy(*t),
225 get_creds_info(*t, roles.admin));
226 return result_t::grant(std::move(apl));
227 }
228
9f95a23c
TL
229 /* Not in cache. Go to the Keystone for validation. This happens even
230 * for the legacy PKI/PKIz token types. That's it, after the PKI/PKIz
231 * RadosGW-side validation has been removed, we always ask Keystone. */
232 t = get_from_keystone(dpp, token);
7c673cae
FG
233
234 if (! t) {
235 return result_t::deny(-EACCES);
236 }
237
238 /* Verify expiration. */
239 if (t->expired()) {
11fdf7f2 240 ldpp_dout(dpp, 0) << "got expired token: " << t->get_project_name()
7c673cae
FG
241 << ":" << t->get_user_name()
242 << " expired: " << t->get_expires() << dendl;
31f18b77 243 return result_t::deny(-EPERM);
7c673cae
FG
244 }
245
246 /* Check for necessary roles. */
247 for (const auto& role : roles.plain) {
248 if (t->has_role(role) == true) {
11fdf7f2 249 ldpp_dout(dpp, 0) << "validated token: " << t->get_project_name()
7c673cae
FG
250 << ":" << t->get_user_name()
251 << " expires: " << t->get_expires() << dendl;
252 token_cache.add(token_id, *t);
253 auto apl = apl_factory->create_apl_remote(cct, s, get_acl_strategy(*t),
254 get_creds_info(*t, roles.admin));
255 return result_t::grant(std::move(apl));
256 }
257 }
258
11fdf7f2
TL
259 ldpp_dout(dpp, 0) << "user does not hold a matching role; required roles: "
260 << g_conf()->rgw_keystone_accepted_roles << dendl;
7c673cae 261
31f18b77 262 return result_t::deny(-EPERM);
7c673cae
FG
263}
264
265
266/*
267 * Try to validate S3 auth against keystone s3token interface
268 */
269std::pair<boost::optional<rgw::keystone::TokenEnvelope>, int>
f67539c2 270EC2Engine::get_from_keystone(const DoutPrefixProvider* dpp, const std::string_view& access_key_id,
7c673cae 271 const std::string& string_to_sign,
f67539c2 272 const std::string_view& signature) const
7c673cae
FG
273{
274 /* prepare keystone url */
275 std::string keystone_url = config.get_endpoint_url();
276 if (keystone_url.empty()) {
277 throw -EINVAL;
278 }
279
280 const auto api_version = config.get_api_version();
9f95a23c 281 if (api_version == rgw::keystone::ApiVersion::VER_3) {
7c673cae
FG
282 keystone_url.append("v3/s3tokens");
283 } else {
284 keystone_url.append("v2.0/s3tokens");
285 }
286
287 /* get authentication token for Keystone. */
288 std::string admin_token;
20effc67 289 int ret = rgw::keystone::Service::get_admin_token(dpp, cct, token_cache, config,
7c673cae
FG
290 admin_token);
291 if (ret < 0) {
11fdf7f2 292 ldpp_dout(dpp, 2) << "s3 keystone: cannot get token for keystone access"
7c673cae
FG
293 << dendl;
294 throw ret;
295 }
296
297 using RGWValidateKeystoneToken
298 = rgw::keystone::Service::RGWValidateKeystoneToken;
299
300 /* The container for plain response obtained from Keystone. It will be
301 * parsed token_envelope_t::parse method. */
302 ceph::bufferlist token_body_bl;
11fdf7f2 303 RGWValidateKeystoneToken validate(cct, "POST", keystone_url, &token_body_bl);
7c673cae
FG
304
305 /* set required headers for keystone request */
306 validate.append_header("X-Auth-Token", admin_token);
307 validate.append_header("Content-Type", "application/json");
308
309 /* check if we want to verify keystone's ssl certs */
310 validate.set_verify_ssl(cct->_conf->rgw_keystone_verify_ssl);
311
312 /* create json credentials request body */
313 JSONFormatter credentials(false);
314 credentials.open_object_section("");
315 credentials.open_object_section("credentials");
31f18b77 316 credentials.dump_string("access", sview2cstr(access_key_id).data());
7c673cae 317 credentials.dump_string("token", rgw::to_base64(string_to_sign));
31f18b77 318 credentials.dump_string("signature", sview2cstr(signature).data());
7c673cae
FG
319 credentials.close_section();
320 credentials.close_section();
321
322 std::stringstream os;
323 credentials.flush(os);
324 validate.set_post_data(os.str());
325 validate.set_send_length(os.str().length());
326
327 /* send request */
9f95a23c 328 ret = validate.process(null_yield);
7c673cae 329 if (ret < 0) {
11fdf7f2 330 ldpp_dout(dpp, 2) << "s3 keystone: token validation ERROR: "
7c673cae
FG
331 << token_body_bl.c_str() << dendl;
332 throw ret;
333 }
334
335 /* if the supplied signature is wrong, we will get 401 from Keystone */
336 if (validate.get_http_status() ==
337 decltype(validate)::HTTP_STATUS_UNAUTHORIZED) {
338 return std::make_pair(boost::none, -ERR_SIGNATURE_NO_MATCH);
339 } else if (validate.get_http_status() ==
340 decltype(validate)::HTTP_STATUS_NOTFOUND) {
341 return std::make_pair(boost::none, -ERR_INVALID_ACCESS_KEY);
342 }
343
344 /* now parse response */
345 rgw::keystone::TokenEnvelope token_envelope;
20effc67 346 ret = token_envelope.parse(dpp, cct, std::string(), token_body_bl, api_version);
7c673cae 347 if (ret < 0) {
11fdf7f2 348 ldpp_dout(dpp, 2) << "s3 keystone: token parsing failed, ret=0" << ret
7c673cae
FG
349 << dendl;
350 throw ret;
351 }
352
353 return std::make_pair(std::move(token_envelope), 0);
354}
355
9f95a23c
TL
356std::pair<boost::optional<std::string>, int> EC2Engine::get_secret_from_keystone(const DoutPrefixProvider* dpp,
357 const std::string& user_id,
f67539c2 358 const std::string_view& access_key_id) const
9f95a23c
TL
359{
360 /* Fetch from /users/{USER_ID}/credentials/OS-EC2/{ACCESS_KEY_ID} */
361 /* Should return json with response key "credential" which contains entry "secret"*/
362
363 /* prepare keystone url */
364 std::string keystone_url = config.get_endpoint_url();
365 if (keystone_url.empty()) {
366 return make_pair(boost::none, -EINVAL);
367 }
368
369 const auto api_version = config.get_api_version();
370 if (api_version == rgw::keystone::ApiVersion::VER_3) {
371 keystone_url.append("v3/");
372 } else {
373 keystone_url.append("v2.0/");
374 }
375 keystone_url.append("users/");
376 keystone_url.append(user_id);
377 keystone_url.append("/credentials/OS-EC2/");
f67539c2 378 keystone_url.append(std::string(access_key_id));
9f95a23c
TL
379
380 /* get authentication token for Keystone. */
381 std::string admin_token;
20effc67 382 int ret = rgw::keystone::Service::get_admin_token(dpp, cct, token_cache, config,
9f95a23c
TL
383 admin_token);
384 if (ret < 0) {
385 ldpp_dout(dpp, 2) << "s3 keystone: cannot get token for keystone access"
386 << dendl;
387 return make_pair(boost::none, ret);
388 }
389
390 using RGWGetAccessSecret
391 = rgw::keystone::Service::RGWKeystoneHTTPTransceiver;
392
393 /* The container for plain response obtained from Keystone.*/
394 ceph::bufferlist token_body_bl;
395 RGWGetAccessSecret secret(cct, "GET", keystone_url, &token_body_bl);
396
397 /* set required headers for keystone request */
398 secret.append_header("X-Auth-Token", admin_token);
399
400 /* check if we want to verify keystone's ssl certs */
401 secret.set_verify_ssl(cct->_conf->rgw_keystone_verify_ssl);
402
403 /* send request */
404 ret = secret.process(null_yield);
405 if (ret < 0) {
406 ldpp_dout(dpp, 2) << "s3 keystone: secret fetching error: "
407 << token_body_bl.c_str() << dendl;
408 return make_pair(boost::none, ret);
409 }
410
411 /* if the supplied signature is wrong, we will get 401 from Keystone */
412 if (secret.get_http_status() ==
413 decltype(secret)::HTTP_STATUS_NOTFOUND) {
414 return make_pair(boost::none, -EINVAL);
415 }
416
417 /* now parse response */
418
419 JSONParser parser;
420 if (! parser.parse(token_body_bl.c_str(), token_body_bl.length())) {
421 ldpp_dout(dpp, 0) << "Keystone credential parse error: malformed json" << dendl;
422 return make_pair(boost::none, -EINVAL);
423 }
424
425 JSONObjIter credential_iter = parser.find_first("credential");
426 std::string secret_string;
427
428 try {
429 if (!credential_iter.end()) {
430 JSONDecoder::decode_json("secret", secret_string, *credential_iter, true);
431 } else {
432 ldpp_dout(dpp, 0) << "Keystone credential not present in return from server" << dendl;
433 return make_pair(boost::none, -EINVAL);
434 }
435 } catch (const JSONDecoder::err& err) {
436 ldpp_dout(dpp, 0) << "Keystone credential parse error: " << err.what() << dendl;
437 return make_pair(boost::none, -EINVAL);
438 }
439
440 return make_pair(secret_string, 0);
441}
442
443/*
444 * Try to get a token for S3 authentication, using a secret cache if available
445 */
446std::pair<boost::optional<rgw::keystone::TokenEnvelope>, int>
447EC2Engine::get_access_token(const DoutPrefixProvider* dpp,
f67539c2 448 const std::string_view& access_key_id,
9f95a23c 449 const std::string& string_to_sign,
f67539c2 450 const std::string_view& signature,
9f95a23c
TL
451 const signature_factory_t& signature_factory) const
452{
453 using server_signature_t = VersionAbstractor::server_signature_t;
454 boost::optional<rgw::keystone::TokenEnvelope> token;
455 int failure_reason;
456
457 /* Get a token from the cache if one has already been stored */
458 boost::optional<boost::tuple<rgw::keystone::TokenEnvelope, std::string>>
f67539c2 459 t = secret_cache.find(std::string(access_key_id));
9f95a23c
TL
460
461 /* Check that credentials can correctly be used to sign data */
462 if (t) {
463 std::string sig(signature);
464 server_signature_t server_signature = signature_factory(cct, t->get<1>(), string_to_sign);
465 if (sig.compare(server_signature) == 0) {
466 return std::make_pair(t->get<0>(), 0);
467 } else {
468 ldpp_dout(dpp, 0) << "Secret string does not correctly sign payload, cache miss" << dendl;
469 }
470 } else {
471 ldpp_dout(dpp, 0) << "No stored secret string, cache miss" << dendl;
472 }
473
474 /* No cached token, token expired, or secret invalid: fall back to keystone */
475 std::tie(token, failure_reason) = get_from_keystone(dpp, access_key_id, string_to_sign, signature);
476
477 if (token) {
478 /* Fetch secret from keystone for the access_key_id */
479 boost::optional<std::string> secret;
480 std::tie(secret, failure_reason) = get_secret_from_keystone(dpp, token->get_user_id(), access_key_id);
481
482 if (secret) {
483 /* Add token, secret pair to cache, and set timeout */
f67539c2 484 secret_cache.add(std::string(access_key_id), *token, *secret);
9f95a23c
TL
485 }
486 }
487
488 return std::make_pair(token, failure_reason);
489}
490
7c673cae
FG
491EC2Engine::acl_strategy_t
492EC2Engine::get_acl_strategy(const EC2Engine::token_envelope_t&) const
493{
494 /* This is based on the assumption that the default acl strategy in
495 * get_perms_from_aclspec, will take care. Extra acl spec is not required. */
496 return nullptr;
497}
498
499EC2Engine::auth_info_t
500EC2Engine::get_creds_info(const EC2Engine::token_envelope_t& token,
501 const std::vector<std::string>& admin_roles
502 ) const noexcept
503{
504 using acct_privilege_t = \
505 rgw::auth::RemoteApplier::AuthInfo::acct_privilege_t;
506
507 /* Check whether the user has an admin status. */
508 acct_privilege_t level = acct_privilege_t::IS_PLAIN_ACCT;
509 for (const auto& admin_role : admin_roles) {
510 if (token.has_role(admin_role)) {
511 level = acct_privilege_t::IS_ADMIN_ACCT;
512 break;
513 }
514 }
515
516 return auth_info_t {
517 /* Suggested account name for the authenticated user. */
518 rgw_user(token.get_project_id()),
519 /* User's display name (aka real name). */
520 token.get_project_name(),
521 /* Keystone doesn't support RGW's subuser concept, so we cannot cut down
522 * the access rights through the perm_mask. At least at this layer. */
523 RGW_PERM_FULL_CONTROL,
524 level,
525 TYPE_KEYSTONE,
526 };
527}
528
31f18b77 529rgw::auth::Engine::result_t EC2Engine::authenticate(
11fdf7f2 530 const DoutPrefixProvider* dpp,
f67539c2
TL
531 const std::string_view& access_key_id,
532 const std::string_view& signature,
533 const std::string_view& session_token,
31f18b77 534 const string_to_sign_t& string_to_sign,
9f95a23c 535 const signature_factory_t& signature_factory,
31f18b77
FG
536 const completer_factory_t& completer_factory,
537 /* Passthorugh only! */
f67539c2
TL
538 const req_state* s,
539 optional_yield y) const
7c673cae
FG
540{
541 /* This will be initialized on the first call to this method. In C++11 it's
542 * also thread-safe. */
543 static const struct RolesCacher {
11fdf7f2 544 explicit RolesCacher(CephContext* const cct) {
7c673cae
FG
545 get_str_vec(cct->_conf->rgw_keystone_accepted_roles, plain);
546 get_str_vec(cct->_conf->rgw_keystone_accepted_admin_roles, admin);
547
548 /* Let's suppose that having an admin role implies also a regular one. */
549 plain.insert(std::end(plain), std::begin(admin), std::end(admin));
550 }
551
552 std::vector<std::string> plain;
553 std::vector<std::string> admin;
554 } accepted_roles(cct);
555
556 boost::optional<token_envelope_t> t;
557 int failure_reason;
558 std::tie(t, failure_reason) = \
9f95a23c 559 get_access_token(dpp, access_key_id, string_to_sign, signature, signature_factory);
7c673cae
FG
560 if (! t) {
561 return result_t::deny(failure_reason);
562 }
563
564 /* Verify expiration. */
565 if (t->expired()) {
11fdf7f2 566 ldpp_dout(dpp, 0) << "got expired token: " << t->get_project_name()
7c673cae
FG
567 << ":" << t->get_user_name()
568 << " expired: " << t->get_expires() << dendl;
569 return result_t::deny();
570 }
571
572 /* check if we have a valid role */
573 bool found = false;
574 for (const auto& role : accepted_roles.plain) {
575 if (t->has_role(role) == true) {
576 found = true;
577 break;
578 }
579 }
580
581 if (! found) {
11fdf7f2 582 ldpp_dout(dpp, 5) << "s3 keystone: user does not hold a matching role;"
7c673cae
FG
583 " required roles: "
584 << cct->_conf->rgw_keystone_accepted_roles << dendl;
585 return result_t::deny();
586 } else {
587 /* everything seems fine, continue with this user */
11fdf7f2 588 ldpp_dout(dpp, 5) << "s3 keystone: validated token: " << t->get_project_name()
7c673cae
FG
589 << ":" << t->get_user_name()
590 << " expires: " << t->get_expires() << dendl;
591
592 auto apl = apl_factory->create_apl_remote(cct, s, get_acl_strategy(*t),
593 get_creds_info(*t, accepted_roles.admin));
31f18b77 594 return result_t::grant(std::move(apl), completer_factory(boost::none));
7c673cae
FG
595 }
596}
597
9f95a23c
TL
598bool SecretCache::find(const std::string& token_id,
599 SecretCache::token_envelope_t& token,
600 std::string &secret)
601{
602 std::lock_guard<std::mutex> l(lock);
603
604 map<std::string, secret_entry>::iterator iter = secrets.find(token_id);
605 if (iter == secrets.end()) {
606 return false;
607 }
608
609 secret_entry& entry = iter->second;
610 secrets_lru.erase(entry.lru_iter);
611
612 const utime_t now = ceph_clock_now();
613 if (entry.token.expired() || now > entry.expires) {
614 secrets.erase(iter);
615 return false;
616 }
617 token = entry.token;
618 secret = entry.secret;
619
620 secrets_lru.push_front(token_id);
621 entry.lru_iter = secrets_lru.begin();
622
623 return true;
624}
625
626void SecretCache::add(const std::string& token_id,
627 const SecretCache::token_envelope_t& token,
628 const std::string& secret)
629{
630 std::lock_guard<std::mutex> l(lock);
631
632 map<string, secret_entry>::iterator iter = secrets.find(token_id);
633 if (iter != secrets.end()) {
634 secret_entry& e = iter->second;
635 secrets_lru.erase(e.lru_iter);
636 }
637
638 const utime_t now = ceph_clock_now();
639 secrets_lru.push_front(token_id);
640 secret_entry& entry = secrets[token_id];
641 entry.token = token;
642 entry.secret = secret;
643 entry.expires = now + s3_token_expiry_length;
644 entry.lru_iter = secrets_lru.begin();
645
646 while (secrets_lru.size() > max) {
647 list<string>::reverse_iterator riter = secrets_lru.rbegin();
648 iter = secrets.find(*riter);
649 assert(iter != secrets.end());
650 secrets.erase(iter);
651 secrets_lru.pop_back();
652 }
653}
654
7c673cae
FG
655}; /* namespace keystone */
656}; /* namespace auth */
657}; /* namespace rgw */