LCOV - code coverage report
Current view: top level - challengerdb - test_challenger_db.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 71.7 % 244 175
Test Date: 2026-09-09 01:07:11 Functions: 100.0 % 14 14

            Line data    Source code
       1              : /*
       2              :   This file is part of Challenger
       3              :   (C) 2023 Taler Systems SA
       4              : 
       5              :   Challenger 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              :   Challenger 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              :   Challenger; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
      15              : */
      16              : /**
      17              :  * @file challengerdb/test_challenger_db.c
      18              :  * @brief testcase for challenger postgres db plugin
      19              :  * @author Christian Grothoff
      20              :  */
      21              : #include "platform.h"
      22              : #include <gnunet/gnunet_util_lib.h>
      23              : #include <gnunet/gnunet_pq_lib.h>
      24              : #include <taler/taler_util.h>
      25              : #include "challenger_database_lib.h"
      26              : #include "challenger-database/drop_tables.h"
      27              : #include "challenger-database/create_tables.h"
      28              : #include "challenger-database/preflight.h"
      29              : #include "challenger-database/gc.h"
      30              : #include "challenger-database/insert_client.h"
      31              : #include "challenger-database/do_insert_validation.h"
      32              : #include "challenger-database/do_challenge_address.h"
      33              : #include "challenger-database/do_solve_challenge.h"
      34              : #include "challenger-database/do_insert_token.h"
      35              : #include "challenger-database/get_token.h"
      36              : #include "challenger-database/get_validation_pkce.h"
      37              : #include "challenger_util.h"
      38              : #include "pg_helper.h"
      39              : #include "challenger-database/delete_client.h"
      40              : #include "challenger-database/do_insert_validation.h"
      41              : 
      42              : 
      43              : #define FAILIF(cond)                            \
      44              :         do {                                          \
      45              :           if (! (cond)) { break;}                       \
      46              :           GNUNET_break (0);                           \
      47              :           goto drop;                                     \
      48              :         } while (0)
      49              : 
      50              : /**
      51              :  * Secret of the client the tests register and act as.
      52              :  */
      53              : #define CLIENT_SECRET "secret-token:test-secret"
      54              : 
      55              : /**
      56              :  * Redirect URI of the client the tests register.
      57              :  */
      58              : #define CLIENT_URI "http://client.example.com/"
      59              : 
      60              : /**
      61              :  * Global return value for the test.  Initially -1, set to 0 upon
      62              :  * completion.   Other values indicate some kind of error.
      63              :  */
      64              : static int result;
      65              : 
      66              : /**
      67              :  * Handle to the database we are testing.
      68              :  */
      69              : static struct CHALLENGERDB_PostgresContext *pg;
      70              : 
      71              : /**
      72              :  * Serial ID of the client registered by #setup_client().
      73              :  */
      74              : static uint64_t client_id;
      75              : 
      76              : 
      77              : /**
      78              :  * Register the OAuth client all tests act as.
      79              :  *
      80              :  * @return #GNUNET_OK on success
      81              :  */
      82              : static enum GNUNET_GenericReturnValue
      83            1 : setup_client (void)
      84              : {
      85            1 :   bool uri_taken = false;
      86              : 
      87            1 :   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
      88            1 :       CHALLENGERDB_insert_client (pg,
      89              :                                   CLIENT_URI,
      90              :                                   CLIENT_SECRET,
      91              :                                   &client_id,
      92              :                                   &uri_taken))
      93              :   {
      94            0 :     GNUNET_break (0);
      95            0 :     return GNUNET_SYSERR;
      96              :   }
      97            1 :   return GNUNET_OK;
      98              : }
      99              : 
     100              : 
     101              : /**
     102              :  * Create a fresh validation for #client_id and have a TAN transmitted
     103              :  * for it, leaving the validation in the state a user reaches by posting
     104              :  * an address to ``/challenge`` and then walking away.
     105              :  *
     106              :  * @param[out] nonce set to the nonce identifying the new validation
     107              :  * @param[out] pin set to the TAN that was "transmitted"
     108              :  * @return #GNUNET_OK on success
     109              :  */
     110              : static enum GNUNET_GenericReturnValue
     111            5 : challenge_validation (struct CHALLENGER_ValidationNonceP *nonce,
     112              :                       uint32_t *pin)
     113              : {
     114              :   struct GNUNET_TIME_Absolute expiration
     115            5 :     = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_HOURS);
     116              :   json_t *address;
     117            5 :   char *state = NULL;
     118            5 :   char *redirect_uri = NULL;
     119              :   struct GNUNET_TIME_Absolute last_tx_time;
     120              :   uint32_t auth_attempts_left;
     121              :   uint32_t pin_transmissions_left;
     122              :   bool pin_transmit;
     123              :   bool address_refused;
     124              :   bool solved;
     125              :   enum GNUNET_DB_QueryStatus qs;
     126              : 
     127            5 :   GNUNET_CRYPTO_random_block (nonce,
     128              :                               sizeof (*nonce));
     129            5 :   qs = CHALLENGERDB_do_insert_validation (pg,
     130              :                                           client_id,
     131              :                                           CLIENT_SECRET,
     132              :                                           nonce,
     133              :                                           expiration,
     134              :                                           NULL);
     135            5 :   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
     136              :   {
     137            0 :     GNUNET_break (0);
     138            0 :     return GNUNET_SYSERR;
     139              :   }
     140            5 :   address = json_pack ("{s:s}",
     141              :                        "filename",
     142              :                        "test-challenger-db.txt");
     143            5 :   GNUNET_assert (NULL != address);
     144            5 :   *pin = 424242;
     145            5 :   qs = CHALLENGERDB_do_challenge_address (pg,
     146              :                                           nonce,
     147              :                                           address,
     148            5 :                                           GNUNET_TIME_UNIT_ZERO,
     149              :                                           pin,
     150              :                                           &state,
     151              :                                           &last_tx_time,
     152              :                                           &auth_attempts_left,
     153              :                                           &pin_transmissions_left,
     154              :                                           &pin_transmit,
     155              :                                           &redirect_uri,
     156              :                                           &address_refused,
     157              :                                           &solved);
     158            5 :   json_decref (address);
     159              :   /* The TAN is only pending until its transmission is confirmed; this
     160              :      stands in for a successful AUTH_COMMAND run. */
     161            5 :   qs = CHALLENGERDB_do_challenge_address_confirm_pin (pg,
     162              :                                                       nonce,
     163              :                                                       &auth_attempts_left);
     164            5 :   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
     165              :   {
     166            0 :     GNUNET_break (0);
     167            0 :     return GNUNET_SYSERR;
     168              :   }
     169            5 :   GNUNET_free (state);
     170            5 :   GNUNET_free (redirect_uri);
     171            5 :   if ( (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs) ||
     172            5 :        (! pin_transmit) ||
     173            5 :        address_refused ||
     174              :        solved)
     175              :   {
     176            0 :     GNUNET_break (0);
     177            0 :     return GNUNET_SYSERR;
     178              :   }
     179            5 :   return GNUNET_OK;
     180              : }
     181              : 
     182              : 
     183              : /**
     184              :  * Enter @a pin for the validation under @a nonce and check it was accepted.
     185              :  *
     186              :  * @param nonce validation to solve
     187              :  * @param pin TAN to enter
     188              :  * @return #GNUNET_OK on success
     189              :  */
     190              : static enum GNUNET_GenericReturnValue
     191            4 : solve_validation (const struct CHALLENGER_ValidationNonceP *nonce,
     192              :                   uint32_t pin)
     193              : {
     194            4 :   char *state = NULL;
     195            4 :   char *redirect_uri = NULL;
     196              :   uint32_t addr_left;
     197              :   uint32_t auth_attempts_left;
     198              :   uint32_t pin_transmissions_left;
     199              :   bool solved;
     200              :   bool exhausted;
     201              :   bool no_challenge;
     202              :   enum GNUNET_DB_QueryStatus qs;
     203              : 
     204            4 :   qs = CHALLENGERDB_do_solve_challenge (pg,
     205              :                                         nonce,
     206              :                                         pin,
     207              :                                         &solved,
     208              :                                         &exhausted,
     209              :                                         &no_challenge,
     210              :                                         &state,
     211              :                                         &addr_left,
     212              :                                         &auth_attempts_left,
     213              :                                         &pin_transmissions_left,
     214              :                                         &redirect_uri);
     215            4 :   GNUNET_free (state);
     216            4 :   GNUNET_free (redirect_uri);
     217            4 :   if ( (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs) ||
     218            4 :        (! solved) ||
     219            4 :        exhausted ||
     220              :        no_challenge)
     221              :   {
     222            0 :     GNUNET_break (0);
     223            0 :     return GNUNET_SYSERR;
     224              :   }
     225            4 :   return GNUNET_OK;
     226              : }
     227              : 
     228              : 
     229              : /**
     230              :  * Mint an access token for the validation under @a nonce.
     231              :  *
     232              :  * @param client client redeeming the authorization code
     233              :  * @param nonce validation to redeem
     234              :  * @param[out] token set to the (random) access token we tried to store
     235              :  * @return transaction status of CHALLENGERDB_do_insert_token()
     236              :  */
     237              : static enum GNUNET_DB_QueryStatus
     238            7 : insert_token (uint64_t client,
     239              :               const struct CHALLENGER_ValidationNonceP *nonce,
     240              :               struct CHALLENGER_AccessTokenP *token)
     241              : {
     242            7 :   GNUNET_CRYPTO_random_block (token,
     243              :                               sizeof (*token));
     244            7 :   return CHALLENGERDB_do_insert_token (pg,
     245              :                                        nonce,
     246              :                                        client,
     247              :                                        token,
     248              :                                        GNUNET_TIME_UNIT_HOURS,
     249              :                                        GNUNET_TIME_UNIT_DAYS);
     250              : }
     251              : 
     252              : 
     253              : /**
     254              :  * Test that a validation for which the user never entered the correct
     255              :  * TAN cannot be redeemed for an access token, and is not even visible to
     256              :  * the ``/token`` lookup.  Without the ``auth_attempts_left < 0`` guard
     257              :  * anyone able to compute the authorization code could mint an
     258              :  * attestation for an address nobody ever proved control over.
     259              :  *
     260              :  * @return #GNUNET_OK on success
     261              :  */
     262              : static enum GNUNET_GenericReturnValue
     263            1 : test_unsolved_not_redeemable (void)
     264              : {
     265              :   struct CHALLENGER_ValidationNonceP nonce;
     266              :   struct CHALLENGER_AccessTokenP token;
     267              :   uint32_t pin;
     268              : 
     269            1 :   if (GNUNET_OK !=
     270            1 :       challenge_validation (&nonce,
     271              :                             &pin))
     272            0 :     return GNUNET_SYSERR;
     273              :   {
     274              :     char *client_secret;
     275              :     json_t *address;
     276              :     char *client_scope;
     277              :     char *client_state;
     278              :     char *client_redirect_uri;
     279              :     char *code_challenge;
     280              :     uint32_t code_challenge_method;
     281              : 
     282            1 :     if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
     283            1 :         CHALLENGERDB_get_validation_pkce (pg,
     284              :                                           &nonce,
     285              :                                           client_id,
     286              :                                           &client_secret,
     287              :                                           &address,
     288              :                                           &client_scope,
     289              :                                           &client_state,
     290              :                                           &client_redirect_uri,
     291              :                                           &code_challenge,
     292              :                                           &code_challenge_method))
     293              :     {
     294            0 :       GNUNET_break (0);
     295            0 :       return GNUNET_SYSERR;
     296              :     }
     297              :   }
     298            1 :   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
     299            1 :       insert_token (client_id,
     300              :                     &nonce,
     301              :                     &token))
     302              :   {
     303            0 :     GNUNET_break (0);
     304            0 :     return GNUNET_SYSERR;
     305              :   }
     306            1 :   return GNUNET_OK;
     307              : }
     308              : 
     309              : 
     310              : /**
     311              :  * Test that a solved validation is redeemable, and redeemable exactly
     312              :  * once (the authorization code must not be replayable).
     313              :  *
     314              :  * @return #GNUNET_OK on success
     315              :  */
     316              : static enum GNUNET_GenericReturnValue
     317            1 : test_solved_redeemable (void)
     318              : {
     319              :   struct CHALLENGER_ValidationNonceP nonce;
     320              :   struct CHALLENGER_AccessTokenP token;
     321              :   uint32_t pin;
     322              : 
     323            1 :   if ( (GNUNET_OK !=
     324            1 :         challenge_validation (&nonce,
     325            1 :                               &pin)) ||
     326              :        (GNUNET_OK !=
     327            1 :         solve_validation (&nonce,
     328              :                           pin)) )
     329            0 :     return GNUNET_SYSERR;
     330            1 :   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     331            1 :       insert_token (client_id,
     332              :                     &nonce,
     333              :                     &token))
     334              :   {
     335            0 :     GNUNET_break (0);
     336            0 :     return GNUNET_SYSERR;
     337              :   }
     338              :   /* replay: the validation was consumed, so this must not mint a token */
     339            1 :   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
     340            1 :       insert_token (client_id,
     341              :                     &nonce,
     342              :                     &token))
     343              :   {
     344            0 :     GNUNET_break (0);
     345            0 :     return GNUNET_SYSERR;
     346              :   }
     347            1 :   return GNUNET_OK;
     348              : }
     349              : 
     350              : 
     351              : /**
     352              :  * Test that the address expiration stored with a freshly minted token is
     353              :  * in the future, and in particular that it does *not* depend on the
     354              :  * validation's ``last_tx_time``.  The regression this guards against
     355              :  * computed the expiration as ``address_expiration + last_tx_time``, which
     356              :  * lands in 1970 whenever that column was never written.  We force the
     357              :  * column back to 0 to pin the property down without relying on any
     358              :  * particular code path leaving it there.
     359              :  *
     360              :  * @return #GNUNET_OK on success
     361              :  */
     362              : static enum GNUNET_GenericReturnValue
     363            1 : test_address_expiry_is_in_the_future (void)
     364              : {
     365            1 :   struct GNUNET_PQ_ExecuteStatement es[] = {
     366            1 :     GNUNET_PQ_make_execute ("UPDATE validations SET last_tx_time=0;"),
     367              :     GNUNET_PQ_EXECUTE_STATEMENT_END
     368              :   };
     369              :   struct CHALLENGER_ValidationNonceP nonce;
     370              :   struct CHALLENGER_AccessTokenP token;
     371              :   struct GNUNET_TIME_Timestamp address_expiration;
     372              :   struct GNUNET_TIME_Absolute now;
     373            1 :   json_t *address = NULL;
     374              :   uint64_t rowid;
     375              :   uint32_t pin;
     376              :   enum GNUNET_GenericReturnValue ret;
     377              : 
     378            1 :   if ( (GNUNET_OK !=
     379            1 :         challenge_validation (&nonce,
     380            1 :                               &pin)) ||
     381              :        (GNUNET_OK !=
     382            1 :         solve_validation (&nonce,
     383              :                           pin)) )
     384            0 :     return GNUNET_SYSERR;
     385            1 :   if (GNUNET_OK !=
     386            1 :       GNUNET_PQ_exec_statements (pg->conn,
     387              :                                  es))
     388              :   {
     389            0 :     GNUNET_break (0);
     390            0 :     return GNUNET_SYSERR;
     391              :   }
     392            1 :   now = GNUNET_TIME_absolute_get ();
     393            1 :   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     394            1 :       insert_token (client_id,
     395              :                     &nonce,
     396              :                     &token))
     397              :   {
     398            0 :     GNUNET_break (0);
     399            0 :     return GNUNET_SYSERR;
     400              :   }
     401            1 :   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     402            1 :       CHALLENGERDB_get_token (pg,
     403              :                               &token,
     404              :                               &rowid,
     405              :                               &address,
     406              :                               &address_expiration))
     407              :   {
     408            0 :     GNUNET_break (0);
     409            0 :     return GNUNET_SYSERR;
     410              :   }
     411            1 :   ret = GNUNET_OK;
     412            1 :   if (address_expiration.abs_time.abs_value_us <= now.abs_value_us)
     413              :   {
     414            0 :     GNUNET_break (0);
     415            0 :     ret = GNUNET_SYSERR;
     416              :   }
     417            1 :   json_decref (address);
     418            1 :   return ret;
     419              : }
     420              : 
     421              : 
     422              : /**
     423              :  * Test that an expired validation is not consumable, even though the
     424              :  * caller (``/token``) checked the expiration in an earlier, separate
     425              :  * transaction.  Everything get_validation_pkce() filters on has to be
     426              :  * repeated by do_insert_token(), or the gap between the two statements is
     427              :  * an unguarded TOCTOU window.
     428              :  *
     429              :  * @return #GNUNET_OK on success
     430              :  */
     431              : static enum GNUNET_GenericReturnValue
     432            1 : test_expired_not_redeemable (void)
     433              : {
     434            1 :   struct GNUNET_PQ_ExecuteStatement es[] = {
     435            1 :     GNUNET_PQ_make_execute ("UPDATE validations SET expiration_time=1;"),
     436              :     GNUNET_PQ_EXECUTE_STATEMENT_END
     437              :   };
     438              :   struct CHALLENGER_ValidationNonceP nonce;
     439              :   struct CHALLENGER_AccessTokenP token;
     440              :   uint32_t pin;
     441              : 
     442            1 :   if ( (GNUNET_OK !=
     443            1 :         challenge_validation (&nonce,
     444            1 :                               &pin)) ||
     445              :        (GNUNET_OK !=
     446            1 :         solve_validation (&nonce,
     447              :                           pin)) )
     448            0 :     return GNUNET_SYSERR;
     449            1 :   if (GNUNET_OK !=
     450            1 :       GNUNET_PQ_exec_statements (pg->conn,
     451              :                                  es))
     452              :   {
     453            0 :     GNUNET_break (0);
     454            0 :     return GNUNET_SYSERR;
     455              :   }
     456            1 :   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
     457            1 :       insert_token (client_id,
     458              :                     &nonce,
     459              :                     &token))
     460              :   {
     461            0 :     GNUNET_break (0);
     462            0 :     return GNUNET_SYSERR;
     463              :   }
     464            1 :   return GNUNET_OK;
     465              : }
     466              : 
     467              : 
     468              : /**
     469              :  * Test that a validation cannot be consumed by a client it does not
     470              :  * belong to.
     471              :  *
     472              :  * @return #GNUNET_OK on success
     473              :  */
     474              : static enum GNUNET_GenericReturnValue
     475            1 : test_foreign_client_not_redeemable (void)
     476              : {
     477              :   struct CHALLENGER_ValidationNonceP nonce;
     478              :   struct CHALLENGER_AccessTokenP token;
     479              :   uint32_t pin;
     480              : 
     481            1 :   if ( (GNUNET_OK !=
     482            1 :         challenge_validation (&nonce,
     483            1 :                               &pin)) ||
     484              :        (GNUNET_OK !=
     485            1 :         solve_validation (&nonce,
     486              :                           pin)) )
     487            0 :     return GNUNET_SYSERR;
     488            1 :   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
     489            1 :       insert_token (client_id + 1,
     490              :                     &nonce,
     491              :                     &token))
     492              :   {
     493            0 :     GNUNET_break (0);
     494            0 :     return GNUNET_SYSERR;
     495              :   }
     496              :   /* ... and the rightful client still can */
     497            1 :   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     498            1 :       insert_token (client_id,
     499              :                     &nonce,
     500              :                     &token))
     501              :   {
     502            0 :     GNUNET_break (0);
     503            0 :     return GNUNET_SYSERR;
     504              :   }
     505            1 :   return GNUNET_OK;
     506              : }
     507              : 
     508              : 
     509              : /**
     510              :  * Run @a sql directly on the database, bypassing the CHALLENGERDB API.
     511              :  * Used to create states (and to make assertions about the schema) that
     512              :  * the API deliberately cannot produce.
     513              :  *
     514              :  * @param sql SQL statement to execute
     515              :  * @return #GNUNET_OK on success
     516              :  */
     517              : static enum GNUNET_GenericReturnValue
     518            2 : exec_sql (const char *sql)
     519              : {
     520            2 :   struct GNUNET_PQ_ExecuteStatement es[] = {
     521            2 :     GNUNET_PQ_make_execute (sql),
     522              :     GNUNET_PQ_EXECUTE_STATEMENT_END
     523              :   };
     524              : 
     525            2 :   return GNUNET_PQ_exec_statements (pg->conn,
     526              :                                     es);
     527              : }
     528              : 
     529              : 
     530              : /**
     531              :  * Test that a client cannot be inserted silently: a URI that is already
     532              :  * registered must be reported as such, and any *other* unique violation
     533              :  * (notably one on the primary key, which happens once the identity sequence
     534              :  * of 'clients' lags behind max(client_serial_id)) must not be misreported as
     535              :  * a duplicate URI.
     536              :  *
     537              :  * @return #GNUNET_OK on success
     538              :  */
     539              : static enum GNUNET_GenericReturnValue
     540            1 : test_insert_client (void)
     541              : {
     542              :   uint64_t nclient_id;
     543              :   bool uri_taken;
     544              : 
     545            1 :   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     546            1 :       CHALLENGERDB_insert_client (pg,
     547              :                                   "https://example.com/a",
     548              :                                   "secret-token:a",
     549              :                                   &nclient_id,
     550              :                                   &uri_taken))
     551              :   {
     552            0 :     GNUNET_break (0);
     553            0 :     return GNUNET_SYSERR;
     554              :   }
     555            1 :   if (uri_taken)
     556              :   {
     557            0 :     GNUNET_break (0);
     558            0 :     return GNUNET_SYSERR;
     559              :   }
     560              :   /* Same URI again: this one really is a duplicate. */
     561            1 :   if ( (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
     562            1 :         CHALLENGERDB_insert_client (pg,
     563              :                                     "https://example.com/a",
     564              :                                     "secret-token:b",
     565              :                                     &nclient_id,
     566            1 :                                     &uri_taken)) ||
     567            1 :        (! uri_taken) )
     568              :   {
     569            0 :     GNUNET_break (0);
     570            0 :     return GNUNET_SYSERR;
     571              :   }
     572              :   /* Rewind the identity sequence, as a restore that did not restore the
     573              :      sequences leaves it: the next INSERT then collides on the primary key,
     574              :      not on the URI. */
     575            1 :   if (GNUNET_OK !=
     576            1 :       exec_sql ("DO $$ BEGIN"
     577              :                 " PERFORM setval("
     578              :                 "   pg_get_serial_sequence('challenger.clients',"
     579              :                 "                          'client_serial_id'),"
     580              :                 "   (SELECT MIN(client_serial_id) FROM challenger.clients),"
     581              :                 "   false);"
     582              :                 "END $$;"))
     583              :   {
     584            0 :     GNUNET_break (0);
     585            0 :     return GNUNET_SYSERR;
     586              :   }
     587            1 :   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
     588            1 :       CHALLENGERDB_insert_client (pg,
     589              :                                   "https://example.com/b",
     590              :                                   "secret-token:b",
     591              :                                   &nclient_id,
     592              :                                   &uri_taken))
     593              :   {
     594              :     /* The PK collision must not go unnoticed. */
     595            0 :     GNUNET_break (0);
     596            0 :     return GNUNET_SYSERR;
     597              :   }
     598            1 :   if (uri_taken)
     599              :   {
     600              :     /* This is the regression: the failure has nothing to do with the URI,
     601              :        and the operator must not be told that the URI already exists. */
     602            0 :     GNUNET_break (0);
     603            0 :     return GNUNET_SYSERR;
     604              :   }
     605              :   /* Repair the sequence, then the very same insert must work. */
     606            1 :   if (GNUNET_OK !=
     607            1 :       exec_sql ("DO $$ BEGIN"
     608              :                 " PERFORM setval("
     609              :                 "   pg_get_serial_sequence('challenger.clients',"
     610              :                 "                          'client_serial_id'),"
     611              :                 "   (SELECT MAX(client_serial_id) FROM challenger.clients),"
     612              :                 "   true);"
     613              :                 "END $$;"))
     614              :   {
     615            0 :     GNUNET_break (0);
     616            0 :     return GNUNET_SYSERR;
     617              :   }
     618            1 :   if ( (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     619            1 :         CHALLENGERDB_insert_client (pg,
     620              :                                     "https://example.com/b",
     621              :                                     "secret-token:b",
     622              :                                     &nclient_id,
     623            1 :                                     &uri_taken)) ||
     624              :        (uri_taken) )
     625              :   {
     626            0 :     GNUNET_break (0);
     627            0 :     return GNUNET_SYSERR;
     628              :   }
     629            1 :   return GNUNET_OK;
     630              : }
     631              : 
     632              : 
     633              : /**
     634              :  * Test that deleting a client reports how many validations it took with it:
     635              :  * validations_client_serial_id_fkey cascades, so an operator would otherwise
     636              :  * abort in-flight KYC processes without being told.
     637              :  *
     638              :  * @return #GNUNET_OK on success
     639              :  */
     640              : static enum GNUNET_GenericReturnValue
     641            1 : test_delete_client (void)
     642              : {
     643            1 :   const char *uri = "https://example.com/del";
     644            1 :   const char *secret = "secret-token:del";
     645              :   struct GNUNET_TIME_Absolute expiration
     646            1 :     = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_HOURS);
     647              :   uint64_t nclient_id;
     648              :   uint64_t validations_deleted;
     649              :   bool uri_taken;
     650              : 
     651            1 :   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     652            1 :       CHALLENGERDB_insert_client (pg,
     653              :                                   uri,
     654              :                                   secret,
     655              :                                   &nclient_id,
     656              :                                   &uri_taken))
     657              :   {
     658            0 :     GNUNET_break (0);
     659            0 :     return GNUNET_SYSERR;
     660              :   }
     661            3 :   for (unsigned int i = 0; i < 2; i++)
     662              :   {
     663              :     struct CHALLENGER_ValidationNonceP nonce;
     664              : 
     665            2 :     GNUNET_CRYPTO_random_block (&nonce,
     666              :                                 sizeof (nonce));
     667            2 :     if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     668            2 :         CHALLENGERDB_do_insert_validation (pg,
     669              :                                            nclient_id,
     670              :                                            secret,
     671              :                                            &nonce,
     672              :                                            expiration,
     673              :                                            NULL))
     674              :     {
     675            0 :       GNUNET_break (0);
     676            0 :       return GNUNET_SYSERR;
     677              :     }
     678              :   }
     679            1 :   if ( (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     680            1 :         CHALLENGERDB_delete_client (pg,
     681              :                                     uri,
     682            1 :                                     &validations_deleted)) ||
     683            1 :        (2 != validations_deleted) )
     684              :   {
     685            0 :     GNUNET_break (0);
     686            0 :     return GNUNET_SYSERR;
     687              :   }
     688              :   /* Deleting it again finds nothing and discards nothing. */
     689            1 :   if ( (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
     690            1 :         CHALLENGERDB_delete_client (pg,
     691              :                                     uri,
     692            1 :                                     &validations_deleted)) ||
     693            1 :        (0 != validations_deleted) )
     694              :   {
     695            0 :     GNUNET_break (0);
     696            0 :     return GNUNET_SYSERR;
     697              :   }
     698            1 :   return GNUNET_OK;
     699              : }
     700              : 
     701              : 
     702              : /**
     703              :  * Main function that will be run by the scheduler.
     704              :  *
     705              :  * @param cls closure with config
     706              :  */
     707              : static void
     708            1 : run (void *cls)
     709              : {
     710            1 :   struct GNUNET_CONFIGURATION_Handle *cfg = cls;
     711              : 
     712            1 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     713              :               "Connecting\n");
     714            1 :   if (NULL == (pg = CHALLENGERDB_connect_admin (cfg)))
     715              :   {
     716            0 :     result = 77;
     717            0 :     return;
     718              :   }
     719            1 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     720              :               "Connected\n");
     721            1 :   if (GNUNET_OK !=
     722            1 :       CHALLENGERDB_drop_tables (pg))
     723              :   {
     724            1 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     725              :                 "Dropping tables failed\n");
     726              :   }
     727            1 :   if (GNUNET_OK !=
     728            1 :       CHALLENGERDB_create_tables (pg))
     729              :   {
     730            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     731              :                 "Creating tables failed\n");
     732            0 :     goto drop;
     733              :   }
     734            1 :   GNUNET_assert (GNUNET_OK ==
     735              :                  CHALLENGERDB_preflight (pg));
     736              :   {
     737            1 :     struct GNUNET_TIME_Absolute ts = GNUNET_TIME_absolute_get ();
     738              : 
     739            1 :     FAILIF (0 >
     740              :             CHALLENGERDB_gc (pg,
     741              :                              ts));
     742              :   }
     743            1 :   FAILIF (GNUNET_OK !=
     744              :           setup_client ());
     745            1 :   FAILIF (GNUNET_OK !=
     746              :           test_unsolved_not_redeemable ());
     747            1 :   FAILIF (GNUNET_OK !=
     748              :           test_solved_redeemable ());
     749            1 :   FAILIF (GNUNET_OK !=
     750              :           test_address_expiry_is_in_the_future ());
     751            1 :   FAILIF (GNUNET_OK !=
     752              :           test_expired_not_redeemable ());
     753            1 :   FAILIF (GNUNET_OK !=
     754              :           test_foreign_client_not_redeemable ());
     755            1 :   FAILIF (GNUNET_OK !=
     756              :           test_insert_client ());
     757            1 :   FAILIF (GNUNET_OK !=
     758              :           test_delete_client ());
     759            1 :   result = 0;
     760            1 : drop:
     761            1 :   GNUNET_break (GNUNET_OK ==
     762              :                 CHALLENGERDB_drop_tables (pg));
     763            1 :   CHALLENGERDB_disconnect (pg);
     764            1 :   pg = NULL;
     765              : }
     766              : 
     767              : 
     768              : int
     769            1 : main (int argc,
     770              :       char *const argv[])
     771              : {
     772              :   struct GNUNET_CONFIGURATION_Handle *cfg;
     773              : 
     774              :   (void) argc;
     775            1 :   result = EXIT_FAILURE;
     776            1 :   GNUNET_log_setup (argv[0],
     777              :                     "DEBUG",
     778              :                     NULL);
     779            1 :   cfg = GNUNET_CONFIGURATION_create (CHALLENGER_project_data ());
     780            1 :   if (GNUNET_OK !=
     781            1 :       GNUNET_CONFIGURATION_parse (cfg,
     782              :                                   "test_challenger_db_postgres.conf"))
     783              :   {
     784            0 :     GNUNET_break (0);
     785            0 :     return EXIT_NOTCONFIGURED;
     786              :   }
     787            1 :   GNUNET_SCHEDULER_run (&run, cfg);
     788            1 :   GNUNET_CONFIGURATION_destroy (cfg);
     789            1 :   return result;
     790              : }
     791              : 
     792              : 
     793              : /* end of test_challenger_db.c */
        

Generated by: LCOV version 2.0-1