New blog post on Alfresco DBP infrastructure and the Activiti community.
Category: Alfresco
Team Kick-Offs
New blog post on development team kick-off activities at Alfresco.
Where are the Digital Asset Management Standards?
A brief post on the CMIS4DAM technical committee is up on the Alfresco blog.
Adding Document Library Views to Alfresco Share
Overview
In this post we’ll go through how to develop and deploy additional view types beyond the default simple and detailed views in the document library in Share.
The techniques here require Alfresco 4.0.2 or later, or a recent checkout of community HEAD.
The Document Library Component
We won’t go into all the details on how the document library is rendered but it’s a fairly standard webscript-based component with ‘server-side’ resources like FreeMarker files for HTML fragments, Javascript for manipulating the data model, and localization properties files, and ‘client-side’ resources sent to the browser like CSS and Javascript.
While adding a document library view will involve changes to both client-side and server-side tiers, most of our notable changes will be to client-side Javascript files sent to the browser. The server-side webscripts changes are primarily to load and instantiate the client-side objects.
The component we’re interested in is at:
alfresco/site-webscripts/org/alfresco/components/documentlibrary/documentlist.*
and note that within that directory resources from include/documentlist.*
are included by some of the primary files.
DocumentList and DocumentListViewRenderer
The DocumentList
Javascript object makes up the bulk of the document library view and is defined in the client-side documentlist.js
file along with event handler methods and other display setup. The DocumentList
object sets up a YUI DataTable which handles most of the work of AJAX calls, pagination, etc.
Each column in the DataTable is assigned a method which dictates how to render that column given the data row. For example, the DataTable calls DocumentList.fnRenderCellThumbnail
for the ‘thumbnail’ column of each row it renders.
The DocumentList also contains a set of registered DocumentListViewRenderers
, ‘simple
‘ and ‘detailed
‘ by default, which do the work of generating the HTML for their view type. In other words, DocumentList.fnRenderCellThumbnail
actually determines the current view renderer based on the current viewRendererName
and hands off its work to DocumentListViewRenderer.renderCellThumbnail
.
This allows developers to create their own objects which extend and override the DocumentListViewRenderer
with methods for their specific needs.
For this example our goal will be to add a view which is very similar to the existing detail view but with larger thumbnails.
Steps to Add a View
Define the View
For a new view we need to define a new DocumentListViewRenderer
object. That customization could be just a new instance of the base DocumentListViewRenderer
with a different name so that alternate CSS is applied and metadata rendered.
The name of the view renderer specified in the constructor is used for CSS class names among other things so a simple example of adding a second detailed view with a larger thumbnail might look like:
new Alfresco.DocumentListViewRenderer("large");
In more complex scenarios you probably want to extend DocumentListViewRenderer
in a client-side documentlist-custom.js
file, which is what we’ll do in this example.
First we want to define a constructor:
Alfresco.DocumentListLargeViewRenderer = function(name) { Alfresco.DocumentListLargeViewRenderer.superclass.constructor.call(this, name); // Defaults to large but we'll copy the metadata from detailed view this.metadataBannerViewName = "detailed"; this.metadataLineViewName = "detailed"; this.thumbnailColumnWidth = 200; return this; };
then make it extend from DocumentListViewRenderer:
YAHOO.extend(Alfresco.DocumentListLargeViewRenderer, Alfresco.DocumentListViewRenderer);
and we’ll override the renderCellThumbnail
method with almost the same code in the detail view, but adding a documents-large
CSS class to the thumbnail container and changing the call to generateThumbnailUrl
to use the imgpreview
thumbnail definition:
Alfresco.DocumentList.generateThumbnailUrl(record, 'imgpreview')
Register the View
Once the view renderer has been defined it needs to be registered with the DocumentList
object via its registerViewRenderer
method. This needs to be done after the DocumentList
object has been created and setup, so a good place to perform the registration is after the YUI Bubbling Event postSetupViewRenderers
has fired. To register the same view above:
YAHOO.Bubbling.subscribe("postSetupViewRenderers", function(layer, args) { var scope = args[1].scope; var largeViewRenderer = new Alfresco.DocumentListLargeViewRenderer("large"); scope.registerViewRenderer(largeViewRenderer); });
Accessing the View
The user has to be able to navigate to this new view and even though we’ve registered it, the DocumentList
needs to know which view renderers are enabled and in what order, both of which are defined in the DocumentList’s options under viewRendererNames
. We can add our custom view above to the server-side FreeMarker model:
model.viewRendererNames.push("large");
Note that the name here must match the name of the view renderer constructor.
Not only does this define that the ‘large
‘ view is enabled, the navigation buttons are also rendered from this viewRendererNames
list, so we’ve taken care of the interface as well.
CSS
Our overridden renderCellThumbnail
method above adds the documents-large
CSS class so we’ll need to define that, as well as the display of the view selection button. Here’s a portion of the documentlist-custom.css
file:
.doclist .thumbnail.documents-large { height: 200px !important; width: 200px !important; } .doclist .thumbnail.documents-large img { max-width: 280px; }
Packaging
There are of course a number of ways this code could be packaged and deployed but this type of customization lends itself particularly well to a share extensibility module which Erik Winlöf and David Draper have blogged about in detail. We’ll assume a standard Maven or Gradle Java project layout for the code, and to keep things simple the example deploys as a Share jar rather than an AMP.
Server-side Resources
We need to define the customization itself:
src/main/resources/alfresco/site-data/extensions/custom-view-renderer.xml
<extension> <modules> <module> <id>Example :: Document List Custom View</id> <auto-deploy>true</auto-deploy> <customizations> <customization> <targetPackageRoot>org.alfresco</targetPackageRoot> <sourcePackageRoot>com.example.alfresco-share-doclib-views-example</sourcePackageRoot> </customization> </customizations> </module> </modules> </extension>
We could place the javascript which creates the new view renderer a number of places, even purely client-side, but as we’ll see in another post it can be handy to have access to the FreeMarker data model so we’ll place it in:
Note that the the wrapping <@markup>
directive there indicates that we want to inject the Javascript after documentListContainer
.
We’ll place the server-side webscript javascript which enables the view at:
which will get its label from the localization file at:
src/main/resources/alfresco/site-webscripts/com/example/alfresco-share-doclib-views-example/components/documentlibrary/documentlist.get.properties
Client-side Resources
For including client-side CSS, Javascript, and image resources in a jar we’ll place them in the META-INF dir at:
src/main/resources/META-INF/alfresco-share-doclib-views-example/components/documentlibrary/*
So at this point src/main/resources
should look like:
Package that as jar or AMP and deploy to your Share instance.
Results
The next time you go to a site’s document library you should see the additional
button for your view and the larger thumbnails:
Since we implemented this as a standard extensibility module you can completely disable it by going to your Share deployment’s module management page at something like http://localhost:8080/share/page/modules/deploy
and disable the module.
What’s Next?
I’ll go into more advanced customizations in future posts, but in the meantime, what kind of doclib view are you going to build?
Inconsistent CMIS Query Results? It’s not You, It’s your Locale.
A simple CMIS query like:
SELECT D.* FROM cmis:document AS D WHERE D.cmis:name LIKE '%Flower%' OR D.cmis:contentStreamFileName LIKE '%Flower%'
was giving me a fit lately, not working at all in RightsPro‘s CMIS plugin which uses OpenCMIS and yielding inconsistent results in CMIS Workbench where re-posting the same query ten times would give the expected result maybe half the time.
Thanks to Florian Müller and this post, it seems as though Alfresco doesn’t always behave as expected when the locale is set in the CMIS session.
Removing the locale session parameters got things working in RightsPro, but I didn’t immediately see an easy way to change the locale in a Workbench session (the log shows that it’s using a default of en_US
), and I still don’t know what’s up with the inconsistent results there, perhaps a coincidental caching issue.
This was all using OpenCMIS 0.3 against Alfresco Enterprise 3.3.1 over SSL.
Searching Custom Aspects via CMIS
I didn’t immediately see any simple, working examples of how to search custom aspect properties via CMIS so I’ll post a brief one here.
The Aspect
In this example we’re using a CMIS query to search for the IPTC caption property provided by the IPTC/EXIF project. After installing that module the properties are applied using an aspect which is defined in $ALFRESCO_HOME/WEB-INF/classes/alfresco/module/iptcexif/model/iptcModel.xml
.
Within that file you’ll see:
<aspects> <aspect name="iptc:iptc">
The Query
In our case the target Alfresco repository is running enterprise 3.3.1 and aspects are implemented via JOIN syntax as very briefly stated in the wiki.
So the resulting query for this particular search of the IPTC caption field ends up looking like:
SELECT D.*, IPTC.* FROM cmis:document AS D JOIN iptc:iptc AS IPTC ON D.cmis:objectId = IPTC.cmis:objectId WHERE IPTC.iptc:caption LIKE '%searchTerm%'
The Changes
There were big changes in Alfresco 3.4 in terms of metadata extraction and I haven’t yet had a chance to update the forge projects (or determine if they’re still even needed) and there are proposals for aspects in the next CMIS spec so I’ll be back to update this post.
Render Me This: Video Thumbnails in Alfresco 3.3
Alfresco has undergone quite a few changes under the hood in version 3.3, including refactoring of the ThumbnailService to make use of the RenditionService, which I had to explore in a recent deep dive to get video working for the thumbnails extension. This is obviously a subject dear to your heart as well or you would have moved on by now.
Alfresco 3.1.
Let’s take a look at how things worked back in the old days, before 3.2 (fuzzy 2009 dream sequence begins…)
You most likely kicked things off with a thumbnail generating URL which would hit a ScriptNode.createThumbnail
method which looked up the thumbnailDefinition
and called the ThumbnailService
with the TransformationOptions
contained in the definition.
The ThumbnailService
then told the ContentService
to perform the transformation which looked up the ContentTransformer
from the TransformerRegistry
and executed transformer.transform
which usually delegated to a RuntimeExec
object to perform the actual command line transformation.
If we were to visualize the key elements of that stack for a synchronous video thumbnail creation it would look something like:
- org.alfresco.repo.jscript.ScriptNode.createThumbnail
- org.alfresco.repo.thumbnail.ThumbnailRegistry.getThumbnailDefinition (loads TransformationOptions)
- org.alfresco.repo.thumbnail.ThumbnailServiceImpl.createThumbnail
- org.alfresco.repo.content.ContentServiceImpl.transform
- org.alfresco.repo.content.transform.ContentTransformerRegistry.getTransformer
- org.alfresco.repo.content.transform.ContentTransformer.transform
- TempFileProvider.createTempFile
- org.alfresco.util.exec.RuntimeExec.execute
- ffmpeg …
- org.alfresco.repo.content.ContentServiceImpl.transform
Well now, that’s not so bad.
Alfresco 3.3.
These Alfresco people don’t mess around. If it’s determined that refactoring should happen, it happens, real damn quick, even if it’s huge. To that end subsystems were rolled out in 3.2 and the RenditionService was introduced in 3.3.
Subsystems. Certain content transformers, like ImageMagick, have been refactored as a subsystem, which is cool. Read the wiki page to get the full story but in short it’s a much more flexible architecture.
The ffmpeg video transformer contained in the latest release of the thumbnails extension follows this precedence and has been refactored as a subsystem, so there.
RenditionService. I wouldn’t say I hate it, but this package should be wary of eating crackers in bed, or maybe I’m just not familiar enough with it yet.
The new rendition service utilizes RenditionDefinitions
which require that any TransformationOptions
be ‘serialized’ into a parameter map, which are then reassembled into TransformationOptions
objects before being passed to the ContentTransformer
. I suppose this is done to make it easier to do things like hand renditions off to another server in a cluster, but it’s a bit of a pain in the ass for developers.
So, back to our synchronous video thumbnail creation scenario (deep breath), the ThumbnailService
now creates a RenditionDefinition
which it passes to the RenditionService
which wraps things up in a PerformRenditionActionExecuter
that gets passed to the ActionService
. The PerformRenditionActionExecuter
calls the ActionService
again which loads the RenderingEngine
from the spring ApplicationContext
. A RenderingEngine
is itself an ActionExecuter
so the ActionService
calls execute
which calls render
which proceeds to rebuild the TransformationOptions
object needed by the ContentService
to get the proper ContentTransformer
so transformer.transform
can use a RuntimeExec
object to perform the actual command line transformation.
The key elements of our pseudo stack/method trace would look like:
- org.alfresco.repo.jscript.ScriptNode.createThumbnail
- org.alfresco.repo.thumbnail.ThumbnailRegistry.getThumbnailDefinition (loads TransformationOptions)
- org.alfresco.repo.thumbnail.ThumbnailServiceImpl.createThumbnail
- …
- org.alfresco.repo.thumbnail.ThumbnailServiceImpl.createRenditionDefinition
- org.alfresco.repo.thumbnail.ThumbnailRegistry.getThumbnailRenditionConvertor().convert(transformationOptions, assocDetails) <- this serializes the transformationOptions into a parameter map
- org.alfresco.repo.rendition.RenditionServiceImpl.render
- org.alfresco.repo.rendition.RenditionServiceImpl.createAndExecuteRenditionAction
- org.alfresco.repo.action.ActionServiceImpl.createAction(PerformRenditionActionExecuter.NAME)
- set the renditionDefintion on performRenditionAction
- org.alfresco.repo.action.ActionServiceImpl.executeAction
- …
- org.alfresco.repo.action.ActionServiceImpl.directActionExecution
- load ActionExecuter from applicationContext
- org.alfresco.repo.rendition.executer.ImageRenderingEngine.execute
- …
- org.alfresco.repo.rendition.executer.ImageRenderingEngine.render
- …
- org.alfresco.repo.rendition.executer.ImageRenderingEngine.getTransformationOptions <- rebuilds parameter map into TransformationOptions objects
- org.alfresco.repo.content.ContentServiceImpl.transform
- org.alfresco.repo.content.transform.ContentTransformerRegistry.getTransformer
- org.alfresco.repo.content.transform.ProxyContentTransformer.transform <- subsystems
- org.alfresco.repo.content.transform.ContentTransformerWorker.transform
- TempFileProvider.createTempFile
- org.alfresco.util.exec.RuntimeExec.execute
- ffmpeg …
- org.alfresco.repo.content.transform.ContentTransformerWorker.transform
- org.alfresco.repo.rendition.RenditionServiceImpl.createAndExecuteRenditionAction
that’s a spicy meatball!
A while ago we submitted an issue to Alfresco suggesting TransformationOptions which contain distinct target AND source options so that one could for example specify that all ‘doclib’ thumbnails be of max-width X and max-height Y (the type of target options currently available) and furthermore, if it’s a document take page 1 (or maybe you need a different page in your deployment) or if it’s a video take 1 frame starting 0.5 seconds in, or if it’s a layered image file take the second layer, etc. (a list of source options).
Alas, this concept of SourceTargetTransformationOptions
hasn’t yet been embraced by the Alfresco team but is used by the video transformer in the thumbnails extension which made refactoring for renditions even more difficult, but you’re not here to listen to me bitch and moan, so I’ll just say that it’s done and seems to be working fine.
So, there you have it, renditions and subsystems as they relate to video thumbnails. Hit me up with any questions or things I’ve missed.
Alfresco Video Thumbnails
It seems like every 6 months to never I hear: “The Alfresco thumbnails forge project used to be cool man, what happened?” Chill Winston, it’s still cool.
Now that Alfresco has a native implementation of thumbnails is the forge project still needed? Well, ‘needed’ is relative I suppose, but check out what the project gets you beyond the basic Alfresco distribution:
- Thumbnails when browsing, searching, or viewing a document in Alfresco Explorer (i.e. not Share)
- CoolIris view when browsing or searching in Alfresco Explorer (which is sweet)
- Actions to force the creation or update of thumbnails using Alfresco’s native thumbnail service
- A video transformer based on ffmpeg with the ability to specify an offset (also sweet)
- A patch to delete and regenerate thumbnails created by old versions of the forge project (Admittedly, not that awesome, but useful nonetheless. No one wants a messy bloated repo.)
There’s also several goodies on the roadmap for future releases of the project including Share mods.
So what are you waiting for? Go manage your modules and pop that amp into your war (for the non-geeks, I promise that’s not gross).
Alfresco 3 Thumbnails and the Forge Project
The thumbnails forge project was started quite a while ago (like Alfresco 0.9 or so… old school) and the first public release was put on the forge in late 2006 (which would have been around Alfresco 1.3 I guess).
The project gained the attention of the Alfresco team and they contacted me about making a few changes to bring it more inline with what they had in mind for an implementation, notably creating a public thumbnail service. Those changes were made and I continued to collaborate with Alfresco on the forge project and gave input on their implementation.
Alfresco 3 now has a native implementation of thumbnails and version 1.0 of the forge project migrated to the new architecture, but it’s still cool.
Here’s a look at some of the differences to be aware of between the old thumbnails project implementation and Alfresco’s new native thumbnails:
Concept | Pre 1.0 Versions of Forge Project | Alfresco 3 Native | |||||||||||||||||||||
The service responsible for generating and retrieving thumbnails |
ThumbnailService The A specific thumbnail can be generated with overriding options |
ThumbnailService The The content property from which the source data will be read |
|||||||||||||||||||||
A common size and destination mimetype for thumbnails |
ThumbnailSpecification
|
ThumbnailDefinition
|
|||||||||||||||||||||
The detailed resizing specifications |
TransformerSpecification The specific |
TransformationOptions The resizing details are specified using classes containing generic |
|||||||||||||||||||||
The model type |
tn:thumbnail
Not being of type content prevents most rules from being run on |
cm:thumbnail
Care must be taken to not run rules or index thumbnail nodes since they One suggestion has been to add the following to the thumbnail type:
|
|||||||||||||||||||||
The model property name of the common size and mimetype destination definition |
tn:specificationName The |
cm:thumbnailName The new |
|||||||||||||||||||||
Default thumbnail sizes (max length/width) |
|
|
- Strikethroughs represent deprecated items.
- Avatar thumbnail size has nothing to do with what I can only assume is an incredibly overhyped movie about pissed off blue tree people since I haven’t been to a theater in like 10 years.
This information has been available in the thumbnails project’s 1.0 release readme but as it’s not shown by default I figured I’d post here as well.
Alfresco, SSO, and LDAP Expiring Date Attribute
Deciding where to handle authorization in a setup where Alfresco’s authentication is handled by CAS which itself authenticates against LDAP may not be as easy as it sounds. This post goes through some of the possibilities and one solution.
LDAP Authentication with an Expiration Date Attribute
On Alfresco version 2.x we extended the LDAPAuthenticationComponentImpl
class to be able to evaluate an LDAP filter string, configurable via spring, which was used primarily for the purpose of determining whether the user’s access to Alfresco had expired, with the expiration date being stored as an LDAP attribute. So our ldap-authenication.properties
file would contain something like:
ldap.authentication.authenticationFilterFormat=(&(uid=%s)(alfrescoExpirationDate>=%sZ))
and the extended Java class LDAPAuthenticationComponentImpl
contained:
ctx = ldapInitialContextFactory.getInitialDirContext(bindDn, new String(password));
…
String authenticationFilter = String.format(
authenticationFilterFormat, new Object[]{userName, timestamp});
…
NamingEnumeration<SearchResult> answer = ctx.search(
authenticationSearchBase, authenticationFilter, userSearchCtls);
if (answer != null && answer.hasMoreElements()) {
setCurrentUser(escapeUserName(userName, escapeCommasInUid));
}
…
Once the expiration date was reached the user was denied access to the application.
Moving to Single Sign On
We’re now migrating to Alfresco 3.x and also have the need to move to a single sign on solution. Jasig’s Central Authentication Service (CAS) was chosen as the implementation, requiring some changes to our expiration strategy.
In our previous configuration, authentication (is this user who they say they are) and authorization (is this user allowed to do x) were intertwined in our single custom LDAPAuthenticationComponentImpl
class. Any other applications authenticating against the same LDAP server could implement their own authentication/authorization methods.
CAS however is only concerned with authentication, not authorization, and applications utilizing it need to be able to decouple those functions, which is a better architecture in most cases anyway.
Our task then is to determine where in the authentication/authorization strategy we should handle this expiration authorization.
Choosing Where to Handle Authorization
Customize the CAS Server?
Modifying the CAS LDAP authentication handler wouldn’t be an appropriate place for our expiration authorization since all CAS services share the same authentication handler and other applications that have no notion of the Alfresco service or its expiration could be denied authentication.
We could try implementing a CAS authorizing serviceRegistryDao
which would in theory allow the CAS server to determine if the user were authorized to use the service requested beyond just the Enabled and SSO Participant settings. An authorization component could be defined that would make the authorization decision based on the service being requested and the user requesting it. Each service could choose its authorization component as an option during setup in the CAS Service Management application.
CAS really wants the client/application to handle authorization though, so customizing the server doesn’t look like a recommended approach.
Customize Alfresco?
We could setup Alfresco’s authentication chain to talk to CAS, but in version 3.2 Alfresco has changed its authentication configuration to use the notion of Authentication Subsystems (which seems nicely implemented) and most documentation indicates that integration with something like CAS is best achieved by using mod_cas or mod_auth_cas in Apache web server, and since we’re already using Apache in front of Tomcat with mod_proxy_ajp that approach seems like a good fit.
Wait, Can a Hacker Just Spoof HTTP Headers?
It looks like AJP between Tomcat and the web server is responsible for parsing these protocol specific (non-HTTP) headers to find the remote user value and set that in Tomcat’s HttpServletRequest,
so a hacker can’t just modify the headers in their browser and have that be translated into an authenticated user.
However, it is extremely important that your Tomcat connector be behind a firewall and only accept connections from known web servers. Otherwise a hacker could set up their own web server with a bogus authentication mechanism which would add an authenticated user header in the AJP message, which in turn would translate to that user being signed in to a protected application.
mod_cas or mod_auth_cas?
With proper security in place we should be OK to use an Apache CAS module for authentication, but which one?
mod_cas:
- hasn’t been maintained in quite a while
- has missing links all over the site
- the documentation incorrectly states that “CAS stands for Common Authorization Service”
- Jasig recommends against it
so, um, we’ll go with mod_auth_cas.
There’s a detailed wiki article on setting up Alfresco authentication through mod_auth_cas but our challenge is to determine where in the flow to inject our authorization.
Option 1: Extend Alfresco’s Request Authn to Grab CAS Attribute
The CAS server can return attributes to the client and if mod_auth_cas passes those attributes through mod_proxy_ajp to Tomcat and Alfresco then we may be able to modify the component which checks that header for the authenticated username, HTTPRequestAuthenticationFilter
, to also look for the expiration date attribute and authorize or deny the user.
After modifying Tomcat’s example snoop application to show all headers and attributes:
<%
java.util.Enumeration eH = request.getHeaderNames();
while (eH.hasMoreElements()) {
String headerName = (String) eH.nextElement();
out.print("Header <b>" + headerName + ": </b>");
out.print(request.getHeader(headerName) + "<br>");
}
out.print("<br><br>");
java.util.Enumeration eA = request.getAttributeNames();
while (eA.hasMoreElements()) {
String attributeName = (String) eA.nextElement();
out.print("Attribute <b>" + attributeName + ": </b>");
out.print(request.getAttribute(attributeName) + "<br>");
}
%>
it doesn’t look like the additional attributes defined to be available to the CAS service are passed through mod_auth_cas and mod_proxy_ajp to the secured application. We might be able to pass the data through SAML attributes but it seems that would require POST requests.
Option 2: Extend Alfresco’s Request Authn to query LDAP directly
If we can’t get the expiration date attribute from the request headers we still may be able to modify the request authentication filter to make a trip directly to the LDAP server to query for the attribute and confirm or deny authorization, but maybe we can do everything at the web server.
Option 3: Handle Authorization at Apache
The ‘auth’ in mod_auth_cas appears to only include authentication but there may be hope in allowing mod_auth_cas to handle just the authentication part then using mod_authnz_ldap or the third-party mod_authz_ldap module to handle the authorization side.
mod_authnz_ldap’s Require ldap-filter looks promising, but we would need to insert a dynamic date (today’s) for comparison to the user’s expiration date. LDAP syntax doesn’t seem to have any keyword for the current date to be handled on the server side and it doesn’t look like mod_authnz_ldap has any special tags for injecting it in the filter string so the source code would have to be modified.
mod_authz_ldap however has this in its filter configuration:
%t The current time in the format YYYYMMDDhhmmss
that may just do it.
Unfortunately, upon further investigation mod_authz_ldap does not support secure SSL/TLS communication with the LDAP server and hasn’t been maintained in a quite a while, so that won’t work.
Dare I try modifying the mod_authnz_ldap source to allow for a dynamic replace of a date tag? Sure, why not?
The last time I wrote C code it was probably on a machine running Mac OS 7.6, but after several hours and as many espressos I’ve taken the relevant code from mod_authz_ldap’s source, tweaked it a bit, and found the appropriate place to inject it in mod_authnz_ldap’s authn_ldap_build_filter
.
You can compile and install a single module on the server with:
apxs -i -a -c mod_authnz_ldap.c
then add the appropriate config to your location directive. So we have CAS performing authentication and a modified mod_authnz_ldap performing authorization on our Tomcat examples app:
<Location /examples>
AuthType CAS
AuthName "CAS"
CASScope /examples
AuthLDAPURL "ldaps://server.company.com/ou=users,dc=company,dc=com?uid?sub?(alfrescoExpirationDate>=$tZ)"
AuthLDAPBindDN "uid=reader,ou=users,dc=company,dc=com"
AuthLDAPBindPassword "*****"
require valid-user
</Location>
Again, my C chops are extremely rusty but I’ve contributed these changes to mod_authnz_ldap to Apache as Bug 47838.
Other Alfresco SSO Caveats
We can secure the Alfresco app in the same manner as /examples above but we’ll have to secure Alfresco Share with SSO as well and may run into issues with ticket parameter clashes.
As stated in one of the forum posts above, the header Alfresco needs to look for is CAS-User. Before Alfresco 3.2 the web.xml changes would look like:
<filter-class>org.alfresco.web.app.servlet.HTTPRequestAuthenticationFilter</filter-class>
<!-- Name of HTTP header containing UserID. -->
<init-param>
<param-name>httpServletRequestAuthHeaderName</param-name>
<param-value>CAS-User</param-value>
</init-param>
...
Other Spring Apps with CAS
In some of our other apps we use Spring Security and will want to integrate them with CAS. There’s a good write up on the details of the communication between spring security and CAS and a forum post on CAS with LDAP authorization that should help.
Hopefully this account of a mad man in pursuit of expiring authorization will be of use to someone, somewhere, someday.