]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-oauth2: Use the new lib-json
authorStephan Bosch <stephan.bosch@open-xchange.com>
Sat, 22 Feb 2020 11:35:25 +0000 (12:35 +0100)
committeraki.tuomi <aki.tuomi@open-xchange.com>
Sat, 18 Nov 2023 18:58:04 +0000 (18:58 +0000)
src/lib-oauth2/Makefile.am
src/lib-oauth2/oauth2-jwt.c
src/lib-oauth2/oauth2-private.h
src/lib-oauth2/oauth2-request.c
src/lib-oauth2/oauth2.c
src/lib-oauth2/test-oauth2-json.c
src/lib-oauth2/test-oauth2-jwt.c

index 0403ce1c99817985f0db81f9f569735af9f27e94..5db401c25880ded5f9a0d01ce0eecd9ea7535b55 100644 (file)
@@ -1,6 +1,7 @@
 AM_CPPFLAGS = \
        -I$(top_srcdir)/src/lib \
        -I$(top_srcdir)/src/lib-test \
+       -I$(top_srcdir)/src/lib-json \
        -I$(top_srcdir)/src/lib-http \
        -I$(top_srcdir)/src/lib-dcrypt \
        -I$(top_srcdir)/src/lib-dict \
@@ -21,6 +22,8 @@ liboauth2_la_SOURCES = \
        oauth2-request.c \
        oauth2-jwt.c \
        oauth2-key-cache.c
+liboauth2_la_DEPENDENCIES = \
+       ../lib-json/libjson.la
 
 test_programs = \
        test-oauth2-json \
@@ -63,7 +66,8 @@ test_oauth2_json_DEPENDENCIES = $(test_deps)
 test_oauth2_jwt_SOURCES = test-oauth2-jwt.c
 test_oauth2_jwt_LDADD = $(test_libs)
 if HAVE_WHOLE_ARCHIVE
-test_oauth2_jwt_LDFLAGS = -Wl,$(LD_WHOLE_ARCHIVE),../lib-ssl-iostream/.libs/libssl_iostream.a,$(LD_NO_WHOLE_ARCHIVE)
+test_oauth2_jwt_LDFLAGS = \
+       -Wl,$(LD_WHOLE_ARCHIVE),../lib-json/.libs/libjson.a,../lib-ssl-iostream/.libs/libssl_iostream.a,$(LD_NO_WHOLE_ARCHIVE)
 endif
 test_oauth2_jwt_DEPENDENCIES = $(test_deps)
 
@@ -71,4 +75,3 @@ check-local:
        for bin in $(test_programs); do \
          if ! $(RUN_TEST) ./$$bin; then exit 1; fi; \
        done
-
index 67d9f3099b5dfb048451dd754cc8cd80e4f6bf00..21c807a90c990722eab26927da8ea05a751e33f9 100644 (file)
@@ -9,7 +9,7 @@
 #include "hash-method.h"
 #include "istream.h"
 #include "iso8601-date.h"
-#include "json-tree.h"
+#include "json-tree.new.h"
 #include "array.h"
 #include "base64.h"
 #include "str-sanitize.h"
 static const struct json_tree_node *
 get_field_node(const struct json_tree *tree, const char *key)
 {
-       const struct json_tree_node *root = json_tree_root(tree);
-       const struct json_tree_node *value_node = json_tree_find_key(root, key);
+       const struct json_tree_node *root = json_tree_get_root_const(tree);
+       const struct json_tree_node *value_node;
 
-       if (value_node == NULL || value_node->value_type == JSON_TYPE_OBJECT ||
-           value_node->value_type == JSON_TYPE_ARRAY)
+       value_node = json_tree_node_get_member(root, key);
+       if (value_node == NULL ||
+           json_tree_node_is_object(value_node) ||
+           json_tree_node_is_array(value_node))
                return NULL;
 
        return value_node;
@@ -43,30 +45,34 @@ get_field(const struct json_tree *tree, const char *key,
        if (value_node == NULL)
                return NULL;
        if (type_r != NULL)
-               *type_r = value_node->value_type;
-       return json_tree_get_value_str(value_node);
+               *type_r = json_tree_node_get_type(value_node);
+       return json_tree_node_as_str(value_node);
 }
 
 static const char *
 get_field_multiple(const struct json_tree *tree, const char *key)
 {
-       const struct json_tree_node *root = json_tree_root(tree);
-       const struct json_tree_node *value_node = json_tree_find_key(root, key);
+       const struct json_tree_node *root = json_tree_get_root_const(tree);
+       const struct json_tree_node *value_node;
 
-       if (value_node == NULL || value_node->value_type == JSON_TYPE_OBJECT)
+       value_node = json_tree_node_get_member(root, key);
+       if (value_node == NULL || json_tree_node_is_object(value_node))
                return NULL;
-       if (value_node->value_type != JSON_TYPE_ARRAY)
-               return json_tree_get_value_str(value_node);
+       if (!json_tree_node_is_array(value_node))
+               return json_tree_node_as_str(value_node);
 
-       const struct json_tree_node *entry_node = json_tree_get_child(value_node);
+       const struct json_tree_node *entry_node;
+
+       entry_node = json_tree_node_get_child(value_node);
        string_t *values = t_str_new(64);
 
-       for (; entry_node != NULL; entry_node = entry_node->next) {
-               if (entry_node->value_type == JSON_TYPE_OBJECT ||
-                   entry_node->value_type == JSON_TYPE_ARRAY)
+       for (; entry_node != NULL;
+            entry_node = json_tree_node_get_next(entry_node)) {
+               if (json_tree_node_is_object(entry_node) ||
+                   json_tree_node_is_array(entry_node))
                        continue;
 
-               const char *value_str = json_tree_get_value_str(entry_node);
+               const char *value_str = json_tree_node_as_str(entry_node);
 
                if (str_len(values) > 0)
                        str_append_c(values, '\t');
@@ -76,30 +82,32 @@ get_field_multiple(const struct json_tree *tree, const char *key)
 }
 
 static int
-get_time_field(const struct json_tree *tree, const char *key, int64_t *value_r)
+get_time_field(const struct json_tree *tree, const char *key,
+              int64_t *value_r)
 {
-       time_t tvalue;
-       enum json_type value_type;
-       const char *value = get_field(tree, key, &value_type);
-       int tz_offset ATTR_UNUSED;
+       const struct json_tree_node *value_node = get_field_node(tree, key);
 
-       if (value == NULL)
+       if (value_node == NULL)
                return 0;
+
+       enum json_type value_type = json_tree_node_get_type(value_node);
+
        if (value_type == JSON_TYPE_NUMBER) {
-               /* Parse with atof() to handle the json valid exponential
-                  formats, but discard the decimal part of the fields as we are
-                  not interested in them.
-
-                  The worst case of x.99999 would appear as almost a second
-                  older than the actual x which is same as saying we processed
-                  it a second later for the purpose of JWT tokens */
-               double v = atof(value);
-               *value_r = (int64_t) v;
-               if (*value_r < 0)
+               int64_t tstamp;
+
+               if (json_tree_node_get_int64(value_node, &tstamp) < 0 ||
+                   tstamp < 0)
                        return -1;
+               *value_r = tstamp;
                return 1;
-       } else if (iso8601_date_parse((const unsigned char*)value,
-                                     strlen(value), &tvalue, &tz_offset)) {
+       }
+
+       const char *value = json_tree_node_as_str(value_node);
+       time_t tvalue;
+       int tz_offset ATTR_UNUSED;
+
+       if (iso8601_date_parse((const unsigned char*)value, strlen(value),
+                              &tvalue, &tz_offset)) {
                if (tvalue < 0)
                        return -1;
                *value_r = tvalue;
@@ -356,7 +364,8 @@ struct jwt_node {
 };
 
 static void
-oauth2_jwt_copy_fields(ARRAY_TYPE(oauth2_field) *fields, struct json_tree *tree)
+oauth2_jwt_copy_fields(ARRAY_TYPE(oauth2_field) *fields,
+                      struct json_tree *tree)
 {
        pool_t pool = array_get_pool(fields);
        ARRAY(struct jwt_node) nodes;
@@ -366,24 +375,26 @@ oauth2_jwt_copy_fields(ARRAY_TYPE(oauth2_field) *fields, struct json_tree *tree)
        struct jwt_node *root = array_append_space(&nodes);
 
        root->prefix = "";
-       root->root = json_tree_root(tree);
+       root->root = json_tree_get_root_const(tree);
 
        while (array_count(&nodes) > 0) {
                const struct jwt_node *subroot = array_front(&nodes);
-               const struct json_tree_node *node = subroot->root;
+               const struct json_tree_node *tnode = subroot->root;
+
+               while (tnode != NULL) {
+                       const struct json_node *jnode =
+                               json_tree_node_get(tnode);
 
-               while (node != NULL) {
-                       if (node->value_type == JSON_TYPE_OBJECT ||
-                           node->value_type == JSON_TYPE_ARRAY) {
+                       if (!json_node_is_singular(jnode)) {
                                root = array_append_space(&nodes);
-                               root->root = node->value.child;
-                               root->array = node->value_type == JSON_TYPE_ARRAY;
-                               if (node->key == NULL)
+                               root->root = json_tree_node_get_child(tnode);
+                               root->array = json_node_is_array(jnode);
+                               if (jnode->name == NULL)
                                        root->prefix = subroot->prefix;
                                else if (*subroot->prefix != '\0')
-                                       root->prefix = t_strconcat(subroot->prefix, node->key, "_", NULL);
+                                       root->prefix = t_strconcat(subroot->prefix, jnode->name, "_", NULL);
                                else
-                                       root->prefix = t_strconcat(node->key, "_", NULL);
+                                       root->prefix = t_strconcat(jnode->name, "_", NULL);
                        } else {
                                struct oauth2_field *field;
                                const char *name;
@@ -404,10 +415,10 @@ oauth2_jwt_copy_fields(ARRAY_TYPE(oauth2_field) *fields, struct json_tree *tree)
                                        }
                                } else {
                                        field = array_append_space(fields);
-                                       field->name = p_strconcat(pool, subroot->prefix, node->key, NULL);
+                                       field->name = p_strconcat(pool, subroot->prefix, jnode->name, NULL);
                                }
 
-                               const char *value = str_tabescape(json_tree_get_value_str(node));
+                               const char *value = str_tabescape(json_node_as_str(jnode));
 
                                if (field->value != NULL) {
                                        field->value = p_strconcat(pool, field->value, "\t", value, NULL);
@@ -415,7 +426,7 @@ oauth2_jwt_copy_fields(ARRAY_TYPE(oauth2_field) *fields, struct json_tree *tree)
                                        field->value = p_strdup(pool, value);
                                }
                        }
-                       node = node->next;
+                       tnode = json_tree_node_get_next(tnode);
                }
                array_pop_front(&nodes);
        }
@@ -452,10 +463,11 @@ static bool check_scope(const char *req, const char *got)
 }
 
 static int
-oauth2_jwt_body_process(const struct oauth2_settings *set, const char *alg,
-                       const char *kid, ARRAY_TYPE(oauth2_field) *fields,
-                       struct json_tree *tree, const char *const *blobs,
-                       const char **error_r)
+oauth2_jwt_body_process(const struct oauth2_settings *set,
+                       const char *alg, const char *kid,
+                       ARRAY_TYPE(oauth2_field) *fields,
+                       struct json_tree *tree,
+                       const char *const *blobs, const char **error_r)
 {
        const char *sub = get_field(tree, "sub", NULL);
        int64_t t0 = time(NULL);
@@ -600,7 +612,7 @@ int oauth2_try_parse_jwt(const struct oauth2_settings *set,
 
        const char *alg, *kid;
        ret = oauth2_jwt_header_process(header_tree, &alg, &kid, error_r);
-       json_tree_deinit(&header_tree);
+       json_tree_unref(&header_tree);
        if (ret < 0)
                return -1;
 
@@ -624,7 +636,7 @@ int oauth2_try_parse_jwt(const struct oauth2_settings *set,
                return -1;
        ret = oauth2_jwt_body_process(set, alg, kid, fields, body_tree, blobs,
                                      error_r);
-       json_tree_deinit(&body_tree);
+       json_tree_unref(&body_tree);
 
        return ret;
 }
index 038f9e14a3ca3ec485fab6517e98fbf81c7867ed..2a8e74916e52c7b78a70d24ff269556961e8c7b0 100644 (file)
@@ -9,7 +9,7 @@ struct oauth2_request {
 
        const struct oauth2_settings *set;
        struct http_client_request *req;
-       struct json_parser *parser;
+       struct json_istream *json_istream;
        struct istream *is;
        struct io *io;
 
@@ -22,7 +22,6 @@ struct oauth2_request {
        void (*json_parsed_cb)(struct oauth2_request *, const char *error);
 
        ARRAY_TYPE(oauth2_field) fields;
-       char *field_name;
 
        oauth2_request_callback_t *req_callback;
        void *req_context;
@@ -31,7 +30,7 @@ struct oauth2_request {
 };
 
 void oauth2_request_parse_json(struct oauth2_request *req);
-int oauth2_json_tree_build(const buffer_t *json, struct json_tree **tree_r,
+int oauth2_json_tree_build(const buffer_t *json, struct json_tree **jtree_r,
                           const char **error_r);
 
 int oauth2_validation_key_cache_lookup_pubkey(
index 462e828b01712e0c0f5e7473299f5bcefae548bd..eb1bbdc35fabe23bd620538622c9b452bc65cad3 100644 (file)
@@ -6,12 +6,16 @@
 #include "str.h"
 #include "http-client.h"
 #include "http-url.h"
-#include "json-parser.h"
+#include "json-istream.h"
 #include "oauth2.h"
 #include "oauth2-private.h"
 
 static void oauth2_request_free(struct oauth2_request *req)
 {
+       json_istream_destroy(&req->json_istream);
+       io_remove(&req->io);
+       i_stream_unref(&req->is);
+
        timeout_remove(&req->to_delayed_error);
        pool_unref(&req->pool);
 }
@@ -85,50 +89,48 @@ static int request_field_cmp(const char *key, const struct oauth2_field *field)
 
 void oauth2_request_parse_json(struct oauth2_request *req)
 {
-       enum json_type type;
-       const char *token, *error;
+       struct json_node jnode;
+       struct json_istream *jinput = req->json_istream;
+       const char *error;
        int ret;
 
-       while((ret = json_parse_next(req->parser, &type, &token)) > 0) {
-               if (req->field_name == NULL) {
-                       if (type != JSON_TYPE_OBJECT_KEY) break;
-                       /* cannot use t_strdup because we might
-                          have to read more */
-                       req->field_name = p_strdup(req->pool, token);
-               } else if (type < JSON_TYPE_STRING) {
-                       /* this should be last allocation */
-                       p_free(req->pool, req->field_name);
-                       json_parse_skip(req->parser);
-               } else {
+       ret = 1;
+       while (ret > 0) {
+               ret = json_istream_read_next(jinput, &jnode);
+               if (ret <= 0)
+                       break;
+               i_assert(jnode.name != NULL);
+               if (json_node_is_singular(&jnode)) {
                        if (!array_is_created(&req->fields))
                                p_array_init(&req->fields, req->pool, 4);
                        struct oauth2_field *field =
                                array_append_space(&req->fields);
-                       field->name = req->field_name;
-                       req->field_name = NULL;
-                       field->value = p_strdup(req->pool, token);
+                       field->name = p_strdup(req->pool, jnode.name);
+                       field->value = p_strdup(req->pool,
+                                               json_node_get_str(&jnode));
                }
        }
 
-       /* read more */
-       if (ret == 0) return;
-
-       io_remove(&req->io);
+       if (ret == 0) {
+               /* need more data */
+               return;
+       }
 
-       if (ret > 0) {
-               (void)json_parser_deinit(&req->parser, &error);
-               error = "Invalid response data";
-       } else if (i_stream_read_eof(req->is) &&
-                  req->is->v_offset == 0 && req->is->stream_errno == 0) {
+       if (i_stream_read_eof(req->is) &&
+           req->is->v_offset == 0 && req->is->stream_errno == 0) {
                /* discard error, empty response is OK. */
-               (void)json_parser_deinit(&req->parser, &error);
-               error = NULL;
-       } else if (json_parser_deinit(&req->parser, &error) == 0) {
                error = NULL;
        } else {
-               i_assert(error != NULL);
+               ret = json_istream_finish(&req->json_istream, &error);
+               i_assert(ret != 0);
+               if (ret < 0) {
+                       error = t_strdup_printf("Invalid JSON in response: %s",
+                                               error);
+               }
        }
 
+       json_istream_destroy(&req->json_istream);
+       io_remove(&req->io);
        i_stream_unref(&req->is);
 
        /* check if fields contain error now */
@@ -171,7 +173,8 @@ oauth2_request_response(const struct http_response *response,
        }
 
        p_array_init(&req->fields, req->pool, 1);
-       req->parser = json_parser_init(req->is);
+       req->json_istream = json_istream_create_object(
+               req->is, NULL, JSON_PARSER_FLAG_NUMBERS_AS_STRING);
        req->json_parsed_cb = oauth2_request_continue;
        req->io = io_add_istream(req->is, oauth2_request_parse_json, req);
        oauth2_request_parse_json(req);
index b96995eab28763a3e568febfcb18e3b8723b2f89..1911fbbf25ddff194ee8d6f26c33fcf70f37f7a0 100644 (file)
@@ -3,34 +3,15 @@
 #include "lib.h"
 #include "buffer.h"
 #include "istream.h"
-#include "json-tree.h"
+#include "json-istream.h"
+#include "json-tree-io.h"
 #include "oauth2.h"
 #include "oauth2-private.h"
 
-int oauth2_json_tree_build(const buffer_t *json, struct json_tree **tree_r,
+int oauth2_json_tree_build(const buffer_t *json, struct json_tree **jtree_r,
                           const char **error_r)
 {
-       struct istream *is = i_stream_create_from_buffer(json);
-       struct json_parser *parser = json_parser_init(is);
-       struct json_tree *tree = json_tree_init();
-       enum json_type type;
-       const char *value;
-       int ret;
-
-       while ((ret = json_parse_next(parser, &type, &value)) > 0) {
-               /* this is safe to reuse here because it gets rewritten in while
-                  loop */
-               ret = json_tree_append(tree, type, value);
-               i_assert(ret == 0);
-       }
-       i_assert(ret != 0);
-       ret = json_parser_deinit(&parser, error_r);
-       i_stream_unref(&is);
-       if (ret != 0)
-               json_tree_deinit(&tree);
-       else
-               *tree_r = tree;
-       return ret;
+       return json_tree_read_buffer(json, 0, jtree_r, error_r);
 }
 
 bool oauth2_valid_token(const char *token)
index 3e50931b2ff7d860888d0fe135654c106ebeee4d..d5222bf43999a2452f29efa0742f6bcf3c55368a 100644 (file)
@@ -2,7 +2,8 @@
 
 #include "lib.h"
 #include "array.h"
-#include "json-parser.h"
+#include "istream.h"
+#include "json-istream.h"
 #include "oauth2.h"
 #include "oauth2-private.h"
 #include "test-common.h"
@@ -70,7 +71,8 @@ static void test_oauth2_json_valid(void)
        req->pool = pool;
        p_array_init(&req->fields, req->pool, 1);
        req->is = test_istream_create_data(test_input, strlen(test_input));
-       req->parser = json_parser_init(req->is);
+       req->json_istream = json_istream_create_object(
+               req->is, NULL, JSON_PARSER_FLAG_NUMBERS_AS_STRING);
        req->json_parsed_cb = test_oauth_json_valid_parsed;
        cb_got_called = FALSE;
 
@@ -124,7 +126,8 @@ static void test_oauth2_json_error(void)
        req->pool = pool;
        p_array_init(&req->fields, req->pool, 1);
        req->is = test_istream_create_data(test_input_1, strlen(test_input_1));
-       req->parser = json_parser_init(req->is);
+       req->json_istream = json_istream_create_object(
+               req->is, NULL, JSON_PARSER_FLAG_NUMBERS_AS_STRING);
        req->req_context = "invalid_request";
        req->json_parsed_cb = test_oauth_json_has_error;
        cb_got_called = FALSE;
@@ -145,7 +148,8 @@ static void test_oauth2_json_error(void)
        req->pool = pool;
        p_array_init(&req->fields, req->pool, 1);
        req->is = test_istream_create_data(test_input_2, strlen(test_input_2));
-       req->parser = json_parser_init(req->is);
+       req->json_istream = json_istream_create_object(
+               req->is, NULL, JSON_PARSER_FLAG_NUMBERS_AS_STRING);
        req->req_context = "Access denied";
        req->json_parsed_cb = test_oauth_json_has_error;
        cb_got_called = FALSE;
index 541b49216d1201031b0d2b438432256a83fbc3ac..2a966c81ef01ae8d52d833414534fb076f64a288 100644 (file)
@@ -9,8 +9,8 @@
 #include "base64.h"
 #include "randgen.h"
 #include "array.h"
-#include "json-parser.h"
 #include "iso8601-date.h"
+#include "json-generator.h"
 #include "oauth2.h"
 #include "oauth2-private.h"
 #include "dcrypt.h"
@@ -756,7 +756,7 @@ static void test_jwt_nested_fields(void)
                                             "\"aud\":[\"dacity\",\"iron\"],"
                                             "\"user\":{"
                                                "\"name\":\"test\","
-                                               "\"features\":[\"one\",\"two\",\"tap\tdance\"],"
+                                               "\"features\":[\"one\",\"two\",\"tap\\tdance\"],"
                                                "\"enabled\":true,"
                                                "\"locations\":{\"shared\":true,\"private\":false}"
                                             "},"