Adding and removing users and things from groups

Previous: Loading and saving users and groups ] [ Top: The user module ] [ Next: Authentication in a CGI environment ]

These are simply some extremely simple wrappers around the xmlapi functionality used to make the specified changes.

user_join()
When a user joins a group, then a link is made in both directions. Just in case the join is redundant (in either direction) we scan both user and group for membership before appending the link.

Note that this only affects structures in memory; the calling app has to know when to save both structures back to the repository. Note further that there is absolutely no locking going on here. Eventually we might want to address this issue in some way (otherwise it will eventually cause us to wish that we had already addressed it...)
 
int user_join (XML * user, XML * group)
{
   XML * child;
   child = xml_firstelem (group);
   while (child) {
      if (!strcmp (child->name, "user"))
         if (!strcmp (xml_attrval (child, "name"), xml_attrval (user, "name")))
            goto ensure_group;
      child = xml_nextelem (child);
   }
   child = xml_create ("user");
   xml_set (child, "name", xml_attrval (user, "name"));
   xml_append (group, child);

ensure_group:
   child = xml_firstelem (user);
   while (child) {
      if (!strcmp (child->name, "group"))
         if (!strcmp (xml_attrval (child, "name"), xml_attrval (group, "name")))
            return 0;
      child = xml_nextelem (child);
   }
   child = xml_create ("group");
   xml_set (child, "name", xml_attrval (group, "name"));
   xml_append (user, child);

   return 0;
}


user_leave()
When a user leaves a group, then, the links in both directions must be removed.
 
int user_leave (XML * user, XML * group)
{
   XML * child;

   child = xml_firstelem (group);
   while (child) {
      if (!strcmp (child->name, "user")) {
         if (!strcmp (xml_attrval (child, "name"), xml_attrval (user, "name"))) {
            xml_delete (child);
            child = xml_firstelem (group);
         }
      }
      child = xml_nextelem (child);
   }

   child = xml_firstelem (user);
   while (child) {
      if (!strcmp (child->name, "group")) {
         if (!strcmp (xml_attrval (child, "name"), xml_attrval (group, "name"))) {
            xml_delete (child);
            child = xml_firstelem (user);
         }
      }
      child = xml_nextelem (child);
   }
}


object_grant()
Permission granting is an easier task, since object inclusion only goes one way (there is no object repository; groups know that they hold objects, but the objects have to ask.)

Note that we don't really care whether we're given a user or a group to add the permission to; they work the same way, and the xmlapi doesn't care.
 
int object_grant (XML * user_or_group, const char * class, const char * object, const char * permission)
{
   XML * child;
   child = xml_firstelem (user_or_group);
   while (child) {
      if (!strcmp (child->name, "object"))
         if (!strcmp (xml_attrval (child, "class"), class) &&
             !strcmp (xml_attrval (child, "object"), object) &&
             !strcmp (xml_attrval (child, "permission"), permission))
            return 0;
      child = xml_nextelem (child);
   }
   child = xml_create ("object");
   xml_set (child, "class", class);
   xml_set (child, "object", object);
   xml_set (child, "permission", permission);
   xml_append (user_or_group, child);
   return 0;
}


object_revoke()
Revocation is a little different. If a NULL is passed in for object or for permission, it matches anything. (You can't do the same for classes. Sorry.)

Revocation is not recursive. That is, if I revoke viewer privilege for a user, one of the user's group memberships might still result in viewer privileges. I'm not sure if this is a bug or a feature, but it is at least documented.
 
int object_revoke (XML * user_or_group, const char * class, const char * object, const char * permission)
{
   XML * child;
   int ok;
   child = xml_firstelem (user_or_group);
   while (child) {
      if (!strcmp (child->name, "object") &&
          !strcmp (xml_attrval (child, "class"), class)) {
         ok = 0;
         if (!object) ok = 1;
         if (!ok) if (!strcmp (xml_attrval (child, "object"), object)) ok = 1;
         if (ok) {
            ok = 0;
            if (!permission) ok = 1;
            if (!ok) if (!strcmp (xml_attrval (child, "permission"), permission)) ok = 1;
            if (ok) {
               xml_delete (child);
               child = xml_firstelem (user_or_group);
            }
         }
      }
      child = xml_nextelem (child);
   }
}


group_include()
Group links are also two-way, and are decorated with the permission level being granted to the containing group. So these functions kind of combine the complexity of the other functions above.

Group inclusion
 
int group_include (XML * outgroup, XML * ingroup, const char * permission)
{
   XML * child;
   child = xml_firstelem (outgroup);
   while (child) {
      if (!strcmp (child->name, "group-include"))
         if (!strcmp (xml_attrval (child, "name"), xml_attrval (ingroup, "name")) &&
             !strcmp (xml_attrval (child, "permission"), permission))
            goto ensure_ingroup;
      child = xml_nextelem (child);
   }
   child = xml_create ("group-include");
   xml_set (child, "name", xml_attrval (ingroup, "name"));
   xml_set (child, "permission", permission);
   xml_append (outgroup, child);

ensure_ingroup:
   child = xml_firstelem (ingroup);
   while (child) {
      if (!strcmp (child->name, "group-included-by"))
         if (!strcmp (xml_attrval (child, "name"), xml_attrval (outgroup, "name")) &&
             !strcmp (xml_attrval (child, "permission"), permission))
            return 0;
      child = xml_nextelem (child);
   }
   child = xml_create ("group-included-by");
   xml_set (child, "name", xml_attrval (outgroup, "name"));
   xml_set (child, "permission", permission);
   xml_append (ingroup, child);

   return 0;
}


group_unlink()
As with object_revoke above,
 
int group_unlink (XML * outgroup, XML * ingroup, const char * permission)
{
   XML * child;
   int ok;
   child = xml_firstelem (outgroup);
   while (child) {
      if (!strcmp (child->name, "group-include") &&
          !strcmp (xml_attrval (child, "name"), xml_attrval (ingroup, "name"))) {
         ok = 0;
         if (!permission) ok = 1;
         if (!ok) if (!strcmp (xml_attrval (child, "permission"), permission)) ok = 1;
         if (ok) {
            xml_delete (child);
            child = xml_firstelem (outgroup);
         }
      }
      child = xml_nextelem (child);
   }

   child = xml_firstelem (ingroup);
   while (child) {
      if (!strcmp (child->name, "group-included-by") &&
          !strcmp (xml_attrval (child, "name"), xml_attrval (outgroup, "name"))) {
         ok = 0;
         if (!permission) ok = 1;
         if (!ok) if (!strcmp (xml_attrval (child, "permission"), permission)) ok = 1;
         if (ok) {
            xml_delete (child);
            child = xml_firstelem (ingroup);
         }
      }
      child = xml_nextelem (child);
   }
}
Previous: Loading and saving users and groups ] [ Top: The user module ] [ Next: Authentication in a CGI environment ]


This code and documentation are released under the terms of the GNU license. They are additionally copyright (c) 2000, Vivtek. All rights reserved except those explicitly granted under the terms of the GNU license.