donau

Donation authority for GNU Taler (experimental)
Log | Files | Refs | Submodules | README | LICENSE

donau-httpd_post-batch-submit.c (7938B)


      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 Affero 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 Affero General Public License for more details.
     12 
     13   You should have received a copy of the GNU Affero General Public License along with
     14   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
     15 */
     16 /**
     17  * @file donau-httpd_post-batch-submit.c
     18  * @brief Handle request to insert a submitted receipt.
     19  * @author Johannes Casaburi
     20  */
     21 #include "donau_config.h"
     22 #include <gnunet/gnunet_util_lib.h>
     23 #include <gnunet/gnunet_json_lib.h>
     24 #include <jansson.h>
     25 #include <microhttpd.h>
     26 #include "taler/taler_json_lib.h"
     27 #include "taler/taler_mhd_lib.h"
     28 #include "taler/taler_signatures.h"
     29 #include "donau-httpd_post-batch-submit.h"
     30 #include "donau-httpd_get-keys.h"
     31 #include "donau-database/insert_submitted_receipts.h"
     32 
     33 
     34 /**
     35  * Closure for #insert_submitted_receipts()
     36  */
     37 struct InsertReceiptContext
     38 {
     39   struct DONAU_HashDonorTaxId h_donor_tax_id;
     40   struct DONAU_DonationReceipt *donation_receipts;
     41   size_t num_dr;
     42   uint64_t donation_year;
     43 };
     44 
     45 /**
     46  * Parse a donation receipt encoded in JSON.
     47  *
     48  * @param[out] dr where to return the result
     49  * @param dr_obj json to parse
     50  * @return #GNUNET_OK if all is fine, #GNUNET_SYSERR if @a dr_obj
     51  * is malformed.
     52  */
     53 static enum GNUNET_GenericReturnValue
     54 parse_json_dr (struct DONAU_DonationReceipt *dr,
     55                const json_t *dr_obj)
     56 {
     57   struct GNUNET_JSON_Specification spec[] = {
     58     GNUNET_JSON_spec_fixed_auto ("h_donation_unit_pub",
     59                                  &dr->h_donation_unit_pub),
     60     GNUNET_JSON_spec_fixed_auto ("nonce",
     61                                  &dr->nonce),
     62     GNUNET_JSON_spec_unblinded_signature ("donation_unit_sig",
     63                                           &dr->donation_unit_sig.unblinded_sig),
     64     GNUNET_JSON_spec_end ()
     65   };
     66 
     67   if (GNUNET_OK !=
     68       GNUNET_JSON_parse (dr_obj,
     69                          spec,
     70                          NULL,
     71                          NULL))
     72   {
     73     GNUNET_break_op (0);
     74     return GNUNET_SYSERR;
     75   }
     76 
     77   return GNUNET_OK;
     78 }
     79 
     80 
     81 /**
     82  * Free data in @a irc, but not @a irc itself
     83  *
     84  * @param[in,out] irc data structure to clean up
     85  */
     86 static void
     87 free_irc (struct InsertReceiptContext *irc)
     88 {
     89   for (size_t i = 0; i<irc->num_dr; i++)
     90     GNUNET_CRYPTO_unblinded_sig_decref (irc->donation_receipts[i].
     91                                         donation_unit_sig.unblinded_sig);
     92   GNUNET_free (irc->donation_receipts);
     93 }
     94 
     95 
     96 enum MHD_Result
     97 DH_handler_post_batch_submit (struct DH_RequestContext *rc,
     98                               const json_t *root,
     99                               const char *const args[])
    100 {
    101   struct InsertReceiptContext irc = {0};
    102   const json_t *donation_receipts;
    103   struct GNUNET_JSON_Specification spec[] = {
    104     GNUNET_JSON_spec_fixed_auto ("h_donor_tax_id",
    105                                  &irc.h_donor_tax_id),
    106     GNUNET_JSON_spec_array_const ("donation_receipts",
    107                                   &donation_receipts),
    108     GNUNET_JSON_spec_uint64 ("donation_year",
    109                              &irc.donation_year),
    110     GNUNET_JSON_spec_end ()
    111   };
    112   size_t num_dr;
    113 
    114   (void) args;
    115   {
    116     enum GNUNET_GenericReturnValue res;
    117 
    118     res = TALER_MHD_parse_json_data (rc->connection,
    119                                      root,
    120                                      spec);
    121     if (GNUNET_SYSERR == res)
    122       return MHD_NO; /* hard failure */
    123     if (GNUNET_NO == res)
    124     {
    125       GNUNET_break_op (0);
    126       return MHD_YES; /* failure */
    127     }
    128   }
    129 
    130   /* parse the donation receipts */
    131   num_dr = json_array_size (donation_receipts);
    132 
    133   if (0 == num_dr)
    134   {
    135     GNUNET_break_op (0);
    136     return TALER_MHD_reply_with_error (rc->connection,
    137                                        MHD_HTTP_BAD_REQUEST,
    138                                        TALER_EC_GENERIC_PARAMETER_MALFORMED,
    139                                        "donation_receipts");
    140   }
    141   {
    142     json_t *dr_obj;
    143     size_t index;
    144 
    145     irc.num_dr = num_dr;
    146     irc.donation_receipts = GNUNET_new_array (num_dr,
    147                                               struct DONAU_DonationReceipt);
    148 
    149     json_array_foreach (donation_receipts,
    150                         index,
    151                         dr_obj)
    152     {
    153       if (GNUNET_SYSERR ==
    154           parse_json_dr (&irc.donation_receipts[index],
    155                          dr_obj))
    156       {
    157         GNUNET_break_op (0);
    158         free_irc (&irc);
    159         return TALER_MHD_reply_with_error (rc->connection,
    160                                            MHD_HTTP_BAD_REQUEST,
    161                                            TALER_EC_GENERIC_PARAMETER_MALFORMED,
    162                                            "donation_receipts");
    163       }
    164     }
    165   }
    166 
    167   for (size_t i = 0; i < num_dr; i++)
    168   {
    169     struct DONAU_UniqueDonorIdentifierHashP udi_hash;
    170     struct DH_DonationUnitKey *dk;
    171 
    172     /* Check nonce unique*/
    173     for (size_t j = i + 1; j < num_dr; j++)
    174     {
    175       if (0 ==
    176           GNUNET_memcmp (&irc.donation_receipts[i].nonce,
    177                          &irc.donation_receipts[j].nonce))
    178       {
    179         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    180                     "Donation receipt nonce is not unique!\n");
    181         free_irc (&irc);
    182         return TALER_MHD_reply_with_error (rc->connection,
    183                                            MHD_HTTP_CONFLICT,
    184                                            TALER_EC_DONAU_DONOR_IDENTIFIER_NONCE_REUSE,
    185                                            NULL);
    186       }
    187     }
    188 
    189     /* Check if donation unit exists*/
    190     if (NULL == (dk = DH_keys_donation_unit_by_hash (
    191                    &irc.donation_receipts[i].h_donation_unit_pub)))
    192     {
    193       GNUNET_break_op (0);
    194       free_irc (&irc);
    195       return TALER_MHD_reply_with_error (rc->connection,
    196                                          MHD_HTTP_NOT_FOUND,
    197                                          TALER_EC_DONAU_GENERIC_DONATION_UNIT_UNKNOWN,
    198                                          NULL);
    199     }
    200 
    201     DONAU_unique_donor_id_hash (
    202       &irc.h_donor_tax_id,
    203       &irc.donation_receipts[i].nonce,
    204       &udi_hash);
    205 
    206     /* Check signature*/
    207     if (GNUNET_OK != DONAU_donation_receipt_verify (
    208           &dk->donation_unit_pub,
    209           &udi_hash,
    210           &irc.donation_receipts[i].donation_unit_sig))
    211     {
    212       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    213                   "Donation receipt signature invalid!\n");
    214       free_irc (&irc);
    215       return TALER_MHD_reply_with_error (rc->connection,
    216                                          MHD_HTTP_FORBIDDEN,
    217                                          TALER_EC_DONAU_DONATION_RECEIPT_SIGNATURE_INVALID,
    218                                          NULL);
    219 
    220     }
    221   }
    222 
    223   {
    224     enum GNUNET_DB_QueryStatus qs;
    225 
    226     qs = DONAUDB_insert_submitted_receipts (DH_context,
    227                                             &irc.h_donor_tax_id,
    228                                             num_dr,
    229                                             irc.donation_receipts,
    230                                             irc.donation_year);
    231     free_irc (&irc);
    232     if (qs < 0)
    233     {
    234       GNUNET_break (0);
    235       return TALER_MHD_reply_with_error (rc->connection,
    236                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
    237                                          TALER_EC_GENERIC_DB_STORE_FAILED,
    238                                          "insert_submitted_receipts");
    239     }
    240   }
    241   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    242               "submitted receipts inserted!\n");
    243 
    244   return TALER_MHD_reply_static (
    245     rc->connection,
    246     MHD_HTTP_CREATED,
    247     NULL,
    248     NULL,
    249     0);
    250 }
    251 
    252 
    253 /* end of donau-httpd_post-batch-submit.c */