Commit e6d9b77d authored by Jan Koester's avatar Jan Koester
Browse files

test

parent 2f2434cf
Loading
Loading
Loading
Loading
Loading
+63 −9
Changes for src/import.cpp: 63 added lines, 9 removed lines.
Original line number Diff line number Diff line
@@ -29,6 +29,9 @@
#include <vector>
#include <string>

#include <set>
#include <utility>

#include <netplus/crypto/base64.h>
#include <uuidp.h>

@@ -38,6 +41,26 @@

namespace fenrirds {

    namespace {
        struct ImportRecord {
            AuthData::Record  rec;
            std::vector<char> data;
        };

        /* dirname of a stored DirectoryObject, empty if it has none */
        std::string storedDirName(AuthBackend &backend, const uuid::uuid &guid) {
            AuthData dat(guid);
            if (!backend.getRecord(dat, DataType::DirectoryObject))
                return "";
            for (const AuthData *c = &dat; c; c = c->next()) {
                if (c->Data.type == DataType::DirectoryObject &&
                    strcmp(c->Data.fieldname, "dirname") == 0 && c->Data.datasize)
                    return std::string(c->Data.data, strnlen(c->Data.data, c->Data.datasize));
            }
            return "";
        }
    }

    int Import::fromJson(AuthBackend &backend, json_object *jroot) {
        AuthBackend::Guard guard(backend);
        if (!jroot)
@@ -47,7 +70,7 @@ namespace fenrirds {
        if (!jrecords || !json_object_is_type(jrecords, json_type_array))
            throw AuthBackendError("import: missing or invalid 'records' array!");

        int count = 0;
        std::vector<ImportRecord> records;
        size_t len = json_object_array_length(jrecords);

        for (size_t i = 0; i < len; ++i) {
@@ -65,7 +88,8 @@ namespace fenrirds {
                continue;

            // Build the record
            AuthData::Record rec{};
            ImportRecord ir{};
            AuthData::Record &rec = ir.rec;
            rec.start = 0xFE;

            // Parse ruid
@@ -84,30 +108,60 @@ namespace fenrirds {
            strncpy(rec.fieldname, json_object_get_string(jfieldname), sizeof(rec.fieldname) - 1);

            // Base64 decode the data
            std::vector<char> rawdata;
            if (jdata) {
                const char *b64str = json_object_get_string(jdata);
                if (b64str && strlen(b64str) > 0) {
                    netplus::base64 b64;
                    b64.setEncodedData(std::string(b64str));
                    b64 >> rawdata;
                    b64 >> ir.data;
                }
            }

            if (!rawdata.empty()) {
                rec.datasize = rawdata.size();
            if (!ir.data.empty()) {
                rec.datasize = ir.data.size();
            } else if (jdatasize) {
                rec.datasize = static_cast<size_t>(json_object_get_int64(jdatasize));
            } else {
                rec.datasize = 0;
            }

            records.push_back(std::move(ir));
        }

        // The import is authoritative: drop existing objects it would
        // duplicate -- same uuid/type, or a directory object at the same DN
        // (the well-known objects a new domain provisions with fresh GUIDs).
        std::set<std::pair<std::string, int>> keys;
        std::set<std::string> dirnames;
        for (const auto &ir : records) {
            keys.emplace(uuid::uuid(ir.rec.ruid).c_str(), ir.rec.type);
            if (ir.rec.type == DataType::DirectoryObject &&
                strcmp(ir.rec.fieldname, "dirname") == 0 && !ir.data.empty())
                dirnames.emplace(ir.data.data(), strnlen(ir.data.data(), ir.data.size()));
        }

        RecordIndex &idx = backend.getIndex();
        if (!idx.valid() || idx.revision() != backend.getRevesion())
            idx.build(backend);
        std::vector<uuid::uuid> existing;
        idx.listByType(DataType::DirectoryObject, existing);
        for (const auto &guid : existing) {
            if (keys.count({guid.c_str(), DataType::DirectoryObject}))
                continue;
            if (dirnames.count(storedDirName(backend, guid)))
                backend.delRecord(guid, DataType::DirectoryObject);
        }
        for (const auto &key : keys)
            backend.delRecord(uuid::uuid(key.first.c_str()), key.second);

        int count = 0;
        for (const auto &ir : records) {
            // Write record to backend
            size_t wd = backend.end();
            backend.setPos(wd);
            backend.write(reinterpret_cast<unsigned char*>(&rec), sizeof(AuthData::Record));
            if (!rawdata.empty()) {
                backend.write(reinterpret_cast<unsigned char*>(rawdata.data()), rawdata.size());
            backend.write(reinterpret_cast<const unsigned char*>(&ir.rec), sizeof(AuthData::Record));
            if (!ir.data.empty()) {
                backend.write(reinterpret_cast<const unsigned char*>(ir.data.data()), ir.data.size());
            }

            ++count;