merchant

Merchant backend to process payments, run by merchants
Log | Files | Refs | Submodules | README | LICENSE

taler-merchant-httpd_private-post-instances-ID-auth.c (11645B)


      1 /*
      2   This file is part of GNU Taler
      3   (C) 2021 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-auth.c
     22  * @brief implementing POST /instances/$ID/auth request handling
     23  * @author Christian Grothoff
     24  * @author Florian Dold
     25  */
     26 #include "platform.h"
     27 #include "taler-merchant-httpd_private-post-instances-ID-auth.h"
     28 #include "taler-merchant-httpd_helper.h"
     29 #include "taler-merchant-httpd_mfa.h"
     30 #include <taler/taler_json_lib.h>
     31 
     32 
     33 /**
     34  * How often do we retry the simple INSERT database transaction?
     35  */
     36 #define MAX_RETRIES 3
     37 
     38 
     39 /**
     40  * Change the authentication settings of an instance.
     41  *
     42  * @param mi instance to modify settings of
     43  * @param connection the MHD connection to handle
     44  * @param[in,out] hc context with further information about the request
     45  * @param auth_override The authentication settings for this instance
     46  *   do not apply due to administrative action. Do not check
     47  *   against the DB value when updating the auth token.
     48  * @param tcs set of multi-factor authorizations required
     49  * @return MHD result code
     50  */
     51 static MHD_RESULT
     52 post_instances_ID_auth (struct TMH_MerchantInstance *mi,
     53                         struct MHD_Connection *connection,
     54                         struct TMH_HandlerContext *hc,
     55                         bool auth_override,
     56                         enum TEH_TanChannelSet tcs)
     57 {
     58   struct TALER_MERCHANTDB_InstanceAuthSettings ias;
     59   const char *auth_pw = NULL;
     60   json_t *jauth = hc->request_body;
     61 
     62   {
     63     enum GNUNET_GenericReturnValue ret;
     64 
     65     ret = TMH_check_auth_config (connection,
     66                                  jauth,
     67                                  &auth_pw);
     68     if (GNUNET_OK != ret)
     69       return (GNUNET_NO == ret) ? MHD_YES : MHD_NO;
     70   }
     71 
     72   if ( (0 != (tcs & TEH_TCS_SMS) &&
     73         ( (NULL == mi->settings.phone) ||
     74           (NULL == TMH_helper_sms) ||
     75           (! mi->settings.phone_validated) ) ) )
     76   {
     77     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     78                 "Cannot reset password: SMS factor not available\n");
     79     return TALER_MHD_reply_with_error (
     80       connection,
     81       MHD_HTTP_FORBIDDEN,
     82       TALER_EC_MERCHANT_GENERIC_MFA_MISSING,
     83       "phone_number");
     84   }
     85   if ( (0 != (tcs & TEH_TCS_EMAIL) &&
     86         ( (NULL == mi->settings.email) ||
     87           (NULL == TMH_helper_email) ||
     88           (! mi->settings.email_validated) ) ) )
     89   {
     90     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     91                 "Cannot reset password: E-mail factor not available\n");
     92     return TALER_MHD_reply_with_error (
     93       connection,
     94       MHD_HTTP_FORBIDDEN,
     95       TALER_EC_MERCHANT_GENERIC_MFA_MISSING,
     96       "email");
     97   }
     98   if (! auth_override)
     99   {
    100     enum GNUNET_GenericReturnValue ret = GNUNET_SYSERR; // fix -Wmaybe-uninitialized
    101 
    102     switch (tcs)
    103     {
    104     case TEH_TCS_NONE:
    105       ret = GNUNET_OK;
    106       break;
    107     case TEH_TCS_SMS:
    108       ret = TMH_mfa_challenges_do (hc,
    109                                    TALER_MERCHANT_MFA_CO_AUTH_CONFIGURATION,
    110                                    true,
    111                                    TALER_MERCHANT_MFA_CHANNEL_SMS,
    112                                    mi->settings.phone,
    113                                    TALER_MERCHANT_MFA_CHANNEL_NONE);
    114       break;
    115     case TEH_TCS_EMAIL:
    116       ret = TMH_mfa_challenges_do (hc,
    117                                    TALER_MERCHANT_MFA_CO_AUTH_CONFIGURATION,
    118                                    true,
    119                                    TALER_MERCHANT_MFA_CHANNEL_EMAIL,
    120                                    mi->settings.email,
    121                                    TALER_MERCHANT_MFA_CHANNEL_NONE);
    122       break;
    123     case TEH_TCS_EMAIL_AND_SMS:
    124       ret = TMH_mfa_challenges_do (hc,
    125                                    TALER_MERCHANT_MFA_CO_AUTH_CONFIGURATION,
    126                                    true,
    127                                    TALER_MERCHANT_MFA_CHANNEL_SMS,
    128                                    mi->settings.phone,
    129                                    TALER_MERCHANT_MFA_CHANNEL_EMAIL,
    130                                    mi->settings.email,
    131                                    TALER_MERCHANT_MFA_CHANNEL_NONE);
    132       break;
    133     }
    134     if (GNUNET_OK != ret)
    135     {
    136       return (GNUNET_NO == ret)
    137         ? MHD_YES
    138         : MHD_NO;
    139     }
    140   }
    141 
    142   if (NULL == auth_pw)
    143   {
    144     memset (&ias.auth_salt,
    145             0,
    146             sizeof (ias.auth_salt));
    147     memset (&ias.auth_hash,
    148             0,
    149             sizeof (ias.auth_hash));
    150   }
    151   else
    152   {
    153     TMH_compute_auth (auth_pw,
    154                       &ias.auth_salt,
    155                       &ias.auth_hash);
    156   }
    157 
    158   /* Store the new auth information in the database */
    159   {
    160     enum GNUNET_DB_QueryStatus qs;
    161 
    162     for (unsigned int i = 0; i<MAX_RETRIES; i++)
    163     {
    164       if (GNUNET_OK !=
    165           TMH_db->start (TMH_db->cls,
    166                          "post /instances/$ID/auth"))
    167       {
    168         return TALER_MHD_reply_with_error (connection,
    169                                            MHD_HTTP_INTERNAL_SERVER_ERROR,
    170                                            TALER_EC_GENERIC_DB_START_FAILED,
    171                                            NULL);
    172       }
    173 
    174       /* Make the authentication update a serializable operation.
    175          We first check that the authentication information
    176          that the caller's request authenticated with
    177          is still up to date.
    178          Otherwise, we've detected a conflicting update
    179          to the authentication. */
    180       {
    181         struct TALER_MERCHANTDB_InstanceAuthSettings db_ias;
    182         enum TALER_ErrorCode ec;
    183 
    184         qs = TMH_db->lookup_instance_auth (TMH_db->cls,
    185                                            mi->settings.id,
    186                                            &db_ias);
    187 
    188         switch (qs)
    189         {
    190         case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    191           /* Instance got purged. */
    192           TMH_db->rollback (TMH_db->cls);
    193           return TALER_MHD_reply_with_error (connection,
    194                                              MHD_HTTP_NOT_FOUND,
    195                                              TALER_EC_MERCHANT_GENERIC_INSTANCE_UNKNOWN,
    196                                              NULL);
    197         case GNUNET_DB_STATUS_SOFT_ERROR:
    198           TMH_db->rollback (TMH_db->cls);
    199           goto retry;
    200         case GNUNET_DB_STATUS_HARD_ERROR:
    201           TMH_db->rollback (TMH_db->cls);
    202           return TALER_MHD_reply_with_error (connection,
    203                                              MHD_HTTP_INTERNAL_SERVER_ERROR,
    204                                              TALER_EC_GENERIC_DB_FETCH_FAILED,
    205                                              NULL);
    206         case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    207           /* Success! */
    208           break;
    209         }
    210 
    211         if (! auth_override)
    212         {
    213           // FIXME are we sure what the scope here is?
    214           ec = TMH_check_token (hc->auth_token,
    215                                 mi->settings.id,
    216                                 &hc->auth_scope);
    217           if (TALER_EC_NONE != ec)
    218           {
    219             TMH_db->rollback (TMH_db->cls);
    220             GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    221                         "Refusing auth change: `%s'\n",
    222                         TALER_ErrorCode_get_hint (ec));
    223             return TALER_MHD_reply_with_error (connection,
    224                                                MHD_HTTP_UNAUTHORIZED,
    225                                                TALER_EC_MERCHANT_GENERIC_UNAUTHORIZED,
    226                                                NULL);
    227           }
    228         }
    229       }
    230 
    231       qs = TMH_db->update_instance_auth (TMH_db->cls,
    232                                          mi->settings.id,
    233                                          &ias);
    234       if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
    235       {
    236         GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    237         TMH_db->rollback (TMH_db->cls);
    238         if (GNUNET_DB_STATUS_HARD_ERROR == qs)
    239         {
    240           return TALER_MHD_reply_with_error (connection,
    241                                              MHD_HTTP_INTERNAL_SERVER_ERROR,
    242                                              TALER_EC_GENERIC_DB_FETCH_FAILED,
    243                                              NULL);
    244         }
    245         goto retry;
    246       }
    247       qs = TMH_db->commit (TMH_db->cls);
    248       if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
    249         qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
    250 retry:
    251       if (GNUNET_DB_STATUS_SOFT_ERROR != qs)
    252         break; /* success! -- or hard failure */
    253     } /* for .. MAX_RETRIES */
    254     if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
    255     {
    256       return TALER_MHD_reply_with_error (connection,
    257                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
    258                                          TALER_EC_GENERIC_DB_COMMIT_FAILED,
    259                                          NULL);
    260     }
    261     /* Finally, also update our running process */
    262     mi->auth = ias;
    263   }
    264   TMH_reload_instances (mi->settings.id);
    265   return TALER_MHD_reply_static (connection,
    266                                  MHD_HTTP_NO_CONTENT,
    267                                  NULL,
    268                                  NULL,
    269                                  0);
    270 }
    271 
    272 
    273 MHD_RESULT
    274 TMH_private_post_instances_ID_auth (const struct TMH_RequestHandler *rh,
    275                                     struct MHD_Connection *connection,
    276                                     struct TMH_HandlerContext *hc)
    277 {
    278   struct TMH_MerchantInstance *mi = hc->instance;
    279 
    280   return post_instances_ID_auth (mi,
    281                                  connection,
    282                                  hc,
    283                                  false,
    284                                  TEH_TCS_NONE);
    285 }
    286 
    287 
    288 MHD_RESULT
    289 TMH_public_post_instances_ID_auth (const struct TMH_RequestHandler *rh,
    290                                    struct MHD_Connection *connection,
    291                                    struct TMH_HandlerContext *hc)
    292 {
    293   struct TMH_MerchantInstance *mi = hc->instance;
    294 
    295   return post_instances_ID_auth (mi,
    296                                  connection,
    297                                  hc,
    298                                  false,
    299                                  TEH_mandatory_tan_channels);
    300 }
    301 
    302 
    303 MHD_RESULT
    304 TMH_private_post_instances_default_ID_auth (
    305   const struct TMH_RequestHandler *rh,
    306   struct MHD_Connection *connection,
    307   struct TMH_HandlerContext *hc)
    308 {
    309   struct TMH_MerchantInstance *mi;
    310   MHD_RESULT ret;
    311 
    312   if ( (NULL == hc->infix) ||
    313        (0 == strcmp ("admin",
    314                      hc->infix)) )
    315   {
    316     GNUNET_break_op (0);
    317     return TALER_MHD_reply_with_error (
    318       connection,
    319       MHD_HTTP_FORBIDDEN,
    320       TALER_EC_MERCHANT_GENERIC_MFA_MISSING,
    321       "not allowed for 'admin' account");
    322   }
    323   mi = TMH_lookup_instance (hc->infix);
    324   if (NULL == mi)
    325   {
    326     return TALER_MHD_reply_with_error (
    327       connection,
    328       MHD_HTTP_NOT_FOUND,
    329       TALER_EC_MERCHANT_GENERIC_INSTANCE_UNKNOWN,
    330       hc->infix);
    331   }
    332   ret = post_instances_ID_auth (mi,
    333                                 connection,
    334                                 hc,
    335                                 true,
    336                                 TEH_TCS_NONE);
    337   return ret;
    338 }
    339 
    340 
    341 /* end of taler-merchant-httpd_private-post-instances-ID-auth.c */