Loading data/admin.html +21 −1 Changes for data/admin.html: 21 added lines, 1 removed line. Original line number Diff line number Diff line Loading @@ -226,6 +226,10 @@ function createUserForm() { html += '<tr><td><label for="lastname">Lastname:</label></td><td><input type="text" name="lastname" id="cu_lastname"></td></tr>'; html += '<tr><td><label for="mail">Mail:</label></td><td><input type="text" name="mail" id="cu_mail"></td></tr>'; html += '<tr><td><label for="pwhash">Password:</label></td><td><input type="password" name="pwhash" id="cu_password"></td></tr>'; html += '<tr><td><label for="role">Rolle:</label></td><td><select name="role" id="cu_role">'; html += '<option value="mitarbeiter">Mitarbeiter</option>'; html += '<option value="admin">Admin</option>'; html += '</select></td></tr>'; html += '<tr><td><label for="avatar">ProfilBild:</label></td><td><input type="file" name="avatar" id="cu_avatar"></td></tr>'; html += '<tr><td></td><td><input type="button" value="Create" onclick="submitCreateUser()"></td></tr>'; html += '</table></form>'; Loading @@ -239,6 +243,7 @@ function submitCreateUser() { formData.append("lastname", document.getElementById("cu_lastname").value); formData.append("mail", document.getElementById("cu_mail").value); formData.append("pwhash", document.getElementById("cu_password").value); formData.append("role", document.getElementById("cu_role").value); var avatar = document.getElementById("cu_avatar").files[0]; if (avatar) formData.append("avatar", avatar); Loading Loading @@ -352,6 +357,10 @@ function createDomainForm() { html += '<tr><td><label>Domain Storage:</label></td><td>File</td></tr>'; html += '<tr><td><label>Domain Options:</label></td><td><input type="text" id="cd_options" class="domaintext" value="" required></td></tr>'; } html += '<tr><td colspan="2"><hr></td></tr>'; html += '<tr><td><label>Admin/Chef Username:</label></td><td><input type="text" id="cd_adminuser" class="domaintext" value=""></td></tr>'; html += '<tr><td><label>Admin/Chef Password:</label></td><td><input type="password" id="cd_adminpw" class="domaintext" value=""></td></tr>'; html += '<tr><td><label>Admin/Chef Mail:</label></td><td><input type="text" id="cd_adminmail" class="domaintext" value=""></td></tr>'; html += '<tr><td></td><td><input type="button" value="Save" onclick="submitCreateDomain()"></td></tr>'; html += '</table></form>'; setContent(html); Loading @@ -360,15 +369,26 @@ function createDomainForm() { function submitCreateDomain() { var optEl = document.getElementById("cd_options"); var nameEl = document.getElementById("cd_name"); var adminUserEl = document.getElementById("cd_adminuser"); var adminPwEl = document.getElementById("cd_adminpw"); var adminMailEl = document.getElementById("cd_adminmail"); if (!nameEl.value.trim() || (optEl && !optEl.value.trim())) { showError("Domain Name and Domain Options are required."); return; } if ((adminUserEl.value.trim() || adminPwEl.value.trim()) && (!adminUserEl.value.trim() || !adminPwEl.value.trim())) { showError("Admin/Chef Username and Password must both be set."); return; } var obj = { domainid: document.getElementById("cd_id").value.trim(), domainname: nameEl.value, domainstorage: parseInt(document.getElementById("cd_storage").value), domainoptions: optEl ? optEl.value : "" domainoptions: optEl ? optEl.value : "", adminusername: adminUserEl.value.trim(), adminpassword: adminPwEl.value, adminmail: adminMailEl.value.trim() }; ajaxPost("/settings/" + currentDomain + "/api/createdomain", obj, function(err, data) { if (err) { showError(err); return; } Loading src/admin.cpp +89 −0 Changes for src/admin.cpp: 89 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -230,6 +230,7 @@ namespace authdb { bool hasAvatar = false; std::string capturedRawPw; std::string capturedUsername; std::string capturedRole; for (const auto &curformdat : curform.multipartData()) { if (curformdat.value.empty()) continue; Loading @@ -245,6 +246,13 @@ namespace authdb { if (tmp == "filename") stortype = BinaryStorage; } // "role" (admin/mitarbeiter) is UI-only: it drives group // membership below, it is not a stored user field. if (name == "role") { capturedRole = std::string(curformdat.value.begin(), curformdat.value.end()); continue; } if (stortype == EmptyStorage) { bool numeric = true; for (auto i : curformdat.value) { Loading Loading @@ -353,6 +361,19 @@ namespace authdb { User user; user.create(*backend, &udat); // role=admin: put the new user into "Domain Admins" right away if (capturedRole == "admin") { uuid::uuid adminGid; if (findGroupByName(*backend, "Domain Admins", adminGid)) { class GroupData grpdat(adminGid); size_t gpos = sizeof(authdb::AuthHeader); Group grp; grp.info(*backend, grpdat, gpos); grpdat.addMember(uid); grp.edit(*backend, grpdat, gpos); } } json_object *jobj = json_object_new_object(); json_object_object_add(jobj, "status", json_object_new_string("ok")); json_object_object_add(jobj, "uid", json_object_new_string(uid.c_str())); Loading Loading @@ -892,6 +913,54 @@ namespace authdb { gid.generate(); class GroupData grpdat(gid); grpdat.setName("Domain Admins"); // Optionally create the initial domain admin/chef user // and put it into "Domain Admins" right away json_object *jadminuser = json_object_object_get(jreq, "adminusername"); json_object *jadminpw = json_object_object_get(jreq, "adminpassword"); if (jadminuser && jadminpw && strlen(json_object_get_string(jadminuser)) > 0 && strlen(json_object_get_string(jadminpw)) > 0) { std::string adminUsername = json_object_get_string(jadminuser); std::string adminRawPw = json_object_get_string(jadminpw); uuid::uuid adminuid; adminuid.generate(); class UserData adat(adminuid); adat.setUserName(adminUsername); json_object *jadminmail = json_object_object_get(jreq, "adminmail"); if (jadminmail && strlen(json_object_get_string(jadminmail)) > 0) adat.setMail(json_object_get_string(jadminmail)); Hash hash; std::string pwhash; hash.hash(adminRawPw, pwhash); adat.setPwHash(pwhash); adat.setAvatar(std::vector<char>( reinterpret_cast<const char *>(profile_webp), reinterpret_cast<const char *>(profile_webp) + sizeof(profile_webp))); if (g_KdcPlugin) { try { std::string realm = json_object_get_string(jname); std::transform(realm.begin(), realm.end(), realm.begin(), [](unsigned char c){ return std::toupper(c); }); std::string salt = realm + adminUsername; auto krbKeyBytes = g_KdcPlugin->string2key( adminRawPw, salt, KDC_ETYPE_AES256_CTS_HMAC_SHA1); adat.setKrbKey(hexEncode(krbKeyBytes)); } catch (...) { /* Non-fatal: user is created without krbkey */ } } User user; user.create(*domBackend, &adat); grpdat.addMember(adminuid); } Group grp; grp.create(*domBackend, &grpdat); Loading Loading @@ -2335,6 +2404,26 @@ namespace authdb { return resolveDomain(cdid, _AdminBackend, dbackend); } bool findGroupByName(AuthBackend &backend, const std::string &name, uuid::uuid &outGid) { Group group; std::vector<uuid::uuid> gids; group.list(backend, gids); for (const auto &gid : gids) { class GroupData gdat(gid); try { size_t gpos = sizeof(authdb::AuthHeader); group.info(backend, gdat, gpos); if (gdat.getName() == name) { outGid = gid; return true; } } catch (AuthBackendError &e) { std::cerr << e.what() << std::endl; } } return false; } authdb::AuthBackend &_AdminBackend; std::shared_ptr<libhtmlpp::HtmlElement> _LoginElement; std::shared_ptr<libhtmlpp::HtmlPage> _Loginpage; Loading Loading
data/admin.html +21 −1 Changes for data/admin.html: 21 added lines, 1 removed line. Original line number Diff line number Diff line Loading @@ -226,6 +226,10 @@ function createUserForm() { html += '<tr><td><label for="lastname">Lastname:</label></td><td><input type="text" name="lastname" id="cu_lastname"></td></tr>'; html += '<tr><td><label for="mail">Mail:</label></td><td><input type="text" name="mail" id="cu_mail"></td></tr>'; html += '<tr><td><label for="pwhash">Password:</label></td><td><input type="password" name="pwhash" id="cu_password"></td></tr>'; html += '<tr><td><label for="role">Rolle:</label></td><td><select name="role" id="cu_role">'; html += '<option value="mitarbeiter">Mitarbeiter</option>'; html += '<option value="admin">Admin</option>'; html += '</select></td></tr>'; html += '<tr><td><label for="avatar">ProfilBild:</label></td><td><input type="file" name="avatar" id="cu_avatar"></td></tr>'; html += '<tr><td></td><td><input type="button" value="Create" onclick="submitCreateUser()"></td></tr>'; html += '</table></form>'; Loading @@ -239,6 +243,7 @@ function submitCreateUser() { formData.append("lastname", document.getElementById("cu_lastname").value); formData.append("mail", document.getElementById("cu_mail").value); formData.append("pwhash", document.getElementById("cu_password").value); formData.append("role", document.getElementById("cu_role").value); var avatar = document.getElementById("cu_avatar").files[0]; if (avatar) formData.append("avatar", avatar); Loading Loading @@ -352,6 +357,10 @@ function createDomainForm() { html += '<tr><td><label>Domain Storage:</label></td><td>File</td></tr>'; html += '<tr><td><label>Domain Options:</label></td><td><input type="text" id="cd_options" class="domaintext" value="" required></td></tr>'; } html += '<tr><td colspan="2"><hr></td></tr>'; html += '<tr><td><label>Admin/Chef Username:</label></td><td><input type="text" id="cd_adminuser" class="domaintext" value=""></td></tr>'; html += '<tr><td><label>Admin/Chef Password:</label></td><td><input type="password" id="cd_adminpw" class="domaintext" value=""></td></tr>'; html += '<tr><td><label>Admin/Chef Mail:</label></td><td><input type="text" id="cd_adminmail" class="domaintext" value=""></td></tr>'; html += '<tr><td></td><td><input type="button" value="Save" onclick="submitCreateDomain()"></td></tr>'; html += '</table></form>'; setContent(html); Loading @@ -360,15 +369,26 @@ function createDomainForm() { function submitCreateDomain() { var optEl = document.getElementById("cd_options"); var nameEl = document.getElementById("cd_name"); var adminUserEl = document.getElementById("cd_adminuser"); var adminPwEl = document.getElementById("cd_adminpw"); var adminMailEl = document.getElementById("cd_adminmail"); if (!nameEl.value.trim() || (optEl && !optEl.value.trim())) { showError("Domain Name and Domain Options are required."); return; } if ((adminUserEl.value.trim() || adminPwEl.value.trim()) && (!adminUserEl.value.trim() || !adminPwEl.value.trim())) { showError("Admin/Chef Username and Password must both be set."); return; } var obj = { domainid: document.getElementById("cd_id").value.trim(), domainname: nameEl.value, domainstorage: parseInt(document.getElementById("cd_storage").value), domainoptions: optEl ? optEl.value : "" domainoptions: optEl ? optEl.value : "", adminusername: adminUserEl.value.trim(), adminpassword: adminPwEl.value, adminmail: adminMailEl.value.trim() }; ajaxPost("/settings/" + currentDomain + "/api/createdomain", obj, function(err, data) { if (err) { showError(err); return; } Loading
src/admin.cpp +89 −0 Changes for src/admin.cpp: 89 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -230,6 +230,7 @@ namespace authdb { bool hasAvatar = false; std::string capturedRawPw; std::string capturedUsername; std::string capturedRole; for (const auto &curformdat : curform.multipartData()) { if (curformdat.value.empty()) continue; Loading @@ -245,6 +246,13 @@ namespace authdb { if (tmp == "filename") stortype = BinaryStorage; } // "role" (admin/mitarbeiter) is UI-only: it drives group // membership below, it is not a stored user field. if (name == "role") { capturedRole = std::string(curformdat.value.begin(), curformdat.value.end()); continue; } if (stortype == EmptyStorage) { bool numeric = true; for (auto i : curformdat.value) { Loading Loading @@ -353,6 +361,19 @@ namespace authdb { User user; user.create(*backend, &udat); // role=admin: put the new user into "Domain Admins" right away if (capturedRole == "admin") { uuid::uuid adminGid; if (findGroupByName(*backend, "Domain Admins", adminGid)) { class GroupData grpdat(adminGid); size_t gpos = sizeof(authdb::AuthHeader); Group grp; grp.info(*backend, grpdat, gpos); grpdat.addMember(uid); grp.edit(*backend, grpdat, gpos); } } json_object *jobj = json_object_new_object(); json_object_object_add(jobj, "status", json_object_new_string("ok")); json_object_object_add(jobj, "uid", json_object_new_string(uid.c_str())); Loading Loading @@ -892,6 +913,54 @@ namespace authdb { gid.generate(); class GroupData grpdat(gid); grpdat.setName("Domain Admins"); // Optionally create the initial domain admin/chef user // and put it into "Domain Admins" right away json_object *jadminuser = json_object_object_get(jreq, "adminusername"); json_object *jadminpw = json_object_object_get(jreq, "adminpassword"); if (jadminuser && jadminpw && strlen(json_object_get_string(jadminuser)) > 0 && strlen(json_object_get_string(jadminpw)) > 0) { std::string adminUsername = json_object_get_string(jadminuser); std::string adminRawPw = json_object_get_string(jadminpw); uuid::uuid adminuid; adminuid.generate(); class UserData adat(adminuid); adat.setUserName(adminUsername); json_object *jadminmail = json_object_object_get(jreq, "adminmail"); if (jadminmail && strlen(json_object_get_string(jadminmail)) > 0) adat.setMail(json_object_get_string(jadminmail)); Hash hash; std::string pwhash; hash.hash(adminRawPw, pwhash); adat.setPwHash(pwhash); adat.setAvatar(std::vector<char>( reinterpret_cast<const char *>(profile_webp), reinterpret_cast<const char *>(profile_webp) + sizeof(profile_webp))); if (g_KdcPlugin) { try { std::string realm = json_object_get_string(jname); std::transform(realm.begin(), realm.end(), realm.begin(), [](unsigned char c){ return std::toupper(c); }); std::string salt = realm + adminUsername; auto krbKeyBytes = g_KdcPlugin->string2key( adminRawPw, salt, KDC_ETYPE_AES256_CTS_HMAC_SHA1); adat.setKrbKey(hexEncode(krbKeyBytes)); } catch (...) { /* Non-fatal: user is created without krbkey */ } } User user; user.create(*domBackend, &adat); grpdat.addMember(adminuid); } Group grp; grp.create(*domBackend, &grpdat); Loading Loading @@ -2335,6 +2404,26 @@ namespace authdb { return resolveDomain(cdid, _AdminBackend, dbackend); } bool findGroupByName(AuthBackend &backend, const std::string &name, uuid::uuid &outGid) { Group group; std::vector<uuid::uuid> gids; group.list(backend, gids); for (const auto &gid : gids) { class GroupData gdat(gid); try { size_t gpos = sizeof(authdb::AuthHeader); group.info(backend, gdat, gpos); if (gdat.getName() == name) { outGid = gid; return true; } } catch (AuthBackendError &e) { std::cerr << e.what() << std::endl; } } return false; } authdb::AuthBackend &_AdminBackend; std::shared_ptr<libhtmlpp::HtmlElement> _LoginElement; std::shared_ptr<libhtmlpp::HtmlPage> _Loginpage; Loading