diff options
| author | 2023-04-09 21:31:56 +0200 | |
|---|---|---|
| committer | 2023-04-09 21:31:56 +0200 | |
| commit | ccce01d285874faa858b6dd639416ca2d014ad07 (patch) | |
| tree | a3d5b8b6bcb2bf1e824b5da095c272469a3e3730 /mastoapi.c | |
| parent | OAuth login now works. (diff) | |
| download | snac2-ccce01d285874faa858b6dd639416ca2d014ad07.tar.gz snac2-ccce01d285874faa858b6dd639416ca2d014ad07.tar.xz snac2-ccce01d285874faa858b6dd639416ca2d014ad07.zip | |
More mastoapi work.
Diffstat (limited to 'mastoapi.c')
| -rw-r--r-- | mastoapi.c | 179 |
1 files changed, 162 insertions, 17 deletions
| @@ -71,6 +71,56 @@ xs_dict *app_get(const char *id) | |||
| 71 | } | 71 | } |
| 72 | 72 | ||
| 73 | 73 | ||
| 74 | int token_add(const char *id, const xs_dict *token) | ||
| 75 | /* stores a token */ | ||
| 76 | { | ||
| 77 | int status = 201; | ||
| 78 | xs *fn = xs_fmt("%s/token/", srv_basedir); | ||
| 79 | FILE *f; | ||
| 80 | |||
| 81 | mkdirx(fn); | ||
| 82 | fn = xs_str_cat(fn, id); | ||
| 83 | fn = xs_str_cat(fn, ".json"); | ||
| 84 | |||
| 85 | if ((f = fopen(fn, "w")) != NULL) { | ||
| 86 | xs *j = xs_json_dumps_pp(token, 4); | ||
| 87 | fwrite(j, strlen(j), 1, f); | ||
| 88 | fclose(f); | ||
| 89 | } | ||
| 90 | else | ||
| 91 | status = 500; | ||
| 92 | |||
| 93 | return status; | ||
| 94 | } | ||
| 95 | |||
| 96 | |||
| 97 | xs_dict *token_get(const char *id) | ||
| 98 | /* gets a token */ | ||
| 99 | { | ||
| 100 | xs *fn = xs_fmt("%s/token/%s.json", srv_basedir, id); | ||
| 101 | xs_dict *token = NULL; | ||
| 102 | FILE *f; | ||
| 103 | |||
| 104 | if ((f = fopen(fn, "r")) != NULL) { | ||
| 105 | xs *j = xs_readall(f); | ||
| 106 | fclose(f); | ||
| 107 | |||
| 108 | token = xs_json_loads(j); | ||
| 109 | } | ||
| 110 | |||
| 111 | return token; | ||
| 112 | } | ||
| 113 | |||
| 114 | |||
| 115 | int token_del(const char *id) | ||
| 116 | /* deletes a token */ | ||
| 117 | { | ||
| 118 | xs *fn = xs_fmt("%s/token/%s.json", srv_basedir, id); | ||
| 119 | |||
| 120 | return unlink(fn); | ||
| 121 | } | ||
| 122 | |||
| 123 | |||
| 74 | const char *login_page = "" | 124 | const char *login_page = "" |
| 75 | "<!DOCTYPE html>\n" | 125 | "<!DOCTYPE html>\n" |
| 76 | "<body><h1>%s OAuth identify</h1>\n" | 126 | "<body><h1>%s OAuth identify</h1>\n" |
| @@ -176,7 +226,18 @@ int oauth_post_handler(const xs_dict *req, const char *q_path, | |||
| 176 | *body = xs_fmt("%s?code=%s", redir, code); | 226 | *body = xs_fmt("%s?code=%s", redir, code); |
| 177 | status = 303; | 227 | status = 303; |
| 178 | 228 | ||
| 179 | srv_debug(0, xs_fmt("oauth x-snac-login: redirect to %s", *body)); | 229 | srv_debug(0, xs_fmt("oauth x-snac-login: success, redirect to %s", *body)); |
| 230 | |||
| 231 | /* assign the login to the app */ | ||
| 232 | xs *app = app_get(cid); | ||
| 233 | |||
| 234 | if (app != NULL) { | ||
| 235 | app = xs_dict_set(app, "uid", login); | ||
| 236 | app = xs_dict_set(app, "code", code); | ||
| 237 | app_add(cid, app); | ||
| 238 | } | ||
| 239 | else | ||
| 240 | srv_log(xs_fmt("oauth x-snac-login: error getting app %s", cid)); | ||
| 180 | } | 241 | } |
| 181 | else | 242 | else |
| 182 | srv_debug(0, xs_fmt("oauth x-snac-login: login '%s' incorrect", login)); | 243 | srv_debug(0, xs_fmt("oauth x-snac-login: login '%s' incorrect", login)); |
| @@ -198,19 +259,44 @@ int oauth_post_handler(const xs_dict *req, const char *q_path, | |||
| 198 | const char *ruri = xs_dict_get(msg, "redirect_uri"); | 259 | const char *ruri = xs_dict_get(msg, "redirect_uri"); |
| 199 | 260 | ||
| 200 | if (gtype && code && cid && csec && ruri) { | 261 | if (gtype && code && cid && csec && ruri) { |
| 201 | xs *rsp = xs_dict_new(); | 262 | xs *app = app_get(cid); |
| 202 | xs *cat = xs_number_new(time(NULL)); | ||
| 203 | xs *token = random_str(); | ||
| 204 | 263 | ||
| 205 | rsp = xs_dict_append(rsp, "access_token", token); | 264 | if (app == NULL) { |
| 206 | rsp = xs_dict_append(rsp, "token_type", "Bearer"); | 265 | status = 401; |
| 207 | rsp = xs_dict_append(rsp, "created_at", cat); | 266 | srv_log(xs_fmt("oauth token: invalid app %s", cid)); |
| 267 | } | ||
| 268 | else | ||
| 269 | if (strcmp(csec, xs_dict_get(app, "client_secret")) != 0) { | ||
| 270 | status = 401; | ||
| 271 | srv_log(xs_fmt("oauth token: invalid client_secret for app %s", cid)); | ||
| 272 | } | ||
| 273 | else { | ||
| 274 | xs *rsp = xs_dict_new(); | ||
| 275 | xs *cat = xs_number_new(time(NULL)); | ||
| 276 | xs *tokid = random_str(); | ||
| 208 | 277 | ||
| 209 | *body = xs_json_dumps_pp(rsp, 4); | 278 | rsp = xs_dict_append(rsp, "access_token", tokid); |
| 210 | *ctype = "application/json"; | 279 | rsp = xs_dict_append(rsp, "token_type", "Bearer"); |
| 211 | status = 200; | 280 | rsp = xs_dict_append(rsp, "created_at", cat); |
| 281 | |||
| 282 | *body = xs_json_dumps_pp(rsp, 4); | ||
| 283 | *ctype = "application/json"; | ||
| 284 | status = 200; | ||
| 285 | |||
| 286 | const char *uid = xs_dict_get(app, "uid"); | ||
| 287 | |||
| 288 | srv_debug(0, xs_fmt("oauth token: " | ||
| 289 | "successful login for %s, new token %s", uid, tokid)); | ||
| 290 | |||
| 291 | xs *token = xs_dict_new(); | ||
| 292 | token = xs_dict_append(token, "token", tokid); | ||
| 293 | token = xs_dict_append(token, "client_id", cid); | ||
| 294 | token = xs_dict_append(token, "client_secret", csec); | ||
| 295 | token = xs_dict_append(token, "uid", uid); | ||
| 296 | token = xs_dict_append(token, "code", code); | ||
| 212 | 297 | ||
| 213 | srv_debug(0, xs_fmt("oauth token: successful login, token %s", token)); | 298 | token_add(tokid, token); |
| 299 | } | ||
| 214 | } | 300 | } |
| 215 | else { | 301 | else { |
| 216 | srv_debug(0, xs_fmt("oauth token: invalid or unset arguments")); | 302 | srv_debug(0, xs_fmt("oauth token: invalid or unset arguments")); |
| @@ -221,15 +307,28 @@ int oauth_post_handler(const xs_dict *req, const char *q_path, | |||
| 221 | if (strcmp(cmd, "/revoke") == 0) { | 307 | if (strcmp(cmd, "/revoke") == 0) { |
| 222 | const char *cid = xs_dict_get(msg, "client_id"); | 308 | const char *cid = xs_dict_get(msg, "client_id"); |
| 223 | const char *csec = xs_dict_get(msg, "client_secret"); | 309 | const char *csec = xs_dict_get(msg, "client_secret"); |
| 224 | const char *token = xs_dict_get(msg, "token"); | 310 | const char *tokid = xs_dict_get(msg, "token"); |
| 311 | |||
| 312 | if (cid && csec && tokid) { | ||
| 313 | xs *token = token_get(tokid); | ||
| 225 | 314 | ||
| 226 | if (cid && csec && token) { | ||
| 227 | *body = xs_str_new("{}"); | 315 | *body = xs_str_new("{}"); |
| 228 | *ctype = "application/json"; | 316 | *ctype = "application/json"; |
| 229 | status = 200; | 317 | |
| 318 | if (token == NULL || strcmp(csec, xs_dict_get(token, "client_secret")) != 0) { | ||
| 319 | srv_debug(0, xs_fmt("oauth revoke: bad secret for token %s", tokid)); | ||
| 320 | status = 403; | ||
| 321 | } | ||
| 322 | else { | ||
| 323 | token_del(tokid); | ||
| 324 | srv_debug(0, xs_fmt("oauth revoke: revoked token %s", tokid)); | ||
| 325 | status = 200; | ||
| 326 | } | ||
| 327 | } | ||
| 328 | else { | ||
| 329 | srv_debug(0, xs_fmt("oauth revoke: invalid or unset arguments")); | ||
| 330 | status = 403; | ||
| 230 | } | 331 | } |
| 231 | else | ||
| 232 | status = 400; | ||
| 233 | } | 332 | } |
| 234 | 333 | ||
| 235 | return status; | 334 | return status; |
| @@ -242,6 +341,8 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, | |||
| 242 | if (!xs_startswith(q_path, "/api/v1/")) | 341 | if (!xs_startswith(q_path, "/api/v1/")) |
| 243 | return 0; | 342 | return 0; |
| 244 | 343 | ||
| 344 | srv_debug(0, xs_fmt("mastoapi_get_handler %s", q_path)); | ||
| 345 | |||
| 245 | { | 346 | { |
| 246 | xs *j = xs_json_dumps_pp(req, 4); | 347 | xs *j = xs_json_dumps_pp(req, 4); |
| 247 | printf("mastoapi get:\n%s\n", j); | 348 | printf("mastoapi get:\n%s\n", j); |
| @@ -250,12 +351,56 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, | |||
| 250 | int status = 404; | 351 | int status = 404; |
| 251 | xs_dict *msg = xs_dict_get(req, "q_vars"); | 352 | xs_dict *msg = xs_dict_get(req, "q_vars"); |
| 252 | xs *cmd = xs_replace(q_path, "/api/v1", ""); | 353 | xs *cmd = xs_replace(q_path, "/api/v1", ""); |
| 354 | char *v; | ||
| 253 | 355 | ||
| 254 | srv_debug(0, xs_fmt("mastoapi_get_handler %s", q_path)); | 356 | snac snac = {0}; |
| 357 | int logged_in = 0; | ||
| 358 | |||
| 359 | /* if there is an authorization field, try to validate it */ | ||
| 360 | if (!xs_is_null(v = xs_dict_get(req, "authorization")) && xs_startswith(v, "Bearer ")) { | ||
| 361 | xs *tokid = xs_replace(v, "Bearer ", ""); | ||
| 362 | xs *token = token_get(tokid); | ||
| 363 | |||
| 364 | if (token != NULL) { | ||
| 365 | const char *uid = xs_dict_get(token, "uid"); | ||
| 366 | |||
| 367 | if (!xs_is_null(uid) && user_open(&snac, uid)) { | ||
| 368 | logged_in = 1; | ||
| 369 | srv_debug(0, xs_fmt("mastoapi auth: valid token for user %s", uid)); | ||
| 370 | } | ||
| 371 | else | ||
| 372 | srv_log(xs_fmt("mastoapi auth: corrupted token %s", tokid)); | ||
| 373 | } | ||
| 374 | else | ||
| 375 | srv_log(xs_fmt("mastoapi auth: invalid token %s", tokid)); | ||
| 376 | } | ||
| 255 | 377 | ||
| 256 | if (strcmp(cmd, "/accounts/verify_credentials") == 0) { | 378 | if (strcmp(cmd, "/accounts/verify_credentials") == 0) { |
| 379 | if (logged_in) { | ||
| 380 | xs_dict *acct = xs_dict_new(); | ||
| 381 | |||
| 382 | acct = xs_dict_append(acct, "id", xs_dict_get(snac.config, "uid")); | ||
| 383 | acct = xs_dict_append(acct, "username", xs_dict_get(snac.config, "uid")); | ||
| 384 | acct = xs_dict_append(acct, "acct", xs_dict_get(snac.config, "uid")); | ||
| 385 | acct = xs_dict_append(acct, "display_name", xs_dict_get(snac.config, "name")); | ||
| 386 | acct = xs_dict_append(acct, "created_at", xs_dict_get(snac.config, "published")); | ||
| 387 | acct = xs_dict_append(acct, "note", xs_dict_get(snac.config, "bio")); | ||
| 388 | acct = xs_dict_append(acct, "url", snac.actor); | ||
| 389 | acct = xs_dict_append(acct, "avatar", xs_dict_get(snac.config, "avatar")); | ||
| 390 | |||
| 391 | *body = xs_json_dumps_pp(acct, 4); | ||
| 392 | *ctype = "application/json"; | ||
| 393 | status = 200; | ||
| 394 | } | ||
| 395 | else { | ||
| 396 | status = 422; // "Unprocessable entity" (no login) | ||
| 397 | } | ||
| 257 | } | 398 | } |
| 258 | 399 | ||
| 400 | /* user cleanup */ | ||
| 401 | if (logged_in) | ||
| 402 | user_free(&snac); | ||
| 403 | |||
| 259 | return status; | 404 | return status; |
| 260 | } | 405 | } |
| 261 | 406 | ||