*/
#define GNUNET_REST_API_NS_IDENTITY_CONSUME "/idp/consume"
+/**
+ * Authorize namespace
+ */
+#define GNUNET_REST_API_NS_AUTHORIZE "/idp/authorize"
+
+/**
+ * Login namespace
+ */
+#define GNUNET_REST_API_NS_LOGIN "/idp/login"
+
/**
* Attribute key
*/
*/
#define ID_REST_STATE_POST_INIT 1
+/**
+ * OIDC response_type key
+ */
+#define OIDC_RESPONSE_TYPE_KEY "response_type"
+
+/**
+ * OIDC client_id key
+ */
+#define OIDC_CLIENT_ID_KEY "client_id"
+
+/**
+ * OIDC scope key
+ */
+#define OIDC_SCOPE_KEY "scope"
+
+/**
+ * OIDC redirect_uri key
+ */
+#define OIDC_REDIRECT_URI_KEY "redirect_uri"
+
+/**
+ * OIDC state key
+ */
+#define OIDC_STATE_KEY "state"
+
+/**
+ * OIDC nonce key
+ */
+#define OIDC_NONCE_KEY "nonce"
+
+/**
+ * OIDC cookie header key
+ */
+#define OIDC_COOKIE_HEADER_KEY "Cookie"
+
+/**
+ * OIDC cookie header information key
+ */
+#define OIDC_COOKIE_HEADER_INFORMATION_KEY "Identity="
+
+/**
+ * OIDC expected response_type while authorizing
+ */
+#define OIDC_EXPECTED_AUTHORIZATION_RESPONSE_TYPE "code"
+
+/**
+ * OIDC expected scope part while authorizing
+ */
+#define OIDC_EXPECTED_AUTHORIZATION_SCOPE "openid"
+
+
+/**
+ * OIDC ignored parameter array
+ */
+char* OIDC_ignored_parameter_array [] =
+{
+ "display",
+ "prompt",
+ "max_age",
+ "ui_locales",
+ "response_mode",
+ "id_token_hint",
+ "login_hint",
+ "acr_values"
+};
+
+/**
+ * OIDC authorized identities and times hashmap
+ */
+struct GNUNET_CONTAINER_MultiHashMap *OIDC_authorized_identities;
/**
* The configuration handle
*/
char *emsg;
+ /**
+ * Error response uri
+ */
+ char *eredirect;
+
+ /**
+ * Error response description
+ */
+ char *edesc;
+
/**
* Reponse code
*/
char *json_error;
GNUNET_asprintf (&json_error,
- "{Error while processing request: %s}",
+ "{error : %s}",
handle->emsg);
resp = GNUNET_REST_create_response (json_error);
handle->proc (handle->proc_cls, resp, handle->response_code);
GNUNET_free (json_error);
}
+/**
+ * Task run on error, sends error message. Cleans up everything.
+ *
+ * @param cls the `struct RequestHandle`
+ */
+static void
+do_redirect_error (void *cls)
+{
+ struct RequestHandle *handle = cls;
+ struct MHD_Response *resp;
+ char* redirect;
+ //TODO handle->url is wrong
+ GNUNET_asprintf (&redirect,
+ "%s?error=%s&error_description=%s",
+ handle->eredirect, handle->emsg, handle->edesc );
+ resp = GNUNET_REST_create_response ("");
+ MHD_add_response_header (resp, "Location", redirect);
+ handle->proc (handle->proc_cls, resp, MHD_HTTP_FOUND);
+ cleanup_handle (handle);
+ GNUNET_free (redirect);
+}
+
/**
* Task run on timeout, sends error message. Cleans up everything.
*
strlen (rnd_str),
&ticket.rnd,
sizeof (uint64_t));
- GNUNET_STRINGS_string_to_data (identity_str,
- strlen (identity_str),
- &ticket.identity,
- sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));
+// GNUNET_STRINGS_string_to_data (identity_str,
+// strlen (identity_str),
+// &ticket.identity,type filter text
+// sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));
GNUNET_STRINGS_string_to_data (audience_str,
strlen (audience_str),
&ticket.audience,
return;
}
+/**
+ * Respond to OPTIONS request
+ *
+ * @param con_handle the connection handle
+ * @param url the url
+ * @param cls the RequestHandle
+ */
+static void
+authorize_cont (struct GNUNET_REST_RequestHandle *con_handle,
+ const char* url,
+ void *cls)
+{
+ struct MHD_Response *resp;
+ struct RequestHandle *handle = cls;
+ char *response_type;
+ char *client_id;
+ char *scope;
+ char *redirect_uri;
+ char *state = NULL;
+ char *nonce = NULL;
+ struct GNUNET_TIME_Absolute current_time, *relog_time;
+ char *login_base_url, *new_redirect;
+ struct GNUNET_HashCode cache_key;
+
+ //TODO clean up method
+
+ /** The Authorization Server MUST validate all the OAuth 2.0 parameters
+ * according to the OAuth 2.0 specification.
+ */
+ /**
+ * If the sub (subject) Claim is requested with a specific value for the
+ * ID Token, the Authorization Server MUST only send a positive response
+ * if the End-User identified by that sub value has an active session with
+ * the Authorization Server or has been Authenticated as a result of the
+ * request. The Authorization Server MUST NOT reply with an ID Token or
+ * Access Token for a different user, even if they have an active session
+ * with the Authorization Server. Such a request can be made either using
+ * an id_token_hint parameter or by requesting a specific Claim Value as
+ * described in Section 5.5.1, if the claims parameter is supported by
+ * the implementation.
+ */
+
+
+
+ // REQUIRED value: client_id
+ GNUNET_CRYPTO_hash (OIDC_CLIENT_ID_KEY, strlen (OIDC_CLIENT_ID_KEY),
+ &cache_key);
+ if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map,
+ &cache_key))
+ {
+ handle->emsg=GNUNET_strdup("invalid_request");
+ handle->edesc=GNUNET_strdup("Missing parameter: client_id");
+ GNUNET_SCHEDULER_add_now (&do_error, handle);
+ return;
+ }
+ client_id = GNUNET_CONTAINER_multihashmap_get(handle->rest_handle->url_param_map,
+ &cache_key);
+ struct GNUNET_CRYPTO_EcdsaPublicKey pubkey;
+ GNUNET_CRYPTO_ecdsa_public_key_from_string(client_id,
+ strlen (client_id),
+ &pubkey);
+// GNUNET_NAMESTORE_zone_to_name();
+ // Checks if client_id is valid:
+ // TODO use GNUNET_NAMESTORE_zone_to_name() function to verify that a delegation to the client_id exists
+ // TODO change check (lookup trusted public_key?)
+// if( strcmp( client_id, "localhost" ) != 0 )
+// {
+// handle->emsg=GNUNET_strdup("unauthorized_client");
+// handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
+// GNUNET_SCHEDULER_add_now (&do_error, handle);
+// return;
+// }
+
+ // REQUIRED value: redirect_uri
+ // TODO verify the redirect uri matches https://<client_id>.zkey[/xyz]
+ GNUNET_CRYPTO_hash (OIDC_REDIRECT_URI_KEY, strlen (OIDC_REDIRECT_URI_KEY),
+ &cache_key);
+ if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map,
+ &cache_key))
+ {
+ handle->emsg=GNUNET_strdup("invalid_request");
+ handle->edesc=GNUNET_strdup("Missing parameter: redirect_uri");
+ GNUNET_SCHEDULER_add_now (&do_error, handle);
+ return;
+ }
+ redirect_uri = GNUNET_CONTAINER_multihashmap_get(handle->rest_handle->url_param_map,
+ &cache_key);
+
+ // verify the redirect uri matches https://<client_id>.zkey[/xyz]
+ // TODO change check (check client_id->public key == address)
+// if( strcmp( redirect_uri, "https://localhost:8000" ) != 0 )
+// {
+// handle->emsg=GNUNET_strdup("invalid_request");
+// handle->edesc=GNUNET_strdup("Invalid or mismatching redirect_uri");
+// GNUNET_SCHEDULER_add_now (&do_error, handle);
+// return;
+// }
+ handle->eredirect = GNUNET_strdup(redirect_uri);
+
+ // REQUIRED value: response_type
+ GNUNET_CRYPTO_hash (OIDC_RESPONSE_TYPE_KEY, strlen (OIDC_RESPONSE_TYPE_KEY),
+ &cache_key);
+ if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map,
+ &cache_key))
+ {
+ handle->emsg=GNUNET_strdup("invalid_request");
+ handle->edesc=GNUNET_strdup("Missing parameter: response_type");
+ GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
+ return;
+ }
+ response_type = GNUNET_CONTAINER_multihashmap_get(handle->rest_handle->url_param_map,
+ &cache_key);
+
+ // REQUIRED value: scope
+ GNUNET_CRYPTO_hash (OIDC_SCOPE_KEY, strlen (OIDC_SCOPE_KEY), &cache_key);
+ if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map,
+ &cache_key))
+ {
+ handle->emsg=GNUNET_strdup("invalid_request");
+ handle->edesc=GNUNET_strdup("Missing parameter: scope");
+ GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
+ return;
+ }
+ scope = GNUNET_CONTAINER_multihashmap_get(handle->rest_handle->url_param_map,
+ &cache_key);
+
+ //RECOMMENDED value: state
+ GNUNET_CRYPTO_hash (OIDC_STATE_KEY, strlen (OIDC_STATE_KEY), &cache_key);
+ if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map,
+ &cache_key))
+ {
+ state = GNUNET_CONTAINER_multihashmap_get(handle->rest_handle->url_param_map,
+ &cache_key);
+ }
+
+ //OPTIONAL value: nonce
+ GNUNET_CRYPTO_hash (OIDC_NONCE_KEY, strlen (OIDC_NONCE_KEY), &cache_key);
+ if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map,
+ &cache_key))
+ {
+ nonce = GNUNET_CONTAINER_multihashmap_get(handle->rest_handle->url_param_map,
+ &cache_key);
+ }
+
+ int number_of_ignored_parameter = sizeof(OIDC_ignored_parameter_array) / sizeof(char *);
+ int iterator;
+ for( iterator = 0; iterator < number_of_ignored_parameter; iterator++ )
+ {
+ GNUNET_CRYPTO_hash (OIDC_ignored_parameter_array[iterator],
+ strlen(OIDC_ignored_parameter_array[iterator]),
+ &cache_key);
+ if(GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains(handle->rest_handle->url_param_map,
+ &cache_key))
+ {
+ handle->emsg=GNUNET_strdup("access_denied");
+ //TODO rewrite error description
+ handle->edesc=GNUNET_strdup("Server will not handle parameter");
+ GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
+ return;
+ }
+ }
+
+ // Checks if response_type is 'code'
+ if( strcmp( response_type, OIDC_EXPECTED_AUTHORIZATION_RESPONSE_TYPE ) != 0 )
+ {
+ handle->emsg=GNUNET_strdup("unsupported_response_type");
+ handle->edesc=GNUNET_strdup("The authorization server does not support "
+ "obtaining this authorization code.");
+ GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
+ return;
+ }
+ // Checks if scope contains 'openid'
+ if( strstr( scope, OIDC_EXPECTED_AUTHORIZATION_SCOPE ) == NULL )
+ {
+ handle->emsg=GNUNET_strdup("invalid_scope");
+ handle->edesc=GNUNET_strdup("The requested scope is invalid, unknown, or "
+ "malformed.");
+ GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
+ return;
+ }
+
+
+ //TODO check other values and use them accordingly
+
+
+ GNUNET_CRYPTO_hash (OIDC_COOKIE_HEADER_KEY, strlen (OIDC_COOKIE_HEADER_KEY),
+ &cache_key);
+ //No identity-cookie -> redirect to login
+ if ( GNUNET_YES
+ == GNUNET_CONTAINER_multihashmap_contains (con_handle->header_param_map,
+ &cache_key) )
+ {
+ //split cookies and find 'Identity' cookie
+ char* cookies = GNUNET_CONTAINER_multihashmap_get (
+ con_handle->header_param_map, &cache_key);
+ char delimiter[] = "; ";
+ char *identity_cookie;
+ identity_cookie = strtok(cookies, delimiter);
+
+ while(identity_cookie != NULL)
+ {
+ if(strstr( identity_cookie, OIDC_COOKIE_HEADER_INFORMATION_KEY ) != NULL)
+ {
+ break;
+ }
+ identity_cookie = strtok(NULL, delimiter);
+ }
+ GNUNET_CRYPTO_hash (identity_cookie, strlen (identity_cookie), &cache_key);
+
+ //No login time for identity -> redirect to login
+ if ( GNUNET_YES
+ == GNUNET_CONTAINER_multihashmap_contains (OIDC_authorized_identities,
+ &cache_key) )
+ {
+ relog_time = GNUNET_CONTAINER_multihashmap_get (
+ OIDC_authorized_identities, &cache_key);
+
+ current_time = GNUNET_TIME_absolute_get();
+
+ GNUNET_CONTAINER_multihashmap_remove_all(OIDC_authorized_identities, &cache_key);
+ // 30 min after old login -> redirect to login
+ if ( current_time.abs_value_us <= relog_time->abs_value_us )
+ {
+ resp = GNUNET_REST_create_response ("");
+ // code = struct GNUNET_IDENTITY_PROVIDER_Ticket
+ GNUNET_IDENTITY_PROVIDER_t
+ MHD_add_response_header (resp, "Location", redirect_uri);
+ handle->proc (handle->proc_cls, resp, MHD_HTTP_FOUND);
+ cleanup_handle (handle);
+ GNUNET_free(relog_time);
+ return;
+ }
+ GNUNET_free(relog_time);
+ }
+ }
+
+
+ // login redirection
+ if ( GNUNET_OK
+ == GNUNET_CONFIGURATION_get_value_string (cfg, "identity-rest-plugin",
+ "address", &login_base_url) )
+ {
+ GNUNET_asprintf (&new_redirect, "%s?%s=%s&%s=%s&%s=%s&%s=%s&%s=%s&%s=%s",
+ login_base_url,
+ OIDC_RESPONSE_TYPE_KEY,
+ response_type,
+ OIDC_CLIENT_ID_KEY,
+ client_id,
+ OIDC_REDIRECT_URI_KEY,
+ redirect_uri,
+ OIDC_SCOPE_KEY,
+ scope,
+ OIDC_STATE_KEY,
+ (NULL == state) ? state : "",
+ OIDC_NONCE_KEY,
+ (NULL == nonce) ? nonce : "");
+ resp = GNUNET_REST_create_response ("");
+ MHD_add_response_header (resp, "Location", new_redirect);
+ }
+ else
+ {
+ handle->emsg = GNUNET_strdup("No server configuration");
+ handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
+ GNUNET_SCHEDULER_add_now (&do_error, handle);
+ return;
+ }
+ handle->proc (handle->proc_cls, resp, MHD_HTTP_FOUND);
+ cleanup_handle (handle);
+ GNUNET_free(new_redirect);
+ return;
+}
+
+
+/**
+ * Combines an identity with a login time and responds OK to login request
+ *
+ * @param con_handle the connection handle
+ * @param url the url
+ * @param cls the RequestHandle
+ */
+static void
+login_cont (struct GNUNET_REST_RequestHandle *con_handle,
+ const char* url,
+ void *cls)
+{
+
+
+ struct MHD_Response *resp = GNUNET_REST_create_response ("");
+ struct RequestHandle *handle = cls;
+ struct GNUNET_HashCode cache_key;
+ struct GNUNET_TIME_Absolute *current_time;
+ char* cookie;
+ json_t *root;
+ json_error_t error;
+ json_t *identity;
+ root = json_loads (handle->rest_handle->data, 0, &error);
+ identity = json_object_get (root, "identity");
+ if ( json_is_string(identity) )
+ {
+ GNUNET_asprintf (&cookie, "Identity=%s", json_string_value (identity));
+
+ GNUNET_CRYPTO_hash (cookie, strlen (cookie), &cache_key);
+ current_time = GNUNET_new(struct GNUNET_TIME_Absolute);
+ *current_time = GNUNET_TIME_relative_to_absolute (
+ GNUNET_TIME_relative_multiply (GNUNET_TIME_relative_get_minute_ (),
+ 30));
+ GNUNET_CONTAINER_multihashmap_put (
+ OIDC_authorized_identities, &cache_key, current_time,
+ GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE);
+
+ handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
+ }
+ else
+ {
+ handle->proc (handle->proc_cls, resp, MHD_HTTP_BAD_REQUEST);
+ }
+ GNUNET_free(cookie);
+ json_decref (root);
+ cleanup_handle (handle);
+ return;
+}
+
/**
* Handle rest request
*
{MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_IDENTITY_ATTRIBUTES, &list_attribute_cont},
{MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_IDENTITY_ATTRIBUTES, &add_attribute_cont},
{MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_IDENTITY_TICKETS, &list_tickets_cont},
+ {MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_AUTHORIZE, &authorize_cont},
+ {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_LOGIN, &login_cont},
+ {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_AUTHORIZE, &authorize_cont},
{MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_IDENTITY_REVOKE, &revoke_ticket_cont},
{MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_IDENTITY_CONSUME, &consume_ticket_cont},
{MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_IDENTITY_PROVIDER,
}
- /**
- * 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 *rest_handle,
GNUNET_REST_ResultProcessor proc,
void *proc_cls)
{
struct RequestHandle *handle = GNUNET_new (struct RequestHandle);
-
+ if ( NULL == OIDC_authorized_identities )
+ {
+ OIDC_authorized_identities = GNUNET_CONTAINER_multihashmap_create (10,
+ GNUNET_NO);
+ }
handle->timeout = GNUNET_TIME_UNIT_FOREVER_REL;
handle->proc_cls = proc_cls;
handle->proc = proc;