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 cookie header key
137 #define OIDC_COOKIE_HEADER_KEY "Cookie"
140 * OIDC cookie header information key
142 #define OIDC_COOKIE_HEADER_INFORMATION_KEY "Identity="
145 * OIDC expected response_type while authorizing
147 #define OIDC_EXPECTED_AUTHORIZATION_RESPONSE_TYPE "code"
150 * OIDC expected scope part while authorizing
152 #define OIDC_EXPECTED_AUTHORIZATION_SCOPE "openid"
154 * OIDC ignored parameter array
156 char* OIDC_ignored_parameter_array [] =
169 * OIDC authorized identities and times hashmap
171 struct GNUNET_CONTAINER_MultiHashMap *OIDC_authorized_identities;
174 * The configuration handle
176 const struct GNUNET_CONFIGURATION_Handle *cfg;
179 * HTTP methods allows for this plugin
181 static char* allow_methods;
184 * @brief struct returned by the initialization function of the plugin
188 const struct GNUNET_CONFIGURATION_Handle *cfg;
199 struct EgoEntry *next;
204 struct EgoEntry *prev;
219 struct GNUNET_IDENTITY_Ego *ego;
228 struct EgoEntry *ego_head;
233 struct EgoEntry *ego_tail;
238 struct EgoEntry *ego_entry;
241 * Ptr to current ego private key
243 const struct GNUNET_CRYPTO_EcdsaPrivateKey *priv_key;
246 * The processing state
251 * Handle to Identity service.
253 struct GNUNET_IDENTITY_Handle *identity_handle;
258 struct GNUNET_REST_RequestHandle *rest_handle;
263 struct GNUNET_NAMESTORE_Handle *namestore_handle;
268 struct GNUNET_IDENTITY_Operation *op;
273 struct GNUNET_IDENTITY_PROVIDER_Handle *idp;
278 struct GNUNET_IDENTITY_PROVIDER_Operation *idp_op;
283 struct GNUNET_IDENTITY_PROVIDER_AttributeIterator *attr_it;
288 struct GNUNET_IDENTITY_PROVIDER_TicketIterator *ticket_it;
291 * Desired timeout for the lookup (default is no timeout).
293 struct GNUNET_TIME_Relative timeout;
296 * ID of a task associated with the resolution process.
298 struct GNUNET_SCHEDULER_Task *timeout_task;
301 * The plugin result processor
303 GNUNET_REST_ResultProcessor proc;
306 * The closure of the result processor
316 * Error response message
326 * Error response description
338 struct GNUNET_JSONAPI_Document *resp_object;
345 * Cleanup lookup handle
346 * @param handle Handle to clean up
349 cleanup_handle (struct RequestHandle *handle)
351 struct EgoEntry *ego_entry;
352 struct EgoEntry *ego_tmp;
353 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
355 if (NULL != handle->resp_object)
356 GNUNET_JSONAPI_document_delete (handle->resp_object);
357 if (NULL != handle->timeout_task)
358 GNUNET_SCHEDULER_cancel (handle->timeout_task);
359 if (NULL != handle->identity_handle)
360 GNUNET_IDENTITY_disconnect (handle->identity_handle);
361 if (NULL != handle->attr_it)
362 GNUNET_IDENTITY_PROVIDER_get_attributes_stop (handle->attr_it);
363 if (NULL != handle->ticket_it)
364 GNUNET_IDENTITY_PROVIDER_ticket_iteration_stop (handle->ticket_it);
365 if (NULL != handle->idp)
366 GNUNET_IDENTITY_PROVIDER_disconnect (handle->idp);
367 if (NULL != handle->url)
368 GNUNET_free (handle->url);
369 if (NULL != handle->emsg)
370 GNUNET_free (handle->emsg);
371 if (NULL != handle->edesc)
372 GNUNET_free (handle->edesc);
373 if (NULL != handle->eredirect)
374 GNUNET_free (handle->eredirect);
375 for (ego_entry = handle->ego_head;
379 ego_entry = ego_entry->next;
380 GNUNET_free (ego_tmp->identifier);
381 GNUNET_free (ego_tmp->keystring);
382 GNUNET_free (ego_tmp);
384 GNUNET_free (handle);
388 cleanup_handle_delayed (void *cls)
390 cleanup_handle (cls);
395 * Task run on error, sends error message. Cleans up everything.
397 * @param cls the `struct RequestHandle`
402 struct RequestHandle *handle = cls;
403 struct MHD_Response *resp;
406 GNUNET_asprintf (&json_error,
409 resp = GNUNET_REST_create_response (json_error);
410 handle->proc (handle->proc_cls, resp, handle->response_code);
411 cleanup_handle (handle);
412 GNUNET_free (json_error);
416 * Task run on error, sends error message. Cleans up everything.
418 * @param cls the `struct RequestHandle`
421 do_redirect_error (void *cls)
423 struct RequestHandle *handle = cls;
424 struct MHD_Response *resp;
426 //TODO handle->url is wrong
427 GNUNET_asprintf (&redirect,
428 "%s?error=%s&error_description=%s",
429 handle->eredirect, handle->emsg, handle->edesc );
430 resp = GNUNET_REST_create_response ("");
431 MHD_add_response_header (resp, "Location", redirect);
432 handle->proc (handle->proc_cls, resp, MHD_HTTP_FOUND);
433 cleanup_handle (handle);
434 GNUNET_free (redirect);
438 * Task run on timeout, sends error message. Cleans up everything.
440 * @param cls the `struct RequestHandle`
443 do_timeout (void *cls)
445 struct RequestHandle *handle = cls;
447 handle->timeout_task = NULL;
453 collect_error_cb (void *cls)
455 struct RequestHandle *handle = cls;
461 finished_cont (void *cls,
465 struct RequestHandle *handle = cls;
466 struct MHD_Response *resp;
468 resp = GNUNET_REST_create_response (emsg);
469 if (GNUNET_OK != success)
471 GNUNET_SCHEDULER_add_now (&do_error, handle);
474 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
475 GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
480 * Return attributes for identity
482 * @param cls the request handle
485 return_response (void *cls)
488 struct RequestHandle *handle = cls;
489 struct MHD_Response *resp;
491 GNUNET_JSONAPI_document_serialize (handle->resp_object, &result_str);
492 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Result %s\n", result_str);
493 resp = GNUNET_REST_create_response (result_str);
494 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
495 GNUNET_free (result_str);
496 cleanup_handle (handle);
501 collect_finished_cb (void *cls)
503 struct RequestHandle *handle = cls;
505 handle->attr_it = NULL;
506 handle->ticket_it = NULL;
507 GNUNET_SCHEDULER_add_now (&return_response, handle);
512 * Collect all attributes for an ego
516 ticket_collect (void *cls,
517 const struct GNUNET_IDENTITY_PROVIDER_Ticket *ticket)
519 struct GNUNET_JSONAPI_Resource *json_resource;
520 struct RequestHandle *handle = cls;
524 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding ticket\n");
525 tmp = GNUNET_STRINGS_data_to_string_alloc (&ticket->rnd,
527 json_resource = GNUNET_JSONAPI_resource_new (GNUNET_REST_JSONAPI_IDENTITY_TICKET,
530 GNUNET_JSONAPI_document_resource_add (handle->resp_object, json_resource);
532 tmp = GNUNET_STRINGS_data_to_string_alloc (&ticket->identity,
533 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));
534 value = json_string (tmp);
535 GNUNET_JSONAPI_resource_add_attr (json_resource,
540 tmp = GNUNET_STRINGS_data_to_string_alloc (&ticket->audience,
541 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));
542 value = json_string (tmp);
543 GNUNET_JSONAPI_resource_add_attr (json_resource,
548 tmp = GNUNET_STRINGS_data_to_string_alloc (&ticket->rnd,
550 value = json_string (tmp);
551 GNUNET_JSONAPI_resource_add_attr (json_resource,
556 GNUNET_IDENTITY_PROVIDER_ticket_iteration_next (handle->ticket_it);
562 * List tickets for identity request
564 * @param con_handle the connection handle
566 * @param cls the RequestHandle
569 list_tickets_cont (struct GNUNET_REST_RequestHandle *con_handle,
573 const struct GNUNET_CRYPTO_EcdsaPrivateKey *priv_key;
574 struct RequestHandle *handle = cls;
575 struct EgoEntry *ego_entry;
578 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Getting tickets for %s.\n",
580 if ( strlen (GNUNET_REST_API_NS_IDENTITY_TICKETS) >=
581 strlen (handle->url))
583 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "No identity given.\n");
584 GNUNET_SCHEDULER_add_now (&do_error, handle);
587 identity = handle->url + strlen (GNUNET_REST_API_NS_IDENTITY_TICKETS) + 1;
589 for (ego_entry = handle->ego_head;
591 ego_entry = ego_entry->next)
592 if (0 == strcmp (identity, ego_entry->identifier))
594 handle->resp_object = GNUNET_JSONAPI_document_new ();
596 if (NULL == ego_entry)
599 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Ego %s not found.\n",
601 GNUNET_SCHEDULER_add_now (&return_response, handle);
604 priv_key = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego);
605 handle->idp = GNUNET_IDENTITY_PROVIDER_connect (cfg);
606 handle->ticket_it = GNUNET_IDENTITY_PROVIDER_ticket_iteration_start (handle->idp,
612 &collect_finished_cb,
618 add_attribute_cont (struct GNUNET_REST_RequestHandle *con_handle,
622 const struct GNUNET_CRYPTO_EcdsaPrivateKey *identity_priv;
623 const char* identity;
624 const char* name_str;
625 const char* value_str;
627 struct RequestHandle *handle = cls;
628 struct EgoEntry *ego_entry;
629 struct MHD_Response *resp;
630 struct GNUNET_IDENTITY_ATTRIBUTE_Claim *attribute;
631 struct GNUNET_JSONAPI_Document *json_obj;
632 struct GNUNET_JSONAPI_Resource *json_res;
633 char term_data[handle->rest_handle->data_size+1];
637 struct GNUNET_JSON_Specification docspec[] = {
638 GNUNET_JSON_spec_jsonapi_document (&json_obj),
639 GNUNET_JSON_spec_end()
642 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding an attribute for %s.\n",
644 if ( strlen (GNUNET_REST_API_NS_IDENTITY_ATTRIBUTES) >=
645 strlen (handle->url))
647 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "No identity given.\n");
648 GNUNET_SCHEDULER_add_now (&do_error, handle);
651 identity = handle->url + strlen (GNUNET_REST_API_NS_IDENTITY_ATTRIBUTES) + 1;
653 for (ego_entry = handle->ego_head;
655 ego_entry = ego_entry->next)
656 if (0 == strcmp (identity, ego_entry->identifier))
659 if (NULL == ego_entry)
661 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
662 "Identity unknown (%s)\n", identity);
663 GNUNET_JSONAPI_document_delete (json_obj);
666 identity_priv = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego);
668 if (0 >= handle->rest_handle->data_size)
670 GNUNET_SCHEDULER_add_now (&do_error, handle);
674 term_data[handle->rest_handle->data_size] = '\0';
675 GNUNET_memcpy (term_data,
676 handle->rest_handle->data,
677 handle->rest_handle->data_size);
678 data_json = json_loads (term_data,
681 GNUNET_assert (GNUNET_OK ==
682 GNUNET_JSON_parse (data_json, docspec,
684 json_decref (data_json);
685 if (NULL == json_obj)
687 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
688 "Unable to parse JSONAPI Object from %s\n",
690 GNUNET_SCHEDULER_add_now (&do_error, handle);
693 if (1 != GNUNET_JSONAPI_document_resource_count (json_obj))
695 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
696 "Cannot create more than 1 resource! (Got %d)\n",
697 GNUNET_JSONAPI_document_resource_count (json_obj));
698 GNUNET_JSONAPI_document_delete (json_obj);
699 GNUNET_SCHEDULER_add_now (&do_error, handle);
702 json_res = GNUNET_JSONAPI_document_get_resource (json_obj, 0);
703 if (GNUNET_NO == GNUNET_JSONAPI_resource_check_type (json_res,
704 GNUNET_REST_JSONAPI_IDENTITY_ATTRIBUTE))
706 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
707 "Unsupported JSON data type\n");
708 GNUNET_JSONAPI_document_delete (json_obj);
709 resp = GNUNET_REST_create_response (NULL);
710 handle->proc (handle->proc_cls, resp, MHD_HTTP_CONFLICT);
711 cleanup_handle (handle);
714 name_str = GNUNET_JSONAPI_resource_get_id (json_res);
715 value_json = GNUNET_JSONAPI_resource_read_attr (json_res,
717 value_str = json_string_value (value_json);
718 attribute = GNUNET_IDENTITY_ATTRIBUTE_claim_new (name_str,
719 GNUNET_IDENTITY_ATTRIBUTE_TYPE_STRING,
721 strlen (value_str) + 1);
722 handle->idp = GNUNET_IDENTITY_PROVIDER_connect (cfg);
723 handle->idp_op = GNUNET_IDENTITY_PROVIDER_attribute_store (handle->idp,
728 GNUNET_free (attribute);
729 GNUNET_JSONAPI_document_delete (json_obj);
735 * Collect all attributes for an ego
739 attr_collect (void *cls,
740 const struct GNUNET_CRYPTO_EcdsaPublicKey *identity,
741 const struct GNUNET_IDENTITY_ATTRIBUTE_Claim *attr)
743 struct GNUNET_JSONAPI_Resource *json_resource;
744 struct RequestHandle *handle = cls;
747 if ((NULL == attr->name) || (NULL == attr->data))
749 GNUNET_IDENTITY_PROVIDER_get_attributes_next (handle->attr_it);
753 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding attribute: %s\n",
755 json_resource = GNUNET_JSONAPI_resource_new (GNUNET_REST_JSONAPI_IDENTITY_ATTRIBUTE,
757 GNUNET_JSONAPI_document_resource_add (handle->resp_object, json_resource);
759 value = json_string (attr->data);
760 GNUNET_JSONAPI_resource_add_attr (json_resource,
764 GNUNET_IDENTITY_PROVIDER_get_attributes_next (handle->attr_it);
770 * List attributes for identity request
772 * @param con_handle the connection handle
774 * @param cls the RequestHandle
777 list_attribute_cont (struct GNUNET_REST_RequestHandle *con_handle,
781 const struct GNUNET_CRYPTO_EcdsaPrivateKey *priv_key;
782 struct RequestHandle *handle = cls;
783 struct EgoEntry *ego_entry;
786 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Getting attributes for %s.\n",
788 if ( strlen (GNUNET_REST_API_NS_IDENTITY_ATTRIBUTES) >=
789 strlen (handle->url))
791 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "No identity given.\n");
792 GNUNET_SCHEDULER_add_now (&do_error, handle);
795 identity = handle->url + strlen (GNUNET_REST_API_NS_IDENTITY_ATTRIBUTES) + 1;
797 for (ego_entry = handle->ego_head;
799 ego_entry = ego_entry->next)
800 if (0 == strcmp (identity, ego_entry->identifier))
802 handle->resp_object = GNUNET_JSONAPI_document_new ();
805 if (NULL == ego_entry)
808 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Ego %s not found.\n",
810 GNUNET_SCHEDULER_add_now (&return_response, handle);
813 priv_key = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego);
814 handle->idp = GNUNET_IDENTITY_PROVIDER_connect (cfg);
815 handle->attr_it = GNUNET_IDENTITY_PROVIDER_get_attributes_start (handle->idp,
821 &collect_finished_cb,
827 revoke_ticket_cont (struct GNUNET_REST_RequestHandle *con_handle,
831 const struct GNUNET_CRYPTO_EcdsaPrivateKey *identity_priv;
832 const char* identity_str;
833 const char* audience_str;
836 struct RequestHandle *handle = cls;
837 struct EgoEntry *ego_entry;
838 struct MHD_Response *resp;
839 struct GNUNET_IDENTITY_PROVIDER_Ticket ticket;
840 struct GNUNET_JSONAPI_Document *json_obj;
841 struct GNUNET_JSONAPI_Resource *json_res;
842 struct GNUNET_CRYPTO_EcdsaPublicKey tmp_pk;
843 char term_data[handle->rest_handle->data_size+1];
845 json_t *identity_json;
846 json_t *audience_json;
849 struct GNUNET_JSON_Specification docspec[] = {
850 GNUNET_JSON_spec_jsonapi_document (&json_obj),
851 GNUNET_JSON_spec_end()
854 if (0 >= handle->rest_handle->data_size)
856 GNUNET_SCHEDULER_add_now (&do_error, handle);
860 term_data[handle->rest_handle->data_size] = '\0';
861 GNUNET_memcpy (term_data,
862 handle->rest_handle->data,
863 handle->rest_handle->data_size);
864 data_json = json_loads (term_data,
867 GNUNET_assert (GNUNET_OK ==
868 GNUNET_JSON_parse (data_json, docspec,
870 json_decref (data_json);
871 if (NULL == json_obj)
873 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
874 "Unable to parse JSONAPI Object from %s\n",
876 GNUNET_SCHEDULER_add_now (&do_error, handle);
879 if (1 != GNUNET_JSONAPI_document_resource_count (json_obj))
881 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
882 "Cannot create more than 1 resource! (Got %d)\n",
883 GNUNET_JSONAPI_document_resource_count (json_obj));
884 GNUNET_JSONAPI_document_delete (json_obj);
885 GNUNET_SCHEDULER_add_now (&do_error, handle);
888 json_res = GNUNET_JSONAPI_document_get_resource (json_obj, 0);
889 if (GNUNET_NO == GNUNET_JSONAPI_resource_check_type (json_res,
890 GNUNET_REST_JSONAPI_IDENTITY_TICKET))
892 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
893 "Unsupported JSON data type\n");
894 GNUNET_JSONAPI_document_delete (json_obj);
895 resp = GNUNET_REST_create_response (NULL);
896 handle->proc (handle->proc_cls, resp, MHD_HTTP_CONFLICT);
897 cleanup_handle (handle);
900 rnd_json = GNUNET_JSONAPI_resource_read_attr (json_res,
902 identity_json = GNUNET_JSONAPI_resource_read_attr (json_res,
904 audience_json = GNUNET_JSONAPI_resource_read_attr (json_res,
906 rnd_str = json_string_value (rnd_json);
907 identity_str = json_string_value (identity_json);
908 audience_str = json_string_value (audience_json);
910 GNUNET_STRINGS_string_to_data (rnd_str,
914 // GNUNET_STRINGS_string_to_data (identity_str,
915 // strlen (identity_str),
916 // &ticket.identity,type filter text
917 // sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));
918 GNUNET_STRINGS_string_to_data (audience_str,
919 strlen (audience_str),
921 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));
923 for (ego_entry = handle->ego_head;
925 ego_entry = ego_entry->next)
927 GNUNET_IDENTITY_ego_get_public_key (ego_entry->ego,
929 if (0 == memcmp (&ticket.identity,
931 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)))
934 if (NULL == ego_entry)
936 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
937 "Identity unknown (%s)\n", identity_str);
938 GNUNET_JSONAPI_document_delete (json_obj);
941 identity_priv = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego);
943 handle->idp = GNUNET_IDENTITY_PROVIDER_connect (cfg);
944 handle->idp_op = GNUNET_IDENTITY_PROVIDER_ticket_revoke (handle->idp,
949 GNUNET_JSONAPI_document_delete (json_obj);
953 consume_cont (void *cls,
954 const struct GNUNET_CRYPTO_EcdsaPublicKey *identity,
955 const struct GNUNET_IDENTITY_ATTRIBUTE_Claim *attr)
957 struct RequestHandle *handle = cls;
958 struct GNUNET_JSONAPI_Resource *json_resource;
961 if (NULL == identity)
963 GNUNET_SCHEDULER_add_now (&return_response, handle);
967 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding attribute: %s\n",
969 json_resource = GNUNET_JSONAPI_resource_new (GNUNET_REST_JSONAPI_IDENTITY_ATTRIBUTE,
971 GNUNET_JSONAPI_document_resource_add (handle->resp_object, json_resource);
973 value = json_string (attr->data);
974 GNUNET_JSONAPI_resource_add_attr (json_resource,
981 consume_ticket_cont (struct GNUNET_REST_RequestHandle *con_handle,
985 const struct GNUNET_CRYPTO_EcdsaPrivateKey *identity_priv;
986 const char* identity_str;
987 const char* audience_str;
990 struct RequestHandle *handle = cls;
991 struct EgoEntry *ego_entry;
992 struct MHD_Response *resp;
993 struct GNUNET_IDENTITY_PROVIDER_Ticket ticket;
994 struct GNUNET_JSONAPI_Document *json_obj;
995 struct GNUNET_JSONAPI_Resource *json_res;
996 struct GNUNET_CRYPTO_EcdsaPublicKey tmp_pk;
997 char term_data[handle->rest_handle->data_size+1];
999 json_t *identity_json;
1000 json_t *audience_json;
1003 struct GNUNET_JSON_Specification docspec[] = {
1004 GNUNET_JSON_spec_jsonapi_document (&json_obj),
1005 GNUNET_JSON_spec_end()
1008 if (0 >= handle->rest_handle->data_size)
1010 GNUNET_SCHEDULER_add_now (&do_error, handle);
1014 term_data[handle->rest_handle->data_size] = '\0';
1015 GNUNET_memcpy (term_data,
1016 handle->rest_handle->data,
1017 handle->rest_handle->data_size);
1018 data_json = json_loads (term_data,
1021 GNUNET_assert (GNUNET_OK ==
1022 GNUNET_JSON_parse (data_json, docspec,
1024 json_decref (data_json);
1025 if (NULL == json_obj)
1027 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1028 "Unable to parse JSONAPI Object from %s\n",
1030 GNUNET_SCHEDULER_add_now (&do_error, handle);
1033 if (1 != GNUNET_JSONAPI_document_resource_count (json_obj))
1035 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1036 "Cannot create more than 1 resource! (Got %d)\n",
1037 GNUNET_JSONAPI_document_resource_count (json_obj));
1038 GNUNET_JSONAPI_document_delete (json_obj);
1039 GNUNET_SCHEDULER_add_now (&do_error, handle);
1042 json_res = GNUNET_JSONAPI_document_get_resource (json_obj, 0);
1043 if (GNUNET_NO == GNUNET_JSONAPI_resource_check_type (json_res,
1044 GNUNET_REST_JSONAPI_IDENTITY_TICKET))
1046 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1047 "Unsupported JSON data type\n");
1048 GNUNET_JSONAPI_document_delete (json_obj);
1049 resp = GNUNET_REST_create_response (NULL);
1050 handle->proc (handle->proc_cls, resp, MHD_HTTP_CONFLICT);
1051 cleanup_handle (handle);
1054 rnd_json = GNUNET_JSONAPI_resource_read_attr (json_res,
1056 identity_json = GNUNET_JSONAPI_resource_read_attr (json_res,
1058 audience_json = GNUNET_JSONAPI_resource_read_attr (json_res,
1060 rnd_str = json_string_value (rnd_json);
1061 identity_str = json_string_value (identity_json);
1062 audience_str = json_string_value (audience_json);
1064 GNUNET_STRINGS_string_to_data (rnd_str,
1068 GNUNET_STRINGS_string_to_data (identity_str,
1069 strlen (identity_str),
1071 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));
1072 GNUNET_STRINGS_string_to_data (audience_str,
1073 strlen (audience_str),
1075 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));
1077 for (ego_entry = handle->ego_head;
1079 ego_entry = ego_entry->next)
1081 GNUNET_IDENTITY_ego_get_public_key (ego_entry->ego,
1083 if (0 == memcmp (&ticket.audience,
1085 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)))
1088 if (NULL == ego_entry)
1090 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1091 "Identity unknown (%s)\n", identity_str);
1092 GNUNET_JSONAPI_document_delete (json_obj);
1095 identity_priv = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego);
1096 handle->resp_object = GNUNET_JSONAPI_document_new ();
1097 handle->idp = GNUNET_IDENTITY_PROVIDER_connect (cfg);
1098 handle->idp_op = GNUNET_IDENTITY_PROVIDER_ticket_consume (handle->idp,
1103 GNUNET_JSONAPI_document_delete (json_obj);
1109 * Respond to OPTIONS request
1111 * @param con_handle the connection handle
1112 * @param url the url
1113 * @param cls the RequestHandle
1116 options_cont (struct GNUNET_REST_RequestHandle *con_handle,
1120 struct MHD_Response *resp;
1121 struct RequestHandle *handle = cls;
1123 //For now, independent of path return all options
1124 resp = GNUNET_REST_create_response (NULL);
1125 MHD_add_response_header (resp,
1126 "Access-Control-Allow-Methods",
1128 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
1129 cleanup_handle (handle);
1134 * Function called if we had an error in zone-to-name mapping.
1137 zone_to_name_error (void *cls)
1139 struct RequestHandle *handle = cls;
1141 handle->emsg = GNUNET_strdup("unauthorized_client");
1142 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1144 GNUNET_NAMESTORE_disconnect (handle->namestore_handle);
1145 handle->namestore_handle = NULL;
1146 GNUNET_SCHEDULER_add_now (&do_error, handle);
1150 * Test if a name mapping was found, if so, continue, else, throw error
1152 * @param cls closure
1153 * @param zone_key public key of the zone
1154 * @param name name that is being mapped (at most 255 characters long)
1155 * @param rd_count number of entries in @a rd array
1156 * @param rd array of records with data to store
1159 zone_to_name_get_cb (void *cls,
1160 const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone_key,
1162 unsigned int rd_count,
1163 const struct GNUNET_GNSRECORD_Data *rd)
1165 struct RequestHandle *handle = cls;
1170 handle->emsg = GNUNET_strdup("unauthorized_client");
1171 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1173 GNUNET_NAMESTORE_disconnect (handle->namestore_handle);
1174 handle->namestore_handle = NULL;
1175 GNUNET_SCHEDULER_add_now (&do_error, handle);
1181 * Respond to authorization GET request
1183 * @param con_handle the connection handle
1184 * @param url the url
1185 * @param cls the RequestHandle
1188 authorize_get_cont (struct GNUNET_REST_RequestHandle *con_handle,
1192 /** The Authorization Server MUST validate all the OAuth 2.0 parameters
1193 * according to the OAuth 2.0 specification.
1196 * If the sub (subject) Claim is requested with a specific value for the
1197 * ID Token, the Authorization Server MUST only send a positive response
1198 * if the End-User identified by that sub value has an active session with
1199 * the Authorization Server or has been Authenticated as a result of the
1200 * request. The Authorization Server MUST NOT reply with an ID Token or
1201 * Access Token for a different user, even if they have an active session
1202 * with the Authorization Server. Such a request can be made either using
1203 * an id_token_hint parameter or by requesting a specific Claim Value as
1204 * described in Section 5.5.1, if the claims parameter is supported by
1205 * the implementation.
1208 struct MHD_Response *resp;
1209 struct RequestHandle *handle = cls;
1210 char *response_type;
1214 char *expected_redirect_uri;
1217 struct GNUNET_TIME_Absolute current_time, *relog_time;
1218 char *login_base_url, *new_redirect;
1219 struct GNUNET_HashCode cache_key;
1220 const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone_pkey;
1221 struct GNUNET_CRYPTO_EcdsaPublicKey pubkey;
1222 int number_of_ignored_parameter, iterator;
1224 // REQUIRED value: client_id
1225 GNUNET_CRYPTO_hash (OIDC_CLIENT_ID_KEY, strlen (OIDC_CLIENT_ID_KEY),
1227 if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map,
1230 handle->emsg=GNUNET_strdup("invalid_request");
1231 handle->edesc=GNUNET_strdup("Missing parameter: client_id");
1232 GNUNET_SCHEDULER_add_now (&do_error, handle);
1235 client_id = GNUNET_CONTAINER_multihashmap_get(handle->rest_handle->url_param_map,
1238 != GNUNET_CRYPTO_ecdsa_public_key_from_string (client_id,
1242 handle->emsg=GNUNET_strdup("unauthorized_client");
1243 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1244 GNUNET_SCHEDULER_add_now (&do_error, handle);
1247 // Checks if client_id is valid:
1248 handle->namestore_handle = GNUNET_NAMESTORE_connect(cfg);
1249 zone_pkey = GNUNET_IDENTITY_ego_get_private_key (handle->ego_entry->ego);
1250 GNUNET_NAMESTORE_zone_to_name (handle->namestore_handle, zone_pkey, &pubkey,
1251 zone_to_name_error, handle, zone_to_name_get_cb,
1255 // REQUIRED value: redirect_uri
1256 GNUNET_CRYPTO_hash (OIDC_REDIRECT_URI_KEY, strlen (OIDC_REDIRECT_URI_KEY),
1258 if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map,
1261 handle->emsg=GNUNET_strdup("invalid_request");
1262 handle->edesc=GNUNET_strdup("Missing parameter: redirect_uri");
1263 GNUNET_SCHEDULER_add_now (&do_error, handle);
1266 redirect_uri = GNUNET_CONTAINER_multihashmap_get(handle->rest_handle->url_param_map,
1269 GNUNET_asprintf (&expected_redirect_uri, "https://%s.zkey", client_id);
1271 // verify the redirect uri matches https://<client_id>.zkey[/xyz]
1272 if( 0 != strncmp( expected_redirect_uri, redirect_uri, strlen(expected_redirect_uri)) )
1274 handle->emsg=GNUNET_strdup("invalid_request");
1275 handle->edesc=GNUNET_strdup("Invalid redirect_uri");
1276 GNUNET_free(expected_redirect_uri);
1277 GNUNET_SCHEDULER_add_now (&do_error, handle);
1280 handle->eredirect = GNUNET_strdup(redirect_uri);
1282 // REQUIRED value: response_type
1283 GNUNET_CRYPTO_hash (OIDC_RESPONSE_TYPE_KEY, strlen (OIDC_RESPONSE_TYPE_KEY),
1285 if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map,
1288 handle->emsg=GNUNET_strdup("invalid_request");
1289 handle->edesc=GNUNET_strdup("Missing parameter: response_type");
1290 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1293 response_type = GNUNET_CONTAINER_multihashmap_get(handle->rest_handle->url_param_map,
1296 // REQUIRED value: scope
1297 GNUNET_CRYPTO_hash (OIDC_SCOPE_KEY, strlen (OIDC_SCOPE_KEY), &cache_key);
1298 if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map,
1301 handle->emsg=GNUNET_strdup("invalid_request");
1302 handle->edesc=GNUNET_strdup("Missing parameter: scope");
1303 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1306 scope = GNUNET_CONTAINER_multihashmap_get(handle->rest_handle->url_param_map,
1309 //RECOMMENDED value: state
1310 GNUNET_CRYPTO_hash (OIDC_STATE_KEY, strlen (OIDC_STATE_KEY), &cache_key);
1311 if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map,
1314 state = GNUNET_CONTAINER_multihashmap_get(handle->rest_handle->url_param_map,
1318 //OPTIONAL value: nonce
1319 GNUNET_CRYPTO_hash (OIDC_NONCE_KEY, strlen (OIDC_NONCE_KEY), &cache_key);
1320 if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map,
1323 nonce = GNUNET_CONTAINER_multihashmap_get(handle->rest_handle->url_param_map,
1327 number_of_ignored_parameter = sizeof(OIDC_ignored_parameter_array) / sizeof(char *);
1328 for( iterator = 0; iterator < number_of_ignored_parameter; iterator++ )
1330 GNUNET_CRYPTO_hash (OIDC_ignored_parameter_array[iterator],
1331 strlen(OIDC_ignored_parameter_array[iterator]),
1333 if(GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains(handle->rest_handle->url_param_map,
1336 handle->emsg=GNUNET_strdup("access_denied");
1337 GNUNET_asprintf (&handle->edesc, "Server will not handle parameter: %s",
1338 OIDC_ignored_parameter_array[iterator]);
1339 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1344 // Checks if response_type is 'code'
1345 if( 0 != strcmp( response_type, OIDC_EXPECTED_AUTHORIZATION_RESPONSE_TYPE ) )
1347 handle->emsg=GNUNET_strdup("unsupported_response_type");
1348 handle->edesc=GNUNET_strdup("The authorization server does not support "
1349 "obtaining this authorization code.");
1350 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1353 // Checks if scope contains 'openid'
1354 if( NULL == strstr( scope, OIDC_EXPECTED_AUTHORIZATION_SCOPE ) )
1356 handle->emsg=GNUNET_strdup("invalid_scope");
1357 handle->edesc=GNUNET_strdup("The requested scope is invalid, unknown, or "
1359 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1363 //TODO check other values and use them accordingly
1365 GNUNET_CRYPTO_hash (OIDC_COOKIE_HEADER_KEY, strlen (OIDC_COOKIE_HEADER_KEY),
1367 //No identity-cookie -> redirect to login
1369 == GNUNET_CONTAINER_multihashmap_contains (con_handle->header_param_map,
1372 //split cookies and find 'Identity' cookie
1373 char* cookies = GNUNET_CONTAINER_multihashmap_get (
1374 con_handle->header_param_map, &cache_key);
1375 char delimiter[] = "; ";
1376 char *identity_cookie;
1377 identity_cookie = strtok(cookies, delimiter);
1379 while ( NULL != identity_cookie )
1381 if ( NULL != strstr (identity_cookie, OIDC_COOKIE_HEADER_INFORMATION_KEY) )
1385 identity_cookie = strtok (NULL, delimiter);
1387 GNUNET_CRYPTO_hash (identity_cookie, strlen (identity_cookie), &cache_key);
1389 //No login time for identity -> redirect to login
1391 == GNUNET_CONTAINER_multihashmap_contains (OIDC_authorized_identities,
1394 relog_time = GNUNET_CONTAINER_multihashmap_get (
1395 OIDC_authorized_identities, &cache_key);
1397 current_time = GNUNET_TIME_absolute_get();
1399 GNUNET_CONTAINER_multihashmap_remove_all(OIDC_authorized_identities, &cache_key);
1400 // 30 min after old login -> redirect to login
1401 if ( current_time.abs_value_us <= relog_time->abs_value_us )
1403 resp = GNUNET_REST_create_response ("");
1405 GNUNET_CRYPTO_ecdsa_public_key_from_string (identity_cookie,
1406 strlen (identity_cookie),
1409 // iterate over egos and compare their public key
1410 // GNUNET_IDENTITY_PROVIDER_get_attributes_start
1411 // iterate over scope variables
1412 char delimiter[] = " ";
1413 char *scope_attribute;
1414 scope_attribute = strtok(scope, delimiter);
1416 while ( NULL != scope_attribute )
1418 if ( NULL == strstr (scope_attribute, OIDC_EXPECTED_AUTHORIZATION_SCOPE) )
1420 // claim attribute from ego
1421 scope_attribute = strtok (NULL, delimiter);
1424 // create an authorization code
1426 // GNUNET_IDENTITY_PROVIDER_t
1428 MHD_add_response_header (resp, "Location", redirect_uri);
1429 handle->proc (handle->proc_cls, resp, MHD_HTTP_FOUND);
1430 cleanup_handle (handle);
1431 GNUNET_free(relog_time);
1434 GNUNET_free(relog_time);
1439 // login redirection
1441 == GNUNET_CONFIGURATION_get_value_string (cfg, "identity-rest-plugin",
1442 "address", &login_base_url) )
1444 GNUNET_asprintf (&new_redirect, "%s?%s=%s&%s=%s&%s=%s&%s=%s&%s=%s&%s=%s",
1446 OIDC_RESPONSE_TYPE_KEY,
1450 OIDC_REDIRECT_URI_KEY,
1455 (NULL == state) ? state : "",
1457 (NULL == nonce) ? nonce : "");
1458 resp = GNUNET_REST_create_response ("");
1459 MHD_add_response_header (resp, "Location", new_redirect);
1463 handle->emsg = GNUNET_strdup("No server configuration");
1464 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1465 GNUNET_SCHEDULER_add_now (&do_error, handle);
1468 handle->proc (handle->proc_cls, resp, MHD_HTTP_FOUND);
1469 cleanup_handle (handle);
1470 GNUNET_free(new_redirect);
1475 * Respond to authorization POST request
1477 * @param con_handle the connection handle
1478 * @param url the url
1479 * @param cls the RequestHandle
1482 authorize_post_cont (struct GNUNET_REST_RequestHandle *con_handle,
1486 /** The Authorization Server MUST validate all the OAuth 2.0 parameters
1487 * according to the OAuth 2.0 specification.
1490 * If the sub (subject) Claim is requested with a specific value for the
1491 * ID Token, the Authorization Server MUST only send a positive response
1492 * if the End-User identified by that sub value has an active session with
1493 * the Authorization Server or has been Authenticated as a result of the
1494 * request. The Authorization Server MUST NOT reply with an ID Token or
1495 * Access Token for a different user, even if they have an active session
1496 * with the Authorization Server. Such a request can be made either using
1497 * an id_token_hint parameter or by requesting a specific Claim Value as
1498 * described in Section 5.5.1, if the claims parameter is supported by
1499 * the implementation.
1502 struct MHD_Response *resp;
1503 struct RequestHandle *handle = cls;
1504 const char *response_type;
1505 const char *client_id;
1507 const char *redirect_uri;
1508 const char *state = NULL;
1509 const char *nonce = NULL;
1510 struct GNUNET_TIME_Absolute current_time, *relog_time;
1511 char *login_base_url;
1513 char *expected_redirect_uri;
1514 json_t *cache_object;
1515 const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone_pkey;
1516 struct GNUNET_CRYPTO_EcdsaPublicKey pubkey;
1517 struct GNUNET_HashCode cache_key;
1518 int number_of_ignored_parameter, iterator;
1522 root = json_loads (handle->rest_handle->data, 0, &error);
1524 // REQUIRED value: client_id
1525 cache_object = json_object_get (root, OIDC_CLIENT_ID_KEY);
1526 if( NULL==cache_object || !json_is_string(cache_object))
1528 handle->emsg=GNUNET_strdup("invalid_request");
1529 handle->edesc=GNUNET_strdup("Missing parameter: client_id");
1530 GNUNET_SCHEDULER_add_now (&do_error, handle);
1533 client_id = json_string_value(cache_object);
1535 != GNUNET_CRYPTO_ecdsa_public_key_from_string (client_id,
1539 handle->emsg=GNUNET_strdup("unauthorized_client");
1540 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1541 GNUNET_SCHEDULER_add_now (&do_error, handle);
1544 // Checks if client_id is valid:
1545 handle->namestore_handle = GNUNET_NAMESTORE_connect(cfg);
1546 zone_pkey = GNUNET_IDENTITY_ego_get_private_key (handle->ego_entry->ego);
1548 // GNUNET_NAMESTORE_zone_to_name (handle->namestore_handle, zone_pkey, &pubkey,
1549 // zone_to_name_error, handle, zone_to_name_cb,
1552 // REQUIRED value: redirect_uri
1553 cache_object = json_object_get (root, OIDC_REDIRECT_URI_KEY);
1554 if( NULL==cache_object || !json_is_string(cache_object))
1556 handle->emsg=GNUNET_strdup("invalid_request");
1557 handle->edesc=GNUNET_strdup("Missing parameter: redirect_uri");
1558 GNUNET_SCHEDULER_add_now (&do_error, handle);
1561 redirect_uri = json_string_value(cache_object);
1563 GNUNET_asprintf (&expected_redirect_uri, "https://%s.zkey", client_id);
1565 // verify the redirect uri matches https://<client_id>.zkey[/xyz]
1566 if( 0 != strncmp( expected_redirect_uri, redirect_uri, strlen(expected_redirect_uri)) )
1568 handle->emsg=GNUNET_strdup("invalid_request");
1569 handle->edesc=GNUNET_strdup("Invalid redirect_uri");
1570 GNUNET_free(expected_redirect_uri);
1571 GNUNET_SCHEDULER_add_now (&do_error, handle);
1574 handle->eredirect = GNUNET_strdup(redirect_uri);
1576 // REQUIRED value: response_type
1577 cache_object = json_object_get (root, OIDC_RESPONSE_TYPE_KEY);
1578 if( NULL==cache_object || !json_is_string(cache_object))
1580 handle->emsg=GNUNET_strdup("invalid_request");
1581 handle->edesc=GNUNET_strdup("Missing parameter: response_type");
1582 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1585 response_type = json_string_value(cache_object);
1587 // REQUIRED value: scope
1588 cache_object = json_object_get (root, OIDC_SCOPE_KEY);
1589 if( NULL==cache_object || !json_is_string(cache_object))
1591 handle->emsg=GNUNET_strdup("invalid_request");
1592 handle->edesc=GNUNET_strdup("Missing parameter: scope");
1593 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1596 scope = json_string_value(cache_object);
1598 //RECOMMENDED value: state
1599 cache_object = json_object_get (root, OIDC_STATE_KEY);
1600 if( NULL!=cache_object || json_is_string(cache_object))
1602 state = json_string_value(cache_object);
1605 //OPTIONAL value: nonce
1606 cache_object = json_object_get (root, OIDC_NONCE_KEY);
1607 if( NULL!=cache_object || json_is_string(cache_object))
1609 nonce = json_string_value(cache_object);
1612 //TODO check other values and use them accordingly
1613 number_of_ignored_parameter = sizeof(OIDC_ignored_parameter_array) / sizeof(char *);
1614 for( iterator = 0; iterator < number_of_ignored_parameter; iterator++ )
1616 cache_object = json_object_get (root, OIDC_ignored_parameter_array[iterator]);
1617 if(json_is_string(cache_object))
1619 handle->emsg=GNUNET_strdup("access_denied");
1620 GNUNET_asprintf (&handle->edesc, "Server will not handle parameter: %s",
1621 OIDC_ignored_parameter_array[iterator]);
1622 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1627 // Checks if response_type is 'code'
1628 if( 0 != strcmp( response_type, OIDC_EXPECTED_AUTHORIZATION_RESPONSE_TYPE ) )
1630 handle->emsg=GNUNET_strdup("unsupported_response_type");
1631 handle->edesc=GNUNET_strdup("The authorization server does not support "
1632 "obtaining this authorization code.");
1633 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1636 // Checks if scope contains 'openid'
1637 if( NULL == strstr( scope, OIDC_EXPECTED_AUTHORIZATION_SCOPE ) )
1639 handle->emsg=GNUNET_strdup("invalid_scope");
1640 handle->edesc=GNUNET_strdup("The requested scope is invalid, unknown, or "
1642 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1647 GNUNET_CRYPTO_hash (OIDC_COOKIE_HEADER_KEY, strlen (OIDC_COOKIE_HEADER_KEY),
1649 //No identity-cookie -> redirect to login
1651 == GNUNET_CONTAINER_multihashmap_contains (con_handle->header_param_map,
1654 //split cookies and find 'Identity' cookie
1655 char* cookies = GNUNET_CONTAINER_multihashmap_get (
1656 con_handle->header_param_map, &cache_key);
1657 char delimiter[] = "; ";
1658 char *identity_cookie;
1659 identity_cookie = strtok(cookies, delimiter);
1661 while ( NULL != identity_cookie )
1663 if ( NULL != strstr (identity_cookie, OIDC_COOKIE_HEADER_INFORMATION_KEY) )
1667 identity_cookie = strtok (NULL, delimiter);
1669 GNUNET_CRYPTO_hash (identity_cookie, strlen (identity_cookie), &cache_key);
1671 //No login time for identity -> redirect to login
1673 == GNUNET_CONTAINER_multihashmap_contains (OIDC_authorized_identities,
1676 relog_time = GNUNET_CONTAINER_multihashmap_get (
1677 OIDC_authorized_identities, &cache_key);
1679 current_time = GNUNET_TIME_absolute_get();
1681 GNUNET_CONTAINER_multihashmap_remove_all(OIDC_authorized_identities, &cache_key);
1682 // 30 min after old login -> redirect to login
1683 if ( current_time.abs_value_us <= relog_time->abs_value_us )
1685 resp = GNUNET_REST_create_response ("");
1687 GNUNET_CRYPTO_ecdsa_public_key_from_string (identity_cookie,
1688 strlen (identity_cookie),
1691 // iterate over egos and compare their public key
1692 // GNUNET_IDENTITY_PROVIDER_get_attributes_start
1693 // iterate over scope variables
1694 char delimiter[] = " ";
1695 char *scope_attribute;
1696 scope_attribute = strtok(scope, delimiter);
1698 while ( NULL != scope_attribute )
1700 if ( NULL == strstr (scope_attribute, OIDC_EXPECTED_AUTHORIZATION_SCOPE) )
1702 // claim attribute from ego
1703 scope_attribute = strtok (NULL, delimiter);
1706 // create an authorization code
1708 // GNUNET_IDENTITY_PROVIDER_t
1710 MHD_add_response_header (resp, "Location", redirect_uri);
1711 handle->proc (handle->proc_cls, resp, MHD_HTTP_FOUND);
1712 cleanup_handle (handle);
1713 GNUNET_free(relog_time);
1716 GNUNET_free(relog_time);
1721 // login redirection
1723 == GNUNET_CONFIGURATION_get_value_string (cfg, "identity-rest-plugin",
1724 "address", &login_base_url) )
1726 GNUNET_asprintf (&new_redirect, "%s?%s=%s&%s=%s&%s=%s&%s=%s&%s=%s&%s=%s",
1728 OIDC_RESPONSE_TYPE_KEY,
1732 OIDC_REDIRECT_URI_KEY,
1737 (NULL == state) ? state : "",
1739 (NULL == nonce) ? nonce : "");
1740 resp = GNUNET_REST_create_response ("");
1741 MHD_add_response_header (resp, "Location", new_redirect);
1745 handle->emsg = GNUNET_strdup("No server configuration");
1746 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1747 GNUNET_SCHEDULER_add_now (&do_error, handle);
1750 handle->proc (handle->proc_cls, resp, MHD_HTTP_FOUND);
1751 cleanup_handle (handle);
1752 GNUNET_free(new_redirect);
1758 * Combines an identity with a login time and responds OK to login request
1760 * @param con_handle the connection handle
1761 * @param url the url
1762 * @param cls the RequestHandle
1765 login_cont (struct GNUNET_REST_RequestHandle *con_handle,
1771 struct MHD_Response *resp = GNUNET_REST_create_response ("");
1772 struct RequestHandle *handle = cls;
1773 struct GNUNET_HashCode cache_key;
1774 struct GNUNET_TIME_Absolute *current_time;
1779 root = json_loads (handle->rest_handle->data, 0, &error);
1780 identity = json_object_get (root, "identity");
1781 if ( json_is_string(identity) )
1783 GNUNET_asprintf (&cookie, "Identity=%s", json_string_value (identity));
1785 GNUNET_CRYPTO_hash (cookie, strlen (cookie), &cache_key);
1786 current_time = GNUNET_new(struct GNUNET_TIME_Absolute);
1787 *current_time = GNUNET_TIME_relative_to_absolute (
1788 GNUNET_TIME_relative_multiply (GNUNET_TIME_relative_get_minute_ (),
1790 GNUNET_CONTAINER_multihashmap_put (
1791 OIDC_authorized_identities, &cache_key, current_time,
1792 GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE);
1794 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
1798 handle->proc (handle->proc_cls, resp, MHD_HTTP_BAD_REQUEST);
1800 GNUNET_free(cookie);
1802 cleanup_handle (handle);
1807 * Handle rest request
1809 * @param handle the request handle
1812 init_cont (struct RequestHandle *handle)
1814 struct GNUNET_REST_RequestHandlerError err;
1815 static const struct GNUNET_REST_RequestHandler handlers[] = {
1816 {MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_IDENTITY_ATTRIBUTES, &list_attribute_cont},
1817 {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_IDENTITY_ATTRIBUTES, &add_attribute_cont},
1818 {MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_IDENTITY_TICKETS, &list_tickets_cont},
1819 {MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_AUTHORIZE, &authorize_get_cont},
1820 {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_LOGIN, &login_cont},
1821 {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_AUTHORIZE, &authorize_post_cont},
1822 {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_IDENTITY_REVOKE, &revoke_ticket_cont},
1823 {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_IDENTITY_CONSUME, &consume_ticket_cont},
1824 {MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_IDENTITY_PROVIDER,
1826 GNUNET_REST_HANDLER_END
1829 if (GNUNET_NO == GNUNET_REST_handle_request (handle->rest_handle,
1834 handle->response_code = err.error_code;
1835 GNUNET_SCHEDULER_add_now (&do_error, handle);
1840 * If listing is enabled, prints information about the egos.
1842 * This function is initially called for all egos and then again
1843 * whenever a ego's identifier changes or if it is deleted. At the
1844 * end of the initial pass over all egos, the function is once called
1845 * with 'NULL' for 'ego'. That does NOT mean that the callback won't
1846 * be invoked in the future or that there was an error.
1848 * When used with 'GNUNET_IDENTITY_create' or 'GNUNET_IDENTITY_get',
1849 * this function is only called ONCE, and 'NULL' being passed in
1850 * 'ego' does indicate an error (i.e. name is taken or no default
1851 * value is known). If 'ego' is non-NULL and if '*ctx'
1852 * is set in those callbacks, the value WILL be passed to a subsequent
1853 * call to the identity callback of 'GNUNET_IDENTITY_connect' (if
1854 * that one was not NULL).
1856 * When an identity is renamed, this function is called with the
1857 * (known) ego but the NEW identifier.
1859 * When an identity is deleted, this function is called with the
1860 * (known) ego and "NULL" for the 'identifier'. In this case,
1861 * the 'ego' is henceforth invalid (and the 'ctx' should also be
1864 * @param cls closure
1865 * @param ego ego handle
1866 * @param ctx context for application to store data for this ego
1867 * (during the lifetime of this process, initially NULL)
1868 * @param identifier identifier assigned by the user for this ego,
1869 * NULL if the user just deleted the ego and it
1870 * must thus no longer be used
1873 list_ego (void *cls,
1874 struct GNUNET_IDENTITY_Ego *ego,
1876 const char *identifier)
1878 struct RequestHandle *handle = cls;
1879 struct EgoEntry *ego_entry;
1880 struct GNUNET_CRYPTO_EcdsaPublicKey pk;
1882 if ((NULL == ego) && (ID_REST_STATE_INIT == handle->state))
1884 handle->state = ID_REST_STATE_POST_INIT;
1888 if (ID_REST_STATE_INIT == handle->state) {
1889 ego_entry = GNUNET_new (struct EgoEntry);
1890 GNUNET_IDENTITY_ego_get_public_key (ego, &pk);
1891 ego_entry->keystring =
1892 GNUNET_CRYPTO_ecdsa_public_key_to_string (&pk);
1893 ego_entry->ego = ego;
1894 ego_entry->identifier = GNUNET_strdup (identifier);
1895 GNUNET_CONTAINER_DLL_insert_tail(handle->ego_head,handle->ego_tail, ego_entry);
1901 rest_identity_process_request(struct GNUNET_REST_RequestHandle *rest_handle,
1902 GNUNET_REST_ResultProcessor proc,
1905 struct RequestHandle *handle = GNUNET_new (struct RequestHandle);
1906 if ( NULL == OIDC_authorized_identities )
1908 OIDC_authorized_identities = GNUNET_CONTAINER_multihashmap_create (10,
1911 handle->timeout = GNUNET_TIME_UNIT_FOREVER_REL;
1912 handle->proc_cls = proc_cls;
1913 handle->proc = proc;
1914 handle->state = ID_REST_STATE_INIT;
1915 handle->rest_handle = rest_handle;
1917 handle->url = GNUNET_strdup (rest_handle->url);
1918 if (handle->url[strlen (handle->url)-1] == '/')
1919 handle->url[strlen (handle->url)-1] = '\0';
1920 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1922 handle->identity_handle = GNUNET_IDENTITY_connect (cfg,
1925 handle->timeout_task =
1926 GNUNET_SCHEDULER_add_delayed (handle->timeout,
1929 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1934 * Entry point for the plugin.
1936 * @param cls Config info
1937 * @return NULL on error, otherwise the plugin context
1940 libgnunet_plugin_rest_identity_provider_init (void *cls)
1942 static struct Plugin plugin;
1943 struct GNUNET_REST_Plugin *api;
1946 if (NULL != plugin.cfg)
1947 return NULL; /* can only initialize once! */
1948 memset (&plugin, 0, sizeof (struct Plugin));
1950 api = GNUNET_new (struct GNUNET_REST_Plugin);
1952 api->name = GNUNET_REST_API_NS_IDENTITY_PROVIDER;
1953 api->process_request = &rest_identity_process_request;
1954 GNUNET_asprintf (&allow_methods,
1955 "%s, %s, %s, %s, %s",
1956 MHD_HTTP_METHOD_GET,
1957 MHD_HTTP_METHOD_POST,
1958 MHD_HTTP_METHOD_PUT,
1959 MHD_HTTP_METHOD_DELETE,
1960 MHD_HTTP_METHOD_OPTIONS);
1962 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1963 _("Identity Provider REST API initialized\n"));
1969 * Exit point from the plugin.
1971 * @param cls the plugin context (as returned by "init")
1972 * @return always NULL
1975 libgnunet_plugin_rest_identity_provider_done (void *cls)
1977 struct GNUNET_REST_Plugin *api = cls;
1978 struct Plugin *plugin = api->cls;
1981 GNUNET_free_non_null (allow_methods);
1983 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1984 "Identity Provider REST plugin is finished\n");
1988 /* end of plugin_rest_identity_provider.c */