From: František Dvořák Date: Fri, 21 Feb 2014 13:19:00 +0000 (+0100) Subject: Initial version of identity management. X-Git-Url: http://scientific.zcu.cz/git/?a=commitdiff_plain;h=48c88c5c2768ac99d2a57ae6ebf490e0d6b4a681;p=dmlite-plugins-vfs.git Initial version of identity management. --- diff --git a/src/VfsAuthn.cpp b/src/VfsAuthn.cpp new file mode 100644 index 0000000..cf2823b --- /dev/null +++ b/src/VfsAuthn.cpp @@ -0,0 +1,436 @@ +#include +#include +#include +#include +#include +#include "Vfs.h" +#include "VfsAuthn.h" + +#define VFS_USERS ".#vfs.users" +#define VFS_GROUPS ".#vfs.groups" +#define VFS_UID_START 500 +#define VFS_GID_START 500 + +using namespace dmlite; + + + +VfsAuthn::VfsAuthn(const std::string& prefix) throw (DmException) +{ + if (*prefix.rbegin() == '/') this->prefix_ = prefix; + else this->prefix_ = prefix + "/"; + + this->nextUid_ = VFS_UID_START; + this->nextGid_ = VFS_GID_START; + + this->noSync_ = false; + + vfsReload(); +} + + + +VfsAuthn::~VfsAuthn() +{ +} + + + +std::string VfsAuthn::getImplId() const throw () +{ + return "VfsAuthn"; +} + + + +SecurityContext *VfsAuthn::createSecurityContext(const SecurityCredentials &cred) throw (DmException) +{ + UserInfo user; + std::vector groups; + + debug("user '%s', groups %zu", cred.clientName.c_str(), cred.fqans.size()); + this->getIdMap(cred.clientName, cred.fqans, &user, &groups); + return new SecurityContext(cred, user, groups); +} + + + +void VfsAuthn::getIdMap(const std::string &userName, const std::vector &groupNames, UserInfo *user, std::vector *groups) throw (DmException) +{ + UserInfo ui; + GroupInfo gi; + size_t ngroups; + + ui = this->newUser(userName); + if (user) *user = ui; + + // disable sync + this->noSync_ = true; + ngroups = this->groups_.size(); + + // find and create if needed + if (groups) groups->clear(); + for (size_t i = 0; i < groupNames.size(); i++) { + gi = this->newGroup(groupNames[i]); + if (groups) groups->push_back(gi); + } + + // bulk update + this->noSync_ = false; + if (ngroups != this->groups_.size()) + vfsSaveGroups(); +} + + + +void VfsAuthn::vfsNewGroup(const std::string& groupName) throw (DmException) { + unsigned int gid; + GroupInfo gi; + + // new gid + gid = this->nextGid_; + while (this->gids_.find(gid) != this->gids_.end()) + gid++; + + // insert + gi.name = groupName; + gi["gid"] = gid; + this->groups_.push_back(gi); + this->gids_.insert(gid); + this->nextGid_ = gid + 1; + + debug("new group '%s', gid %d", groupName.c_str(), gid); +} + + + +void VfsAuthn::vfsNewUser(const std::string& userName) throw (DmException) { + unsigned int uid; + UserInfo ui; + + // new uid + uid = this->nextUid_; + while (this->uids_.find(uid) != this->uids_.end()) + uid++; + + // insert + ui.name = userName; + ui["uid"] = uid; + this->users_.push_back(ui); + this->uids_.insert(uid); + this->nextUid_ = uid + 1; + + debug("new user '%s', uid %d", userName.c_str(), uid); +} + + + +GroupInfo VfsAuthn::newGroup(const std::string& groupName) throw (DmException) +{ + size_t i; + + for (i = 0; i < this->groups_.size(); i++) + if (this->groups_[i].name == groupName) break; + + if (i >= this->groups_.size()) { + vfsNewGroup(groupName); + vfsSaveGroups(); + } + + return this->groups_[i]; +} + + + +GroupInfo VfsAuthn::getGroup(gid_t gid) throw (DmException) +{ + size_t i; + + for (i = 0; i < this->groups_.size(); i++) + if (this->groups_[i].getUnsigned("gid", 0) == gid) + break; + + if (i >= this->groups_.size()) + vfsThrow(DMLITE_SYSERR(DMLITE_NO_SUCH_GROUP), "group id %u not found", gid); + + return this->groups_[i]; +} + + + +GroupInfo VfsAuthn::getGroup(const std::string &groupName) throw (DmException) +{ + size_t i; + + for (i = 0; i < this->groups_.size(); i++) + if (this->groups_[i].name == groupName) + break; + + if (i >= this->groups_.size()) + vfsThrow(DMLITE_SYSERR(DMLITE_NO_SUCH_GROUP), "group name '%s' not found", groupName.c_str()); + + return this->groups_[i]; +} + + + +GroupInfo VfsAuthn::getGroup(const std::string& key, + const boost::any& value) throw (DmException) +{ + if (key != "gid") + vfsThrow(DMLITE_SYSERR(DMLITE_UNKNOWN_KEY), + "VfsAuthn does not support querying by " + key); + + gid_t gid = Extensible::anyToUnsigned(value); + return this->getGroup(gid); +} + + + +std::vector VfsAuthn::getGroups(void) throw (DmException) +{ + return this->groups_; +} + + + +void VfsAuthn::updateGroup(const GroupInfo&) throw (DmException) +{ + vfsThrow(DMLITE_SYSERR(ENOSYS), "not supported in VfsAuthn"); +} + + + +void VfsAuthn::deleteGroup(const std::string& groupName) throw (DmException) +{ + size_t i; + gid_t gid; + + for (i = 0; i < this->groups_.size(); i++) + if (this->groups_[i].name == groupName) break; + + if (i >= this->groups_.size()) + return; + + gid = this->groups_[i].getUnsigned("gid"); + debug("delete group '%s', gid %u", groupName.c_str(), gid); + this->groups_.erase(this->groups_.begin() + i); + this->gids_.erase(gid); + //not needed immediately: if (this->nextGid_ > gid) this->nextGid_ = gid; + vfsSaveGroups(); +} + + + +UserInfo VfsAuthn::newUser(const std::string& userName) throw (DmException) +{ + size_t i; + + for (i = 0; i < this->users_.size(); i++) + if (this->users_[i].name == userName) break; + + if (i >= this->users_.size()) { + vfsNewUser(userName); + vfsSaveUsers(); + } + + return this->users_[i]; +} + + + +UserInfo VfsAuthn::getUser(uid_t uid) throw (DmException) +{ + size_t i; + + for (i = 0; i < this->users_.size(); i++) + if (this->users_[i].getUnsigned("uid", 0) == uid) + break; + + if (i >= this->users_.size()) + vfsThrow(DMLITE_SYSERR(DMLITE_NO_SUCH_USER), "user id %u not found", uid); + + return this->users_[i]; +} + + + +UserInfo VfsAuthn::getUser(const std::string& userName) throw (DmException) +{ + size_t i; + + for (i = 0; i < this->users_.size(); i++) + if (this->users_[i].name == userName) + break; + + if (i >= this->users_.size()) + vfsThrow(DMLITE_SYSERR(DMLITE_NO_SUCH_USER), "user name '%s' not found", userName.c_str()); + + return this->users_[i]; +} + + + +UserInfo VfsAuthn::getUser(const std::string& key, + const boost::any& value) throw (DmException) +{ + if (key != "uid") + vfsThrow(DMLITE_SYSERR(DMLITE_UNKNOWN_KEY), + "VfsAuthn does not support querying by " + key); + + uid_t uid = Extensible::anyToUnsigned(value); + return this->getUser(uid); +} + + + +std::vector VfsAuthn::getUsers(void) throw (DmException) +{ + return this->users_; +} + + + +void VfsAuthn::updateUser(const UserInfo&) throw (DmException) +{ + vfsThrow(DMLITE_SYSERR(ENOSYS), "not supported in VfsAuthn"); +} + + + +void VfsAuthn::deleteUser(const std::string& userName) throw (DmException) +{ + size_t i; + uid_t uid; + + for (i = 0; i < this->users_.size(); i++) + if (this->users_[i].name == userName) break; + + if (i >= this->users_.size()) + return; + + uid = this->users_[i].getUnsigned("uid"); + debug("delete user '%s', uid %u", userName.c_str(), uid); + this->users_.erase(this->users_.begin() + i); + this->uids_.erase(uid); + //not needed immediately: if (this->nextUid_ > uid) this->nextUid_ = uid; + vfsSaveUsers(); +} + + + +void VfsAuthn::vfsReload() throw (DmException) { + std::string entrypp; + std::ifstream *f; + const char *entry; + unsigned int uid, gid, banned; + int pos, ret, n; + UserInfo ui; + GroupInfo gi; + + this->users_.clear(); + this->uids_.clear(); + f = new std::ifstream((this->prefix_ + VFS_USERS).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); + } + f->close(); + } + delete f; + + this->groups_.clear(); + this->gids_.clear(); + f = new std::ifstream((this->prefix_ + VFS_GROUPS).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); + } + f->close(); + } + delete f; +} + + + +void VfsAuthn::vfsSaveUsers() throw (DmException) { + std::ofstream f; + std::string newName, name; + + if (this->noSync_) return; + + name = this->prefix_ + VFS_USERS; + newName = this->prefix_ + ".new" + VFS_USERS; + + f.open(newName.c_str()); + if (!f) + vfsThrow(EIO, "could not create new version of users file"); + + 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"); +} + + + +void VfsAuthn::vfsSaveGroups() throw (DmException) { + std::ofstream f; + std::string newName, name; + + if (this->noSync_) return; + + name = this->prefix_ + VFS_GROUPS; + newName = this->prefix_ + ".new" + VFS_GROUPS; + + f.open(newName.c_str()); + if (!f) + vfsThrow(EIO, "could not create new version of groups file"); + + 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"; + } + + f.close(); + wrapCall(rename(newName.c_str(), name.c_str()), "could not save groups file"); +} diff --git a/src/VfsAuthn.h b/src/VfsAuthn.h new file mode 100644 index 0000000..396b519 --- /dev/null +++ b/src/VfsAuthn.h @@ -0,0 +1,54 @@ +#ifndef VFSAUTHN_H +#define VFSAUTHN_H + +#include +#include +#include + + +namespace dmlite { + + class VfsAuthn: public Authn + { + public: + VfsAuthn(const std::string& prefix) throw (DmException); + ~VfsAuthn(); + + std::string getImplId(void) const throw (); + SecurityContext *createSecurityContext(const SecurityCredentials &cred) throw (DmException); + void getIdMap(const std::string &userName, const std::vector &groupNames, UserInfo *user, std::vector *groups) throw (DmException); + + GroupInfo newGroup(const std::string&) throw (DmException); + GroupInfo getGroup(gid_t gid) throw (DmException); + GroupInfo getGroup(const std::string &groupName) throw (DmException); + GroupInfo getGroup(const std::string& key, const boost::any& value) throw (DmException); + std::vector getGroups(void) throw (DmException); + void updateGroup(const GroupInfo&) throw (DmException); + void deleteGroup(const std::string&) throw (DmException); + + UserInfo newUser(const std::string&) throw (DmException); + UserInfo getUser(uid_t uid) throw (DmException); + UserInfo getUser(const std::string& userName) throw (DmException); + UserInfo getUser(const std::string& key, const boost::any& value) throw (DmException); + std::vector getUsers(void) throw (DmException); + void updateUser(const UserInfo&) throw (DmException); + void deleteUser(const std::string&) throw (DmException); + + protected: + void vfsNewGroup(const std::string& groupName) throw (DmException); + void vfsNewUser(const std::string& userName) throw (DmException); + void vfsReload() throw (DmException); + void vfsSaveGroups() throw (DmException); + void vfsSaveUsers() throw (DmException); + + std::string prefix_; + std::vector users_; + std::vector groups_; + std::set uids_, gids_; /// helper sets with UID/GID + unsigned int nextUid_, nextGid_; /// next UID/GID after the one last inserted + bool noSync_; /// saving users/groups disabled + }; + +}; + +#endif