Loading plugin/ad/ad.cpp +46 −20 Changes for plugin/ad/ad.cpp: 46 added lines, 20 removed lines. Original line number Diff line number Diff line Loading @@ -885,8 +885,11 @@ bool ActiveDirectory::dirImportNative(const std::string &domain, int &migrated, try { std::string v = udat.getLastname(); if (!v.empty()) a["sn"] = {v}; } catch (...) {} 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 { d->dir->create(dn, a, nullptr); ++migrated; } 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; } 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; } /* native GroupData -> SID-less directory group + members (by username) */ Loading @@ -899,39 +902,62 @@ bool ActiveDirectory::dirImportNative(const std::string &domain, int &migrated, try { size_t p = sizeof(AuthHeader); group.info(*bck, gdat, p); } catch (...) { continue; } std::string gname = gdat.getName(); if (gname.empty()) continue; if (d->dir->bySamAccountName(gname)) continue; /* find-or-create the directory group: MERGE into an existing one * (e.g. a provisioned "Domain Admins") instead of dropping its * imported members */ dsdb::Dn dn = users.child("CN", gname); if (d->dir->byDn(dn)) continue; uuid::uuid groupGuid; std::string groupDn; auto existing = d->dir->bySamAccountName(gname); if (!existing) existing = d->dir->byDn(dn); /* same CN, maybe no sAMAccountName indexed */ if (existing) { groupGuid = existing->objectGuid; groupDn = existing->dn.str(); } else { dsdb::AttrMap a; a["objectClass"] = {"top", "group"}; a["objectCategory"] = {"CN=Group,CN=Schema,CN=Configuration," + d->meta.baseDn.str()}; a["sAMAccountName"] = {gname}; a["cn"] = {gname}; a["name"] = {gname}; a["distinguishedName"] = {dn.str()}; try { groupGuid = d->dir->create(dn, a, nullptr); groupDn = dn.str(); ++migrated; } catch (std::exception &e) { std::cerr << "[AD] dirImportNative: group '" << gname << "' skipped: " << e.what() << std::endl; continue; } } /* add each native member (resolved by username) to the directory * group — member + memberOf, duplicate-safe so a re-import is a no-op */ std::vector<uuid::uuid> mems; gdat.getMembers(mems); dsdb::Values memberDns; for (const auto &muid : mems) { class UserData mu(muid); User u; try { size_t p = sizeof(AuthHeader); u.info(*bck, mu, p); std::string msam = mu.getUsername(); if (!msam.empty()) memberDns.push_back(users.child("CN", msam).str()); } catch (...) {} std::string msam; try { size_t p = sizeof(AuthHeader); u.info(*bck, mu, p); msam = mu.getUsername(); } catch (...) { continue; } if (msam.empty()) continue; auto me = d->dir->bySamAccountName(msam); if (!me) continue; std::string mdn = me->dn.str(); auto grpEnt = d->dir->byGuid(groupGuid); if (grpEnt) { dsdb::Values members = grpEnt->get("member") ? *grpEnt->get("member") : dsdb::Values{}; if (std::find(members.begin(), members.end(), mdn) == members.end()) { members.push_back(mdn); dsdb::AttrMap ga; ga["member"] = members; try { d->dir->modify(groupGuid, ga, nullptr); } catch (...) {} } } if (!memberDns.empty()) a["member"] = memberDns; try { d->dir->create(dn, a, nullptr); for (auto &mdn : memberDns) { dsdb::Dn md; if (!dsdb::Dn::parse(mdn, md)) continue; auto me = d->dir->byDn(md); if (!me) continue; dsdb::AttrMap up; dsdb::Values mo = me->get("memberOf") ? *me->get("memberOf") : dsdb::Values{}; mo.push_back(dn.str()); up["memberOf"] = mo; try { d->dir->modify(me->objectGuid, up, nullptr); } catch (...) {} if (std::find(mo.begin(), mo.end(), groupDn) == mo.end()) { mo.push_back(groupDn); dsdb::AttrMap ua; ua["memberOf"] = mo; try { d->dir->modify(me->objectGuid, ua, nullptr); } catch (...) {} } } ++migrated; } catch (...) {} } std::cerr << "[AD] dirImportNative: native groups=" << gids.size() << std::endl; } std::cerr << "[AD] dirImportNative domain=" << domain << " migrated=" << migrated << std::endl; return true; Loading src/admin.cpp +31 −0 Changes for src/admin.cpp: 31 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -2079,6 +2079,37 @@ namespace fenrirds { std::string err; if (!domain.empty() && !g_AdPlugin->dirImportNative(domain, migrated, err)) std::cerr << "import: dirImportNative failed: " << err << std::endl; /* ensure the domain has the default policies (so the Policies * view is populated), with allowed pointing at the directory * "Domain Admins" group — matching session memberships */ if (!domain.empty()) { uuid::uuid adminsGuid; bool haveAdmins = false; dsdb::Entry da; std::string e2; if (g_AdPlugin->dirGetBySam(domain, "Domain Admins", da, e2)) { adminsGuid = da.objectGuid; haveAdmins = true; } std::vector<uuid::uuid> allowed; if (haveAdmins) allowed.push_back(adminsGuid); 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)) continue; class GpoData gd(gpoid); gd.setName(gpo_default[i][1]); gd.setDesc(gpo_default[i][2]); bool defval = (strcmp(gpo_default[i][3], "true") == 0); gd.setGpoValue(defval); if (!defval && haveAdmins) gd.setAllowed(allowed); try { gpo.create(*backend, &gd); } catch (...) {} } } } json_object *jobj = json_object_new_object(); Loading Loading
plugin/ad/ad.cpp +46 −20 Changes for plugin/ad/ad.cpp: 46 added lines, 20 removed lines. Original line number Diff line number Diff line Loading @@ -885,8 +885,11 @@ bool ActiveDirectory::dirImportNative(const std::string &domain, int &migrated, try { std::string v = udat.getLastname(); if (!v.empty()) a["sn"] = {v}; } catch (...) {} 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 { d->dir->create(dn, a, nullptr); ++migrated; } 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; } 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; } /* native GroupData -> SID-less directory group + members (by username) */ Loading @@ -899,39 +902,62 @@ bool ActiveDirectory::dirImportNative(const std::string &domain, int &migrated, try { size_t p = sizeof(AuthHeader); group.info(*bck, gdat, p); } catch (...) { continue; } std::string gname = gdat.getName(); if (gname.empty()) continue; if (d->dir->bySamAccountName(gname)) continue; /* find-or-create the directory group: MERGE into an existing one * (e.g. a provisioned "Domain Admins") instead of dropping its * imported members */ dsdb::Dn dn = users.child("CN", gname); if (d->dir->byDn(dn)) continue; uuid::uuid groupGuid; std::string groupDn; auto existing = d->dir->bySamAccountName(gname); if (!existing) existing = d->dir->byDn(dn); /* same CN, maybe no sAMAccountName indexed */ if (existing) { groupGuid = existing->objectGuid; groupDn = existing->dn.str(); } else { dsdb::AttrMap a; a["objectClass"] = {"top", "group"}; a["objectCategory"] = {"CN=Group,CN=Schema,CN=Configuration," + d->meta.baseDn.str()}; a["sAMAccountName"] = {gname}; a["cn"] = {gname}; a["name"] = {gname}; a["distinguishedName"] = {dn.str()}; try { groupGuid = d->dir->create(dn, a, nullptr); groupDn = dn.str(); ++migrated; } catch (std::exception &e) { std::cerr << "[AD] dirImportNative: group '" << gname << "' skipped: " << e.what() << std::endl; continue; } } /* add each native member (resolved by username) to the directory * group — member + memberOf, duplicate-safe so a re-import is a no-op */ std::vector<uuid::uuid> mems; gdat.getMembers(mems); dsdb::Values memberDns; for (const auto &muid : mems) { class UserData mu(muid); User u; try { size_t p = sizeof(AuthHeader); u.info(*bck, mu, p); std::string msam = mu.getUsername(); if (!msam.empty()) memberDns.push_back(users.child("CN", msam).str()); } catch (...) {} std::string msam; try { size_t p = sizeof(AuthHeader); u.info(*bck, mu, p); msam = mu.getUsername(); } catch (...) { continue; } if (msam.empty()) continue; auto me = d->dir->bySamAccountName(msam); if (!me) continue; std::string mdn = me->dn.str(); auto grpEnt = d->dir->byGuid(groupGuid); if (grpEnt) { dsdb::Values members = grpEnt->get("member") ? *grpEnt->get("member") : dsdb::Values{}; if (std::find(members.begin(), members.end(), mdn) == members.end()) { members.push_back(mdn); dsdb::AttrMap ga; ga["member"] = members; try { d->dir->modify(groupGuid, ga, nullptr); } catch (...) {} } } if (!memberDns.empty()) a["member"] = memberDns; try { d->dir->create(dn, a, nullptr); for (auto &mdn : memberDns) { dsdb::Dn md; if (!dsdb::Dn::parse(mdn, md)) continue; auto me = d->dir->byDn(md); if (!me) continue; dsdb::AttrMap up; dsdb::Values mo = me->get("memberOf") ? *me->get("memberOf") : dsdb::Values{}; mo.push_back(dn.str()); up["memberOf"] = mo; try { d->dir->modify(me->objectGuid, up, nullptr); } catch (...) {} if (std::find(mo.begin(), mo.end(), groupDn) == mo.end()) { mo.push_back(groupDn); dsdb::AttrMap ua; ua["memberOf"] = mo; try { d->dir->modify(me->objectGuid, ua, nullptr); } catch (...) {} } } ++migrated; } catch (...) {} } std::cerr << "[AD] dirImportNative: native groups=" << gids.size() << std::endl; } std::cerr << "[AD] dirImportNative domain=" << domain << " migrated=" << migrated << std::endl; return true; Loading
src/admin.cpp +31 −0 Changes for src/admin.cpp: 31 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -2079,6 +2079,37 @@ namespace fenrirds { std::string err; if (!domain.empty() && !g_AdPlugin->dirImportNative(domain, migrated, err)) std::cerr << "import: dirImportNative failed: " << err << std::endl; /* ensure the domain has the default policies (so the Policies * view is populated), with allowed pointing at the directory * "Domain Admins" group — matching session memberships */ if (!domain.empty()) { uuid::uuid adminsGuid; bool haveAdmins = false; dsdb::Entry da; std::string e2; if (g_AdPlugin->dirGetBySam(domain, "Domain Admins", da, e2)) { adminsGuid = da.objectGuid; haveAdmins = true; } std::vector<uuid::uuid> allowed; if (haveAdmins) allowed.push_back(adminsGuid); 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)) continue; class GpoData gd(gpoid); gd.setName(gpo_default[i][1]); gd.setDesc(gpo_default[i][2]); bool defval = (strcmp(gpo_default[i][3], "true") == 0); gd.setGpoValue(defval); if (!defval && haveAdmins) gd.setAllowed(allowed); try { gpo.create(*backend, &gd); } catch (...) {} } } } json_object *jobj = json_object_new_object(); Loading