From 8b22f677adfde6212410c5abb2aff27988cc997c Mon Sep 17 00:00:00 2001 From: "Schanzenbach, Martin" Date: Fri, 6 Oct 2017 19:31:46 +0200 Subject: [PATCH] -readd new rest plugin --- src/identity-provider/Makefile.am | 16 +- .../plugin_rest_identity_provider.c | 632 ++++++++++++++++++ src/identity-provider/test_idp.conf | 2 + src/identity-provider/test_idp_issue.sh | 2 + 4 files changed, 651 insertions(+), 1 deletion(-) create mode 100644 src/identity-provider/plugin_rest_identity_provider.c diff --git a/src/identity-provider/Makefile.am b/src/identity-provider/Makefile.am index 0aabc2143..64a54457d 100644 --- a/src/identity-provider/Makefile.am +++ b/src/identity-provider/Makefile.am @@ -26,7 +26,8 @@ pkgcfg_DATA = \ lib_LTLIBRARIES = \ libgnunetidentityprovider.la plugin_LTLIBRARIES = \ - libgnunet_plugin_gnsrecord_identity_provider.la \ + libgnunet_plugin_rest_identity_provider.la \ + libgnunet_plugin_gnsrecord_identity_provider.la \ $(SQLITE_PLUGIN) bin_PROGRAMS = \ @@ -82,6 +83,19 @@ libgnunetidentityprovider_la_LDFLAGS = \ $(GN_LIB_LDFLAGS) $(WINFLAGS) \ -version-info 0:0:0 +libgnunet_plugin_rest_identity_provider_la_SOURCES = \ + plugin_rest_identity_provider.c +libgnunet_plugin_rest_identity_provider_la_LIBADD = \ + $(top_builddir)/src/identity/libgnunetidentity.la \ + libgnunetidentityprovider.la \ + $(top_builddir)/src/rest/libgnunetrest.la \ + $(top_builddir)/src/jsonapi/libgnunetjsonapi.la \ + $(top_builddir)/src/namestore/libgnunetnamestore.la \ + $(top_builddir)/src/util/libgnunetutil.la $(XLIBS) \ + $(LTLIBINTL) -ljansson -lmicrohttpd +libgnunet_plugin_rest_identity_provider_la_LDFLAGS = \ + $(GN_PLUGIN_LDFLAGS) + gnunet_idp_SOURCES = \ gnunet-idp.c gnunet_idp_LDADD = \ diff --git a/src/identity-provider/plugin_rest_identity_provider.c b/src/identity-provider/plugin_rest_identity_provider.c new file mode 100644 index 000000000..b8fb3d1cb --- /dev/null +++ b/src/identity-provider/plugin_rest_identity_provider.c @@ -0,0 +1,632 @@ +/* + This file is part of GNUnet. + Copyright (C) 2012-2015 GNUnet e.V. + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 3, or (at your + option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + */ +/** + * @author Martin Schanzenbach + * @file identity/plugin_rest_identity.c + * @brief GNUnet Namestore REST plugin + * + */ + +#include "platform.h" +#include "gnunet_rest_plugin.h" +#include "gnunet_identity_service.h" +#include "gnunet_gns_service.h" +#include "gnunet_gnsrecord_lib.h" +#include "gnunet_namestore_service.h" +#include "gnunet_rest_lib.h" +#include "gnunet_jsonapi_lib.h" +#include "gnunet_jsonapi_util.h" +#include "microhttpd.h" +#include +#include +#include "gnunet_signatures.h" +#include "gnunet_identity_provider_service.h" + +/** + * REST root namespace + */ +#define GNUNET_REST_API_NS_IDENTITY_PROVIDER "/idp" + +/** + * Attribute namespace + */ +#define GNUNET_REST_API_NS_IDENTITY_ATTRIBUTES "/idp/attributes" + +/** + * Attribute key + */ +#define GNUNET_REST_JSONAPI_IDENTITY_ATTRIBUTE "attribute" + +/** + * Value key + */ +#define GNUNET_REST_JSONAPI_IDENTITY_ATTRIBUTE_VALUE "value" + +/** + * State while collecting all egos + */ +#define ID_REST_STATE_INIT 0 + +/** + * Done collecting egos + */ +#define ID_REST_STATE_POST_INIT 1 + + +/** + * The configuration handle + */ +const struct GNUNET_CONFIGURATION_Handle *cfg; + +/** + * HTTP methods allows for this plugin + */ +static char* allow_methods; + +/** + * @brief struct returned by the initialization function of the plugin + */ +struct Plugin +{ + const struct GNUNET_CONFIGURATION_Handle *cfg; +}; + +/** + * The ego list + */ +struct EgoEntry +{ + /** + * DLL + */ + struct EgoEntry *next; + + /** + * DLL + */ + struct EgoEntry *prev; + + /** + * Ego Identifier + */ + char *identifier; + + /** + * Public key string + */ + char *keystring; + + /** + * The Ego + */ + struct GNUNET_IDENTITY_Ego *ego; +}; + + +struct RequestHandle +{ + /** + * Ego list + */ + struct EgoEntry *ego_head; + + /** + * Ego list + */ + struct EgoEntry *ego_tail; + + /** + * Selected ego + */ + struct EgoEntry *ego_entry; + + /** + * Ptr to current ego private key + */ + const struct GNUNET_CRYPTO_EcdsaPrivateKey *priv_key; + + /** + * Handle to the rest connection + */ + struct GNUNET_REST_RequestHandle *conndata_handle; + + /** + * The processing state + */ + int state; + + /** + * Handle to Identity service. + */ + struct GNUNET_IDENTITY_Handle *identity_handle; + + /** + * IDENTITY Operation + */ + struct GNUNET_IDENTITY_Operation *op; + + /** + * Identity Provider + */ + struct GNUNET_IDENTITY_PROVIDER_Handle *idp; + + /** + * Idp Operation + */ + struct GNUNET_IDENTITY_PROVIDER_Operation *idp_op; + + /** + * Attribute iterator + */ + struct GNUNET_IDENTITY_PROVIDER_AttributeIterator *attr_it; + + /** + * Desired timeout for the lookup (default is no timeout). + */ + struct GNUNET_TIME_Relative timeout; + + /** + * ID of a task associated with the resolution process. + */ + struct GNUNET_SCHEDULER_Task *timeout_task; + + /** + * The plugin result processor + */ + GNUNET_REST_ResultProcessor proc; + + /** + * The closure of the result processor + */ + void *proc_cls; + + /** + * The url + */ + char *url; + + /** + * Error response message + */ + char *emsg; + + /** + * Reponse code + */ + int response_code; + + /** + * Response object + */ + struct GNUNET_JSONAPI_Document *resp_object; + + /** + * Resource object + */ + struct GNUNET_JSONAPI_Resource *json_resource; + +}; + + + +/** + * Cleanup lookup handle + * @param handle Handle to clean up + */ +static void +cleanup_handle (struct RequestHandle *handle) +{ + struct EgoEntry *ego_entry; + struct EgoEntry *ego_tmp; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Cleaning up\n"); + if (NULL != handle->resp_object) + GNUNET_JSONAPI_document_delete (handle->resp_object); + if (NULL != handle->timeout_task) + GNUNET_SCHEDULER_cancel (handle->timeout_task); + if (NULL != handle->identity_handle) + GNUNET_IDENTITY_disconnect (handle->identity_handle); + if (NULL != handle->idp) + GNUNET_IDENTITY_PROVIDER_disconnect (handle->idp); + if (NULL != handle->attr_it) + GNUNET_IDENTITY_PROVIDER_get_attributes_stop (handle->attr_it); + if (NULL != handle->url) + GNUNET_free (handle->url); + if (NULL != handle->emsg) + GNUNET_free (handle->emsg); + for (ego_entry = handle->ego_head; + NULL != ego_entry;) + { + ego_tmp = ego_entry; + ego_entry = ego_entry->next; + GNUNET_free (ego_tmp->identifier); + GNUNET_free (ego_tmp->keystring); + GNUNET_free (ego_tmp); + } + GNUNET_free (handle); +} + +/** + * Task run on error, sends error message. Cleans up everything. + * + * @param cls the `struct RequestHandle` + */ +static void +do_error (void *cls) +{ + struct RequestHandle *handle = cls; + struct MHD_Response *resp; + char *json_error; + + GNUNET_asprintf (&json_error, + "{Error while processing request: %s}", + handle->emsg); + resp = GNUNET_REST_create_response (json_error); + handle->proc (handle->proc_cls, resp, handle->response_code); + cleanup_handle (handle); + GNUNET_free (json_error); +} + +/** + * Task run on timeout, sends error message. Cleans up everything. + * + * @param cls the `struct RequestHandle` + */ +static void +do_timeout (void *cls) +{ + struct RequestHandle *handle = cls; + + handle->timeout_task = NULL; + do_error (handle); +} + + +static void +attr_collect_error_cb (void *cls) +{ + struct RequestHandle *handle = cls; + + do_error (handle); +} + +/** + * Return attributes for identity + * + * @param cls the request handle + */ +static void +return_attr_list (void *cls) +{ + char* result_str; + struct RequestHandle *handle = cls; + struct MHD_Response *resp; + + GNUNET_JSONAPI_document_serialize (handle->resp_object, &result_str); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Result %s\n", result_str); + resp = GNUNET_REST_create_response (result_str); + handle->proc (handle->proc_cls, resp, MHD_HTTP_OK); + GNUNET_free (result_str); + cleanup_handle (handle); +} + + +static void +attr_collect_finished_cb (void *cls) +{ + struct RequestHandle *handle = cls; + //Done + handle->attr_it = NULL; + GNUNET_SCHEDULER_add_now (&return_attr_list, handle); +} + +/** + * Collect all attributes for an ego + * + */ +static void +attr_collect (void *cls, + const struct GNUNET_CRYPTO_EcdsaPublicKey *identity, + const struct GNUNET_IDENTITY_PROVIDER_Attribute *attr) +{ + struct RequestHandle *handle = cls; + json_t *value; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding attribute: %s\n", + attr->name); + value = json_string (attr->data); + GNUNET_JSONAPI_resource_add_attr (handle->json_resource, + attr->name, + value); + json_decref (value); + GNUNET_IDENTITY_PROVIDER_get_attributes_next (handle->attr_it); +} + + + +/** + * List attributes for identity request + * + * @param con_handle the connection handle + * @param url the url + * @param cls the RequestHandle + */ +static void +list_attribute_cont (struct GNUNET_REST_RequestHandle *con_handle, + const char* url, + void *cls) +{ + const struct GNUNET_CRYPTO_EcdsaPrivateKey *priv_key; + struct RequestHandle *handle = cls; + struct EgoEntry *ego_entry; + char *identity; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Getting attributes for %s.\n", + handle->url); + if ( strlen (GNUNET_REST_API_NS_IDENTITY_ATTRIBUTES) >= + strlen (handle->url)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "No identity given.\n"); + GNUNET_SCHEDULER_add_now (&do_error, handle); + return; + } + identity = handle->url + strlen (GNUNET_REST_API_NS_IDENTITY_ATTRIBUTES) + 1; + + for (ego_entry = handle->ego_head; + NULL != ego_entry; + ego_entry = ego_entry->next) + if (0 == strcmp (identity, ego_entry->identifier)) + break; + handle->resp_object = GNUNET_JSONAPI_document_new (); + + handle->json_resource = GNUNET_JSONAPI_resource_new (GNUNET_REST_JSONAPI_IDENTITY_ATTRIBUTE, + identity); + GNUNET_JSONAPI_document_resource_add (handle->resp_object, handle->json_resource); + + if (NULL == ego_entry) + { + //Done + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Ego %s not found.\n", + identity); + GNUNET_SCHEDULER_add_now (&return_attr_list, handle); + return; + } + priv_key = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego); + handle->idp = GNUNET_IDENTITY_PROVIDER_connect (cfg); + handle->attr_it = GNUNET_IDENTITY_PROVIDER_get_attributes_start (handle->idp, + priv_key, + &attr_collect_error_cb, + handle, + &attr_collect, + handle, + &attr_collect_finished_cb, + handle); +} + + +/** + * Respond to OPTIONS request + * + * @param con_handle the connection handle + * @param url the url + * @param cls the RequestHandle + */ +static void +options_cont (struct GNUNET_REST_RequestHandle *con_handle, + const char* url, + void *cls) +{ + struct MHD_Response *resp; + struct RequestHandle *handle = cls; + + //For now, independent of path return all options + resp = GNUNET_REST_create_response (NULL); + MHD_add_response_header (resp, + "Access-Control-Allow-Methods", + allow_methods); + handle->proc (handle->proc_cls, resp, MHD_HTTP_OK); + cleanup_handle (handle); + return; +} + +/** + * Handle rest request + * + * @param handle the request handle + */ +static void +init_cont (struct RequestHandle *handle) +{ + struct GNUNET_REST_RequestHandlerError err; + static const struct GNUNET_REST_RequestHandler handlers[] = { + {MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_IDENTITY_ATTRIBUTES, &list_attribute_cont}, + {MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_IDENTITY_PROVIDER, + &options_cont}, + GNUNET_REST_HANDLER_END + }; + + if (GNUNET_NO == GNUNET_REST_handle_request (handle->conndata_handle, + handlers, + &err, + handle)) + { + handle->response_code = err.error_code; + GNUNET_SCHEDULER_add_now (&do_error, handle); + } +} + +/** + * If listing is enabled, prints information about the egos. + * + * This function is initially called for all egos and then again + * whenever a ego's identifier changes or if it is deleted. At the + * end of the initial pass over all egos, the function is once called + * with 'NULL' for 'ego'. That does NOT mean that the callback won't + * be invoked in the future or that there was an error. + * + * When used with 'GNUNET_IDENTITY_create' or 'GNUNET_IDENTITY_get', + * this function is only called ONCE, and 'NULL' being passed in + * 'ego' does indicate an error (i.e. name is taken or no default + * value is known). If 'ego' is non-NULL and if '*ctx' + * is set in those callbacks, the value WILL be passed to a subsequent + * call to the identity callback of 'GNUNET_IDENTITY_connect' (if + * that one was not NULL). + * + * When an identity is renamed, this function is called with the + * (known) ego but the NEW identifier. + * + * When an identity is deleted, this function is called with the + * (known) ego and "NULL" for the 'identifier'. In this case, + * the 'ego' is henceforth invalid (and the 'ctx' should also be + * cleaned up). + * + * @param cls closure + * @param ego ego handle + * @param ctx context for application to store data for this ego + * (during the lifetime of this process, initially NULL) + * @param identifier identifier assigned by the user for this ego, + * NULL if the user just deleted the ego and it + * must thus no longer be used + */ +static void +list_ego (void *cls, + struct GNUNET_IDENTITY_Ego *ego, + void **ctx, + const char *identifier) +{ + struct RequestHandle *handle = cls; + struct EgoEntry *ego_entry; + struct GNUNET_CRYPTO_EcdsaPublicKey pk; + + if ((NULL == ego) && (ID_REST_STATE_INIT == handle->state)) + { + handle->state = ID_REST_STATE_POST_INIT; + init_cont (handle); + return; + } + if (ID_REST_STATE_INIT == handle->state) { + ego_entry = GNUNET_new (struct EgoEntry); + GNUNET_IDENTITY_ego_get_public_key (ego, &pk); + ego_entry->keystring = + GNUNET_CRYPTO_ecdsa_public_key_to_string (&pk); + ego_entry->ego = ego; + ego_entry->identifier = GNUNET_strdup (identifier); + GNUNET_CONTAINER_DLL_insert_tail(handle->ego_head,handle->ego_tail, ego_entry); + } + +} + +/** + * Function processing the REST call + * + * @param method HTTP method + * @param url URL of the HTTP request + * @param data body of the HTTP request (optional) + * @param data_size length of the body + * @param proc callback function for the result + * @param proc_cls closure for callback function + * @return GNUNET_OK if request accepted + */ +static void +rest_identity_process_request(struct GNUNET_REST_RequestHandle *conndata_handle, + GNUNET_REST_ResultProcessor proc, + void *proc_cls) +{ + struct RequestHandle *handle = GNUNET_new (struct RequestHandle); + + handle->timeout = GNUNET_TIME_UNIT_FOREVER_REL; + handle->proc_cls = proc_cls; + handle->proc = proc; + handle->state = ID_REST_STATE_INIT; + handle->conndata_handle = conndata_handle; + + + handle->url = GNUNET_strdup (conndata_handle->url); + if (handle->url[strlen (handle->url)-1] == '/') + handle->url[strlen (handle->url)-1] = '\0'; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Connecting...\n"); + handle->identity_handle = GNUNET_IDENTITY_connect (cfg, + &list_ego, + handle); + handle->timeout_task = + GNUNET_SCHEDULER_add_delayed (handle->timeout, + &do_timeout, + handle); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Connected\n"); +} + +/** + * Entry point for the plugin. + * + * @param cls Config info + * @return NULL on error, otherwise the plugin context + */ +void * +libgnunet_plugin_rest_identity_provider_init (void *cls) +{ + static struct Plugin plugin; + struct GNUNET_REST_Plugin *api; + + cfg = cls; + if (NULL != plugin.cfg) + return NULL; /* can only initialize once! */ + memset (&plugin, 0, sizeof (struct Plugin)); + plugin.cfg = cfg; + api = GNUNET_new (struct GNUNET_REST_Plugin); + api->cls = &plugin; + api->name = GNUNET_REST_API_NS_IDENTITY_PROVIDER; + api->process_request = &rest_identity_process_request; + GNUNET_asprintf (&allow_methods, + "%s, %s, %s, %s, %s", + MHD_HTTP_METHOD_GET, + MHD_HTTP_METHOD_POST, + MHD_HTTP_METHOD_PUT, + MHD_HTTP_METHOD_DELETE, + MHD_HTTP_METHOD_OPTIONS); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + _("Identity Provider REST API initialized\n")); + return api; +} + + +/** + * Exit point from the plugin. + * + * @param cls the plugin context (as returned by "init") + * @return always NULL + */ +void * +libgnunet_plugin_rest_identity_provider_done (void *cls) +{ + struct GNUNET_REST_Plugin *api = cls; + struct Plugin *plugin = api->cls; + + plugin->cfg = NULL; + GNUNET_free_non_null (allow_methods); + GNUNET_free (api); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Identity Provider REST plugin is finished\n"); + return NULL; +} + +/* end of plugin_rest_identity_provider.c */ diff --git a/src/identity-provider/test_idp.conf b/src/identity-provider/test_idp.conf index a457bbe8f..9669bef7e 100644 --- a/src/identity-provider/test_idp.conf +++ b/src/identity-provider/test_idp.conf @@ -27,3 +27,5 @@ RECORD_PUT_INTERVAL = 1 h ZONE_PUBLISH_TIME_WINDOW = 1 h DNS_ROOT=PD67SGHF3E0447TU9HADIVU9OM7V4QHTOG0EBU69TFRI2LG63DR0 +[rest] +PREFIX = valgrind --leak-check=full --track-origins=yes --log-file=/tmp/rest diff --git a/src/identity-provider/test_idp_issue.sh b/src/identity-provider/test_idp_issue.sh index f2f48057d..8cd1297d3 100755 --- a/src/identity-provider/test_idp_issue.sh +++ b/src/identity-provider/test_idp_issue.sh @@ -24,6 +24,7 @@ which timeout &> /dev/null && DO_TIMEOUT="timeout 30" TEST_ATTR="test" gnunet-arm -s -c test_idp.conf +gnunet-arm -i rest -c test_idp.conf gnunet-identity -C testego -c test_idp.conf gnunet-identity -C rpego -c test_idp.conf SUBJECT_KEY=$(gnunet-identity -d -c test_idp.conf | grep rpego | awk '{print $3}') @@ -32,6 +33,7 @@ gnunet-idp -e testego -a email -V john@doe.gnu -c test_idp.conf gnunet-idp -e testego -a name -V John -c test_idp.conf #gnunet-idp -e testego -D -c test_idp.conf TICKET=$(gnunet-idp -e testego -i "email,name" -r $SUBJECT_KEY -c test_idp.conf | awk '{print $1}') +curl http://localhost:7776/idp/attributes/testego #echo "Consuming $TICKET" gnunet-idp -e rpego -C $TICKET -c test_idp.conf gnunet-arm -e -c test_idp.conf -- 2.25.1