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