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

Compare with Current View Page History

« Previous Version 20 Next »

Summary

Grouper manages permissions for actions used in Linux commands.  The Grouper permissions are exported to a text file and kept in sync (batch and real time) via the grouperClient on the linux server.

Using

Add a user to the sudoers file.  e.g. allow user mchyzer to restart tomcats (that are allowed in Grouper).  Note, you pass in the user's name in the first command line arg so the command knows who is running it (since it is run as appadmin by sudo)

mchyzer ALL=(appadmin) /opt/appserv/binMgmt/clusterTomcatHelper.sh mchyzer *

At this point the user is not allowed to do anything, since they have no permissions in Grouper, and clusterTomcatHelper.sh uses Grouper permissions to decide if the user can perform the action.

Go to the Grouper permissions screen, and add the user to the role for this server



Assign permissions to the user



Architecture

Diagram of permissions in use, cron updates, and XMPP real time updates

clusterLinuxCommandPermissions

One-time setup on the server where the permissions are needed

Create a permission definition:



Add action inheritance so "all" implies the others



Create a role for an environment



Add permission names for each application, in this case prefix the real app name with "app_"



Assign permissions to a user in the role




Change log consumer

This is the part that is a jar installed in the Grouper loader and an edit in the grouper-loader.properties to send permissions to XMPP

/**
 * @author mchyzer
 * $Id: ClusterLinuxChangeLogConsumer.java,v 1.2 2012/05/21 17:01:24 mchyzer Exp $
 */
package edu.upenn.isc.clusterLinuxClc;

import java.util.List;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;

import edu.internet2.middleware.grouper.app.loader.GrouperLoaderConfig;
import edu.internet2.middleware.grouper.changeLog.ChangeLogConsumerBase;
import edu.internet2.middleware.grouper.changeLog.ChangeLogEntry;
import edu.internet2.middleware.grouper.changeLog.ChangeLogLabels;
import edu.internet2.middleware.grouper.changeLog.ChangeLogProcessorMetadata;
import edu.internet2.middleware.grouper.changeLog.ChangeLogTypeBuiltin;
import edu.internet2.middleware.grouper.util.GrouperUtil;
import edu.internet2.middleware.grouper.xmpp.XmppConnectionBean;


/**
 * change log consumer to listen for specific gruoper actions that should tell linux to refresh
 * their permissions
 */
public class ClusterLinuxChangeLogConsumer extends ChangeLogConsumerBase {

  /** */
  private static final Log LOG = GrouperUtil.getLog(ClusterLinuxChangeLogConsumer.class);

  /**
   * @see edu.internet2.middleware.grouper.changeLog.ChangeLogConsumerBase#processChangeLogEntries(java.util.List, edu.internet2.middleware.grouper.changeLog.ChangeLogProcessorMetadata)
   */
  @Override
  public long processChangeLogEntries(List<ChangeLogEntry> changeLogEntryList,
      ChangeLogProcessorMetadata changeLogProcessorMetadata) {
    
    //identifies which config is running, multiple could be running
    String consumerName = changeLogProcessorMetadata.getConsumerName();
    
    long currentId = -1;

    XmppConnectionBean xmppConnectionBean = null;
    String recipient = null;

    String grouperFolderBase = null;

    {
      String grouperFolderBaseConfigName = "changeLog.consumer." + consumerName + ".clusterLinux.grouperFolderBase";

      grouperFolderBase = GrouperLoaderConfig.getPropertyString(grouperFolderBaseConfigName, true);
    }

    boolean sendMessage = false;
    
    for (ChangeLogEntry changeLogEntry : changeLogEntryList) {

      //try catch so we can track that we made some progress
      try {

        currentId = changeLogEntry.getSequenceNumber();
        
        //only need to send once
        if (sendMessage) {
          continue;
        }

        //this might be a little aggressive, but basically if a permission or member changes in the clusterLinux folder of
        //grouper then send a refresh message
        //note: not using constants for permissions so it works in 2.0 and 2.1...
        if (StringUtils.equals("permission", changeLogEntry.getChangeLogType().getChangeLogCategory())
            && (StringUtils.equals("addPermission", changeLogEntry.getChangeLogType().getActionName())
                || StringUtils.equals("deletePermission", changeLogEntry.getChangeLogType().getActionName()))) {
          
          String permissionName = changeLogEntry.retrieveValueForLabel("attributeDefNameName");
          
          if (permissionName != null && permissionName.startsWith(grouperFolderBase)) {
            sendMessage = true;
          }          
          if (LOG.isDebugEnabled()) {
            LOG.debug("Processing changeLog #" + currentId + ", permissionName: " + permissionName
                + ", grouperFolderBase: " + grouperFolderBase
                + ", message: "
                + changeLogEntry.getChangeLogType().getChangeLogCategory() + "."
                + changeLogEntry.getChangeLogType().getActionName() + ", sendMessage: " + sendMessage
                );
          }
        } else if (changeLogEntry.equalsCategoryAndAction(ChangeLogTypeBuiltin.MEMBERSHIP_ADD)
            || changeLogEntry.equalsCategoryAndAction(ChangeLogTypeBuiltin.MEMBERSHIP_DELETE)
            || changeLogEntry.equalsCategoryAndAction(ChangeLogTypeBuiltin.MEMBERSHIP_UPDATE)) {
          
          String roleName = changeLogEntry.retrieveValueForLabel(ChangeLogLabels.MEMBERSHIP_ADD.groupName);

          if (roleName != null && roleName.startsWith(grouperFolderBase)) {
            sendMessage = true;
          }
          if (LOG.isDebugEnabled()) {
            LOG.debug("Processing changeLog #" + currentId + ", roleName: " + roleName
              + ", grouperFolderBase: " + grouperFolderBase
              + ", message: "
              + changeLogEntry.getChangeLogType().getChangeLogCategory() + "."
              + changeLogEntry.getChangeLogType().getActionName() + ", sendMessage: " + sendMessage
              );
          }
          
        } else if (StringUtils.equals("permission", changeLogEntry.getChangeLogType().getChangeLogCategory())
            && StringUtils.equals("permissionChangeOnRole", changeLogEntry.getChangeLogType().getActionName())) {
          
          //note, this is 2.1+
          
          String roleName = changeLogEntry.retrieveValueForLabel("roleName");

          if (roleName != null && roleName.startsWith(grouperFolderBase)) {
            sendMessage = true;
          }
          if (LOG.isDebugEnabled()) {
            LOG.debug("Processing changeLog #" + currentId + ", roleName: " + roleName
              + ", grouperFolderBase: " + grouperFolderBase
              + ", message: "
              + changeLogEntry.getChangeLogType().getChangeLogCategory() + "."
              + changeLogEntry.getChangeLogType().getActionName() + ", sendMessage: " + sendMessage
              );
          }
        } else {
          if (LOG.isDebugEnabled()) {
            LOG.debug("Processing changeLog #" + currentId + ", "
              + changeLogEntry.getChangeLogType().getChangeLogCategory() + "."
              + changeLogEntry.getChangeLogType().getActionName() + ", sendMessage: " + sendMessage
              );
          }
        }          
          
        //is there something to send?
        if (sendMessage) {
          
          String message = "Update permissions";
          
          if (xmppConnectionBean == null) {

            recipient = GrouperLoaderConfig.getPropertyString("changeLog.consumer."
                + consumerName + ".publisher.recipient", "");

            String xmppServer = GrouperLoaderConfig.getPropertyString("xmpp.server.host");
            int port = GrouperLoaderConfig.getPropertyInt("xmpp.server.port", -1);
            String username = GrouperLoaderConfig.getPropertyString("xmpp.user", "");
            String password = GrouperLoaderConfig.getPropertyString("xmpp.pass", "");
            String resource = GrouperLoaderConfig.getPropertyString("xmpp.resource", "");

            xmppConnectionBean = new XmppConnectionBean(xmppServer, port, username, resource, password);
            
          }
          
          xmppConnectionBean.sendMessage(recipient, message);

        }
      } catch (Exception e) {
        //we unsuccessfully processed this record... decide whether to wait, throw, ignore, log, etc...
        LOG.error("problem with id: " + currentId, e);
        changeLogProcessorMetadata.setHadProblem(true);
        changeLogProcessorMetadata.setRecordException(e);
        changeLogProcessorMetadata.setRecordExceptionSequence(currentId);
        //stop here
        return currentId;
        //continue
      }
    }

    return currentId;
  }

}

Build that consumer into a jar:

<project name="clusterLinuxChangeLogConsumer" default="build" basedir=".">

  <!-- declare the ant-contrib tasks -->
  <taskdef resource="net/sf/antcontrib/antlib.xml" />

  <!-- copy build.properties if not there already -->
  <if><not><available file="build.properties" /></not>
    <then><copy file="build.example.properties" tofile="build.properties" /></then>
  </if>

  <!-- Grouper Global Build Properties -->
  <property file="${basedir}/build.properties"/>

  <target name="build" description="full build" depends="init,clean,compile,jarPrepare,jar">
  </target>

  <target name="init">
    <tstamp />

    <property name="main.sourceChangeLogDir" value="../sourceChangeLogConsumer" />

    <property name="main.lib" value="../lib" />

    <property name="main.binChangeLogDir" value="../dist/binChangeLog" />
    <property name="main.outputDir" value="../dist" />

    <property name="main.appName" value="clusterLinuxChangeLog" />
    <property name="main.jarFileChangeLog" value="${main.outputDir}/${main.appName}.jar" />

    <path id="main.changeLogClasspath">
      <fileset dir="${main.lib}">
        <include name="**/*.jar" />
      </fileset>
      <fileset file="${grouper.jar.location}" />
    </path>

  </target>

  <target name="clean" depends="init">
    <mkdir dir="${main.binChangeLogDir}" />
    <delete dir="${main.binChangeLogDir}" />
    <mkdir dir="${main.binChangeLogDir}" />

  </target>

  <target name="compile">
    <mkdir dir="${main.outputDir}" />
    <mkdir dir="${main.binChangeLogDir}" />

    <javac  target="1.6"
      srcdir="${main.sourceChangeLogDir}" destdir="${main.binChangeLogDir}" debug="true" >
      <classpath refid="main.changeLogClasspath" />    
    </javac>
 
  </target>

  <target name="jarPrepare">
    <mkdir dir="${main.binChangeLogDir}" />

    <copy todir="${main.binChangeLogDir}">
      <fileset dir="${main.sourceChangeLogDir}">
        <include name="**/*.java"/>      <!-- source -->
      </fileset>
    </copy>
    
    <mkdir dir="${main.binChangeLogDir}/clusterLinux" />
    
    <copy todir="${main.binChangeLogDir}/clusterLinux" file="build.xml" />
    
  </target>

  <target name="jar">
    <tstamp>
        <format property="the.timestamp" pattern="yyyy/MM/dd HH:mm:ss" />
    </tstamp>
    <jar jarfile="${main.jarFileChangeLog}" duplicate="fail">
      <fileset dir="${main.binChangeLogDir}" />
      <manifest>
        <attribute name="Built-By"                value="${user.name}"/>
        <attribute name="Implementation-Vendor"   value="Penn"/>
        <attribute name="Implementation-Title"    value="clusterLinuxChangeLog"/>
        <attribute name="Build-Timestamp"         value="${the.timestamp}"/>
      </manifest>
     </jar>
     <echo message="Output is: ${main.jarFileChangeLog}" />
   </target>
 
 
</project>

If you want to log the change log, add this to the log4j.properties:

log4j.logger.edu.upenn.isc.clusterLinuxClc.ClusterLinuxChangeLogConsumer = DEBUG

If you need more debugging, try these:

log4j.logger.edu.internet2.middleware.grouper.changeLog = DEBUG

log4j.logger.edu.internet2.middleware.grouper.app.loader = DEBUG

Make the jar with ant, copy to the grouper loader lib dir, and edit the grouper-loader.properties

#####################################
## CLUSTER LINUX CONSUMER
#####################################

changeLog.consumer.clusterLinuxTest.class = edu.upenn.isc.clusterLinuxClc.ClusterLinuxChangeLogConsumer

changeLog.consumer.clusterLinuxTest.publisher.recipient = someuser@school.edu/clusterLinuxTest, anotheruser@school.edu, someuser@school.edu/clusterLinuxTestLegacy

changeLog.consumer.clusterLinuxTest.clusterLinux.grouperFolderBase = xxxxxxx:apps:fast:clusterLinux

changeLog.consumer.clusterLinuxTest.quartzCron =

Restart the loader and add/remove someone from the role, or change permissions, see an XMPP message being sent

(4:35:01 PM) PennGroups: Update permissions

Note, the body of the message isn't important... if the message comes from Grouper and goes to the grouperClient keeping the file in sync, then it will do a 1 minute delay full refresh if one isnt already scheduled (to batch up multiple changes)

If you have DEBUG logging on for the change log consumer, you will see entries like this:

120521 163500.260 [DefaultQuartzScheduler_Worker-5] DEBUG edu.upenn.isc.clusterLinuxClc.ClusterLinuxChangeLogConsumer -
Processing changeLog #3593567, permissionName: XXXXXXXXX:apps:fast:clusterLinux:permissions:tomcats:app_directory,
grouperFolderBase: XXXXXXXXX:apps:fast:clusterLinux, message: attributeAssign.deleteAttributeAssign, sendMessage: true

sdf

  • No labels