merchant

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

taler-merchant-httpd_post-management-instances-INSTANCE-auth.c (11717B)


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