Commit 6599e4ea authored by Jan Koester's avatar Jan Koester
Browse files

test

parent ffffb03b
Loading
Loading
Loading
Loading
Loading
+17 −0
Changes for data/admin.html: 17 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -448,6 +448,7 @@ function loadDomains() {
            html += '<ul class="domaintoolbar">';
            if (isSuperAdmin) {
                html += '<li><a class="button primary" href="#" onclick="createDomainForm(); return false;">CreateDomain</a></li>';
                html += '<li><a class="button" href="#" onclick="migrateNative(); return false;">Native Konten migrieren</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>';
@@ -458,6 +459,22 @@ function loadDomains() {
    });
}

/* Fold every domain's leftover native accounts into the directory (uids are kept). */
function migrateNative() {
    if (!confirm("Alle nativen Benutzer/Gruppen aller Domains ins Verzeichnis übernehmen? Die nativen Einträge der übernommenen Benutzer werden danach entfernt.")) return;
    ajaxPost("/settings/admin/api/migratenative", {}, function(err, data) {
        if (err) { showError(err); return; }
        var lines = ["Migriert: " + data.migrated];
        (data.domains || []).forEach(function(d) {
            if (d.migrated || (d.skipped && d.skipped.length))
                lines.push(d.domain + ": " + d.migrated + " migriert" +
                           (d.skipped && d.skipped.length ? ", übersprungen (Name existiert schon im Verzeichnis): " + d.skipped.join(", ") : ""));
        });
        alert(lines.join("\n"));
        loadDomains();
    });
}

function generateUUID() {
    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
        var r = Math.random() * 16 | 0, v = c === 'x' ? r : (r & 0x3 | 0x8);
+18 −4
Changes for plugin/ad/ad.cpp: 18 added lines, 4 removed lines.
Original line number Diff line number Diff line
@@ -909,7 +909,8 @@ bool ActiveDirectory::dirRemoveMember(const std::string &domain, const uuid::uui

bool ActiveDirectory::dirImportNative(const std::string &domain, int &migrated,
                                      std::string &err,
                                      std::map<std::string, std::string> *groupMap) {
                                      std::map<std::string, std::string> *groupMap,
                                      std::vector<std::string> *skipped) {
    if (g_Replication && g_Replication->isSlave()) {
        err = "this node is a replication slave (read-only); make changes on the master";
        return false;
@@ -924,6 +925,7 @@ bool ActiveDirectory::dirImportNative(const std::string &domain, int &migrated,
    dsdb::Dn users = d->meta.baseDn.child("CN", "Users");

    /* native UserData -> SID-less directory user, sha512 hash kept in @webHash */
    std::vector<uuid::uuid> migratedUids;
    {
        User user;
        std::vector<uuid::uuid> uids;
@@ -933,9 +935,11 @@ bool ActiveDirectory::dirImportNative(const std::string &domain, int &migrated,
            try { size_t p = sizeof(AuthHeader); user.info(*bck, udat, p); } catch (...) { continue; }
            std::string sam = udat.getUsername();
            if (sam.empty()) continue;
            if (d->dir->bySamAccountName(sam)) continue;
            dsdb::Dn dn = users.child("CN", sam);
            if (d->dir->byDn(dn)) continue;
            if (d->dir->bySamAccountName(sam) || d->dir->byDn(dn)) {
                if (skipped) skipped->push_back(sam);
                continue;
            }
            dsdb::AttrMap a;
            a["objectClass"]    = {"top", "person", "organizationalPerson", "user"};
            a["objectCategory"] = {"CN=Person,CN=Schema,CN=Configuration," + d->meta.baseDn.str()};
@@ -947,7 +951,10 @@ bool ActiveDirectory::dirImportNative(const std::string &domain, int &migrated,
            try { std::string v = udat.getMail();      if (!v.empty()) a["mail"] = {v}; } catch (...) {}
            try { std::string v = udat.getPwHash();    if (!v.empty()) a["@webHash"] = {v}; } catch (...) {}
            try { std::vector<char> av; udat.getAvatar(av); if (!av.empty()) a["thumbnailPhoto"] = {std::string(av.begin(), av.end())}; } catch (...) {}
            try { d->dir->create(dn, a, nullptr); ++migrated; }
            try { std::vector<char> sg; udat.getSignature(sg); if (!sg.empty()) a["fenrirdsSignature"] = {std::string(sg.begin(), sg.end())}; } catch (...) {}
            /* keep the native uid unless some other object already owns it */
            const uuid::uuid *keep = d->dir->byGuid(uid) ? nullptr : &uid;
            try { d->dir->create(dn, a, nullptr, keep); ++migrated; migratedUids.push_back(uid); }
            catch (std::exception &e) { std::cerr << "[AD] dirImportNative: user '" << sam << "' skipped: " << e.what() << std::endl; }
        }
        std::cerr << "[AD] dirImportNative: native users=" << uids.size() << std::endl;
@@ -1030,6 +1037,13 @@ bool ActiveDirectory::dirImportNative(const std::string &domain, int &migrated,
        }
        std::cerr << "[AD] dirImportNative: native groups=" << gids.size() << std::endl;
    }

    /* drop the migrated native users only now: the group pass above still
     * resolves native members through them */
    for (const auto &uid : migratedUids) {
        try { User().remove(*bck, uid); }
        catch (std::exception &e) { std::cerr << "[AD] dirImportNative: native user " << uid.c_str() << " not removed: " << e.what() << std::endl; }
    }
    std::cerr << "[AD] dirImportNative domain=" << domain << " migrated=" << migrated << std::endl;
    return true;
}
+2 −1
Changes for plugin/ad/ad.h: 2 added lines, 1 removed line.
Original line number Diff line number Diff line
@@ -282,7 +282,8 @@ namespace fenrirds {
        void reloadDomains() override { loadDomains(); }
        bool dirImportNative(const std::string &domain, int &migrated,
                             std::string &err,
                             std::map<std::string, std::string> *groupMap = nullptr) override;
                             std::map<std::string, std::string> *groupMap = nullptr,
                             std::vector<std::string> *skipped = nullptr) override;
        bool dirSetWindowsLogin(const std::string &domain, const uuid::uuid &guid,
                                bool enable, const std::string &clearPassword,
                                std::string &err) override;
+80 −31
Changes for src/admin.cpp: 80 added lines, 31 removed lines.
Original line number Diff line number Diff line
@@ -2256,46 +2256,33 @@ namespace fenrirds {
            json_object_put(jobj);
        }

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

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

            int count = 0;
            {
                /* scoped: release the backend write lock before migrating, so
                 * the directory's own locking (below) can't deadlock against it */
                AuthBackend::Guard guard(*backend);
                count = Import::fromJson(*backend, jreq);
            }
            json_object_put(jreq);

            /* Import restores raw native records. For a real domain, fold any
             * native UserData/GroupData into the directory (single source) so
             * imported users/groups show up and can log in. */
        /* Fold a domain's native UserData/GroupData into the directory (single
         * source of truth, see AdPlugin::dirImportNative), point GPOs that
         * still allow native group uuids at the migrated directory groups,
         * and make sure the domain has the default policies. Returns the
         * number of migrated objects. */
        int migrateNativeDomain(const char *cdid, AuthBackend &backend,
                                std::vector<std::string> *skipped = nullptr) {
            int migrated = 0;
            if (strcmp(cdid, "admin") != 0 && g_AdPlugin) {
            if (strcmp(cdid, "admin") == 0 || !g_AdPlugin)
                return 0;
            std::string domain = domainNameFor(cdid);
            std::string err;
            std::map<std::string, std::string> groupMap;
                if (!domain.empty() && !g_AdPlugin->dirImportNative(domain, migrated, err, &groupMap))
                    std::cerr << "import: dirImportNative failed: " << err << std::endl;
            if (!domain.empty() && !g_AdPlugin->dirImportNative(domain, migrated, err, &groupMap, skipped))
                std::cerr << "migrateNative: dirImportNative failed for " << domain << ": " << err << std::endl;

            /* imported GPOs still allow the native group uuids: point them
             * at the directory groups those were migrated to */
            if (!groupMap.empty()) {
                    AuthBackend::Guard g(*backend);
                AuthBackend::Guard g(backend);
                Gpo gpo;
                std::vector<uuid::uuid> gpoids;
                    gpo.list(*backend, gpoids);
                gpo.list(backend, gpoids);
                for (const auto &gpoid : gpoids) {
                    class GpoData gd(gpoid);
                    size_t gp = sizeof(AuthHeader);
                        try { gpo.info(*backend, gd, gp); } catch (...) { continue; }
                    try { gpo.info(backend, gd, gp); } catch (...) { continue; }
                    std::vector<uuid::uuid> allow;
                    gd.getAlowed(allow);
                    bool changed = false;
@@ -2308,7 +2295,7 @@ namespace fenrirds {
                    }
                    if (!changed) continue;
                    gd.setAllowed(allow);
                        try { gpo.edit(*backend, gd); } catch (...) {}
                    try { gpo.edit(backend, gd); } catch (...) {}
                }
            }

@@ -2326,12 +2313,12 @@ namespace fenrirds {
                }
                std::vector<uuid::uuid> allowed;
                if (haveAdmins) allowed.push_back(adminsGuid);
                    AuthBackend::Guard g(*backend);
                AuthBackend::Guard g(backend);
                Gpo gpo;
                for (size_t i = 0; gpo_default[i][0] != nullptr; ++i) {
                    uuid::uuid gpoid(gpo_default[i][0]);
                    int ret = 0;
                        if (gpo.exists(*backend, gpoid, nullptr, ret))
                    if (gpo.exists(backend, gpoid, nullptr, ret))
                        continue;
                    class GpoData gd(gpoid);
                    gd.setName(gpo_default[i][1]);
@@ -2339,10 +2326,70 @@ namespace fenrirds {
                    bool defval = (strcmp(gpo_default[i][3], "true") == 0);
                    gd.setGpoValue(defval);
                    if (!defval && haveAdmins) gd.setAllowed(allowed);
                        try { gpo.create(*backend, &gd); } catch (...) {}
                    try { gpo.create(backend, &gd); } catch (...) {}
                }
            }
            return migrated;
        }

        /* ---- JSON API: Migrate native accounts of all domains ----
         * Accounts created natively (before the directory existed, or by old
         * versions) only live as UserData in the domain backend: they can log
         * in via api.cpp's native fallback but are invisible to everything
         * directory-based (edituser/getuser/getsignature, user list). */
        void apiMigrateNative(libhttppp::HttpRequest &curreq, const SessionData *sdat) {
            if (!sdat || !isSuperAdmin(sdat))
                throw AuthBackendError("migrateNative: nur SuperAdmins von admin.local erlaubt!");
            json_object *jobj = json_object_new_object();
            json_object *jdoms = json_object_new_array();
            int total = 0;
            Domain domain;
            std::vector<uuid::uuid> dids;
            domain.list(_AdminBackend, dids);
            for (const auto &did : dids) {
                AuthBackend *backend = getDomain(did.c_str());
                if (!backend) continue;
                std::vector<std::string> skipped;
                int n = migrateNativeDomain(did.c_str(), *backend, &skipped);
                total += n;
                json_object *jd = json_object_new_object();
                json_object_object_add(jd, "domain", json_object_new_string(domainNameFor(did.c_str()).c_str()));
                json_object_object_add(jd, "migrated", json_object_new_int(n));
                json_object *jsk = json_object_new_array();
                for (const auto &sam : skipped)
                    json_object_array_add(jsk, json_object_new_string(sam.c_str()));
                json_object_object_add(jd, "skipped", jsk);
                json_object_array_add(jdoms, jd);
            }
            json_object_object_add(jobj, "status", json_object_new_string("ok"));
            json_object_object_add(jobj, "migrated", json_object_new_int(total));
            json_object_object_add(jobj, "domains", jdoms);
            sendJson(curreq, jobj);
            json_object_put(jobj);
        }

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

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

            int count = 0;
            {
                /* scoped: release the backend write lock before migrating, so
                 * the directory's own locking (below) can't deadlock against it */
                AuthBackend::Guard guard(*backend);
                count = Import::fromJson(*backend, jreq);
            }
            json_object_put(jreq);

            /* Import restores raw native records. For a real domain, fold any
             * native UserData/GroupData into the directory (single source) so
             * imported users/groups show up and can log in. */
            int migrated = migrateNativeDomain(cdid, *backend);

            json_object *jobj = json_object_new_object();
            json_object_object_add(jobj, "status", json_object_new_string("ok"));
@@ -2714,6 +2761,8 @@ namespace fenrirds {
                        apiListDomains(curreq);
                    } else if (strcmp(apiurl, "checkpermissions") == 0) {
                        apiCheckPermissions(curreq, sdat);
                    } else if (strcmp(apiurl, "migratenative") == 0) {
                        apiMigrateNative(curreq, sdat);
                    } else if (strcmp(apiurl, "createdomain") == 0) {
                        apiCreateDomain(curreq, sdat);
                    } else if (strcmp(apiurl, "removedomain") == 0 && param[0]) {
+5 −1
Changes for src/dsdb/directory.cpp: 5 added lines, 1 removed line.
Original line number Diff line number Diff line
@@ -262,13 +262,17 @@ uint32_t Directory::allocateRid() {
    return rid;
}

uuid::uuid Directory::create(const Dn &dn, AttrMap attrs, const Secrets *secrets) {
uuid::uuid Directory::create(const Dn &dn, AttrMap attrs, const Secrets *secrets,
                             const uuid::uuid *presetGuid) {
    std::lock_guard<std::mutex> lk(_Mtx);
    ensureFresh();
    if (findByDnLocked(dn))
        throw std::runtime_error("entry exists: " + dn.str());

    uuid::uuid guid;
    if (presetGuid && !presetGuid->empty())
        guid = *presetGuid;
    else
        guid.generate();

    /* name / cn from the RDN */
Loading