]> git.proxmox.com Git - ceph.git/blob - ceph/src/rgw/rgw_oidc_provider.cc
b9aceac83d04d2173b98aeafe1c56d9930c1b80e
[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 optional_yield y)
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,
42 bl, exclusive, NULL, real_time(), y);
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
60 int RGWOIDCProvider::create(bool exclusive, optional_yield y)
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;
101 ret = store_url(idp_url, exclusive, y);
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
111 int RGWOIDCProvider::delete_obj(optional_yield y)
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;
131 ret = rgw_delete_system_obj(svc->sysobj, pool, oid, NULL, y);
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 }