Overview

There are a few different ways of authenticating to the Grouper UI. For any method, the mapping of the login username to a Grouper subject works the same.

After authentication, Grouper will attempt to retrieve the username by these methods in order:

  • httpServletRequest.getRemoteUser()
  • a specific http header, as configured in property grouper.ui.authentication.http.header in grouper-ui.properties
  • servlet attribute REMOTE_USER
  • httpServletRequest.getUserPrincipal().getName()
  • httpServletRequest session attribute "authUser"

If none of these return a non-empty string, the authentication will fail with error:

"You have an anonymous session since you are not logged in, but this section requires you to be logged in. Maybe No username found. Your identity provider might not be sending your username to this application. Either you need to use a different identity provider, or ask your IT department to send your username to this application."


If the username is obtained in the above step, the Grouper UI will attempt to convert this to a subject, trying as either an id or an identifier. By default it will search in all subject sources. If you want to constrain the login to specific subject sources, this can be configured in grouper-ui.properties value grouper.ui.authentication.sourceIds. For example, you may want to limit logins to regular people, excluding irrelevant accounts such as GrouperSystem. You can specify multiple sources as a comma-separated list.

Since the resolver will look up the subject by either id or identifier, the SQL or LDAP queries in subject.properties may be adjusted to resolve on alternative fields, in case the login username differs from the normal usage of id or identifier. For example, if a source normally uses uid as identifier but the login scheme utilizes eduPersonPrincipalName style usernames (e.g. uid@example.com), an LDAP source's searchSubjectByIdentifier parameter can be modified to use both as identifiers:

subjectApi.source.myLdap.search.searchSubjectByIdentifier.param.filter.value = (&(|(uid=%TERM%)(eduPersonPrincipalName=%TERM%))(objectclass=eduPerson))

As an alternative, if the web server sets a "uid" http header after login (e.g., if using a Shibboleth SSO identity provider that releases uid), the configuration property grouper.ui.authentication.http.header can be set to "uid". In this case, the resolver will use the uid header value as the %TERM% to look up.

Note: if you use a load balancer or reverse proxy that offloads the SSL then you need to set this in the tomcat server.xml <Connector> element for AJP.  Something (maybe csrfguard) will redirect from non SSL to SSL and that will send 302's to ajax and cause errors when trying to parse XML and cant.

<Connector ... scheme="https" secure="true" />


Grouper built-in authentication (v2.5+)

For a UI/WS that was installed using the Grouper Installer, Tomcat authentication using web.xml security-constraint directives is not used by default since v2.5. It has been replaced by a Grouper authentication module that stores usernames and encrypted passwords in the database.  See this page for further explanation. It is not enabled by default, except for a quickstart container. It is enabled by setting container parameters GROUPER_UI_GROUPER_AUTH and GROUPER_WS_GROUPER_AUTH, or by grouper.hibernate.properties parameters grouper.is.ui.basicAuthn and grouper.is.ws.basicAuthn. It is not recommended to use tomcat-users.xml anymore, but it can be added back manually if desired. Besides built-in authentication, external authentication services like Shibboleth and CAS are excellent options.

Authentication using Shibboleth Single Sign-on (SSO)

Thanks to Newcastle University for providing this information on Authentication to Grouper using Shibboleth.

This setup assumes an Apache web server with the mod_shib module available, on a server with a Shibboleth service provider (SP) already set up. The servlet container (Tomcat or other) is connected to Apache, typically by proxy_ajp or another mechanism, and the authentication is removed from Tomcat and the Grouper servlet configuration as described below. Apache takes control over the authentication, thus maintaining a login session independent of Grouper. Any users trying to access the UI will need to create a valid session with Shibboleth, before being granted or denied access to the Grouper application.

The following page will explain how we have achieved this, there maybe some difference in configurations required depending on server set up.

  1. Configure Shibboleth so that it will protect any content that lives under the main Grouper install directory. To do this, enable mod_shib (your location and version may vary), and require Shibboleth for the /grouper/* URI;

    LoadModule mod_shib /usr/lib64/shibboleth/mod_shib_22.so
    
    <Location /grouper>
    Authtype shibboleth
    ShibRequireSession On
    require shibboleth
    ShibUseHeaders On</Location>
  2. Then add an AJP proxy to the Apache configuration to forward requests for /grouper to the Tomcat install

    LoadModule proxy_ajp_module modules/mod_proxy_ajp.so
    
    ProxyPass /grouper ajp://localhost:8009/grouper
    ProxyPassReverse /grouper ajp://localhost:8009/grouper
  3. Add an AJP connector to Tomcat. The Tomcat conf/server.xml configuration may already have an AJP connector set up. If so, make sure the property tomcatAuthentication= "false" is included.

    <Connector port= "8009"   protocol= "AJP/1.3"   tomcatAuthentication= "false"   redirectPort= "8443"   />
  4. (< 2.5) Remove authentication from the servlet application. From the grouper WEB-INF/web.xml file, remove all security-constraint, login-config, and security-role sections:

      <security-constraint>
        <web-resource-collection>
          <web-resource-name>UI</web-resource-name>
          <url-pattern>/grouperUi/app/*</url-pattern>
        </web-resource-collection>
        <auth-constraint>
          <role-name>grouper_user</role-name>
        </auth-constraint>
      </security-constraint>
      <security-constraint>
        <web-resource-collection>
          <web-resource-name>UI</web-resource-name>
          <url-pattern>/grouperUi/appHtml/*</url-pattern>
        </web-resource-collection>
        <auth-constraint>
          <role-name>grouper_user</role-name>
        </auth-constraint>
      </security-constraint>
      <security-constraint>
        <web-resource-collection>
          <web-resource-name>UI</web-resource-name>
          <url-pattern>/grouperExternal/app/*</url-pattern>
        </web-resource-collection>
        <auth-constraint>
          <role-name>grouper_user</role-name>
        </auth-constraint>
      </security-constraint>
      <login-config>
        <auth-method>BASIC</auth-method>
        <realm-name>Grouper Application</realm-name>
      </login-config>
      <security-role>
        <description>
          The role that is required to log in to the Grouper UI
        </description>
        <role-name>grouper_user</role-name>
      </security-role>

The configurations shown above maybe slightly different for other environments, though hopefully it will be of some help.

CAS Authentication to the Grouper UI

Thanks to California Polytechnic State University for providing information on Authentication to Grouper using CAS.


  • Method 1: mod_auth_cas
  • Method 2: Tomcat <Realm> authentication
  • Method 3: Client configuration using web.xml (<v2.5.0)
  • Previous way to integrate CAS with Grouper (<v2.4.0)
    • The yale-cas-auth java jar file is included with the installation of the Grouper UI.  There are a few steps we needed to implement it:
        • Configuration Steps to enable CAS Authentication
        • Deployment Steps
      • Troubleshoot


CAS Authentication to Grouper (v2.5+) TAP Container Using mod_auth_cas

Due to changes in packaging and instrumenting of the web application (i.e., web.xml is not the primary configuration method any more), previously working configuration methods require more work, or are no longer functional. Integration with CAS can still be accomplished by the use of mod_auth_cas within the Apache httpd process and some selective configuration file updates.

This will assume that you have disabled other environment variable enabled methods of authentication and that you are building your own local docker image for deployment as per Install the Grouper container with maturity level 1.

  1. Create a directory for your docker overlay and create the directory slashRoot inside it.
  2. Create a Dockerfile with at least the following contents.

    ARG GROUPER_VERSION=2.5.XX

    FROM i2incommon/grouper:${GROUPER_VERSION}

    # Need to install CAS so it can be used at the Apache server level
    RUN yum -y install mod_auth_cas

    # this will overlay all the files from /opt/grouperContainer/slashRoot on to /
    COPY slashRoot /

    RUN chown -R tomcat:tomcat /opt/grouper \
    && chown -R tomcat:tomcat /opt/tomee

  3. Add the files below under slashRoot:
    1. /etc/httpd/conf.d/auth_cas.conf.cas

      auth_cas.conf.cas
      CASCookiePath /var/cache/httpd/mod_auth_cas/
      CASLoginURL https://__CAS_HOST_NAME__/cas/login
      CASValidateURL https://__CAS_HOST_NAME__/cas/serviceValidate
      CASRootProxiedAs https://__GROUPER_UI_HOST_NAME__
      
      #CASDebug On
      CASVersion 2
      
      <Location /grouper>
        Authtype CAS
        require valid-user
      </Location>
    2. /usr/local/bin/grouperScriptHooks.sh
      1. This hook script only installs the CAS filter in the apache layer when running the UI.  It copies the file above over the default installed by yum, and then replaces the placeholder text with environment variables.

    3. grouperScriptHooks.sh
      #!/bin/sh
      
      # called at the beginning of the container startup
      # after logging is setup
      # grouperScriptHooks_prepConfPost() {
      #	return
      # }
      
      # called after the component command has been prepped
      # grouperScriptHooks_prepComponentPost() {
      # 	return
      # }
      
      # called after the finishPrep is called before the setupFiles
      # grouperScriptHooks_finishPrepPost() {
      # 	return
      # }
      
      # called after the setupFiles functions is called, almost before the process starts
      grouperScriptHooks_setupFilesPost() {
        echo "RUNNING CUSTOM grouperScriptHooks_setupFilesPost: GROUPER_UI=$GROUPER_UI"
        if [ "$GROUPER_UI" = "true" ]; then
          # Install needed CAS configuration
          cp -v /etc/httpd/conf.d/auth_cas.conf.cas /etc/httpd/conf.d/auth_cas.conf
          # Populate with this instance's hostnames
          sed -i "s|__CAS_HOST_NAME__|$CUSTOM_CAS_HOST_NAME|g"               /etc/httpd/conf.d/auth_cas.conf
          sed -i "s|__GROUPER_UI_HOST_NAME__|$GROUPER_APACHE_SERVER_NAME|g"  /etc/httpd/conf.d/auth_cas.conf
          echo "Enabled CAS Authentication Using CAS_HOST_NAME=$CUSTOM_CAS_HOST_NAME"
        fi
      
      	return
      }
      
      # called after the chown at end of setupFiles, right before the process starts
      # grouperScriptHooks_setupFilesPostChown() {
      # 	return
      # }
      
      # export everything
      export -f grouperScriptHooks_setupFilesPost
  4. Either in your dockerfile with ENV commands or upon startup of your container, set the following environment variables:

    1. GROUPER_APACHE_SERVER_NAME : host name (no scheme or path) of the server.  Will be used in the Apache ServerName directive and to build the service URL used for redirects back from CAS.
    2. CUSTOM_CAS_HOST_NAME : host name (no scheme or path) of the CAS server.
  5. You will also likely want to set GROUPERUI_LOGOUT_REDIRECTTOURL to http://${CUSTOM_CAS_HOST_NAME}/cas/logout
  6. Build and tag your docker image and then run as per the install instruction page linked above.  As with the other authentication methods, you should be able to see that the user ID from CAS has been proxied through by turning on logging by adding the below to your log4j.properties file.

    log4j.logger.edu.internet2.middleware.grouper.ui.GrouperUiFilter = DEBUG


CAS Authentication Method 2: Tomcat <Realm> Authentication

See also:  https://github.com/apereo/java-cas-client

Tomcat authentication using realms works both before and after v2.5.0, although some of the file locations differ in containers.

The context definition in server.xml for Tomcat (in the container, this is /opt/tomee/conf/Catalina/localhost/grouper.xml) looks like this:

<Context docBase= "/ucd/opt/grouper-ui/dist/grouper"   path= "/grouper"
     reloadable= "false" mapperContextRootRedirectEnabled="true" mapperDirectoryRedirectEnabled="true" >
 
 
   <Realm className= "org.jasig.cas.client.tomcat.v85.PropertiesCasRealm"
      propertiesFilePath= "/etc/tomcat/grouper-users.properties"
    />
 
     <!--
        If you  do   not need to map users to roles via a grouper-users.properties file use this instead.
        <Realm className= "org.jasig.cas.client.tomcat.v85.AssertionCasRealm"   />
     -->
 
   <Valve className= "org.jasig.cas.client.tomcat.v85.Cas20CasAuthenticator"
      encoding= "UTF-8"
      casServerLoginUrl= "https://CAS_SERVER/cas/login"
      casServerUrlPrefix= "https://CAS_SERVER/cas/"
      serverName= "https://server-name.edu"
    />
 
 
   <!-- Single sign-out support -->
   <Valve className= "org.jasig.cas.client.tomcat.v85.SingleSignOutValve"
     artifactParameterName= "SAMLart"
   />
</Context>


The following jar files will need to go into the Tomcat lib directory (with current versions as of September 2020). The container equivalent folder is /opt/tomee/lib. For other versions of Tomcat (TomEE in the container is compatible with Tomcat 8.5), change v85 to v8, v7, or v6 as appropriate.

  • org.jasig.cas.client : cas-client-core (v3.6.1) [Download]
  • org.jasig.cas.client : cas-client-integration-tomcat-common (v3.6.1) [Download]
  • org.jasig.cas.client : cas-client-integration-tomcat-v85 (v3.6.1) [Download]
  • org.slf4j : slf4j-api (v1.7.26) [Download] (in versions <2.5.0 only)

(< v2.5.0) In Grouper's WEB-INF/web.xml, comment out the login-config and security-role sections. The security-constraint sections should remain so that authentication is triggered. The role-name can be changed to "*" (or "**" if that doesn't work) to allow all validated users to log in.

(v2.5.0+ container) In Grouper's WEB-INF/web.xml, add:

    <security-constraint>
        <display-name>Web Login Service</display-name>
        <web-resource-collection>
            <web-resource-name>user authentication</web-resource-name>
            <url-pattern>/*</url-pattern>
        </web-resource-collection>
        <auth-constraint>
            <role-name>**</role-name>
        </auth-constraint>
        <!-- uncomment in production? Or with SSL?
        <user-data-constraint>
            <transport-guarantee>CONFIDENTIAL</transport-guarantee> 
        </user-data-constraint>
        -->
    </security-constraint>


To sum up, the files that would go into a slashRoot directory of a Maturity 0 container are:

  • ./opt/grouper/grouperWebapp/WEB-INF/web.xml
  • ./opt/tomee/lib/cas-client-core-3.6.1.jar
  • ./opt/tomee/lib/cas-client-integration-tomcat-v85-3.6.1.jar
  • ./opt/tomee/lib/cas-client-integration-tomcat-common-3.6.1.jar
  • ./opt/tomee/conf/Catalina/localhost/grouper.xml

In addition to these files, if there are issues with Tomcat communicating with CAS (i.e., for back-channel ticket validation), the server certificate will need to be added to ./usr/lib/jvm/java-1.8.0-amazon-corretto/jre/lib/security/cacerts.

CAS Authentication Method 3: Client Configuration Using web.xml and custom jsp bootstrap (< v2.5.0)

This method makes changes solely within the Grouper web application, without affecting the Tomcat configuration. It is no longer functional since v2.5.0, because Grouper's internal authorization filters always take precedence and interfere.

1. Download the  cas-client-core jar file  (current version is cas-client-core-3.6.1.jar as of September 2020)

2. Copy the web applications top-level index.jsp to a new subdirectory cas/

3. Edit cas/index.jsp to reference parent directory instead of the current one

<%@ include file="../WEB-INF/grouperUi2/assetsJsp/commonTaglib.jsp"%>
String location="../grouperUi/app/UiV2Main.index?operation=UiV2Main.indexMain";


4. Add to WEB-INF/classes/Owasp.CsrfGuard.overlay.properties

org.owasp.csrfguard.unprotected.CASLogin=%servletContext%/cas/*


5. Add to WEB-INF/web.xml, changing parameters as needed. Based on  https://apereo.atlassian.net/wiki/spaces/CASC/pages/103252594/Configuring+the+Jasig+CAS+Client+for+Java+in+the+web.xml

< filter >
   < filter-name >CAS Authentication Filter</ filter-name >
   < filter-class >org.jasig.cas.client.authentication.AuthenticationFilter</ filter-class >
   < init-param >
     < param-name >casServerUrlPrefix</ param-name >
     < param-value >http://localhost:8080/cas</ param-value >
   </ init-param >
   < init-param >
     < param-name >serverName</ param-name >
     < param-value >http://localhost:8080</ param-value >
   </ init-param >
</ filter >
< filter-mapping >
     < filter-name >CAS Authentication Filter</ filter-name >
     < url-pattern >/cas/*</ url-pattern >
</ filter-mapping >
< filter >
   < filter-name >CAS Validation Filter</ filter-name >
   < filter-class >org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter</ filter-class >
   < init-param >
     < param-name >casServerUrlPrefix</ param-name >
     < param-value >http://localhost:8080/cas</ param-value >
   </ init-param >
   < init-param >
     < param-name >serverName</ param-name >
     < param-value >http://localhost:8080</ param-value >
   </ init-param >
</ filter >
< filter-mapping >
     < filter-name >CAS Validation Filter</ filter-name >
     < url-pattern >/cas/*</ url-pattern >
</ filter-mapping >
< filter >
   < filter-name >CAS HttpServletRequest Wrapper Filter</ filter-name >
   < filter-class >org.jasig.cas.client.util.HttpServletRequestWrapperFilter</ filter-class >
</ filter >
< filter-mapping >
     < filter-name >CAS HttpServletRequest Wrapper Filter</ filter-name >
     < url-pattern >/cas/*</ url-pattern >
</ filter-mapping >


6. Start Tomcat, check catalina and localhost logs if any startup errors

7. Go to  URI /grouper/cas/index.jsp to trigger the start of a CAS session.


Previous way to integrate CAS with Grouper (prior to Grouper 2.4.0)

The yale-cas-auth java jar file is included with the installation of the Grouper UI.  There are a few steps we needed to implement it:

Configuration Steps to enable CAS Authentication
  1. Add the cas authentication piece to the build.xml file in the Grouper UI home/build folder: /deploy/AppServers/grouper-ui folder:
    /deploy/AppServers/grouper-ui/build.xml

    <ant antfile= "build.xml"   target= "webapp"   dir= "${contrib.dir}/yale-cas-auth"   inheritrefs= "true"   />

    It should go just below the following section in the build.xml file:

    <!-- Call any site specific build script. This may be used to introduce site specific Struts action,
                                  local Subject implementations etc -->
                     <antcall target= "-additional-build" >
                             <param name= "target"   value= "webapp" />
                                                     <reference refid= "ui.class.path.for.run" />
                     </antcall>

    Implementer note: There are several  -additional-build  sections. Ensure you find the one that has a  target  with a value of "webapp".

  2. Modify the following 3 lines in the build.properties file that is in the yale-cas-auth folder -- enter proper URLs for your organization:
    /deploy/AppServers/grouper-ui/contrib/yale-cas-auth/build.properties

    #Grouper CAS Integration  for   CalPoly
    sso.login.url=https: //mydev.YourCampus.edu/cas/login
    sso.validate.url=https: //mydev.YourCampus.edu:443/cas/serviceValidate
    grouper.server.name=s-grouper.its.YourCampus.edu

  3. Modify the struts-config.xml file to skip the login prompt by changing the callLogin path to home.do instead of login.do:
    /deploy/AppServers/grouper-ui/webapp/WEB-INF/struts-config.xml

    <forward name= "callLogin"   path= "/home.do"   redirect= "true" />

  4. Ensure the REMOTE_USER value that is returned from CAS is configured as one of the subject identifiers in the sources.xml:
    /deploy/AppServers/grouper/conf/sources.xml

    <init-param>
       <!-- col which identifies the row, perhaps not subjectId -->
       <param-name>subjectIdentifierCol0</param-name>
       <param-value>SUBJECT_NAME</param-value>
    </init-param>

    This is based on using the GrouperJdbcSourceAdapter2 source adapter type

  5. Ensure the grouper URL has been added to the CAS Services Registry.
Deployment Steps
  1. From the /deploy/AppServers/grouper-ui directory, create a new war file:

    ant war

  2. Remove the grouper directory in the $TOMCAT_HOME/webapps folder:

    cd /deploy/AppServers/tomcat/webapps
    rm -rf grouper

  3. Copy the new war file to the webapps directory (overwrite existing grouper.war file):

    cp /deploy/AppServers/grouper-ui/dist/grouper.war .

  4. Stop and restart Tomcat.

Troubleshoot

See debug information in logs in log4j.properties

log4j.logger.edu.internet2.middleware.grouper.ui.GrouperUiFilter = DEBUG


See Also


Grouper Built-in Basic Authentication to UI and Web Services

Grouper Web Services Authentication 

University of Pennsylvania Example

For an overview of authenticating to Grouper using Shib, see also the  Grouper UI Training Video, around minute 7.30.