« July 2007 | Main | October 2007 »

August 2007 Archives

August 12, 2007

Using OpenLDAP with WebLogic Server

This was originally posted on my dev2dev blog August 12th, 2007.

A customer I was working with this week had some difficulty using Active Directory in conjunction with WebLogic Server Security.  I've always used the Embedded LDAP server that ships with WLS as my user and group store since most of my work is just demos and prototypes, but I thought this would be an excellent opportunity to see what it is like to configure an external LDAP provider.  Since I don't have easy access to Active Directory, I decided to use OpenLDAP.  I am an LDAP newbie and was surprised at the lack of results that I received searching on google for my LDAP 101, but I was able to piece enough information together from wikipedia and other articles to get me going.

Install and configure OpenLDAP

OpenLDAP is typically used on *nix systems, but my laptop runs Windows XP.  I was able to find someone that makes a Windows Distribution and I retrieved version 2.2.29.  It's very straight forward to download and install it as a Windows Service.  Similar to Apache's httpd.conf file, the sldapd.conf in the base directory is the master configuration file.

I had to change two things about my file.  At the top, I added support for additional schemas based on advice on an email thread.  It seems like it is very common to use the inetOrgPerson object class based off of my limited shoulder surfing at customer sites, so I added support for that and one other one named cosine.

#
# See slapd.conf(5) for details on configuration options.
# This file should NOT be world readable.
#
ucdata-path    ./ucdata
include        ./schema/core.schema
include        ./schema/cosine.schema
include        ./schema/inetorgperson.schema

At the very bottom of the sldapd.conf file, you should see a few other things to configure.

database    bdb
#suffix        "dc=my-domain,dc=com"
suffix        "dc=bea,dc=com"
rootdn        "cn=Manager,dc=bea,dc=com"
# Cleartext passwords, especially for the rootdn, should
# be avoid.  See slappasswd(8) and slapd.conf(5) for details.
# Use of strong authentication encouraged.
rootpw        secret
# The database directory MUST exist prior to running slapd AND 
# should only be accessible by the slapd and slap tools.
# Mode 700 recommended.
directory    ./data
# Indices to maintain
index    objectClass    eq
So what this section says is that OpenLDAP will use BDB as it's database and store the information in the data directory.  These are defaults.  This was helpful to know as I messed up my database a few times playing around with various settings and could always just stop OpenLDAP, delete the data directory and start OpenLDAP to start from a clean slate.  The default suffix for entries typically follows the domain name conventions, so in this case I used bea.com where the DC stands for Domain Component.  This site has additional definitions for the other LDAP abbreviations like CN (Common Name) and SN (Sir Name).  The default for the root user name is Manager and the password defaults to secret.  Of course in a real production setting you would want to encrypt this password, but for my demo purposes, this works fine.  Now I started the Windows Service, executed a netstat command and observed that I was now listening on the LDAP port of 389 and was up and running.

Add Users and Groups

When you first start OpenLDAP, there is no user/group structure provided, you have to add those entries yourself.  LDAP uses the LDIF format to import and export entries into LDAP.    I was able to find some examples and modify them to have a user/group structure that worked for my example purposes.  Like I said, I'm an LDAP newbie, so do not consider this a recommended structure for your enterprise, but it worked for me to store both users and groups in a very basic way.  I'll show you a tool that you can use later to do this visually, but it's helpful to know what's going on under the covers so you understand what the tool is doing.

dn: dc=bea,dc=com
dc: bea
objectClass: top
objectClass: domain

dn: ou=people,dc=bea,dc=com
ou: people
objectClass: top
objectClass: organizationalUnit

dn:cn=jbayer,ou=people,dc=bea,dc=com
objectClass:inetOrgPerson
cn:jbayer
sn:Bayer
uid:jbayer
userPassword:weblogic

dn: ou=groups,dc=bea,dc=com
ou: groups
objectClass: top
objectClass: organizationalUnit

dn: cn=groupA,ou=groups,dc=bea,dc=com
objectClass: top
objectClass: groupOfNames
cn: groupA
member: cn=jbayer,ou=people,dc=bea,dc=com


Above are the contents of a file I called base.ldif.  Here I define two organization units, people and groups.  Under people I have a user single user named jbayer and under groups I have one group named groupA, of which jbayer is a member.  To import this into OpenLDAP, I just use the ldapadd command as shown below.
 
C:\Program Files\OpenLDAP>ldapadd.exe -f base.ldif -xv -D "cn=Manager,dc=bea,dc=com" -w secret
ldap_initialize( <DEFAULT> )
add dc:
        bea
add objectClass:
        top
        domain
adding new entry "dc=bea,dc=com"
modify complete

add ou:
people
add objectClass:
top
organizationalUnit
adding new entry "ou=people,dc=bea,dc=com"
modify complete

add objectClass:
inetOrgPerson
add cn:
jbayer
add sn:
Bayer
add uid:
jbayer
add userPassword:
weblogic
adding new entry "cn=jbayer,ou=people,dc=bea,dc=com"
modify complete

add ou:
groups
add objectClass:
top
organizationalUnit
adding new entry "ou=groups,dc=bea,dc=com"
modify complete

add objectClass:
top
groupOfNames
add cn:
groupA
add member:
cn=jbayer,ou=people,dc=bea,dc=com
adding new entry "cn=groupA,ou=groups,dc=bea,dc=com"


Using an LDAP Browser

Since doing all this command line stuff isn't very visual, you can also use this Java LDAP Browser to view/modify OpenLDAP entries and to validate that your entries got imported correctly.  Dowload the tool and use connection settings similar to mine shown below (click the thumbnail to get a larger view), which should result in you being able to connect and browse the tree.  You can also use this tool to add and edit entries, but I won't cover that here.  Edocs also has instructions on how you can use this same tool to browse the Embedded LDAP server that comes with WLS.
 
openLdapConfig ldapBrowser [3]

Configure WebLogic Server

By default, WebLogic Server uses an security realm called myrealm that uses the Embedded LDAP server configured with the Default Authenticator.  In order to add OpenLDAP as a source, you have to configure an additional Authentication Provider to the realm.  Here are the steps for configuring WLS 10, although the steps are similar with other WLS versions.

  1. Login to the WLS console - my example servier is at http://localhost:7001/console  with user weblogic and password weblogic
  2. Browse to Security Realms->myrealm
  3. Click on the Providers tab
  4. Browse to the Authentication section
  5. Click the Lock and Edit button
  6. Click the new button and select OpenLDAPAuthenticator and give it a name, I chose openLDAPAuthenticator
  7. Click on the newly created Authenticator and select the Provider Specific tab
  8. I changed the following settings from the provider specific defaults based on the values I loaded in the ldif file shown earlier:

Group Base DN:  ou=groups,dc=bea,dc=com
Static Group Object Class:  groupOfNames
User Base DN:  ou=people,dc=bea,dc=com
User Object Class:  inetOrgPerson
Principal:  cn=Manager,dc=bea,dc=com
Host:  localhost
Credential:  secret
Confirm Credential:  secret
Static Group DNs from Member DN Filter:  (&(member=%M)(objectclass=groupOfNames))
User From Name Filter:  (&(cn=%u)(objectclass=inetOrgPerson))
Group From Name Filter:  (&(cn=%g)(objectclass=groupOfNames))

One Major Gotcha - Setting the Default Authenticator to something other than "Required"

Now you can save and Active the session.  WebLogic Server needs to be restarted for changes in the Authenticator to take effect, but before you restart there is one other change we have to make.  Authenticators have an attribute named Control Flag.  The value is either REQUIRED, REQUISITE, SUFFICIENT, or OPTIONAL.  See the help in the console for detailed explanation of these values.  The Default Authenticator has a default value of REQUIRED that should be changed to either SUFFICIENT or OPTIONAL in order for users that are only OpenLDAP to be able to login to with the WebLogic Security Framework without also having to be in Embedded LDAP.

After restarting, log back into the console, browse to your realm and select the Users and Groups tab.  You should see the user you added.  If you select a user from OpenLDAP you should be able to change their password as well as see their group membership if everything is configured properly.
 
jbayer 

Secure a web application

You can try it out by creating a simple web app with a security-constraint element in web.xml entry as follows that defines a role named SecuredUser and uses it to protect all resources in the web app:
<security-constraint>
 <web-resource-collection>
  <web-resource-name>restricted</web-resource-name>
  <url-pattern>/*</url-pattern>
 </web-resource-collection>
 <auth-constraint>
  <role-name>SecuredUser</role-name>
 </auth-constraint>
</security-constraint>
<login-config>
 <auth-method>BASIC</auth-method>
 <realm-name>SecuredRealm</realm-name>
</login-config>
<security-role>
 <role-name>SecuredUser</role-name>
</security-role>
Then in your weblogic.xml define element to add an security-role-assignment element to a specific user.
<wls:security-role-assignment>
     <wls:role-name>SecuredUser</wls:role-name>
     <wls:principal-name>jbayer</wls:principal-name>
</wls:security-role-assignment>
If you would rather use LDAP groups to specify the role assignment, which is much more likely to be the case, then you would use a section like this in your weblogic.xml.
<wls:security-role-assignment>
     <wls:role-name>SecuredUser</wls:role-name>
     <wls:externally-defined/>
</wls:security-role-assignment>

In this case, instead of explicitly naming all of the users you want in that role in each web applications deployment descriptors which is not a very good practice for an enterprise, the role SecuredUser will be assumed to be a Global Role defined in your realm's Roles and Policies -> Global Roles.  In the console, you can assign the Global Role SecuredUser to all users with membership in groupA for example.

What if you want to find out additional LDAP attributes other than users and groups and use them in your applications?  In a subsequent post I plan on showing how to use an LDAP control to do that or use the Unified User Profile feature of WebLogic Portal to automatically stuff those values in the user profile.

August 30, 2007

Changing WebLogic Annotations on the Fly (more detail)

This was originally posted on my dev2dev blog August 30th, 2007.

One of my customers asked me about a common use case:  they would like to change the value of a Java annotation in their deployment artifacts at deployment time without changing the code.  This way they can avoid having separate EAR files for each environment.  Using the same EAR file is very desirable because you know what you tested in QA is the same code that you are putting into production.  I'd consider that a best practice.

In this case, assume we are using Beehive Service Control to invoke a web service.  The URL of that service changes depending one each environment (dev, qa, prod).  Since Beehive is heavily annotations based  and the URL endpoint of the web service is actually put into code, how can we change that URL for the various environments without building a separate EAR file for each environment?  The solution is a combination of deployment plans (JSR-88) and a Beehive annotation override.  It turns out that Chris Hogue has already blogged this and his steps apply to 9.x and 10.x versions of WLS.  His entry is very straight-forward, but I found that the WebLogic console interface was not as intuitive as Chris's post..  So I wrote up the steps I followed to get it working.  These steps will create a plan.xml and the Beehive annotation-overrides.xml files for you,.  Once you understand the mechanisms, you could script this and apply it to other WEB-INF or META-INF files and values that might change between environments and say goodbye to multiple EAR files.

Here's Chris description of the steps to follow after deploying your EAR to WLS:

Now say I've deployed it to the production server. After deploying it the first time I can go into the WLS console, locate the control, and change the value through a simple form. To find it in the console, navigate to Deployments > [your application] > Deployment Plan > Resource Dependencies. Then locate your control in the "Controls" tree to find the available settings. It should look something like this:

I suggest you read Chris's full entry, but I'm going to get a little more granular with the instructions.

  1. First of all you need to be in a Change Session for this to take effect.
  2. Then you have to click on the “(No value specified)” label, and it will magically turn into a text box.  That was non-intuitive hang-up number 1 for me.
  3. Enter the value, BUT DO NOT LEAVE THE TEXT BOX.  In order for your value to take effect you have to hit the <ENTER> key to submit to the server while the cursor is still in the text box.  If you put that mouse anywhere else, let's say by trying to click the “Save” button, your changes will be lost, and the screen will refresh saying that the save was successful, but the Override Value will still say "(No value specified)".  This was my non-intuitive hang-up number 2.
  4. After hitting the enter key and the page loads and shows the value you typed in, then click the “Save” button.  I must admit user error on this one, I forgot to save once.  Don't miss this step.
  5. Now can activate your changes and update the application for it to take effect.

For curiosity you can browse to the directory where your deployment plan is stored (you select the directory yourself).  Your plan.xml will refer to an annotations-overrides.xml file, which you will find in the web application directory as META-INF/annotations-overrides.xml.  Here's an example of mine.  Notice the override-value where I switch to port 7001 instead of 7011.  I hope this more detailed explanation of Chris's post helps avoid the hang-ups that I encountered.

<?xml version='1.0' encoding='UTF-8'?>
<annotation-overrides xmlns="http://www.bea.com/2004/03/wlw/external-config/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <annotated-class>
    <annotated-class-name>test.HelloWebServiceControl</annotated-class-name>
    <component-type>Control Extension (JCX)</component-type>
    <annotation>
      <annotation-class-name>com.bea.control.ServiceControl$Location</annotation-class-name>
      <array-member>
        <member-name>urls</member-name>
        <member-value>http://localhost:7011/WebServiceWEB/HelloWebService</member-value>
        <override-value>http://localhost:7001/WebServiceWEB/HelloWebService</override-value>
        <cleartext-override-value>http://localhost:7001/WebServiceWEB/HelloWebService</cleartext-override-value>
      </array-member>
    </annotation>
  </annotated-class>
</annotation-overrides>

About August 2007

This page contains all entries posted to James Bayer's Blog in August 2007. They are listed from oldest to newest.

July 2007 is the previous archive.

October 2007 is the next archive.

Many more can be found on the main index page or by looking through the archives.

Powered by
Movable Type and Oracle