Line data Source code
1 : /*
2 : This file is part of GNU Taler
3 : (C) 2023, 2025 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-token.c
22 : * @brief implementing POST /instances/$ID/token request handling
23 : * @author Christian Grothoff
24 : */
25 : #include "platform.h"
26 : #include "taler-merchant-httpd_private-post-instances-ID-token.h"
27 : #include "taler-merchant-httpd_helper.h"
28 : #include "taler-merchant-httpd_mfa.h"
29 : #include <taler/taler_json_lib.h>
30 :
31 :
32 : /**
33 : * Default duration for the validity of a login token.
34 : */
35 : #define DEFAULT_DURATION GNUNET_TIME_UNIT_DAYS
36 :
37 :
38 : MHD_RESULT
39 15 : TMH_private_post_instances_ID_token (const struct TMH_RequestHandler *rh,
40 : struct MHD_Connection *connection,
41 : struct TMH_HandlerContext *hc)
42 : {
43 15 : struct TMH_MerchantInstance *mi = hc->instance;
44 15 : json_t *jtoken = hc->request_body;
45 : const char *scope;
46 : const char *description;
47 15 : enum TMH_AuthScope iscope = TMH_AS_NONE;
48 15 : bool refreshable = false;
49 : struct TALER_MERCHANTDB_LoginTokenP btoken;
50 : struct GNUNET_TIME_Relative duration
51 15 : = DEFAULT_DURATION;
52 : struct GNUNET_TIME_Timestamp expiration_time;
53 : struct GNUNET_JSON_Specification spec[] = {
54 15 : GNUNET_JSON_spec_string ("scope",
55 : &scope),
56 15 : GNUNET_JSON_spec_mark_optional (
57 : GNUNET_JSON_spec_relative_time ("duration",
58 : &duration),
59 : NULL),
60 15 : GNUNET_JSON_spec_mark_optional (
61 : GNUNET_JSON_spec_bool ("refreshable",
62 : &refreshable),
63 : NULL),
64 15 : GNUNET_JSON_spec_mark_optional (
65 : GNUNET_JSON_spec_string ("description",
66 : &description),
67 : NULL),
68 15 : GNUNET_JSON_spec_end ()
69 : };
70 : enum GNUNET_DB_QueryStatus qs;
71 :
72 : {
73 : enum GNUNET_GenericReturnValue res;
74 :
75 15 : res = TALER_MHD_parse_json_data (connection,
76 : jtoken,
77 : spec);
78 15 : if (GNUNET_OK != res)
79 0 : return (GNUNET_NO == res) ? MHD_YES : MHD_NO;
80 : }
81 15 : GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE,
82 : &btoken,
83 : sizeof (btoken));
84 15 : expiration_time = GNUNET_TIME_relative_to_timestamp (duration);
85 : {
86 : char *tmp_scope;
87 : char *scope_prefix;
88 : char *scope_suffix;
89 :
90 15 : tmp_scope = GNUNET_strdup (scope);
91 15 : scope_prefix = strtok (tmp_scope,
92 : ":");
93 15 : scope_suffix = strtok (NULL,
94 : ":");
95 : /* We allow <SCOPE>:REFRESHABLE syntax */
96 15 : if ( (NULL != scope_suffix) &&
97 2 : (0 == strcasecmp (scope_suffix,
98 : "refreshable")))
99 2 : refreshable = true;
100 15 : iscope = TMH_get_scope_by_name (scope_prefix);
101 15 : if (TMH_AS_NONE == iscope)
102 : {
103 0 : GNUNET_break_op (0);
104 0 : GNUNET_free (tmp_scope);
105 0 : return TALER_MHD_reply_with_ec (connection,
106 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
107 : "scope");
108 : }
109 15 : GNUNET_free (tmp_scope);
110 : }
111 15 : if (refreshable)
112 9 : iscope |= TMH_AS_REFRESHABLE;
113 15 : if (! TMH_scope_is_subset (hc->auth_scope,
114 : iscope))
115 : {
116 : /* more permissions requested for the new token, not allowed */
117 1 : GNUNET_break_op (0);
118 1 : return TALER_MHD_reply_with_ec (connection,
119 : TALER_EC_GENERIC_TOKEN_PERMISSION_INSUFFICIENT,
120 : NULL);
121 : }
122 14 : if (NULL == description)
123 : {
124 13 : description = "";
125 : }
126 :
127 : {
128 : enum GNUNET_GenericReturnValue ret =
129 14 : TMH_mfa_check_simple (hc,
130 : TALER_MERCHANT_MFA_CO_AUTH_TOKEN_CREATION,
131 : mi);
132 :
133 14 : if (GNUNET_OK != ret)
134 : {
135 : return (GNUNET_NO == ret)
136 : ? MHD_YES
137 0 : : MHD_NO;
138 : }
139 : }
140 :
141 14 : qs = TMH_db->insert_login_token (TMH_db->cls,
142 14 : mi->settings.id,
143 : &btoken,
144 : GNUNET_TIME_timestamp_get (),
145 : expiration_time,
146 : iscope,
147 : description);
148 14 : switch (qs)
149 : {
150 0 : case GNUNET_DB_STATUS_HARD_ERROR:
151 : case GNUNET_DB_STATUS_SOFT_ERROR:
152 : case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
153 0 : GNUNET_break (0);
154 0 : return TALER_MHD_reply_with_ec (connection,
155 : TALER_EC_GENERIC_DB_STORE_FAILED,
156 : "insert_login_token");
157 14 : case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
158 14 : break;
159 : }
160 :
161 : {
162 : char *tok;
163 : MHD_RESULT ret;
164 : char *val;
165 :
166 14 : val = GNUNET_STRINGS_data_to_string_alloc (&btoken,
167 : sizeof (btoken));
168 14 : GNUNET_asprintf (&tok,
169 : RFC_8959_PREFIX "%s",
170 : val);
171 14 : GNUNET_free (val);
172 14 : ret = TALER_MHD_REPLY_JSON_PACK (
173 : connection,
174 : MHD_HTTP_OK,
175 : GNUNET_JSON_pack_string ("access_token",
176 : tok),
177 : GNUNET_JSON_pack_string ("token",
178 : tok),
179 : GNUNET_JSON_pack_string ("scope",
180 : scope),
181 : GNUNET_JSON_pack_bool ("refreshable",
182 : refreshable),
183 : GNUNET_JSON_pack_timestamp ("expiration",
184 : expiration_time));
185 14 : GNUNET_free (tok);
186 14 : return ret;
187 : }
188 : }
189 :
190 :
191 : /* end of taler-merchant-httpd_private-post-instances-ID-token.c */
|