- merge with master
[oweals/gnunet.git] / src / identity-provider / plugin_rest_identity_provider.c
1 /*
2    This file is part of GNUnet.
3    Copyright (C) 2012-2015 GNUnet e.V.
4
5    GNUnet is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published
7    by the Free Software Foundation; either version 3, or (at your
8    option) any later version.
9
10    GNUnet 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 GNU
13    General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with GNUnet; see the file COPYING.  If not, write to the
17    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18    Boston, MA 02110-1301, USA.
19    */
20 /**
21  * @author Martin Schanzenbach
22  * @file identity/plugin_rest_identity.c
23  * @brief GNUnet Namestore REST plugin
24  *
25  */
26
27 #include "platform.h"
28 #include "gnunet_rest_plugin.h"
29 #include "gnunet_identity_service.h"
30 #include "gnunet_gns_service.h"
31 #include "gnunet_gnsrecord_lib.h"
32 #include "gnunet_namestore_service.h"
33 #include "gnunet_rest_lib.h"
34 #include "gnunet_jsonapi_lib.h"
35 #include "gnunet_jsonapi_util.h"
36 #include "microhttpd.h"
37 #include <jansson.h>
38 #include <inttypes.h>
39 #include "gnunet_signatures.h"
40 #include "gnunet_identity_provider_service.h"
41
42 /**
43  * REST root namespace
44  */
45 #define GNUNET_REST_API_NS_IDENTITY_PROVIDER "/idp"
46
47 /**
48  * Issue namespace
49  */
50 #define GNUNET_REST_API_NS_IDENTITY_TOKEN_ISSUE "/idp/issue"
51
52 /**
53  * Check namespace TODO
54  */
55 #define GNUNET_REST_API_NS_IDENTITY_TOKEN_CHECK "/idp/check"
56
57 /**
58  * Token namespace
59  */
60 #define GNUNET_REST_API_NS_IDENTITY_OAUTH2_TOKEN "/idp/token"
61
62 /**
63  * The parameter name in which the ticket must be provided
64  */
65 #define GNUNET_REST_JSONAPI_IDENTITY_PROVIDER_TICKET "ticket"
66
67 /**
68  * The parameter name in which the expected nonce must be provided
69  */
70 #define GNUNET_REST_JSONAPI_IDENTITY_PROVIDER_EXPECTED_NONCE "expected_nonce"
71
72 /**
73  * The parameter name in which the ticket must be provided
74  */
75 #define GNUNET_REST_JSONAPI_IDENTITY_PROVIDER_TOKEN "token"
76
77 /**
78  * The URL parameter name in which the nonce must be provided
79  */
80 #define GNUNET_IDENTITY_TOKEN_REQUEST_NONCE "nonce"
81
82 /**
83  * State while collecting all egos
84  */
85 #define ID_REST_STATE_INIT 0
86
87 /**
88  * Done collecting egos
89  */
90 #define ID_REST_STATE_POST_INIT 1
91
92 /**
93  * Resource type
94  */
95 #define GNUNET_REST_JSONAPI_IDENTITY_TOKEN "token"
96
97 /**
98  * URL parameter to create a GNUid token for a specific audience
99  */
100 #define GNUNET_REST_JSONAPI_IDENTITY_AUD_REQUEST "audience"
101
102 /**
103  * URL parameter to create a GNUid token for a specific issuer (EGO)
104  */
105 #define GNUNET_REST_JSONAPI_IDENTITY_ISS_REQUEST "issuer"
106
107 /**
108  * Attributes passed to issue request
109  */
110 #define GNUNET_IDENTITY_TOKEN_ATTR_LIST "requested_attrs"
111
112 /**
113  * Attributes passed to issue request
114  */
115 #define GNUNET_IDENTITY_TOKEN_V_ATTR_LIST "requested_verified_attrs"
116
117
118 /**
119  * Token expiration string
120  */
121 #define GNUNET_IDENTITY_TOKEN_EXP_STRING "expiration"
122
123 /**
124  * Error messages
125  */
126 #define GNUNET_REST_ERROR_RESOURCE_INVALID "Resource location invalid"
127 #define GNUNET_REST_ERROR_NO_DATA "No data"
128
129 /**
130  * GNUid token lifetime
131  */
132 #define GNUNET_GNUID_TOKEN_EXPIRATION_MICROSECONDS 300000000
133
134 /**
135  * The configuration handle
136  */
137 const struct GNUNET_CONFIGURATION_Handle *cfg;
138
139 /**
140  * HTTP methods allows for this plugin
141  */
142 static char* allow_methods;
143
144 /**
145  * @brief struct returned by the initialization function of the plugin
146  */
147 struct Plugin
148 {
149   const struct GNUNET_CONFIGURATION_Handle *cfg;
150 };
151
152 /**
153  * The ego list
154  */
155 struct EgoEntry
156 {
157   /**
158    * DLL
159    */
160   struct EgoEntry *next;
161
162   /**
163    * DLL
164    */
165   struct EgoEntry *prev;
166
167   /**
168    * Ego Identifier
169    */
170   char *identifier;
171
172   /**
173    * Public key string
174    */
175   char *keystring;
176
177   /**
178    * The Ego
179    */
180   struct GNUNET_IDENTITY_Ego *ego;
181 };
182
183
184 struct RequestHandle
185 {
186   /**
187    * Ego list
188    */
189   struct EgoEntry *ego_head;
190
191   /**
192    * Ego list
193    */
194   struct EgoEntry *ego_tail;
195
196   /**
197    * Selected ego
198    */
199   struct EgoEntry *ego_entry;
200
201   /**
202    * Ptr to current ego private key
203    */
204   const struct GNUNET_CRYPTO_EcdsaPrivateKey *priv_key;
205
206   /**
207    * Handle to the rest connection
208    */
209   struct GNUNET_REST_RequestHandle *conndata_handle;
210
211   /**
212    * The processing state
213    */
214   int state;
215
216   /**
217    * Handle to Identity service.
218    */
219   struct GNUNET_IDENTITY_Handle *identity_handle;
220
221   /**
222    * IDENTITY Operation
223    */
224   struct GNUNET_IDENTITY_Operation *op;
225
226   /**
227    * Identity Provider
228    */
229   struct GNUNET_IDENTITY_PROVIDER_Handle *idp;
230
231   /**
232    * Idp Operation
233    */
234   struct GNUNET_IDENTITY_PROVIDER_Operation *idp_op;
235
236   /**
237    * Handle to NS service
238    */
239   struct GNUNET_NAMESTORE_Handle *ns_handle;
240
241   /**
242    * NS iterator
243    */
244   struct GNUNET_NAMESTORE_ZoneIterator *ns_it;
245
246   /**
247    * NS Handle
248    */
249   struct GNUNET_NAMESTORE_QueueEntry *ns_qe;
250
251   /**
252    * Desired timeout for the lookup (default is no timeout).
253    */
254   struct GNUNET_TIME_Relative timeout;
255
256   /**
257    * ID of a task associated with the resolution process.
258    */
259   struct GNUNET_SCHEDULER_Task *timeout_task;
260
261   /**
262    * The plugin result processor
263    */
264   GNUNET_REST_ResultProcessor proc;
265
266   /**
267    * The closure of the result processor
268    */
269   void *proc_cls;
270
271   /**
272    * The url
273    */
274   char *url;
275
276   /**
277    * Error response message
278    */
279   char *emsg;
280
281   /**
282    * Reponse code
283    */
284   int response_code;
285
286   /**
287    * Response object
288    */
289   struct GNUNET_JSONAPI_Document *resp_object;
290
291 };
292
293
294 /**
295  * Cleanup lookup handle
296  * @param handle Handle to clean up
297  */
298 static void
299 cleanup_handle (struct RequestHandle *handle)
300 {
301   struct EgoEntry *ego_entry;
302   struct EgoEntry *ego_tmp;
303   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
304               "Cleaning up\n");
305   if (NULL != handle->resp_object)
306     GNUNET_JSONAPI_document_delete (handle->resp_object);
307   if (NULL != handle->timeout_task)
308     GNUNET_SCHEDULER_cancel (handle->timeout_task);
309   if (NULL != handle->identity_handle)
310     GNUNET_IDENTITY_disconnect (handle->identity_handle);
311   if (NULL != handle->idp)
312     GNUNET_IDENTITY_PROVIDER_disconnect (handle->idp);
313   if (NULL != handle->ns_it)
314     GNUNET_NAMESTORE_zone_iteration_stop (handle->ns_it);
315   if (NULL != handle->ns_qe)
316     GNUNET_NAMESTORE_cancel (handle->ns_qe);
317   if (NULL != handle->ns_handle)
318     GNUNET_NAMESTORE_disconnect (handle->ns_handle);
319   if (NULL != handle->url)
320     GNUNET_free (handle->url);
321   if (NULL != handle->emsg)
322     GNUNET_free (handle->emsg);
323   for (ego_entry = handle->ego_head;
324        NULL != ego_entry;)
325   {
326     ego_tmp = ego_entry;
327     ego_entry = ego_entry->next;
328     GNUNET_free (ego_tmp->identifier);
329     GNUNET_free (ego_tmp->keystring);
330     GNUNET_free (ego_tmp);
331   }
332   GNUNET_free (handle);
333 }
334
335
336 /**
337  * Task run on error, sends error message.  Cleans up everything.
338  *
339  * @param cls the `struct RequestHandle`
340  */
341 static void
342 do_error (void *cls)
343 {
344   struct RequestHandle *handle = cls;
345   struct MHD_Response *resp;
346   char *json_error;
347
348   GNUNET_asprintf (&json_error,
349                    "{Error while processing request: %s}",
350                    handle->emsg);
351   resp = GNUNET_REST_create_response (json_error);
352   handle->proc (handle->proc_cls, resp, handle->response_code);
353   cleanup_handle (handle);
354   GNUNET_free (json_error);
355 }
356
357 /**
358  * Task run on timeout, sends error message.  Cleans up everything.
359  *
360  * @param cls the `struct RequestHandle`
361  */
362 static void
363 do_timeout (void *cls)
364 {
365   struct RequestHandle *handle = cls;
366
367   handle->timeout_task = NULL;
368   do_error (handle);
369 }
370
371
372 /**
373  * Task run on shutdown.  Cleans up everything.
374  *
375  * @param cls unused
376  */
377 static void
378 do_cleanup_handle_delayed (void *cls)
379 {
380   struct RequestHandle *handle = cls;
381
382   cleanup_handle (handle);
383 }
384
385
386 /**
387  * Get a ticket for identity
388  * @param cls the handle
389  * @param ticket the ticket returned from the idp
390  */
391 static void
392 token_creat_cont (void *cls,
393                   const char *label,
394                   const struct GNUNET_IDENTITY_PROVIDER_Ticket *ticket,
395                   const struct GNUNET_IDENTITY_PROVIDER_Token *token)
396 {
397   struct GNUNET_JSONAPI_Resource *json_resource;
398   struct RequestHandle *handle = cls;
399   struct MHD_Response *resp;
400   json_t *ticket_json;
401   json_t *token_json;
402   char *ticket_str;
403   char *token_str;
404   char *result_str;
405
406   if (NULL == ticket)
407   {
408     handle->emsg = GNUNET_strdup ("Error in token issue");
409     GNUNET_SCHEDULER_add_now (&do_error, handle);
410     return;
411   }
412
413   handle->resp_object = GNUNET_JSONAPI_document_new ();
414   json_resource = GNUNET_JSONAPI_resource_new (GNUNET_REST_JSONAPI_IDENTITY_PROVIDER_TICKET,
415                                                     label);
416   ticket_str = GNUNET_IDENTITY_PROVIDER_ticket_to_string (ticket);
417   token_str = GNUNET_IDENTITY_PROVIDER_token_to_string (token);
418   ticket_json = json_string (ticket_str);
419   token_json = json_string (token_str);
420   GNUNET_JSONAPI_resource_add_attr (json_resource,
421                                          GNUNET_REST_JSONAPI_IDENTITY_PROVIDER_TICKET,
422                                          ticket_json);
423   GNUNET_JSONAPI_resource_add_attr (json_resource,
424                                          GNUNET_REST_JSONAPI_IDENTITY_PROVIDER_TOKEN,
425                                          token_json);
426   GNUNET_free (ticket_str);
427   GNUNET_free (token_str);
428   json_decref (ticket_json);
429   json_decref (token_json);
430   GNUNET_JSONAPI_document_resource_add (handle->resp_object, json_resource);
431
432   GNUNET_JSONAPI_document_serialize (handle->resp_object, &result_str);
433   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Result %s\n", result_str);
434   resp = GNUNET_REST_create_response (result_str);
435   handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
436   GNUNET_free (result_str);
437   GNUNET_SCHEDULER_add_now (&do_cleanup_handle_delayed, handle);
438 }
439
440
441 /**
442  * Continueationf for token issue request
443  *
444  * @param con the Rest handle
445  * @param url the requested url
446  * @param cls the request handle
447  */
448 static void
449 issue_token_cont (struct GNUNET_REST_RequestHandle *con,
450                   const char *url,
451                   void *cls)
452 {
453   const struct GNUNET_CRYPTO_EcdsaPrivateKey *priv_key;
454   const char *egoname;
455
456   struct RequestHandle *handle = cls;
457   struct EgoEntry *ego_entry;
458   struct GNUNET_HashCode key;
459   struct MHD_Response *resp;
460   struct GNUNET_CRYPTO_EcdsaPublicKey pub_key;
461   struct GNUNET_CRYPTO_EcdsaPublicKey aud_key;
462   struct GNUNET_TIME_Relative etime_rel;
463   struct GNUNET_TIME_Absolute exp_time;
464   char *ego_val;
465   char *audience;
466   char *exp_str;
467   char *nonce_str;
468   char *scopes;
469   char *vattrs;
470   uint64_t time;
471   uint64_t nonce;
472
473   if (GNUNET_NO == GNUNET_REST_namespace_match (handle->url,
474                                                 GNUNET_REST_API_NS_IDENTITY_TOKEN_ISSUE))
475   {
476     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "URL invalid: %s\n", handle->url);
477     resp = GNUNET_REST_create_response (NULL);
478     handle->proc (handle->proc_cls, resp, MHD_HTTP_BAD_REQUEST);
479     cleanup_handle (handle);
480     return;
481   }
482   egoname = NULL;
483   ego_entry = NULL;
484   GNUNET_CRYPTO_hash (GNUNET_REST_JSONAPI_IDENTITY_ISS_REQUEST,
485                       strlen (GNUNET_REST_JSONAPI_IDENTITY_ISS_REQUEST),
486                       &key);
487   if ( GNUNET_YES !=
488        GNUNET_CONTAINER_multihashmap_contains (handle->conndata_handle->url_param_map,
489                                                &key) )
490   {
491     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
492                 "Issuer not found\n");
493     GNUNET_SCHEDULER_add_now (&do_error, handle);
494     return;
495   }
496   ego_val = GNUNET_CONTAINER_multihashmap_get (handle->conndata_handle->url_param_map,
497                                                &key);
498   if (NULL == ego_val)
499   {
500     GNUNET_SCHEDULER_add_now (&do_error, handle);
501     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
502                 "Ego invalid: %s\n",
503                 ego_val);
504     return;
505   }
506   for (ego_entry = handle->ego_head;
507        NULL != ego_entry;
508        ego_entry = ego_entry->next)
509   {
510     if (0 != strcmp (ego_val, ego_entry->identifier))
511       continue;
512     egoname = ego_entry->identifier;
513     break;
514   }
515   if ( (NULL == egoname) ||
516        (NULL == ego_entry) )
517   {
518     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
519                 "Ego not found: %s\n",
520                 ego_val);
521     GNUNET_SCHEDULER_add_now (&do_error, handle);
522     return;
523   }
524   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
525               "Ego to issue token for: %s\n",
526               egoname);
527
528
529   //Meta info
530   GNUNET_CRYPTO_hash (GNUNET_IDENTITY_TOKEN_ATTR_LIST,
531                       strlen (GNUNET_IDENTITY_TOKEN_ATTR_LIST),
532                       &key);
533
534   scopes = NULL;
535   if ( GNUNET_YES !=
536        GNUNET_CONTAINER_multihashmap_contains (handle->conndata_handle->url_param_map,
537                                                &key) )
538   {
539     handle->emsg = GNUNET_strdup ("Scopes missing!\n");
540     GNUNET_SCHEDULER_add_now (&do_error, handle);
541     return;
542   }
543   scopes = GNUNET_CONTAINER_multihashmap_get (handle->conndata_handle->url_param_map,
544                                               &key);
545
546   //vattrs
547   GNUNET_CRYPTO_hash (GNUNET_IDENTITY_TOKEN_V_ATTR_LIST,
548                       strlen (GNUNET_IDENTITY_TOKEN_V_ATTR_LIST),
549                       &key);
550
551   vattrs = NULL;
552   if ( GNUNET_YES ==
553        GNUNET_CONTAINER_multihashmap_contains (handle->conndata_handle->url_param_map,
554                                                &key) )
555   {
556     vattrs = GNUNET_CONTAINER_multihashmap_get (handle->conndata_handle->url_param_map,
557                                                 &key);
558   }
559
560
561
562   //Token audience
563   GNUNET_CRYPTO_hash (GNUNET_REST_JSONAPI_IDENTITY_AUD_REQUEST,
564                       strlen (GNUNET_REST_JSONAPI_IDENTITY_AUD_REQUEST),
565                       &key);
566   audience = NULL;
567   if ( GNUNET_YES !=
568        GNUNET_CONTAINER_multihashmap_contains (handle->conndata_handle->url_param_map,
569                                                &key) )
570   {
571     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
572                 "Audience missing!\n");
573     GNUNET_SCHEDULER_add_now (&do_error, handle);
574     return;
575   }
576   audience = GNUNET_CONTAINER_multihashmap_get (handle->conndata_handle->url_param_map,
577                                                 &key);
578   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
579               "Audience to issue token for: %s\n",
580               audience);
581
582   priv_key = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego);
583   GNUNET_IDENTITY_ego_get_public_key (ego_entry->ego,
584                                       &pub_key);
585   GNUNET_STRINGS_string_to_data (audience,
586                                  strlen (audience),
587                                  &aud_key,
588                                  sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));
589
590   //Remote nonce
591   nonce_str = NULL;
592   GNUNET_CRYPTO_hash (GNUNET_IDENTITY_TOKEN_REQUEST_NONCE,
593                       strlen (GNUNET_IDENTITY_TOKEN_REQUEST_NONCE),
594                       &key);
595   if ( GNUNET_YES !=
596        GNUNET_CONTAINER_multihashmap_contains (handle->conndata_handle->url_param_map,
597                                                &key) )
598   {
599     handle->emsg = GNUNET_strdup ("Request nonce missing!\n");
600     GNUNET_SCHEDULER_add_now (&do_error, handle);
601     return;
602   }
603   nonce_str = GNUNET_CONTAINER_multihashmap_get (handle->conndata_handle->url_param_map,
604                                                  &key);
605   GNUNET_assert (NULL != nonce_str);
606   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
607               "Request nonce: %s\n",
608               nonce_str);
609   GNUNET_assert (1 == sscanf (nonce_str, "%"SCNu64, &nonce));
610
611   //Get expiration for token from URL parameter
612   GNUNET_CRYPTO_hash (GNUNET_IDENTITY_TOKEN_EXP_STRING,
613                       strlen (GNUNET_IDENTITY_TOKEN_EXP_STRING),
614                       &key);
615
616   exp_str = NULL;
617   if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (handle->conndata_handle->url_param_map,
618                                                             &key))
619   {
620     exp_str = GNUNET_CONTAINER_multihashmap_get (handle->conndata_handle->url_param_map,
621                                                  &key);
622   }
623   if (NULL == exp_str) {
624     handle->emsg = GNUNET_strdup ("No expiration given!\n");
625     GNUNET_SCHEDULER_add_now (&do_error, handle);
626     return;
627   }
628
629   if (GNUNET_OK !=
630       GNUNET_STRINGS_fancy_time_to_relative (exp_str,
631                                              &etime_rel))
632   {
633     handle->emsg = GNUNET_strdup ("Expiration invalid!\n");
634     GNUNET_SCHEDULER_add_now (&do_error, handle);
635     return;
636   }
637   time = GNUNET_TIME_absolute_get().abs_value_us;
638   exp_time.abs_value_us = time + etime_rel.rel_value_us;
639
640   handle->idp = GNUNET_IDENTITY_PROVIDER_connect (cfg);
641   handle->idp_op = GNUNET_IDENTITY_PROVIDER_issue_token (handle->idp,
642                                                          priv_key,
643                                                          &aud_key,
644                                                          scopes,
645                                                          vattrs,
646                                                          exp_time,
647                                                          nonce,
648                                                          &token_creat_cont,
649                                                          handle);
650
651 }
652
653
654 /**
655  * Build a GNUid token for identity
656  *
657  * @param cls the request handle
658  */
659 static void
660 return_token_list (void *cls)
661 {
662   char* result_str;
663   struct RequestHandle *handle = cls;
664   struct MHD_Response *resp;
665
666   GNUNET_JSONAPI_document_serialize (handle->resp_object, &result_str);
667   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Result %s\n", result_str);
668   resp = GNUNET_REST_create_response (result_str);
669   handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
670   GNUNET_free (result_str);
671   cleanup_handle (handle);
672 }
673
674
675 static void
676 token_collect_error_cb (void *cls)
677 {
678   struct RequestHandle *handle = cls;
679
680   do_error (handle);
681 }
682
683
684 /**
685  * Collect all tokens for an ego
686  *
687  * TODO move this into the identity-provider service
688  *
689  */
690 static void
691 token_collect (void *cls,
692                const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
693                const char *label,
694                unsigned int rd_count,
695                const struct GNUNET_GNSRECORD_Data *rd);
696
697
698 static void
699 token_collect_finished_cb (void *cls)
700 {
701   struct RequestHandle *handle = cls;
702   struct EgoEntry *ego_tmp;
703   const struct GNUNET_CRYPTO_EcdsaPrivateKey *priv_key;
704
705   ego_tmp = handle->ego_head;
706   GNUNET_CONTAINER_DLL_remove (handle->ego_head,
707                                handle->ego_tail,
708                                ego_tmp);
709   GNUNET_free (ego_tmp->identifier);
710   GNUNET_free (ego_tmp->keystring);
711   GNUNET_free (ego_tmp);
712
713   if (NULL == handle->ego_head)
714   {
715     //Done
716     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding token END\n");
717     handle->ns_it = NULL;
718     GNUNET_SCHEDULER_add_now (&return_token_list, handle);
719     return;
720   }
721
722   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
723               "Next ego: %s\n",
724               handle->ego_head->identifier);
725   priv_key = GNUNET_IDENTITY_ego_get_private_key (handle->ego_head->ego);
726   handle->ns_it = GNUNET_NAMESTORE_zone_iteration_start (handle->ns_handle,
727                                                          priv_key,
728                                                          &token_collect_error_cb,
729                                                          handle,
730                                                          &token_collect,
731                                                          handle,
732                                                          &token_collect_finished_cb,
733                                                          handle);
734 }
735
736
737 /**
738  * Collect all tokens for an ego
739  *
740  * TODO move this into the identity-provider service
741  *
742  */
743 static void
744 token_collect (void *cls,
745                const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
746                const char *label,
747                unsigned int rd_count,
748                const struct GNUNET_GNSRECORD_Data *rd)
749 {
750   struct RequestHandle *handle = cls;
751   int i;
752   char* data;
753   struct GNUNET_JSONAPI_Resource *json_resource;
754   json_t *issuer;
755   json_t *token;
756
757   for (i = 0; i < rd_count; i++)
758   {
759     if (rd[i].record_type == GNUNET_GNSRECORD_TYPE_ID_TOKEN)
760     {
761       data = GNUNET_GNSRECORD_value_to_string (rd[i].record_type,
762                                                rd[i].data,
763                                                rd[i].data_size);
764       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding token: %s\n", data);
765       json_resource = GNUNET_JSONAPI_resource_new (GNUNET_REST_JSONAPI_IDENTITY_TOKEN,
766                                                    label);
767       issuer = json_string (handle->ego_head->identifier);
768       GNUNET_JSONAPI_resource_add_attr (json_resource,
769                                         GNUNET_REST_JSONAPI_IDENTITY_ISS_REQUEST,
770                                         issuer);
771       json_decref (issuer);
772       token = json_string (data);
773       GNUNET_JSONAPI_resource_add_attr (json_resource,
774                                         GNUNET_REST_JSONAPI_IDENTITY_TOKEN,
775                                         token);
776       json_decref (token);
777
778       GNUNET_JSONAPI_document_resource_add (handle->resp_object, json_resource);
779       GNUNET_free (data);
780     }
781   }
782
783   GNUNET_NAMESTORE_zone_iterator_next (handle->ns_it);
784 }
785
786
787
788 /**
789  * Respond to OPTIONS request
790  *
791  * @param con_handle the connection handle
792  * @param url the url
793  * @param cls the RequestHandle
794  */
795 static void
796 list_token_cont (struct GNUNET_REST_RequestHandle *con_handle,
797                  const char* url,
798                  void *cls)
799 {
800   char* ego_val;
801   struct GNUNET_HashCode key;
802   const struct GNUNET_CRYPTO_EcdsaPrivateKey *priv_key;
803   struct RequestHandle *handle = cls;
804   struct EgoEntry *ego_entry;
805   struct EgoEntry *ego_tmp;
806
807   GNUNET_CRYPTO_hash (GNUNET_REST_JSONAPI_IDENTITY_ISS_REQUEST,
808                       strlen (GNUNET_REST_JSONAPI_IDENTITY_ISS_REQUEST),
809                       &key);
810
811   if ( GNUNET_YES !=
812        GNUNET_CONTAINER_multihashmap_contains (handle->conndata_handle->url_param_map,
813                                                &key) )
814   {
815     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "No issuer given.\n");
816     GNUNET_SCHEDULER_add_now (&do_error, handle);
817     return;
818   }
819   ego_val = GNUNET_CONTAINER_multihashmap_get (handle->conndata_handle->url_param_map,
820                                                &key);
821   GNUNET_assert (NULL != ego_val);
822   //Remove non-matching egos
823   for (ego_entry = handle->ego_head;
824        NULL != ego_entry;)
825   {
826     ego_tmp = ego_entry;
827     ego_entry = ego_entry->next;
828     if (0 != strcmp (ego_val, ego_tmp->identifier))
829     {
830       GNUNET_CONTAINER_DLL_remove (handle->ego_head,
831                                    handle->ego_tail,
832                                    ego_tmp);
833       GNUNET_free (ego_tmp->identifier);
834       GNUNET_free (ego_tmp->keystring);
835       GNUNET_free (ego_tmp);
836     }
837   }
838   handle->resp_object = GNUNET_JSONAPI_document_new ();
839   if (NULL == handle->ego_head)
840   {
841     //Done
842     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "No results.\n");
843     GNUNET_SCHEDULER_add_now (&return_token_list, handle);
844     return;
845   }
846   priv_key = GNUNET_IDENTITY_ego_get_private_key (handle->ego_head->ego);
847   handle->ns_handle = GNUNET_NAMESTORE_connect (cfg);
848   handle->ns_it = GNUNET_NAMESTORE_zone_iteration_start (handle->ns_handle,
849                                                          priv_key,
850                                                          &token_collect_error_cb,
851                                                          handle,
852                                                          &token_collect,
853                                                          handle,
854                                                          &token_collect_finished_cb,
855                                                          handle);
856
857 }
858
859 /**
860  * Return token to requestor
861  *
862  * @param cls request handle
863  * @param token the token
864  */
865 static void
866 exchange_cont (void *cls,
867                const struct GNUNET_IDENTITY_PROVIDER_Token *token,
868                uint64_t ticket_nonce)
869 {
870   json_t *root;
871   struct RequestHandle *handle = cls;
872   struct MHD_Response *resp;
873   struct GNUNET_HashCode key;
874   char* result;
875   char* token_str;
876   char* nonce_str;
877   uint64_t expected_nonce;
878
879   //Get nonce
880   GNUNET_CRYPTO_hash (GNUNET_REST_JSONAPI_IDENTITY_PROVIDER_EXPECTED_NONCE,
881                       strlen (GNUNET_REST_JSONAPI_IDENTITY_PROVIDER_EXPECTED_NONCE),
882                       &key);
883
884   if ( GNUNET_NO ==
885        GNUNET_CONTAINER_multihashmap_contains (handle->conndata_handle->url_param_map,
886                                                &key) )
887   {
888     handle->emsg = GNUNET_strdup ("No nonce given.");
889     GNUNET_SCHEDULER_add_now (&do_error, handle);
890     return;
891   }
892   nonce_str = GNUNET_CONTAINER_multihashmap_get (handle->conndata_handle->url_param_map,
893                                                  &key);
894   GNUNET_assert (NULL != nonce_str);
895   GNUNET_assert (1 == sscanf (nonce_str, "%"SCNu64, &expected_nonce));
896
897   if (ticket_nonce != expected_nonce)
898   {
899     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
900                 "Ticket nonce %"SCNu64" does not match expected nonce %"SCNu64"\n",
901                 ticket_nonce, expected_nonce);
902     handle->emsg = GNUNET_strdup ("Ticket nonce does not match expected nonce\n");
903     GNUNET_SCHEDULER_add_now (&do_error, handle);
904     return;
905   }
906
907   root = json_object ();
908   token_str = GNUNET_IDENTITY_PROVIDER_token_to_string (token);
909   json_object_set_new (root, "token", json_string (token_str));
910   json_object_set_new (root, "token_type", json_string ("jwt"));
911   GNUNET_free (token_str);
912
913   result = json_dumps (root, JSON_INDENT(1));
914   resp = GNUNET_REST_create_response (result);
915   GNUNET_free (result);
916   handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
917   cleanup_handle (handle);
918   json_decref (root);
919 }
920
921
922 /**
923  *
924  * Callback called when identity for token exchange has been found
925  *
926  * @param cls request handle
927  * @param ego the identity to use as issuer
928  * @param ctx user context
929  * @param name identity name
930  *
931  */
932 static void
933 exchange_token_ticket_cb (void *cls,
934                           struct GNUNET_IDENTITY_Ego *ego,
935                           void **ctx,
936                           const char *name)
937 {
938   struct RequestHandle *handle = cls;
939   struct GNUNET_HashCode key;
940   struct GNUNET_IDENTITY_PROVIDER_Ticket *ticket;
941   char* ticket_str;
942
943   handle->op = NULL;
944
945   if (NULL == ego)
946   {
947     handle->emsg = GNUNET_strdup ("No identity found.");
948     GNUNET_SCHEDULER_add_now (&do_error, handle);
949     return;
950   }
951
952   //Get ticket
953   GNUNET_CRYPTO_hash (GNUNET_REST_JSONAPI_IDENTITY_PROVIDER_TICKET,
954                       strlen (GNUNET_REST_JSONAPI_IDENTITY_PROVIDER_TICKET),
955                       &key);
956
957   if ( GNUNET_NO ==
958        GNUNET_CONTAINER_multihashmap_contains (handle->conndata_handle->url_param_map,
959                                                &key) )
960   {
961     handle->emsg = GNUNET_strdup ("No ticket given.");
962     GNUNET_SCHEDULER_add_now (&do_error, handle);
963     return;
964   }
965   ticket_str = GNUNET_CONTAINER_multihashmap_get (handle->conndata_handle->url_param_map,
966                                                   &key);
967   handle->priv_key = GNUNET_IDENTITY_ego_get_private_key (ego);
968   GNUNET_IDENTITY_PROVIDER_string_to_ticket (ticket_str,
969                                              &ticket);
970
971   handle->idp = GNUNET_IDENTITY_PROVIDER_connect (cfg);
972   handle->idp_op = GNUNET_IDENTITY_PROVIDER_exchange_ticket (handle->idp,
973                                                              ticket,
974                                                              handle->priv_key,
975                                                              &exchange_cont,
976                                                              handle);
977   GNUNET_IDENTITY_PROVIDER_ticket_destroy (ticket);
978
979 }
980
981
982
983 /**
984  * Respond to issue request
985  *
986  * @param con_handle the connection handle
987  * @param url the url
988  * @param cls the RequestHandle
989  */
990 static void
991 exchange_token_ticket_cont (struct GNUNET_REST_RequestHandle *con_handle,
992                             const char* url,
993                             void *cls)
994 {
995   struct RequestHandle *handle = cls;
996
997   //Get token from GNS
998   handle->op = GNUNET_IDENTITY_get (handle->identity_handle,
999                                     "gns-master",
1000                                     &exchange_token_ticket_cb,
1001                                     handle);
1002 }
1003
1004 /**
1005  * Respond to OPTIONS request
1006  *
1007  * @param con_handle the connection handle
1008  * @param url the url
1009  * @param cls the RequestHandle
1010  */
1011 static void
1012 options_cont (struct GNUNET_REST_RequestHandle *con_handle,
1013               const char* url,
1014               void *cls)
1015 {
1016   struct MHD_Response *resp;
1017   struct RequestHandle *handle = cls;
1018
1019   //For now, independent of path return all options
1020   resp = GNUNET_REST_create_response (NULL);
1021   MHD_add_response_header (resp,
1022                            "Access-Control-Allow-Methods",
1023                            allow_methods);
1024   handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
1025   cleanup_handle (handle);
1026   return;
1027 }
1028
1029 /**
1030  * Handle rest request
1031  *
1032  * @param handle the request handle
1033  */
1034 static void
1035 init_cont (struct RequestHandle *handle)
1036 {
1037   struct GNUNET_REST_RequestHandlerError err;
1038   static const struct GNUNET_REST_RequestHandler handlers[] = {
1039     {MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_IDENTITY_TOKEN_ISSUE, &issue_token_cont},
1040     //{MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_IDENTITY_TOKEN_CHECK, &check_token_cont},
1041     {MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_IDENTITY_PROVIDER, &list_token_cont},
1042     {MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_IDENTITY_PROVIDER, &options_cont},
1043     {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_IDENTITY_OAUTH2_TOKEN, &exchange_token_ticket_cont},
1044     GNUNET_REST_HANDLER_END
1045   };
1046
1047   if (GNUNET_NO == GNUNET_REST_handle_request (handle->conndata_handle,
1048                                                handlers,
1049                                                &err,
1050                                                handle))
1051   {
1052     handle->response_code = err.error_code;
1053     GNUNET_SCHEDULER_add_now (&do_error, handle);
1054   }
1055 }
1056
1057 /**
1058  * If listing is enabled, prints information about the egos.
1059  *
1060  * This function is initially called for all egos and then again
1061  * whenever a ego's identifier changes or if it is deleted.  At the
1062  * end of the initial pass over all egos, the function is once called
1063  * with 'NULL' for 'ego'. That does NOT mean that the callback won't
1064  * be invoked in the future or that there was an error.
1065  *
1066  * When used with 'GNUNET_IDENTITY_create' or 'GNUNET_IDENTITY_get',
1067  * this function is only called ONCE, and 'NULL' being passed in
1068  * 'ego' does indicate an error (i.e. name is taken or no default
1069  * value is known).  If 'ego' is non-NULL and if '*ctx'
1070  * is set in those callbacks, the value WILL be passed to a subsequent
1071  * call to the identity callback of 'GNUNET_IDENTITY_connect' (if
1072  * that one was not NULL).
1073  *
1074  * When an identity is renamed, this function is called with the
1075  * (known) ego but the NEW identifier.
1076  *
1077  * When an identity is deleted, this function is called with the
1078  * (known) ego and "NULL" for the 'identifier'.  In this case,
1079  * the 'ego' is henceforth invalid (and the 'ctx' should also be
1080  * cleaned up).
1081  *
1082  * @param cls closure
1083  * @param ego ego handle
1084  * @param ctx context for application to store data for this ego
1085  *                 (during the lifetime of this process, initially NULL)
1086  * @param identifier identifier assigned by the user for this ego,
1087  *                   NULL if the user just deleted the ego and it
1088  *                   must thus no longer be used
1089  */
1090 static void
1091 list_ego (void *cls,
1092           struct GNUNET_IDENTITY_Ego *ego,
1093           void **ctx,
1094           const char *identifier)
1095 {
1096   struct RequestHandle *handle = cls;
1097   struct EgoEntry *ego_entry;
1098   struct GNUNET_CRYPTO_EcdsaPublicKey pk;
1099
1100   if ((NULL == ego) && (ID_REST_STATE_INIT == handle->state))
1101   {
1102     handle->state = ID_REST_STATE_POST_INIT;
1103     init_cont (handle);
1104     return;
1105   }
1106   if (ID_REST_STATE_INIT == handle->state) {
1107     ego_entry = GNUNET_new (struct EgoEntry);
1108     GNUNET_IDENTITY_ego_get_public_key (ego, &pk);
1109     ego_entry->keystring =
1110       GNUNET_CRYPTO_ecdsa_public_key_to_string (&pk);
1111     ego_entry->ego = ego;
1112     ego_entry->identifier = GNUNET_strdup (identifier);
1113     GNUNET_CONTAINER_DLL_insert_tail(handle->ego_head,handle->ego_tail, ego_entry);
1114   }
1115
1116 }
1117
1118 /**
1119  * Function processing the REST call
1120  *
1121  * @param method HTTP method
1122  * @param url URL of the HTTP request
1123  * @param data body of the HTTP request (optional)
1124  * @param data_size length of the body
1125  * @param proc callback function for the result
1126  * @param proc_cls closure for callback function
1127  * @return GNUNET_OK if request accepted
1128  */
1129 static void
1130 rest_identity_process_request(struct GNUNET_REST_RequestHandle *conndata_handle,
1131                               GNUNET_REST_ResultProcessor proc,
1132                               void *proc_cls)
1133 {
1134   struct RequestHandle *handle = GNUNET_new (struct RequestHandle);
1135
1136   handle->timeout = GNUNET_TIME_UNIT_FOREVER_REL;
1137   handle->proc_cls = proc_cls;
1138   handle->proc = proc;
1139   handle->state = ID_REST_STATE_INIT;
1140   handle->conndata_handle = conndata_handle;
1141
1142
1143   handle->url = GNUNET_strdup (conndata_handle->url);
1144   if (handle->url[strlen (handle->url)-1] == '/')
1145     handle->url[strlen (handle->url)-1] = '\0';
1146   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1147               "Connecting...\n");
1148   handle->identity_handle = GNUNET_IDENTITY_connect (cfg,
1149                                                      &list_ego,
1150                                                      handle);
1151   handle->timeout_task =
1152     GNUNET_SCHEDULER_add_delayed (handle->timeout,
1153                                   &do_timeout,
1154                                   handle);
1155   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1156               "Connected\n");
1157 }
1158
1159 /**
1160  * Entry point for the plugin.
1161  *
1162  * @param cls Config info
1163  * @return NULL on error, otherwise the plugin context
1164  */
1165 void *
1166 libgnunet_plugin_rest_identity_provider_init (void *cls)
1167 {
1168   static struct Plugin plugin;
1169   struct GNUNET_REST_Plugin *api;
1170
1171   cfg = cls;
1172   if (NULL != plugin.cfg)
1173     return NULL;                /* can only initialize once! */
1174   memset (&plugin, 0, sizeof (struct Plugin));
1175   plugin.cfg = cfg;
1176   api = GNUNET_new (struct GNUNET_REST_Plugin);
1177   api->cls = &plugin;
1178   api->name = GNUNET_REST_API_NS_IDENTITY_PROVIDER;
1179   api->process_request = &rest_identity_process_request;
1180   GNUNET_asprintf (&allow_methods,
1181                    "%s, %s, %s, %s, %s",
1182                    MHD_HTTP_METHOD_GET,
1183                    MHD_HTTP_METHOD_POST,
1184                    MHD_HTTP_METHOD_PUT,
1185                    MHD_HTTP_METHOD_DELETE,
1186                    MHD_HTTP_METHOD_OPTIONS);
1187
1188   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1189               _("Identity Token REST API initialized\n"));
1190   return api;
1191 }
1192
1193
1194 /**
1195  * Exit point from the plugin.
1196  *
1197  * @param cls the plugin context (as returned by "init")
1198  * @return always NULL
1199  */
1200 void *
1201 libgnunet_plugin_rest_identity_provider_done (void *cls)
1202 {
1203   struct GNUNET_REST_Plugin *api = cls;
1204   struct Plugin *plugin = api->cls;
1205
1206   plugin->cfg = NULL;
1207   GNUNET_free_non_null (allow_methods);
1208   GNUNET_free (api);
1209   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1210               "Identity Token REST plugin is finished\n");
1211   return NULL;
1212 }
1213
1214 /* end of plugin_rest_gns.c */