]>
Commit | Line | Data |
---|---|---|
f91f0fd5 TL |
1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- |
2 | // vim: ts=8 sw=2 smarttab ft=cpp | |
3 | ||
4 | #include <errno.h> | |
5 | #include <ctime> | |
6 | #include <regex> | |
7 | ||
8 | #include "common/errno.h" | |
9 | #include "common/Formatter.h" | |
10 | #include "common/ceph_json.h" | |
11 | #include "common/ceph_time.h" | |
12 | #include "rgw_rados.h" | |
13 | #include "rgw_zone.h" | |
14 | ||
15 | #include "include/types.h" | |
16 | #include "rgw_string.h" | |
17 | ||
18 | #include "rgw_common.h" | |
19 | #include "rgw_tools.h" | |
20 | #include "rgw_oidc_provider.h" | |
21 | ||
22 | #include "services/svc_zone.h" | |
23 | #include "services/svc_sys_obj.h" | |
24 | ||
25 | #define dout_subsys ceph_subsys_rgw | |
26 | ||
27 | const string RGWOIDCProvider::oidc_url_oid_prefix = "oidc_url."; | |
28 | const string RGWOIDCProvider::oidc_arn_prefix = "arn:aws:iam::"; | |
29 | ||
f67539c2 TL |
30 | int RGWOIDCProvider::store_url(const string& url, bool exclusive, |
31 | optional_yield y) | |
f91f0fd5 TL |
32 | { |
33 | using ceph::encode; | |
34 | string oid = tenant + get_url_oid_prefix() + url; | |
35 | ||
36 | auto svc = ctl->svc; | |
37 | ||
38 | bufferlist bl; | |
39 | encode(*this, bl); | |
40 | auto obj_ctx = svc->sysobj->init_obj_ctx(); | |
41 | return rgw_put_system_obj(obj_ctx, svc->zone->get_zone_params().oidc_pool, oid, | |
f67539c2 | 42 | bl, exclusive, NULL, real_time(), y); |
f91f0fd5 TL |
43 | } |
44 | ||
45 | int RGWOIDCProvider::get_tenant_url_from_arn(string& tenant, string& url) | |
46 | { | |
47 | auto provider_arn = rgw::ARN::parse(arn); | |
48 | if (!provider_arn) { | |
49 | return -EINVAL; | |
50 | } | |
51 | url = provider_arn->resource; | |
52 | tenant = provider_arn->account; | |
53 | auto pos = url.find("oidc-provider/"); | |
54 | if (pos != std::string::npos) { | |
55 | url.erase(pos, 14); | |
56 | } | |
57 | return 0; | |
58 | } | |
59 | ||
f67539c2 | 60 | int RGWOIDCProvider::create(bool exclusive, optional_yield y) |
f91f0fd5 TL |
61 | { |
62 | int ret; | |
63 | ||
64 | if (! validate_input()) { | |
65 | return -EINVAL; | |
66 | } | |
67 | ||
68 | string idp_url = url_remove_prefix(provider_url); | |
69 | ||
70 | /* check to see the name is not used */ | |
71 | ret = read_url(idp_url, tenant); | |
72 | if (exclusive && ret == 0) { | |
73 | ldout(cct, 0) << "ERROR: url " << provider_url << " already in use" | |
74 | << id << dendl; | |
75 | return -EEXIST; | |
76 | } else if ( ret < 0 && ret != -ENOENT) { | |
77 | ldout(cct, 0) << "failed reading provider url " << provider_url << ": " | |
78 | << cpp_strerror(-ret) << dendl; | |
79 | return ret; | |
80 | } | |
81 | ||
82 | //arn | |
83 | arn = oidc_arn_prefix + tenant + ":oidc-provider/" + idp_url; | |
84 | ||
85 | // Creation time | |
86 | real_clock::time_point t = real_clock::now(); | |
87 | ||
88 | struct timeval tv; | |
89 | real_clock::to_timeval(t, tv); | |
90 | ||
91 | char buf[30]; | |
92 | struct tm result; | |
93 | gmtime_r(&tv.tv_sec, &result); | |
94 | strftime(buf,30,"%Y-%m-%dT%H:%M:%S", &result); | |
95 | sprintf(buf + strlen(buf),".%dZ",(int)tv.tv_usec/1000); | |
96 | creation_date.assign(buf, strlen(buf)); | |
97 | ||
98 | auto svc = ctl->svc; | |
99 | ||
100 | auto& pool = svc->zone->get_zone_params().oidc_pool; | |
f67539c2 | 101 | ret = store_url(idp_url, exclusive, y); |
f91f0fd5 TL |
102 | if (ret < 0) { |
103 | ldout(cct, 0) << "ERROR: storing role info in pool: " << pool.name << ": " | |
104 | << provider_url << ": " << cpp_strerror(-ret) << dendl; | |
105 | return ret; | |
106 | } | |
107 | ||
108 | return 0; | |
109 | } | |
110 | ||
f67539c2 | 111 | int RGWOIDCProvider::delete_obj(optional_yield y) |
f91f0fd5 TL |
112 | { |
113 | auto svc = ctl->svc; | |
114 | auto& pool = svc->zone->get_zone_params().oidc_pool; | |
115 | ||
116 | string url, tenant; | |
117 | auto ret = get_tenant_url_from_arn(tenant, url); | |
118 | if (ret < 0) { | |
119 | ldout(cct, 0) << "ERROR: failed to parse arn" << dendl; | |
120 | return -EINVAL; | |
121 | } | |
122 | ||
123 | if (this->tenant != tenant) { | |
124 | ldout(cct, 0) << "ERROR: tenant in arn doesn't match that of user " << this->tenant << ", " | |
125 | << tenant << ": " << dendl; | |
126 | return -EINVAL; | |
127 | } | |
128 | ||
129 | // Delete url | |
130 | string oid = tenant + get_url_oid_prefix() + url; | |
f67539c2 | 131 | ret = rgw_delete_system_obj(svc->sysobj, pool, oid, NULL, y); |
f91f0fd5 TL |
132 | if (ret < 0) { |
133 | ldout(cct, 0) << "ERROR: deleting oidc url from pool: " << pool.name << ": " | |
134 | << provider_url << ": " << cpp_strerror(-ret) << dendl; | |
135 | } | |
136 | ||
137 | return ret; | |
138 | } | |
139 | ||
140 | int RGWOIDCProvider::get() | |
141 | { | |
142 | string url, tenant; | |
143 | auto ret = get_tenant_url_from_arn(tenant, url); | |
144 | if (ret < 0) { | |
145 | ldout(cct, 0) << "ERROR: failed to parse arn" << dendl; | |
146 | return -EINVAL; | |
147 | } | |
148 | ||
149 | if (this->tenant != tenant) { | |
150 | ldout(cct, 0) << "ERROR: tenant in arn doesn't match that of user " << this->tenant << ", " | |
151 | << tenant << ": " << dendl; | |
152 | return -EINVAL; | |
153 | } | |
154 | ||
155 | ret = read_url(url, tenant); | |
156 | if (ret < 0) { | |
157 | return ret; | |
158 | } | |
159 | ||
160 | return 0; | |
161 | } | |
162 | ||
163 | void RGWOIDCProvider::dump(Formatter *f) const | |
164 | { | |
165 | encode_json("OpenIDConnectProviderArn", arn, f); | |
166 | } | |
167 | ||
168 | void RGWOIDCProvider::dump_all(Formatter *f) const | |
169 | { | |
170 | f->open_object_section("ClientIDList"); | |
171 | for (auto it : client_ids) { | |
172 | encode_json("member", it, f); | |
173 | } | |
174 | f->close_section(); | |
175 | encode_json("CreateDate", creation_date, f); | |
176 | f->open_object_section("ThumbprintList"); | |
177 | for (auto it : thumbprints) { | |
178 | encode_json("member", it, f); | |
179 | } | |
180 | f->close_section(); | |
181 | encode_json("Url", provider_url, f); | |
182 | } | |
183 | ||
184 | void RGWOIDCProvider::decode_json(JSONObj *obj) | |
185 | { | |
186 | JSONDecoder::decode_json("OpenIDConnectProviderArn", arn, obj); | |
187 | } | |
188 | ||
189 | int RGWOIDCProvider::read_url(const string& url, const string& tenant) | |
190 | { | |
191 | auto svc = ctl->svc; | |
192 | auto& pool = svc->zone->get_zone_params().oidc_pool; | |
193 | string oid = tenant + get_url_oid_prefix() + url; | |
194 | bufferlist bl; | |
195 | auto obj_ctx = svc->sysobj->init_obj_ctx(); | |
196 | ||
197 | int ret = rgw_get_system_obj(obj_ctx, pool, oid, bl, NULL, NULL, null_yield); | |
198 | if (ret < 0) { | |
199 | return ret; | |
200 | } | |
201 | ||
202 | try { | |
203 | using ceph::decode; | |
204 | auto iter = bl.cbegin(); | |
205 | decode(*this, iter); | |
206 | } catch (buffer::error& err) { | |
207 | ldout(cct, 0) << "ERROR: failed to decode oidc provider info from pool: " << pool.name << | |
208 | ": " << url << dendl; | |
209 | return -EIO; | |
210 | } | |
211 | ||
212 | return 0; | |
213 | } | |
214 | ||
215 | bool RGWOIDCProvider::validate_input() | |
216 | { | |
217 | if (provider_url.length() > MAX_OIDC_URL_LEN) { | |
218 | ldout(cct, 0) << "ERROR: Invalid length of url " << dendl; | |
219 | return false; | |
220 | } | |
221 | if (client_ids.size() > MAX_OIDC_NUM_CLIENT_IDS) { | |
222 | ldout(cct, 0) << "ERROR: Invalid number of client ids " << dendl; | |
223 | return false; | |
224 | } | |
225 | ||
226 | for (auto& it : client_ids) { | |
227 | if (it.length() > MAX_OIDC_CLIENT_ID_LEN) { | |
228 | return false; | |
229 | } | |
230 | } | |
231 | ||
232 | if (thumbprints.size() > MAX_OIDC_NUM_THUMBPRINTS) { | |
233 | ldout(cct, 0) << "ERROR: Invalid number of thumbprints " << thumbprints.size() << dendl; | |
234 | return false; | |
235 | } | |
236 | ||
237 | for (auto& it : thumbprints) { | |
238 | if (it.length() > MAX_OIDC_THUMBPRINT_LEN) { | |
239 | return false; | |
240 | } | |
241 | } | |
242 | ||
243 | return true; | |
244 | } | |
245 | ||
246 | int RGWOIDCProvider::get_providers(RGWRados *store, | |
247 | const string& tenant, | |
248 | vector<RGWOIDCProvider>& providers) | |
249 | { | |
250 | auto ctl = store->pctl; | |
251 | auto svc = ctl->svc; | |
252 | auto pool = store->svc.zone->get_zone_params().oidc_pool; | |
253 | string prefix = tenant + oidc_url_oid_prefix; | |
254 | ||
255 | //Get the filtered objects | |
256 | list<string> result; | |
257 | bool is_truncated; | |
258 | RGWListRawObjsCtx ctx; | |
259 | do { | |
260 | list<string> oids; | |
261 | int r = store->list_raw_objects(pool, prefix, 1000, ctx, oids, &is_truncated); | |
262 | if (r < 0) { | |
263 | ldout(ctl->cct, 0) << "ERROR: listing filtered objects failed: " << pool.name << ": " | |
264 | << prefix << ": " << cpp_strerror(-r) << dendl; | |
265 | return r; | |
266 | } | |
267 | for (const auto& iter : oids) { | |
268 | RGWOIDCProvider provider(ctl->cct, store->pctl); | |
269 | bufferlist bl; | |
270 | auto obj_ctx = svc->sysobj->init_obj_ctx(); | |
271 | ||
272 | int ret = rgw_get_system_obj(obj_ctx, pool, iter, bl, NULL, NULL, null_yield); | |
273 | if (ret < 0) { | |
274 | return ret; | |
275 | } | |
276 | ||
277 | try { | |
278 | using ceph::decode; | |
279 | auto iter = bl.cbegin(); | |
280 | decode(provider, iter); | |
281 | } catch (buffer::error& err) { | |
282 | ldout(ctl->cct, 0) << "ERROR: failed to decode oidc provider info from pool: " << pool.name << | |
283 | ": " << iter << dendl; | |
284 | return -EIO; | |
285 | } | |
286 | ||
287 | providers.push_back(std::move(provider)); | |
288 | } | |
289 | } while (is_truncated); | |
290 | ||
291 | return 0; | |
292 | } | |
293 | ||
294 | const string& RGWOIDCProvider::get_url_oid_prefix() | |
295 | { | |
296 | return oidc_url_oid_prefix; | |
297 | } |