You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 3 Next »

Freie Universität Berlin - Grouper Page

UnixGroupHook

So, you want to use Grouper for Unix groups? This is basically quite easy, export your Grouper groups to LDAP, configure PAM or whatever and you're set - but... Grouper groups do not have a GID by default, so we have to create one.

To do so, you need a Grouper group type, let's call it "unixGroup", and a hook. The group type adds a group attribute "gid", the hook needs to set this GID and save it somewhere (we don't want duplicate GIDs and a group should regain it's old GID after losing the group type or even after deletion and recreation).

Here's our solution. It is for Grouper 1.4, but should work with 1.5 and up too:

import edu.internet2.middleware.grouper.Group;
import edu.internet2.middleware.grouper.GroupType;
import edu.internet2.middleware.grouper.GroupTypeFinder;
import edu.internet2.middleware.grouper.exception.SchemaException;
import edu.internet2.middleware.grouper.exception.AttributeNotFoundException;
import edu.internet2.middleware.grouper.exception.GroupModifyException;
import edu.internet2.middleware.grouper.exception.InsufficientPrivilegeException;
import edu.internet2.middleware.grouper.hibernate.HibernateSession;
import edu.internet2.middleware.grouper.util.GrouperUtil;
import java.util.List;
import java.util.ArrayList;
import org.apache.commons.logging.Log;

/**
 * For a Hook to set and keep group ID for groups with special type.
 *
 * You need to prepare the grouper database and add a table and sequence
 * like this (for PostgreSQL) - you may or may not want another table
 * layout, especially the sequence):
 * CREATE SEQUENCE gid_seq
 *                 INCREMENT 1
 *                 MINVALUE 20000 -- min value of GIDs
 *                 MAXVALUE 4294967295 -- max value of GIDs
 *                 START 20000
 *                 CACHE 1;
 * CREATE TABLE public.gid_group
 * (
 *    group_name varchar(1024) PRIMARY KEY,
 *    gid integer DEFAULT nextval('gid_seq') NOT NULL UNIQUE
 * )
 * WITHOUT OIDS;
 * ALTER TABLE public.gid_group OWNER TO grouper
 * COMMENT ON TABLE public.gid_group IS
 * 'Assignment of Unix group ID to Grouper group names';
 * COMMENT ON COLUMN public.gid_group.group_name IS
 * 'Full name of the group';
 * COMMENT ON COLUMN public.gid_group.gid IS
 * 'Unix Group ID of the group';
 * CREATE INDEX group_name_idx2 ON gid_group(group_name);
 * ANALYZE gid_group;
 *
 * Also, you need a Group type with name UNIX_GROUP_TYPE_NAME (see below).
 * This could be created with GSH:
 * subj=SubjectFinder.findById("GrouperSystem")
 * sess=GrouperSession.start(subj)
 * type=GroupType.createType(sess, "unixGroup")
 * read=Privilege.getInstance("read")
 * admin=Privilege.getInstance("admin")
 * type.addAttribute(sess, "gid", read, admin, false)
 *
 * @author Mirko Tasler
 *
 */
public class UnixGroupTypeHook {

    // logging
    private static final Log LOG = GrouperUtil.getLog(UnixGroupTypeHook.class);

    // Name of unix group type
    private static final String UNIX_GROUP_TYPE_NAME="unixGroup";

    // Name of GID group attribute
    private static final String GID_ATTRIBUTE_NAME="gid";

    // SQL Table name
    private static final String sqlTable="gid_group";

    // Grouper Unix group type
    private static GroupType UNIX_GROUP=null;

    // SQL to select gid of a group from sqlTable
    private static final String SQL_SELECT_GID=
                      "select gid from " + sqlTable + " where group_name=?";

    // SQL to insert a group into sqlTable
    private static final String SQL_INSERT_GID=
                      "insert into " + sqlTable + " (group_name) values (?)";

    /**
     * Fetch the Unix group type
     * @return The Grouper unixGroup type
     * @throws SchemaException if retrieval error (i.e. type doesn't exists)
     */
    private synchronized static GroupType getType() {
        try {
            if (UNIX_GROUP==null) {
                UNIX_GROUP=GroupTypeFinder.find(UNIX_GROUP_TYPE_NAME);
            }
            return UNIX_GROUP;
        }
        catch (SchemaException se) {
            // catches exception from GroupTypeFinder.find("groupName")
            LOG.fatal("Could not retrieve " + UNIX_GROUP_TYPE_NAME + " type! " +
                      "Check your configuration - you DID create that " +
                      "type during installation, didn't you?", se);
            // make sure we crash... installation could be incomplete/corrupt!
            throw new RuntimeException(se);
        }
    }

    /**
     * Fetch the Unix GID for a certain group from the Database, create one
     * if none exists.
     * @param groupName The name of the group whose GID we want
     * @return The GID of the supplied group
     */
    private static int getGid(String groupName) {
        // create PreparedStatement param list
        List<Object> params=new ArrayList<Object>();
        params.add(groupName);
        try {
            // query GID; if not available, this will throw an RuntimeException
            int gid=HibernateSession.bySqlStatic().select(int.class,
                                                          SQL_SELECT_GID,
                                                          params);
            return gid;
        }
        catch (RuntimeException rt) {
            // this could have various reasons:
            // 1. database or table are invalid
            // 2. this group_name isn't in the table yet
            // 3. hibernate session is somehow broken
            // if reason 2., this insert + select should do the trick.
            // ...otherwise, it will provocate another RuntimeException,
            // since we cannot handle that here anyway.
            try {
                HibernateSession.bySqlStatic().executeSql(SQL_INSERT_GID,
                                                          params);
                int gid=HibernateSession.bySqlStatic().select(int.class,
                                                              SQL_SELECT_GID,
                                                              params);
                return gid;
            }
            catch (RuntimeException rt2) {
                LOG.error("Unable to insert or query " + sqlTable + "." +
                          "Please check correct creation of " + sqlTable +
                          " (sqlTable) (see sequence and table)!");
                throw rt2;
            }
        }
    }

    /**
     * Set the Unix Group ID for a Grouper group with type unixGroup
     * @param group The group we'd like to set the GID for
     */
    public static void setUnixGroupId(Group group) {
        String groupName=group.getName();
        if (group.hasType(UnixGroupTypeHook.getType())) {
            // check if group already has GID
            String attr=group.getAttributeOrNull(GID_ATTRIBUTE_NAME);
            int gid=UnixGroupTypeHook.getGid(groupName);
            // check if GID is already set
            int curGid=-1;
            if (attr!=null) {
                try { curGid=Integer.parseInt(attr); }
                catch (NumberFormatException nfe) { }
            }
            // if GID is unset (-1) or wrong, we (re-)set the attribute
            if (curGid!=gid) {
                try {
                    group.setAttribute(GID_ATTRIBUTE_NAME,
                                       new Integer(gid).toString());
                    group.store();
                }
                catch (AttributeNotFoundException anfe) {
                    // catches exception from group.setAttribute
                    LOG.error("Could not set " + UNIX_GROUP_TYPE_NAME +
                              " attribute " + GID_ATTRIBUTE_NAME +
                              " for group " + groupName + "!", anfe);
                }
                catch (GroupModifyException gme) {
                    // catches exception from group.setAttribute
                    // thrown if the group cannot be modified
                    LOG.error("Could not modify " + UNIX_GROUP_TYPE_NAME +
                              " for group " + groupName + "!", gme);
                }
                catch (InsufficientPrivilegeException ipe) {
                    // catches exception from group.setAttribute
                    // should not happen, since we should have enough privs
                    // or that group wouldn't even have been created.
                    LOG.warn("Not enough privileges to change attributes " +
                             "for group " + groupName + "!", ipe);
                }
            }
        }
    }

}

To use this, you just need to create the table and sequence, unixGroup type as suggested in the code comments, then you can call public static void setUnixGroupId(Group group) from your Hook in groupPostInsert and groupPostUpdate.

Note: The common LDAP groupOfNamesdoes not provide a GID attribute. Thus, you may need to extend it to "grouperGroupOfNames" in your LDAP Schema (and configure Grouper accordingly):

 objectClasses: ( 1.3.6.1.4.1.18898.7.2.2.1 NAME 'grouperGroupOfNames' DESC 'object class groupOfNames with additional attributes for grouper' SUP groupOfNames STRUCTURAL MAY gidNumber )

Also, the common name for the GID attribute in LDAP is "gidNumber", so you want probably something like

<groups structure=... root-dn=...>
    ...
    <!-- Map Grouper's UnixGroup gid to LDAP's gidNumber -->
    <group-attribute-map group-attribute="gid" ldap-attribute="gidNumber" />
    ...
</groups>

in your ldappc.xml.

  • No labels