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 [] =
166 * OIDC authorized identities and times hashmap
168 struct GNUNET_CONTAINER_MultiHashMap *OIDC_authorized_identities;
171 * The configuration handle
173 const struct GNUNET_CONFIGURATION_Handle *cfg;
176 * HTTP methods allows for this plugin
178 static char* allow_methods;
181 * @brief struct returned by the initialization function of the plugin
185 const struct GNUNET_CONFIGURATION_Handle *cfg;
196 struct EgoEntry *next;
201 struct EgoEntry *prev;
216 struct GNUNET_IDENTITY_Ego *ego;
225 struct EgoEntry *ego_head;
230 struct EgoEntry *ego_tail;
235 struct EgoEntry *ego_entry;
238 * Ptr to current ego private key
240 const struct GNUNET_CRYPTO_EcdsaPrivateKey *priv_key;
243 * The processing state
248 * Handle to Identity service.
250 struct GNUNET_IDENTITY_Handle *identity_handle;
255 struct GNUNET_REST_RequestHandle *rest_handle;
261 struct GNUNET_IDENTITY_Operation *op;
266 struct GNUNET_IDENTITY_PROVIDER_Handle *idp;
271 struct GNUNET_IDENTITY_PROVIDER_Operation *idp_op;
276 struct GNUNET_IDENTITY_PROVIDER_AttributeIterator *attr_it;
281 struct GNUNET_IDENTITY_PROVIDER_TicketIterator *ticket_it;
284 * Desired timeout for the lookup (default is no timeout).
286 struct GNUNET_TIME_Relative timeout;
289 * ID of a task associated with the resolution process.
291 struct GNUNET_SCHEDULER_Task *timeout_task;
294 * The plugin result processor
296 GNUNET_REST_ResultProcessor proc;
299 * The closure of the result processor
309 * Error response message
319 * Error response description
331 struct GNUNET_JSONAPI_Document *resp_object;
338 * Cleanup lookup handle
339 * @param handle Handle to clean up
342 cleanup_handle (struct RequestHandle *handle)
344 struct EgoEntry *ego_entry;
345 struct EgoEntry *ego_tmp;
346 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
348 if (NULL != handle->resp_object)
349 GNUNET_JSONAPI_document_delete (handle->resp_object);
350 if (NULL != handle->timeout_task)
351 GNUNET_SCHEDULER_cancel (handle->timeout_task);
352 if (NULL != handle->identity_handle)
353 GNUNET_IDENTITY_disconnect (handle->identity_handle);
354 if (NULL != handle->attr_it)
355 GNUNET_IDENTITY_PROVIDER_get_attributes_stop (handle->attr_it);
356 if (NULL != handle->ticket_it)
357 GNUNET_IDENTITY_PROVIDER_ticket_iteration_stop (handle->ticket_it);
358 if (NULL != handle->idp)
359 GNUNET_IDENTITY_PROVIDER_disconnect (handle->idp);
360 if (NULL != handle->url)
361 GNUNET_free (handle->url);
362 if (NULL != handle->emsg)
363 GNUNET_free (handle->emsg);
364 for (ego_entry = handle->ego_head;
368 ego_entry = ego_entry->next;
369 GNUNET_free (ego_tmp->identifier);
370 GNUNET_free (ego_tmp->keystring);
371 GNUNET_free (ego_tmp);
373 GNUNET_free (handle);
377 cleanup_handle_delayed (void *cls)
379 cleanup_handle (cls);
384 * Task run on error, sends error message. Cleans up everything.
386 * @param cls the `struct RequestHandle`
391 struct RequestHandle *handle = cls;
392 struct MHD_Response *resp;
395 GNUNET_asprintf (&json_error,
398 resp = GNUNET_REST_create_response (json_error);
399 handle->proc (handle->proc_cls, resp, handle->response_code);
400 cleanup_handle (handle);
401 GNUNET_free (json_error);
405 * Task run on error, sends error message. Cleans up everything.
407 * @param cls the `struct RequestHandle`
410 do_redirect_error (void *cls)
412 struct RequestHandle *handle = cls;
413 struct MHD_Response *resp;
415 //TODO handle->url is wrong
416 GNUNET_asprintf (&redirect,
417 "%s?error=%s&error_description=%s",
418 handle->eredirect, handle->emsg, handle->edesc );
419 resp = GNUNET_REST_create_response ("");
420 MHD_add_response_header (resp, "Location", redirect);
421 handle->proc (handle->proc_cls, resp, MHD_HTTP_FOUND);
422 cleanup_handle (handle);
423 GNUNET_free (redirect);
427 * Task run on timeout, sends error message. Cleans up everything.
429 * @param cls the `struct RequestHandle`
432 do_timeout (void *cls)
434 struct RequestHandle *handle = cls;
436 handle->timeout_task = NULL;
442 collect_error_cb (void *cls)
444 struct RequestHandle *handle = cls;
450 finished_cont (void *cls,
454 struct RequestHandle *handle = cls;
455 struct MHD_Response *resp;
457 resp = GNUNET_REST_create_response (emsg);
458 if (GNUNET_OK != success)
460 GNUNET_SCHEDULER_add_now (&do_error, handle);
463 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
464 GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
469 * Return attributes for identity
471 * @param cls the request handle
474 return_response (void *cls)
477 struct RequestHandle *handle = cls;
478 struct MHD_Response *resp;
480 GNUNET_JSONAPI_document_serialize (handle->resp_object, &result_str);
481 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Result %s\n", result_str);
482 resp = GNUNET_REST_create_response (result_str);
483 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
484 GNUNET_free (result_str);
485 cleanup_handle (handle);
490 collect_finished_cb (void *cls)
492 struct RequestHandle *handle = cls;
494 handle->attr_it = NULL;
495 handle->ticket_it = NULL;
496 GNUNET_SCHEDULER_add_now (&return_response, handle);
501 * Collect all attributes for an ego
505 ticket_collect (void *cls,
506 const struct GNUNET_IDENTITY_PROVIDER_Ticket *ticket)
508 struct GNUNET_JSONAPI_Resource *json_resource;
509 struct RequestHandle *handle = cls;
513 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding ticket\n");
514 tmp = GNUNET_STRINGS_data_to_string_alloc (&ticket->rnd,
516 json_resource = GNUNET_JSONAPI_resource_new (GNUNET_REST_JSONAPI_IDENTITY_TICKET,
519 GNUNET_JSONAPI_document_resource_add (handle->resp_object, json_resource);
521 tmp = GNUNET_STRINGS_data_to_string_alloc (&ticket->identity,
522 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));
523 value = json_string (tmp);
524 GNUNET_JSONAPI_resource_add_attr (json_resource,
529 tmp = GNUNET_STRINGS_data_to_string_alloc (&ticket->audience,
530 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));
531 value = json_string (tmp);
532 GNUNET_JSONAPI_resource_add_attr (json_resource,
537 tmp = GNUNET_STRINGS_data_to_string_alloc (&ticket->rnd,
539 value = json_string (tmp);
540 GNUNET_JSONAPI_resource_add_attr (json_resource,
545 GNUNET_IDENTITY_PROVIDER_ticket_iteration_next (handle->ticket_it);
551 * List tickets for identity request
553 * @param con_handle the connection handle
555 * @param cls the RequestHandle
558 list_tickets_cont (struct GNUNET_REST_RequestHandle *con_handle,
562 const struct GNUNET_CRYPTO_EcdsaPrivateKey *priv_key;
563 struct RequestHandle *handle = cls;
564 struct EgoEntry *ego_entry;
567 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Getting tickets for %s.\n",
569 if ( strlen (GNUNET_REST_API_NS_IDENTITY_TICKETS) >=
570 strlen (handle->url))
572 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "No identity given.\n");
573 GNUNET_SCHEDULER_add_now (&do_error, handle);
576 identity = handle->url + strlen (GNUNET_REST_API_NS_IDENTITY_TICKETS) + 1;
578 for (ego_entry = handle->ego_head;
580 ego_entry = ego_entry->next)
581 if (0 == strcmp (identity, ego_entry->identifier))
583 handle->resp_object = GNUNET_JSONAPI_document_new ();
585 if (NULL == ego_entry)
588 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Ego %s not found.\n",
590 GNUNET_SCHEDULER_add_now (&return_response, handle);
593 priv_key = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego);
594 handle->idp = GNUNET_IDENTITY_PROVIDER_connect (cfg);
595 handle->ticket_it = GNUNET_IDENTITY_PROVIDER_ticket_iteration_start (handle->idp,
601 &collect_finished_cb,
607 add_attribute_cont (struct GNUNET_REST_RequestHandle *con_handle,
611 const struct GNUNET_CRYPTO_EcdsaPrivateKey *identity_priv;
612 const char* identity;
613 const char* name_str;
614 const char* value_str;
616 struct RequestHandle *handle = cls;
617 struct EgoEntry *ego_entry;
618 struct MHD_Response *resp;
619 struct GNUNET_IDENTITY_ATTRIBUTE_Claim *attribute;
620 struct GNUNET_JSONAPI_Document *json_obj;
621 struct GNUNET_JSONAPI_Resource *json_res;
622 char term_data[handle->rest_handle->data_size+1];
626 struct GNUNET_JSON_Specification docspec[] = {
627 GNUNET_JSON_spec_jsonapi_document (&json_obj),
628 GNUNET_JSON_spec_end()
631 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding an attribute for %s.\n",
633 if ( strlen (GNUNET_REST_API_NS_IDENTITY_ATTRIBUTES) >=
634 strlen (handle->url))
636 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "No identity given.\n");
637 GNUNET_SCHEDULER_add_now (&do_error, handle);
640 identity = handle->url + strlen (GNUNET_REST_API_NS_IDENTITY_ATTRIBUTES) + 1;
642 for (ego_entry = handle->ego_head;
644 ego_entry = ego_entry->next)
645 if (0 == strcmp (identity, ego_entry->identifier))
648 if (NULL == ego_entry)
650 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
651 "Identity unknown (%s)\n", identity);
652 GNUNET_JSONAPI_document_delete (json_obj);
655 identity_priv = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego);
657 if (0 >= handle->rest_handle->data_size)
659 GNUNET_SCHEDULER_add_now (&do_error, handle);
663 term_data[handle->rest_handle->data_size] = '\0';
664 GNUNET_memcpy (term_data,
665 handle->rest_handle->data,
666 handle->rest_handle->data_size);
667 data_json = json_loads (term_data,
670 GNUNET_assert (GNUNET_OK ==
671 GNUNET_JSON_parse (data_json, docspec,
673 json_decref (data_json);
674 if (NULL == json_obj)
676 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
677 "Unable to parse JSONAPI Object from %s\n",
679 GNUNET_SCHEDULER_add_now (&do_error, handle);
682 if (1 != GNUNET_JSONAPI_document_resource_count (json_obj))
684 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
685 "Cannot create more than 1 resource! (Got %d)\n",
686 GNUNET_JSONAPI_document_resource_count (json_obj));
687 GNUNET_JSONAPI_document_delete (json_obj);
688 GNUNET_SCHEDULER_add_now (&do_error, handle);
691 json_res = GNUNET_JSONAPI_document_get_resource (json_obj, 0);
692 if (GNUNET_NO == GNUNET_JSONAPI_resource_check_type (json_res,
693 GNUNET_REST_JSONAPI_IDENTITY_ATTRIBUTE))
695 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
696 "Unsupported JSON data type\n");
697 GNUNET_JSONAPI_document_delete (json_obj);
698 resp = GNUNET_REST_create_response (NULL);
699 handle->proc (handle->proc_cls, resp, MHD_HTTP_CONFLICT);
700 cleanup_handle (handle);
703 name_str = GNUNET_JSONAPI_resource_get_id (json_res);
704 value_json = GNUNET_JSONAPI_resource_read_attr (json_res,
706 value_str = json_string_value (value_json);
707 attribute = GNUNET_IDENTITY_ATTRIBUTE_claim_new (name_str,
708 GNUNET_IDENTITY_ATTRIBUTE_TYPE_STRING,
710 strlen (value_str) + 1);
711 handle->idp = GNUNET_IDENTITY_PROVIDER_connect (cfg);
712 handle->idp_op = GNUNET_IDENTITY_PROVIDER_attribute_store (handle->idp,
717 GNUNET_free (attribute);
718 GNUNET_JSONAPI_document_delete (json_obj);
724 * Collect all attributes for an ego
728 attr_collect (void *cls,
729 const struct GNUNET_CRYPTO_EcdsaPublicKey *identity,
730 const struct GNUNET_IDENTITY_ATTRIBUTE_Claim *attr)
732 struct GNUNET_JSONAPI_Resource *json_resource;
733 struct RequestHandle *handle = cls;
736 if ((NULL == attr->name) || (NULL == attr->data))
738 GNUNET_IDENTITY_PROVIDER_get_attributes_next (handle->attr_it);
742 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding attribute: %s\n",
744 json_resource = GNUNET_JSONAPI_resource_new (GNUNET_REST_JSONAPI_IDENTITY_ATTRIBUTE,
746 GNUNET_JSONAPI_document_resource_add (handle->resp_object, json_resource);
748 value = json_string (attr->data);
749 GNUNET_JSONAPI_resource_add_attr (json_resource,
753 GNUNET_IDENTITY_PROVIDER_get_attributes_next (handle->attr_it);
759 * List attributes for identity request
761 * @param con_handle the connection handle
763 * @param cls the RequestHandle
766 list_attribute_cont (struct GNUNET_REST_RequestHandle *con_handle,
770 const struct GNUNET_CRYPTO_EcdsaPrivateKey *priv_key;
771 struct RequestHandle *handle = cls;
772 struct EgoEntry *ego_entry;
775 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Getting attributes for %s.\n",
777 if ( strlen (GNUNET_REST_API_NS_IDENTITY_ATTRIBUTES) >=
778 strlen (handle->url))
780 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "No identity given.\n");
781 GNUNET_SCHEDULER_add_now (&do_error, handle);
784 identity = handle->url + strlen (GNUNET_REST_API_NS_IDENTITY_ATTRIBUTES) + 1;
786 for (ego_entry = handle->ego_head;
788 ego_entry = ego_entry->next)
789 if (0 == strcmp (identity, ego_entry->identifier))
791 handle->resp_object = GNUNET_JSONAPI_document_new ();
794 if (NULL == ego_entry)
797 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Ego %s not found.\n",
799 GNUNET_SCHEDULER_add_now (&return_response, handle);
802 priv_key = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego);
803 handle->idp = GNUNET_IDENTITY_PROVIDER_connect (cfg);
804 handle->attr_it = GNUNET_IDENTITY_PROVIDER_get_attributes_start (handle->idp,
810 &collect_finished_cb,
816 revoke_ticket_cont (struct GNUNET_REST_RequestHandle *con_handle,
820 const struct GNUNET_CRYPTO_EcdsaPrivateKey *identity_priv;
821 const char* identity_str;
822 const char* audience_str;
825 struct RequestHandle *handle = cls;
826 struct EgoEntry *ego_entry;
827 struct MHD_Response *resp;
828 struct GNUNET_IDENTITY_PROVIDER_Ticket ticket;
829 struct GNUNET_JSONAPI_Document *json_obj;
830 struct GNUNET_JSONAPI_Resource *json_res;
831 struct GNUNET_CRYPTO_EcdsaPublicKey tmp_pk;
832 char term_data[handle->rest_handle->data_size+1];
834 json_t *identity_json;
835 json_t *audience_json;
838 struct GNUNET_JSON_Specification docspec[] = {
839 GNUNET_JSON_spec_jsonapi_document (&json_obj),
840 GNUNET_JSON_spec_end()
843 if (0 >= handle->rest_handle->data_size)
845 GNUNET_SCHEDULER_add_now (&do_error, handle);
849 term_data[handle->rest_handle->data_size] = '\0';
850 GNUNET_memcpy (term_data,
851 handle->rest_handle->data,
852 handle->rest_handle->data_size);
853 data_json = json_loads (term_data,
856 GNUNET_assert (GNUNET_OK ==
857 GNUNET_JSON_parse (data_json, docspec,
859 json_decref (data_json);
860 if (NULL == json_obj)
862 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
863 "Unable to parse JSONAPI Object from %s\n",
865 GNUNET_SCHEDULER_add_now (&do_error, handle);
868 if (1 != GNUNET_JSONAPI_document_resource_count (json_obj))
870 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
871 "Cannot create more than 1 resource! (Got %d)\n",
872 GNUNET_JSONAPI_document_resource_count (json_obj));
873 GNUNET_JSONAPI_document_delete (json_obj);
874 GNUNET_SCHEDULER_add_now (&do_error, handle);
877 json_res = GNUNET_JSONAPI_document_get_resource (json_obj, 0);
878 if (GNUNET_NO == GNUNET_JSONAPI_resource_check_type (json_res,
879 GNUNET_REST_JSONAPI_IDENTITY_TICKET))
881 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
882 "Unsupported JSON data type\n");
883 GNUNET_JSONAPI_document_delete (json_obj);
884 resp = GNUNET_REST_create_response (NULL);
885 handle->proc (handle->proc_cls, resp, MHD_HTTP_CONFLICT);
886 cleanup_handle (handle);
889 rnd_json = GNUNET_JSONAPI_resource_read_attr (json_res,
891 identity_json = GNUNET_JSONAPI_resource_read_attr (json_res,
893 audience_json = GNUNET_JSONAPI_resource_read_attr (json_res,
895 rnd_str = json_string_value (rnd_json);
896 identity_str = json_string_value (identity_json);
897 audience_str = json_string_value (audience_json);
899 GNUNET_STRINGS_string_to_data (rnd_str,
903 // GNUNET_STRINGS_string_to_data (identity_str,
904 // strlen (identity_str),
905 // &ticket.identity,type filter text
906 // sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));
907 GNUNET_STRINGS_string_to_data (audience_str,
908 strlen (audience_str),
910 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));
912 for (ego_entry = handle->ego_head;
914 ego_entry = ego_entry->next)
916 GNUNET_IDENTITY_ego_get_public_key (ego_entry->ego,
918 if (0 == memcmp (&ticket.identity,
920 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)))
923 if (NULL == ego_entry)
925 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
926 "Identity unknown (%s)\n", identity_str);
927 GNUNET_JSONAPI_document_delete (json_obj);
930 identity_priv = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego);
932 handle->idp = GNUNET_IDENTITY_PROVIDER_connect (cfg);
933 handle->idp_op = GNUNET_IDENTITY_PROVIDER_ticket_revoke (handle->idp,
938 GNUNET_JSONAPI_document_delete (json_obj);
942 consume_cont (void *cls,
943 const struct GNUNET_CRYPTO_EcdsaPublicKey *identity,
944 const struct GNUNET_IDENTITY_ATTRIBUTE_Claim *attr)
946 struct RequestHandle *handle = cls;
947 struct GNUNET_JSONAPI_Resource *json_resource;
950 if (NULL == identity)
952 GNUNET_SCHEDULER_add_now (&return_response, handle);
956 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding attribute: %s\n",
958 json_resource = GNUNET_JSONAPI_resource_new (GNUNET_REST_JSONAPI_IDENTITY_ATTRIBUTE,
960 GNUNET_JSONAPI_document_resource_add (handle->resp_object, json_resource);
962 value = json_string (attr->data);
963 GNUNET_JSONAPI_resource_add_attr (json_resource,
970 consume_ticket_cont (struct GNUNET_REST_RequestHandle *con_handle,
974 const struct GNUNET_CRYPTO_EcdsaPrivateKey *identity_priv;
975 const char* identity_str;
976 const char* audience_str;
979 struct RequestHandle *handle = cls;
980 struct EgoEntry *ego_entry;
981 struct MHD_Response *resp;
982 struct GNUNET_IDENTITY_PROVIDER_Ticket ticket;
983 struct GNUNET_JSONAPI_Document *json_obj;
984 struct GNUNET_JSONAPI_Resource *json_res;
985 struct GNUNET_CRYPTO_EcdsaPublicKey tmp_pk;
986 char term_data[handle->rest_handle->data_size+1];
988 json_t *identity_json;
989 json_t *audience_json;
992 struct GNUNET_JSON_Specification docspec[] = {
993 GNUNET_JSON_spec_jsonapi_document (&json_obj),
994 GNUNET_JSON_spec_end()
997 if (0 >= handle->rest_handle->data_size)
999 GNUNET_SCHEDULER_add_now (&do_error, handle);
1003 term_data[handle->rest_handle->data_size] = '\0';
1004 GNUNET_memcpy (term_data,
1005 handle->rest_handle->data,
1006 handle->rest_handle->data_size);
1007 data_json = json_loads (term_data,
1010 GNUNET_assert (GNUNET_OK ==
1011 GNUNET_JSON_parse (data_json, docspec,
1013 json_decref (data_json);
1014 if (NULL == json_obj)
1016 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1017 "Unable to parse JSONAPI Object from %s\n",
1019 GNUNET_SCHEDULER_add_now (&do_error, handle);
1022 if (1 != GNUNET_JSONAPI_document_resource_count (json_obj))
1024 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1025 "Cannot create more than 1 resource! (Got %d)\n",
1026 GNUNET_JSONAPI_document_resource_count (json_obj));
1027 GNUNET_JSONAPI_document_delete (json_obj);
1028 GNUNET_SCHEDULER_add_now (&do_error, handle);
1031 json_res = GNUNET_JSONAPI_document_get_resource (json_obj, 0);
1032 if (GNUNET_NO == GNUNET_JSONAPI_resource_check_type (json_res,
1033 GNUNET_REST_JSONAPI_IDENTITY_TICKET))
1035 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1036 "Unsupported JSON data type\n");
1037 GNUNET_JSONAPI_document_delete (json_obj);
1038 resp = GNUNET_REST_create_response (NULL);
1039 handle->proc (handle->proc_cls, resp, MHD_HTTP_CONFLICT);
1040 cleanup_handle (handle);
1043 rnd_json = GNUNET_JSONAPI_resource_read_attr (json_res,
1045 identity_json = GNUNET_JSONAPI_resource_read_attr (json_res,
1047 audience_json = GNUNET_JSONAPI_resource_read_attr (json_res,
1049 rnd_str = json_string_value (rnd_json);
1050 identity_str = json_string_value (identity_json);
1051 audience_str = json_string_value (audience_json);
1053 GNUNET_STRINGS_string_to_data (rnd_str,
1057 GNUNET_STRINGS_string_to_data (identity_str,
1058 strlen (identity_str),
1060 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));
1061 GNUNET_STRINGS_string_to_data (audience_str,
1062 strlen (audience_str),
1064 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));
1066 for (ego_entry = handle->ego_head;
1068 ego_entry = ego_entry->next)
1070 GNUNET_IDENTITY_ego_get_public_key (ego_entry->ego,
1072 if (0 == memcmp (&ticket.audience,
1074 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)))
1077 if (NULL == ego_entry)
1079 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1080 "Identity unknown (%s)\n", identity_str);
1081 GNUNET_JSONAPI_document_delete (json_obj);
1084 identity_priv = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego);
1085 handle->resp_object = GNUNET_JSONAPI_document_new ();
1086 handle->idp = GNUNET_IDENTITY_PROVIDER_connect (cfg);
1087 handle->idp_op = GNUNET_IDENTITY_PROVIDER_ticket_consume (handle->idp,
1092 GNUNET_JSONAPI_document_delete (json_obj);
1098 * Respond to OPTIONS request
1100 * @param con_handle the connection handle
1101 * @param url the url
1102 * @param cls the RequestHandle
1105 options_cont (struct GNUNET_REST_RequestHandle *con_handle,
1109 struct MHD_Response *resp;
1110 struct RequestHandle *handle = cls;
1112 //For now, independent of path return all options
1113 resp = GNUNET_REST_create_response (NULL);
1114 MHD_add_response_header (resp,
1115 "Access-Control-Allow-Methods",
1117 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
1118 cleanup_handle (handle);
1123 * Respond to OPTIONS request
1125 * @param con_handle the connection handle
1126 * @param url the url
1127 * @param cls the RequestHandle
1130 authorize_cont (struct GNUNET_REST_RequestHandle *con_handle,
1134 struct MHD_Response *resp;
1135 struct RequestHandle *handle = cls;
1136 char *response_type, *client_id, *scope, *redirect_uri, *state = 0,
1138 struct timeval now, login_time;
1139 OIDC_authorized_identities = GNUNET_CONTAINER_multihashmap_create( 10, GNUNET_NO );
1140 char *login_base_url, *new_redirect;
1141 struct GNUNET_HashCode cache_key;
1143 //TODO clean up method
1145 /** The Authorization Server MUST validate all the OAuth 2.0 parameters
1146 * according to the OAuth 2.0 specification.
1149 * If the sub (subject) Claim is requested with a specific value for the
1150 * ID Token, the Authorization Server MUST only send a positive response
1151 * if the End-User identified by that sub value has an active session with
1152 * the Authorization Server or has been Authenticated as a result of the
1153 * request. The Authorization Server MUST NOT reply with an ID Token or
1154 * Access Token for a different user, even if they have an active session
1155 * with the Authorization Server. Such a request can be made either using
1156 * an id_token_hint parameter or by requesting a specific Claim Value as
1157 * described in Section 5.5.1, if the claims parameter is supported by
1158 * the implementation.
1163 // REQUIRED value: client_id
1164 GNUNET_CRYPTO_hash (OIDC_CLIENT_ID_KEY, strlen (OIDC_CLIENT_ID_KEY),
1166 if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map,
1169 handle->emsg=GNUNET_strdup("invalid_request");
1170 handle->edesc=GNUNET_strdup("Missing parameter: client_id");
1171 GNUNET_SCHEDULER_add_now (&do_error, handle);
1174 client_id = GNUNET_CONTAINER_multihashmap_get(handle->rest_handle->url_param_map,
1177 // Checks if client_id is valid:
1178 // TODO change check (lookup trusted public_key?)
1179 // if( strcmp( client_id, "localhost" ) != 0 )
1181 // handle->emsg=GNUNET_strdup("unauthorized_client");
1182 // handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1183 // GNUNET_SCHEDULER_add_now (&do_error, handle);
1187 // REQUIRED value: redirect_uri
1188 GNUNET_CRYPTO_hash (OIDC_REDIRECT_URI_KEY, strlen (OIDC_REDIRECT_URI_KEY),
1190 if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map,
1193 handle->emsg=GNUNET_strdup("invalid_request");
1194 handle->edesc=GNUNET_strdup("Missing parameter: redirect_uri");
1195 GNUNET_SCHEDULER_add_now (&do_error, handle);
1198 redirect_uri = GNUNET_CONTAINER_multihashmap_get(handle->rest_handle->url_param_map,
1201 // Checks if redirect_uri is valid:
1202 // TODO change check (check client_id->public key == address)
1203 // if( strcmp( redirect_uri, "https://localhost:8000" ) != 0 )
1205 // handle->emsg=GNUNET_strdup("invalid_request");
1206 // handle->edesc=GNUNET_strdup("Invalid or mismatching redirect_uri");
1207 // GNUNET_SCHEDULER_add_now (&do_error, handle);
1210 handle->eredirect = GNUNET_strdup(redirect_uri);
1212 // REQUIRED value: response_type
1213 GNUNET_CRYPTO_hash (OIDC_RESPONSE_TYPE_KEY, strlen (OIDC_RESPONSE_TYPE_KEY),
1215 if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map,
1218 handle->emsg=GNUNET_strdup("invalid_request");
1219 handle->edesc=GNUNET_strdup("Missing parameter: response_type");
1220 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1223 response_type = GNUNET_CONTAINER_multihashmap_get(handle->rest_handle->url_param_map,
1226 // REQUIRED value: scope
1227 GNUNET_CRYPTO_hash (OIDC_SCOPE_KEY, strlen (OIDC_SCOPE_KEY), &cache_key);
1228 if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map,
1231 handle->emsg=GNUNET_strdup("invalid_request");
1232 handle->edesc=GNUNET_strdup("Missing parameter: scope");
1233 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1236 scope = GNUNET_CONTAINER_multihashmap_get(handle->rest_handle->url_param_map,
1239 //RECOMMENDED value: state
1240 GNUNET_CRYPTO_hash (OIDC_STATE_KEY, strlen (OIDC_STATE_KEY), &cache_key);
1241 if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map,
1244 state = GNUNET_CONTAINER_multihashmap_get(handle->rest_handle->url_param_map,
1248 //OPTIONAL value: nonce
1249 GNUNET_CRYPTO_hash (OIDC_NONCE_KEY, strlen (OIDC_NONCE_KEY), &cache_key);
1250 if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map,
1253 nonce = GNUNET_CONTAINER_multihashmap_get(handle->rest_handle->url_param_map,
1257 int number_of_ignored_parameter = sizeof(OIDC_ignored_parameter_array) / sizeof(char *);
1259 for( iterator = 0; iterator < number_of_ignored_parameter; iterator++ )
1261 GNUNET_CRYPTO_hash (OIDC_ignored_parameter_array[iterator],
1262 strlen(OIDC_ignored_parameter_array[iterator]),
1264 if(GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains(handle->rest_handle->url_param_map,
1267 handle->emsg=GNUNET_strdup("access_denied");
1268 //TODO rewrite error description
1269 handle->edesc=GNUNET_strdup("Server will not handle parameter");
1270 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1275 // Checks if response_type is 'code'
1276 if( strcmp( response_type, OIDC_EXPECTED_AUTHORIZATION_RESPONSE_TYPE ) != 0 )
1278 handle->emsg=GNUNET_strdup("unsupported_response_type");
1279 handle->edesc=GNUNET_strdup("The authorization server does not support "
1280 "obtaining this authorization code.");
1281 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1284 // Checks if scope contains 'openid'
1285 if( strstr( scope, OIDC_EXPECTED_AUTHORIZATION_SCOPE ) == NULL )
1287 handle->emsg=GNUNET_strdup("invalid_scope");
1288 handle->edesc=GNUNET_strdup("The requested scope is invalid, unknown, or "
1290 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1295 //TODO check other values and use them accordingly
1300 //if header-authorization == ID
1301 //if ID is still logged
1302 // ego get Public Key of Identity
1303 // return token with public key?
1306 GNUNET_CRYPTO_hash (OIDC_AUTHORIZATION_HEADER_KEY,
1307 strlen (OIDC_AUTHORIZATION_HEADER_KEY),
1309 //No Authorization Parameter -> redirect to login
1310 if(GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains(con_handle->header_param_map,
1314 == GNUNET_CONFIGURATION_get_value_string (cfg, "identity-rest-plugin",
1315 "address", &login_base_url) )
1317 GNUNET_asprintf (&new_redirect, "%s?%s=%s&%s=%s&%s=%s&%s=%s&%s=%s&%s=%s",
1319 OIDC_RESPONSE_TYPE_KEY,
1323 OIDC_REDIRECT_URI_KEY,
1328 (state) ? state : "",
1330 (nonce) ? nonce : "");
1331 resp = GNUNET_REST_create_response ("");
1332 MHD_add_response_header (resp, "Location", new_redirect);
1336 handle->emsg = GNUNET_strdup("No server configuration");
1337 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1338 GNUNET_SCHEDULER_add_now (&do_error, handle);
1341 handle->proc (handle->proc_cls, resp, MHD_HTTP_FOUND);
1342 cleanup_handle (handle);
1343 GNUNET_free(new_redirect);
1348 char* identity = GNUNET_CONTAINER_multihashmap_get ( con_handle->header_param_map,
1350 GNUNET_CRYPTO_hash (identity, strlen (identity), &cache_key);
1351 if(GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains(OIDC_authorized_identities,
1354 login_time = *(struct timeval *)GNUNET_CONTAINER_multihashmap_get(OIDC_authorized_identities,
1356 gettimeofday(&now, NULL);
1357 //After 30 minutes redirect to login
1358 if( now.tv_sec - login_time.tv_sec >= 1800)
1360 //TODO remove redundancy [redirect to login]
1362 == GNUNET_CONFIGURATION_get_value_string (cfg, "identity-rest-plugin",
1363 "address", &login_base_url) )
1365 GNUNET_asprintf (&new_redirect, "%s?%s=%s&%s=%s&%s=%s&%s=%s&%s=%s&%s=%s",
1367 OIDC_RESPONSE_TYPE_KEY,
1371 OIDC_REDIRECT_URI_KEY,
1376 (state) ? state : "",
1378 (nonce) ? nonce : "");
1379 resp = GNUNET_REST_create_response ("");
1380 MHD_add_response_header (resp, "Location", new_redirect);
1384 handle->emsg = GNUNET_strdup("No server configuration");
1385 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1386 GNUNET_SCHEDULER_add_now (&do_error, handle);
1389 handle->proc (handle->proc_cls, resp, MHD_HTTP_FOUND);
1390 cleanup_handle (handle);
1391 GNUNET_free(new_redirect);
1397 gettimeofday( &now, NULL );
1398 GNUNET_CONTAINER_multihashmap_put( OIDC_authorized_identities, &cache_key, &now,
1399 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY);
1401 resp = GNUNET_REST_create_response ("");
1402 // MHD_add_response_header (resp, "Access-Control-Allow-Origin", "*");
1403 MHD_add_response_header (resp, "Location", redirect_uri);
1404 handle->proc (handle->proc_cls, resp, MHD_HTTP_FOUND);
1405 cleanup_handle (handle);
1412 * Respond to LOGIN request
1414 * @param con_handle the connection handle
1415 * @param url the url
1416 * @param cls the RequestHandle
1419 login_cont (struct GNUNET_REST_RequestHandle *con_handle,
1423 struct MHD_Response *resp = GNUNET_REST_create_response ("");
1424 struct RequestHandle *handle = cls;
1429 root = json_loads( handle->rest_handle->data, 0, &error );
1430 identity = json_object_get(root, "identity");
1431 if(json_is_string(identity))
1433 GNUNET_asprintf(&cookie,"Identity=%s",json_string_value(identity));
1434 MHD_add_response_header (resp, "Set-Cookie", cookie);
1435 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
1439 handle->proc (handle->proc_cls, resp, MHD_HTTP_BAD_REQUEST);
1442 cleanup_handle (handle);
1443 GNUNET_free(cookie);
1448 * Handle rest request
1450 * @param handle the request handle
1453 init_cont (struct RequestHandle *handle)
1455 struct GNUNET_REST_RequestHandlerError err;
1456 static const struct GNUNET_REST_RequestHandler handlers[] = {
1457 {MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_IDENTITY_ATTRIBUTES, &list_attribute_cont},
1458 {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_IDENTITY_ATTRIBUTES, &add_attribute_cont},
1459 {MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_IDENTITY_TICKETS, &list_tickets_cont},
1460 {MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_AUTHORIZE, &authorize_cont},
1461 {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_LOGIN, &login_cont},
1462 {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_AUTHORIZE, &authorize_cont},
1463 {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_IDENTITY_REVOKE, &revoke_ticket_cont},
1464 {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_IDENTITY_CONSUME, &consume_ticket_cont},
1465 {MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_IDENTITY_PROVIDER,
1467 GNUNET_REST_HANDLER_END
1470 if (GNUNET_NO == GNUNET_REST_handle_request (handle->rest_handle,
1475 handle->response_code = err.error_code;
1476 GNUNET_SCHEDULER_add_now (&do_error, handle);
1481 * If listing is enabled, prints information about the egos.
1483 * This function is initially called for all egos and then again
1484 * whenever a ego's identifier changes or if it is deleted. At the
1485 * end of the initial pass over all egos, the function is once called
1486 * with 'NULL' for 'ego'. That does NOT mean that the callback won't
1487 * be invoked in the future or that there was an error.
1489 * When used with 'GNUNET_IDENTITY_create' or 'GNUNET_IDENTITY_get',
1490 * this function is only called ONCE, and 'NULL' being passed in
1491 * 'ego' does indicate an error (i.e. name is taken or no default
1492 * value is known). If 'ego' is non-NULL and if '*ctx'
1493 * is set in those callbacks, the value WILL be passed to a subsequent
1494 * call to the identity callback of 'GNUNET_IDENTITY_connect' (if
1495 * that one was not NULL).
1497 * When an identity is renamed, this function is called with the
1498 * (known) ego but the NEW identifier.
1500 * When an identity is deleted, this function is called with the
1501 * (known) ego and "NULL" for the 'identifier'. In this case,
1502 * the 'ego' is henceforth invalid (and the 'ctx' should also be
1505 * @param cls closure
1506 * @param ego ego handle
1507 * @param ctx context for application to store data for this ego
1508 * (during the lifetime of this process, initially NULL)
1509 * @param identifier identifier assigned by the user for this ego,
1510 * NULL if the user just deleted the ego and it
1511 * must thus no longer be used
1514 list_ego (void *cls,
1515 struct GNUNET_IDENTITY_Ego *ego,
1517 const char *identifier)
1519 struct RequestHandle *handle = cls;
1520 struct EgoEntry *ego_entry;
1521 struct GNUNET_CRYPTO_EcdsaPublicKey pk;
1523 if ((NULL == ego) && (ID_REST_STATE_INIT == handle->state))
1525 handle->state = ID_REST_STATE_POST_INIT;
1529 if (ID_REST_STATE_INIT == handle->state) {
1530 ego_entry = GNUNET_new (struct EgoEntry);
1531 GNUNET_IDENTITY_ego_get_public_key (ego, &pk);
1532 ego_entry->keystring =
1533 GNUNET_CRYPTO_ecdsa_public_key_to_string (&pk);
1534 ego_entry->ego = ego;
1535 ego_entry->identifier = GNUNET_strdup (identifier);
1536 GNUNET_CONTAINER_DLL_insert_tail(handle->ego_head,handle->ego_tail, ego_entry);
1542 * Function processing the REST call
1544 * @param method HTTP method
1545 * @param url URL of the HTTP request
1546 * @param data body of the HTTP request (optional)
1547 * @param data_size length of the body
1548 * @param proc callback function for the result
1549 * @param proc_cls closure for callback function
1550 * @return GNUNET_OK if request accepted
1553 rest_identity_process_request(struct GNUNET_REST_RequestHandle *rest_handle,
1554 GNUNET_REST_ResultProcessor proc,
1557 struct RequestHandle *handle = GNUNET_new (struct RequestHandle);
1559 handle->timeout = GNUNET_TIME_UNIT_FOREVER_REL;
1560 handle->proc_cls = proc_cls;
1561 handle->proc = proc;
1562 handle->state = ID_REST_STATE_INIT;
1563 handle->rest_handle = rest_handle;
1565 handle->url = GNUNET_strdup (rest_handle->url);
1566 if (handle->url[strlen (handle->url)-1] == '/')
1567 handle->url[strlen (handle->url)-1] = '\0';
1568 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1570 handle->identity_handle = GNUNET_IDENTITY_connect (cfg,
1573 handle->timeout_task =
1574 GNUNET_SCHEDULER_add_delayed (handle->timeout,
1577 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1582 * Entry point for the plugin.
1584 * @param cls Config info
1585 * @return NULL on error, otherwise the plugin context
1588 libgnunet_plugin_rest_identity_provider_init (void *cls)
1590 static struct Plugin plugin;
1591 struct GNUNET_REST_Plugin *api;
1594 if (NULL != plugin.cfg)
1595 return NULL; /* can only initialize once! */
1596 memset (&plugin, 0, sizeof (struct Plugin));
1598 api = GNUNET_new (struct GNUNET_REST_Plugin);
1600 api->name = GNUNET_REST_API_NS_IDENTITY_PROVIDER;
1601 api->process_request = &rest_identity_process_request;
1602 GNUNET_asprintf (&allow_methods,
1603 "%s, %s, %s, %s, %s",
1604 MHD_HTTP_METHOD_GET,
1605 MHD_HTTP_METHOD_POST,
1606 MHD_HTTP_METHOD_PUT,
1607 MHD_HTTP_METHOD_DELETE,
1608 MHD_HTTP_METHOD_OPTIONS);
1610 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1611 _("Identity Provider REST API initialized\n"));
1617 * Exit point from the plugin.
1619 * @param cls the plugin context (as returned by "init")
1620 * @return always NULL
1623 libgnunet_plugin_rest_identity_provider_done (void *cls)
1625 struct GNUNET_REST_Plugin *api = cls;
1626 struct Plugin *plugin = api->cls;
1629 GNUNET_free_non_null (allow_methods);
1631 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1632 "Identity Provider REST plugin is finished\n");
1636 /* end of plugin_rest_identity_provider.c */