From OpenNMS
Warning ... These notes are totally a work-in progress. You should also be aware that I'm not an Acegi Guru, and I'm learning as I go along.
Note on OpenNMS Versions
These notes refer to OpenNMS version 1.3.2 through 1.6.x. Beginning with 1.7.x acegi was replaced with spring, so you should read Spring Security and LDAP.
Acegi Security
OpenNMS now uses Acegi Security to handle authentication and authorization for the webapp. This page is a work in progress and a placemarker for Jonathan's notes on using Acegi Security and LDAP / Active Directory together. A good starting point for somebody who wanted to figure this out would be the Acegi Security Reference Documentation.
Acegi Security and OpenNMS
The key file is applicationContext-acegi-security.xml which lives in WEB-INF in the directory OPENNMS_HOME/jetty-webapps/opennms if you are using OpenNMS 1.3.7 or later or OPENNMS_HOME/webapps/opennms if you are using OpenNMS 1.3.6 or earlier (or are otherwise using Tomcat or another servlet container in place of Jetty). As shipped, this uses a DAO Authentication Provider that plugs in the existing users.xml (to provide user id and password information) and magic-users.properties (to provide role information for admin, read-only and rtc users) files. In this way, user configuration for the Acegi enabled OpenNMS is compatible with previous releases. The key advantage with Acegi Security is that you can plug in different implementations of the Acegi AuthenticationProvider, one of which is the Ldap Authentication Provider. What's more, the different Authentication Providers are "stackable". You can (and probably want to) use the Dao Authentication provider and Ldap authentication provider in tandem, to allow you to introduce new users to the webapp without breaking the existing admin and rtc users.
Key at this point to understand is that in order for some parts of the webapp to function (particularly the selective display of the admin links for users with the admin role), there is an OpenNMS implementation of LdapAuthoritiesPopulator (UserAttributeLdapAuthoritiesPopulator). This is used to replicate the existing code that grants authorities to users. This implementation only supports granting authorities by the addition of additional attributes to that users entry in the directory. Active Directory users may be more accustomed to granting authorities (or access) by membership of a given group. This is not yet supported, though it is possible to have the webapp function to some degree without it.
Configuring applicationContext-acegi-security.xml
The relevant boilerplate in applicationContext-acegi-security.xml is:
<bean id="initialDirContextFactory"
class="org.acegisecurity.ldap.DefaultInitialDirContextFactory">
<constructor-arg value="ldap://%%%put LDAP server address here%%%:389/"/>
</bean>
<bean id="userSearch"
class="org.acegisecurity.ldap.search.FilterBasedLdapUserSearch">
<constructor-arg index="0">
<value></value>
</constructor-arg>
<constructor-arg index="1">
<value>(login={0})</value>
</constructor-arg>
<constructor-arg index="2">
<ref local="initialDirContextFactory" />
</constructor-arg>
<property name="searchSubtree">
<value>true</value>
</property>
</bean>
<bean id="userAttributeLdapAuthoritiesPopulator"
class="org.opennms.web.acegisecurity.UserAttributeLdapAuthoritiesPopulator">
<constructor-arg><ref local="initialDirContextFactory"/></constructor-arg>
<constructor-arg><value>%%%put role attribute here%%%</value></constructor-arg>
</bean>
<bean id="ldapAuthenticationProvider"
class="org.acegisecurity.providers.ldap.LdapAuthenticationProvider">
<constructor-arg>
<bean class="org.acegisecurity.providers.ldap.authenticator.BindAuthenticator">
<constructor-arg><ref local="initialDirContextFactory"/></constructor-arg>
<property name="userSearch"><ref bean="userSearch"/></property>
</bean>
</constructor-arg>
<constructor-arg>
<ref bean="userAttributeLdapAuthoritiesPopulator"/>
</constructor-arg>
</bean>
A customised version might look like this:
<bean id="initialDirContextFactory"
class="org.acegisecurity.ldap.DefaultInitialDirContextFactory">
<constructor-arg value="ldap://192.168.1.1:389/"/>
<property name="managerDn">
<value>CN=opennms,OU=Service Accounts,DC=opennms,DC=org</value>
</property>
<property name="managerPassword">
<value>OpenNMS</value>
</property>
</bean>
<bean id="userSearch"
class="org.acegisecurity.ldap.search.FilterBasedLdapUserSearch">
<constructor-arg index="0">
<value>OU=Users,DC=opennms,DC=org</value>
</constructor-arg>
<constructor-arg index="1">
<value>(sAMAccountName={0})</value>
</constructor-arg>
<constructor-arg index="2">
<ref local="initialDirContextFactory"/>
</constructor-arg>
<property name="searchSubtree">
<value>true</value>
</property>
</bean>
<bean id="userAttributeLdapAuthoritiesPopulator"
class="org.opennms.web.acegisecurity.UserAttributeLdapAuthoritiesPopulator">
<constructor-arg><ref local="initialDirContextFactory"/></constructor-arg>
<constructor-arg><value>extensionAttribute2</value></constructor-arg>
</bean>
<bean id="ldapAuthenticationProvider"
class="org.acegisecurity.providers.ldap.LdapAuthenticationProvider">
<constructor-arg>
<bean class="org.acegisecurity.providers.ldap.authenticator.BindAuthenticator">
<constructor-arg><ref local="initialDirContextFactory"/></constructor-arg>
<property name="userSearch"><ref bean="userSearch"/></property>
</bean>
</constructor-arg>
<constructor-arg>
<ref bean="userAttributeLdapAuthoritiesPopulator"/>
</constructor-arg>
</bean>
This example will perform its initial bind to the directory with a DN of "CN=opennms,OU=Service Accounts,DC=opennms,DC=org" and a password of "OpenNMS". It then searches the directory from "OU=Users,DC=opennms,DC=org" downwards, looking for an sAMAccountName that matches the proffered userid (yes this is active directory). It will then rebind as the user, with the offered password to authenticate the user. Finally it will retrieve the role from the users attribute extensionAttribute2, which should be "ROLE_USER" for users only, and multivalued with separate values of "ROLE_USER" and "ROLE_ADMIN" for admins. This caught me out for a couple of days until dj and gvainfo straightened me out. Unfortunately AD's extensionAttribute2 is not a multivalued attribute (none of the extentionAttributeN attributes are). To get around this, I needed to make another change to applicationContext-acegi-security.xml. Instead of requiring admin users to have attributes of both ROLE_ADMIN _and_ ROLE_USER, I gave ROLE_ADMIN the same rights as ROLE_USER with the addition of access to the admin pages thus:
<bean id="filterInvocationInterceptor" class="org.acegisecurity.intercept.web.FilterSecurityInterceptor">
<property name="authenticationManager"><ref bean="authenticationManager"/></property>
<property name="accessDecisionManager"><ref local="httpRequestAccessDecisionManager"/></property>
<property name="objectDefinitionSource">
<value>
CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
PATTERN_TYPE_APACHE_ANT
/logoff.jsp=ROLE_ANONYMOUS,ROLE_USER,ROLE_ADMIN
/acegilogin.jsp*=ROLE_ANONYMOUS,ROLE_USER,ROLE_ADMIN
/css/styles.css=ROLE_ANONYMOUS,ROLE_USER,ROLE_ADMIN
/css/print.css=ROLE_ANONYMOUS,ROLE_USER,ROLE_ADMIN
/js/pngfix.js=ROLE_ANONYMOUS,ROLE_USER,ROLE_ADMIN
/js/global.js=ROLE_ANONYMOUS,ROLE_USER,ROLE_ADMIN
/images/logo.png=ROLE_ANONYMOUS,ROLE_USER,ROLE_ADMIN
/images/logo-background.gif=ROLE_ANONYMOUS,ROLE_USER,ROLE_ADMIN
/images/headingbg.png=ROLE_ANONYMOUS,ROLE_USER,ROLE_ADMIN
/images/mainbg.png=ROLE_ANONYMOUS,ROLE_USER,ROLE_ADMIN
/images/headerbg.png=ROLE_ANONYMOUS,ROLE_USER,ROLE_ADMIN
/images/footerbg.png=ROLE_ANONYMOUS,ROLE_USER,ROLE_ADMIN
/admin/**=ROLE_ADMIN
/rtc/post/**=ROLE_RTC
/webstart/**=ROLE_ANONYMOUS,ROLE_USER,ROLE_ADMIN
/**=ROLE_USER,ROLE_ADMIN
</value>
</property>
</bean>
This is probably not the best way of doing things as it may break stuff elsewhere in OpenNMS, but for now it seems to work for me.
We used ADSI Edit from the Microsoft Server 2003 resource kit to edit the extensionAttribute2 field. Take care if you do this yourself on a live AD schema.
Group Membership 02/14/08
I successfully configured an openNMS 1.3.9-1 setup to authenticate from LDAP, using group memberships to determine access rights. I tried to follow the documentation provided here and on Acegi's website, but it still took me half a day to get it setup correctly. It actually was pretty simple, provided one knew what bean properties controlled what aspect of the LDAP setup. This should only take you 5 minutes at the most. Please use Acegi's Java Doc repository as a reference.
You need a working LDAP setup. Once you have that, you only have to edit one file: $OPENNMS_HOME/jetty-webapps/opennms/WEB-INF/applicationContext-acegi-security.xml . Backup this file before continuing!
In the AUTHENTICATION section, uncomment the ldapAuthenticationProvider tag. Although the comment in the file says to comment out the daoAuthenticationProvider tag, I left it there and ordered it under the ldap tag so I didn't have to add a rtc user into the LDAP directory. This also means you still can log in with your initial username if your LDAP server goes down. The disadvantage is that all internal calls for rtc-user - authentication will first go to the LDAP-server. If you put the daoAuthenticationProvider on top, authentication first tries the users defined in users.xml and if this fails it will try to authenticate against LDAP.
<bean id="authenticationManager" class="org.acegisecurity.providers.ProviderManager">
<property name="providers">
<list>
<ref local="ldapAuthenticationProvider"/>
<ref local="daoAuthenticationProvider"/>
<ref local="anonymousAuthenticationProvider"/>
</list>
</property>
</bean>
Now this is the not so hard part.
1. In the LDAP AUTHENTICATION section, you want to add your LDAP information and your manager DN information in the first bean construct.
2. In the userSearch bean, you need to add your LDAP base information, and, if necessary, change the construct-arg index=1 tag to whatever you identify your users by. We use 'uid'.
3. In the groupLdapAuthoritiesPopulator bean, you need to put the full dn of the group object, e.g ou=group,dc=example,dc=com. Property groupSearchFilter defines the attribute openNMS will search each group to find the members. memberUid is for Posixgroups type. Next, edit the properties userGroups and adminGroups and put, IN UPPERCASE LETTERS, the name of the groups you are using to define user roles. I used administrators and users in my LDAP setup, so I had to put that here in uppercase.
4. Lastly, in the LdapAuthenticationProvider bean, you need to reference the groupLdapAuthoritiesPopulator bean for this all to work.
See below for an example.
<bean id="initialDirContextFactory"
class="org.acegisecurity.ldap.DefaultInitialDirContextFactory">
<constructor-arg value="ldap://%%YOUR LDAP URL%%:389/"/>
<property name="managerDn">
<value>%%ROOT USER%%</value>
</property>
<property name="managerPassword">
<value>%%ROOT PASSWORD%%</value>
</property>
</bean>
<bean id="userSearch"
class="org.acegisecurity.ldap.search.FilterBasedLdapUserSearch">
<constructor-arg index="0">
<value>%%YOUR LDAP BASE GOES HERE%%</value>
</constructor-arg>
<constructor-arg index="1">
<value>(uid={0})</value>
</constructor-arg>
<constructor-arg index="2">
<ref local="initialDirContextFactory" />
</constructor-arg>
<property name="searchSubtree">
<value>true</value>
</property>
</bean>
<bean id="userAttributeLdapAuthoritiesPopulator"
class="org.opennms.web.acegisecurity.UserAttributeLdapAuthoritiesPopulator">
<constructor-arg><ref local="initialDirContextFactory"/></constructor-arg>
<constructor-arg><value>%%YOUR USER ATTRIBUTE HERE%%</value></constructor-arg>
</bean>
<bean id="groupLdapAuthoritiesPopulator"
class="org.opennms.web.acegisecurity.UserGroupLdapAuthoritiesPopulator">
<constructor-arg><ref local="initialDirContextFactory"/></constructor-arg>
<constructor-arg>
<value>%%FULL DN OF THE GROUP OBJECT%%</value>
</constructor-arg>
<property name="groupSearchFilter"><value>memberUid={1}</value></property>
<property name="groupRoleAttribute"><value>cn</value></property>
<property name="searchSubtree"><value>false</value></property>
<property name="convertToUpperCase"><value>true</value></property>
<property name="userGroups">
<list>
<value>ADMINISTRATORS</value>
<value>USERS</value>
<value>any group</value>
</list>
</property>
<property name="adminGroups">
<list>
<value>ADMINISTRATORS</value>
</list>
</property>
</bean>
<bean id="ldapAuthenticationProvider"
class="org.acegisecurity.providers.ldap.LdapAuthenticationProvider">
<constructor-arg>
<bean class="org.acegisecurity.providers.ldap.authenticator.BindAuthenticator">
<constructor-arg><ref local="initialDirContextFactory"/></constructor-arg>
<property name="userSearch"><ref bean="userSearch"/></property>
</bean>
</constructor-arg>
<constructor-arg>
<ref bean="groupLdapAuthoritiesPopulator"/>
</constructor-arg>
</bean>
That's it. Restart openNMS and watch the $OPENNMS_HOME/logs/webapp/web.log file to troubleshoot. If after a restart all you see in the file is a line about the java vm, then you have an error in the xml file. Edit the file and try again. Good luck!
This worked for us with one minor change in our RedHat Directory Server environment. Group membership uses uniquemember and takes the DN. Apparently {1} provides just the uid (which is perfect for memberUid). {0} provides the complete DN. Thus we set: <property name="groupSearchFilter"><value>uniquemember={0}</value></property>
We also hit some issues trying to use LDAPS. The applicationContext-acegi-security.xml changes were simple. We simple changed <constructor-arg value="ldap://%%YOUR LDAP URL%%:389/"/> to <constructor-arg value="ldaps://%%YOUR LDAP URL%%:636/"/>
Our problem was trusting the certificates on the LDAP servers. To do this, we set up custom keystores as follows (change the path as appropriate to your environment)
cd /usr/java/jdk1.5.0_18/bin
./keytool -import -alias myca -keystore /etc/pki/tls/certs/cacerts.jks -file /etc/pki/tls/certs/MyCA.pem -trustcacerts
./keytool -import -alias myca -keystore /etc/pki/tls/private/keystore.jks -file /etc/pki/tls/certs/MyCA.pem -trustcacerts
(change the passwords and tighten up security)
chmod 600 /etc/pki/tls/private/keystore.jks
./keytool -storepasswd -keystore /etc/pki/tls/certs/cacerts.jks
./keytool -storepasswd -keystore /etc/pki/tls/private/keystore.jks
history -c (the passwords are echoed in the clear)
Then we needed to edit /opt/opennms/etc/opennms.properties. Before doing so, we tighten security so nothing leaks out via the file or backup versions: chmod o-rwx /opt/opennms/etc/opennms.properties Then we edit the file by adding:
- SSL Configuration
javax.net.ssl.keyStore=/etc/pki/tls/private/keystore.jks
javax.net.ssl.keyStorePassword=newpassword
javax.net.ssl.trustStore=/etc/pki/tls/certs/cacerts.jks
javax.net.ssl.trustStorePassword=newpassword
We restarted OpenNMS and it worked. Thanks to Ron Roskens for his help.
Group Membership - beware, work in progress
I have had a crack at getting this to work with active directory using Group membership. I was able to get authentication and access control working, however, due to the absence of any OpenNMS specific code to process authorities ina way that existing, non-spring code could understand, the webapp was substantially unaware of my role (and thus headers were not displayed with the correct roles in the webapp). For interest, though this is not supported, the ldapAuthenticationProvider and userAttributeLdapAuthoritiesPopulator beans would look something like this:
<bean id="ldapAuthoritiesPopulator"
class="org.acegisecurity.providers.ldap.populator.DefaultLdapAuthoritiesPopulator">
<constructor-arg><ref local="initialDirContextFactory"/></constructor-arg>
<constructor-arg>
<value>ou=App_Auth,ou=Security Groups,dc=opennms,dc=org</value>
</constructor-arg>
<property name="groupRoleAttribute"><value>cn</value></property>
<property name="searchSubtree"><value>false</value></property>
<property name="convertToUpperCase"><value>true</value></property>
</bean>
<bean id="ldapAuthenticationProvider"
class="org.acegisecurity.providers.ldap.LdapAuthenticationProvider">
<constructor-arg>
<bean class="org.acegisecurity.providers.ldap.authenticator.BindAuthenticator">
<constructor-arg><ref local="initialDirContextFactory"/></constructor-arg>
<property name="userSearch"><ref bean="userSearch"/></property>
</bean>
</constructor-arg>
<constructor-arg>
<ref bean="ldapAuthoritiesPopulator"/>
</constructor-arg>
</bean>
I created a couple of groups at the level "ou=App_Auth,ou=Security Groups,dc=opennms,dc=org", one called opennms_user and one called opennms_admin. Again, users needed to be in just the opennms_user group, admins in in opennms_admin
I then had to edit the filterInvocationInterceptor further down the file.
<bean id="filterInvocationInterceptor" class="org.acegisecurity.intercept.web.FilterSecurityInterceptor">
<property name="authenticationManager"><ref bean="authenticationManager"/></property>
<property name="accessDecisionManager"><ref local="httpRequestAccessDecisionManager"/></property>
<property name="objectDefinitionSource">
<value>
CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
PATTERN_TYPE_APACHE_ANT
/logoff.jsp=ROLE_ANONYMOUS,ROLE_USER,ROLE_OPENNMS_USER
/acegilogin.jsp*=ROLE_ANONYMOUS,ROLE_USER,ROLE_OPENNMS_USER
/css/styles.css=ROLE_ANONYMOUS,ROLE_USER,ROLE_OPENNMS_USER
/css/print.css=ROLE_ANONYMOUS,ROLE_USER,ROLE_OPENNMS_USER
/js/pngfix.js=ROLE_ANONYMOUS,ROLE_USER,ROLE_OPENNMS_USER
/js/global.js=ROLE_ANONYMOUS,ROLE_USER,ROLE_OPENNMS_USER
/images/logo.png=ROLE_ANONYMOUS,ROLE_USER,ROLE_OPENNMS_USER
/images/logo-background.gif=ROLE_ANONYMOUS,ROLE_USER,ROLE_OPENNMS_USER
/images/headingbg.png=ROLE_ANONYMOUS,ROLE_USER,ROLE_OPENNMS_USER
/images/mainbg.png=ROLE_ANONYMOUS,ROLE_USER,ROLE_OPENNMS_USER
/images/headerbg.png=ROLE_ANONYMOUS,ROLE_USER,ROLE_OPENNMS_USER
/images/footerbg.png=ROLE_ANONYMOUS,ROLE_USER,ROLE_OPENNMS_USER
/admin/**=ROLE_ADMIN,ROLE_OPENNMS_ADMIN
/rtc/post/**=ROLE_RTC
/webstart/**=ROLE_ANONYMOUS,ROLE_USER,ROLE_OPENNMS_USER
/**=ROLE_USER,ROLE_OPENNMS_USER
</value>
</property>
</bean>
As I said, this seemed to work as far as access control and authorization went, but the webapp was unaware of the role granted when it came to displaying appropriate options (although admin users could access admin pages directly and regular users could not).
Group Membership Redux - ONMS 1.7.0 working config
OPENNMS_HOME/jetty-webapps/opennms/WEB-INF/applicationContext-acegi-security.xml
Original
<!-- ========================== AUTHENTICATION ======================== -->
<bean id="authenticationManager" class="org.acegisecurity.providers.ProviderManager">
<property name="providers">
<list>
<ref local="daoAuthenticationProvider"/>
<!--
Uncomment the following reference to ldapAuthenticationProvider
and comment-out the above reference to the
daoAuthenticationProvider if you want to use LDAP. You'll also
need to uncomment the LDAP AUTHENTICATION section below, as well.
-->
<!--
<ref local="ldapAuthenticationProvider"/>
-->
<ref local="anonymousAuthenticationProvider"/>
</list>
</property>
</bean>
New
<!-- ========================== AUTHENTICATION ======================== -->
<bean id="authenticationManager" class="org.acegisecurity.providers.ProviderManager">
<property name="providers">
<list>
<!--
Uncomment the following reference to ldapAuthenticationProvider
and comment-out the above reference to the
daoAuthenticationProvider if you want to use LDAP. You'll also
need to uncomment the LDAP AUTHENTICATION section below, as well.
-->
<ref local="ldapAuthenticationProvider"/>
<ref local="daoAuthenticationProvider"/>
<ref local="anonymousAuthenticationProvider"/>
</list>
</property>
</bean>
Original
-->
<!--
<bean id="initialDirContextFactory"
class="org.acegisecurity.ldap.DefaultInitialDirContextFactory">
<constructor-arg value="ldap://%%%put LDAP server address here%%%:389/"/>
</bean>
New
-->
<bean id="initialDirContextFactory"
class="org.acegisecurity.ldap.DefaultInitialDirContextFactory">
<constructor-arg value="ldaps://yourserverhere:636/"/>
<property name="managerDn">
<value>cn=SomeName,dc=com</value>
</property>
<property name="managerPassword">
<value>yourpassword</value>
</property>
</bean>
Original
<bean id="userSearch"
class="org.acegisecurity.ldap.search.FilterBasedLdapUserSearch">
<constructor-arg index="0">
<value></value>
</constructor-arg>
<constructor-arg index="1">
<value>(login={0})</value>
</constructor-arg>
<constructor-arg index="2">
<ref local="initialDirContextFactory" />
</constructor-arg>
<property name="searchSubtree">
<value>true</value>
</property>
</bean>
New
<bean id="userSearch"
class="org.acegisecurity.ldap.search.FilterBasedLdapUserSearch">
<constructor-arg index="0">
<value>ou=BaseOU,dc=com</value>
</constructor-arg>
<constructor-arg index="1">
<value>(uid={0})</value>
</constructor-arg>
<constructor-arg index="2">
<ref local="initialDirContextFactory" />
</constructor-arg>
<property name="searchSubtree">
<value>true</value>
</property>
</bean>
Original
<bean id="groupLdapAuthoritiesPopulator"
class="org.opennms.web.acegisecurity.UserGroupLdapAuthoritiesPopulator">
<constructor-arg><ref local="initialDirContextFactory"/></constructor-arg>
<constructor-arg>
<value>%%% put your search group path here%%%</value>
</constructor-arg>
<property name="groupRoleAttribute"><value>cn</value></property>
<property name="searchSubtree"><value>false</value></property>
<property name="convertToUpperCase"><value>true</value></property>
<property name="userGroups">
<list>
<value>OPENNMS_USER</value>
<value>OPENNMS_ADMIN</value>
<value>any group</value>
</list>
</property>
<property name="adminGroups">
<list>
<value>OPENNMS_ADMIN</value>
</list>
New
<bean id="groupLdapAuthoritiesPopulator"
class="org.opennms.web.acegisecurity.UserGroupLdapAuthoritiesPopulator">
<constructor-arg><ref local="initialDirContextFactory"/></constructor-arg>
<constructor-arg>
<value>ou=Groups,dc=com</value>
</constructor-arg>
<property name="groupSearchFilter"><value>memberUid={1}</value></property>
<property name="groupRoleAttribute"><value>cn</value></property>
<property name="searchSubtree"><value>false</value></property>
<property name="convertToUpperCase"><value>true</value></property>
<property name="userGroups">
<list>
<value>ONMS_ADMIN</value>
<value>ONMS_USER</value>
</list>
</property>
<property name="adminGroups">
<list>
<value>ONMS_ADMIN</value>
</list>
</property>
<property name="readonlyGroups">
<list>
<value>ONMS_RO</value>
</list>
</property>
<property name="dashboardGroups">
<list>
<value>ONMS_DASH</value>
</list>
Original
<constructor-arg>
<ref bean="userAttributeLdapAuthoritiesPopulator"/>
</constructor-arg>
</bean>
-->
<bean id="roleVoter" class="org.acegisecurity.vote.RoleVoter"/>
New
<constructor-arg>
<!--
<ref bean="userAttributeLdapAuthoritiesPopulator"/>
-->
<ref bean="groupLdapAuthoritiesPopulator"/>
</constructor-arg>
</bean>
<bean id="roleVoter" class="org.acegisecurity.vote.RoleVoter"/>
Ldap entries
dn: cn=ONMS_ADMIN,ou=Groups,dc=com gidNumber: 7000 userPassword:: e2NyeXB0fXg= memberUid: adminsname objectClass: posixGroup objectClass: top description: OpenNMS admin group cn: ONMS_ADMIN dn: cn=ONMS_USER,ou=Groups,dc=com gidNumber: 7001 userPassword:: e2NyeXB0fXg= memberUid: reguser objectClass: posixGroup objectClass: top description: OpenNMS user group cn: ONMS_USER dn: cn=ONMS_DASH,dc=com gidNumber: 7002 userPassword:: e2NyeXB0fXg= memberUid: dashboarduser objectClass: posixGroup objectClass: top description: OpenNMS Dashboard user group cn: ONMS_DASH dn: cn=ONMS_RO,dc=com gidNumber: 7003 userPassword:: e2NyeXB0fXg= memberUid: dashboarduser objectClass: posixGroup objectClass: top description: OpenNMS Read Only user cn: ONMS_RO
OPENNMS_HOME/etc/magic-users.properties
Original
roles=rtc, admin, rouser, dashboard
New
roles=rtc
Original
# This role allows users access to configuration and # administrative web pages. role.admin.name=OpenNMS Administrator role.admin.users=admin # This role disallows user write access role.rouser.name=OpenNMS Read-Only User role.rouser.users= # This role allows access to the dashboard only role.dashboard.name=OpenNMS Dashboard User role.dashboard.users= role.dashboard.notInDefaultGroup=true
New
# This role allows users access to configuration and # administrative web pages. #role.admin.name=OpenNMS Administrator #role.admin.users=admin # This role disallows user write access #role.rouser.name=OpenNMS Read-Only User #role.rouser.users= # This role allows access to the dashboard only #role.dashboard.name=OpenNMS Dashboard User #role.dashboard.users= #role.dashboard.notInDefaultGroup=true
Configuring Acegi to work with Novell eDirectory
This actually is working with group permissions. This heavily based on the work of the others who contributed to this page. Here is my sanitized configuration. Note that the names of the groups must be all caps when convertToUpperCase is set to true.
<bean id="initialDirContextFactory"
class="org.acegisecurity.ldap.DefaultInitialDirContextFactory">
<constructor-arg value="ldaps://ldap.mycompany.com:636/"/>
</bean>
<bean id="userSearch"
class="org.acegisecurity.ldap.search.FilterBasedLdapUserSearch">
<constructor-arg index="0">
<value></value>
</constructor-arg>
<constructor-arg index="1">
<value>(cn={0})</value>
</constructor-arg>
<constructor-arg index="2">
<ref local="initialDirContextFactory" />
</constructor-arg>
<property name="searchSubtree">
<value>true</value>
</property>
</bean>
<bean id="userAttributeLdapAuthoritiesPopulator"
class="org.opennms.web.acegisecurity.UserAttributeLdapAuthoritiesPopulator">
<constructor-arg><ref local="initialDirContextFactory"/></constructor-arg>
<constructor-arg><value>groupMembership</value></constructor-arg>
</bean>
<bean id="groupLdapAuthoritiesPopulator"
class="org.opennms.web.acegisecurity.UserGroupLdapAuthoritiesPopulator">
<constructor-arg><ref local="initialDirContextFactory"/></constructor-arg>
<constructor-arg>
<value>ou=MYSITE,o=MYCOMPANY</value>
</constructor-arg>
<property name="groupSearchFilter"><value>member={0}</value></property>
<property name="searchSubtree"><value>true</value></property>
<property name="convertToUpperCase"><value>true</value></property>
<property name="userGroups">
<list>
<value>ONMS_USER</value>
<value>ONMS_ADMIN</value>
</list>
</property>
<property name="adminGroups">
<list>
<value>ONMS_ADMIN</value>
</list>
</property>
</bean>
<bean id="ldapAuthenticationProvider"
class="org.acegisecurity.providers.ldap.LdapAuthenticationProvider">
<constructor-arg>
<bean class="org.acegisecurity.providers.ldap.authenticator.BindAuthenticator">
<constructor-arg><ref local="initialDirContextFactory"/></constructor-arg>
<property name="userSearch"><ref bean="userSearch"/></property>
</bean>
</constructor-arg>
<constructor-arg>
<ref bean="groupLdapAuthoritiesPopulator"/>
</constructor-arg>
</bean>
You can use http://acegisecurity.org/acegi-security/apidocs/org/acegisecurity/providers/ldap/populator/DefaultLdapAuthoritiesPopulator.html to find definitions for the properties.
Configuring Acegi to work with Active Directory (Windows 2k3)
Last modified: 2008-10-22
This has been configured to use Windows groups for assigning roles.
The Active Directory group names are in CAPS and match the exact group name as it is in Active Directory.
<bean id="initialDirContextFactory"
class="org.acegisecurity.ldap.DefaultInitialDirContextFactory">
<constructor-arg value="ldap://ldap.yourdomain.com:389/"/>
<property name="managerDn">
<value>CN=LDAP Manager,OU=Users,DC=metalsales,DC=us,DC=com</value>
</property>
<property name="managerPassword">
<value>managerPassword</value>
</property>
<property name="extraEnvVars">
<map>
<entry>
<key>
<value>java.naming.referral</value>
</key>
<value>follow</value>
</entry>
</map>
</property>
</bean>
<bean id="userSearch"
class="org.acegisecurity.ldap.search.FilterBasedLdapUserSearch">
<constructor-arg index="0">
<value>ou=Users,dc=yourcompany,DC=com</value>
</constructor-arg>
<constructor-arg index="1">
<value>(sAMAccountName={0})</value>
</constructor-arg>
<constructor-arg index="2">
<ref local="initialDirContextFactory" />
</constructor-arg>
<property name="searchSubtree">
<value>true</value>
</property>
</bean>
<bean id="groupLdapAuthoritiesPopulator"
class="org.opennms.web.acegisecurity.UserGroupLdapAuthoritiesPopulator">
<constructor-arg><ref local="initialDirContextFactory"/></constructor-arg>
<constructor-arg>
<value>ou=Groups,dc=corp,dc=airband,dc=com</value>
</constructor-arg>
<property name="groupSearchFilter"><value>member={0}</value></property>
<property name="groupRoleAttribute"><value>cn</value></property>
<property name="searchSubtree"><value>true</value></property>
<property name="convertToUpperCase"><value>true</value></property>
<property name="userGroups">
<list>
<value>NETWORKUSERs</value>
<value>ENGINEERING</value>
</list>
</property>
<property name="adminGroups">
<list>
<value>ENGINEERING</value>
</list>
</property>
</bean>
<bean id="ldapAuthenticationProvider"
class="org.acegisecurity.providers.ldap.LdapAuthenticationProvider">
<constructor-arg>
<bean class="org.acegisecurity.providers.ldap.authenticator.BindAuthenticator">
<constructor-arg><ref local="initialDirContextFactory"/></constructor-arg>
<property name="userSearch"><ref bean="userSearch"/></property>
</bean>
</constructor-arg>
<constructor-arg>
<ref bean="groupLdapAuthoritiesPopulator"/>
</constructor-arg>
</bean>
Configuring Acegi to work with LDAP – Microsoft services for Unix on Active Directory.
Environment
- Solaris 10/x86 with Opennms 1.5.94 in /opt/opennms1.5.94
- Active directory W2000 server deptcd01 (10.228.130.65) to serve domain dept.yourcomp.com with Microsoft services for unix 3.0 (msSFU30) installed.
- Access to the AD branch is gained with: cn=linuxbind,ou=InformaticsDept,dc=dept,dc=yourcomp,dc=com and password bind1234
- Domain users are assigned to group unixusers of msSFU30 and can login on the Solaris host with their domain user and password.
- Check whether you have a ldap client on Solaris.
# svcs ldap/client STATE STIME FMRI online 12:01:05 svc:/network/ldap/client:default
- If not, activate the client and its mapping with the command:
# ldapclient -v manual \ -a credentialLevel=proxy \ -a authenticationMethod=simple \ -a proxyDN=cn=linuxbind,ou=InformaticsDept,dc=dept,dc=yourcomp,dc=com \ -a proxyPassword=bind1234 \ -a defaultSearchBase=ou=InformaticsDept,dc=dept,dc=yourcomp,dc=com \ -a domainName=dept.yourcomp.com \ -a "defaultServerList=10.228.130.65" \ -a attributeMap=passwd:userPassword=msSFU30Password \ -a attributeMap=passwd:uid=sAMAccountName \ -a attributeMap=passwd:uidnumber=msSFU30UidNumber \ -a attributeMap=passwd:gidnumber=msSFU30GidNumber \ -a attributeMap=passwd:homedirectory=msSFU30HomeDirectory \ -a attributeMap=passwd:loginshell=msSFU30LoginShell \ -a attributeMap=passwd:gecos=cn \ -a attributeMap=shadow:userPassword=msSFU30Password \ -a attributeMap=shadow:shadowflag=shadowFlag \ -a attributeMap=shadow:uid=sAMAccountName \ -a attributeMap=group:userpassword=msSFU30Password \ -a attributeMap=group:memberuid=memberUid \ -a attributeMap=group:gidnumber=msSFU30gidNumber \ -a objectClassMap=passwd:posixAccount=User \ -a objectClassMap=shadow:shadowAccount=User \ -a objectClassMap=group:posixGroup=Group \ -a servicesearchDescriptor=passwd:ou=InformaticsDept,dc=dept,dc=yourcomp,dc=com?sub \ -a servicesearchDescriptor=shadow:ou=InformaticsDept,dc=dept,dc=yourcomp,dc=com?sub \ -a servicesearchDescriptor=group:ou=InformaticsDept,dc=dept,dc=yourcomp,dc=com?sub
- Check whether you have a good connection from solaris authenticating to the AD server:
# ldaplist passwd youruser dn: gecos=Real Username,OU=InformaticsDept,DC=dept,DC=yourcomp,DC=com
- Check whether user is in the unixusers group (msSFU30PosixMemberOf field) :
# ldaplist -l passwd youruser
dn: gecos=Real Username,OU=InformaticsDept,DC=dept,DC=yourcomp,DC=com
memberOf: CN=GroupInformtx,OU=InformaticsDept,DC=dept,DC=yourcomp,DC=com
msSFU30PosixMemberOf: CN=unixusers,OU=InformaticsDept,DC=dept,DC=yourcomp,DC=com
accountExpires: 0
badPasswordTime: 128716711574375000
badPwdCount: 0
...
- Check whether you can access the group unixusers and that user youruser is there:
# ldaplist -l group unixusers
dn: CN=unixusers,OU=InformaticsDept,DC=dept,DC=yourcomp,DC=com
msSFU30PosixMember: CN=Real Username2,OU=InformaticsDept,DC=dept,DC=yourcomp,DC=com
msSFU30PosixMember: CN=Real Username3,OU=InformaticsDept,DC=dept,DC=yourcomp,DC=com
msSFU30PosixMember: CN=Real Username4,OU=InformaticsDept,DC=dept,DC=yourcomp,DC=com
msSFU30PosixMember: CN=Real Username,OU=InformaticsDept,DC=dept,DC=yourcomp,DC=com
msSFU30PosixMember: CN=Real Username5,OU=InformaticsDept,DC=dept,DC=yourcomp,DC=com
..<snip>..
cn: unixusers
groupType: -2147483646
instanceType: 4
distinguishedName: CN=unixusers,OU=InformaticsDept,DC=dept,DC=yourcomp,DC=com
objectCategory: CN=Group,CN=Schema,CN=Configuration,DC=dept,DC=yourcomp,DC=com
objectClass: top
objectClass: posixGroup
objectGUID: "å;ôé²K¡ÅY«W¬Ç
objectSid:
name: unixusers
sAMAccountName: unixusers
sAMAccountType: 268435456
uSNChanged: 8430838
uSNCreated: 3176438
whenChanged: 20081120161358.0Z
whenCreated: 20070321121835.0Z
gidNumber: 10000
msSFUName: unixusers
syncNisDomain: DEPT
gidnumber: 10000
msSFU30Name: unixusers
msSFU30NisDomain: NISGlobal
Setting Opennms up for authentication to the Microsoft Services for Unix AD extension
- Edit the $OPENNMS_HOME/jetty-webapps/opennms/WEB-INF/applicationContext-acegi-security.xml file and change the following sections (leave dao authentication there for being able to access at least with your admin account in case of a failing LDAP):
<bean id="authenticationManager" class="org.acegisecurity.providers.ProviderManager">
<property name="providers">
<list>
<ref local="daoAuthenticationProvider"/>
<!--
Uncomment the following reference to ldapAuthenticationProvider
and comment-out the above reference to the
daoAuthenticationProvider if you want to use LDAP. You'll also
need to uncomment the LDAP AUTHENTICATION section below, as well.
-->
<ref local="ldapAuthenticationProvider"/>
<ref local="anonymousAuthenticationProvider"/>
</list>
</property>
</bean>
....
<bean id="initialDirContextFactory"
class="org.acegisecurity.ldap.DefaultInitialDirContextFactory">
<constructor-arg value="ldap://10.228.130.65:389/"/>
<property name="managerDn">
<value>CN=linuxbind,OU=InformaticsDept,DC=dept,DC=yourcomp,DC=com</value>
</property>
<property name="managerPassword">
<value>bind1234</value>
</property>
</bean>
....
<bean id="userSearch" class="org.acegisecurity.ldap.search.FilterBasedLdapUserSearch">
<constructor-arg index="0">
<value>OU=InformaticsDept,DC=dept,DC=yourcomp,DC=com</value>
</constructor-arg>
<constructor-arg index="1">
<value>(msSFU30Name={0})</value>
</constructor-arg>
<constructor-arg index="2">
<ref local="initialDirContextFactory" />
</constructor-arg>
<property name="searchSubtree">
<value>true</value>
</property>
</bean>
....
<bean id="groupLdapAuthoritiesPopulator"
class="org.opennms.web.acegisecurity.UserGroupLdapAuthoritiesPopulator">
<constructor-arg>
<ref local="initialDirContextFactory"/>
</constructor-arg>
<constructor-arg>
<value>CN=unixusers,OU=InformaticsDept,DC=dept,DC=yourcomp,DC=com</value>
</constructor-arg>
<property name="groupSearchFilter">
<!--
{0} full DN of user
{1} username only
-->
<value>msSFU30PosixMember={0}</value>
</property>
<property name="groupRoleAttribute"><value>cn</value></property>
<property name="searchSubtree"><value>true</value></property>
<property name="convertToUpperCase"><value>true</value></property>
<property name="userGroups">
<list>
<value>UNIXUSERS</value>
</list>
</property>
<property name="adminGroups">
<list>
<value>OPENNMS_ADMIN</value>
</list>
</property>
</bean>
....
<bean id="ldapAuthenticationProvider"
class="org.acegisecurity.providers.ldap.LdapAuthenticationProvider">
<constructor-arg>
<bean class="org.acegisecurity.providers.ldap.authenticator.BindAuthenticator">
<constructor-arg>
<ref local="initialDirContextFactory"/>
</constructor-arg>
<property name="userSearch">
<ref bean="userSearch"/>
</property>
</bean>
</constructor-arg>
<constructor-arg>
<ref bean="groupLdapAuthoritiesPopulator"/>
</constructor-arg>
</bean>
Now a user will be checked to LDAP/AD/msSFU30. If found and with the correct password, the user is checked in the unixusers group of the AD ( CN=unixusers,OU=InformaticsDept,DC=dept,DC=yourcomp,DC=com). If the full DN of the user matches one of the msSFU30PosixMember entries, the cn of the group is compared to the UNIXUSERS string. If correct access is granted to the user with 'userGroups' rights to Opennms.
The identifier {0} forces the checking of the whole dn of the user, given the output of ldaplist -l group unixusers, as documented in http://www.acegisecurity.org/acegi-security/apidocs/org/acegisecurity/providers/ldap/populator/DefaultLdapAuthoritiesPopulator.html
- Now restart opennms and watch the file $OPENNMS_HOME/logs/webapp/web.log. When you login to opennms with user 'youruser', you will see:
2008-11-20 17:10:25,794 DEBUG [btpool0-5] UserGroupLdapAuthoritiesPopulator: Getting authorities for user CN=Real Username,OU=InformaticsDept,DC=dept,DC=yourcomp,DC=com
2008-11-20 17:10:25,794 DEBUG [btpool0-5] UserGroupLdapAuthoritiesPopulator: Searching for roles for user 'youruser', DN = 'CN=Real Username,OU=InformaticsDept,DC=dept,DC=yourcomp,DC=com', with filter msSFU30PosixMember={0} in search base 'CN=unixusers,OU=InformaticsDept,DC=dept,DC=yourcomp,DC=com'
2008-11-20 17:10:25,796 DEBUG [btpool0-5] UserGroupLdapAuthoritiesPopulator: Roles from search: [unixusers]
2008-11-20 17:10:25,796 DEBUG [btpool0-5] UserGroupLdapAuthoritiesPopulator: Parsing Role: UNIXUSERS
2008-11-20 17:10:25,796 DEBUG [btpool0-5] UserGroupLdapAuthoritiesPopulator: Adding Role User : ROLE_USER
In case the user is not in the group 'unixusers' the messages will be:
2008-11-20 17:09:14,199 DEBUG [btpool0-5] UserGroupLdapAuthoritiesPopulator: Getting authorities for user CN=Real Username,OU=InformaticsDept,DC=dept,DC=yourcomp,DC=com
2008-11-20 17:09:14,199 DEBUG [btpool0-5] UserGroupLdapAuthoritiesPopulator: Searching for roles for user 'youruser', DN = 'CN=Real Username,OU=InformaticsDept,DC=dept,DC=yourcomp,DC=com', with filter msSFU30PosixMember={0} in search base 'CN=unixusers,OU=InformaticsDept,DC=dept,DC=yourcomp,DC=com'
2008-11-20 17:09:14,201 DEBUG [btpool0-5] UserGroupLdapAuthoritiesPopulator: Roles from search: []
- To assign users with administrative rights to Opennms using msSFU30, add a msSFU30 group in the Active Directory at wish. In this case the name of the group should be opennms_admin, accordingly the list value of the property name "adminGroups".
Configuring acegi to work with SunOne LDAP
This following is an example that works with SunOne LDAP server.
Importing SSL certs
Here is an easy way to import an SSL cert into java. The default keystore password is "changeit".
applicationContext-acegi-security.xml
<bean id="initialDirContextFactory"
class="org.acegisecurity.ldap.DefaultInitialDirContextFactory">
<constructor-arg value="ldaps://ldap.example.com:636/"/>
</bean>
<bean id="userSearch"
class="org.acegisecurity.ldap.search.FilterBasedLdapUserSearch">
<constructor-arg index="0">
<value>ou=people,dc=company,dc=com</value>
</constructor-arg>
<constructor-arg index="1">
<value>(uid={0})</value>
</constructor-arg>
<constructor-arg index="2">
<ref local="initialDirContextFactory" />
</constructor-arg>
<property name="searchSubtree">
<value>true</value>
</property>
</bean>
<bean id="userAttributeLdapAuthoritiesPopulator"
class="org.opennms.web.acegisecurity.UserAttributeLdapAuthoritiesPopulator">
<constructor-arg><ref local="initialDirContextFactory"/></constructor-arg>
<constructor-arg><value>groupMembership</value></constructor-arg>
</bean>
<bean id="groupLdapAuthoritiesPopulator"
class="org.opennms.web.acegisecurity.UserGroupLdapAuthoritiesPopulator">
<constructor-arg><ref local="initialDirContextFactory"/></constructor-arg>
<constructor-arg>
<value>ou=group,dc=company,dc=com</value>
</constructor-arg>
<property name="groupSearchFilter"><value>memberUid={1}</value></property>
<property name="groupRoleAttribute"><value>cn</value></property>
<property name="searchSubtree"><value>true</value></property>
<property name="convertToUpperCase"><value>true</value></property>
<property name="userGroups">
<list>
<value>OPENNMS</value>
<value>OPENNMS_ADMIN</value>
</list>
</property>
<property name="adminGroups">
<list>
<value>OPENNMS_ADMIN</value>
</list>
</property>
</bean>
<bean id="ldapAuthenticationProvider"
class="org.acegisecurity.providers.ldap.LdapAuthenticationProvider">
<constructor-arg>
<bean class="org.acegisecurity.providers.ldap.authenticator.BindAuthenticator">
<constructor-arg><ref local="initialDirContextFactory"/></constructor-arg>
<property name="userSearch"><ref bean="userSearch"/></property>
</bean>
</constructor-arg>
<constructor-arg>
<ref bean="groupLdapAuthoritiesPopulator"/>
</constructor-arg>
</bean>






