In this chapter we will learn how to implement a custom provider that integrates some of our legacy SSO systems.
Developing a provider consists of the following tasks:
LoginModule
that interacts with our SSO and returns Principals to WebLogicAs we shall see, the hardest work will be configuring Maven to have a reproducible and industry-ready project that can create the MBean JAR file for us.
There is no WebLogic MBeanMaker plugin in our application and also the existing tool is not really Maven friendly.
What we will do is an integration of the existing technologies into Maven using the maven-antrun-plugin
plugin as a bridge between these two worlds. These technologies are so invasive that we will have to disable some standard and common Maven plugins such as maven-jar-plugin
and maven-install-plugin
. In a sense, the work of the compile plugin will be done by this piece of ancient BEA technology.
But all of this makes sense if we want to have a piece of software that can be integrated into modern build systems, and if we want to have a good development environment where we can work in a write-deploy-run lifecycle.
We will start simple, with the shortest and simplest Maven POM we can have, as shown in the following code snippet:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>net.lucamasini.security</groupId> <artifactId>chapter-4-auth-provider</artifactId> <version>1.0-SNAPSHOT</version> </project>
Here, we didn't specify a parent POM and we left the standard JAR packaging as it is—even if this is not managed by Maven—because it will be done directly by WebLogic MBeanMaker. We are not going to specify, here, all the properties that are needed because there are too many and they will be introduced progressively as we proceed with this chapter.
Our projects need some dependencies from the WebLogic binaries to compile. The different binaries that need to be added are explained in the following steps:
<dependency> <groupId>com.bea.core</groupId> <artifactId>commons.security.api</artifactId> <version>1.1.0.0_6-2-0-0</version> <scope>system</scope> <systemPath> ${middleware.home}/modules/ com.bea.core.common.security.api_1.1.0.0_6-2-0-0.jar </systemPath> </dependency>
LoginModule
(WLSUser
and WLSGroup
), as shown in the following code snippet:<dependency> <groupId>com.bea.core</groupId> <artifactId>weblogic.security</artifactId> <version>1.1.0.0_6-2-0-0</version> <scope>system</scope> <systemPath> ${middleware.home}/modules/ com.bea.core.weblogic.security_1.1.0.0_6-2-0-0.jar </systemPath> </dependency>
<dependency> <groupId>oracle</groupId> <artifactId>weblogic</artifactId> <version>${weblogic.version}</version> <scope>system</scope> <systemPath> ${middleware.home}/wlserver/server/lib/weblogic.jar </systemPath> </dependency>
<middleware.home>/path/to/wls1211_dev</middleware.home>
<weblogic.version>12.1.1.0</weblogic.version>
Another easy step is to
reconfigure some common Maven plugins, such as compile and resources. Inside the <build>
tag, we add the
following code:
<plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> <version>2.0.2</version> <configuration> <encoding>UTF-8</encoding> <source>${maven.compiler.source}</source> <target>${maven.compiler.target}</target> </configuration> </plugin> <plugin> <artifactId>maven-resources-plugin</artifactId> <version>2.5</version> <configuration> <encoding>UTF-8</encoding> </configuration> <executions> <execution> <id>default-install</id> <phase>install</phase> <goals> <goal>copy-resources</goal> </goals> <configuration> <outputDirectory> ${domain.dir}/lib/mbeantypes </outputDirectory> <resources> <resource> <directory> ${project.build.directory} </directory> <includes> <include> ${project.build.finalName}.jar </include> </includes> </resource> </resources> </configuration> </execution> </executions> </plugin> </plugins>
Here, we configure UTF-8
as standard encoding so that it is platform independent (remember to configure your source editor) and we set source
/target
version for the javac
compiler. Of course, we need to define the following two new properties:
<maven.compiler.source>1.6</maven.compiler.source> <maven.compiler.target>1.6</maven.compiler.target>
As already
explained, the WebLogic MBeanMaker takes care of packaging the created artifact, so we need to disable the standard maven-jar-plugin
plugin. Moreover, the out-of-the-box install goal does not make any sense in this environment, so we configure an ad hoc execution of maven-resources-plugin
that moves the generated JAR file inside the correct folder, because of which we can also disable the standard maven-install-plugin
plugin, as shown in the following code snippet:
<plugin> <artifactId>maven-jar-plugin</artifactId> <version>2.4</version> <executions> <execution> <id>default-jar</id> <phase>none</phase> </execution> </executions> </plugin> <plugin> <artifactId>maven-install-plugin</artifactId> <version>2.3.1</version> <executions> <execution> <id>default-install</id> <phase>none</phase> </execution> </executions> </plugin>
We can see that in order to disable one of the standard Maven plugins, all we need to do is bind its internal execution ID to the none
execution phase.
The install
plugin is replaced by the default-install
execution of maven-resources-plugin
, where we tell it to move the generated JAR inside the WebLogic domain's lib/mbeantype
folder, which is the default folder for custom Providers. For that we need to define a new property, as shown in the following code snippet:
<domain.dir>/path/to/your/domain</domain.dir>
The path should point to the folder where we created the development domain.
Now another plugin, this time a custom plugin from Codehaus' Mojo project, one that does something that Maven should do on its own—add another source folder.
Quite often
you have more than one place where sources
are placed because one of them was written by a developer and the others are autogenerated. This kind of configuration is not possible using the standard set of Maven plugins. Luckily, this simple plugin that does a lot of other interesting things, allows us to add other folders as source
folders. This plugin is used in the following code snippet:
<plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>build-helper-maven-plugin</artifactId> <version>1.5</version> <executions> <execution> <id>add-source</id> <phase>generate-sources</phase> <goals> <goal>add-source</goal> </goals> <configuration> <sources> <source>${generated.sources.dir}</source> </sources> </configuration> </execution> </executions> </plugin>
The execution of the add-source
goal is bound to the generate-sources
phase so that everything is consistent within this POM configuration. What is ${generated.sources.dir}
? It is a new property, defined as follows:
<generated.sources.dir> ${project.build.directory}/generated-source </generated.sources.dir>
It is the destination where we will tell WebLogic MBeanMaker to write its custom generated classes.
The last simple task we will do inside this POM is to enable resource filtering. Again inside the <build>
tag,
we add the following code:
<resources> <resource> <filtering>true</filtering> <directory>src/main/resources</directory> </resource> </resources>
WebLogic's MBean is an old BEA-patented technology and is based on the following:
That said, it is a really old piece of software and it's tied to WebLogic and its custom development environment, based on Ant and proprietary APIs. Integration with Maven is possible in the following two ways:
Of course, the former is a better solution that can spare us a lot of time configuring the POM, but you need to have some time to write and maintain it over an extended period of time and over different WebLogic versions.
Using the latter solution instead is far less elegant because we need to write some old Ant XML configuration. But this way it's really easy to integrate it with Maven.
That said, we will explore the second solution and we will configure Ant to execute the generator for us. The first thing to do is to configure the plugin and its dependencies, as shown in the following code snippet:
<plugin> <artifactId>maven-antrun-plugin</artifactId> <version>1.3</version> <dependencies> <dependency> <groupId>weblogic</groupId> <artifactId>weblogic</artifactId> <version>${weblogic.version}</version> <scope>system</scope> <systemPath> ${middleware.home}/wlserver/server/lib/weblogic.jar </systemPath> </dependency> </dependencies> <executions> … …
Then, we will
code the two execution
tags. First, the MBean definition file is read and then the intermediary files are created, as shown in the following code snippet:
<execution> <id>generate-mbean</id> <phase>process-resources</phase> <goals> <goal>run</goal> </goals> <configuration> <tasks> <java fork="true" classname="weblogic.management.commo.WebLogicMBeanMaker" classpathref="maven.plugin.classpath"> <jvmarg value="-DMDF=${project.build.outputDirectory}/ PacktSiteUsersAuthentication.xml" /> <jvmarg value="-Dfiles=${project.build.outputDirectory}" /> <jvmarg value="-DcreateStubs=true" /> <jvmarg value="-Dverbose=true" /> </java> </tasks> </configuration> </execution>
Here, we launch the WebLogic MBeanMaker telling it to parse the XML file inside the output folder. This works because we linked this execution to the process-resources
and so we can be sure that maven-resources-plugin
has filtered our resources and placed them inside the configured output folder. We also tell it to place the intermediate files inside the output folder and to eventually overwrite existing stubs (not in our case because we are using a generated folder). Finally, we enable the verbose mode to display any important information in case we need them.
In the second execution
, the intermediary files and developer code is compiled and packaged as an MBean, as shown in the following code snippet:
<execution> <id>generate-jar</id> <phase>compile</phase> <goals> <goal>run</goal> </goals> <configuration> <tasks> <java fork="true" classname="weblogic.management.commo.WebLogicMBeanMaker" classpathref="maven.plugin.classpath"> <jvmarg value="-DMJF=${jar.file}" /> <jvmarg value="-Dfiles=${project.build.outputDirectory}" /> <jvmarg value="-DcreateStubs=true" /> <jvmarg value="-DpreserveStubs=true" /> <jvmarg value="-Dverbose=true" /> <arg value="-preserveStubs" /> </java> <move todir="${generated.sources.dir}/${package.dir}" file="${project.build.outputDirectory}/ PacktSiteUsersAuthenticationImpl.java" /> <move todir="${generated.sources.dir}"> <fileset dir="${project.build.outputDirectory}"> <include name="**/*.java" /> </fileset> </move> </tasks> </configuration> </execution>
Of course, here we need to define two new Maven properties; the first points to the JAR file that we want to be generated, as shown in the following code snippet:
<jar.file> ${project.build.directory}/${project.build.finalName}.jar </jar.file>
The second is used to preserve package structure during the move task, as shown in the following code:
<package.dir> net/lucamasini/security </package.dir>
Maven configuration is now completed. This POM can be used as a template for every Authentication Provider we need to do, and we can proceed to the core implementation of the MBean, starting with its XML definition.
The central
point of our development iteration is the MDF file, where we define the MBean that is a wrapper of LoginModule
, which we will implement. This is a custom XML and you need the commo.dtd
file that can be found inside the WebLogic installation (under the $MW_HOME/wl_server/server/lib
folder) and copy it inside the src/main/resources
folder of your project, because from now on it will be referenced using the SYSTEM
document type, as shown in the following code snippet:
<?xml version="1.0" ?> <!DOCTYPE MBeanType SYSTEM "commo.dtd">
Of course, the main tag is named MBeanType
and defines how the MBean class will be generated; the MBeanType
is defined as follows:
<MBeanType Name = "PacktSiteUsersAuthentication" DisplayName = "PacktSiteUsersAuthentication" Package = "net.lucamasini.security" Extends = "weblogic.management.security.authentication.Authenticator" PersistPolicy = "OnUpdate" >
Every attribute of this tag is very important, starting with the name that will be used to define a new XML schema type for the security namespace inside the WebLogic's config.xml
file, as defined in the following code snippet:
<sec:authentication-provider xmlns:ext="http://xmlns.oracle.com/weblogic/security/extension" xsi:type="ext:packt-site-users-authenticationType"> <sec:name>packt</sec:name> <sec:control-flag>OPTIONAL</sec:control-flag> </sec:authentication-provider>
Here, we can see that xsi:type
is the MBean name lowered, with a -
between lowercase letters and with Type
as a suffix.
The DisplayName
attribute is instead used inside the WebLogic console and that is what we see in the drop-down menu when we add a new Authentication Provider.
Package
is the Java package of the generated artifacts and Extends
is the standard MBean we decided to extend—weblogic.management.security.authentication.Authenticator
—when we develop an Authentication Provider.
The last attribute—PersistPolicy = "OnUpdate"
—tells to generate an MBean that will write itself in the storage file (usually the config.xml
) every time one of its attributes is modified.
Finally, we can run an mvn
install to generate our Authentication Provider, as shown in the following command:
mvn install
This provider is not yet a working provider, but we can catch a glimpse of what we are going to build.
It can be installed under the WebLogic console, but then WebLogic will refuse to start with a [Security:097533]SecurityProvider service class name for PackProvider is not specified error message because we haven't yet configured the MDF, and nor have we written the implementation of our MBean. But apart from this, WebLogic knows what we have just generated and tries to use it (if you are panicking about your WebLogic domain, just delete the sec:
section inside the config.xml
file).
Peering inside the target folder, we can notice a special class under the weblogic.management.security
package; in our sample it will be named CHAPTER4_AUTH_PROVIDER_1_0_SNAPSHOT1345106773809855000BeanInfoFactory
. It is named with the name of the MBean JAR file along with the current time in milliseconds, which is different every time we do the install (for that always remember to do a clean before install). This class is a factory that, given the interface, references the standard JavaBean's BeanInfo
class of our MBean implementation.
We can proceed with writing our MDF and adding the configuration for ProviderClassName
as follows:
<MBeanAttribute Name = "ProviderClassName" Type = "java.lang.String" Writeable = "false" Default = ""net.lucamasini.security.PacktAuthProviderImpl"" />
Please note that we need to use the "
XML entity because the ProviderClassName
attribute needs a value between quotes, but these are now used by the XML file itself.
If not, when we relaunch the install and start WebLogic, we will get a completely different error message—[Security:097534]Failed to obtain an instance of class net.lucamasini.security.PacktAuthProviderImpl. This is because we've now specified which class will be our MBean implementation and WebLogic tries to create a new instance of a class that doesn't exist yet.
The next two attributes that we are going to add are those that the WebLogic administrator will see when he/she will configures the provider; these attributes are as follows:
<MBeanAttribute Name = "Description" Type = "java.lang.String" Writeable = "false" Default = ""WebLogic Packt Authentication Provider"" /> <MBeanAttribute Name = "Version" Type = "java.lang.String" Writeable = "false" Default = ""1.0"" />
In description
and version
, we can add whatever we prefer; these are two informative strings and we can check this in the MBeanImplBeanInfo
class by looking for these two properties.
Finally, we add a real configuration property, the URL that will be used by our provider to interact with our legacy SSO system, as shown in the following code:
<MBeanAttribute Name = "URL" Type = "java.lang.String" Writeable = "true" Default = ""${authentication.services.url}"" />
In a
different manner from the other attributes, this can also be written using the console (or directly into the config.xml
file) by our system administrator. We are not hardcoding the URL inside the MDF file, but using Maven's resource filtering so that we can configure this parameter inside our POM—maybe with different values between our environments (development, stage, and production).
Of course, our POM must be enriched with the following new property:
<authentication.services.url> http://localhost:8080/sso.jsp </authentication.services.url>
Our MDF file configuration is completed. We can now code the MBean's delegate implementation class.