]>
Commit | Line | Data |
---|---|---|
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 | 3 | |
1e59de90 | 4 | #pragma once |
7c673cae FG |
5 | |
6 | #include <functional> | |
20effc67 | 7 | #include <optional> |
7c673cae FG |
8 | #include <ostream> |
9 | #include <type_traits> | |
10 | #include <system_error> | |
11 | #include <utility> | |
12 | ||
13 | #include "rgw_common.h" | |
11fdf7f2 | 14 | #include "rgw_web_idp.h" |
7c673cae FG |
15 | |
16 | #define RGW_USER_ANON_ID "anonymous" | |
17 | ||
9f95a23c | 18 | class RGWCtl; |
2a845540 | 19 | struct rgw_log_entry; |
1e59de90 | 20 | struct req_state; |
9f95a23c | 21 | |
7c673cae FG |
22 | namespace rgw { |
23 | namespace auth { | |
24 | ||
25 | using Exception = std::system_error; | |
26 | ||
27 | ||
28 | /* Load information about identity that will be used by RGWOp to authorize | |
29 | * any operation that comes from an authenticated user. */ | |
30 | class Identity { | |
31 | public: | |
32 | typedef std::map<std::string, int> aclspec_t; | |
31f18b77 | 33 | using idset_t = boost::container::flat_set<Principal>; |
7c673cae FG |
34 | |
35 | virtual ~Identity() = default; | |
36 | ||
37 | /* Translate the ACL provided in @aclspec into concrete permission set that | |
38 | * can be used during the authorization phase (RGWOp::verify_permission). | |
39 | * On error throws rgw::auth::Exception storing the reason. | |
40 | * | |
41 | * NOTE: an implementation is responsible for giving the real semantic to | |
42 | * the items in @aclspec. That is, their meaning may depend on particular | |
43 | * applier that is being used. */ | |
11fdf7f2 | 44 | virtual uint32_t get_perms_from_aclspec(const DoutPrefixProvider* dpp, const aclspec_t& aclspec) const = 0; |
7c673cae FG |
45 | |
46 | /* Verify whether a given identity *can be treated as* an admin of rgw_user | |
47 | * (account in Swift's terminology) specified in @uid. On error throws | |
48 | * rgw::auth::Exception storing the reason. */ | |
49 | virtual bool is_admin_of(const rgw_user& uid) const = 0; | |
50 | ||
51 | /* Verify whether a given identity *is* the owner of the rgw_user (account | |
52 | * in the Swift's terminology) specified in @uid. On internal error throws | |
53 | * rgw::auth::Exception storing the reason. */ | |
54 | virtual bool is_owner_of(const rgw_user& uid) const = 0; | |
55 | ||
56 | /* Return the permission mask that is used to narrow down the set of | |
57 | * operations allowed for a given identity. This method reflects the idea | |
58 | * of subuser tied to RGWUserInfo. On error throws rgw::auth::Exception | |
59 | * with the reason. */ | |
60 | virtual uint32_t get_perm_mask() const = 0; | |
61 | ||
f91f0fd5 | 62 | virtual bool is_anonymous() const { |
7c673cae FG |
63 | /* If the identity owns the anonymous account (rgw_user), it's considered |
64 | * the anonymous identity. On error throws rgw::auth::Exception storing | |
65 | * the reason. */ | |
66 | return is_owner_of(rgw_user(RGW_USER_ANON_ID)); | |
67 | } | |
68 | ||
69 | virtual void to_str(std::ostream& out) const = 0; | |
31f18b77 FG |
70 | |
71 | /* Verify whether a given identity corresponds to an identity in the | |
72 | provided set */ | |
73 | virtual bool is_identity(const idset_t& ids) const = 0; | |
11fdf7f2 TL |
74 | |
75 | /* Identity Type: RGW/ LDAP/ Keystone */ | |
76 | virtual uint32_t get_identity_type() const = 0; | |
77 | ||
78 | /* Name of Account */ | |
20effc67 | 79 | virtual std::string get_acct_name() const = 0; |
f6b5b4d7 TL |
80 | |
81 | /* Subuser of Account */ | |
20effc67 | 82 | virtual std::string get_subuser() const = 0; |
f67539c2 | 83 | |
20effc67 | 84 | virtual std::string get_role_tenant() const { return ""; } |
2a845540 TL |
85 | |
86 | /* write any auth-specific fields that are safe to expose in the ops log */ | |
87 | virtual void write_ops_log_entry(rgw_log_entry& entry) const {}; | |
7c673cae FG |
88 | }; |
89 | ||
90 | inline std::ostream& operator<<(std::ostream& out, | |
91 | const rgw::auth::Identity& id) { | |
92 | id.to_str(out); | |
93 | return out; | |
94 | } | |
95 | ||
96 | ||
9f95a23c TL |
97 | std::unique_ptr<rgw::auth::Identity> |
98 | transform_old_authinfo(CephContext* const cct, | |
99 | const rgw_user& auth_id, | |
100 | const int perm_mask, | |
101 | const bool is_admin, | |
102 | const uint32_t type); | |
7c673cae FG |
103 | std::unique_ptr<Identity> transform_old_authinfo(const req_state* const s); |
104 | ||
105 | ||
106 | /* Interface for classes applying changes to request state/RADOS store | |
107 | * imposed by a particular rgw::auth::Engine. | |
108 | * | |
109 | * In contrast to rgw::auth::Engine, implementations of this interface | |
9f95a23c | 110 | * are allowed to handle req_state or RGWUserCtl in the read-write manner. |
7c673cae FG |
111 | * |
112 | * It's expected that most (if not all) of implementations will also | |
113 | * conform to rgw::auth::Identity interface to provide authorization | |
114 | * policy (ACLs, account's ownership and entitlement). */ | |
115 | class IdentityApplier : public Identity { | |
116 | public: | |
117 | typedef std::unique_ptr<IdentityApplier> aplptr_t; | |
118 | ||
119 | virtual ~IdentityApplier() {}; | |
120 | ||
121 | /* Fill provided RGWUserInfo with information about the account that | |
122 | * RGWOp will operate on. Errors are handled solely through exceptions. | |
123 | * | |
124 | * XXX: be aware that the "account" term refers to rgw_user. The naming | |
125 | * is legacy. */ | |
11fdf7f2 | 126 | virtual void load_acct_info(const DoutPrefixProvider* dpp, RGWUserInfo& user_info) const = 0; /* out */ |
7c673cae FG |
127 | |
128 | /* Apply any changes to request state. This method will be most useful for | |
31f18b77 | 129 | * TempURL of Swift API. */ |
11fdf7f2 | 130 | virtual void modify_request_state(const DoutPrefixProvider* dpp, req_state* s) const {} /* in/out */ |
7c673cae FG |
131 | }; |
132 | ||
133 | ||
134 | /* Interface class for completing the two-step authentication process. | |
135 | * Completer provides the second step - the complete() method that should | |
136 | * be called after Engine::authenticate() but before *committing* results | |
137 | * of an RGWOp (or sending a response in the case of non-mutating ops). | |
138 | * | |
139 | * The motivation driving the interface is to address those authentication | |
140 | * schemas that require message integrity verification *without* in-memory | |
141 | * data buffering. Typical examples are AWS Auth v4 and the auth mechanism | |
142 | * of browser uploads facilities both in S3 and Swift APIs (see RGWPostObj). | |
143 | * The workflow of request from the authentication point-of-view does look | |
144 | * like following one: | |
145 | * A. authenticate (Engine::authenticate), | |
146 | * B. authorize (see RGWOp::verify_permissions), | |
147 | * C. execute-prepare (init potential data modifications), | |
148 | * D. authenticate-complete - (Completer::complete), | |
149 | * E. execute-commit - commit the modifications from point C. */ | |
150 | class Completer { | |
151 | public: | |
31f18b77 FG |
152 | /* It's expected that Completers would tend to implement many interfaces |
153 | * and be used not only in req_state::auth::completer. Ref counting their | |
11fdf7f2 | 154 | * instances would be helpful. */ |
31f18b77 | 155 | typedef std::shared_ptr<Completer> cmplptr_t; |
7c673cae FG |
156 | |
157 | virtual ~Completer() = default; | |
158 | ||
159 | /* Complete the authentication process. Return boolean indicating whether | |
160 | * the completion succeeded. On error throws rgw::auth::Exception storing | |
161 | * the reason. */ | |
162 | virtual bool complete() = 0; | |
31f18b77 FG |
163 | |
164 | /* Apply any changes to request state. The initial use case was injecting | |
165 | * the AWSv4 filter over rgw::io::RestfulClient in req_state. */ | |
11fdf7f2 | 166 | virtual void modify_request_state(const DoutPrefixProvider* dpp, req_state* s) = 0; /* in/out */ |
7c673cae FG |
167 | }; |
168 | ||
169 | ||
170 | /* Interface class for authentication backends (auth engines) in RadosGW. | |
171 | * | |
172 | * An engine is supposed only to authenticate (not authorize!) requests | |
173 | * basing on their req_state and - if access has been granted - provide | |
174 | * an upper layer with: | |
175 | * - rgw::auth::IdentityApplier to commit all changes to the request state as | |
176 | * well as to the RADOS store (creating an account, synchronizing | |
177 | * user-related information with external databases and so on). | |
178 | * - rgw::auth::Completer (optionally) to finish the authentication | |
179 | * of the request. Typical use case is verifying message integrity | |
180 | * in AWS Auth v4 and browser uploads (RGWPostObj). | |
181 | * | |
182 | * Both of them are supposed to be wrapped in Engine::AuthResult. | |
183 | * | |
184 | * The authentication process consists of two steps: | |
185 | * - Engine::authenticate() which should be called before *initiating* | |
186 | * any modifications to RADOS store that are related to an operation | |
187 | * a client wants to perform (RGWOp::execute). | |
188 | * - Completer::complete() supposed to be called, if completer has been | |
189 | * returned, after the authenticate() step but before *committing* | |
190 | * those modifications or sending a response (RGWOp::complete). | |
191 | * | |
192 | * An engine outlives both Applier and Completer. It's intended to live | |
193 | * since RadosGW's initialization and handle multiple requests till | |
194 | * a reconfiguration. | |
195 | * | |
196 | * Auth engine MUST NOT make any changes to req_state nor RADOS store. | |
197 | * This is solely an Applier's responsibility! | |
198 | * | |
199 | * Separation between authentication and global state modification has | |
200 | * been introduced because many auth engines are orthogonal to appliers | |
201 | * and thus they can be decoupled. Additional motivation is to clearly | |
202 | * distinguish all portions of code modifying data structures. */ | |
203 | class Engine { | |
204 | public: | |
205 | virtual ~Engine() = default; | |
206 | ||
207 | class AuthResult { | |
208 | struct rejection_mark_t {}; | |
209 | bool is_rejected = false; | |
210 | int reason = 0; | |
211 | ||
212 | std::pair<IdentityApplier::aplptr_t, Completer::cmplptr_t> result_pair; | |
213 | ||
11fdf7f2 | 214 | explicit AuthResult(const int reason) |
7c673cae FG |
215 | : reason(reason) { |
216 | } | |
217 | ||
218 | AuthResult(rejection_mark_t&&, const int reason) | |
219 | : is_rejected(true), | |
220 | reason(reason) { | |
221 | } | |
222 | ||
223 | /* Allow only the reasonable combintations - returning just Completer | |
224 | * without accompanying IdentityApplier is strictly prohibited! */ | |
11fdf7f2 | 225 | explicit AuthResult(IdentityApplier::aplptr_t&& applier) |
7c673cae FG |
226 | : result_pair(std::move(applier), nullptr) { |
227 | } | |
228 | ||
229 | AuthResult(IdentityApplier::aplptr_t&& applier, | |
230 | Completer::cmplptr_t&& completer) | |
231 | : result_pair(std::move(applier), std::move(completer)) { | |
232 | } | |
233 | ||
234 | public: | |
235 | enum class Status { | |
236 | /* Engine doesn't grant the access but also doesn't reject it. */ | |
237 | DENIED, | |
238 | ||
11fdf7f2 | 239 | /* Engine successfully authenicated requester. */ |
7c673cae FG |
240 | GRANTED, |
241 | ||
242 | /* Engine strictly indicates that a request should be rejected | |
243 | * without trying any further engine. */ | |
244 | REJECTED | |
245 | }; | |
246 | ||
247 | Status get_status() const { | |
248 | if (is_rejected) { | |
249 | return Status::REJECTED; | |
250 | } else if (! result_pair.first) { | |
251 | return Status::DENIED; | |
252 | } else { | |
253 | return Status::GRANTED; | |
254 | } | |
255 | } | |
256 | ||
257 | int get_reason() const { | |
258 | return reason; | |
259 | } | |
260 | ||
261 | IdentityApplier::aplptr_t get_applier() { | |
262 | return std::move(result_pair.first); | |
263 | } | |
264 | ||
265 | Completer::cmplptr_t&& get_completer() { | |
266 | return std::move(result_pair.second); | |
267 | } | |
268 | ||
269 | static AuthResult reject(const int reason = -EACCES) { | |
270 | return AuthResult(rejection_mark_t(), reason); | |
271 | } | |
272 | ||
273 | static AuthResult deny(const int reason = -EACCES) { | |
274 | return AuthResult(reason); | |
275 | } | |
276 | ||
277 | static AuthResult grant(IdentityApplier::aplptr_t&& applier) { | |
278 | return AuthResult(std::move(applier)); | |
279 | } | |
280 | ||
281 | static AuthResult grant(IdentityApplier::aplptr_t&& applier, | |
282 | Completer::cmplptr_t&& completer) { | |
283 | return AuthResult(std::move(applier), std::move(completer)); | |
284 | } | |
285 | }; | |
286 | ||
287 | using result_t = AuthResult; | |
288 | ||
289 | /* Get name of the auth engine. */ | |
290 | virtual const char* get_name() const noexcept = 0; | |
291 | ||
292 | /* Throwing method for identity verification. When the check is positive | |
293 | * an implementation should return Engine::result_t containing: | |
294 | * - a non-null pointer to an object conforming the Applier interface. | |
295 | * Otherwise, the authentication is treated as failed. | |
296 | * - a (potentially null) pointer to an object conforming the Completer | |
297 | * interface. | |
298 | * | |
299 | * On error throws rgw::auth::Exception containing the reason. */ | |
f67539c2 | 300 | virtual result_t authenticate(const DoutPrefixProvider* dpp, const req_state* s, optional_yield y) const = 0; |
7c673cae FG |
301 | }; |
302 | ||
303 | ||
304 | /* Interface for extracting a token basing from data carried by req_state. */ | |
305 | class TokenExtractor { | |
306 | public: | |
307 | virtual ~TokenExtractor() = default; | |
308 | virtual std::string get_token(const req_state* s) const = 0; | |
309 | }; | |
310 | ||
311 | ||
312 | /* Abstract class for stacking sub-engines to expose them as a single | |
313 | * Engine. It is responsible for ordering its sub-engines and managing | |
314 | * fall-backs between them. Derivatee is supposed to encapsulate engine | |
315 | * instances and add them using the add_engine() method in the order it | |
316 | * wants to be tried during the call to authenticate(). | |
317 | * | |
318 | * Each new Strategy should be exposed to StrategyRegistry for handling | |
319 | * the dynamic reconfiguration. */ | |
320 | class Strategy : public Engine { | |
321 | public: | |
322 | /* Specifiers controlling what happens when an associated engine fails. | |
323 | * The names and semantic has been borrowed mostly from libpam. */ | |
324 | enum class Control { | |
325 | /* Failure of an engine injected with the REQUISITE specifier aborts | |
326 | * the strategy's authentication process immediately. No other engine | |
327 | * will be tried. */ | |
328 | REQUISITE, | |
329 | ||
330 | /* Success of an engine injected with the SUFFICIENT specifier ends | |
331 | * strategy's authentication process successfully. However, denying | |
332 | * doesn't abort it -- there will be fall-back to following engine | |
333 | * if the one that failed wasn't the last one. */ | |
334 | SUFFICIENT, | |
335 | ||
336 | /* Like SUFFICIENT with the exception that on failure the reason code | |
337 | * is not overridden. Instead, it's taken directly from the last tried | |
338 | * non-FALLBACK engine. If there was no previous non-FALLBACK engine | |
339 | * in a Strategy, then the result_t::deny(reason = -EACCES) is used. */ | |
340 | FALLBACK, | |
341 | }; | |
342 | ||
f67539c2 | 343 | Engine::result_t authenticate(const DoutPrefixProvider* dpp, const req_state* s, optional_yield y) const override final; |
7c673cae FG |
344 | |
345 | bool is_empty() const { | |
346 | return auth_stack.empty(); | |
347 | } | |
348 | ||
f67539c2 | 349 | static int apply(const DoutPrefixProvider* dpp, const Strategy& auth_strategy, req_state* s, optional_yield y) noexcept; |
31f18b77 | 350 | |
7c673cae FG |
351 | private: |
352 | /* Using the reference wrapper here to explicitly point out we are not | |
353 | * interested in storing nulls while preserving the dynamic polymorphism. */ | |
354 | using stack_item_t = std::pair<std::reference_wrapper<const Engine>, | |
355 | Control>; | |
356 | std::vector<stack_item_t> auth_stack; | |
357 | ||
358 | protected: | |
359 | void add_engine(Control ctrl_flag, const Engine& engine) noexcept; | |
360 | }; | |
361 | ||
362 | ||
363 | /* A class aggregating the knowledge about all Strategies in RadosGW. It is | |
364 | * responsible for handling the dynamic reconfiguration on e.g. realm update. | |
365 | * The definition is in rgw/rgw_auth_registry.h, | |
366 | * | |
367 | * Each new Strategy should be exposed to it. */ | |
368 | class StrategyRegistry; | |
369 | ||
11fdf7f2 | 370 | class WebIdentityApplier : public IdentityApplier { |
20effc67 TL |
371 | std::string sub; |
372 | std::string iss; | |
373 | std::string aud; | |
374 | std::string client_id; | |
375 | std::string user_name; | |
11fdf7f2 TL |
376 | protected: |
377 | CephContext* const cct; | |
1e59de90 | 378 | rgw::sal::Driver* driver; |
20effc67 TL |
379 | std::string role_session; |
380 | std::string role_tenant; | |
381 | std::unordered_multimap<std::string, std::string> token_claims; | |
382 | boost::optional<std::multimap<std::string,std::string>> role_tags; | |
383 | boost::optional<std::set<std::pair<std::string, std::string>>> principal_tags; | |
11fdf7f2 | 384 | |
20effc67 | 385 | std::string get_idp_url() const; |
11fdf7f2 | 386 | |
f67539c2 TL |
387 | void create_account(const DoutPrefixProvider* dpp, |
388 | const rgw_user& acct_user, | |
20effc67 | 389 | const std::string& display_name, |
f67539c2 | 390 | RGWUserInfo& user_info) const; /* out */ |
28e407b8 | 391 | public: |
11fdf7f2 | 392 | WebIdentityApplier( CephContext* const cct, |
1e59de90 | 393 | rgw::sal::Driver* driver, |
20effc67 TL |
394 | const std::string& role_session, |
395 | const std::string& role_tenant, | |
396 | const std::unordered_multimap<std::string, std::string>& token_claims, | |
397 | boost::optional<std::multimap<std::string,std::string>> role_tags, | |
398 | boost::optional<std::set<std::pair<std::string, std::string>>> principal_tags) | |
399 | : cct(cct), | |
1e59de90 | 400 | driver(driver), |
f91f0fd5 | 401 | role_session(role_session), |
f67539c2 | 402 | role_tenant(role_tenant), |
20effc67 TL |
403 | token_claims(token_claims), |
404 | role_tags(role_tags), | |
405 | principal_tags(principal_tags) { | |
406 | const auto& sub = token_claims.find("sub"); | |
407 | if(sub != token_claims.end()) { | |
408 | this->sub = sub->second; | |
409 | } | |
410 | ||
411 | const auto& iss = token_claims.find("iss"); | |
412 | if(iss != token_claims.end()) { | |
413 | this->iss = iss->second; | |
414 | } | |
415 | ||
416 | const auto& aud = token_claims.find("aud"); | |
417 | if(aud != token_claims.end()) { | |
418 | this->aud = aud->second; | |
419 | } | |
420 | ||
421 | const auto& client_id = token_claims.find("client_id"); | |
422 | if(client_id != token_claims.end()) { | |
423 | this->client_id = client_id->second; | |
424 | } else { | |
425 | const auto& azp = token_claims.find("azp"); | |
426 | if (azp != token_claims.end()) { | |
427 | this->client_id = azp->second; | |
428 | } | |
429 | } | |
430 | ||
431 | const auto& user_name = token_claims.find("username"); | |
432 | if(user_name != token_claims.end()) { | |
433 | this->user_name = user_name->second; | |
434 | } else { | |
435 | const auto& given_username = token_claims.find("given_username"); | |
436 | if (given_username != token_claims.end()) { | |
437 | this->user_name = given_username->second; | |
438 | } | |
439 | } | |
28e407b8 | 440 | } |
28e407b8 | 441 | |
11fdf7f2 TL |
442 | void modify_request_state(const DoutPrefixProvider *dpp, req_state* s) const override; |
443 | ||
444 | uint32_t get_perms_from_aclspec(const DoutPrefixProvider* dpp, const aclspec_t& aclspec) const override { | |
445 | return RGW_PERM_NONE; | |
446 | } | |
447 | ||
448 | bool is_admin_of(const rgw_user& uid) const override { | |
449 | return false; | |
450 | } | |
451 | ||
452 | bool is_owner_of(const rgw_user& uid) const override { | |
20effc67 | 453 | if (uid.id == this->sub && uid.tenant == role_tenant && uid.ns == "oidc") { |
f67539c2 TL |
454 | return true; |
455 | } | |
11fdf7f2 TL |
456 | return false; |
457 | } | |
458 | ||
459 | uint32_t get_perm_mask() const override { | |
460 | return RGW_PERM_NONE; | |
461 | } | |
462 | ||
463 | void to_str(std::ostream& out) const override; | |
464 | ||
465 | bool is_identity(const idset_t& ids) const override; | |
466 | ||
f67539c2 TL |
467 | void load_acct_info(const DoutPrefixProvider* dpp, RGWUserInfo& user_info) const override; |
468 | ||
11fdf7f2 TL |
469 | uint32_t get_identity_type() const override { |
470 | return TYPE_WEB; | |
471 | } | |
472 | ||
20effc67 TL |
473 | std::string get_acct_name() const override { |
474 | return this->user_name; | |
11fdf7f2 TL |
475 | } |
476 | ||
20effc67 | 477 | std::string get_subuser() const override { |
f6b5b4d7 TL |
478 | return {}; |
479 | } | |
480 | ||
11fdf7f2 TL |
481 | struct Factory { |
482 | virtual ~Factory() {} | |
483 | ||
484 | virtual aplptr_t create_apl_web_identity( CephContext* cct, | |
485 | const req_state* s, | |
20effc67 TL |
486 | const std::string& role_session, |
487 | const std::string& role_tenant, | |
488 | const std::unordered_multimap<std::string, std::string>& token, | |
489 | boost::optional<std::multimap<std::string, std::string>>, | |
490 | boost::optional<std::set<std::pair<std::string, std::string>>> principal_tags) const = 0; | |
11fdf7f2 TL |
491 | }; |
492 | }; | |
28e407b8 | 493 | |
9f95a23c TL |
494 | class ImplicitTenants: public md_config_obs_t { |
495 | public: | |
496 | enum implicit_tenant_flag_bits {IMPLICIT_TENANTS_SWIFT=1, | |
497 | IMPLICIT_TENANTS_S3=2, IMPLICIT_TENANTS_BAD = -1, }; | |
498 | private: | |
499 | int saved; | |
500 | void recompute_value(const ConfigProxy& ); | |
501 | class ImplicitTenantValue { | |
502 | friend class ImplicitTenants; | |
503 | int v; | |
504 | ImplicitTenantValue(int v) : v(v) {}; | |
505 | public: | |
506 | bool inline is_split_mode() | |
507 | { | |
508 | assert(v != IMPLICIT_TENANTS_BAD); | |
509 | return v == IMPLICIT_TENANTS_SWIFT || v == IMPLICIT_TENANTS_S3; | |
510 | } | |
511 | bool inline implicit_tenants_for_(const implicit_tenant_flag_bits bit) | |
512 | { | |
513 | assert(v != IMPLICIT_TENANTS_BAD); | |
514 | return static_cast<bool>(v&bit); | |
515 | } | |
516 | }; | |
517 | public: | |
518 | ImplicitTenants(const ConfigProxy& c) { recompute_value(c);} | |
1e59de90 | 519 | ImplicitTenantValue get_value() const { |
9f95a23c TL |
520 | return ImplicitTenantValue(saved); |
521 | } | |
522 | private: | |
523 | const char** get_tracked_conf_keys() const override; | |
524 | void handle_conf_change(const ConfigProxy& conf, | |
525 | const std::set <std::string> &changed) override; | |
526 | }; | |
527 | ||
528 | std::tuple<bool,bool> implicit_tenants_enabled_for_swift(CephContext * const cct); | |
529 | std::tuple<bool,bool> implicit_tenants_enabled_for_s3(CephContext * const cct); | |
530 | ||
7c673cae FG |
531 | /* rgw::auth::RemoteApplier targets those authentication engines which don't |
532 | * need to ask the RADOS store while performing the auth process. Instead, | |
533 | * they obtain credentials from an external source like Keystone or LDAP. | |
534 | * | |
535 | * As the authenticated user may not have an account yet, RGWRemoteAuthApplier | |
536 | * must be able to create it basing on data passed by an auth engine. Those | |
537 | * data will be used to fill RGWUserInfo structure. */ | |
538 | class RemoteApplier : public IdentityApplier { | |
539 | public: | |
540 | class AuthInfo { | |
541 | friend class RemoteApplier; | |
542 | protected: | |
543 | const rgw_user acct_user; | |
544 | const std::string acct_name; | |
545 | const uint32_t perm_mask; | |
546 | const bool is_admin; | |
547 | const uint32_t acct_type; | |
2a845540 TL |
548 | const std::string access_key_id; |
549 | const std::string subuser; | |
7c673cae FG |
550 | |
551 | public: | |
552 | enum class acct_privilege_t { | |
553 | IS_ADMIN_ACCT, | |
554 | IS_PLAIN_ACCT | |
555 | }; | |
556 | ||
2a845540 TL |
557 | static const std::string NO_SUBUSER; |
558 | static const std::string NO_ACCESS_KEY; | |
559 | ||
7c673cae FG |
560 | AuthInfo(const rgw_user& acct_user, |
561 | const std::string& acct_name, | |
562 | const uint32_t perm_mask, | |
563 | const acct_privilege_t level, | |
2a845540 TL |
564 | const std::string access_key_id, |
565 | const std::string subuser, | |
7c673cae FG |
566 | const uint32_t acct_type=TYPE_NONE) |
567 | : acct_user(acct_user), | |
568 | acct_name(acct_name), | |
569 | perm_mask(perm_mask), | |
570 | is_admin(acct_privilege_t::IS_ADMIN_ACCT == level), | |
2a845540 TL |
571 | acct_type(acct_type), |
572 | access_key_id(access_key_id), | |
573 | subuser(subuser) { | |
7c673cae FG |
574 | } |
575 | }; | |
576 | ||
577 | using aclspec_t = rgw::auth::Identity::aclspec_t; | |
578 | typedef std::function<uint32_t(const aclspec_t&)> acl_strategy_t; | |
579 | ||
580 | protected: | |
581 | CephContext* const cct; | |
582 | ||
583 | /* Read-write is intensional here due to RGWUserInfo creation process. */ | |
1e59de90 | 584 | rgw::sal::Driver* driver; |
7c673cae FG |
585 | |
586 | /* Supplemental strategy for extracting permissions from ACLs. Its results | |
587 | * will be combined (ORed) with a default strategy that is responsible for | |
588 | * handling backward compatibility. */ | |
589 | const acl_strategy_t extra_acl_strategy; | |
590 | ||
591 | const AuthInfo info; | |
1e59de90 | 592 | const rgw::auth::ImplicitTenants& implicit_tenant_context; |
9f95a23c | 593 | const rgw::auth::ImplicitTenants::implicit_tenant_flag_bits implicit_tenant_bit; |
7c673cae | 594 | |
11fdf7f2 TL |
595 | virtual void create_account(const DoutPrefixProvider* dpp, |
596 | const rgw_user& acct_user, | |
9f95a23c | 597 | bool implicit_tenant, |
7c673cae FG |
598 | RGWUserInfo& user_info) const; /* out */ |
599 | ||
600 | public: | |
601 | RemoteApplier(CephContext* const cct, | |
1e59de90 | 602 | rgw::sal::Driver* driver, |
7c673cae FG |
603 | acl_strategy_t&& extra_acl_strategy, |
604 | const AuthInfo& info, | |
1e59de90 | 605 | const rgw::auth::ImplicitTenants& implicit_tenant_context, |
9f95a23c | 606 | rgw::auth::ImplicitTenants::implicit_tenant_flag_bits implicit_tenant_bit) |
7c673cae | 607 | : cct(cct), |
1e59de90 | 608 | driver(driver), |
7c673cae FG |
609 | extra_acl_strategy(std::move(extra_acl_strategy)), |
610 | info(info), | |
9f95a23c TL |
611 | implicit_tenant_context(implicit_tenant_context), |
612 | implicit_tenant_bit(implicit_tenant_bit) { | |
7c673cae FG |
613 | } |
614 | ||
11fdf7f2 | 615 | uint32_t get_perms_from_aclspec(const DoutPrefixProvider* dpp, const aclspec_t& aclspec) const override; |
7c673cae FG |
616 | bool is_admin_of(const rgw_user& uid) const override; |
617 | bool is_owner_of(const rgw_user& uid) const override; | |
31f18b77 FG |
618 | bool is_identity(const idset_t& ids) const override; |
619 | ||
7c673cae FG |
620 | uint32_t get_perm_mask() const override { return info.perm_mask; } |
621 | void to_str(std::ostream& out) const override; | |
11fdf7f2 | 622 | void load_acct_info(const DoutPrefixProvider* dpp, RGWUserInfo& user_info) const override; /* out */ |
2a845540 | 623 | void write_ops_log_entry(rgw_log_entry& entry) const override; |
11fdf7f2 | 624 | uint32_t get_identity_type() const override { return info.acct_type; } |
20effc67 TL |
625 | std::string get_acct_name() const override { return info.acct_name; } |
626 | std::string get_subuser() const override { return {}; } | |
7c673cae FG |
627 | |
628 | struct Factory { | |
629 | virtual ~Factory() {} | |
630 | /* Providing r-value reference here is required intensionally. Callee is | |
631 | * thus disallowed to handle std::function in a way that could inhibit | |
632 | * the move behaviour (like forgetting about std::moving a l-value). */ | |
633 | virtual aplptr_t create_apl_remote(CephContext* cct, | |
634 | const req_state* s, | |
635 | acl_strategy_t&& extra_acl_strategy, | |
11fdf7f2 | 636 | const AuthInfo &info) const = 0; |
7c673cae FG |
637 | }; |
638 | }; | |
639 | ||
640 | ||
641 | /* rgw::auth::LocalApplier targets those auth engines that base on the data | |
642 | * enclosed in the RGWUserInfo control structure. As a side effect of doing | |
643 | * the authentication process, they must have it loaded. Leveraging this is | |
644 | * a way to avoid unnecessary calls to underlying RADOS store. */ | |
645 | class LocalApplier : public IdentityApplier { | |
646 | using aclspec_t = rgw::auth::Identity::aclspec_t; | |
647 | ||
648 | protected: | |
649 | const RGWUserInfo user_info; | |
650 | const std::string subuser; | |
11fdf7f2 | 651 | uint32_t perm_mask; |
2a845540 | 652 | const std::string access_key_id; |
7c673cae FG |
653 | |
654 | uint32_t get_perm_mask(const std::string& subuser_name, | |
655 | const RGWUserInfo &uinfo) const; | |
656 | ||
657 | public: | |
658 | static const std::string NO_SUBUSER; | |
2a845540 | 659 | static const std::string NO_ACCESS_KEY; |
7c673cae FG |
660 | |
661 | LocalApplier(CephContext* const cct, | |
662 | const RGWUserInfo& user_info, | |
11fdf7f2 | 663 | std::string subuser, |
2a845540 TL |
664 | const std::optional<uint32_t>& perm_mask, |
665 | const std::string access_key_id) | |
7c673cae | 666 | : user_info(user_info), |
20effc67 | 667 | subuser(std::move(subuser)), |
2a845540 TL |
668 | perm_mask(perm_mask.value_or(RGW_PERM_INVALID)), |
669 | access_key_id(access_key_id) { | |
7c673cae FG |
670 | } |
671 | ||
672 | ||
11fdf7f2 | 673 | uint32_t get_perms_from_aclspec(const DoutPrefixProvider* dpp, const aclspec_t& aclspec) const override; |
7c673cae FG |
674 | bool is_admin_of(const rgw_user& uid) const override; |
675 | bool is_owner_of(const rgw_user& uid) const override; | |
31f18b77 | 676 | bool is_identity(const idset_t& ids) const override; |
7c673cae | 677 | uint32_t get_perm_mask() const override { |
11fdf7f2 TL |
678 | if (this->perm_mask == RGW_PERM_INVALID) { |
679 | return get_perm_mask(subuser, user_info); | |
680 | } else { | |
681 | return this->perm_mask; | |
682 | } | |
7c673cae FG |
683 | } |
684 | void to_str(std::ostream& out) const override; | |
11fdf7f2 TL |
685 | void load_acct_info(const DoutPrefixProvider* dpp, RGWUserInfo& user_info) const override; /* out */ |
686 | uint32_t get_identity_type() const override { return TYPE_RGW; } | |
20effc67 TL |
687 | std::string get_acct_name() const override { return {}; } |
688 | std::string get_subuser() const override { return subuser; } | |
2a845540 | 689 | void write_ops_log_entry(rgw_log_entry& entry) const override; |
7c673cae FG |
690 | |
691 | struct Factory { | |
692 | virtual ~Factory() {} | |
693 | virtual aplptr_t create_apl_local(CephContext* cct, | |
694 | const req_state* s, | |
695 | const RGWUserInfo& user_info, | |
11fdf7f2 | 696 | const std::string& subuser, |
2a845540 TL |
697 | const std::optional<uint32_t>& perm_mask, |
698 | const std::string& access_key_id) const = 0; | |
7c673cae FG |
699 | }; |
700 | }; | |
701 | ||
11fdf7f2 | 702 | class RoleApplier : public IdentityApplier { |
f91f0fd5 TL |
703 | public: |
704 | struct Role { | |
20effc67 TL |
705 | std::string id; |
706 | std::string name; | |
707 | std::string tenant; | |
708 | std::vector<std::string> role_policies; | |
709 | }; | |
710 | struct TokenAttrs { | |
711 | rgw_user user_id; | |
712 | std::string token_policy; | |
713 | std::string role_session_name; | |
714 | std::vector<std::string> token_claims; | |
715 | std::string token_issued_at; | |
716 | std::vector<std::pair<std::string, std::string>> principal_tags; | |
717 | }; | |
11fdf7f2 | 718 | protected: |
20effc67 TL |
719 | Role role; |
720 | TokenAttrs token_attrs; | |
11fdf7f2 TL |
721 | |
722 | public: | |
723 | ||
724 | RoleApplier(CephContext* const cct, | |
f91f0fd5 | 725 | const Role& role, |
20effc67 | 726 | const TokenAttrs& token_attrs) |
f91f0fd5 | 727 | : role(role), |
20effc67 | 728 | token_attrs(token_attrs) {} |
11fdf7f2 TL |
729 | |
730 | uint32_t get_perms_from_aclspec(const DoutPrefixProvider* dpp, const aclspec_t& aclspec) const override { | |
731 | return 0; | |
732 | } | |
733 | bool is_admin_of(const rgw_user& uid) const override { | |
734 | return false; | |
735 | } | |
736 | bool is_owner_of(const rgw_user& uid) const override { | |
20effc67 | 737 | return (this->token_attrs.user_id.id == uid.id && this->token_attrs.user_id.tenant == uid.tenant && this->token_attrs.user_id.ns == uid.ns); |
11fdf7f2 TL |
738 | } |
739 | bool is_identity(const idset_t& ids) const override; | |
740 | uint32_t get_perm_mask() const override { | |
20effc67 | 741 | return RGW_PERM_NONE; |
11fdf7f2 TL |
742 | } |
743 | void to_str(std::ostream& out) const override; | |
744 | void load_acct_info(const DoutPrefixProvider* dpp, RGWUserInfo& user_info) const override; /* out */ | |
745 | uint32_t get_identity_type() const override { return TYPE_ROLE; } | |
20effc67 TL |
746 | std::string get_acct_name() const override { return {}; } |
747 | std::string get_subuser() const override { return {}; } | |
11fdf7f2 | 748 | void modify_request_state(const DoutPrefixProvider* dpp, req_state* s) const override; |
20effc67 | 749 | std::string get_role_tenant() const override { return role.tenant; } |
11fdf7f2 TL |
750 | |
751 | struct Factory { | |
752 | virtual ~Factory() {} | |
753 | virtual aplptr_t create_apl_role( CephContext* cct, | |
754 | const req_state* s, | |
f67539c2 | 755 | const rgw::auth::RoleApplier::Role& role, |
20effc67 | 756 | const rgw::auth::RoleApplier::TokenAttrs& token_attrs) const = 0; |
11fdf7f2 TL |
757 | }; |
758 | }; | |
7c673cae FG |
759 | |
760 | /* The anonymous abstract engine. */ | |
761 | class AnonymousEngine : public Engine { | |
762 | CephContext* const cct; | |
763 | const rgw::auth::LocalApplier::Factory* const apl_factory; | |
764 | ||
765 | public: | |
766 | AnonymousEngine(CephContext* const cct, | |
767 | const rgw::auth::LocalApplier::Factory* const apl_factory) | |
768 | : cct(cct), | |
769 | apl_factory(apl_factory) { | |
770 | } | |
771 | ||
772 | const char* get_name() const noexcept override { | |
773 | return "rgw::auth::AnonymousEngine"; | |
774 | } | |
775 | ||
f67539c2 | 776 | Engine::result_t authenticate(const DoutPrefixProvider* dpp, const req_state* s, optional_yield y) const override final; |
7c673cae FG |
777 | |
778 | protected: | |
779 | virtual bool is_applicable(const req_state*) const noexcept { | |
780 | return true; | |
781 | } | |
782 | }; | |
783 | ||
784 | } /* namespace auth */ | |
785 | } /* namespace rgw */ | |
786 | ||
787 | ||
788 | uint32_t rgw_perms_from_aclspec_default_strategy( | |
789 | const rgw_user& uid, | |
b3b6e05e TL |
790 | const rgw::auth::Identity::aclspec_t& aclspec, |
791 | const DoutPrefixProvider *dpp); |