taler-merchant-httpd_private-post-instances-ID-token.c (6102B)
1 /* 2 This file is part of GNU Taler 3 (C) 2023, 2025 Taler Systems SA 4 5 GNU Taler is free software; you can redistribute it and/or modify 6 it under the terms of the GNU Affero General Public License as 7 published by the Free Software Foundation; either version 3, 8 or (at your option) any later version. 9 10 GNU Taler is distributed in the hope that it will be useful, but 11 WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 GNU General Public License for more details. 14 15 You should have received a copy of the GNU General Public 16 License along with TALER; see the file COPYING. If not, 17 see <http://www.gnu.org/licenses/> 18 */ 19 20 /** 21 * @file taler-merchant-httpd_private-post-instances-ID-token.c 22 * @brief implementing POST /instances/$ID/token request handling 23 * @author Christian Grothoff 24 */ 25 #include "platform.h" 26 #include "taler-merchant-httpd_private-post-instances-ID-token.h" 27 #include "taler-merchant-httpd_helper.h" 28 #include "taler-merchant-httpd_mfa.h" 29 #include <taler/taler_json_lib.h> 30 31 32 /** 33 * Default duration for the validity of a login token. 34 */ 35 #define DEFAULT_DURATION GNUNET_TIME_UNIT_DAYS 36 37 38 MHD_RESULT 39 TMH_private_post_instances_ID_token (const struct TMH_RequestHandler *rh, 40 struct MHD_Connection *connection, 41 struct TMH_HandlerContext *hc) 42 { 43 struct TMH_MerchantInstance *mi = hc->instance; 44 json_t *jtoken = hc->request_body; 45 const char *scope; 46 const char *description; 47 enum TMH_AuthScope iscope = TMH_AS_NONE; 48 bool refreshable = false; 49 struct TALER_MERCHANTDB_LoginTokenP btoken; 50 struct GNUNET_TIME_Relative duration 51 = DEFAULT_DURATION; 52 struct GNUNET_TIME_Timestamp expiration_time; 53 struct GNUNET_JSON_Specification spec[] = { 54 GNUNET_JSON_spec_string ("scope", 55 &scope), 56 GNUNET_JSON_spec_mark_optional ( 57 GNUNET_JSON_spec_relative_time ("duration", 58 &duration), 59 NULL), 60 GNUNET_JSON_spec_mark_optional ( 61 GNUNET_JSON_spec_bool ("refreshable", 62 &refreshable), 63 NULL), 64 GNUNET_JSON_spec_mark_optional ( 65 GNUNET_JSON_spec_string ("description", 66 &description), 67 NULL), 68 GNUNET_JSON_spec_end () 69 }; 70 enum GNUNET_DB_QueryStatus qs; 71 72 { 73 enum GNUNET_GenericReturnValue res; 74 75 res = TALER_MHD_parse_json_data (connection, 76 jtoken, 77 spec); 78 if (GNUNET_OK != res) 79 return (GNUNET_NO == res) ? MHD_YES : MHD_NO; 80 } 81 GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE, 82 &btoken, 83 sizeof (btoken)); 84 expiration_time = GNUNET_TIME_relative_to_timestamp (duration); 85 { 86 char *tmp_scope; 87 char *scope_prefix; 88 char *scope_suffix; 89 90 tmp_scope = GNUNET_strdup (scope); 91 scope_prefix = strtok (tmp_scope, 92 ":"); 93 scope_suffix = strtok (NULL, 94 ":"); 95 /* We allow <SCOPE>:REFRESHABLE syntax */ 96 if ( (NULL != scope_suffix) && 97 (0 == strcasecmp (scope_suffix, 98 "refreshable"))) 99 refreshable = true; 100 iscope = TMH_get_scope_by_name (scope_prefix); 101 if (TMH_AS_NONE == iscope) 102 { 103 GNUNET_break_op (0); 104 GNUNET_free (tmp_scope); 105 return TALER_MHD_reply_with_ec (connection, 106 TALER_EC_GENERIC_PARAMETER_MALFORMED, 107 "scope"); 108 } 109 GNUNET_free (tmp_scope); 110 } 111 if (refreshable) 112 iscope |= TMH_AS_REFRESHABLE; 113 if (! TMH_scope_is_subset (hc->auth_scope, 114 iscope)) 115 { 116 /* more permissions requested for the new token, not allowed */ 117 GNUNET_break_op (0); 118 return TALER_MHD_reply_with_ec (connection, 119 TALER_EC_GENERIC_TOKEN_PERMISSION_INSUFFICIENT, 120 NULL); 121 } 122 if (NULL == description) 123 { 124 description = ""; 125 } 126 127 { 128 enum GNUNET_GenericReturnValue ret = 129 TMH_mfa_check_simple (hc, 130 TALER_MERCHANT_MFA_CO_AUTH_TOKEN_CREATION, 131 mi); 132 133 if (GNUNET_OK != ret) 134 { 135 return (GNUNET_NO == ret) 136 ? MHD_YES 137 : MHD_NO; 138 } 139 } 140 141 qs = TMH_db->insert_login_token (TMH_db->cls, 142 mi->settings.id, 143 &btoken, 144 GNUNET_TIME_timestamp_get (), 145 expiration_time, 146 iscope, 147 description); 148 switch (qs) 149 { 150 case GNUNET_DB_STATUS_HARD_ERROR: 151 case GNUNET_DB_STATUS_SOFT_ERROR: 152 case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: 153 GNUNET_break (0); 154 return TALER_MHD_reply_with_ec (connection, 155 TALER_EC_GENERIC_DB_STORE_FAILED, 156 "insert_login_token"); 157 case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: 158 break; 159 } 160 161 { 162 char *tok; 163 MHD_RESULT ret; 164 char *val; 165 166 val = GNUNET_STRINGS_data_to_string_alloc (&btoken, 167 sizeof (btoken)); 168 GNUNET_asprintf (&tok, 169 RFC_8959_PREFIX "%s", 170 val); 171 GNUNET_free (val); 172 ret = TALER_MHD_REPLY_JSON_PACK ( 173 connection, 174 MHD_HTTP_OK, 175 GNUNET_JSON_pack_string ("access_token", 176 tok), 177 GNUNET_JSON_pack_string ("token", 178 tok), 179 GNUNET_JSON_pack_string ("scope", 180 scope), 181 GNUNET_JSON_pack_bool ("refreshable", 182 refreshable), 183 GNUNET_JSON_pack_timestamp ("expiration", 184 expiration_time)); 185 GNUNET_free (tok); 186 return ret; 187 } 188 } 189 190 191 /* end of taler-merchant-httpd_private-post-instances-ID-token.c */