diff options
author | Christian Grothoff <christian@grothoff.org> | 2024-11-13 14:22:27 +0100 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2024-11-13 14:22:40 +0100 |
commit | 7fff8238330c6258101f7942e30f7bebced42c8b (patch) | |
tree | 4de2c0f8f4ba8e8844092582d01f29f3b6089cf4 | |
parent | 5dfdd12a4403318cf8498a55c2bbcf3134427067 (diff) |
implement API to use the /accounts//token API within libtalerbank
-rw-r--r-- | src/bank-lib/Makefile.am | 1 | ||||
-rw-r--r-- | src/bank-lib/bank_api_account_token.c | 280 | ||||
-rw-r--r-- | src/include/taler_bank_service.h | 146 |
3 files changed, 427 insertions, 0 deletions
diff --git a/src/bank-lib/Makefile.am b/src/bank-lib/Makefile.am index b170d2d5f..4315c322a 100644 --- a/src/bank-lib/Makefile.am +++ b/src/bank-lib/Makefile.am @@ -37,6 +37,7 @@ libtalerbank_la_LDFLAGS = \ -version-info 2:0:0 \ -no-undefined libtalerbank_la_SOURCES = \ + bank_api_account_token.c \ bank_api_admin_add_incoming.c \ bank_api_admin_add_kycauth.c \ bank_api_common.c bank_api_common.h \ diff --git a/src/bank-lib/bank_api_account_token.c b/src/bank-lib/bank_api_account_token.c new file mode 100644 index 000000000..0bdd5aced --- /dev/null +++ b/src/bank-lib/bank_api_account_token.c @@ -0,0 +1,280 @@ +/* + This file is part of TALER + Copyright (C) 2015--2024 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file bank-lib/bank_api_account_token.c + * @brief Implementation of the /account/$ACC/token requests of the bank's HTTP API + * @author Christian Grothoff + */ +#include "platform.h" +#include "bank_api_common.h" +#include <microhttpd.h> /* just for HTTP status codes */ +#include "taler_signatures.h" +#include "taler_curl_lib.h" + + +struct TALER_BANK_AccountTokenHandle +{ + + /** + * The url for this request. + */ + char *request_url; + + /** + * POST context. + */ + struct TALER_CURL_PostContext post_ctx; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_BANK_AccountTokenCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + +}; + + +/** + * Function called when we're done processing the + * HTTP /account/$ACC/token request. + * + * @param cls the `struct TALER_BANK_AccountTokenHandle` + * @param response_code HTTP response code, 0 on error + * @param response parsed JSON result, NULL on error + */ +static void +handle_account_token_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_BANK_AccountTokenHandle *aai = cls; + const json_t *j = response; + struct TALER_BANK_AccountTokenResponse ir = { + .http_status = response_code, + .response = response + }; + + aai->job = NULL; + switch (response_code) + { + case 0: + ir.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + case MHD_HTTP_OK: + { + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_string ("access_token", + &ir.details.ok.access_token), + GNUNET_JSON_spec_timestamp ("expiration", + &ir.details.ok.expiration), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (j, + spec, + NULL, NULL)) + { + GNUNET_break_op (0); + ir.http_status = 0; + ir.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + } + } + break; + case MHD_HTTP_BAD_REQUEST: + /* This should never happen, either us or the bank is buggy + (or API version conflict); just pass JSON reply to the application */ + GNUNET_break_op (0); + ir.ec = TALER_JSON_get_error_code (j); + break; + case MHD_HTTP_FORBIDDEN: + /* Access denied */ + ir.ec = TALER_JSON_get_error_code (j); + break; + case MHD_HTTP_UNAUTHORIZED: + /* Nothing really to verify, bank says the password is invalid; we should + pass the JSON reply to the application */ + ir.ec = TALER_JSON_get_error_code (j); + break; + case MHD_HTTP_NOT_FOUND: + /* Nothing really to verify, maybe account really does not exist. + We should pass the JSON reply to the application */ + ir.ec = TALER_JSON_get_error_code (j); + break; + case MHD_HTTP_INTERNAL_SERVER_ERROR: + /* Server had an internal issue; we should retry, but this API + leaves this to the application */ + ir.ec = TALER_JSON_get_error_code (j); + break; + default: + /* unexpected response code */ + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u\n", + (unsigned int) response_code); + GNUNET_break (0); + ir.ec = TALER_JSON_get_error_code (j); + break; + } + aai->cb (aai->cb_cls, + &ir); + TALER_BANK_account_token_cancel (aai); +} + + +/** + * Convert @a scope to string. + * + * @param scope a scope + * @return string encoding of the scope + */ +static const char * +scope_to_string (enum TALER_BANK_TokenScope scope) +{ + switch (scope) + { + case TALER_BANK_TOKEN_SCOPE_READONLY: + return "readonly"; + case TALER_BANK_TOKEN_SCOPE_READWRITE: + return "readwrite"; + case TALER_BANK_TOKEN_SCOPE_REVENUE: + return "revenue"; + case TALER_BANK_TOKEN_SCOPE_WIREGATEWAY: + return "wiregateway"; + } + GNUNET_break (0); + return NULL; +} + + +struct TALER_BANK_AccountTokenHandle * +TALER_BANK_account_token ( + struct GNUNET_CURL_Context *ctx, + const struct TALER_BANK_AuthenticationData *auth, + const char *account_name, + enum TALER_BANK_TokenScope scope, + bool refreshable, + const char *description, + struct GNUNET_TIME_Relative duration, + TALER_BANK_AccountTokenCallback res_cb, + void *res_cb_cls) +{ + struct TALER_BANK_AccountTokenHandle *ath; + json_t *token_req; + CURL *eh; + + token_req = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("scope", + scope_to_string (scope)), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_string ("description", + description)), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_time_rel ("duration", + duration)), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_bool ("refreshable", + refreshable))); + if (NULL == token_req) + { + GNUNET_break (0); + return NULL; + } + ath = GNUNET_new (struct TALER_BANK_AccountTokenHandle); + ath->cb = res_cb; + ath->cb_cls = res_cb_cls; + { + char *path; + + GNUNET_asprintf (&path, + "accounts/%s/token", + account_name); + ath->request_url = TALER_url_join (auth->wire_gateway_url, + path, + NULL); + GNUNET_free (path); + } + if (NULL == ath->request_url) + { + GNUNET_free (ath); + json_decref (token_req); + return NULL; + } + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Requesting access token at `%s'\n", + ath->request_url); + ath->post_ctx.headers + = curl_slist_append ( + ath->post_ctx.headers, + "Content-Type: application/json"); + eh = curl_easy_init (); + if ( (NULL == eh) || + (GNUNET_OK != + TALER_BANK_setup_auth_ (eh, + auth)) || + (CURLE_OK != + curl_easy_setopt (eh, + CURLOPT_URL, + ath->request_url)) || + (GNUNET_OK != + TALER_curl_easy_post (&ath->post_ctx, + eh, + token_req)) ) + { + GNUNET_break (0); + TALER_BANK_account_token_cancel (ath); + if (NULL != eh) + curl_easy_cleanup (eh); + json_decref (token_req); + return NULL; + } + json_decref (token_req); + ath->job = GNUNET_CURL_job_add2 (ctx, + eh, + ath->post_ctx.headers, + &handle_account_token_finished, + ath); + GNUNET_assert (NULL != ath->job); + return ath; +} + + +void +TALER_BANK_account_token_cancel ( + struct TALER_BANK_AccountTokenHandle *ath) +{ + if (NULL != ath->job) + { + GNUNET_CURL_job_cancel (ath->job); + ath->job = NULL; + } + TALER_curl_easy_post_finished (&ath->post_ctx); + GNUNET_free (ath->request_url); + GNUNET_free (ath); +} + + +/* end of bank_api_account_token.c */ diff --git a/src/include/taler_bank_service.h b/src/include/taler_bank_service.h index a891c5b7c..29c99a6a7 100644 --- a/src/include/taler_bank_service.h +++ b/src/include/taler_bank_service.h @@ -112,6 +112,152 @@ struct TALER_BANK_AuthenticationData }; +/* ********************* /accounts/$ACC/token *********************** */ + + +/** + * @brief A /accounts/$USERNAME/token request handle + */ +struct TALER_BANK_AccountTokenHandle; + + +/** + * Response details for a token request. + */ +struct TALER_BANK_AccountTokenResponse +{ + + /** + * HTTP status. + */ + unsigned int http_status; + + /** + * Taler error code, #TALER_EC_NONE on success. + */ + enum TALER_ErrorCode ec; + + /** + * Full response, NULL if body was not in JSON format. + */ + const json_t *response; + + /** + * Details returned depending on the @e http_status. + */ + union + { + + /** + * Details if status was #MHD_HTTP_OK + */ + struct + { + /** + * Access token to use. + */ + const char *access_token; + + /** + * time when the token will expire. + */ + struct GNUNET_TIME_Timestamp expiration; + + } ok; + + } details; + +}; + +/** + * Callbacks of this type are used to return the result of submitting + * a request for an access token to the bank. + * + * @param cls closure + * @param atr response details + */ +typedef void +(*TALER_BANK_AccountTokenCallback) ( + void *cls, + const struct TALER_BANK_AccountTokenResponse *atr); + + +/** + * Possible access scopes for bank bearer tokens. + */ +enum TALER_BANK_TokenScope +{ + + /** + * Only grant read-access to the account. Useful for + * human auditors. + */ + TALER_BANK_TOKEN_SCOPE_READONLY, + + /** + * Grants full read-write access to the account. Useful + * for the SPA. Strongly recommended to limit validity + * duration. + */ + TALER_BANK_TOKEN_SCOPE_READWRITE, + + /** + * Only grant (read-access to) the revenue API. Useful for + * merchant backends. + */ + TALER_BANK_TOKEN_SCOPE_REVENUE, + + /** + * Only grant access to the wire gateway API. Useful for + * the exchange. + */ + TALER_BANK_TOKEN_SCOPE_WIREGATEWAY + +}; + + +/** + * Requests an access token from the bank. Note that this + * request is against the CORE banking API and not done by + * exchange code itself (but used to get access tokens when testing). + * + * @param ctx curl context for the event loop + * @param auth authentication data to send to the bank + * @param account_name username of the bank account to get a token for + * @param scope requested token scope + * @param refreshable true if the token should be refreshable + * @param description human-readable token description (for token management) + * @param duration requested token validity, use zero for default + * @param res_cb the callback to call when the final result for this request is available + * @param res_cb_cls closure for the above callback + * @return NULL + * if the inputs are invalid (i.e. invalid amount) or internal errors. + * In this case, the callback is not called. + */ +struct TALER_BANK_AccountTokenHandle * +TALER_BANK_account_token ( + struct GNUNET_CURL_Context *ctx, + const struct TALER_BANK_AuthenticationData *auth, + const char *account_name, + enum TALER_BANK_TokenScope scope, + bool refreshable, + const char *description, + struct GNUNET_TIME_Relative duration, + TALER_BANK_AccountTokenCallback res_cb, + void *res_cb_cls); + + +/** + * Cancel an add incoming operation. This function cannot be used on a + * request handle if a response is already served for it. + * + * @param[in] aai the admin add incoming request handle + */ +void +TALER_BANK_account_token_cancel ( + struct TALER_BANK_AccountTokenHandle *ath); + + /* ********************* /admin/add-incoming *********************** */ |