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 9 : 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 9 : const char *auth_pw = NULL;
53 9 : json_t *jauth = hc->request_body;
54 :
55 : {
56 : enum GNUNET_GenericReturnValue ret;
57 :
58 9 : ret = TMH_check_auth_config (connection,
59 : jauth,
60 : &auth_pw);
61 9 : if (GNUNET_OK != ret)
62 0 : return (GNUNET_NO == ret) ? MHD_YES : MHD_NO;
63 : }
64 :
65 9 : if (NULL == auth_pw)
66 : {
67 2 : memset (&ias.auth_salt,
68 : 0,
69 : sizeof (ias.auth_salt));
70 2 : memset (&ias.auth_hash,
71 : 0,
72 : sizeof (ias.auth_hash));
73 : }
74 : else
75 : {
76 7 : TMH_compute_auth (auth_pw,
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 9 : for (unsigned int i = 0; i<MAX_RETRIES; i++)
86 : {
87 9 : if (GNUNET_OK !=
88 9 : 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 : enum TALER_ErrorCode ec;
106 :
107 9 : qs = TMH_db->lookup_instance_auth (TMH_db->cls,
108 9 : mi->settings.id,
109 : &db_ias);
110 :
111 9 : switch (qs)
112 : {
113 0 : case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
114 : /* Instance got purged. */
115 0 : TMH_db->rollback (TMH_db->cls);
116 0 : return TALER_MHD_reply_with_error (connection,
117 : MHD_HTTP_NOT_FOUND,
118 : TALER_EC_MERCHANT_GENERIC_INSTANCE_UNKNOWN,
119 : NULL);
120 0 : case GNUNET_DB_STATUS_SOFT_ERROR:
121 0 : TMH_db->rollback (TMH_db->cls);
122 0 : goto retry;
123 0 : case GNUNET_DB_STATUS_HARD_ERROR:
124 0 : TMH_db->rollback (TMH_db->cls);
125 0 : return TALER_MHD_reply_with_error (connection,
126 : MHD_HTTP_INTERNAL_SERVER_ERROR,
127 : TALER_EC_GENERIC_DB_FETCH_FAILED,
128 : NULL);
129 9 : case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
130 : /* Success! */
131 9 : break;
132 : }
133 :
134 9 : if (! mi->auth_override)
135 : {
136 : // FIXME are we sure what the scope here is?
137 6 : ec = TMH_check_token (hc->auth_token,
138 6 : mi->settings.id,
139 : &hc->auth_scope);
140 6 : if (TALER_EC_NONE != ec)
141 : {
142 0 : TMH_db->rollback (TMH_db->cls);
143 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
144 : "Refusing auth change: `%s'\n",
145 : TALER_ErrorCode_get_hint (ec));
146 0 : return TALER_MHD_reply_with_error (connection,
147 : MHD_HTTP_UNAUTHORIZED,
148 : TALER_EC_MERCHANT_GENERIC_UNAUTHORIZED,
149 : NULL);
150 : }
151 : }
152 : }
153 :
154 9 : qs = TMH_db->update_instance_auth (TMH_db->cls,
155 9 : mi->settings.id,
156 : &ias);
157 9 : if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
158 : {
159 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
160 0 : TMH_db->rollback (TMH_db->cls);
161 0 : if (GNUNET_DB_STATUS_HARD_ERROR == qs)
162 : {
163 0 : return TALER_MHD_reply_with_error (connection,
164 : MHD_HTTP_INTERNAL_SERVER_ERROR,
165 : TALER_EC_GENERIC_DB_FETCH_FAILED,
166 : NULL);
167 : }
168 0 : goto retry;
169 : }
170 9 : qs = TMH_db->commit (TMH_db->cls);
171 9 : if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
172 9 : qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
173 0 : retry:
174 9 : if (GNUNET_DB_STATUS_SOFT_ERROR != qs)
175 9 : break; /* success! -- or hard failure */
176 : } /* for .. MAX_RETRIES */
177 9 : if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
178 : {
179 0 : return TALER_MHD_reply_with_error (connection,
180 : MHD_HTTP_INTERNAL_SERVER_ERROR,
181 : TALER_EC_GENERIC_DB_COMMIT_FAILED,
182 : NULL);
183 : }
184 : /* Finally, also update our running process */
185 9 : mi->auth = ias;
186 : }
187 9 : mi->auth_override = false;
188 9 : TMH_reload_instances (mi->settings.id);
189 9 : return TALER_MHD_reply_static (connection,
190 : MHD_HTTP_NO_CONTENT,
191 : NULL,
192 : NULL,
193 : 0);
194 : }
195 :
196 :
197 : MHD_RESULT
198 6 : TMH_private_post_instances_ID_auth (const struct TMH_RequestHandler *rh,
199 : struct MHD_Connection *connection,
200 : struct TMH_HandlerContext *hc)
201 : {
202 6 : struct TMH_MerchantInstance *mi = hc->instance;
203 :
204 6 : return post_instances_ID_auth (mi,
205 : connection,
206 : hc);
207 : }
208 :
209 :
210 : MHD_RESULT
211 3 : TMH_private_post_instances_default_ID_auth (
212 : const struct TMH_RequestHandler *rh,
213 : struct MHD_Connection *connection,
214 : struct TMH_HandlerContext *hc)
215 : {
216 : struct TMH_MerchantInstance *mi;
217 : MHD_RESULT ret;
218 :
219 3 : mi = TMH_lookup_instance (hc->infix);
220 3 : if (NULL == mi)
221 : {
222 0 : return TALER_MHD_reply_with_error (connection,
223 : MHD_HTTP_NOT_FOUND,
224 : TALER_EC_MERCHANT_GENERIC_INSTANCE_UNKNOWN,
225 0 : hc->infix);
226 : }
227 3 : mi->auth_override = true;
228 3 : ret = post_instances_ID_auth (mi,
229 : connection,
230 : hc);
231 3 : return ret;
232 : }
233 :
234 :
235 : /* end of taler-merchant-httpd_private-post-instances-ID-auth.c */
|