Initial version of identity management.
authorFrantišek Dvořák <valtri@civ.zcu.cz>
Wed, 16 Oct 2013 21:11:42 +0000 (23:11 +0200)
committerFrantišek Dvořák <valtri@civ.zcu.cz>
Sat, 19 Oct 2013 19:15:52 +0000 (21:15 +0200)
src/Vfs.cpp
src/VfsAuthn.cpp
src/VfsAuthn.h

index 65f8ea9..10493f8 100644 (file)
@@ -134,7 +134,7 @@ IODriver* VfsFactory::createIODriver(PluginManager*) throw (DmException)
 Authn* VfsFactory::createAuthn(PluginManager*) throw (DmException)
 {
   debug("");
-  return new VfsAuthn();
+  return new VfsAuthn(this->nsPrefix_);
 }
 
 
index 725eda7..cf2823b 100644 (file)
@@ -1,29 +1,31 @@
 #include <sys/types.h>
-#include <grp.h>
-#include <pwd.h>
 #include <unistd.h>
+#include <cstdio>
+#include <fstream>
 #include <dmlite/cpp/dmlite.h>
 #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() throw (DmException)
+VfsAuthn::VfsAuthn(const std::string& prefix) throw (DmException)
 {
-  char buffer[1024];
-  struct passwd pwd;
-  struct passwd *result;
-  uid_t uid;
+  if (*prefix.rbegin() == '/') this->prefix_ = prefix;
+  else this->prefix_ = prefix + "/";
 
-  uid = geteuid();
+  this->nextUid_ = VFS_UID_START;
+  this->nextGid_ = VFS_GID_START;
 
-  if (getpwuid_r(uid, &pwd, buffer, sizeof(buffer), &result) != 0 || result == NULL)
-    vfsThrowErrno("current user not found (uid %d)", uid);
+  this->noSync_ = false;
 
-  this->currentUser = std::string(result->pw_name);
-  this->currentUid = uid;
+  vfsReload();
 }
 
 
@@ -45,10 +47,9 @@ SecurityContext *VfsAuthn::createSecurityContext(const SecurityCredentials &cred
 {
   UserInfo user;
   std::vector<GroupInfo> groups;
-  std::string userNameEmpty;
-  std::vector<std::string> groupNamesEmpty;
 
-  this->getIdMap(userNameEmpty, groupNamesEmpty, &user, &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);
 }
 
@@ -56,82 +57,119 @@ SecurityContext *VfsAuthn::createSecurityContext(const SecurityCredentials &cred
 
 void VfsAuthn::getIdMap(const std::string &userName, const std::vector<std::string> &groupNames, UserInfo *user, std::vector<GroupInfo> *groups) throw (DmException)
 {
-  gid_t gid;
-  gid_t gids[20];
-  int ngids, i;
   UserInfo ui;
-  bool egid = false;
   GroupInfo gi;
+  size_t ngroups;
 
-  ui["banned"] = 0;
-  ui["uid"] = this->currentUid;
-  ui.name = this->currentUser;
-  *user = ui;
+  ui = this->newUser(userName);
+  if (user) *user = ui;
 
-  gid = getegid();
-  ngids = getgroups(sizeof(gids)/sizeof(gid_t), gids);
-  wrapCall(ngids, "error getting current groups");
+  // disable sync
+  this->noSync_ = true;
+  ngroups = this->groups_.size();
 
-  for (i = 0; i < ngids; i++) {
-    if (gids[i] == gid) egid = true;
-    gi = this->getGroup(gids[i]);
-    groups->push_back(gi);
-  }
-  if (!egid) {
-    gi = this->getGroup(gid);
-    groups->push_back(gi);
+  // 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);
 }
 
 
 
-GroupInfo VfsAuthn::newGroup(const std::string&) throw (DmException)
+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)
 {
-  vfsThrow(DMLITE_SYSERR(ENOSYS), "not supported in VfsAuthn");
+  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)
 {
-  struct group  grp;
-  struct group *result;
-  char          buffer[1024];
-  int res;
-  GroupInfo gi;
-
-  res = getgrgid_r(gid, &grp, buffer, sizeof(buffer), &result);
+  size_t i;
 
-  if (res != 0 || result == NULL)
-    vfsThrow(DMLITE_NO_SUCH_GROUP, "Group with id %d not found", gid);
+  for (i = 0; i < this->groups_.size(); i++)
+    if (this->groups_[i].getUnsigned("gid", 0) == gid)
+      break;
 
-  gi["banned"] = 0;
-  gi["gid"]    = result->gr_gid;
-  gi.name      = result->gr_name;
+  if (i >= this->groups_.size())
+    vfsThrow(DMLITE_SYSERR(DMLITE_NO_SUCH_GROUP), "group id %u not found", gid);
 
-  return gi;
+  return this->groups_[i];
 }
 
 
 
 GroupInfo VfsAuthn::getGroup(const std::string &groupName) throw (DmException)
 {
-  struct group  grp;
-  struct group *result;
-  char buffer[1024];
-  int res;
-  GroupInfo gi;
-
-  res = getgrnam_r(groupName.c_str(), &grp, buffer, sizeof(buffer), &result);
+  size_t i;
 
-  if (res != 0 || result == NULL)
-    vfsThrow(DMLITE_NO_SUCH_GROUP, "Group '%s' not found", groupName.c_str());
+  for (i = 0; i < this->groups_.size(); i++)
+    if (this->groups_[i].name == groupName)
+      break;
 
-  gi["banned"] = 0;
-  gi["gid"]    = result->gr_gid;
-  gi.name      = result->gr_name;
+  if (i >= this->groups_.size())
+    vfsThrow(DMLITE_SYSERR(DMLITE_NO_SUCH_GROUP), "group name '%s' not found", groupName.c_str());
 
-  return gi;
+  return this->groups_[i];
 }
 
 
@@ -151,20 +189,7 @@ GroupInfo VfsAuthn::getGroup(const std::string& key,
 
 std::vector<GroupInfo> VfsAuthn::getGroups(void) throw (DmException)
 {
-  std::vector<GroupInfo> groups;
-  GroupInfo group;
-  struct group* ent;
-
-  setgrent();
-  while ((ent = getgrent()) != NULL) {
-    group.clear();
-    group.name   = ent->gr_name;
-    group["gid"] = ent->gr_gid;
-    groups.push_back(group);
-  }
-  endgrent();
-
-  return groups;
+  return this->groups_;
 }
 
 
@@ -176,51 +201,72 @@ void VfsAuthn::updateGroup(const GroupInfo&) throw (DmException)
 
 
 
-void VfsAuthn::deleteGroup(const std::string&) throw (DmException)
+void VfsAuthn::deleteGroup(const std::string& groupName) throw (DmException)
 {
-  vfsThrow(DMLITE_SYSERR(ENOSYS), "not supported in VfsAuthn");
-}
+  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;
 
-UserInfo VfsAuthn::newUser(const std::string&) throw (DmException)
-{
-  vfsThrow(DMLITE_SYSERR(ENOSYS), "not supported in VfsAuthn");
+  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::getUser(const std::string& userName, gid_t* group) throw (DmException)
+UserInfo VfsAuthn::newUser(const std::string& userName) throw (DmException)
 {
-  struct passwd  pwd;
-  struct passwd *result;
-  char           buffer[1024];
-  int res;
-  UserInfo ui;
+  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];
+}
 
-  if (userName != this->currentUser)
-    vfsThrow(DMLITE_NO_SUCH_USER, "Only current effective local user is supported (%s)", this->currentUser.c_str());
 
-  res = getpwnam_r(userName.c_str(), &pwd, buffer, sizeof(buffer), &result);
 
-  if (res != 0 || result == NULL)
-    vfsThrow(DMLITE_NO_SUCH_USER, "User '%s' not found", userName.c_str());
+UserInfo VfsAuthn::getUser(uid_t uid) throw (DmException)
+{
+  size_t i;
 
-  ui["banned"] = 0;
-  ui["uid"]    = result->pw_uid;
-  ui.name      = result->pw_name;
+  for (i = 0; i < this->users_.size(); i++)
+    if (this->users_[i].getUnsigned("uid", 0) == uid)
+      break;
 
-  *group = result->pw_gid;
+  if (i >= this->users_.size())
+    vfsThrow(DMLITE_SYSERR(DMLITE_NO_SUCH_USER), "user id %u not found", uid);
 
-  return ui;
+  return this->users_[i];
 }
 
 
 
 UserInfo VfsAuthn::getUser(const std::string& userName) throw (DmException)
 {
-  gid_t ignore;
-  return this->getUser(userName, &ignore);
+  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];
 }
 
 
@@ -228,41 +274,19 @@ UserInfo VfsAuthn::getUser(const std::string& userName) throw (DmException)
 UserInfo VfsAuthn::getUser(const std::string& key,
                            const boost::any& value) throw (DmException)
 {
-  uid_t         uid;
-  struct passwd pwdbuf, *upwd;
-  char          buffer[512];
-  int res;
-  UserInfo u;
-
   if (key != "uid")
     vfsThrow(DMLITE_SYSERR(DMLITE_UNKNOWN_KEY),
                       "VfsAuthn does not support querying by " + key);
 
-  uid = Extensible::anyToUnsigned(value);
-  if (uid != this->currentUid)
-    vfsThrow(DMLITE_NO_SUCH_USER, "Only current effective local user is supported (%s)", this->currentUser.c_str());
-
-  res = getpwuid_r(uid, &pwdbuf, buffer, sizeof(buffer), &upwd);
-  if (res != 0 || upwd == NULL)
-    vfsThrow(DMLITE_NO_SUCH_USER, "User with id %d not found", uid);
-
-  u.name   = upwd->pw_name;
-  u["uid"] = upwd->pw_uid;
-  return u;
+  uid_t uid = Extensible::anyToUnsigned(value);
+  return this->getUser(uid);
 }
 
 
 
 std::vector<UserInfo> VfsAuthn::getUsers(void) throw (DmException)
 {
-  std::vector<UserInfo> users;
-  UserInfo user;
-
-  user.name = this->currentUser;
-  user["uid"] = this->currentUid;
-  users.push_back(user);
-
-  return users;
+  return this->users_;
 }
 
 
@@ -274,7 +298,139 @@ void VfsAuthn::updateUser(const UserInfo&) throw (DmException)
 
 
 
-void VfsAuthn::deleteUser(const std::string&) throw (DmException)
+void VfsAuthn::deleteUser(const std::string& userName) throw (DmException)
 {
-  vfsThrow(DMLITE_SYSERR(ENOSYS), "not supported in VfsAuthn");
+  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");
 }
index 6effb5d..396b519 100644 (file)
@@ -2,6 +2,7 @@
 #define VFSAUTHN_H
 
 #include <sys/types.h>
+#include <set>
 #include <dmlite/cpp/authn.h>
 
 
@@ -10,7 +11,7 @@ namespace dmlite {
   class VfsAuthn: public Authn
   {
     public:
-      VfsAuthn() throw (DmException);
+      VfsAuthn(const std::string& prefix) throw (DmException);
       ~VfsAuthn();
 
       std::string getImplId(void) const throw ();
@@ -26,7 +27,7 @@ namespace dmlite {
       void deleteGroup(const std::string&) throw (DmException);
 
       UserInfo newUser(const std::string&) throw (DmException);
-      UserInfo getUser(const std::string& userName, gid_t* group) 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<UserInfo> getUsers(void) throw (DmException);
@@ -34,8 +35,18 @@ namespace dmlite {
       void deleteUser(const std::string&) throw (DmException);
 
     protected:
-      uid_t currentUid;
-      std::string currentUser;
+      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<UserInfo> users_;
+      std::vector<GroupInfo> groups_;
+      std::set<unsigned int> 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
   };
 
 };