diff options
Diffstat (limited to 'src/backend/anastasis-httpd_policy.c')
-rw-r--r-- | src/backend/anastasis-httpd_policy.c | 252 |
1 files changed, 252 insertions, 0 deletions
diff --git a/src/backend/anastasis-httpd_policy.c b/src/backend/anastasis-httpd_policy.c new file mode 100644 index 0000000..2417e15 --- /dev/null +++ b/src/backend/anastasis-httpd_policy.c | |||
@@ -0,0 +1,252 @@ | |||
1 | /* | ||
2 | This file is part of TALER | ||
3 | Copyright (C) 2019, 2021 Taler Systems SA | ||
4 | |||
5 | TALER is free software; you can redistribute it and/or modify it under the | ||
6 | terms of the GNU Affero General Public License as published by the Free Software | ||
7 | Foundation; either version 3, or (at your option) any later version. | ||
8 | |||
9 | TALER is distributed in the hope that it will be useful, but WITHOUT ANY | ||
10 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR | ||
11 | A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. | ||
12 | |||
13 | You should have received a copy of the GNU Affero General Public License along with | ||
14 | TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> | ||
15 | */ | ||
16 | /** | ||
17 | * @file anastasis-httpd_policy.c | ||
18 | * @brief functions to handle incoming requests on /policy/ | ||
19 | * @author Dennis Neufeld | ||
20 | * @author Dominik Meister | ||
21 | * @author Christian Grothoff | ||
22 | */ | ||
23 | #include "platform.h" | ||
24 | #include "anastasis-httpd.h" | ||
25 | #include "anastasis-httpd_policy.h" | ||
26 | #include "anastasis_service.h" | ||
27 | #include <gnunet/gnunet_util_lib.h> | ||
28 | #include <gnunet/gnunet_rest_lib.h> | ||
29 | #include <taler/taler_json_lib.h> | ||
30 | #include <taler/taler_merchant_service.h> | ||
31 | #include <taler/taler_signatures.h> | ||
32 | |||
33 | /** | ||
34 | * How long do we hold an HTTP client connection if | ||
35 | * we are awaiting payment before giving up? | ||
36 | */ | ||
37 | #define CHECK_PAYMENT_GENERIC_TIMEOUT GNUNET_TIME_relative_multiply ( \ | ||
38 | GNUNET_TIME_UNIT_MINUTES, 30) | ||
39 | |||
40 | |||
41 | /** | ||
42 | * Return the current recoverydocument of @a account on @a connection | ||
43 | * using @a default_http_status on success. | ||
44 | * | ||
45 | * @param connection MHD connection to use | ||
46 | * @param account account to query | ||
47 | * @return MHD result code | ||
48 | */ | ||
49 | static MHD_RESULT | ||
50 | return_policy (struct MHD_Connection *connection, | ||
51 | const struct ANASTASIS_CRYPTO_AccountPublicKeyP *account_pub) | ||
52 | { | ||
53 | enum GNUNET_DB_QueryStatus qs; | ||
54 | struct MHD_Response *resp; | ||
55 | struct ANASTASIS_AccountSignatureP account_sig; | ||
56 | struct GNUNET_HashCode recovery_data_hash; | ||
57 | const char *version_s; | ||
58 | char version_b[14]; | ||
59 | uint32_t version; | ||
60 | void *res_recovery_data; | ||
61 | size_t res_recovery_data_size; | ||
62 | |||
63 | version_s = MHD_lookup_connection_value (connection, | ||
64 | MHD_GET_ARGUMENT_KIND, | ||
65 | "version"); | ||
66 | if (NULL != version_s) | ||
67 | { | ||
68 | char dummy; | ||
69 | |||
70 | if (1 != sscanf (version_s, | ||
71 | "%u%c", | ||
72 | &version, | ||
73 | &dummy)) | ||
74 | { | ||
75 | return TALER_MHD_reply_with_error (connection, | ||
76 | MHD_HTTP_BAD_REQUEST, | ||
77 | TALER_EC_GENERIC_PARAMETER_MALFORMED, | ||
78 | "version"); | ||
79 | } | ||
80 | qs = db->get_recovery_document (db->cls, | ||
81 | account_pub, | ||
82 | version, | ||
83 | &account_sig, | ||
84 | &recovery_data_hash, | ||
85 | &res_recovery_data_size, | ||
86 | &res_recovery_data); | ||
87 | } | ||
88 | else | ||
89 | { | ||
90 | qs = db->get_latest_recovery_document (db->cls, | ||
91 | account_pub, | ||
92 | &account_sig, | ||
93 | &recovery_data_hash, | ||
94 | &res_recovery_data_size, | ||
95 | &res_recovery_data, | ||
96 | &version); | ||
97 | GNUNET_snprintf (version_b, | ||
98 | sizeof (version_b), | ||
99 | "%u", | ||
100 | (unsigned int) version); | ||
101 | version_s = version_b; | ||
102 | } | ||
103 | switch (qs) | ||
104 | { | ||
105 | case GNUNET_DB_STATUS_HARD_ERROR: | ||
106 | GNUNET_break (0); | ||
107 | return TALER_MHD_reply_with_error (connection, | ||
108 | MHD_HTTP_INTERNAL_SERVER_ERROR, | ||
109 | TALER_EC_GENERIC_DB_FETCH_FAILED, | ||
110 | "get_recovery_document"); | ||
111 | case GNUNET_DB_STATUS_SOFT_ERROR: | ||
112 | GNUNET_break (0); | ||
113 | return TALER_MHD_reply_with_error (connection, | ||
114 | MHD_HTTP_INTERNAL_SERVER_ERROR, | ||
115 | TALER_EC_GENERIC_DB_SOFT_FAILURE, | ||
116 | "get_recovery_document"); | ||
117 | case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: | ||
118 | return TALER_MHD_reply_with_error (connection, | ||
119 | MHD_HTTP_NOT_FOUND, | ||
120 | TALER_EC_ANASTASIS_POLICY_NOT_FOUND, | ||
121 | NULL); | ||
122 | case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: | ||
123 | /* interesting case below */ | ||
124 | break; | ||
125 | } | ||
126 | resp = MHD_create_response_from_buffer (res_recovery_data_size, | ||
127 | res_recovery_data, | ||
128 | MHD_RESPMEM_MUST_FREE); | ||
129 | TALER_MHD_add_global_headers (resp); | ||
130 | { | ||
131 | char *sig_s; | ||
132 | char *etag; | ||
133 | |||
134 | sig_s = GNUNET_STRINGS_data_to_string_alloc (&account_sig, | ||
135 | sizeof (account_sig)); | ||
136 | etag = GNUNET_STRINGS_data_to_string_alloc (&recovery_data_hash, | ||
137 | sizeof (recovery_data_hash)); | ||
138 | GNUNET_break (MHD_YES == | ||
139 | MHD_add_response_header (resp, | ||
140 | ANASTASIS_HTTP_HEADER_POLICY_SIGNATURE, | ||
141 | sig_s)); | ||
142 | GNUNET_break (MHD_YES == | ||
143 | MHD_add_response_header (resp, | ||
144 | ANASTASIS_HTTP_HEADER_POLICY_VERSION, | ||
145 | version_s)); | ||
146 | GNUNET_break (MHD_YES == | ||
147 | MHD_add_response_header (resp, | ||
148 | MHD_HTTP_HEADER_ETAG, | ||
149 | etag)); | ||
150 | GNUNET_free (etag); | ||
151 | GNUNET_free (sig_s); | ||
152 | } | ||
153 | { | ||
154 | MHD_RESULT ret; | ||
155 | |||
156 | ret = MHD_queue_response (connection, | ||
157 | MHD_HTTP_OK, | ||
158 | resp); | ||
159 | MHD_destroy_response (resp); | ||
160 | return ret; | ||
161 | } | ||
162 | } | ||
163 | |||
164 | |||
165 | MHD_RESULT | ||
166 | AH_policy_get (struct MHD_Connection *connection, | ||
167 | const struct ANASTASIS_CRYPTO_AccountPublicKeyP *account_pub) | ||
168 | { | ||
169 | struct GNUNET_HashCode recovery_data_hash; | ||
170 | enum ANASTASIS_DB_AccountStatus as; | ||
171 | MHD_RESULT ret; | ||
172 | uint32_t version; | ||
173 | struct GNUNET_TIME_Absolute expiration; | ||
174 | |||
175 | as = db->lookup_account (db->cls, | ||
176 | account_pub, | ||
177 | &expiration, | ||
178 | &recovery_data_hash, | ||
179 | &version); | ||
180 | switch (as) | ||
181 | { | ||
182 | case ANASTASIS_DB_ACCOUNT_STATUS_PAYMENT_REQUIRED: | ||
183 | return TALER_MHD_reply_with_error (connection, | ||
184 | MHD_HTTP_NOT_FOUND, | ||
185 | TALER_EC_SYNC_ACCOUNT_UNKNOWN, | ||
186 | NULL); | ||
187 | case ANASTASIS_DB_ACCOUNT_STATUS_HARD_ERROR: | ||
188 | GNUNET_break (0); | ||
189 | return TALER_MHD_reply_with_error (connection, | ||
190 | MHD_HTTP_INTERNAL_SERVER_ERROR, | ||
191 | TALER_EC_GENERIC_DB_FETCH_FAILED, | ||
192 | "lookup account"); | ||
193 | case ANASTASIS_DB_ACCOUNT_STATUS_NO_RESULTS: | ||
194 | { | ||
195 | struct MHD_Response *resp; | ||
196 | |||
197 | resp = MHD_create_response_from_buffer (0, | ||
198 | NULL, | ||
199 | MHD_RESPMEM_PERSISTENT); | ||
200 | TALER_MHD_add_global_headers (resp); | ||
201 | ret = MHD_queue_response (connection, | ||
202 | MHD_HTTP_NO_CONTENT, | ||
203 | resp); | ||
204 | MHD_destroy_response (resp); | ||
205 | } | ||
206 | return ret; | ||
207 | case ANASTASIS_DB_ACCOUNT_STATUS_VALID_HASH_RETURNED: | ||
208 | { | ||
209 | const char *inm; | ||
210 | |||
211 | inm = MHD_lookup_connection_value (connection, | ||
212 | MHD_HEADER_KIND, | ||
213 | MHD_HTTP_HEADER_IF_NONE_MATCH); | ||
214 | if (NULL != inm) | ||
215 | { | ||
216 | struct GNUNET_HashCode inm_h; | ||
217 | |||
218 | if (GNUNET_OK != | ||
219 | GNUNET_STRINGS_string_to_data (inm, | ||
220 | strlen (inm), | ||
221 | &inm_h, | ||
222 | sizeof (inm_h))) | ||
223 | { | ||
224 | GNUNET_break_op (0); | ||
225 | return TALER_MHD_reply_with_error (connection, | ||
226 | MHD_HTTP_BAD_REQUEST, | ||
227 | TALER_EC_ANASTASIS_POLICY_BAD_IF_NONE_MATCH, | ||
228 | "Etag must be a base32-encoded SHA-512 hash"); | ||
229 | } | ||
230 | if (0 == GNUNET_memcmp (&inm_h, | ||
231 | &recovery_data_hash)) | ||
232 | { | ||
233 | struct MHD_Response *resp; | ||
234 | |||
235 | resp = MHD_create_response_from_buffer (0, | ||
236 | NULL, | ||
237 | MHD_RESPMEM_PERSISTENT); | ||
238 | TALER_MHD_add_global_headers (resp); | ||
239 | ret = MHD_queue_response (connection, | ||
240 | MHD_HTTP_NOT_MODIFIED, | ||
241 | resp); | ||
242 | MHD_destroy_response (resp); | ||
243 | return ret; | ||
244 | } | ||
245 | } | ||
246 | } | ||
247 | /* We have a result, should fetch and return it! */ | ||
248 | break; | ||
249 | } | ||
250 | return return_policy (connection, | ||
251 | account_pub); | ||
252 | } | ||