From: František Dvořák Date: Sat, 19 Oct 2013 19:17:29 +0000 (+0200) Subject: New version of identity management. Using general csv file, so any number of fields... X-Git-Url: http://scientific.zcu.cz/git/?a=commitdiff_plain;h=681b58e0e40a645c7b88bc6e05f024c91a2c3a47;p=dmlite-plugins-vfs.git New version of identity management. Using general csv file, so any number of fields could be used. --- diff --git a/src/VfsAuthn.cpp b/src/VfsAuthn.cpp index 0bb916b..3d65e0a 100644 --- a/src/VfsAuthn.cpp +++ b/src/VfsAuthn.cpp @@ -27,7 +27,7 @@ VfsAuthn::VfsAuthn(const std::string& prefix) throw (DmException) this->dirtyUsers_ = false; this->dirtyGroups_ = false; - vfsReload(); + vfsLoad(); } @@ -327,134 +327,229 @@ void VfsAuthn::deleteUser(const std::string& userName) throw (DmException) -void VfsAuthn::vfsReload() throw (DmException) { - std::string entrypp; - std::ifstream *f; - const char *entry; - unsigned int uid, gid, banned; - int pos, ret, n; +static void csv_users_load_entry(void *ctx, const Extensible& values) { + std::vector *uis = (std::vector *)ctx; UserInfo ui; + + ui.name = values.getString("name"); + ui.copy(values); + ui.erase("name"); + uis->push_back(ui); +} + + + +static void csv_groups_load_entry(void *ctx, const Extensible& values) { + std::vector *gis = (std::vector *)ctx; GroupInfo gi; + gi.name = values.getString("name"); + gi.copy(values); + gi.erase("name"); + gis->push_back(gi); +} + + + +void VfsAuthn::vfsLoad() throw (DmException) { + // users this->users_.clear(); this->uids_.clear(); - f = new std::ifstream((this->prefix_ + VFS_USERS_FILE).c_str()); - if (f->is_open()) { - n = 0; - while (std::getline(*f, entrypp)) { - n++; - entry = entrypp.c_str(); - ret = sscanf(entry, "%u%*[ \t]%u%*[ \t]%n", &uid, &banned, &pos); - if (ret != 2) { - f->close(); - delete f; - vfsThrow(ret == EOF ? DMLITE_SYSERR(EINVAL) : errno, "parsing users file failed at line %d (%s)", n, entry); - } - //printf("entry: %u %u '%s'\n", uid, banned, entry + pos); - - ui.clear(); - ui.name = entry + pos; - ui["uid"] = uid; - ui["banned"] = banned ? 1 : 0; - this->users_.push_back(ui); - // no check - we're OK with multiple users on the same uid - this->uids_.insert(uid); + try { + csvLoad(VFS_USERS_FILE, (void *)&this->users_, this->userFields_, csv_users_load_entry); + } catch (DmException& e) { + switch (e.code()) { + case DMLITE_SYSERR(EIO): + break; + default: + vfsThrow(e.code(), std::string("error parsing users file ") + e.what()); + break; } - f->close(); } - delete f; + for (size_t i = 0; i < this->users_.size(); i++) + this->uids_.insert(this->users_[i].getUnsigned("uid")); + // groups this->groups_.clear(); this->gids_.clear(); - f = new std::ifstream((this->prefix_ + VFS_GROUPS_FILE).c_str()); - if (f->is_open()) { - n = 0; - while (std::getline(*f, entrypp)) { - n++; - entry = entrypp.c_str(); - ret = sscanf(entry, "%u%*[ \t]%u%*[ \t]%n", &gid, &banned, &pos); - if (ret != 2) { - f->close(); - delete f; - vfsThrow(ret == EOF ? DMLITE_SYSERR(EINVAL) : errno, "parsing groups file failed at line %d (%s)", n, entry); - } - //printf("entry: %u %u '%s'\n", gid, banned, entry + pos); - - gi.clear(); - gi.name = entry + pos; - gi["gid"] = gid; - gi["banned"] = banned ? 1 : 0; - this->groups_.push_back(gi); - // no check - we're OK with multiple groups on the same gid - this->gids_.insert(gid); + try { + csvLoad(VFS_GROUPS_FILE, (void *)&this->groups_, this->groupFields_, csv_groups_load_entry); + } catch (DmException& e) { + switch (e.code()) { + case DMLITE_SYSERR(EIO): + break; + default: + vfsThrow(e.code(), std::string("error parsing groups file ") + e.what()); + break; } - f->close(); } - delete f; + for (size_t i = 0; i < this->groups_.size(); i++) + this->gids_.insert(this->groups_[i].getUnsigned("gid")); } -void VfsAuthn::vfsSaveUsers() throw (DmException) { - std::ofstream f; - std::string newName, name; +static bool csv_users_save_entry(void *ctx, size_t n, Extensible& values) { + std::vector *uis = (std::vector *)ctx; + + if (n < uis->size()) { + values.copy((*uis)[n]); + values["name"] = (*uis)[n].name; + return true; + } + + return false; +} + + +void VfsAuthn::vfsSaveUsers() throw (DmException) { if (this->noSync_) { this->dirtyUsers_ = true; return; } - name = this->prefix_ + VFS_USERS_FILE; - newName = this->prefix_ + ".new" + VFS_USERS_FILE; - - f.open(newName.c_str()); - if (!f) { + try { + csvSave(VFS_USERS_FILE, (void *)&this->users_, this->userFields_, csv_users_save_entry); + } catch (DmException& e) { log(LOG_NOTICE, "could not create new version of users file in '%s'", this->prefix_.c_str()); this->dirtyUsers_ = true; return; - } - - for (size_t i = 0; i < this->users_.size(); i++) { - f << this->users_[i].getUnsigned("uid") << "\t"; - f << this->users_[i].getUnsigned("banned") << "\t"; - f << this->users_[i].name << "\n"; } - f.close(); - wrapCall(rename(newName.c_str(), name.c_str()), "could not save users file"); - this->dirtyUsers_ = false; } -void VfsAuthn::vfsSaveGroups() throw (DmException) { - std::ofstream f; - std::string newName, name; +static bool csv_groups_save_entry(void *ctx, size_t n, Extensible& values) { + std::vector *gis = (std::vector *)ctx; + + if (n < gis->size()) { + values.copy((*gis)[n]); + values["name"] = (*gis)[n].name; + return true; + } + + return false; +} + + +void VfsAuthn::vfsSaveGroups() throw (DmException) { if (this->noSync_) { this->dirtyGroups_ = true; return; } - name = this->prefix_ + VFS_GROUPS_FILE; - newName = this->prefix_ + ".new" + VFS_GROUPS_FILE; - - f.open(newName.c_str()); - if (!f) { + try { + csvSave(VFS_GROUPS_FILE, (void *)&this->groups_, this->groupFields_, csv_groups_save_entry); + } catch (DmException& e) { log(LOG_NOTICE, "could not create new version of groups file in '%s'", this->prefix_.c_str()); this->dirtyGroups_ = true; return; } - for (size_t i = 0; i < this->groups_.size(); i++) { - f << this->groups_[i].getUnsigned("gid") << "\t"; - f << this->groups_[i].getUnsigned("banned") << "\t"; - f << this->groups_[i].name << "\n"; + this->dirtyGroups_ = false; +} + + + +void VfsAuthn::csvLoad(std::string fileName, void *ctx, std::set& keySet, csv_load_entry_f *callback) throw (DmException) { + std::ifstream f; + std::string line; + size_t i, n; + char *s, *value, *ptr; + std::vector keys; + dmlite::Extensible values; + + f.open((this->prefix_ + fileName).c_str()); + if (!f) + throw DmException(DMLITE_SYSERR(EIO), "can't open file"); + + // header + if (!std::getline(f, line)) + return; + ptr = s = strdup(line.c_str()); + value = strsep(&ptr, "\t"); + while (value) { + keys.push_back(value); + value = strsep(&ptr, "\t"); + } + free(s); + + // name column always in the end + if (keys.back() != "name") + throw DmException(DMLITE_SYSERR(EINVAL), "at line 1 - missing 'name' column (%s)", line.c_str()); + + keySet.clear(); + for (size_t i = 0; i < keys.size(); i++) + keySet.insert(keys[i]); + + // data + n = 1; + while (std::getline(f, line)) { + n++; + + values.clear(); + ptr = s = strdup(line.c_str()); + value = strsep(&ptr, "\t"); + for (i = 0; i < keys.size() && value; i++) { + if (value[0] != '\0') + values[keys[i]] = std::string(value); + value = strsep(&ptr, "\t"); + } + free(s); + + if (i != keys.size()) + throw DmException(DMLITE_SYSERR(EINVAL), "at line %d - missing columns (%s)", n, line.c_str()); + + (*callback)(ctx, values); } +} - f.close(); - wrapCall(rename(newName.c_str(), name.c_str()), "could not save groups file"); - this->dirtyGroups_ = false; + +void VfsAuthn::csvSave(std::string fileName, void *ctx, std::set& keySet, csv_save_entry_f *callback) throw (DmException) { + std::ofstream f; + std::string newPath, path; + size_t n; + std::vector keys; + dmlite::Extensible values; + + path = this->prefix_ + fileName; + newPath = this->prefix_ + ".new" + fileName; + + f.open(newPath.c_str()); + if (!f) + throw DmException(DMLITE_SYSERR(EIO), "could create file '%s'", newPath.c_str()); + + // name column always in the end + for (std::set::iterator it = keySet.begin(); it != keySet.end(); it++) + if (*it == "name") continue; + else keys.push_back(*it); + keys.push_back("name"); + + // header + for (size_t i = 0; i < keys.size(); i++) { + if (i) f << "\t"; + f << keys[i]; + } + f << std::endl; + + // data + n = 0; + while ((*callback)(ctx, n, values)) { + for (size_t i = 0; i < keys.size(); i++) { + if (i) f << "\t"; + f << values.getString(keys[i]); + } + f << std::endl; + values.clear(); + n++; + } + + f.close(); + if (rename(newPath.c_str(), path.c_str()) == -1) + throw DmException(errno, "could not save file '%s'", path.c_str()); } diff --git a/src/VfsAuthn.h b/src/VfsAuthn.h index 8e7cf24..e557e3c 100644 --- a/src/VfsAuthn.h +++ b/src/VfsAuthn.h @@ -8,6 +8,9 @@ namespace dmlite { + typedef void (csv_load_entry_f)(void *ctx, const dmlite::Extensible&); + typedef bool (csv_save_entry_f)(void *ctx, size_t n, dmlite::Extensible&); + class VfsAuthn: public Authn { public: @@ -37,17 +40,20 @@ namespace dmlite { protected: void vfsNewGroup(const std::string& groupName) throw (DmException); void vfsNewUser(const std::string& userName) throw (DmException); - void vfsReload() throw (DmException); + void vfsLoad() throw (DmException); void vfsSaveGroups() throw (DmException); void vfsSaveUsers() throw (DmException); + void csvLoad(std::string fileName, void *ctx, std::set& keys, csv_load_entry_f *callback) throw (DmException); + void csvSave(std::string fileName, void *ctx, std::set& keys, csv_save_entry_f *callback) throw (DmException); - std::string prefix_; + std::string prefix_; /// namespace local disk path prefix std::vector users_; std::vector groups_; std::set uids_, gids_; /// helper sets with UID/GID + std::set userFields_, groupFields_; /// all fields used in user/group info unsigned int nextUid_, nextGid_; /// next UID/GID after the one last inserted bool noSync_; /// saving users/groups disabled - bool dirtyUsers_, dirtyGroups_; /// data which need to be saved + bool dirtyUsers_, dirtyGroups_; /// data needed to be saved }; };