diff options
Diffstat (limited to 'src/kyclogic')
| -rw-r--r-- | src/kyclogic/kyclogic_api.c | 78 | ||||
| -rw-r--r-- | src/kyclogic/plugin_kyclogic_oauth2.c | 186 | ||||
| -rw-r--r-- | src/kyclogic/plugin_kyclogic_template.c | 2 | ||||
| -rw-r--r-- | src/kyclogic/taler-exchange-kyc-tester.c | 5 | 
4 files changed, 256 insertions, 15 deletions
| diff --git a/src/kyclogic/kyclogic_api.c b/src/kyclogic/kyclogic_api.c index f2d31acf..84b88f33 100644 --- a/src/kyclogic/kyclogic_api.c +++ b/src/kyclogic/kyclogic_api.c @@ -279,12 +279,22 @@ load_logic (const struct GNUNET_CONFIGURATION_Handle *cfg,    GNUNET_asprintf (&lib_name,                     "libtaler_plugin_kyclogic_%s",                     name); +  for (unsigned int i = 0; i<num_kyc_logics; i++) +    if (0 == strcmp (lib_name, +                     kyc_logics[i]->library_name)) +    { +      GNUNET_free (lib_name); +      return kyc_logics[i]; +    }    plugin = GNUNET_PLUGIN_load (lib_name,                                 (void *) cfg);    if (NULL != plugin)      plugin->library_name = lib_name;    else      GNUNET_free (lib_name); +  GNUNET_array_append (kyc_logics, +                       num_kyc_logics, +                       plugin);    return plugin;  } @@ -471,6 +481,14 @@ add_provider (const struct GNUNET_CONFIGURATION_Handle *cfg,  } +/** + * Parse configuration @a cfg in section @a section for + * the specification of a KYC trigger. + * + * @param cfg configuration to parse + * @param section configuration section to parse + * @return #GNUNET_OK on success + */  static enum GNUNET_GenericReturnValue  add_trigger (const struct GNUNET_CONFIGURATION_Handle *cfg,               const char *section) @@ -797,6 +815,9 @@ eval_trigger (void *cls,    struct GNUNET_TIME_Relative duration;    bool bump = true; +  GNUNET_log (GNUNET_ERROR_TYPE_INFO, +              "KYC check with new amount %s\n", +              TALER_amount2s (amount));    duration = GNUNET_TIME_absolute_get_duration (date);    if (ttc->have_total)    { @@ -812,19 +833,31 @@ eval_trigger (void *cls,    else    {      ttc->total = *amount; +    ttc->have_total = true;    } +  GNUNET_log (GNUNET_ERROR_TYPE_INFO, +              "KYC check: new total is %s\n", +              TALER_amount2s (&ttc->total));    for (unsigned int i = ttc->start; i<num_kyc_triggers; i++)    {      const struct TALER_KYCLOGIC_KycTrigger *kt = kyc_triggers[i];      if (ttc->event != kt->trigger) +    { +      GNUNET_log (GNUNET_ERROR_TYPE_INFO, +                  "KYC check #%u: trigger type does not match\n", +                  i);        continue; +    }      duration = GNUNET_TIME_relative_max (duration,                                           kt->timeframe);      if (GNUNET_TIME_relative_cmp (kt->timeframe,                                    >,                                    duration))      { +      GNUNET_log (GNUNET_ERROR_TYPE_INFO, +                  "KYC check #%u: amount is beyond time limit\n", +                  i);        if (bump)          ttc->start = i;        return GNUNET_OK; @@ -833,6 +866,9 @@ eval_trigger (void *cls,          TALER_amount_cmp (&ttc->total,                            &kt->threshold))      { +      GNUNET_log (GNUNET_ERROR_TYPE_INFO, +                  "KYC check #%u: amount is below treshold\n", +                  i);        if (bump)          ttc->start = i;        bump = false; @@ -848,6 +884,9 @@ eval_trigger (void *cls,        for (unsigned int k = 0; k<*ttc->needed_cnt; k++)          if (ttc->needed[k] == rc)          { +          GNUNET_log (GNUNET_ERROR_TYPE_INFO, +                      "KYC rule #%u already listed\n", +                      j);            found = true;            break;          } @@ -857,6 +896,11 @@ eval_trigger (void *cls,          (*ttc->needed_cnt)++;        }      } +    GNUNET_log (GNUNET_ERROR_TYPE_INFO, +                "KYC check #%u (%s) is applicable, %u checks needed so far\n", +                i, +                ttc->needed[(*ttc->needed_cnt) - 1]->name, +                *ttc->needed_cnt);    }    if (bump)      return GNUNET_NO; /* we hit all possible triggers! */ @@ -903,13 +947,22 @@ remove_satisfied (void *cls,      if (0 != strcasecmp (provider_name,                           kp->provider_section_name))        continue; +    GNUNET_log (GNUNET_ERROR_TYPE_INFO, +                "Provider `%s' satisfied\n", +                provider_name);      for (unsigned int j = 0; j<kp->num_checks; j++)      {        const struct TALER_KYCLOGIC_KycCheck *kc = kp->provided_checks[j]; +      GNUNET_log (GNUNET_ERROR_TYPE_INFO, +                  "Provider satisfies check `%s'\n", +                  kc->name);        for (unsigned int k = 0; k<*rc->needed_cnt; k++)          if (kc == rc->needed[k])          { +          GNUNET_log (GNUNET_ERROR_TYPE_INFO, +                      "Removing check `%s' from list\n", +                      kc->name);            rc->needed[k] = rc->needed[*rc->needed_cnt - 1];            (*rc->needed_cnt)--;            if (0 == *rc->needed_cnt) @@ -973,15 +1026,14 @@ TALER_KYCLOGIC_kyc_test_required (enum TALER_KYCLOGIC_KycTriggerEvent event,      /* Check what provider checks are already satisfied for h_payto (with         database), remove those from the 'needed' array. */ -    GNUNET_break (0); -    // FIXME: do via callback!      qs = ki (ki_cls,               h_payto, -             & -             remove_satisfied, +             &remove_satisfied,               &rc);      GNUNET_break (qs >= 0);  // FIXME: handle DB failure more nicely?    } +  if (0 == needed_cnt) +    return NULL;    /* Count maximum number of remaining checks covered by any       provider */ @@ -1059,4 +1111,22 @@ TALER_KYCLOGIC_kyc_get_logic (const char *provider_section_name,  } +void +TALER_KYCLOGIC_kyc_iterate_thresholds ( +  enum TALER_KYCLOGIC_KycTriggerEvent event, +  TALER_KYCLOGIC_KycThresholdIterator it, +  void *it_cls) +{ +  for (unsigned int i = 0; i<num_kyc_triggers; i++) +  { +    const struct TALER_KYCLOGIC_KycTrigger *kt = kyc_triggers[i]; + +    if (event != kt->trigger) +      continue; +    it (it_cls, +        &kt->threshold); +  } +} + +  /* end of taler-exchange-httpd_kyc.c */ diff --git a/src/kyclogic/plugin_kyclogic_oauth2.c b/src/kyclogic/plugin_kyclogic_oauth2.c index cbf5ea3a..b9384e8f 100644 --- a/src/kyclogic/plugin_kyclogic_oauth2.c +++ b/src/kyclogic/plugin_kyclogic_oauth2.c @@ -69,6 +69,11 @@ struct TALER_KYCLOGIC_ProviderDetails    struct PluginState *ps;    /** +   * Configuration section that configured us. +   */ +  char *section; + +  /**     * URL of the OAuth2.0 endpoint for KYC checks.     * (token/auth)     */ @@ -265,6 +270,7 @@ struct TALER_KYCLOGIC_WebhookHandle  static void  oauth2_unload_configuration (struct TALER_KYCLOGIC_ProviderDetails *pd)  { +  GNUNET_free (pd->section);    GNUNET_free (pd->auth_url);    GNUNET_free (pd->login_url);    GNUNET_free (pd->info_url); @@ -292,6 +298,7 @@ oauth2_load_configuration (void *cls,    pd = GNUNET_new (struct TALER_KYCLOGIC_ProviderDetails);    pd->ps = ps; +  pd->section = GNUNET_strdup (provider_section_name);    if (GNUNET_OK !=        GNUNET_CONFIGURATION_get_value_time (ps->cfg,                                             provider_section_name, @@ -467,9 +474,10 @@ initiate_task (void *cls)    hps = GNUNET_STRINGS_data_to_string_alloc (&ih->h_payto,                                               sizeof (ih->h_payto));    GNUNET_asprintf (&redirect_uri, -                   "%s/kyc-proof/%s/oauth2/%s", +                   "%s/kyc-proof/%s/%s/%s",                     ps->exchange_base_url,                     hps, +                   pd->section,                     legi_s);    redirect_uri_encoded = TALER_urlencode (redirect_uri);    GNUNET_free (redirect_uri); @@ -532,7 +540,11 @@ oauth2_initiate (void *cls,  static void  oauth2_initiate_cancel (struct TALER_KYCLOGIC_InitiateHandle *ih)  { -  GNUNET_SCHEDULER_cancel (ih->task); +  if (NULL != ih->task) +  { +    GNUNET_SCHEDULER_cancel (ih->task); +    ih->task = NULL; +  }    GNUNET_free (ih);  } @@ -659,6 +671,9 @@ parse_proof_success_reply (struct TALER_KYCLOGIC_ProofHandle *ph,    if (GNUNET_OK != res)    {      GNUNET_break_op (0); +    json_dumpf (j, +                stderr, +                JSON_INDENT (2));      ph->status = TALER_KYCLOGIC_STATUS_PROVIDER_FAILED;      ph->response        = TALER_MHD_make_error ( @@ -691,6 +706,9 @@ parse_proof_success_reply (struct TALER_KYCLOGIC_ProofHandle *ph,      if (GNUNET_OK != res)      {        GNUNET_break_op (0); +      json_dumpf (data, +                  stderr, +                  JSON_INDENT (2));        ph->status = TALER_KYCLOGIC_STATUS_PROVIDER_FAILED;        ph->response          = TALER_MHD_make_error ( @@ -741,6 +759,9 @@ handle_curl_proof_finished (void *cls,                                 j);      break;    default: +    GNUNET_log (GNUNET_ERROR_TYPE_WARNING, +                "OAuth2.0 info URL returned HTTP status %u\n", +                (unsigned int) response_code);      handle_proof_error (ph,                          j);      break; @@ -751,6 +772,147 @@ handle_curl_proof_finished (void *cls,  /** + * After we are done with the CURL interaction we + * need to fetch the user's account details. + * + * @param cls our `struct KycProofContext` + * @param response_code HTTP response code from server, 0 on hard error + * @param response in JSON, NULL if response was not in JSON format + */ +static void +handle_curl_login_finished (void *cls, +                            long response_code, +                            const void *response) +{ +  struct TALER_KYCLOGIC_ProofHandle *ph = cls; +  const json_t *j = response; + +  ph->job = NULL; +  switch (response_code) +  { +  case MHD_HTTP_OK: +    { +      const char *access_token; +      const char *token_type; +      uint64_t expires_in_s; +      const char *refresh_token; +      struct GNUNET_JSON_Specification spec[] = { +        GNUNET_JSON_spec_string ("access_token", +                                 &access_token), +        GNUNET_JSON_spec_string ("token_type", +                                 &token_type), +        GNUNET_JSON_spec_uint64 ("expires_in", +                                 &expires_in_s), +        GNUNET_JSON_spec_string ("refresh_token", +                                 &refresh_token), +        GNUNET_JSON_spec_end () +      }; +      CURL *eh; + +      { +        enum GNUNET_GenericReturnValue res; +        const char *emsg; +        unsigned int line; + +        res = GNUNET_JSON_parse (j, +                                 spec, +                                 &emsg, +                                 &line); +        if (GNUNET_OK != res) +        { +          GNUNET_break_op (0); +          ph->response +            = TALER_MHD_make_error ( +                TALER_EC_EXCHANGE_KYC_PROOF_BACKEND_INVALID_RESPONSE, +                "Unexpected response from KYC gateway"); +          ph->http_status +            = MHD_HTTP_BAD_GATEWAY; +          break; +        } +      } +      if (0 != strcasecmp (token_type, +                           "bearer")) +      { +        GNUNET_break_op (0); +        ph->response +          = TALER_MHD_make_error ( +              TALER_EC_EXCHANGE_KYC_PROOF_BACKEND_INVALID_RESPONSE, +              "Unexpected token type in response from KYC gateway"); +        ph->http_status +          = MHD_HTTP_BAD_GATEWAY; +        break; +      } + +      /* We guard against a few characters that could +         conceivably be abused to mess with the HTTP header */ +      if ( (NULL != strchr (access_token, +                            '\n')) || +           (NULL != strchr (access_token, +                            '\r')) || +           (NULL != strchr (access_token, +                            ' ')) || +           (NULL != strchr (access_token, +                            ';')) ) +      { +        GNUNET_break_op (0); +        ph->response +          = TALER_MHD_make_error ( +              TALER_EC_EXCHANGE_KYC_PROOF_BACKEND_INVALID_RESPONSE, +              "Illegal character in access token"); +        ph->http_status +          = MHD_HTTP_BAD_GATEWAY; +        break; +      } + +      eh = curl_easy_init (); +      if (NULL == eh) +      { +        GNUNET_break_op (0); +        ph->response +          = TALER_MHD_make_error ( +              TALER_EC_GENERIC_ALLOCATION_FAILURE, +              "curl_easy_init"); +        ph->http_status +          = MHD_HTTP_INTERNAL_SERVER_ERROR; +        break; +      } +      GNUNET_assert (CURLE_OK == +                     curl_easy_setopt (eh, +                                       CURLOPT_URL, +                                       ph->pd->info_url)); +      { +        char *hdr; +        struct curl_slist *slist; + +        GNUNET_asprintf (&hdr, +                         "%s: Bearer %s", +                         MHD_HTTP_HEADER_AUTHORIZATION, +                         access_token); +        slist = curl_slist_append (NULL, +                                   hdr); +        ph->job = GNUNET_CURL_job_add2 (ph->pd->ps->curl_ctx, +                                        eh, +                                        slist, +                                        &handle_curl_proof_finished, +                                        ph); +        curl_slist_free_all (slist); +        GNUNET_free (hdr); +      } +      return; +    } +  default: +    GNUNET_log (GNUNET_ERROR_TYPE_WARNING, +                "OAuth2.0 login URL returned HTTP status %u\n", +                (unsigned int) response_code); +    handle_proof_error (ph, +                        j); +    break; +  } +  return_proof_response (ph); +} + + +/**   * Check KYC status and return status to human.   *   * @param cls the @e cls of this struct with the plugin-specific state @@ -758,6 +920,7 @@ handle_curl_proof_finished (void *cls,   * @param url_path rest of the URL after `/kyc-webhook/`   * @param connection MHD connection object (for HTTP headers)   * @param account_id which account to trigger process for + * @param legi_row row in the table the legitimization is for   * @param provider_user_id user ID (or NULL) the proof is for   * @param provider_legitimization_id legitimization ID the proof is for   * @param cb function to call with the result @@ -770,6 +933,7 @@ oauth2_proof (void *cls,                const char *const url_path[],                struct MHD_Connection *connection,                const struct TALER_PaytoHashP *account_id, +              uint64_t legi_row,                const char *provider_user_id,                const char *provider_legitimization_id,                TALER_KYCLOGIC_ProofCallback cb, @@ -779,16 +943,20 @@ oauth2_proof (void *cls,    struct TALER_KYCLOGIC_ProofHandle *ph;    const char *code; -  if (strlen (provider_legitimization_id) >= -      sizeof (ph->provider_legitimization_id)) +  GNUNET_break (NULL == provider_user_id); +  ph = GNUNET_new (struct TALER_KYCLOGIC_ProofHandle); +  GNUNET_snprintf (ph->provider_legitimization_id, +                   sizeof (ph->provider_legitimization_id), +                   "%llu", +                   (unsigned long long) legi_row); +  if ( (NULL != provider_legitimization_id) && +       (0 != strcmp (provider_legitimization_id, +                     ph->provider_legitimization_id)))    {      GNUNET_break (0); +    GNUNET_free (ph);      return NULL;    } -  GNUNET_break (NULL == provider_user_id); -  ph = GNUNET_new (struct TALER_KYCLOGIC_ProofHandle); -  strcpy (ph->provider_legitimization_id, -          provider_legitimization_id);    ph->pd = pd;    ph->connection = connection;    ph->h_payto = *account_id; @@ -891,7 +1059,7 @@ oauth2_proof (void *cls,    ph->job = GNUNET_CURL_job_add (ps->curl_ctx,                                   ph->eh, -                                 &handle_curl_proof_finished, +                                 &handle_curl_login_finished,                                   ph);    return ph;  } diff --git a/src/kyclogic/plugin_kyclogic_template.c b/src/kyclogic/plugin_kyclogic_template.c index f5e583db..7765061e 100644 --- a/src/kyclogic/plugin_kyclogic_template.c +++ b/src/kyclogic/plugin_kyclogic_template.c @@ -253,6 +253,7 @@ template_initiate_cancel (struct TALER_KYCLOGIC_InitiateHandle *ih)   * @param url_path rest of the URL after `/kyc-webhook/`   * @param connection MHD connection object (for HTTP headers)   * @param account_id which account to trigger process for + * @param legi_row row in the table the legitimization is for   * @param provider_user_id user ID (or NULL) the proof is for   * @param provider_legitimization_id legitimization ID the proof is for   * @param cb function to call with the result @@ -265,6 +266,7 @@ template_proof (void *cls,                  const char *const url_path[],                  struct MHD_Connection *connection,                  const struct TALER_PaytoHashP *account_id, +                uint64_t legi_row,                  const char *provider_user_id,                  const char *provider_legitimization_id,                  TALER_KYCLOGIC_ProofCallback cb, diff --git a/src/kyclogic/taler-exchange-kyc-tester.c b/src/kyclogic/taler-exchange-kyc-tester.c index eecd6b83..d1265626 100644 --- a/src/kyclogic/taler-exchange-kyc-tester.c +++ b/src/kyclogic/taler-exchange-kyc-tester.c @@ -754,6 +754,7 @@ handler_kyc_proof_get (                           &args[2],                           rc->connection,                           &h_payto, +                         kyc_row_id,                           cmd_provider_user_id,                           cmd_provider_legitimization_id,                           &proof_cb, @@ -1456,7 +1457,7 @@ main (int argc,        "use the given provider user ID (overridden if -i is also used)",        &cmd_provider_user_id),      GNUNET_GETOPT_option_string ( -      'l', +      'U',        "legitimization",        "ID",        "use the given provider legitimization ID (overridden if -i is also used)", @@ -1464,7 +1465,7 @@ main (int argc,      GNUNET_GETOPT_option_base32_fixed_size (        'p',        "payto-hash", -      "URI", +      "HASH",        "base32 encoding of the hash of a payto://-URI to use for the account (otherwise a random value will be used)",        &cmd_line_h_payto,        sizeof (cmd_line_h_payto)), | 
