2 This file is part of GNUnet.
3 Copyright (C) 2012-2015 GNUnet e.V.
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.
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.
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.
21 * @author Martin Schanzenbach
22 * @file identity/plugin_rest_identity.c
23 * @brief GNUnet Namestore REST plugin
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"
39 #include "gnunet_signatures.h"
40 #include "gnunet_identity_attribute_lib.h"
41 #include "gnunet_identity_provider_service.h"
46 #define GNUNET_REST_API_NS_IDENTITY_PROVIDER "/idp"
51 #define GNUNET_REST_API_NS_IDENTITY_ATTRIBUTES "/idp/attributes"
56 #define GNUNET_REST_API_NS_IDENTITY_TICKETS "/idp/tickets"
61 #define GNUNET_REST_API_NS_IDENTITY_REVOKE "/idp/revoke"
66 #define GNUNET_REST_API_NS_IDENTITY_CONSUME "/idp/consume"
71 #define GNUNET_REST_API_NS_AUTHORIZE "/idp/authorize"
76 #define GNUNET_REST_API_NS_LOGIN "/idp/login"
81 #define GNUNET_REST_JSONAPI_IDENTITY_ATTRIBUTE "attribute"
86 #define GNUNET_REST_JSONAPI_IDENTITY_TICKET "ticket"
92 #define GNUNET_REST_JSONAPI_IDENTITY_ATTRIBUTE_VALUE "value"
95 * State while collecting all egos
97 #define ID_REST_STATE_INIT 0
100 * Done collecting egos
102 #define ID_REST_STATE_POST_INIT 1
105 * OIDC response_type key
107 #define OIDC_RESPONSE_TYPE_KEY "response_type"
112 #define OIDC_CLIENT_ID_KEY "client_id"
117 #define OIDC_SCOPE_KEY "scope"
120 * OIDC redirect_uri key
122 #define OIDC_REDIRECT_URI_KEY "redirect_uri"
127 #define OIDC_STATE_KEY "state"
132 #define OIDC_NONCE_KEY "nonce"
135 * OIDC authorization header key
137 #define OIDC_AUTHORIZATION_HEADER_KEY "Authorization"
140 * OIDC expected response_type while authorizing
142 #define OIDC_EXPECTED_AUTHORIZATION_RESPONSE_TYPE "code"
145 * OIDC expected scope part while authorizing
147 #define OIDC_EXPECTED_AUTHORIZATION_SCOPE "openid"
151 * OIDC ignored parameter array
153 char* OIDC_ignored_parameter_array [] =
155 "display", "prompt", "max_age", "ui_locales", "response_mode",
156 "id_token_hint", "login_hint", "acr_values"
160 * OIDC authorized identities and times hashmap
162 struct GNUNET_CONTAINER_MultiHashMap *OIDC_authorized_identities;
165 * The configuration handle
167 const struct GNUNET_CONFIGURATION_Handle *cfg;
170 * HTTP methods allows for this plugin
172 static char* allow_methods;
175 * @brief struct returned by the initialization function of the plugin
179 const struct GNUNET_CONFIGURATION_Handle *cfg;
190 struct EgoEntry *next;
195 struct EgoEntry *prev;
210 struct GNUNET_IDENTITY_Ego *ego;
219 struct EgoEntry *ego_head;
224 struct EgoEntry *ego_tail;
229 struct EgoEntry *ego_entry;
232 * Ptr to current ego private key
234 const struct GNUNET_CRYPTO_EcdsaPrivateKey *priv_key;
237 * The processing state
242 * Handle to Identity service.
244 struct GNUNET_IDENTITY_Handle *identity_handle;
249 struct GNUNET_REST_RequestHandle *rest_handle;
255 struct GNUNET_IDENTITY_Operation *op;
260 struct GNUNET_IDENTITY_PROVIDER_Handle *idp;
265 struct GNUNET_IDENTITY_PROVIDER_Operation *idp_op;
270 struct GNUNET_IDENTITY_PROVIDER_AttributeIterator *attr_it;
275 struct GNUNET_IDENTITY_PROVIDER_TicketIterator *ticket_it;
278 * Desired timeout for the lookup (default is no timeout).
280 struct GNUNET_TIME_Relative timeout;
283 * ID of a task associated with the resolution process.
285 struct GNUNET_SCHEDULER_Task *timeout_task;
288 * The plugin result processor
290 GNUNET_REST_ResultProcessor proc;
293 * The closure of the result processor
303 * Error response message
313 * Error response description
325 struct GNUNET_JSONAPI_Document *resp_object;
332 * Cleanup lookup handle
333 * @param handle Handle to clean up
336 cleanup_handle (struct RequestHandle *handle)
338 struct EgoEntry *ego_entry;
339 struct EgoEntry *ego_tmp;
340 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
342 if (NULL != handle->resp_object)
343 GNUNET_JSONAPI_document_delete (handle->resp_object);
344 if (NULL != handle->timeout_task)
345 GNUNET_SCHEDULER_cancel (handle->timeout_task);
346 if (NULL != handle->identity_handle)
347 GNUNET_IDENTITY_disconnect (handle->identity_handle);
348 if (NULL != handle->attr_it)
349 GNUNET_IDENTITY_PROVIDER_get_attributes_stop (handle->attr_it);
350 if (NULL != handle->ticket_it)
351 GNUNET_IDENTITY_PROVIDER_ticket_iteration_stop (handle->ticket_it);
352 if (NULL != handle->idp)
353 GNUNET_IDENTITY_PROVIDER_disconnect (handle->idp);
354 if (NULL != handle->url)
355 GNUNET_free (handle->url);
356 if (NULL != handle->emsg)
357 GNUNET_free (handle->emsg);
358 for (ego_entry = handle->ego_head;
362 ego_entry = ego_entry->next;
363 GNUNET_free (ego_tmp->identifier);
364 GNUNET_free (ego_tmp->keystring);
365 GNUNET_free (ego_tmp);
367 GNUNET_free (handle);
371 cleanup_handle_delayed (void *cls)
373 cleanup_handle (cls);
378 * Task run on error, sends error message. Cleans up everything.
380 * @param cls the `struct RequestHandle`
385 struct RequestHandle *handle = cls;
386 struct MHD_Response *resp;
389 GNUNET_asprintf (&json_error,
392 resp = GNUNET_REST_create_response (json_error);
393 handle->proc (handle->proc_cls, resp, handle->response_code);
394 cleanup_handle (handle);
395 GNUNET_free (json_error);
399 * Task run on error, sends error message. Cleans up everything.
401 * @param cls the `struct RequestHandle`
404 do_redirect_error (void *cls)
406 struct RequestHandle *handle = cls;
407 struct MHD_Response *resp;
409 //TODO handle->url is wrong
410 GNUNET_asprintf (&redirect,
411 "%s?error=%s&error_description=%s",
412 handle->eredirect, handle->emsg, handle->edesc );
413 resp = GNUNET_REST_create_response ("");
414 MHD_add_response_header (resp, "Location", redirect);
415 handle->proc (handle->proc_cls, resp, MHD_HTTP_FOUND);
416 cleanup_handle (handle);
417 GNUNET_free (redirect);
421 * Task run on timeout, sends error message. Cleans up everything.
423 * @param cls the `struct RequestHandle`
426 do_timeout (void *cls)
428 struct RequestHandle *handle = cls;
430 handle->timeout_task = NULL;
436 collect_error_cb (void *cls)
438 struct RequestHandle *handle = cls;
444 finished_cont (void *cls,
448 struct RequestHandle *handle = cls;
449 struct MHD_Response *resp;
451 resp = GNUNET_REST_create_response (emsg);
452 if (GNUNET_OK != success)
454 GNUNET_SCHEDULER_add_now (&do_error, handle);
457 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
458 GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
463 * Return attributes for identity
465 * @param cls the request handle
468 return_response (void *cls)
471 struct RequestHandle *handle = cls;
472 struct MHD_Response *resp;
474 GNUNET_JSONAPI_document_serialize (handle->resp_object, &result_str);
475 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Result %s\n", result_str);
476 resp = GNUNET_REST_create_response (result_str);
477 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
478 GNUNET_free (result_str);
479 cleanup_handle (handle);
484 collect_finished_cb (void *cls)
486 struct RequestHandle *handle = cls;
488 handle->attr_it = NULL;
489 handle->ticket_it = NULL;
490 GNUNET_SCHEDULER_add_now (&return_response, handle);
495 * Collect all attributes for an ego
499 ticket_collect (void *cls,
500 const struct GNUNET_IDENTITY_PROVIDER_Ticket *ticket)
502 struct GNUNET_JSONAPI_Resource *json_resource;
503 struct RequestHandle *handle = cls;
507 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding ticket\n");
508 tmp = GNUNET_STRINGS_data_to_string_alloc (&ticket->rnd,
510 json_resource = GNUNET_JSONAPI_resource_new (GNUNET_REST_JSONAPI_IDENTITY_TICKET,
513 GNUNET_JSONAPI_document_resource_add (handle->resp_object, json_resource);
515 tmp = GNUNET_STRINGS_data_to_string_alloc (&ticket->identity,
516 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));
517 value = json_string (tmp);
518 GNUNET_JSONAPI_resource_add_attr (json_resource,
523 tmp = GNUNET_STRINGS_data_to_string_alloc (&ticket->audience,
524 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));
525 value = json_string (tmp);
526 GNUNET_JSONAPI_resource_add_attr (json_resource,
531 tmp = GNUNET_STRINGS_data_to_string_alloc (&ticket->rnd,
533 value = json_string (tmp);
534 GNUNET_JSONAPI_resource_add_attr (json_resource,
539 GNUNET_IDENTITY_PROVIDER_ticket_iteration_next (handle->ticket_it);
545 * List tickets for identity request
547 * @param con_handle the connection handle
549 * @param cls the RequestHandle
552 list_tickets_cont (struct GNUNET_REST_RequestHandle *con_handle,
556 const struct GNUNET_CRYPTO_EcdsaPrivateKey *priv_key;
557 struct RequestHandle *handle = cls;
558 struct EgoEntry *ego_entry;
561 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Getting tickets for %s.\n",
563 if ( strlen (GNUNET_REST_API_NS_IDENTITY_TICKETS) >=
564 strlen (handle->url))
566 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "No identity given.\n");
567 GNUNET_SCHEDULER_add_now (&do_error, handle);
570 identity = handle->url + strlen (GNUNET_REST_API_NS_IDENTITY_TICKETS) + 1;
572 for (ego_entry = handle->ego_head;
574 ego_entry = ego_entry->next)
575 if (0 == strcmp (identity, ego_entry->identifier))
577 handle->resp_object = GNUNET_JSONAPI_document_new ();
579 if (NULL == ego_entry)
582 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Ego %s not found.\n",
584 GNUNET_SCHEDULER_add_now (&return_response, handle);
587 priv_key = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego);
588 handle->idp = GNUNET_IDENTITY_PROVIDER_connect (cfg);
589 handle->ticket_it = GNUNET_IDENTITY_PROVIDER_ticket_iteration_start (handle->idp,
595 &collect_finished_cb,
601 add_attribute_cont (struct GNUNET_REST_RequestHandle *con_handle,
605 const struct GNUNET_CRYPTO_EcdsaPrivateKey *identity_priv;
606 const char* identity;
607 const char* name_str;
608 const char* value_str;
610 struct RequestHandle *handle = cls;
611 struct EgoEntry *ego_entry;
612 struct MHD_Response *resp;
613 struct GNUNET_IDENTITY_ATTRIBUTE_Claim *attribute;
614 struct GNUNET_JSONAPI_Document *json_obj;
615 struct GNUNET_JSONAPI_Resource *json_res;
616 char term_data[handle->rest_handle->data_size+1];
620 struct GNUNET_JSON_Specification docspec[] = {
621 GNUNET_JSON_spec_jsonapi_document (&json_obj),
622 GNUNET_JSON_spec_end()
625 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding an attribute for %s.\n",
627 if ( strlen (GNUNET_REST_API_NS_IDENTITY_ATTRIBUTES) >=
628 strlen (handle->url))
630 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "No identity given.\n");
631 GNUNET_SCHEDULER_add_now (&do_error, handle);
634 identity = handle->url + strlen (GNUNET_REST_API_NS_IDENTITY_ATTRIBUTES) + 1;
636 for (ego_entry = handle->ego_head;
638 ego_entry = ego_entry->next)
639 if (0 == strcmp (identity, ego_entry->identifier))
642 if (NULL == ego_entry)
644 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
645 "Identity unknown (%s)\n", identity);
646 GNUNET_JSONAPI_document_delete (json_obj);
649 identity_priv = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego);
651 if (0 >= handle->rest_handle->data_size)
653 GNUNET_SCHEDULER_add_now (&do_error, handle);
657 term_data[handle->rest_handle->data_size] = '\0';
658 GNUNET_memcpy (term_data,
659 handle->rest_handle->data,
660 handle->rest_handle->data_size);
661 data_json = json_loads (term_data,
664 GNUNET_assert (GNUNET_OK ==
665 GNUNET_JSON_parse (data_json, docspec,
667 json_decref (data_json);
668 if (NULL == json_obj)
670 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
671 "Unable to parse JSONAPI Object from %s\n",
673 GNUNET_SCHEDULER_add_now (&do_error, handle);
676 if (1 != GNUNET_JSONAPI_document_resource_count (json_obj))
678 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
679 "Cannot create more than 1 resource! (Got %d)\n",
680 GNUNET_JSONAPI_document_resource_count (json_obj));
681 GNUNET_JSONAPI_document_delete (json_obj);
682 GNUNET_SCHEDULER_add_now (&do_error, handle);
685 json_res = GNUNET_JSONAPI_document_get_resource (json_obj, 0);
686 if (GNUNET_NO == GNUNET_JSONAPI_resource_check_type (json_res,
687 GNUNET_REST_JSONAPI_IDENTITY_ATTRIBUTE))
689 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
690 "Unsupported JSON data type\n");
691 GNUNET_JSONAPI_document_delete (json_obj);
692 resp = GNUNET_REST_create_response (NULL);
693 handle->proc (handle->proc_cls, resp, MHD_HTTP_CONFLICT);
694 cleanup_handle (handle);
697 name_str = GNUNET_JSONAPI_resource_get_id (json_res);
698 value_json = GNUNET_JSONAPI_resource_read_attr (json_res,
700 value_str = json_string_value (value_json);
701 attribute = GNUNET_IDENTITY_ATTRIBUTE_claim_new (name_str,
702 GNUNET_IDENTITY_ATTRIBUTE_TYPE_STRING,
704 strlen (value_str) + 1);
705 handle->idp = GNUNET_IDENTITY_PROVIDER_connect (cfg);
706 handle->idp_op = GNUNET_IDENTITY_PROVIDER_attribute_store (handle->idp,
711 GNUNET_free (attribute);
712 GNUNET_JSONAPI_document_delete (json_obj);
718 * Collect all attributes for an ego
722 attr_collect (void *cls,
723 const struct GNUNET_CRYPTO_EcdsaPublicKey *identity,
724 const struct GNUNET_IDENTITY_ATTRIBUTE_Claim *attr)
726 struct GNUNET_JSONAPI_Resource *json_resource;
727 struct RequestHandle *handle = cls;
730 if ((NULL == attr->name) || (NULL == attr->data))
732 GNUNET_IDENTITY_PROVIDER_get_attributes_next (handle->attr_it);
736 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding attribute: %s\n",
738 json_resource = GNUNET_JSONAPI_resource_new (GNUNET_REST_JSONAPI_IDENTITY_ATTRIBUTE,
740 GNUNET_JSONAPI_document_resource_add (handle->resp_object, json_resource);
742 value = json_string (attr->data);
743 GNUNET_JSONAPI_resource_add_attr (json_resource,
747 GNUNET_IDENTITY_PROVIDER_get_attributes_next (handle->attr_it);
753 * List attributes for identity request
755 * @param con_handle the connection handle
757 * @param cls the RequestHandle
760 list_attribute_cont (struct GNUNET_REST_RequestHandle *con_handle,
764 const struct GNUNET_CRYPTO_EcdsaPrivateKey *priv_key;
765 struct RequestHandle *handle = cls;
766 struct EgoEntry *ego_entry;
769 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Getting attributes for %s.\n",
771 if ( strlen (GNUNET_REST_API_NS_IDENTITY_ATTRIBUTES) >=
772 strlen (handle->url))
774 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "No identity given.\n");
775 GNUNET_SCHEDULER_add_now (&do_error, handle);
778 identity = handle->url + strlen (GNUNET_REST_API_NS_IDENTITY_ATTRIBUTES) + 1;
780 for (ego_entry = handle->ego_head;
782 ego_entry = ego_entry->next)
783 if (0 == strcmp (identity, ego_entry->identifier))
785 handle->resp_object = GNUNET_JSONAPI_document_new ();
788 if (NULL == ego_entry)
791 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Ego %s not found.\n",
793 GNUNET_SCHEDULER_add_now (&return_response, handle);
796 priv_key = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego);
797 handle->idp = GNUNET_IDENTITY_PROVIDER_connect (cfg);
798 handle->attr_it = GNUNET_IDENTITY_PROVIDER_get_attributes_start (handle->idp,
804 &collect_finished_cb,
810 revoke_ticket_cont (struct GNUNET_REST_RequestHandle *con_handle,
814 const struct GNUNET_CRYPTO_EcdsaPrivateKey *identity_priv;
815 const char* identity_str;
816 const char* audience_str;
819 struct RequestHandle *handle = cls;
820 struct EgoEntry *ego_entry;
821 struct MHD_Response *resp;
822 struct GNUNET_IDENTITY_PROVIDER_Ticket ticket;
823 struct GNUNET_JSONAPI_Document *json_obj;
824 struct GNUNET_JSONAPI_Resource *json_res;
825 struct GNUNET_CRYPTO_EcdsaPublicKey tmp_pk;
826 char term_data[handle->rest_handle->data_size+1];
828 json_t *identity_json;
829 json_t *audience_json;
832 struct GNUNET_JSON_Specification docspec[] = {
833 GNUNET_JSON_spec_jsonapi_document (&json_obj),
834 GNUNET_JSON_spec_end()
837 if (0 >= handle->rest_handle->data_size)
839 GNUNET_SCHEDULER_add_now (&do_error, handle);
843 term_data[handle->rest_handle->data_size] = '\0';
844 GNUNET_memcpy (term_data,
845 handle->rest_handle->data,
846 handle->rest_handle->data_size);
847 data_json = json_loads (term_data,
850 GNUNET_assert (GNUNET_OK ==
851 GNUNET_JSON_parse (data_json, docspec,
853 json_decref (data_json);
854 if (NULL == json_obj)
856 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
857 "Unable to parse JSONAPI Object from %s\n",
859 GNUNET_SCHEDULER_add_now (&do_error, handle);
862 if (1 != GNUNET_JSONAPI_document_resource_count (json_obj))
864 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
865 "Cannot create more than 1 resource! (Got %d)\n",
866 GNUNET_JSONAPI_document_resource_count (json_obj));
867 GNUNET_JSONAPI_document_delete (json_obj);
868 GNUNET_SCHEDULER_add_now (&do_error, handle);
871 json_res = GNUNET_JSONAPI_document_get_resource (json_obj, 0);
872 if (GNUNET_NO == GNUNET_JSONAPI_resource_check_type (json_res,
873 GNUNET_REST_JSONAPI_IDENTITY_TICKET))
875 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
876 "Unsupported JSON data type\n");
877 GNUNET_JSONAPI_document_delete (json_obj);
878 resp = GNUNET_REST_create_response (NULL);
879 handle->proc (handle->proc_cls, resp, MHD_HTTP_CONFLICT);
880 cleanup_handle (handle);
883 rnd_json = GNUNET_JSONAPI_resource_read_attr (json_res,
885 identity_json = GNUNET_JSONAPI_resource_read_attr (json_res,
887 audience_json = GNUNET_JSONAPI_resource_read_attr (json_res,
889 rnd_str = json_string_value (rnd_json);
890 identity_str = json_string_value (identity_json);
891 audience_str = json_string_value (audience_json);
893 GNUNET_STRINGS_string_to_data (rnd_str,
897 // GNUNET_STRINGS_string_to_data (identity_str,
898 // strlen (identity_str),
899 // &ticket.identity,type filter text
900 // sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));
901 GNUNET_STRINGS_string_to_data (audience_str,
902 strlen (audience_str),
904 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));
906 for (ego_entry = handle->ego_head;
908 ego_entry = ego_entry->next)
910 GNUNET_IDENTITY_ego_get_public_key (ego_entry->ego,
912 if (0 == memcmp (&ticket.identity,
914 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)))
917 if (NULL == ego_entry)
919 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
920 "Identity unknown (%s)\n", identity_str);
921 GNUNET_JSONAPI_document_delete (json_obj);
924 identity_priv = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego);
926 handle->idp = GNUNET_IDENTITY_PROVIDER_connect (cfg);
927 handle->idp_op = GNUNET_IDENTITY_PROVIDER_ticket_revoke (handle->idp,
932 GNUNET_JSONAPI_document_delete (json_obj);
936 consume_cont (void *cls,
937 const struct GNUNET_CRYPTO_EcdsaPublicKey *identity,
938 const struct GNUNET_IDENTITY_ATTRIBUTE_Claim *attr)
940 struct RequestHandle *handle = cls;
941 struct GNUNET_JSONAPI_Resource *json_resource;
944 if (NULL == identity)
946 GNUNET_SCHEDULER_add_now (&return_response, handle);
950 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding attribute: %s\n",
952 json_resource = GNUNET_JSONAPI_resource_new (GNUNET_REST_JSONAPI_IDENTITY_ATTRIBUTE,
954 GNUNET_JSONAPI_document_resource_add (handle->resp_object, json_resource);
956 value = json_string (attr->data);
957 GNUNET_JSONAPI_resource_add_attr (json_resource,
964 consume_ticket_cont (struct GNUNET_REST_RequestHandle *con_handle,
968 const struct GNUNET_CRYPTO_EcdsaPrivateKey *identity_priv;
969 const char* identity_str;
970 const char* audience_str;
973 struct RequestHandle *handle = cls;
974 struct EgoEntry *ego_entry;
975 struct MHD_Response *resp;
976 struct GNUNET_IDENTITY_PROVIDER_Ticket ticket;
977 struct GNUNET_JSONAPI_Document *json_obj;
978 struct GNUNET_JSONAPI_Resource *json_res;
979 struct GNUNET_CRYPTO_EcdsaPublicKey tmp_pk;
980 char term_data[handle->rest_handle->data_size+1];
982 json_t *identity_json;
983 json_t *audience_json;
986 struct GNUNET_JSON_Specification docspec[] = {
987 GNUNET_JSON_spec_jsonapi_document (&json_obj),
988 GNUNET_JSON_spec_end()
991 if (0 >= handle->rest_handle->data_size)
993 GNUNET_SCHEDULER_add_now (&do_error, handle);
997 term_data[handle->rest_handle->data_size] = '\0';
998 GNUNET_memcpy (term_data,
999 handle->rest_handle->data,
1000 handle->rest_handle->data_size);
1001 data_json = json_loads (term_data,
1004 GNUNET_assert (GNUNET_OK ==
1005 GNUNET_JSON_parse (data_json, docspec,
1007 json_decref (data_json);
1008 if (NULL == json_obj)
1010 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1011 "Unable to parse JSONAPI Object from %s\n",
1013 GNUNET_SCHEDULER_add_now (&do_error, handle);
1016 if (1 != GNUNET_JSONAPI_document_resource_count (json_obj))
1018 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1019 "Cannot create more than 1 resource! (Got %d)\n",
1020 GNUNET_JSONAPI_document_resource_count (json_obj));
1021 GNUNET_JSONAPI_document_delete (json_obj);
1022 GNUNET_SCHEDULER_add_now (&do_error, handle);
1025 json_res = GNUNET_JSONAPI_document_get_resource (json_obj, 0);
1026 if (GNUNET_NO == GNUNET_JSONAPI_resource_check_type (json_res,
1027 GNUNET_REST_JSONAPI_IDENTITY_TICKET))
1029 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1030 "Unsupported JSON data type\n");
1031 GNUNET_JSONAPI_document_delete (json_obj);
1032 resp = GNUNET_REST_create_response (NULL);
1033 handle->proc (handle->proc_cls, resp, MHD_HTTP_CONFLICT);
1034 cleanup_handle (handle);
1037 rnd_json = GNUNET_JSONAPI_resource_read_attr (json_res,
1039 identity_json = GNUNET_JSONAPI_resource_read_attr (json_res,
1041 audience_json = GNUNET_JSONAPI_resource_read_attr (json_res,
1043 rnd_str = json_string_value (rnd_json);
1044 identity_str = json_string_value (identity_json);
1045 audience_str = json_string_value (audience_json);
1047 GNUNET_STRINGS_string_to_data (rnd_str,
1051 GNUNET_STRINGS_string_to_data (identity_str,
1052 strlen (identity_str),
1054 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));
1055 GNUNET_STRINGS_string_to_data (audience_str,
1056 strlen (audience_str),
1058 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));
1060 for (ego_entry = handle->ego_head;
1062 ego_entry = ego_entry->next)
1064 GNUNET_IDENTITY_ego_get_public_key (ego_entry->ego,
1066 if (0 == memcmp (&ticket.audience,
1068 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)))
1071 if (NULL == ego_entry)
1073 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1074 "Identity unknown (%s)\n", identity_str);
1075 GNUNET_JSONAPI_document_delete (json_obj);
1078 identity_priv = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego);
1079 handle->resp_object = GNUNET_JSONAPI_document_new ();
1080 handle->idp = GNUNET_IDENTITY_PROVIDER_connect (cfg);
1081 handle->idp_op = GNUNET_IDENTITY_PROVIDER_ticket_consume (handle->idp,
1086 GNUNET_JSONAPI_document_delete (json_obj);
1092 * Respond to OPTIONS request
1094 * @param con_handle the connection handle
1095 * @param url the url
1096 * @param cls the RequestHandle
1099 options_cont (struct GNUNET_REST_RequestHandle *con_handle,
1103 struct MHD_Response *resp;
1104 struct RequestHandle *handle = cls;
1106 //For now, independent of path return all options
1107 resp = GNUNET_REST_create_response (NULL);
1108 MHD_add_response_header (resp,
1109 "Access-Control-Allow-Methods",
1111 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
1112 cleanup_handle (handle);
1117 * Respond to OPTIONS request
1119 * @param con_handle the connection handle
1120 * @param url the url
1121 * @param cls the RequestHandle
1124 authorize_cont (struct GNUNET_REST_RequestHandle *con_handle,
1128 struct MHD_Response *resp;
1129 struct RequestHandle *handle = cls;
1130 char *response_type, *client_id, *scope, *redirect_uri, *state = 0,
1132 struct timeval now, login_time;
1133 OIDC_authorized_identities = GNUNET_CONTAINER_multihashmap_create( 10, GNUNET_NO );
1134 char *login_base_url, *new_redirect;
1135 struct GNUNET_HashCode cache_key;
1137 //TODO clean up method
1139 /** The Authorization Server MUST validate all the OAuth 2.0 parameters
1140 * according to the OAuth 2.0 specification.
1143 * If the sub (subject) Claim is requested with a specific value for the
1144 * ID Token, the Authorization Server MUST only send a positive response
1145 * if the End-User identified by that sub value has an active session with
1146 * the Authorization Server or has been Authenticated as a result of the
1147 * request. The Authorization Server MUST NOT reply with an ID Token or
1148 * Access Token for a different user, even if they have an active session
1149 * with the Authorization Server. Such a request can be made either using
1150 * an id_token_hint parameter or by requesting a specific Claim Value as
1151 * described in Section 5.5.1, if the claims parameter is supported by
1152 * the implementation.
1157 // REQUIRED value: client_id
1158 GNUNET_CRYPTO_hash (OIDC_CLIENT_ID_KEY, strlen (OIDC_CLIENT_ID_KEY),
1160 if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map,
1163 handle->emsg=GNUNET_strdup("invalid_request");
1164 handle->edesc=GNUNET_strdup("Missing parameter: client_id");
1165 GNUNET_SCHEDULER_add_now (&do_error, handle);
1168 client_id = GNUNET_CONTAINER_multihashmap_get(handle->rest_handle->url_param_map,
1171 // Checks if client_id is valid:
1172 // TODO change check (lookup trusted public_key?)
1173 // if( strcmp( client_id, "localhost" ) != 0 )
1175 // handle->emsg=GNUNET_strdup("unauthorized_client");
1176 // handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1177 // GNUNET_SCHEDULER_add_now (&do_error, handle);
1181 // REQUIRED value: redirect_uri
1182 GNUNET_CRYPTO_hash (OIDC_REDIRECT_URI_KEY, strlen (OIDC_REDIRECT_URI_KEY),
1184 if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map,
1187 handle->emsg=GNUNET_strdup("invalid_request");
1188 handle->edesc=GNUNET_strdup("Missing parameter: redirect_uri");
1189 GNUNET_SCHEDULER_add_now (&do_error, handle);
1192 redirect_uri = GNUNET_CONTAINER_multihashmap_get(handle->rest_handle->url_param_map,
1195 // Checks if redirect_uri is valid:
1196 // TODO change check (check client_id->public key == address)
1197 // if( strcmp( redirect_uri, "https://localhost:8000" ) != 0 )
1199 // handle->emsg=GNUNET_strdup("invalid_request");
1200 // handle->edesc=GNUNET_strdup("Invalid or mismatching redirect_uri");
1201 // GNUNET_SCHEDULER_add_now (&do_error, handle);
1204 handle->eredirect = GNUNET_strdup(redirect_uri);
1206 // REQUIRED value: response_type
1207 GNUNET_CRYPTO_hash (OIDC_RESPONSE_TYPE_KEY, strlen (OIDC_RESPONSE_TYPE_KEY),
1209 if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map,
1212 handle->emsg=GNUNET_strdup("invalid_request");
1213 handle->edesc=GNUNET_strdup("Missing parameter: response_type");
1214 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1217 response_type = GNUNET_CONTAINER_multihashmap_get(handle->rest_handle->url_param_map,
1220 // REQUIRED value: scope
1221 GNUNET_CRYPTO_hash (OIDC_SCOPE_KEY, strlen (OIDC_SCOPE_KEY), &cache_key);
1222 if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map,
1225 handle->emsg=GNUNET_strdup("invalid_request");
1226 handle->edesc=GNUNET_strdup("Missing parameter: scope");
1227 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1230 scope = GNUNET_CONTAINER_multihashmap_get(handle->rest_handle->url_param_map,
1233 //RECOMMENDED value: state
1234 GNUNET_CRYPTO_hash (OIDC_STATE_KEY, strlen (OIDC_STATE_KEY), &cache_key);
1235 if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map,
1238 state = GNUNET_CONTAINER_multihashmap_get(handle->rest_handle->url_param_map,
1242 //OPTIONAL value: nonce
1243 GNUNET_CRYPTO_hash (OIDC_NONCE_KEY, strlen (OIDC_NONCE_KEY), &cache_key);
1244 if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map,
1247 nonce = GNUNET_CONTAINER_multihashmap_get(handle->rest_handle->url_param_map,
1251 int number_of_ignored_parameter = sizeof(OIDC_ignored_parameter_array) / sizeof(char *);
1253 for( iterator = 0; iterator < number_of_ignored_parameter; iterator++ )
1255 GNUNET_CRYPTO_hash (OIDC_ignored_parameter_array[iterator],
1256 strlen(OIDC_ignored_parameter_array[iterator]),
1258 if(GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains(handle->rest_handle->url_param_map,
1261 handle->emsg=GNUNET_strdup("access_denied");
1262 //TODO rewrite error description
1263 handle->edesc=GNUNET_strdup("Server will not handle parameter");
1264 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1269 // Checks if response_type is 'code'
1270 if( strcmp( response_type, OIDC_EXPECTED_AUTHORIZATION_RESPONSE_TYPE ) != 0 )
1272 handle->emsg=GNUNET_strdup("unsupported_response_type");
1273 handle->edesc=GNUNET_strdup("The authorization server does not support "
1274 "obtaining this authorization code.");
1275 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1278 // Checks if scope contains 'openid'
1279 if( strstr( scope, OIDC_EXPECTED_AUTHORIZATION_SCOPE ) == NULL )
1281 handle->emsg=GNUNET_strdup("invalid_scope");
1282 handle->edesc=GNUNET_strdup("The requested scope is invalid, unknown, or "
1284 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1289 //TODO check other values and use them accordingly
1294 //if header-authorization == ID
1295 //if ID is still logged
1296 // ego get Public Key of Identity
1297 // return token with public key?
1300 GNUNET_CRYPTO_hash (OIDC_AUTHORIZATION_HEADER_KEY,
1301 strlen (OIDC_AUTHORIZATION_HEADER_KEY),
1303 //No Authorization Parameter -> redirect to login
1304 if(GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains(con_handle->header_param_map,
1308 == GNUNET_CONFIGURATION_get_value_string (cfg, "identity-rest-plugin",
1309 "address", &login_base_url) )
1311 GNUNET_asprintf (&new_redirect, "%s?%s=%s&%s=%s&%s=%s&%s=%s&%s=%s&%s=%s",
1313 OIDC_RESPONSE_TYPE_KEY,
1317 OIDC_REDIRECT_URI_KEY,
1322 (state) ? state : "",
1324 (nonce) ? nonce : "");
1325 resp = GNUNET_REST_create_response ("");
1326 MHD_add_response_header (resp, "Location", new_redirect);
1330 handle->emsg = GNUNET_strdup("No server configuration");
1331 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1332 GNUNET_SCHEDULER_add_now (&do_error, handle);
1335 handle->proc (handle->proc_cls, resp, MHD_HTTP_FOUND);
1336 cleanup_handle (handle);
1337 GNUNET_free(new_redirect);
1342 char* identity = GNUNET_CONTAINER_multihashmap_get ( con_handle->header_param_map,
1344 GNUNET_CRYPTO_hash (identity, strlen (identity), &cache_key);
1345 if(GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains(OIDC_authorized_identities,
1348 login_time = *(struct timeval *)GNUNET_CONTAINER_multihashmap_get(OIDC_authorized_identities,
1350 gettimeofday(&now, NULL);
1351 //After 30 minutes redirect to login
1352 if( now.tv_sec - login_time.tv_sec >= 1800)
1354 //TODO remove redundancy [redirect to login]
1356 == GNUNET_CONFIGURATION_get_value_string (cfg, "identity-rest-plugin",
1357 "address", &login_base_url) )
1359 GNUNET_asprintf (&new_redirect, "%s?%s=%s&%s=%s&%s=%s&%s=%s&%s=%s&%s=%s",
1361 OIDC_RESPONSE_TYPE_KEY,
1365 OIDC_REDIRECT_URI_KEY,
1370 (state) ? state : "",
1372 (nonce) ? nonce : "");
1373 resp = GNUNET_REST_create_response ("");
1374 MHD_add_response_header (resp, "Location", new_redirect);
1378 handle->emsg = GNUNET_strdup("No server configuration");
1379 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1380 GNUNET_SCHEDULER_add_now (&do_error, handle);
1383 handle->proc (handle->proc_cls, resp, MHD_HTTP_FOUND);
1384 cleanup_handle (handle);
1385 GNUNET_free(new_redirect);
1391 gettimeofday( &now, NULL );
1392 GNUNET_CONTAINER_multihashmap_put( OIDC_authorized_identities, &cache_key, &now,
1393 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY);
1395 resp = GNUNET_REST_create_response ("");
1396 // MHD_add_response_header (resp, "Access-Control-Allow-Origin", "*");
1397 MHD_add_response_header (resp, "Location", redirect_uri);
1398 handle->proc (handle->proc_cls, resp, MHD_HTTP_FOUND);
1399 cleanup_handle (handle);
1406 * Respond to LOGIN request
1408 * @param con_handle the connection handle
1409 * @param url the url
1410 * @param cls the RequestHandle
1413 login_cont (struct GNUNET_REST_RequestHandle *con_handle,
1417 struct MHD_Response *resp = GNUNET_REST_create_response ("");
1418 struct RequestHandle *handle = cls;
1423 root = json_loads( handle->rest_handle->data, 0, &error );
1424 identity = json_object_get(root, "identity");
1425 if(json_is_string(identity))
1427 GNUNET_asprintf(&cookie,"Identity=%s",json_string_value(identity));
1428 MHD_add_response_header (resp, "Set-Cookie", cookie);
1429 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
1433 handle->proc (handle->proc_cls, resp, MHD_HTTP_BAD_REQUEST);
1436 cleanup_handle (handle);
1437 GNUNET_free(cookie);
1442 * Handle rest request
1444 * @param handle the request handle
1447 init_cont (struct RequestHandle *handle)
1449 struct GNUNET_REST_RequestHandlerError err;
1450 static const struct GNUNET_REST_RequestHandler handlers[] = {
1451 {MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_IDENTITY_ATTRIBUTES, &list_attribute_cont},
1452 {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_IDENTITY_ATTRIBUTES, &add_attribute_cont},
1453 {MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_IDENTITY_TICKETS, &list_tickets_cont},
1454 {MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_AUTHORIZE, &authorize_cont},
1455 {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_LOGIN, &login_cont},
1456 {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_AUTHORIZE, &authorize_cont},
1457 {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_IDENTITY_REVOKE, &revoke_ticket_cont},
1458 {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_IDENTITY_CONSUME, &consume_ticket_cont},
1459 {MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_IDENTITY_PROVIDER,
1461 GNUNET_REST_HANDLER_END
1464 if (GNUNET_NO == GNUNET_REST_handle_request (handle->rest_handle,
1469 handle->response_code = err.error_code;
1470 GNUNET_SCHEDULER_add_now (&do_error, handle);
1475 * If listing is enabled, prints information about the egos.
1477 * This function is initially called for all egos and then again
1478 * whenever a ego's identifier changes or if it is deleted. At the
1479 * end of the initial pass over all egos, the function is once called
1480 * with 'NULL' for 'ego'. That does NOT mean that the callback won't
1481 * be invoked in the future or that there was an error.
1483 * When used with 'GNUNET_IDENTITY_create' or 'GNUNET_IDENTITY_get',
1484 * this function is only called ONCE, and 'NULL' being passed in
1485 * 'ego' does indicate an error (i.e. name is taken or no default
1486 * value is known). If 'ego' is non-NULL and if '*ctx'
1487 * is set in those callbacks, the value WILL be passed to a subsequent
1488 * call to the identity callback of 'GNUNET_IDENTITY_connect' (if
1489 * that one was not NULL).
1491 * When an identity is renamed, this function is called with the
1492 * (known) ego but the NEW identifier.
1494 * When an identity is deleted, this function is called with the
1495 * (known) ego and "NULL" for the 'identifier'. In this case,
1496 * the 'ego' is henceforth invalid (and the 'ctx' should also be
1499 * @param cls closure
1500 * @param ego ego handle
1501 * @param ctx context for application to store data for this ego
1502 * (during the lifetime of this process, initially NULL)
1503 * @param identifier identifier assigned by the user for this ego,
1504 * NULL if the user just deleted the ego and it
1505 * must thus no longer be used
1508 list_ego (void *cls,
1509 struct GNUNET_IDENTITY_Ego *ego,
1511 const char *identifier)
1513 struct RequestHandle *handle = cls;
1514 struct EgoEntry *ego_entry;
1515 struct GNUNET_CRYPTO_EcdsaPublicKey pk;
1517 if ((NULL == ego) && (ID_REST_STATE_INIT == handle->state))
1519 handle->state = ID_REST_STATE_POST_INIT;
1523 if (ID_REST_STATE_INIT == handle->state) {
1524 ego_entry = GNUNET_new (struct EgoEntry);
1525 GNUNET_IDENTITY_ego_get_public_key (ego, &pk);
1526 ego_entry->keystring =
1527 GNUNET_CRYPTO_ecdsa_public_key_to_string (&pk);
1528 ego_entry->ego = ego;
1529 ego_entry->identifier = GNUNET_strdup (identifier);
1530 GNUNET_CONTAINER_DLL_insert_tail(handle->ego_head,handle->ego_tail, ego_entry);
1536 * Function processing the REST call
1538 * @param method HTTP method
1539 * @param url URL of the HTTP request
1540 * @param data body of the HTTP request (optional)
1541 * @param data_size length of the body
1542 * @param proc callback function for the result
1543 * @param proc_cls closure for callback function
1544 * @return GNUNET_OK if request accepted
1547 rest_identity_process_request(struct GNUNET_REST_RequestHandle *rest_handle,
1548 GNUNET_REST_ResultProcessor proc,
1551 struct RequestHandle *handle = GNUNET_new (struct RequestHandle);
1553 handle->timeout = GNUNET_TIME_UNIT_FOREVER_REL;
1554 handle->proc_cls = proc_cls;
1555 handle->proc = proc;
1556 handle->state = ID_REST_STATE_INIT;
1557 handle->rest_handle = rest_handle;
1559 handle->url = GNUNET_strdup (rest_handle->url);
1560 if (handle->url[strlen (handle->url)-1] == '/')
1561 handle->url[strlen (handle->url)-1] = '\0';
1562 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1564 handle->identity_handle = GNUNET_IDENTITY_connect (cfg,
1567 handle->timeout_task =
1568 GNUNET_SCHEDULER_add_delayed (handle->timeout,
1571 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1576 * Entry point for the plugin.
1578 * @param cls Config info
1579 * @return NULL on error, otherwise the plugin context
1582 libgnunet_plugin_rest_identity_provider_init (void *cls)
1584 static struct Plugin plugin;
1585 struct GNUNET_REST_Plugin *api;
1588 if (NULL != plugin.cfg)
1589 return NULL; /* can only initialize once! */
1590 memset (&plugin, 0, sizeof (struct Plugin));
1592 api = GNUNET_new (struct GNUNET_REST_Plugin);
1594 api->name = GNUNET_REST_API_NS_IDENTITY_PROVIDER;
1595 api->process_request = &rest_identity_process_request;
1596 GNUNET_asprintf (&allow_methods,
1597 "%s, %s, %s, %s, %s",
1598 MHD_HTTP_METHOD_GET,
1599 MHD_HTTP_METHOD_POST,
1600 MHD_HTTP_METHOD_PUT,
1601 MHD_HTTP_METHOD_DELETE,
1602 MHD_HTTP_METHOD_OPTIONS);
1604 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1605 _("Identity Provider REST API initialized\n"));
1611 * Exit point from the plugin.
1613 * @param cls the plugin context (as returned by "init")
1614 * @return always NULL
1617 libgnunet_plugin_rest_identity_provider_done (void *cls)
1619 struct GNUNET_REST_Plugin *api = cls;
1620 struct Plugin *plugin = api->cls;
1623 GNUNET_free_non_null (allow_methods);
1625 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1626 "Identity Provider REST plugin is finished\n");
1630 /* end of plugin_rest_identity_provider.c */