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 * @author Philippe Buschmann
23 * @file identity/plugin_rest_identity.c
24 * @brief GNUnet Namestore REST plugin
29 #include "gnunet_rest_plugin.h"
30 #include "gnunet_identity_service.h"
31 #include "gnunet_gns_service.h"
32 #include "gnunet_gnsrecord_lib.h"
33 #include "gnunet_namestore_service.h"
34 #include "gnunet_rest_lib.h"
35 #include "gnunet_jsonapi_lib.h"
36 #include "gnunet_jsonapi_util.h"
37 #include "microhttpd.h"
40 #include "gnunet_signatures.h"
41 #include "gnunet_identity_attribute_lib.h"
42 #include "gnunet_identity_provider_service.h"
48 #define GNUNET_REST_API_NS_IDENTITY_PROVIDER "/idp"
53 #define GNUNET_REST_API_NS_IDENTITY_ATTRIBUTES "/idp/attributes"
58 #define GNUNET_REST_API_NS_IDENTITY_TICKETS "/idp/tickets"
63 #define GNUNET_REST_API_NS_IDENTITY_REVOKE "/idp/revoke"
68 #define GNUNET_REST_API_NS_IDENTITY_CONSUME "/idp/consume"
73 #define GNUNET_REST_API_NS_AUTHORIZE "/idp/authorize"
78 #define GNUNET_REST_API_NS_TOKEN "/idp/token"
83 #define GNUNET_REST_API_NS_USERINFO "/idp/userinfo"
88 #define GNUNET_REST_API_NS_LOGIN "/idp/login"
93 #define GNUNET_REST_JSONAPI_IDENTITY_ATTRIBUTE "attribute"
98 #define GNUNET_REST_JSONAPI_IDENTITY_TICKET "ticket"
104 #define GNUNET_REST_JSONAPI_IDENTITY_ATTRIBUTE_VALUE "value"
107 * State while collecting all egos
109 #define ID_REST_STATE_INIT 0
112 * Done collecting egos
114 #define ID_REST_STATE_POST_INIT 1
117 * OIDC grant_type key
119 #define OIDC_GRANT_TYPE_KEY "grant_type"
122 * OIDC grant_type key
124 #define OIDC_GRANT_TYPE_VALUE "authorization_code"
129 #define OIDC_CODE_KEY "code"
132 * OIDC response_type key
134 #define OIDC_RESPONSE_TYPE_KEY "response_type"
139 #define OIDC_CLIENT_ID_KEY "client_id"
144 #define OIDC_SCOPE_KEY "scope"
147 * OIDC redirect_uri key
149 #define OIDC_REDIRECT_URI_KEY "redirect_uri"
154 #define OIDC_STATE_KEY "state"
159 #define OIDC_NONCE_KEY "nonce"
162 * OIDC cookie header key
164 #define OIDC_COOKIE_HEADER_KEY "Cookie"
167 * OIDC cookie header information key
169 #define OIDC_AUTHORIZATION_HEADER_KEY "Authorization"
172 * OIDC cookie header information key
174 #define OIDC_COOKIE_HEADER_INFORMATION_KEY "Identity="
177 * OIDC expected response_type while authorizing
179 #define OIDC_EXPECTED_AUTHORIZATION_RESPONSE_TYPE "code"
182 * OIDC expected scope part while authorizing
184 #define OIDC_EXPECTED_AUTHORIZATION_SCOPE "openid"
187 * OIDC ignored parameter array
189 char* OIDC_ignored_parameter_array [] =
202 * OIDC authorized identities and times hashmap
204 struct GNUNET_CONTAINER_MultiHashMap *OIDC_identity_login_time;
207 * OIDC authorized identities and times hashmap
209 struct GNUNET_CONTAINER_MultiHashMap *OIDC_identity_grants;
212 * OIDC ticket/code use only once
214 struct GNUNET_CONTAINER_MultiHashMap *OIDC_ticket_once;
217 * OIDC access_token to ticket and ego
219 struct GNUNET_CONTAINER_MultiHashMap *OIDC_interpret_access_token;
222 * The configuration handle
224 const struct GNUNET_CONFIGURATION_Handle *cfg;
227 * HTTP methods allows for this plugin
229 static char* allow_methods;
232 * @brief struct returned by the initialization function of the plugin
236 const struct GNUNET_CONFIGURATION_Handle *cfg;
240 * OIDC needed variables
242 struct OIDC_Variables
245 struct GNUNET_CRYPTO_EcdsaPublicKey client_pkey;
249 int is_client_trusted;
261 char *login_identity;
275 struct EgoEntry *next;
280 struct EgoEntry *prev;
295 struct GNUNET_IDENTITY_Ego *ego;
304 struct EgoEntry *ego_head;
309 struct EgoEntry *ego_tail;
314 struct EgoEntry *ego_entry;
317 * Pointer to ego private key
319 struct GNUNET_CRYPTO_EcdsaPrivateKey priv_key;
324 struct OIDC_Variables *oidc;
327 * The processing state
332 * Handle to Identity service.
334 struct GNUNET_IDENTITY_Handle *identity_handle;
339 struct GNUNET_REST_RequestHandle *rest_handle;
342 * Handle to NAMESTORE
344 struct GNUNET_NAMESTORE_Handle *namestore_handle;
347 * Iterator for NAMESTORE
349 struct GNUNET_NAMESTORE_ZoneIterator *namestore_handle_it;
352 * Attribute claim list
354 struct GNUNET_IDENTITY_ATTRIBUTE_ClaimList *attr_list;
359 struct GNUNET_IDENTITY_Operation *op;
364 struct GNUNET_IDENTITY_PROVIDER_Handle *idp;
369 struct GNUNET_IDENTITY_PROVIDER_Operation *idp_op;
374 struct GNUNET_IDENTITY_PROVIDER_AttributeIterator *attr_it;
379 struct GNUNET_IDENTITY_PROVIDER_TicketIterator *ticket_it;
382 * Desired timeout for the lookup (default is no timeout).
384 struct GNUNET_TIME_Relative timeout;
387 * ID of a task associated with the resolution process.
389 struct GNUNET_SCHEDULER_Task *timeout_task;
392 * The plugin result processor
394 GNUNET_REST_ResultProcessor proc;
397 * The closure of the result processor
407 * Error response message
412 * Error response description
424 struct GNUNET_JSONAPI_Document *resp_object;
429 * Cleanup lookup handle
430 * @param handle Handle to clean up
433 cleanup_handle (struct RequestHandle *handle)
435 struct GNUNET_IDENTITY_ATTRIBUTE_ClaimListEntry *claim_entry;
436 struct GNUNET_IDENTITY_ATTRIBUTE_ClaimListEntry *claim_tmp;
437 struct EgoEntry *ego_entry;
438 struct EgoEntry *ego_tmp;
439 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
441 if (NULL != handle->resp_object)
442 GNUNET_JSONAPI_document_delete (handle->resp_object);
443 if (NULL != handle->timeout_task)
444 GNUNET_SCHEDULER_cancel (handle->timeout_task);
445 if (NULL != handle->identity_handle)
446 GNUNET_IDENTITY_disconnect (handle->identity_handle);
447 if (NULL != handle->attr_it)
448 GNUNET_IDENTITY_PROVIDER_get_attributes_stop (handle->attr_it);
449 if (NULL != handle->ticket_it)
450 GNUNET_IDENTITY_PROVIDER_ticket_iteration_stop (handle->ticket_it);
451 if (NULL != handle->idp)
452 GNUNET_IDENTITY_PROVIDER_disconnect (handle->idp);
453 if (NULL != handle->url)
454 GNUNET_free (handle->url);
455 if (NULL != handle->emsg)
456 GNUNET_free (handle->emsg);
457 if (NULL != handle->edesc)
458 GNUNET_free (handle->edesc);
459 if (NULL != handle->namestore_handle)
460 GNUNET_NAMESTORE_disconnect (handle->namestore_handle);
461 if (NULL != handle->oidc)
463 if (NULL != handle->oidc->client_id)
464 GNUNET_free(handle->oidc->client_id);
465 if (NULL != handle->oidc->login_identity)
466 GNUNET_free(handle->oidc->login_identity);
467 if (NULL != handle->oidc->nonce)
468 GNUNET_free(handle->oidc->nonce);
469 if (NULL != handle->oidc->redirect_uri)
470 GNUNET_free(handle->oidc->redirect_uri);
471 if (NULL != handle->oidc->response_type)
472 GNUNET_free(handle->oidc->response_type);
473 if (NULL != handle->oidc->scope)
474 GNUNET_free(handle->oidc->scope);
475 if (NULL != handle->oidc->state)
476 GNUNET_free(handle->oidc->state);
477 if (NULL != handle->oidc->response)
478 json_decref(handle->oidc->response);
479 GNUNET_free(handle->oidc);
481 if ( NULL != handle->attr_list )
483 for (claim_entry = handle->attr_list->list_head;
484 NULL != claim_entry;)
486 claim_tmp = claim_entry;
487 claim_entry = claim_entry->next;
488 GNUNET_free(claim_tmp->claim);
489 GNUNET_free(claim_tmp);
491 GNUNET_free (handle->attr_list);
493 for (ego_entry = handle->ego_head;
497 ego_entry = ego_entry->next;
498 GNUNET_free (ego_tmp->identifier);
499 GNUNET_free (ego_tmp->keystring);
500 GNUNET_free (ego_tmp);
502 if (NULL != handle->attr_it)
504 GNUNET_free(handle->attr_it);
506 GNUNET_free (handle);
510 cleanup_handle_delayed (void *cls)
512 cleanup_handle (cls);
517 * Task run on error, sends error message. Cleans up everything.
519 * @param cls the `struct RequestHandle`
524 struct RequestHandle *handle = cls;
525 struct MHD_Response *resp;
528 GNUNET_asprintf (&json_error, "{ \"error\" : \"%s\", \"error_description\" : \"%s\"%s%s%s}",
530 (NULL != handle->edesc) ? handle->edesc : "",
531 (NULL != handle->oidc->state) ? ", \"state\":\"" : "",
532 (NULL != handle->oidc->state) ? handle->oidc->state : "",
533 (NULL != handle->oidc->state) ? "\"" : "");
534 if ( 0 == handle->response_code )
536 handle->response_code = MHD_HTTP_BAD_REQUEST;
538 resp = GNUNET_REST_create_response (json_error);
539 if (MHD_HTTP_UNAUTHORIZED == handle->response_code)
541 MHD_add_response_header(resp, "WWW-Authenticate", "Basic");
543 MHD_add_response_header (resp, "Content-Type", "application/json");
544 handle->proc (handle->proc_cls, resp, handle->response_code);
545 GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
546 GNUNET_free (json_error);
551 * Task run on error in userinfo endpoint, sends error header. Cleans up
554 * @param cls the `struct RequestHandle`
557 do_userinfo_error (void *cls)
559 struct RequestHandle *handle = cls;
560 struct MHD_Response *resp;
563 GNUNET_asprintf (&error, "error=\"%s\", error_description=\"%s\"",
565 (NULL != handle->edesc) ? handle->edesc : "");
566 resp = GNUNET_REST_create_response ("");
567 MHD_add_response_header(resp, "WWW-Authenticate", error);
568 handle->proc (handle->proc_cls, resp, handle->response_code);
569 GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
575 * Task run on error, sends error message and redirects. Cleans up everything.
577 * @param cls the `struct RequestHandle`
580 do_redirect_error (void *cls)
582 struct RequestHandle *handle = cls;
583 struct MHD_Response *resp;
585 GNUNET_asprintf (&redirect,
586 "%s?error=%s&error_description=%s%s%s",
587 handle->oidc->redirect_uri, handle->emsg, handle->edesc,
588 (NULL != handle->oidc->state) ? "&state=" : "",
589 (NULL != handle->oidc->state) ? handle->oidc->state : "");
590 resp = GNUNET_REST_create_response ("");
591 MHD_add_response_header (resp, "Location", redirect);
592 handle->proc (handle->proc_cls, resp, MHD_HTTP_FOUND);
593 GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
594 GNUNET_free (redirect);
598 * Task run on timeout, sends error message. Cleans up everything.
600 * @param cls the `struct RequestHandle`
603 do_timeout (void *cls)
605 struct RequestHandle *handle = cls;
607 handle->timeout_task = NULL;
613 collect_error_cb (void *cls)
615 struct RequestHandle *handle = cls;
621 finished_cont (void *cls,
625 struct RequestHandle *handle = cls;
626 struct MHD_Response *resp;
628 resp = GNUNET_REST_create_response (emsg);
629 if (GNUNET_OK != success)
631 GNUNET_SCHEDULER_add_now (&do_error, handle);
634 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
635 GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
640 * Return attributes for identity
642 * @param cls the request handle
645 return_response (void *cls)
648 struct RequestHandle *handle = cls;
649 struct MHD_Response *resp;
651 GNUNET_JSONAPI_document_serialize (handle->resp_object, &result_str);
652 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Result %s\n", result_str);
653 resp = GNUNET_REST_create_response (result_str);
654 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
655 GNUNET_free (result_str);
656 cleanup_handle (handle);
660 * Return attributes for claim
662 * @param cls the request handle
665 return_userinfo_response (void *cls)
668 struct RequestHandle *handle = cls;
669 struct MHD_Response *resp;
671 result_str = json_dumps (handle->oidc->response, 0);
673 resp = GNUNET_REST_create_response (result_str);
674 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
675 GNUNET_free (result_str);
676 cleanup_handle (handle);
680 * Returns base64 encoded string without padding
682 * @param string the string to encode
683 * @return base64 encoded string
686 base_64_encode(char *string)
689 GNUNET_STRINGS_base64_encode(string,strlen(string),&output);
690 int index = strlen(output)-1;
691 while ('=' == output[index])
693 output[index] = '\0';
700 collect_finished_cb (void *cls)
702 struct RequestHandle *handle = cls;
704 handle->attr_it = NULL;
705 handle->ticket_it = NULL;
706 GNUNET_SCHEDULER_add_now (&return_response, handle);
711 * Collect all attributes for an ego
715 ticket_collect (void *cls,
716 const struct GNUNET_IDENTITY_PROVIDER_Ticket *ticket)
718 struct GNUNET_JSONAPI_Resource *json_resource;
719 struct RequestHandle *handle = cls;
723 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding ticket\n");
724 tmp = GNUNET_STRINGS_data_to_string_alloc (&ticket->rnd,
726 json_resource = GNUNET_JSONAPI_resource_new (GNUNET_REST_JSONAPI_IDENTITY_TICKET,
729 GNUNET_JSONAPI_document_resource_add (handle->resp_object, json_resource);
731 tmp = GNUNET_STRINGS_data_to_string_alloc (&ticket->identity,
732 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));
733 value = json_string (tmp);
734 GNUNET_JSONAPI_resource_add_attr (json_resource,
739 tmp = GNUNET_STRINGS_data_to_string_alloc (&ticket->audience,
740 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));
741 value = json_string (tmp);
742 GNUNET_JSONAPI_resource_add_attr (json_resource,
747 tmp = GNUNET_STRINGS_data_to_string_alloc (&ticket->rnd,
749 value = json_string (tmp);
750 GNUNET_JSONAPI_resource_add_attr (json_resource,
755 GNUNET_IDENTITY_PROVIDER_ticket_iteration_next (handle->ticket_it);
761 * List tickets for identity request
763 * @param con_handle the connection handle
765 * @param cls the RequestHandle
768 list_tickets_cont (struct GNUNET_REST_RequestHandle *con_handle,
772 const struct GNUNET_CRYPTO_EcdsaPrivateKey *priv_key;
773 struct RequestHandle *handle = cls;
774 struct EgoEntry *ego_entry;
777 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Getting tickets for %s.\n",
779 if ( strlen (GNUNET_REST_API_NS_IDENTITY_TICKETS) >=
780 strlen (handle->url))
782 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "No identity given.\n");
783 GNUNET_SCHEDULER_add_now (&do_error, handle);
786 identity = handle->url + strlen (GNUNET_REST_API_NS_IDENTITY_TICKETS) + 1;
788 for (ego_entry = handle->ego_head;
790 ego_entry = ego_entry->next)
791 if (0 == strcmp (identity, ego_entry->identifier))
793 handle->resp_object = GNUNET_JSONAPI_document_new ();
795 if (NULL == ego_entry)
798 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Ego %s not found.\n",
800 GNUNET_SCHEDULER_add_now (&return_response, handle);
803 priv_key = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego);
804 handle->idp = GNUNET_IDENTITY_PROVIDER_connect (cfg);
805 handle->ticket_it = GNUNET_IDENTITY_PROVIDER_ticket_iteration_start (handle->idp,
811 &collect_finished_cb,
817 add_attribute_cont (struct GNUNET_REST_RequestHandle *con_handle,
821 const struct GNUNET_CRYPTO_EcdsaPrivateKey *identity_priv;
822 const char* identity;
823 const char* name_str;
824 const char* value_str;
826 struct RequestHandle *handle = cls;
827 struct EgoEntry *ego_entry;
828 struct MHD_Response *resp;
829 struct GNUNET_IDENTITY_ATTRIBUTE_Claim *attribute;
830 struct GNUNET_JSONAPI_Document *json_obj;
831 struct GNUNET_JSONAPI_Resource *json_res;
832 char term_data[handle->rest_handle->data_size+1];
836 struct GNUNET_JSON_Specification docspec[] = {
837 GNUNET_JSON_spec_jsonapi_document (&json_obj),
838 GNUNET_JSON_spec_end()
841 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding an attribute for %s.\n",
843 if ( strlen (GNUNET_REST_API_NS_IDENTITY_ATTRIBUTES) >=
844 strlen (handle->url))
846 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "No identity given.\n");
847 GNUNET_SCHEDULER_add_now (&do_error, handle);
850 identity = handle->url + strlen (GNUNET_REST_API_NS_IDENTITY_ATTRIBUTES) + 1;
852 for (ego_entry = handle->ego_head;
854 ego_entry = ego_entry->next)
855 if (0 == strcmp (identity, ego_entry->identifier))
858 if (NULL == ego_entry)
860 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
861 "Identity unknown (%s)\n", identity);
862 GNUNET_JSONAPI_document_delete (json_obj);
865 identity_priv = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego);
867 if (0 >= handle->rest_handle->data_size)
869 GNUNET_SCHEDULER_add_now (&do_error, handle);
873 term_data[handle->rest_handle->data_size] = '\0';
874 GNUNET_memcpy (term_data,
875 handle->rest_handle->data,
876 handle->rest_handle->data_size);
877 data_json = json_loads (term_data,
880 GNUNET_assert (GNUNET_OK ==
881 GNUNET_JSON_parse (data_json, docspec,
883 json_decref (data_json);
884 if (NULL == json_obj)
886 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
887 "Unable to parse JSONAPI Object from %s\n",
889 GNUNET_SCHEDULER_add_now (&do_error, handle);
892 if (1 != GNUNET_JSONAPI_document_resource_count (json_obj))
894 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
895 "Cannot create more than 1 resource! (Got %d)\n",
896 GNUNET_JSONAPI_document_resource_count (json_obj));
897 GNUNET_JSONAPI_document_delete (json_obj);
898 GNUNET_SCHEDULER_add_now (&do_error, handle);
901 json_res = GNUNET_JSONAPI_document_get_resource (json_obj, 0);
902 if (GNUNET_NO == GNUNET_JSONAPI_resource_check_type (json_res,
903 GNUNET_REST_JSONAPI_IDENTITY_ATTRIBUTE))
905 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
906 "Unsupported JSON data type\n");
907 GNUNET_JSONAPI_document_delete (json_obj);
908 resp = GNUNET_REST_create_response (NULL);
909 handle->proc (handle->proc_cls, resp, MHD_HTTP_CONFLICT);
910 cleanup_handle (handle);
913 name_str = GNUNET_JSONAPI_resource_get_id (json_res);
914 value_json = GNUNET_JSONAPI_resource_read_attr (json_res,
916 value_str = json_string_value (value_json);
917 attribute = GNUNET_IDENTITY_ATTRIBUTE_claim_new (name_str,
918 GNUNET_IDENTITY_ATTRIBUTE_TYPE_STRING,
920 strlen (value_str) + 1);
921 handle->idp = GNUNET_IDENTITY_PROVIDER_connect (cfg);
922 handle->idp_op = GNUNET_IDENTITY_PROVIDER_attribute_store (handle->idp,
927 GNUNET_free (attribute);
928 GNUNET_JSONAPI_document_delete (json_obj);
934 * Collect all attributes for an ego
938 attr_collect (void *cls,
939 const struct GNUNET_CRYPTO_EcdsaPublicKey *identity,
940 const struct GNUNET_IDENTITY_ATTRIBUTE_Claim *attr)
942 struct GNUNET_JSONAPI_Resource *json_resource;
943 struct RequestHandle *handle = cls;
947 if ((NULL == attr->name) || (NULL == attr->data))
949 GNUNET_IDENTITY_PROVIDER_get_attributes_next (handle->attr_it);
953 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding attribute: %s\n",
955 json_resource = GNUNET_JSONAPI_resource_new (GNUNET_REST_JSONAPI_IDENTITY_ATTRIBUTE,
957 GNUNET_JSONAPI_document_resource_add (handle->resp_object, json_resource);
959 tmp_value = GNUNET_IDENTITY_ATTRIBUTE_value_to_string (attr->type,
963 value = json_string (tmp_value);
965 GNUNET_JSONAPI_resource_add_attr (json_resource,
969 GNUNET_free(tmp_value);
970 GNUNET_IDENTITY_PROVIDER_get_attributes_next (handle->attr_it);
976 * List attributes for identity request
978 * @param con_handle the connection handle
980 * @param cls the RequestHandle
983 list_attribute_cont (struct GNUNET_REST_RequestHandle *con_handle,
987 const struct GNUNET_CRYPTO_EcdsaPrivateKey *priv_key;
988 struct RequestHandle *handle = cls;
989 struct EgoEntry *ego_entry;
992 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Getting attributes for %s.\n",
994 if ( strlen (GNUNET_REST_API_NS_IDENTITY_ATTRIBUTES) >=
995 strlen (handle->url))
997 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "No identity given.\n");
998 GNUNET_SCHEDULER_add_now (&do_error, handle);
1001 identity = handle->url + strlen (GNUNET_REST_API_NS_IDENTITY_ATTRIBUTES) + 1;
1003 for (ego_entry = handle->ego_head;
1005 ego_entry = ego_entry->next)
1006 if (0 == strcmp (identity, ego_entry->identifier))
1008 handle->resp_object = GNUNET_JSONAPI_document_new ();
1011 if (NULL == ego_entry)
1014 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Ego %s not found.\n",
1016 GNUNET_SCHEDULER_add_now (&return_response, handle);
1019 priv_key = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego);
1020 handle->idp = GNUNET_IDENTITY_PROVIDER_connect (cfg);
1021 handle->attr_it = GNUNET_IDENTITY_PROVIDER_get_attributes_start (handle->idp,
1027 &collect_finished_cb,
1033 revoke_ticket_cont (struct GNUNET_REST_RequestHandle *con_handle,
1037 const struct GNUNET_CRYPTO_EcdsaPrivateKey *identity_priv;
1038 const char* identity_str;
1039 const char* audience_str;
1040 const char* rnd_str;
1042 struct RequestHandle *handle = cls;
1043 struct EgoEntry *ego_entry;
1044 struct MHD_Response *resp;
1045 struct GNUNET_IDENTITY_PROVIDER_Ticket ticket;
1046 struct GNUNET_JSONAPI_Document *json_obj;
1047 struct GNUNET_JSONAPI_Resource *json_res;
1048 struct GNUNET_CRYPTO_EcdsaPublicKey tmp_pk;
1049 char term_data[handle->rest_handle->data_size+1];
1051 json_t *identity_json;
1052 json_t *audience_json;
1055 struct GNUNET_JSON_Specification docspec[] = {
1056 GNUNET_JSON_spec_jsonapi_document (&json_obj),
1057 GNUNET_JSON_spec_end()
1060 if (0 >= handle->rest_handle->data_size)
1062 GNUNET_SCHEDULER_add_now (&do_error, handle);
1066 term_data[handle->rest_handle->data_size] = '\0';
1067 GNUNET_memcpy (term_data,
1068 handle->rest_handle->data,
1069 handle->rest_handle->data_size);
1070 data_json = json_loads (term_data,
1073 GNUNET_assert (GNUNET_OK ==
1074 GNUNET_JSON_parse (data_json, docspec,
1076 json_decref (data_json);
1077 if (NULL == json_obj)
1079 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1080 "Unable to parse JSONAPI Object from %s\n",
1082 GNUNET_SCHEDULER_add_now (&do_error, handle);
1085 if (1 != GNUNET_JSONAPI_document_resource_count (json_obj))
1087 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1088 "Cannot create more than 1 resource! (Got %d)\n",
1089 GNUNET_JSONAPI_document_resource_count (json_obj));
1090 GNUNET_JSONAPI_document_delete (json_obj);
1091 GNUNET_SCHEDULER_add_now (&do_error, handle);
1094 json_res = GNUNET_JSONAPI_document_get_resource (json_obj, 0);
1095 if (GNUNET_NO == GNUNET_JSONAPI_resource_check_type (json_res,
1096 GNUNET_REST_JSONAPI_IDENTITY_TICKET))
1098 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1099 "Unsupported JSON data type\n");
1100 GNUNET_JSONAPI_document_delete (json_obj);
1101 resp = GNUNET_REST_create_response (NULL);
1102 handle->proc (handle->proc_cls, resp, MHD_HTTP_CONFLICT);
1103 cleanup_handle (handle);
1106 rnd_json = GNUNET_JSONAPI_resource_read_attr (json_res,
1108 identity_json = GNUNET_JSONAPI_resource_read_attr (json_res,
1110 audience_json = GNUNET_JSONAPI_resource_read_attr (json_res,
1112 rnd_str = json_string_value (rnd_json);
1113 identity_str = json_string_value (identity_json);
1114 audience_str = json_string_value (audience_json);
1116 GNUNET_STRINGS_string_to_data (rnd_str,
1120 GNUNET_STRINGS_string_to_data (identity_str,
1121 strlen (identity_str),
1123 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));
1124 GNUNET_STRINGS_string_to_data (audience_str,
1125 strlen (audience_str),
1127 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));
1129 for (ego_entry = handle->ego_head;
1131 ego_entry = ego_entry->next)
1133 GNUNET_IDENTITY_ego_get_public_key (ego_entry->ego,
1135 if (0 == memcmp (&ticket.identity,
1137 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)))
1140 if (NULL == ego_entry)
1142 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1143 "Identity unknown (%s)\n", identity_str);
1144 GNUNET_JSONAPI_document_delete (json_obj);
1147 identity_priv = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego);
1149 handle->idp = GNUNET_IDENTITY_PROVIDER_connect (cfg);
1150 handle->idp_op = GNUNET_IDENTITY_PROVIDER_ticket_revoke (handle->idp,
1155 GNUNET_JSONAPI_document_delete (json_obj);
1159 consume_cont (void *cls,
1160 const struct GNUNET_CRYPTO_EcdsaPublicKey *identity,
1161 const struct GNUNET_IDENTITY_ATTRIBUTE_Claim *attr)
1163 struct RequestHandle *handle = cls;
1164 struct GNUNET_JSONAPI_Resource *json_resource;
1167 if (NULL == identity)
1169 GNUNET_SCHEDULER_add_now (&return_response, handle);
1173 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding attribute: %s\n",
1175 json_resource = GNUNET_JSONAPI_resource_new (GNUNET_REST_JSONAPI_IDENTITY_ATTRIBUTE,
1177 GNUNET_JSONAPI_document_resource_add (handle->resp_object, json_resource);
1179 value = json_string (attr->data);
1180 GNUNET_JSONAPI_resource_add_attr (json_resource,
1183 json_decref (value);
1187 consume_ticket_cont (struct GNUNET_REST_RequestHandle *con_handle,
1191 const struct GNUNET_CRYPTO_EcdsaPrivateKey *identity_priv;
1192 const char* identity_str;
1193 const char* audience_str;
1194 const char* rnd_str;
1196 struct RequestHandle *handle = cls;
1197 struct EgoEntry *ego_entry;
1198 struct MHD_Response *resp;
1199 struct GNUNET_IDENTITY_PROVIDER_Ticket ticket;
1200 struct GNUNET_JSONAPI_Document *json_obj;
1201 struct GNUNET_JSONAPI_Resource *json_res;
1202 struct GNUNET_CRYPTO_EcdsaPublicKey tmp_pk;
1203 char term_data[handle->rest_handle->data_size+1];
1205 json_t *identity_json;
1206 json_t *audience_json;
1209 struct GNUNET_JSON_Specification docspec[] = {
1210 GNUNET_JSON_spec_jsonapi_document (&json_obj),
1211 GNUNET_JSON_spec_end()
1214 if (0 >= handle->rest_handle->data_size)
1216 GNUNET_SCHEDULER_add_now (&do_error, handle);
1220 term_data[handle->rest_handle->data_size] = '\0';
1221 GNUNET_memcpy (term_data,
1222 handle->rest_handle->data,
1223 handle->rest_handle->data_size);
1224 data_json = json_loads (term_data,
1227 GNUNET_assert (GNUNET_OK ==
1228 GNUNET_JSON_parse (data_json, docspec,
1230 json_decref (data_json);
1231 if (NULL == json_obj)
1233 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1234 "Unable to parse JSONAPI Object from %s\n",
1236 GNUNET_SCHEDULER_add_now (&do_error, handle);
1239 if (1 != GNUNET_JSONAPI_document_resource_count (json_obj))
1241 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1242 "Cannot create more than 1 resource! (Got %d)\n",
1243 GNUNET_JSONAPI_document_resource_count (json_obj));
1244 GNUNET_JSONAPI_document_delete (json_obj);
1245 GNUNET_SCHEDULER_add_now (&do_error, handle);
1248 json_res = GNUNET_JSONAPI_document_get_resource (json_obj, 0);
1249 if (GNUNET_NO == GNUNET_JSONAPI_resource_check_type (json_res,
1250 GNUNET_REST_JSONAPI_IDENTITY_TICKET))
1252 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1253 "Unsupported JSON data type\n");
1254 GNUNET_JSONAPI_document_delete (json_obj);
1255 resp = GNUNET_REST_create_response (NULL);
1256 handle->proc (handle->proc_cls, resp, MHD_HTTP_CONFLICT);
1257 cleanup_handle (handle);
1260 rnd_json = GNUNET_JSONAPI_resource_read_attr (json_res,
1262 identity_json = GNUNET_JSONAPI_resource_read_attr (json_res,
1264 audience_json = GNUNET_JSONAPI_resource_read_attr (json_res,
1266 rnd_str = json_string_value (rnd_json);
1267 identity_str = json_string_value (identity_json);
1268 audience_str = json_string_value (audience_json);
1270 GNUNET_STRINGS_string_to_data (rnd_str,
1274 GNUNET_STRINGS_string_to_data (identity_str,
1275 strlen (identity_str),
1277 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));
1278 GNUNET_STRINGS_string_to_data (audience_str,
1279 strlen (audience_str),
1281 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));
1283 for (ego_entry = handle->ego_head;
1285 ego_entry = ego_entry->next)
1287 GNUNET_IDENTITY_ego_get_public_key (ego_entry->ego,
1289 if (0 == memcmp (&ticket.audience,
1291 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)))
1294 if (NULL == ego_entry)
1296 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1297 "Identity unknown (%s)\n", identity_str);
1298 GNUNET_JSONAPI_document_delete (json_obj);
1301 identity_priv = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego);
1302 handle->resp_object = GNUNET_JSONAPI_document_new ();
1303 handle->idp = GNUNET_IDENTITY_PROVIDER_connect (cfg);
1304 handle->idp_op = GNUNET_IDENTITY_PROVIDER_ticket_consume (handle->idp,
1309 GNUNET_JSONAPI_document_delete (json_obj);
1315 * Respond to OPTIONS request
1317 * @param con_handle the connection handle
1318 * @param url the url
1319 * @param cls the RequestHandle
1322 options_cont (struct GNUNET_REST_RequestHandle *con_handle,
1326 struct MHD_Response *resp;
1327 struct RequestHandle *handle = cls;
1329 //For now, independent of path return all options
1330 resp = GNUNET_REST_create_response (NULL);
1331 MHD_add_response_header (resp,
1332 "Access-Control-Allow-Methods",
1334 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
1335 cleanup_handle (handle);
1340 * Interprets cookie header and pass its identity keystring to handle
1343 cookie_identity_interpretation (struct RequestHandle *handle)
1345 struct GNUNET_HashCode cache_key;
1347 struct GNUNET_TIME_Absolute current_time, *relog_time;
1348 char delimiter[] = "; ";
1350 //gets identity of login try with cookie
1351 GNUNET_CRYPTO_hash (OIDC_COOKIE_HEADER_KEY, strlen (OIDC_COOKIE_HEADER_KEY),
1353 if ( GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->header_param_map,
1356 //splits cookies and find 'Identity' cookie
1357 cookies = GNUNET_CONTAINER_multihashmap_get ( handle->rest_handle->header_param_map, &cache_key);
1358 handle->oidc->login_identity = strtok(cookies, delimiter);
1360 while ( NULL != handle->oidc->login_identity )
1362 if ( NULL != strstr (handle->oidc->login_identity, OIDC_COOKIE_HEADER_INFORMATION_KEY) )
1366 handle->oidc->login_identity = strtok (NULL, delimiter);
1368 GNUNET_CRYPTO_hash (handle->oidc->login_identity, strlen (handle->oidc->login_identity),
1370 if ( GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (OIDC_identity_login_time, &cache_key) )
1372 relog_time = GNUNET_CONTAINER_multihashmap_get (OIDC_identity_login_time,
1374 current_time = GNUNET_TIME_absolute_get ();
1375 // 30 min after old login -> redirect to login
1376 if ( current_time.abs_value_us <= relog_time->abs_value_us )
1378 handle->oidc->login_identity = strtok(handle->oidc->login_identity, OIDC_COOKIE_HEADER_INFORMATION_KEY);
1379 handle->oidc->login_identity = GNUNET_strdup(handle->oidc->login_identity);
1384 handle->oidc->login_identity = NULL;
1390 * Redirects to login page stored in configuration file
1393 login_redirection(void *cls)
1395 char *login_base_url;
1397 struct MHD_Response *resp;
1398 struct RequestHandle *handle = cls;
1401 == GNUNET_CONFIGURATION_get_value_string (cfg, "identity-rest-plugin",
1402 "address", &login_base_url) )
1404 GNUNET_asprintf (&new_redirect, "%s?%s=%s&%s=%s&%s=%s&%s=%s&%s=%s&%s=%s",
1406 OIDC_RESPONSE_TYPE_KEY,
1407 handle->oidc->response_type,
1409 handle->oidc->client_id,
1410 OIDC_REDIRECT_URI_KEY,
1411 handle->oidc->redirect_uri,
1413 handle->oidc->scope,
1415 (NULL != handle->oidc->state) ? handle->oidc->state : "",
1417 (NULL != handle->oidc->nonce) ? handle->oidc->nonce : "");
1418 resp = GNUNET_REST_create_response ("");
1419 MHD_add_response_header (resp, "Location", new_redirect);
1420 GNUNET_free(login_base_url);
1424 handle->emsg = GNUNET_strdup("server_error");
1425 handle->edesc = GNUNET_strdup ("gnunet configuration failed");
1426 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1427 GNUNET_SCHEDULER_add_now (&do_error, handle);
1430 handle->proc (handle->proc_cls, resp, MHD_HTTP_FOUND);
1431 GNUNET_free(new_redirect);
1432 GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
1436 * Does internal server error when iteration failed.
1439 oidc_iteration_error (void *cls)
1441 struct RequestHandle *handle = cls;
1442 handle->emsg = GNUNET_strdup("INTERNAL_SERVER_ERROR");
1443 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1444 GNUNET_SCHEDULER_add_now (&do_error, handle);
1448 * Issues ticket and redirects to relying party with the authorization code as
1449 * parameter. Otherwise redirects with error
1452 oidc_ticket_issue_cb (void* cls,
1453 const struct GNUNET_IDENTITY_PROVIDER_Ticket *ticket)
1455 struct RequestHandle *handle = cls;
1456 struct MHD_Response *resp;
1459 char *code_json_string;
1460 char *code_base64_final_string;
1461 handle->idp_op = NULL;
1462 resp = GNUNET_REST_create_response ("");
1463 if (NULL != ticket) {
1464 ticket_str = GNUNET_STRINGS_data_to_string_alloc (ticket,
1465 sizeof (struct GNUNET_IDENTITY_PROVIDER_Ticket));
1466 //TODO change if more attributes are needed (see max_age)
1467 GNUNET_asprintf (&code_json_string, "{\"ticket\":\"%s\"%s%s%s}",
1469 (NULL != handle->oidc->nonce) ? ", \"nonce\":\"" : "",
1470 (NULL != handle->oidc->nonce) ? handle->oidc->nonce : "",
1471 (NULL != handle->oidc->nonce) ? "\"" : "");
1472 code_base64_final_string = base_64_encode(code_json_string);
1473 GNUNET_asprintf (&redirect_uri, "%s?%s=%s&state=%s",
1474 handle->oidc->redirect_uri,
1475 handle->oidc->response_type,
1476 code_base64_final_string, handle->oidc->state);
1477 MHD_add_response_header (resp, "Location", redirect_uri);
1478 handle->proc (handle->proc_cls, resp, MHD_HTTP_FOUND);
1479 GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
1480 GNUNET_free (redirect_uri);
1481 GNUNET_free (ticket_str);
1482 GNUNET_free (code_json_string);
1483 GNUNET_free (code_base64_final_string);
1486 handle->emsg = GNUNET_strdup("server_error");
1487 handle->edesc = GNUNET_strdup("Server cannot generate ticket.");
1488 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1492 oidc_collect_finished_cb (void *cls)
1494 struct RequestHandle *handle = cls;
1495 handle->attr_it = NULL;
1496 handle->ticket_it = NULL;
1497 if (NULL == handle->attr_list->list_head)
1499 handle->emsg = GNUNET_strdup("invalid_scope");
1500 handle->edesc = GNUNET_strdup("The requested scope is not available.");
1501 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1504 handle->idp_op = GNUNET_IDENTITY_PROVIDER_ticket_issue (handle->idp,
1506 &handle->oidc->client_pkey,
1508 &oidc_ticket_issue_cb,
1514 * Collects all attributes for an ego if in scope parameter
1517 oidc_attr_collect (void *cls,
1518 const struct GNUNET_CRYPTO_EcdsaPublicKey *identity,
1519 const struct GNUNET_IDENTITY_ATTRIBUTE_Claim *attr)
1521 struct RequestHandle *handle = cls;
1522 struct GNUNET_IDENTITY_ATTRIBUTE_ClaimListEntry *le;
1523 char* scope_variables;
1524 char* scope_variable;
1525 char delimiter[]=" ";
1527 if ( (NULL == attr->name) || (NULL == attr->data) )
1529 GNUNET_IDENTITY_PROVIDER_get_attributes_next (handle->attr_it);
1533 scope_variables = GNUNET_strdup(handle->oidc->scope);
1534 scope_variable = strtok (scope_variables, delimiter);
1535 while (NULL != scope_variable)
1537 if ( 0 == strcmp (attr->name, scope_variable) )
1541 scope_variable = strtok (NULL, delimiter);
1543 if ( NULL == scope_variable )
1545 GNUNET_IDENTITY_PROVIDER_get_attributes_next (handle->attr_it);
1546 GNUNET_free(scope_variables);
1549 GNUNET_free(scope_variables);
1551 le = GNUNET_new(struct GNUNET_IDENTITY_ATTRIBUTE_ClaimListEntry);
1552 le->claim = GNUNET_IDENTITY_ATTRIBUTE_claim_new (attr->name, attr->type,
1553 attr->data, attr->data_size);
1554 GNUNET_CONTAINER_DLL_insert(handle->attr_list->list_head,
1555 handle->attr_list->list_tail, le);
1556 GNUNET_IDENTITY_PROVIDER_get_attributes_next (handle->attr_it);
1561 * Checks time and cookie and redirects accordingly
1564 login_check (void *cls)
1566 struct RequestHandle *handle = cls;
1567 struct GNUNET_TIME_Absolute current_time, *relog_time;
1568 struct GNUNET_CRYPTO_EcdsaPublicKey pubkey, ego_pkey;
1569 struct GNUNET_HashCode cache_key;
1570 char *identity_cookie;
1572 GNUNET_asprintf (&identity_cookie, "Identity=%s", handle->oidc->login_identity);
1573 GNUNET_CRYPTO_hash (identity_cookie, strlen (identity_cookie), &cache_key);
1574 GNUNET_free(identity_cookie);
1575 //No login time for identity -> redirect to login
1577 == GNUNET_CONTAINER_multihashmap_contains (OIDC_identity_login_time,
1580 relog_time = GNUNET_CONTAINER_multihashmap_get (OIDC_identity_login_time,
1582 current_time = GNUNET_TIME_absolute_get ();
1583 // 30 min after old login -> redirect to login
1584 if ( current_time.abs_value_us <= relog_time->abs_value_us )
1587 != GNUNET_CRYPTO_ecdsa_public_key_from_string (
1588 handle->oidc->login_identity,
1589 strlen (handle->oidc->login_identity), &pubkey) )
1591 handle->emsg = GNUNET_strdup("invalid_cookie");
1592 handle->edesc = GNUNET_strdup(
1593 "The cookie of a login identity is not valid");
1594 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1597 // iterate over egos and compare their public key
1598 for (handle->ego_entry = handle->ego_head;
1599 NULL != handle->ego_entry; handle->ego_entry = handle->ego_entry->next)
1601 GNUNET_IDENTITY_ego_get_public_key (handle->ego_entry->ego, &ego_pkey);
1603 == memcmp (&ego_pkey, &pubkey,
1604 sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey)) )
1606 handle->priv_key = *GNUNET_IDENTITY_ego_get_private_key (
1607 handle->ego_entry->ego);
1608 handle->resp_object = GNUNET_JSONAPI_document_new ();
1609 handle->idp = GNUNET_IDENTITY_PROVIDER_connect (cfg);
1610 handle->attr_list = GNUNET_new(
1611 struct GNUNET_IDENTITY_ATTRIBUTE_ClaimList);
1612 handle->attr_it = GNUNET_IDENTITY_PROVIDER_get_attributes_start (
1613 handle->idp, &handle->priv_key, &oidc_iteration_error, handle,
1614 &oidc_attr_collect, handle, &oidc_collect_finished_cb, handle);
1618 handle->emsg = GNUNET_strdup("invalid_cookie");
1619 handle->edesc = GNUNET_strdup(
1620 "The cookie of the login identity is not valid");
1621 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1628 * Searches for client_id in namestore. If found trust status stored in handle
1629 * Else continues to search
1631 * @param handle the RequestHandle
1634 namestore_iteration_callback (
1635 void *cls, const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone_key,
1636 const char *rname, unsigned int rd_len,
1637 const struct GNUNET_GNSRECORD_Data *rd)
1639 struct RequestHandle *handle = cls;
1640 struct GNUNET_CRYPTO_EcdsaPublicKey login_identity_pkey;
1641 struct GNUNET_CRYPTO_EcdsaPublicKey current_zone_pkey;
1644 for (i = 0; i < rd_len; i++)
1646 if ( GNUNET_GNSRECORD_TYPE_PKEY != rd[i].record_type )
1649 if ( NULL != handle->oidc->login_identity )
1651 GNUNET_CRYPTO_ecdsa_public_key_from_string (
1652 handle->oidc->login_identity,
1653 strlen (handle->oidc->login_identity),
1654 &login_identity_pkey);
1655 GNUNET_IDENTITY_ego_get_public_key (handle->ego_entry->ego,
1656 ¤t_zone_pkey);
1658 if ( 0 == memcmp (rd[i].data, &handle->oidc->client_pkey,
1659 sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey)) )
1661 if ( 0 == memcmp (&login_identity_pkey, ¤t_zone_pkey,
1662 sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey)) )
1664 handle->oidc->is_client_trusted = GNUNET_YES;
1670 if ( 0 == memcmp (rd[i].data, &handle->oidc->client_pkey,
1671 sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey)) )
1673 handle->oidc->is_client_trusted = GNUNET_YES;
1678 GNUNET_NAMESTORE_zone_iterator_next (handle->namestore_handle_it);
1682 * Iteration over all results finished, build final
1685 * @param cls the `struct RequestHandle`
1687 static void namestore_iteration_finished (void *cls)
1689 struct RequestHandle *handle = cls;
1690 struct GNUNET_HashCode cache_key;
1692 char *expected_redirect_uri;
1693 char *expected_scope;
1694 char delimiter[]=" ";
1695 int number_of_ignored_parameter, iterator;
1698 handle->ego_entry = handle->ego_entry->next;
1700 if(NULL != handle->ego_entry)
1702 handle->priv_key = *GNUNET_IDENTITY_ego_get_private_key (handle->ego_entry->ego);
1703 handle->namestore_handle_it = GNUNET_NAMESTORE_zone_iteration_start (handle->namestore_handle, &handle->priv_key,
1704 &oidc_iteration_error, handle, &namestore_iteration_callback, handle,
1705 &namestore_iteration_finished, handle);
1708 if (GNUNET_NO == handle->oidc->is_client_trusted)
1710 handle->emsg = GNUNET_strdup("unauthorized_client");
1711 handle->edesc = GNUNET_strdup("The client is not authorized to request an "
1712 "authorization code using this method.");
1713 GNUNET_SCHEDULER_add_now (&do_error, handle);
1717 // REQUIRED value: redirect_uri
1718 GNUNET_CRYPTO_hash (OIDC_REDIRECT_URI_KEY, strlen (OIDC_REDIRECT_URI_KEY),
1720 if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map,
1723 handle->emsg=GNUNET_strdup("invalid_request");
1724 handle->edesc=GNUNET_strdup("missing parameter redirect_uri");
1725 GNUNET_SCHEDULER_add_now (&do_error, handle);
1728 handle->oidc->redirect_uri = GNUNET_CONTAINER_multihashmap_get(handle->rest_handle->url_param_map,
1731 GNUNET_asprintf (&expected_redirect_uri, "https://%s.zkey", handle->oidc->client_id);
1732 // verify the redirect uri matches https://<client_id>.zkey[/xyz]
1733 if( 0 != strncmp( expected_redirect_uri, handle->oidc->redirect_uri, strlen(expected_redirect_uri)) )
1735 handle->oidc->redirect_uri = NULL;
1736 handle->emsg=GNUNET_strdup("invalid_request");
1737 handle->edesc=GNUNET_strdup("Invalid redirect_uri");
1738 GNUNET_SCHEDULER_add_now (&do_error, handle);
1739 GNUNET_free(expected_redirect_uri);
1742 handle->oidc->redirect_uri = GNUNET_strdup(handle->oidc->redirect_uri);
1744 GNUNET_free(expected_redirect_uri);
1745 // REQUIRED value: response_type
1746 GNUNET_CRYPTO_hash (OIDC_RESPONSE_TYPE_KEY, strlen (OIDC_RESPONSE_TYPE_KEY),
1748 if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map,
1751 handle->emsg=GNUNET_strdup("invalid_request");
1752 handle->edesc=GNUNET_strdup("missing parameter response_type");
1753 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1756 handle->oidc->response_type = GNUNET_CONTAINER_multihashmap_get(handle->rest_handle->url_param_map,
1758 handle->oidc->response_type = GNUNET_strdup (handle->oidc->response_type);
1760 // REQUIRED value: scope
1761 GNUNET_CRYPTO_hash (OIDC_SCOPE_KEY, strlen (OIDC_SCOPE_KEY), &cache_key);
1762 if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map,
1765 handle->emsg=GNUNET_strdup("invalid_request");
1766 handle->edesc=GNUNET_strdup("missing parameter scope");
1767 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1770 handle->oidc->scope = GNUNET_CONTAINER_multihashmap_get(handle->rest_handle->url_param_map,
1772 handle->oidc->scope = GNUNET_strdup(handle->oidc->scope);
1774 //OPTIONAL value: nonce
1775 GNUNET_CRYPTO_hash (OIDC_NONCE_KEY, strlen (OIDC_NONCE_KEY), &cache_key);
1776 if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map,
1779 handle->oidc->nonce = GNUNET_CONTAINER_multihashmap_get(handle->rest_handle->url_param_map,
1781 handle->oidc->nonce = GNUNET_strdup (handle->oidc->nonce);
1784 //TODO check other values if needed
1785 number_of_ignored_parameter = sizeof(OIDC_ignored_parameter_array) / sizeof(char *);
1786 for( iterator = 0; iterator < number_of_ignored_parameter; iterator++ )
1788 GNUNET_CRYPTO_hash (OIDC_ignored_parameter_array[iterator],
1789 strlen(OIDC_ignored_parameter_array[iterator]),
1791 if(GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains(handle->rest_handle->url_param_map,
1794 handle->emsg=GNUNET_strdup("access_denied");
1795 GNUNET_asprintf (&handle->edesc, "Server will not handle parameter: %s",
1796 OIDC_ignored_parameter_array[iterator]);
1797 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1802 // Checks if response_type is 'code'
1803 if( 0 != strcmp( handle->oidc->response_type, OIDC_EXPECTED_AUTHORIZATION_RESPONSE_TYPE ) )
1805 handle->emsg=GNUNET_strdup("unsupported_response_type");
1806 handle->edesc=GNUNET_strdup("The authorization server does not support "
1807 "obtaining this authorization code.");
1808 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1812 // Checks if scope contains 'openid'
1813 expected_scope = GNUNET_strdup(handle->oidc->scope);
1815 test = strtok (expected_scope, delimiter);
1816 while (NULL != test)
1818 if ( 0 == strcmp (OIDC_EXPECTED_AUTHORIZATION_SCOPE, expected_scope) )
1822 test = strtok (NULL, delimiter);
1826 handle->emsg = GNUNET_strdup("invalid_scope");
1827 handle->edesc=GNUNET_strdup("The requested scope is invalid, unknown, or "
1829 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1830 GNUNET_free(expected_scope);
1834 GNUNET_free(expected_scope);
1836 if( NULL != handle->oidc->login_identity )
1838 GNUNET_SCHEDULER_add_now(&login_check,handle);
1842 GNUNET_SCHEDULER_add_now(&login_redirection,handle);
1846 * Responds to authorization GET and url-encoded POST request
1848 * @param con_handle the connection handle
1849 * @param url the url
1850 * @param cls the RequestHandle
1853 authorize_endpoint (struct GNUNET_REST_RequestHandle *con_handle,
1857 struct RequestHandle *handle = cls;
1858 struct GNUNET_HashCode cache_key;
1860 cookie_identity_interpretation(handle);
1862 //RECOMMENDED value: state - REQUIRED for answers
1863 GNUNET_CRYPTO_hash (OIDC_STATE_KEY, strlen (OIDC_STATE_KEY), &cache_key);
1864 if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map,
1867 handle->oidc->state = GNUNET_CONTAINER_multihashmap_get(handle->rest_handle->url_param_map,
1869 handle->oidc->state = GNUNET_strdup (handle->oidc->state);
1872 // REQUIRED value: client_id
1873 GNUNET_CRYPTO_hash (OIDC_CLIENT_ID_KEY, strlen (OIDC_CLIENT_ID_KEY),
1875 if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map,
1878 handle->emsg=GNUNET_strdup("invalid_request");
1879 handle->edesc=GNUNET_strdup("missing parameter client_id");
1880 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1881 GNUNET_SCHEDULER_add_now (&do_error, handle);
1884 handle->oidc->client_id = GNUNET_CONTAINER_multihashmap_get(handle->rest_handle->url_param_map,
1886 handle->oidc->client_id = GNUNET_strdup (handle->oidc->client_id);
1889 != GNUNET_CRYPTO_ecdsa_public_key_from_string (handle->oidc->client_id,
1890 strlen (handle->oidc->client_id),
1891 &handle->oidc->client_pkey) )
1893 handle->emsg = GNUNET_strdup("unauthorized_client");
1894 handle->edesc = GNUNET_strdup("The client is not authorized to request an "
1895 "authorization code using this method.");
1896 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1897 GNUNET_SCHEDULER_add_now (&do_error, handle);
1902 if ( NULL == handle->ego_head )
1904 handle->emsg = GNUNET_strdup("server_error");
1905 handle->edesc = GNUNET_strdup ("Egos are missing");
1906 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1907 GNUNET_SCHEDULER_add_now (&do_error, handle);
1911 handle->ego_entry = handle->ego_head;
1912 handle->priv_key = *GNUNET_IDENTITY_ego_get_private_key (handle->ego_head->ego);
1913 handle->oidc->is_client_trusted = GNUNET_NO;
1915 // Checks if client_id is valid:
1916 handle->namestore_handle_it = GNUNET_NAMESTORE_zone_iteration_start (
1917 handle->namestore_handle, &handle->priv_key, &oidc_iteration_error,
1918 handle, &namestore_iteration_callback, handle,
1919 &namestore_iteration_finished, handle);
1923 * Combines an identity with a login time and responds OK to login request
1925 * @param con_handle the connection handle
1926 * @param url the url
1927 * @param cls the RequestHandle
1930 login_cont (struct GNUNET_REST_RequestHandle *con_handle,
1934 struct MHD_Response *resp = GNUNET_REST_create_response ("");
1935 struct RequestHandle *handle = cls;
1936 struct GNUNET_HashCode cache_key;
1937 struct GNUNET_TIME_Absolute *current_time;
1938 struct GNUNET_TIME_Absolute *last_time;
1943 root = json_loads (handle->rest_handle->data, 0, &error);
1944 identity = json_object_get (root, "identity");
1945 if ( json_is_string(identity) )
1947 GNUNET_asprintf (&cookie, "Identity=%s", json_string_value (identity));
1948 MHD_add_response_header (resp, "Set-Cookie", cookie);
1949 MHD_add_response_header (resp, "Access-Control-Allow-Methods", "POST");
1950 GNUNET_CRYPTO_hash (cookie, strlen (cookie), &cache_key);
1952 current_time = GNUNET_new(struct GNUNET_TIME_Absolute);
1953 *current_time = GNUNET_TIME_relative_to_absolute (
1954 GNUNET_TIME_relative_multiply (GNUNET_TIME_relative_get_minute_ (),
1956 last_time = GNUNET_CONTAINER_multihashmap_get(OIDC_identity_login_time, &cache_key);
1957 if (NULL != last_time)
1959 GNUNET_free(last_time);
1961 GNUNET_CONTAINER_multihashmap_put (
1962 OIDC_identity_login_time, &cache_key, current_time,
1963 GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE);
1965 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
1966 GNUNET_free(cookie);
1970 handle->proc (handle->proc_cls, resp, MHD_HTTP_BAD_REQUEST);
1973 GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
1978 * Responds to token url-encoded POST request
1980 * @param con_handle the connection handle
1981 * @param url the url
1982 * @param cls the RequestHandle
1985 token_endpoint (struct GNUNET_REST_RequestHandle *con_handle,
1989 //TODO static strings
1990 struct RequestHandle *handle = cls;
1991 struct GNUNET_HashCode cache_key;
1992 char *authorization, *credentials;
1993 char delimiter[]=" ";
1994 char delimiter_user_psw[]=":";
1995 char *grant_type, *code, *redirect_uri, *expected_redirect_uri;
1996 char *user_psw = NULL, *client_id, *psw;
1998 int client_exists = GNUNET_NO;
1999 struct MHD_Response *resp;
2001 json_t *root, *ticket_string, *nonce, *max_age;
2003 char *json_response;
2006 * Check Authorization
2008 GNUNET_CRYPTO_hash (OIDC_AUTHORIZATION_HEADER_KEY,
2009 strlen (OIDC_AUTHORIZATION_HEADER_KEY),
2011 if ( GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->header_param_map,
2014 handle->emsg=GNUNET_strdup("invalid_client");
2015 handle->edesc=GNUNET_strdup("missing authorization");
2016 handle->response_code = MHD_HTTP_UNAUTHORIZED;
2017 GNUNET_SCHEDULER_add_now (&do_error, handle);
2020 authorization = GNUNET_CONTAINER_multihashmap_get ( handle->rest_handle->header_param_map, &cache_key);
2022 //split header in "Basic" and [content]
2023 credentials = strtok (authorization, delimiter);
2024 if (0 != strcmp ("Basic",credentials))
2026 handle->emsg=GNUNET_strdup("invalid_client");
2027 handle->response_code = MHD_HTTP_UNAUTHORIZED;
2028 GNUNET_SCHEDULER_add_now (&do_error, handle);
2031 credentials = strtok(NULL, delimiter);
2032 if (NULL == credentials)
2034 handle->emsg=GNUNET_strdup("invalid_client");
2035 handle->response_code = MHD_HTTP_UNAUTHORIZED;
2036 GNUNET_SCHEDULER_add_now (&do_error, handle);
2039 GNUNET_STRINGS_base64_decode (credentials, strlen (credentials), &user_psw);
2041 if ( NULL == user_psw )
2043 handle->emsg=GNUNET_strdup("invalid_client");
2044 handle->response_code = MHD_HTTP_UNAUTHORIZED;
2045 GNUNET_SCHEDULER_add_now (&do_error, handle);
2048 client_id = strtok (user_psw, delimiter_user_psw);
2049 if ( NULL == client_id )
2051 GNUNET_free_non_null(user_psw);
2052 handle->emsg=GNUNET_strdup("invalid_client");
2053 handle->response_code = MHD_HTTP_UNAUTHORIZED;
2054 GNUNET_SCHEDULER_add_now (&do_error, handle);
2057 psw = strtok (NULL, delimiter_user_psw);
2060 GNUNET_free_non_null(user_psw);
2061 handle->emsg=GNUNET_strdup("invalid_client");
2062 handle->response_code = MHD_HTTP_UNAUTHORIZED;
2063 GNUNET_SCHEDULER_add_now (&do_error, handle);
2067 //check client password
2069 == GNUNET_CONFIGURATION_get_value_string (cfg, "identity-rest-plugin",
2070 "psw", &expected_psw) )
2072 if (0 != strcmp (expected_psw, psw))
2074 GNUNET_free_non_null(user_psw);
2075 GNUNET_free(expected_psw);
2076 handle->emsg=GNUNET_strdup("invalid_client");
2077 handle->response_code = MHD_HTTP_UNAUTHORIZED;
2078 GNUNET_SCHEDULER_add_now (&do_error, handle);
2081 GNUNET_free(expected_psw);
2085 GNUNET_free_non_null(user_psw);
2086 handle->emsg = GNUNET_strdup("server_error");
2087 handle->edesc = GNUNET_strdup ("gnunet configuration failed");
2088 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
2089 GNUNET_SCHEDULER_add_now (&do_error, handle);
2094 for (handle->ego_entry = handle->ego_head; NULL != handle->ego_entry->next; )
2096 if ( 0 == strcmp(handle->ego_entry->keystring, client_id))
2098 client_exists = GNUNET_YES;
2101 handle->ego_entry = handle->ego_entry->next;
2103 if (GNUNET_NO == client_exists)
2105 GNUNET_free_non_null(user_psw);
2106 handle->emsg=GNUNET_strdup("invalid_client");
2107 handle->response_code = MHD_HTTP_UNAUTHORIZED;
2108 GNUNET_SCHEDULER_add_now (&do_error, handle);
2116 //TODO Do not allow multiple equal parameter names
2117 //REQUIRED grant_type
2118 GNUNET_CRYPTO_hash (OIDC_GRANT_TYPE_KEY, strlen (OIDC_GRANT_TYPE_KEY), &cache_key);
2120 == GNUNET_CONTAINER_multihashmap_contains (
2121 handle->rest_handle->url_param_map, &cache_key) )
2123 GNUNET_free_non_null(user_psw);
2124 handle->emsg = GNUNET_strdup("invalid_request");
2125 handle->edesc = GNUNET_strdup("missing parameter grant_type");
2126 handle->response_code = MHD_HTTP_BAD_REQUEST;
2127 GNUNET_SCHEDULER_add_now (&do_error, handle);
2130 grant_type = GNUNET_CONTAINER_multihashmap_get (
2131 handle->rest_handle->url_param_map, &cache_key);
2134 GNUNET_CRYPTO_hash (OIDC_CODE_KEY, strlen (OIDC_CODE_KEY), &cache_key);
2136 == GNUNET_CONTAINER_multihashmap_contains (
2137 handle->rest_handle->url_param_map, &cache_key) )
2139 GNUNET_free_non_null(user_psw);
2140 handle->emsg = GNUNET_strdup("invalid_request");
2141 handle->edesc = GNUNET_strdup("missing parameter code");
2142 handle->response_code = MHD_HTTP_BAD_REQUEST;
2143 GNUNET_SCHEDULER_add_now (&do_error, handle);
2146 code = GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->url_param_map,
2149 //REQUIRED redirect_uri
2150 GNUNET_CRYPTO_hash (OIDC_REDIRECT_URI_KEY, strlen (OIDC_REDIRECT_URI_KEY),
2153 == GNUNET_CONTAINER_multihashmap_contains (
2154 handle->rest_handle->url_param_map, &cache_key) )
2156 GNUNET_free_non_null(user_psw);
2157 handle->emsg = GNUNET_strdup("invalid_request");
2158 handle->edesc = GNUNET_strdup("missing parameter redirect_uri");
2159 handle->response_code = MHD_HTTP_BAD_REQUEST;
2160 GNUNET_SCHEDULER_add_now (&do_error, handle);
2163 redirect_uri = GNUNET_CONTAINER_multihashmap_get (
2164 handle->rest_handle->url_param_map, &cache_key);
2167 //Check parameter grant_type == "authorization_code"
2168 if (0 != strcmp(OIDC_GRANT_TYPE_VALUE, grant_type))
2170 GNUNET_free_non_null(user_psw);
2171 handle->emsg=GNUNET_strdup("unsupported_grant_type");
2172 handle->response_code = MHD_HTTP_BAD_REQUEST;
2173 GNUNET_SCHEDULER_add_now (&do_error, handle);
2176 // check redirect_uri
2177 GNUNET_asprintf (&expected_redirect_uri, "https://%s.zkey", client_id);
2178 // verify the redirect uri matches https://<client_id>.zkey[/xyz]
2179 if( 0 != strncmp( expected_redirect_uri, redirect_uri, strlen(expected_redirect_uri)) )
2181 GNUNET_free_non_null(user_psw);
2182 handle->emsg=GNUNET_strdup("invalid_request");
2183 handle->edesc=GNUNET_strdup("Invalid redirect_uri");
2184 handle->response_code = MHD_HTTP_BAD_REQUEST;
2185 GNUNET_SCHEDULER_add_now (&do_error, handle);
2186 GNUNET_free(expected_redirect_uri);
2189 GNUNET_free(expected_redirect_uri);
2190 GNUNET_CRYPTO_hash (code, strlen (code), &cache_key);
2193 == GNUNET_CONTAINER_multihashmap_put (OIDC_ticket_once,
2196 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY) )
2198 GNUNET_free_non_null(user_psw);
2199 handle->emsg = GNUNET_strdup("invalid_request");
2200 handle->edesc = GNUNET_strdup("Cannot use the same code more than once");
2201 handle->response_code = MHD_HTTP_BAD_REQUEST;
2202 GNUNET_SCHEDULER_add_now (&do_error, handle);
2207 GNUNET_STRINGS_base64_decode(code,strlen(code),&code_output);
2208 root = json_loads (code_output, 0, &error);
2209 GNUNET_free(code_output);
2210 ticket_string = json_object_get (root, "ticket");
2211 nonce = json_object_get (root, "nonce");
2212 max_age = json_object_get (root, "max_age");
2214 if(ticket_string == NULL && !json_is_string(ticket_string))
2216 GNUNET_free_non_null(user_psw);
2217 handle->emsg = GNUNET_strdup("invalid_request");
2218 handle->edesc = GNUNET_strdup("invalid code");
2219 handle->response_code = MHD_HTTP_BAD_REQUEST;
2220 GNUNET_SCHEDULER_add_now (&do_error, handle);
2224 struct GNUNET_IDENTITY_PROVIDER_Ticket *ticket = GNUNET_new(struct GNUNET_IDENTITY_PROVIDER_Ticket);
2226 != GNUNET_STRINGS_string_to_data (json_string_value(ticket_string),
2227 strlen (json_string_value(ticket_string)),
2229 sizeof(struct GNUNET_IDENTITY_PROVIDER_Ticket)))
2231 GNUNET_free_non_null(user_psw);
2232 handle->emsg = GNUNET_strdup("invalid_request");
2233 handle->edesc = GNUNET_strdup("invalid code");
2234 handle->response_code = MHD_HTTP_BAD_REQUEST;
2235 GNUNET_SCHEDULER_add_now (&do_error, handle);
2236 GNUNET_free(ticket);
2239 // this is the current client (relying party)
2240 struct GNUNET_CRYPTO_EcdsaPublicKey pub_key;
2241 GNUNET_IDENTITY_ego_get_public_key(handle->ego_entry->ego,&pub_key);
2242 if (0 != memcmp(&pub_key,&ticket->audience,sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey)))
2244 GNUNET_free_non_null(user_psw);
2245 handle->emsg = GNUNET_strdup("invalid_request");
2246 handle->edesc = GNUNET_strdup("invalid code");
2247 handle->response_code = MHD_HTTP_BAD_REQUEST;
2248 GNUNET_SCHEDULER_add_now (&do_error, handle);
2249 GNUNET_free(ticket);
2254 unsigned long long int expiration_time;
2256 != GNUNET_CONFIGURATION_get_value_number(cfg, "identity-rest-plugin",
2257 "expiration_time", &expiration_time) )
2259 GNUNET_free_non_null(user_psw);
2260 handle->emsg = GNUNET_strdup("server_error");
2261 handle->edesc = GNUNET_strdup ("gnunet configuration failed");
2262 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
2263 GNUNET_SCHEDULER_add_now (&do_error, handle);
2264 GNUNET_free(ticket);
2268 struct GNUNET_IDENTITY_ATTRIBUTE_ClaimList *cl = GNUNET_new (struct GNUNET_IDENTITY_ATTRIBUTE_ClaimList);
2269 //aud REQUIRED public key client_id must be there
2270 GNUNET_IDENTITY_ATTRIBUTE_list_add(cl,
2272 GNUNET_IDENTITY_ATTRIBUTE_TYPE_STRING,
2275 //exp REQUIRED time expired from config
2276 struct GNUNET_TIME_Absolute exp_time = GNUNET_TIME_relative_to_absolute (
2277 GNUNET_TIME_relative_multiply (GNUNET_TIME_relative_get_second_ (),
2279 const char* exp_time_string = GNUNET_STRINGS_absolute_time_to_string(exp_time);
2280 GNUNET_IDENTITY_ATTRIBUTE_list_add (cl,
2282 GNUNET_IDENTITY_ATTRIBUTE_TYPE_STRING,
2284 strlen(exp_time_string));
2285 //iat REQUIRED time now
2286 struct GNUNET_TIME_Absolute time_now = GNUNET_TIME_absolute_get();
2287 const char* time_now_string = GNUNET_STRINGS_absolute_time_to_string(time_now);
2288 GNUNET_IDENTITY_ATTRIBUTE_list_add (cl,
2290 GNUNET_IDENTITY_ATTRIBUTE_TYPE_STRING,
2292 strlen(time_now_string));
2293 //nonce only if nonce is provided
2294 if ( NULL != nonce && json_is_string(nonce) )
2296 GNUNET_IDENTITY_ATTRIBUTE_list_add (cl,
2298 GNUNET_IDENTITY_ATTRIBUTE_TYPE_STRING,
2299 json_string_value(nonce),
2300 strlen(json_string_value(nonce)));
2302 //auth_time only if max_age is provided
2303 if ( NULL != max_age && json_is_string(max_age) )
2305 GNUNET_IDENTITY_ATTRIBUTE_list_add (cl,
2307 GNUNET_IDENTITY_ATTRIBUTE_TYPE_STRING,
2308 json_string_value(max_age),
2309 strlen(json_string_value(max_age)));
2311 //TODO OPTIONAL acr,amr,azp
2313 struct EgoEntry *ego_entry;
2314 for (ego_entry = handle->ego_head; NULL != ego_entry; ego_entry = ego_entry->next)
2316 GNUNET_IDENTITY_ego_get_public_key (ego_entry->ego, &pub_key);
2317 if (0 == memcmp (&pub_key, &ticket->audience, sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey)))
2322 if ( NULL == ego_entry )
2324 GNUNET_free_non_null(user_psw);
2325 handle->emsg = GNUNET_strdup("invalid_request");
2326 handle->edesc = GNUNET_strdup("invalid code....");
2327 handle->response_code = MHD_HTTP_BAD_REQUEST;
2328 GNUNET_SCHEDULER_add_now (&do_error, handle);
2329 GNUNET_free(ticket);
2332 char *id_token = jwt_create_from_list(&ticket->audience,
2334 GNUNET_IDENTITY_ego_get_private_key(ego_entry->ego));
2336 //Create random access_token
2337 char* access_token_number;
2339 uint64_t random_number;
2340 random_number = GNUNET_CRYPTO_random_u64(GNUNET_CRYPTO_QUALITY_NONCE, UINT64_MAX);
2341 GNUNET_asprintf(&access_token_number, "%" PRIu64, random_number);
2342 GNUNET_STRINGS_base64_encode(access_token_number,strlen(access_token_number),&access_token);
2346 //TODO OPTIONAL add refresh_token and scope
2347 GNUNET_asprintf (&json_response,
2348 "{ \"access_token\" : \"%s\", "
2349 "\"token_type\" : \"Bearer\", "
2350 "\"expires_in\" : %d, "
2351 "\"id_token\" : \"%s\"}",
2355 GNUNET_CRYPTO_hash(access_token, strlen(access_token), &cache_key);
2356 char *id_ticket_combination;
2357 GNUNET_asprintf(&id_ticket_combination,
2360 json_string_value(ticket_string));
2361 GNUNET_CONTAINER_multihashmap_put(OIDC_interpret_access_token,
2363 id_ticket_combination,
2364 GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE);
2366 resp = GNUNET_REST_create_response (json_response);
2367 MHD_add_response_header (resp, "Cache-Control", "no-store");
2368 MHD_add_response_header (resp, "Pragma", "no-cache");
2369 MHD_add_response_header (resp, "Content-Type", "application/json");
2370 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
2372 GNUNET_IDENTITY_ATTRIBUTE_list_destroy(cl);
2373 GNUNET_free(access_token_number);
2374 GNUNET_free(access_token);
2375 GNUNET_free(user_psw);
2376 GNUNET_free(json_response);
2377 GNUNET_free(ticket);
2378 GNUNET_free(id_token);
2380 GNUNET_SCHEDULER_add_now(&cleanup_handle_delayed, handle);
2384 * Collects claims and stores them in handle
2387 consume_ticket (void *cls,
2388 const struct GNUNET_CRYPTO_EcdsaPublicKey *identity,
2389 const struct GNUNET_IDENTITY_ATTRIBUTE_Claim *attr)
2391 struct RequestHandle *handle = cls;
2393 if (NULL == identity)
2395 GNUNET_SCHEDULER_add_now (&return_userinfo_response, handle);
2399 json_object_set_new (handle->oidc->response,
2401 json_string(attr->data));
2405 * Responds to userinfo GET and url-encoded POST request
2407 * @param con_handle the connection handle
2408 * @param url the url
2409 * @param cls the RequestHandle
2412 userinfo_endpoint (struct GNUNET_REST_RequestHandle *con_handle,
2413 const char* url, void *cls)
2415 //TODO expiration time
2416 struct RequestHandle *handle = cls;
2417 char delimiter[] = " ";
2418 char delimiter_db[] = ";";
2419 struct GNUNET_HashCode cache_key;
2420 char *authorization, *authorization_type, *authorization_access_token;
2421 char *client_ticket, *client, *ticket_str;
2422 struct GNUNET_IDENTITY_PROVIDER_Ticket *ticket;
2424 GNUNET_CRYPTO_hash (OIDC_AUTHORIZATION_HEADER_KEY,
2425 strlen (OIDC_AUTHORIZATION_HEADER_KEY),
2428 == GNUNET_CONTAINER_multihashmap_contains (
2429 handle->rest_handle->header_param_map, &cache_key) )
2431 handle->emsg = GNUNET_strdup("invalid_token");
2432 handle->edesc = GNUNET_strdup("No Access Token");
2433 handle->response_code = MHD_HTTP_UNAUTHORIZED;
2434 GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
2437 authorization = GNUNET_CONTAINER_multihashmap_get (
2438 handle->rest_handle->header_param_map, &cache_key);
2440 //split header in "Bearer" and access_token
2441 authorization = GNUNET_strdup(authorization);
2442 authorization_type = strtok (authorization, delimiter);
2443 if ( 0 != strcmp ("Bearer", authorization_type) )
2445 handle->emsg = GNUNET_strdup("invalid_token");
2446 handle->edesc = GNUNET_strdup("No Access Token");
2447 handle->response_code = MHD_HTTP_UNAUTHORIZED;
2448 GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
2449 GNUNET_free(authorization);
2452 authorization_access_token = strtok (NULL, delimiter);
2453 if ( NULL == authorization_access_token )
2455 handle->emsg = GNUNET_strdup("invalid_token");
2456 handle->edesc = GNUNET_strdup("No Access Token");
2457 handle->response_code = MHD_HTTP_UNAUTHORIZED;
2458 GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
2459 GNUNET_free(authorization);
2463 GNUNET_CRYPTO_hash (authorization_access_token,
2464 strlen (authorization_access_token),
2466 if ( GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (OIDC_interpret_access_token,
2469 handle->emsg = GNUNET_strdup("invalid_token");
2470 handle->edesc = GNUNET_strdup("The Access Token expired");
2471 handle->response_code = MHD_HTTP_UNAUTHORIZED;
2472 GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
2473 GNUNET_free(authorization);
2477 client_ticket = GNUNET_CONTAINER_multihashmap_get(OIDC_interpret_access_token,
2479 client_ticket = GNUNET_strdup(client_ticket);
2480 client = strtok(client_ticket,delimiter_db);
2483 handle->emsg = GNUNET_strdup("invalid_token");
2484 handle->edesc = GNUNET_strdup("The Access Token expired");
2485 handle->response_code = MHD_HTTP_UNAUTHORIZED;
2486 GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
2487 GNUNET_free(authorization);
2488 GNUNET_free(client_ticket);
2491 handle->ego_entry = handle->ego_head;
2492 for(; NULL != handle->ego_entry; handle->ego_entry = handle->ego_entry->next)
2494 if (0 == strcmp(handle->ego_entry->keystring,client))
2499 if (NULL == handle->ego_entry)
2501 handle->emsg = GNUNET_strdup("invalid_token");
2502 handle->edesc = GNUNET_strdup("The Access Token expired");
2503 handle->response_code = MHD_HTTP_UNAUTHORIZED;
2504 GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
2505 GNUNET_free(authorization);
2506 GNUNET_free(client_ticket);
2509 ticket_str = strtok(NULL, delimiter_db);
2510 if (NULL == ticket_str)
2512 handle->emsg = GNUNET_strdup("invalid_token");
2513 handle->edesc = GNUNET_strdup("The Access Token expired");
2514 handle->response_code = MHD_HTTP_UNAUTHORIZED;
2515 GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
2516 GNUNET_free(authorization);
2517 GNUNET_free(client_ticket);
2520 ticket = GNUNET_new(struct GNUNET_IDENTITY_PROVIDER_Ticket);
2522 != GNUNET_STRINGS_string_to_data (ticket_str,
2523 strlen (ticket_str),
2525 sizeof(struct GNUNET_IDENTITY_PROVIDER_Ticket)))
2527 handle->emsg = GNUNET_strdup("invalid_token");
2528 handle->edesc = GNUNET_strdup("The Access Token expired");
2529 handle->response_code = MHD_HTTP_UNAUTHORIZED;
2530 GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
2531 GNUNET_free(ticket);
2532 GNUNET_free(authorization);
2533 GNUNET_free(client_ticket);
2537 handle->idp = GNUNET_IDENTITY_PROVIDER_connect (cfg);
2538 handle->oidc->response = json_object();
2539 json_object_set_new( handle->oidc->response, "sub", json_string( handle->ego_entry->keystring));
2540 handle->idp_op = GNUNET_IDENTITY_PROVIDER_ticket_consume (
2542 GNUNET_IDENTITY_ego_get_private_key (handle->ego_entry->ego),
2546 GNUNET_free(ticket);
2547 GNUNET_free(authorization);
2548 GNUNET_free(client_ticket);
2554 * Handle rest request
2556 * @param handle the request handle
2559 init_cont (struct RequestHandle *handle)
2561 struct GNUNET_REST_RequestHandlerError err;
2562 static const struct GNUNET_REST_RequestHandler handlers[] = {
2563 {MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_IDENTITY_ATTRIBUTES, &list_attribute_cont},
2564 {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_IDENTITY_ATTRIBUTES, &add_attribute_cont},
2565 {MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_IDENTITY_TICKETS, &list_tickets_cont},
2566 {MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_AUTHORIZE, &authorize_endpoint},
2567 {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_AUTHORIZE, &authorize_endpoint}, //url-encoded
2568 {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_LOGIN, &login_cont},
2569 {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_TOKEN, &token_endpoint },
2570 {MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_USERINFO, &userinfo_endpoint },
2571 {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_USERINFO, &userinfo_endpoint },
2572 {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_IDENTITY_REVOKE, &revoke_ticket_cont},
2573 {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_IDENTITY_CONSUME, &consume_ticket_cont},
2574 {MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_IDENTITY_PROVIDER,
2576 GNUNET_REST_HANDLER_END
2579 if (GNUNET_NO == GNUNET_REST_handle_request (handle->rest_handle,
2584 handle->response_code = err.error_code;
2585 GNUNET_SCHEDULER_add_now (&do_error, handle);
2590 * If listing is enabled, prints information about the egos.
2592 * This function is initially called for all egos and then again
2593 * whenever a ego's identifier changes or if it is deleted. At the
2594 * end of the initial pass over all egos, the function is once called
2595 * with 'NULL' for 'ego'. That does NOT mean that the callback won't
2596 * be invoked in the future or that there was an error.
2598 * When used with 'GNUNET_IDENTITY_create' or 'GNUNET_IDENTITY_get',
2599 * this function is only called ONCE, and 'NULL' being passed in
2600 * 'ego' does indicate an error (i.e. name is taken or no default
2601 * value is known). If 'ego' is non-NULL and if '*ctx'
2602 * is set in those callbacks, the value WILL be passed to a subsequent
2603 * call to the identity callback of 'GNUNET_IDENTITY_connect' (if
2604 * that one was not NULL).
2606 * When an identity is renamed, this function is called with the
2607 * (known) ego but the NEW identifier.
2609 * When an identity is deleted, this function is called with the
2610 * (known) ego and "NULL" for the 'identifier'. In this case,
2611 * the 'ego' is henceforth invalid (and the 'ctx' should also be
2614 * @param cls closure
2615 * @param ego ego handle
2616 * @param ctx context for application to store data for this ego
2617 * (during the lifetime of this process, initially NULL)
2618 * @param identifier identifier assigned by the user for this ego,
2619 * NULL if the user just deleted the ego and it
2620 * must thus no longer be used
2623 list_ego (void *cls,
2624 struct GNUNET_IDENTITY_Ego *ego,
2626 const char *identifier)
2628 struct RequestHandle *handle = cls;
2629 struct EgoEntry *ego_entry;
2630 struct GNUNET_CRYPTO_EcdsaPublicKey pk;
2632 if ((NULL == ego) && (ID_REST_STATE_INIT == handle->state))
2634 handle->state = ID_REST_STATE_POST_INIT;
2638 if (ID_REST_STATE_INIT == handle->state) {
2639 ego_entry = GNUNET_new (struct EgoEntry);
2640 GNUNET_IDENTITY_ego_get_public_key (ego, &pk);
2641 ego_entry->keystring =
2642 GNUNET_CRYPTO_ecdsa_public_key_to_string (&pk);
2643 ego_entry->ego = ego;
2644 ego_entry->identifier = GNUNET_strdup (identifier);
2645 GNUNET_CONTAINER_DLL_insert_tail(handle->ego_head,handle->ego_tail, ego_entry);
2651 rest_identity_process_request(struct GNUNET_REST_RequestHandle *rest_handle,
2652 GNUNET_REST_ResultProcessor proc,
2655 struct RequestHandle *handle = GNUNET_new (struct RequestHandle);
2656 handle->oidc = GNUNET_new (struct OIDC_Variables);
2657 if ( NULL == OIDC_identity_login_time )
2658 OIDC_identity_login_time = GNUNET_CONTAINER_multihashmap_create (10, GNUNET_NO);
2659 if ( NULL == OIDC_identity_grants )
2660 OIDC_identity_grants = GNUNET_CONTAINER_multihashmap_create (10, GNUNET_NO);
2661 if ( NULL == OIDC_ticket_once )
2662 OIDC_ticket_once = GNUNET_CONTAINER_multihashmap_create (10, GNUNET_NO);
2663 if ( NULL == OIDC_interpret_access_token )
2664 OIDC_interpret_access_token = GNUNET_CONTAINER_multihashmap_create (10, GNUNET_NO);
2665 handle->response_code = 0;
2666 handle->timeout = GNUNET_TIME_UNIT_FOREVER_REL;
2667 handle->proc_cls = proc_cls;
2668 handle->proc = proc;
2669 handle->state = ID_REST_STATE_INIT;
2670 handle->rest_handle = rest_handle;
2672 handle->url = GNUNET_strdup (rest_handle->url);
2673 if (handle->url[strlen (handle->url)-1] == '/')
2674 handle->url[strlen (handle->url)-1] = '\0';
2675 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2677 handle->identity_handle = GNUNET_IDENTITY_connect (cfg,
2680 handle->namestore_handle = GNUNET_NAMESTORE_connect (cfg);
2681 handle->timeout_task =
2682 GNUNET_SCHEDULER_add_delayed (handle->timeout,
2685 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2690 * Entry point for the plugin.
2692 * @param cls Config info
2693 * @return NULL on error, otherwise the plugin context
2696 libgnunet_plugin_rest_identity_provider_init (void *cls)
2698 static struct Plugin plugin;
2699 struct GNUNET_REST_Plugin *api;
2702 if (NULL != plugin.cfg)
2703 return NULL; /* can only initialize once! */
2704 memset (&plugin, 0, sizeof (struct Plugin));
2706 api = GNUNET_new (struct GNUNET_REST_Plugin);
2708 api->name = GNUNET_REST_API_NS_IDENTITY_PROVIDER;
2709 api->process_request = &rest_identity_process_request;
2710 GNUNET_asprintf (&allow_methods,
2711 "%s, %s, %s, %s, %s",
2712 MHD_HTTP_METHOD_GET,
2713 MHD_HTTP_METHOD_POST,
2714 MHD_HTTP_METHOD_PUT,
2715 MHD_HTTP_METHOD_DELETE,
2716 MHD_HTTP_METHOD_OPTIONS);
2718 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2719 _("Identity Provider REST API initialized\n"));
2725 * Exit point from the plugin.
2727 * @param cls the plugin context (as returned by "init")
2728 * @return always NULL
2731 libgnunet_plugin_rest_identity_provider_done (void *cls)
2733 struct GNUNET_REST_Plugin *api = cls;
2734 struct Plugin *plugin = api->cls;
2737 struct GNUNET_CONTAINER_MultiHashMapIterator *hashmap_it;
2739 hashmap_it = GNUNET_CONTAINER_multihashmap_iterator_create (
2740 OIDC_identity_login_time);
2741 while (GNUNET_YES ==
2742 GNUNET_CONTAINER_multihashmap_iterator_next (hashmap_it, NULL, value))
2747 GNUNET_CONTAINER_multihashmap_destroy(OIDC_identity_login_time);
2748 hashmap_it = GNUNET_CONTAINER_multihashmap_iterator_create (OIDC_identity_grants);
2749 while (GNUNET_YES ==
2750 GNUNET_CONTAINER_multihashmap_iterator_next (hashmap_it, NULL, value))
2755 GNUNET_CONTAINER_multihashmap_destroy(OIDC_identity_grants);
2756 hashmap_it = GNUNET_CONTAINER_multihashmap_iterator_create (OIDC_ticket_once);
2757 while (GNUNET_YES ==
2758 GNUNET_CONTAINER_multihashmap_iterator_next (hashmap_it, NULL, value))
2763 GNUNET_CONTAINER_multihashmap_destroy(OIDC_ticket_once);
2764 hashmap_it = GNUNET_CONTAINER_multihashmap_iterator_create (OIDC_interpret_access_token);
2765 while (GNUNET_YES ==
2766 GNUNET_CONTAINER_multihashmap_iterator_next (hashmap_it, NULL, value))
2771 GNUNET_CONTAINER_multihashmap_destroy(OIDC_interpret_access_token);
2772 GNUNET_CONTAINER_multihashmap_iterator_destroy(hashmap_it);
2773 GNUNET_free_non_null (allow_methods);
2775 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2776 "Identity Provider REST plugin is finished\n");
2780 /* end of plugin_rest_identity_provider.c */