get_token_family_key.c (8457B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2024 Taler Systems SA 4 5 TALER is free software; you can redistribute it and/or modify it under the 6 terms of the GNU General Public License as published by the Free Software 7 Foundation; either version 3, or (at your option) any later version. 8 9 TALER is distributed in the hope that it will be useful, but WITHOUT ANY 10 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 A PARTICULAR PURPOSE. See the GNU General Public License for more details. 12 13 You should have received a copy of the GNU General Public License along with 14 TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> 15 */ 16 /** 17 * @file src/backenddb/get_token_family_key.c 18 * @brief Implementation of the get_token_family_key function for Postgres 19 * @author Christian Blättler 20 */ 21 #include "platform.h" 22 #include <gnunet/gnunet_pq_lib.h> 23 #include <gnunet/gnunet_time_lib.h> 24 #include <string.h> 25 #include <taler/taler_error_codes.h> 26 #include <taler/taler_dbevents.h> 27 #include <taler/taler_pq_lib.h> 28 #include "merchant-database/get_token_family_key.h" 29 #include "merchant-database/start.h" 30 #include "helper.h" 31 32 33 enum GNUNET_DB_QueryStatus 34 TALER_MERCHANTDB_lock_token_family ( 35 struct TALER_MERCHANTDB_PostgresContext *pg, 36 const char *token_family_slug, 37 bool *started_transaction) 38 { 39 struct GNUNET_PQ_QueryParam params[] = { 40 GNUNET_PQ_query_param_string (token_family_slug), 41 GNUNET_PQ_query_param_end 42 }; 43 44 *started_transaction = false; 45 GNUNET_assert (NULL != pg->current_merchant_id); 46 if (NULL == pg->transaction_name) 47 { 48 if (GNUNET_OK != 49 TALER_MERCHANTDB_start_read_committed (pg, 50 "ensure token family key")) 51 return GNUNET_DB_STATUS_HARD_ERROR; 52 *started_transaction = true; 53 } 54 /* Touch the parent even when no keys exist. A plain SELECT FOR UPDATE 55 would allow a caller with an older SERIALIZABLE snapshot to miss keys 56 inserted by the previous lock holder. Updating the row forces that 57 caller to fail with a serialization error instead of minting a duplicate. 58 Under READ COMMITTED the subsequent lookup sees the committed key. */ 59 TMH_PQ_prepare_anon (pg, 60 "UPDATE merchant_token_families" 61 " SET issued=issued" 62 " WHERE slug=$1"); 63 return GNUNET_PQ_eval_prepared_non_select (pg->conn, 64 "", 65 params); 66 } 67 68 69 enum GNUNET_DB_QueryStatus 70 TALER_MERCHANTDB_get_token_family_key ( 71 struct TALER_MERCHANTDB_PostgresContext *pg, 72 const char *instance_id, 73 const char *token_family_slug, 74 struct GNUNET_TIME_Timestamp valid_at, 75 struct GNUNET_TIME_Timestamp sign_until, 76 struct TALER_MERCHANTDB_TokenFamilyKeyDetails *details) 77 { 78 struct GNUNET_PQ_QueryParam params[] = { 79 GNUNET_PQ_query_param_string (token_family_slug), 80 GNUNET_PQ_query_param_timestamp (&valid_at), 81 GNUNET_PQ_query_param_timestamp (&sign_until), 82 GNUNET_PQ_query_param_end 83 }; 84 char *kind; 85 struct GNUNET_PQ_ResultSpec rs[] = { 86 GNUNET_PQ_result_spec_allow_null ( 87 GNUNET_PQ_result_spec_blind_sign_pub ("pub", 88 &details->pub.public_key), 89 NULL), 90 GNUNET_PQ_result_spec_allow_null ( 91 GNUNET_PQ_result_spec_blind_sign_priv ("priv", 92 &details->priv.private_key), 93 NULL), 94 GNUNET_PQ_result_spec_allow_null ( 95 GNUNET_PQ_result_spec_timestamp ("signature_validity_start", 96 &details->signature_validity_start), 97 NULL), 98 GNUNET_PQ_result_spec_allow_null ( 99 GNUNET_PQ_result_spec_timestamp ("signature_validity_end", 100 &details->signature_validity_end), 101 NULL), 102 GNUNET_PQ_result_spec_allow_null ( 103 GNUNET_PQ_result_spec_timestamp ("private_key_deleted_at", 104 &details->private_key_deleted_at), 105 NULL), 106 GNUNET_PQ_result_spec_string ("slug", 107 &details->token_family.slug), 108 GNUNET_PQ_result_spec_string ("name", 109 &details->token_family.name), 110 GNUNET_PQ_result_spec_string ("cipher_choice", 111 &details->token_family.cipher_spec), 112 GNUNET_PQ_result_spec_string ("description", 113 &details->token_family.description), 114 TALER_PQ_result_spec_json ("description_i18n", 115 &details->token_family.description_i18n), 116 GNUNET_PQ_result_spec_timestamp ("valid_after", 117 &details->token_family.valid_after), 118 GNUNET_PQ_result_spec_timestamp ("valid_before", 119 &details->token_family.valid_before), 120 GNUNET_PQ_result_spec_relative_time ("duration", 121 &details->token_family.duration), 122 GNUNET_PQ_result_spec_relative_time ("validity_granularity", 123 &details->token_family. 124 validity_granularity), 125 GNUNET_PQ_result_spec_relative_time ("start_offset", 126 &details->token_family.start_offset), 127 GNUNET_PQ_result_spec_string ("kind", 128 &kind), 129 GNUNET_PQ_result_spec_uint64 ("issued", 130 &details->token_family.issued), 131 GNUNET_PQ_result_spec_uint64 ("used", 132 &details->token_family.used), 133 GNUNET_PQ_result_spec_end 134 }; 135 enum GNUNET_DB_QueryStatus qs; 136 137 GNUNET_assert (NULL != pg->current_merchant_id); 138 GNUNET_assert (0 == strcmp (instance_id, 139 pg->current_merchant_id)); 140 TMH_PQ_prepare_anon (pg, 141 "SELECT" 142 " h_pub" 143 ",pub" 144 ",priv" 145 ",cipher_choice" 146 ",mtfk.signature_validity_start" 147 ",mtfk.signature_validity_end" 148 ",mtfk.private_key_deleted_at" 149 ",slug" 150 ",name" 151 ",description" 152 ",description_i18n::TEXT" 153 ",mtf.valid_after" 154 ",mtf.valid_before" 155 ",duration" 156 ",validity_granularity" 157 ",start_offset" 158 ",kind" 159 ",issued" 160 ",used" 161 " FROM merchant_token_families mtf" 162 " LEFT JOIN merchant_token_family_keys mtfk" 163 " ON ( (mtf.token_family_serial = mtfk.token_family_serial)" 164 " AND ($2 >= mtfk.signature_validity_start)" 165 " AND ($2 <= mtfk.signature_validity_end)" 166 " AND ($3 <= mtfk.private_key_deleted_at) )" 167 " WHERE slug=$1" 168 " ORDER BY mtfk.signature_validity_start ASC" 169 " LIMIT 1"); 170 memset (details, 171 0, 172 sizeof (*details)); 173 qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, 174 "", 175 params, 176 rs); 177 if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) 178 { 179 if (0 == strcmp (kind, 180 "discount")) 181 { 182 details->token_family.kind = TALER_MERCHANTDB_TFK_Discount; 183 } 184 else if (0 == strcmp (kind, 185 "subscription")) 186 { 187 details->token_family.kind = TALER_MERCHANTDB_TFK_Subscription; 188 } 189 else 190 { 191 GNUNET_free (kind); 192 GNUNET_free (details->token_family.slug); 193 GNUNET_free (details->token_family.name); 194 GNUNET_free (details->token_family.description); 195 json_decref (details->token_family.description_i18n); 196 GNUNET_CRYPTO_blind_sign_pub_decref (details->pub.public_key); 197 GNUNET_CRYPTO_blind_sign_priv_decref (details->priv.private_key); 198 GNUNET_free (details->token_family.cipher_spec); 199 GNUNET_break (0); 200 return GNUNET_DB_STATUS_HARD_ERROR; 201 } 202 GNUNET_free (kind); 203 } 204 return qs; 205 }