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