Commit 6cec910c authored by jan.koester's avatar jan.koester
Browse files

export import implemented

parent 9f6b421f
Loading
Loading
Loading
Loading
Loading
+54 −1
Changes for data/admin.html: 54 added lines, 1 removed line.
Original line number Diff line number Diff line
@@ -308,9 +308,13 @@ function loadDomains() {
                }
            }
            html += '</table>';
            html += '<ul class="domaintoolbar">';
            if (isSuperAdmin) {
                html += '<ul class="domaintoolbar"><li><a class="button" href="#" onclick="createDomainForm(); return false;">CreateDomain</a></li></ul>';
                html += '<li><a class="button" href="#" onclick="createDomainForm(); return false;">CreateDomain</a></li>';
            }
            html += '<li><a class="button" href="#" onclick="exportDb(); return false;">Export</a></li>';
            html += '<li><a class="button" href="#" onclick="importDbForm(); return false;">Import</a></li>';
            html += '</ul>';
            setContent(html);
        });
    });
@@ -348,6 +352,55 @@ function removeDomain(did) {
    });
}

function exportDb() {
    ajaxGet("/settings/" + currentDomain + "/api/export", function(err, data) {
        if (err) { showError(err); return; }
        var jsonStr = JSON.stringify(data, null, 2);
        var blob = new Blob([jsonStr], {type: "application/json"});
        var a = document.createElement("a");
        a.href = URL.createObjectURL(blob);
        a.download = currentDomain + "-export.json";
        document.body.appendChild(a);
        a.click();
        document.body.removeChild(a);
        URL.revokeObjectURL(a.href);
    });
}

function importDbForm() {
    var html = '<h3>Import Database</h3>';
    html += '<form id="importdbform" class="admin-form">';
    html += '<table>';
    html += '<tr><td><label>JSON File:</label></td><td><input type="file" id="import_file" accept=".json"></td></tr>';
    html += '<tr><td></td><td><input type="button" value="Import" onclick="submitImportDb()"></td></tr>';
    html += '</table></form>';
    setContent(html);
}

function submitImportDb() {
    var fileInput = document.getElementById("import_file");
    if (!fileInput.files || !fileInput.files[0]) {
        showError("Bitte eine JSON-Datei auswaehlen!");
        return;
    }
    var reader = new FileReader();
    reader.onload = function(e) {
        var jsonData;
        try {
            jsonData = JSON.parse(e.target.result);
        } catch(ex) {
            showError("Ungueltige JSON-Datei: " + ex.message);
            return;
        }
        ajaxPost("/settings/" + currentDomain + "/api/import", jsonData, function(err, data) {
            if (err) { showError(err); return; }
            alert("Import erfolgreich: " + (data.imported || 0) + " Records importiert.");
            loadDomains();
        });
    };
    reader.readAsText(fileInput.files[0]);
}

/* ---- Groups ---- */
function loadGroups() {
    currentView = loadGroups;
+2 −0
Changes for src/CMakeLists.txt: 2 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -10,6 +10,8 @@ list(APPEND authdbSrcs
    client.cpp
    hash.cpp
    hash/sha512.cpp
    export.cpp
    import.cpp
)

add_library( authobj OBJECT ${authdbSrcs} )
+39 −0
Changes for src/admin.cpp: 39 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -48,6 +48,8 @@
#include "gpo.h"
#include "client.h"
#include "session.h"
#include "export.h"
#include "import.h"

namespace authdb {

@@ -1230,6 +1232,37 @@ namespace authdb {
            json_object_put(jobj);
        }

        /* ---- JSON API: Export Database ---- */
        void apiExportDb(libhttppp::HttpRequest &curreq, const char *cdid) {
            DomainBackend dbackend(_AdminBackend);
            AuthBackend *backend = resolveDomain(cdid, _AdminBackend, dbackend);
            if (!backend) throw AuthBackendError("export: could not resolve domain!");

            json_object *jobj = Export::toJson(*backend);
            sendJson(curreq, jobj);
            json_object_put(jobj);
        }

        /* ---- JSON API: Import Database ---- */
        void apiImportDb(libhttppp::HttpRequest &curreq, const char *cdid) {
            DomainBackend dbackend(_AdminBackend);
            AuthBackend *backend = resolveDomain(cdid, _AdminBackend, dbackend);
            if (!backend) throw AuthBackendError("import: could not resolve domain!");

            std::string reqbody(curreq.RecvData.begin(), curreq.RecvData.end());
            json_object *jreq = json_tokener_parse(reqbody.c_str());
            if (!jreq) throw AuthBackendError("import: invalid JSON!");

            int count = Import::fromJson(*backend, jreq);
            json_object_put(jreq);

            json_object *jobj = json_object_new_object();
            json_object_object_add(jobj, "status", json_object_new_string("ok"));
            json_object_object_add(jobj, "imported", json_object_new_int(count));
            sendJson(curreq, jobj);
            json_object_put(jobj);
        }

        /* ---- Login (keeps HTML form-based login for compatibility) ---- */
        void login(libhttppp::HttpRequest &curreq, const int tid, ULONG_PTR args) {
            libhttppp::HttpForm form;
@@ -1488,6 +1521,12 @@ namespace authdb {
                    } else if (strcmp(apiurl, "editclient") == 0 && param[0]) {
                        checkDomainAccess(sdat, domain);
                        apiEditClient(curreq, domain, param);
                    } else if (strcmp(apiurl, "export") == 0) {
                        checkDomainAccess(sdat, domain);
                        apiExportDb(curreq, domain);
                    } else if (strcmp(apiurl, "import") == 0) {
                        checkDomainAccess(sdat, domain);
                        apiImportDb(curreq, domain);
                    } else {
                        sendJsonError(curreq, "Unknown API endpoint", 404);
                    }
+108 −0
Changes for src/export.cpp: 108 added lines, 0 removed lines.
Original line number Diff line number Diff line
/*******************************************************************************
 * Copyright (c) 2025, Jan Koester jan.koester@gmx.net
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * Redistributions of source code must retain the above copyright
 *      notice, this list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright
 *      notice, this list of conditions and the following disclaimer in the
 *      documentation and/or other materials provided with the distribution.
 * Neither the name of the <organization> nor the
 *      names of its contributors may be used to endorse or promote products
 *      derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *******************************************************************************/

#include <memory>
#include <cstring>
#include <vector>
#include <string>

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

#include "export.h"
#include "types.h"

namespace authdb {

    json_object* Export::toJson(AuthBackend &backend) {
        json_object *jroot = json_object_new_object();
        json_object *jarr  = json_object_new_array();

        size_t rd = sizeof(AuthHeader), end = backend.end();

        while (rd < end) {
            std::shared_ptr<AuthData::Record> cur = std::make_shared<AuthData::Record>();
            cur->type = EmptyData;
            backend.setPos(rd);
            backend.read(reinterpret_cast<unsigned char*>(cur.get()), sizeof(AuthData::Record));
            rd = backend.getPos() + cur->datasize;

            // Skip empty/deleted records
            if (cur->type == EmptyData)
                continue;

            // Read record data
            std::vector<char> rawdata;
            if (cur->datasize > 0) {
                rawdata.resize(cur->datasize);
                backend.read(reinterpret_cast<unsigned char*>(rawdata.data()), cur->datasize);
            }

            // Build JSON record
            json_object *jrec = json_object_new_object();

            // ruid as uuid string
            json_object_object_add(jrec, "ruid",
                json_object_new_string(uuid::uuid(cur->ruid).c_str()));

            json_object_object_add(jrec, "type",
                json_object_new_int(cur->type));

            json_object_object_add(jrec, "storage",
                json_object_new_int(cur->storage));

            json_object_object_add(jrec, "fieldname",
                json_object_new_string(cur->fieldname));

            json_object_object_add(jrec, "datasize",
                json_object_new_int64(static_cast<int64_t>(cur->datasize)));

            json_object_object_add(jrec, "count",
                json_object_new_int64(static_cast<int64_t>(cur->count)));

            // Base64 encode the data
            if (!rawdata.empty()) {
                netplus::base64 b64;
                b64 << rawdata;
                std::string encoded;
                b64 >> encoded;
                json_object_object_add(jrec, "data",
                    json_object_new_string(encoded.c_str()));
            } else {
                json_object_object_add(jrec, "data",
                    json_object_new_string(""));
            }

            json_object_array_add(jarr, jrec);
        }

        json_object_object_add(jroot, "version", json_object_new_int(VERSION));
        json_object_object_add(jroot, "records", jarr);

        return jroot;
    }
};
+43 −0
Changes for src/export.h: 43 added lines, 0 removed lines.
Original line number Diff line number Diff line
/*******************************************************************************
 * Copyright (c) 2025, Jan Koester jan.koester@gmx.net
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * Redistributions of source code must retain the above copyright
 *      notice, this list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright
 *      notice, this list of conditions and the following disclaimer in the
 *      documentation and/or other materials provided with the distribution.
 * Neither the name of the <organization> nor the
 *      names of its contributors may be used to endorse or promote products
 *      derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *******************************************************************************/

#pragma once

#include <json-c/json.h>
#include "authdb.h"

namespace authdb {
    class Export {
    public:
        /**
         * Export all records from a backend to a JSON object.
         * Record data is base64-encoded.
         * Returns a new json_object* — caller must json_object_put() it.
         */
        static json_object* toJson(AuthBackend &backend);
    };
};
Loading