Line data Source code
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/taler_json_lib.h>
30 :
31 :
32 : /**
33 : * How often do we retry the simple INSERT database transaction?
34 : */
35 : #define MAX_RETRIES 3
36 :
37 :
38 : /**
39 : * Change the authentication settings of an instance.
40 : *
41 : * @param mi instance to modify settings of
42 : * @param connection the MHD connection to handle
43 : * @param[in,out] hc context with further information about the request
44 : * @return MHD result code
45 : */
46 : static MHD_RESULT
47 0 : post_instances_ID_auth (struct TMH_MerchantInstance *mi,
48 : struct MHD_Connection *connection,
49 : struct TMH_HandlerContext *hc)
50 : {
51 : struct TALER_MERCHANTDB_InstanceAuthSettings ias;
52 0 : const char *auth_token = NULL;
53 0 : json_t *jauth = hc->request_body;
54 :
55 : {
56 : enum GNUNET_GenericReturnValue ret;
57 :
58 0 : ret = TMH_check_auth_config (connection,
59 : jauth,
60 : &auth_token);
61 0 : if (GNUNET_OK != ret)
62 0 : return (GNUNET_NO == ret) ? MHD_YES : MHD_NO;
63 : }
64 :
65 0 : if (NULL == auth_token)
66 : {
67 0 : memset (&ias.auth_salt,
68 : 0,
69 : sizeof (ias.auth_salt));
70 0 : memset (&ias.auth_hash,
71 : 0,
72 : sizeof (ias.auth_hash));
73 : }
74 : else
75 : {
76 0 : TMH_compute_auth (auth_token,
77 : &ias.auth_salt,
78 : &ias.auth_hash);
79 : }
80 :
81 : /* Store the new auth information in the database */
82 : {
83 : enum GNUNET_DB_QueryStatus qs;
84 :
85 0 : for (unsigned int i = 0; i<MAX_RETRIES; i++)
86 : {
87 0 : if (GNUNET_OK !=
88 0 : TMH_db->start (TMH_db->cls,
89 : "post /instances/$ID/auth"))
90 : {
91 0 : return TALER_MHD_reply_with_error (connection,
92 : MHD_HTTP_INTERNAL_SERVER_ERROR,
93 : TALER_EC_GENERIC_DB_START_FAILED,
94 : NULL);
95 : }
96 :
97 : /* Make the authentication update a serializable operation.
98 : We first check that the authentication information
99 : that the caller's request authenticated with
100 : is still up to date.
101 : Otherwise, we've detected a conflicting update
102 : to the authentication. */
103 : {
104 : struct TALER_MERCHANTDB_InstanceAuthSettings db_ias;
105 :
106 0 : qs = TMH_db->lookup_instance_auth (TMH_db->cls,
107 0 : mi->settings.id,
108 : &db_ias);
109 :
110 0 : switch (qs)
111 : {
112 0 : case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
113 : /* Instance got purged. */
114 0 : TMH_db->rollback (TMH_db->cls);
115 0 : return TALER_MHD_reply_with_error (connection,
116 : MHD_HTTP_NOT_FOUND,
117 : TALER_EC_MERCHANT_GENERIC_INSTANCE_UNKNOWN,
118 : NULL);
119 0 : case GNUNET_DB_STATUS_SOFT_ERROR:
120 0 : TMH_db->rollback (TMH_db->cls);
121 0 : goto retry;
122 0 : case GNUNET_DB_STATUS_HARD_ERROR:
123 0 : TMH_db->rollback (TMH_db->cls);
124 0 : return TALER_MHD_reply_with_error (connection,
125 : MHD_HTTP_INTERNAL_SERVER_ERROR,
126 : TALER_EC_GENERIC_DB_FETCH_FAILED,
127 : NULL);
128 0 : case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
129 : /* Success! */
130 0 : break;
131 : }
132 :
133 0 : if ( (NULL == TMH_default_auth) &&
134 0 : (! mi->auth_override) &&
135 : (GNUNET_OK !=
136 0 : TMH_check_auth (hc->auth_token,
137 : &db_ias.auth_salt,
138 : &db_ias.auth_hash)) )
139 : {
140 0 : TMH_db->rollback (TMH_db->cls);
141 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
142 : "Refusing auth change: old token does not match\n");
143 0 : return TALER_MHD_reply_with_error (connection,
144 : MHD_HTTP_UNAUTHORIZED,
145 : TALER_EC_MERCHANT_GENERIC_UNAUTHORIZED,
146 : NULL);
147 : }
148 : }
149 :
150 0 : qs = TMH_db->update_instance_auth (TMH_db->cls,
151 0 : mi->settings.id,
152 : &ias);
153 0 : if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
154 : {
155 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
156 0 : TMH_db->rollback (TMH_db->cls);
157 0 : if (GNUNET_DB_STATUS_HARD_ERROR == qs)
158 : {
159 0 : return TALER_MHD_reply_with_error (connection,
160 : MHD_HTTP_INTERNAL_SERVER_ERROR,
161 : TALER_EC_GENERIC_DB_FETCH_FAILED,
162 : NULL);
163 : }
164 0 : goto retry;
165 : }
166 0 : qs = TMH_db->commit (TMH_db->cls);
167 0 : if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
168 0 : qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
169 0 : retry:
170 0 : if (GNUNET_DB_STATUS_SOFT_ERROR != qs)
171 0 : break; /* success! -- or hard failure */
172 : } /* for .. MAX_RETRIES */
173 0 : if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
174 : {
175 0 : return TALER_MHD_reply_with_error (connection,
176 : MHD_HTTP_INTERNAL_SERVER_ERROR,
177 : TALER_EC_GENERIC_DB_COMMIT_FAILED,
178 : NULL);
179 : }
180 : /* Finally, also update our running process */
181 0 : mi->auth = ias;
182 : }
183 0 : mi->auth_override = false;
184 0 : if (0 == strcmp (mi->settings.id,
185 : "default"))
186 : {
187 : /* The default auth string should've been
188 : cleared with the first request
189 : for the default instance. */
190 0 : GNUNET_assert (NULL == TMH_default_auth);
191 : }
192 0 : TMH_reload_instances (mi->settings.id);
193 0 : return TALER_MHD_reply_static (connection,
194 : MHD_HTTP_NO_CONTENT,
195 : NULL,
196 : NULL,
197 : 0);
198 : }
199 :
200 :
201 : MHD_RESULT
202 0 : TMH_private_post_instances_ID_auth (const struct TMH_RequestHandler *rh,
203 : struct MHD_Connection *connection,
204 : struct TMH_HandlerContext *hc)
205 : {
206 0 : struct TMH_MerchantInstance *mi = hc->instance;
207 :
208 0 : return post_instances_ID_auth (mi,
209 : connection,
210 : hc);
211 : }
212 :
213 :
214 : MHD_RESULT
215 0 : TMH_private_post_instances_default_ID_auth (const struct TMH_RequestHandler *rh,
216 : struct MHD_Connection *connection,
217 : struct TMH_HandlerContext *hc)
218 : {
219 : struct TMH_MerchantInstance *mi;
220 : MHD_RESULT ret;
221 :
222 0 : mi = TMH_lookup_instance (hc->infix);
223 0 : if (NULL == mi)
224 : {
225 0 : return TALER_MHD_reply_with_error (connection,
226 : MHD_HTTP_NOT_FOUND,
227 : TALER_EC_MERCHANT_GENERIC_INSTANCE_UNKNOWN,
228 0 : hc->infix);
229 : }
230 0 : mi->auth_override = true;
231 0 : ret = post_instances_ID_auth (mi,
232 : connection,
233 : hc);
234 0 : mi->auth_override = false;
235 0 : return ret;
236 : }
237 :
238 :
239 : /* end of taler-merchant-httpd_private-post-instances-ID-auth.c */
|