/*
This file is part of GNU Taler
(C) 2021 Taler Systems SA
GNU Taler is free software; you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation; either version 3,
or (at your option) any later version.
GNU Taler is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public
License along with TALER; see the file COPYING. If not,
see
*/
/**
* @file taler-merchant-httpd_private-post-instances-ID-auth.c
* @brief implementing POST /instances/$ID/auth request handling
* @author Christian Grothoff
* @author Florian Dold
*/
#include "platform.h"
#include "taler-merchant-httpd_private-post-instances-ID-auth.h"
#include "taler-merchant-httpd_helper.h"
#include
/**
* How often do we retry the simple INSERT database transaction?
*/
#define MAX_RETRIES 3
/**
* Change the authentication settings of an instance.
*
* @param mi instance to modify settings of
* @param connection the MHD connection to handle
* @param[in,out] hc context with further information about the request
* @return MHD result code
*/
static MHD_RESULT
post_instances_ID_auth (struct TMH_MerchantInstance *mi,
struct MHD_Connection *connection,
struct TMH_HandlerContext *hc)
{
struct TALER_MERCHANTDB_InstanceAuthSettings ias;
const char *auth_token = NULL;
json_t *jauth = hc->request_body;
{
enum GNUNET_GenericReturnValue ret;
ret = TMH_check_auth_config (connection,
jauth,
&auth_token);
if (GNUNET_OK != ret)
return (GNUNET_NO == ret) ? MHD_YES : MHD_NO;
}
if (NULL == auth_token)
{
memset (&ias.auth_salt,
0,
sizeof (ias.auth_salt));
memset (&ias.auth_hash,
0,
sizeof (ias.auth_hash));
}
else
{
TMH_compute_auth (auth_token,
&ias.auth_salt,
&ias.auth_hash);
}
/* Store the new auth information in the database */
{
enum GNUNET_DB_QueryStatus qs;
for (unsigned int i = 0; istart (TMH_db->cls,
"post /instances/$ID/auth"))
{
return TALER_MHD_reply_with_error (connection,
MHD_HTTP_INTERNAL_SERVER_ERROR,
TALER_EC_GENERIC_DB_START_FAILED,
NULL);
}
/* Make the authentication update a serializable operation.
We first check that the authentication information
that the caller's request authenticated with
is still up to date.
Otherwise, we've detected a conflicting update
to the authentication. */
{
struct TALER_MERCHANTDB_InstanceAuthSettings db_ias;
qs = TMH_db->lookup_instance_auth (TMH_db->cls,
mi->settings.id,
&db_ias);
switch (qs)
{
case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
/* Instance got purged. */
TMH_db->rollback (TMH_db->cls);
return TALER_MHD_reply_with_error (connection,
MHD_HTTP_NOT_FOUND,
TALER_EC_MERCHANT_GENERIC_INSTANCE_UNKNOWN,
NULL);
case GNUNET_DB_STATUS_SOFT_ERROR:
TMH_db->rollback (TMH_db->cls);
goto retry;
case GNUNET_DB_STATUS_HARD_ERROR:
TMH_db->rollback (TMH_db->cls);
return TALER_MHD_reply_with_error (connection,
MHD_HTTP_INTERNAL_SERVER_ERROR,
TALER_EC_GENERIC_DB_FETCH_FAILED,
NULL);
case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
/* Success! */
break;
}
if ( (NULL == TMH_default_auth) &&
(! mi->auth_override) &&
(GNUNET_OK !=
TMH_check_auth (hc->auth_token,
&db_ias.auth_salt,
&db_ias.auth_hash)) )
{
TMH_db->rollback (TMH_db->cls);
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Refusing auth change: old token does not match\n");
return TALER_MHD_reply_with_error (connection,
MHD_HTTP_UNAUTHORIZED,
TALER_EC_MERCHANT_GENERIC_UNAUTHORIZED,
NULL);
}
}
qs = TMH_db->update_instance_auth (TMH_db->cls,
mi->settings.id,
&ias);
if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
{
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
TMH_db->rollback (TMH_db->cls);
if (GNUNET_DB_STATUS_HARD_ERROR == qs)
{
return TALER_MHD_reply_with_error (connection,
MHD_HTTP_INTERNAL_SERVER_ERROR,
TALER_EC_GENERIC_DB_FETCH_FAILED,
NULL);
}
goto retry;
}
qs = TMH_db->commit (TMH_db->cls);
if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
retry:
if (GNUNET_DB_STATUS_SOFT_ERROR != qs)
break; /* success! -- or hard failure */
} /* for .. MAX_RETRIES */
if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
{
return TALER_MHD_reply_with_error (connection,
MHD_HTTP_INTERNAL_SERVER_ERROR,
TALER_EC_GENERIC_DB_COMMIT_FAILED,
NULL);
}
/* Finally, also update our running process */
mi->auth = ias;
}
mi->auth_override = false;
if (0 == strcmp (mi->settings.id,
"default"))
{
/* The default auth string should've been
cleared with the first request
for the default instance. */
GNUNET_assert (NULL == TMH_default_auth);
}
TMH_reload_instances (mi->settings.id);
return TALER_MHD_reply_static (connection,
MHD_HTTP_NO_CONTENT,
NULL,
NULL,
0);
}
MHD_RESULT
TMH_private_post_instances_ID_auth (const struct TMH_RequestHandler *rh,
struct MHD_Connection *connection,
struct TMH_HandlerContext *hc)
{
struct TMH_MerchantInstance *mi = hc->instance;
return post_instances_ID_auth (mi,
connection,
hc);
}
MHD_RESULT
TMH_private_post_instances_default_ID_auth (const struct TMH_RequestHandler *rh,
struct MHD_Connection *connection,
struct TMH_HandlerContext *hc)
{
struct TMH_MerchantInstance *mi;
MHD_RESULT ret;
mi = TMH_lookup_instance (hc->infix);
if (NULL == mi)
{
return TALER_MHD_reply_with_error (connection,
MHD_HTTP_NOT_FOUND,
TALER_EC_MERCHANT_GENERIC_INSTANCE_UNKNOWN,
hc->infix);
}
mi->auth_override = true;
ret = post_instances_ID_auth (mi,
connection,
hc);
return ret;
}
/* end of taler-merchant-httpd_private-post-instances-ID-auth.c */