Commit b539b3ce authored by Scott Vokes's avatar Scott Vokes
Browse files

Accumulate multiple ACLs form a single JSON file, when present.

parent 56040f5d
Loading
Loading
Loading
Loading
+129 −5
Original line number Diff line number Diff line
@@ -19,7 +19,10 @@
*/
#include <string.h>
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>

#include "kinetic_logger.h"
#include "acl.h"
#include "json.h"

@@ -42,6 +45,10 @@ static permission_pair permission_table[] = {

#define PERM_TABLE_ROWS sizeof(permission_table)/sizeof(permission_table)[0]

static acl_of_file_res read_ACLs(const char *buf, size_t buf_size, struct ACL **instance);
static acl_of_file_res read_next_ACL(const char *buf, size_t buf_size,
    size_t offset, size_t *new_offset, struct json_tokener *tokener,
    struct ACL **instance);
static acl_of_file_res unpack_scopes(struct ACL *acl,
    int scope_count, json_object *scopes);

@@ -67,8 +74,124 @@ acl_of_file(const char *path, struct ACL **instance) {
        return ACL_ERROR_NULL;
    }
    
    struct json_object *obj = json_object_from_file(path);
    if (obj == NULL) { return ACL_ERROR_BAD_JSON; }
    int fd = open(path, O_RDONLY);
    if (fd == -1) {
#ifndef TEST
        LOGF0("Failed ot open file '%s': %s", path, strerror(errno));
#endif
        errno = 0;
        return ACL_ERROR_BAD_JSON;
    }
    acl_of_file_res res = ACL_ERROR_NULL;

    const int BUF_START_SIZE = 256;
    char *buf = malloc(BUF_START_SIZE);
    if (buf == NULL) {
        res = ACL_ERROR_MEMORY;
        goto cleanup;
    }
    size_t buf_sz = BUF_START_SIZE;
    size_t buf_used = 0;

    ssize_t read_sz = 0;
    for (;;) {
        read_sz = read(fd, &buf[buf_used], buf_sz - buf_used);
        if (read_sz == -1) {
            res = ACL_ERROR_JSON_FILE;
            goto cleanup;
        } else if (read_sz == 0) {
            break;
        } else {
            buf_used += read_sz;
            if (buf_sz == buf_used) {
                size_t nsz = 2 * buf_sz;
                char *nbuf = realloc(buf, nsz);
                if (nbuf) {
                    buf_sz = nsz;
                    buf = nbuf;
                }
            }
        }
    }

#ifndef TEST
    LOGF2(" -- read %zd bytes, parsing...\n", buf_used);
#endif
    res = read_ACLs(buf, buf_used, instance);
cleanup:
    if (buf) { free(buf); }
    close(fd);
    return res;
}

static acl_of_file_res read_ACLs(const char *buf, size_t buf_size, struct ACL **instance) {
    struct ACL *cur = NULL;
    struct ACL *first = NULL;

    struct json_tokener* tokener = json_tokener_new();
    if (tokener == NULL) { return ACL_ERROR_MEMORY; }

    size_t offset = 0;

    acl_of_file_res res = ACL_ERROR_NULL;
    while (buf_size - offset > 0) {
        size_t offset_out = 0;
        struct ACL *new_acl = NULL;
#ifndef TEST
        LOGF2(" -- reading next ACL at offset %zd, rem %zd\n", offset, buf_size - offset);
#endif
        res = read_next_ACL(buf, buf_size, offset,
            &offset_out, tokener, &new_acl);
        offset += offset_out;
#ifndef TEST
        LOGF2(" -- result %d, offset_out %zd\n", res, offset);
#endif
        if (res == ACL_OK) {
            if (first == NULL) {
                first = new_acl;
                *instance = first;
                cur = first;
            } else {
                assert(cur);
                cur->next = new_acl;
                cur = new_acl;
            }
        } else {
            break;
        }
    }

    /* cleanup */
    json_tokener_free(tokener);
    
    if (res == ACL_END_OF_STREAM || res == ACL_OK) {
        if (first == NULL) {
            LOG2("Failed to read any JSON objects\n");
            return ACL_ERROR_BAD_JSON;
        } else {            /* read at least one ACL */
            return ACL_OK;
        }
    } else {
        acl_free(first);
        return res;
    }
}

static acl_of_file_res read_next_ACL(const char *buf, size_t buf_size,
        size_t offset, size_t *new_offset,
        struct json_tokener *tokener, struct ACL **instance) {
    struct json_object *obj = json_tokener_parse_ex(tokener,
        &buf[offset], buf_size - offset);
    if (obj == NULL) {
        if (json_tokener_get_error(tokener) == json_tokener_error_parse_eof) {
            return ACL_END_OF_STREAM;
        } else {
            LOGF2("JSON error %d\n", json_tokener_get_error(tokener));
            return ACL_ERROR_BAD_JSON;
        }
    }
    
    *new_offset = tokener->char_offset;
    
    acl_of_file_res res = ACL_ERROR_NULL;
    
@@ -134,7 +257,6 @@ acl_of_file(const char *path, struct ACL **instance) {
    json_object_put(obj);
    *instance = acl;
    return ACL_OK;
    
cleanup:
    if (obj) { json_object_put(obj); }
    return res;
@@ -239,11 +361,13 @@ void acl_fprintf(FILE *f, struct ACL *acl) {
}

void acl_free(struct ACL *acl) {
    if (acl) {
    while (acl) {
        if (acl->hmacKey) { free(acl->hmacKey); }
        for (size_t i = 0; i < acl->scopeCount; i++) {
            if (acl->scopes[i].value) { free(acl->scopes[i].value); }
        }
        struct ACL *next = acl->next;
        free(acl);
        acl = next;
    }
}
+6 −3
Original line number Diff line number Diff line
@@ -48,16 +48,19 @@ struct ACL {
    int64_t identity;
    struct hmac_key *hmacKey;
    size_t scopeCount;
    struct ACL *next;
    struct acl_scope scopes[];
};

typedef enum {
    ACL_OK = 0,
    ACL_END_OF_STREAM = 1,
    ACL_ERROR_NULL = -1,
    ACL_ERROR_MEMORY = -2,
    ACL_ERROR_BAD_JSON = -3,
    ACL_ERROR_MISSING_FIELD = -4,
    ACL_ERROR_INVALID_FIELD = -5,
    ACL_ERROR_JSON_FILE = -3,
    ACL_ERROR_BAD_JSON = -4,
    ACL_ERROR_MISSING_FIELD = -5,
    ACL_ERROR_INVALID_FIELD = -6,
} acl_of_file_res;

#endif
+28 −0
Original line number Diff line number Diff line
{
    "identity": 1,
    "key": "a3b38c37298f7f01a377518dae81dd99655b2be8129c3b2c6357b7e779064159",
    "HMACAlgorithm": "HmacSHA1",
    "scope": [
        {
            "permission": "READ"
        },
        {
            "offset": 0,
            "value": "foo",
            "permission": "WRITE"
        }
    ]
}

{
    "identity": 2,
    "key": "13010b8d8acdbe6abc005840aad1dc5dedb4345e681ed4e3c4645d891241d6b2",
    "HMACAlgorithm": "HmacSHA1", 

    "scope": [
        {
            "permission": "SECURITY",
            "TlsRequired": "true"
        }
    ]
}
+46 −1
Original line number Diff line number Diff line
@@ -22,6 +22,8 @@
#include <string.h>
#include <stdio.h>

#include "mock_kinetic_logger.h"

#define TEST_DIR(F) ("test/unit/acl/" F)

void test_acl_of_empty_JSON_object_should_fail(void)
@@ -34,7 +36,8 @@ void test_acl_of_empty_JSON_object_should_fail(void)
void test_acl_of_nonexistent_file_should_fail(void)
{
    struct ACL *acl = NULL;
    acl_of_file_res res = acl_of_file(TEST_DIR("nonexistent.json"), &acl);
    const char *path = TEST_DIR("nonexistent.json");
    acl_of_file_res res = acl_of_file(path, &acl);
    TEST_ASSERT_EQUAL(ACL_ERROR_BAD_JSON, res);
}

@@ -136,3 +139,45 @@ void test_acl_of_file_should_handle_multiple_permissions(void)

    acl_free(acl);
}

void test_acl_should_handle_multiple_JSON_objects(void)
{
    struct ACL *acl = NULL;
    acl_of_file_res res = acl_of_file(TEST_DIR("ex_multi.json"), &acl);
    TEST_ASSERT_EQUAL(ACL_OK, res);

    TEST_ASSERT_EQUAL(1, acl->identity);

    TEST_ASSERT_EQUAL(HMAC_SHA1, acl->hmacKey->type);
    TEST_ASSERT_EQUAL(64, acl->hmacKey->length);
    TEST_ASSERT_EQUAL(0, strcmp((const char *)acl->hmacKey->key,
            "a3b38c37298f7f01a377518dae81dd99655b2be8129c3b2c6357b7e779064159"));

    TEST_ASSERT_EQUAL(2, acl->scopeCount);

    TEST_ASSERT_EQUAL(1, acl->scopes[0].permission_count);
    TEST_ASSERT_EQUAL(PERM_READ, acl->scopes[0].permissions[0]);

    TEST_ASSERT_EQUAL(0, acl->scopes[1].offset);
    TEST_ASSERT_EQUAL(1, acl->scopes[1].permission_count);
    TEST_ASSERT_EQUAL(PERM_WRITE, acl->scopes[1].permissions[0]);
    TEST_ASSERT_EQUAL(3, acl->scopes[1].valueSize);
    TEST_ASSERT_EQUAL(0, strcmp((char *)acl->scopes[1].value, "foo"));

    struct ACL *acl2 = acl->next;
    TEST_ASSERT_NOT_NULL(acl2);

    TEST_ASSERT_EQUAL(2, acl2->identity);

    TEST_ASSERT_EQUAL(HMAC_SHA1, acl2->hmacKey->type);
    TEST_ASSERT_EQUAL(64, acl2->hmacKey->length);
    TEST_ASSERT_EQUAL(0, strcmp((const char *)acl2->hmacKey->key,
            "13010b8d8acdbe6abc005840aad1dc5dedb4345e681ed4e3c4645d891241d6b2"));

    TEST_ASSERT_EQUAL(1, acl2->scopeCount);
    TEST_ASSERT_EQUAL(1, acl2->scopes[0].permission_count);
    TEST_ASSERT_EQUAL(PERM_SECURITY, acl2->scopes[0].permissions[0]);
    TEST_ASSERT_EQUAL(true, acl2->scopes[0].tlsRequired);

    acl_free(acl);
}