Initial version of identity management.
authorFrantišek Dvořák <valtri@civ.zcu.cz>
Fri, 21 Feb 2014 13:19:00 +0000 (14:19 +0100)
committerFrantišek Dvořák <valtri@civ.zcu.cz>
Fri, 21 Feb 2014 13:36:36 +0000 (14:36 +0100)
src/VfsAuthn.cpp [new file with mode: 0644]
src/VfsAuthn.h [new file with mode: 0644]

diff --git a/src/VfsAuthn.cpp b/src/VfsAuthn.cpp
new file mode 100644 (file)
index 0000000..cf2823b
--- /dev/null
@@ -0,0 +1,436 @@
+#include <sys/types.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(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<GroupInfo> 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<std::string> &groupNames, UserInfo *user, std::vector<GroupInfo> *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<GroupInfo> 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<UserInfo> 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 (file)
index 0000000..396b519
--- /dev/null
@@ -0,0 +1,54 @@
+#ifndef VFSAUTHN_H
+#define VFSAUTHN_H
+
+#include <sys/types.h>
+#include <set>
+#include <dmlite/cpp/authn.h>
+
+
+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<std::string> &groupNames, UserInfo *user, std::vector<GroupInfo> *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<GroupInfo> 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<UserInfo> 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<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
+  };
+
+};
+
+#endif