Saturday, May 31, 2014

NoSuchMethodError SpnegoAuthenticationProcessingFilter AbstractAuthenticationToken

In a nutshell, the solution to this problem involved cloning the git project, building the sources and deploying the newly created artifact (jar) to my local maven repository; see below for me details.

As part of an attempt to kerberize an web application (see here for details), which uses Spring Security's LDAP, I added in the necessary dependencies shown here:
<dependency>
    <groupId>org.springframework.security.extensions</groupId>
    <artifactId>spring-security-kerberos-core</artifactId>
    <version>1.0.0.M1</version>
</dependency>
and which I found here:
https://spring.io/blog/2009/09/28/spring-security-kerberos-spnego-extension

Next I configured the Spring Security XML configuration file as follows (description can be found at the link above too)

<beans ... >

<sec:http entry-point-ref="spnegoEntryPoint">
    <sec:intercept-url pattern="/**" access="IS_AUTHENTICATED_FULLY" />
    <sec:custom-filter ref="spnegoAuthenticationProcessingFilter" position="BASIC_AUTH_FILTER" />
</sec:http>
    
    
<bean id="spnegoEntryPoint" class="org.springframework.security.extensions.kerberos.web.SpnegoEntryPoint" />

<bean id="spnegoAuthenticationProcessingFilter" class="org.springframework.security.extensions.kerberos.web.SpnegoAuthenticationProcessingFilter">
    <property name="authenticationManager" ref="authenticationManager" />
</bean>

<sec:authentication-manager alias="authenticationManager">
    <sec:authentication-provider ref="kerberosServiceAuthenticationProvider" />
</sec:authentication-manager>

<bean id="kerberosServiceAuthenticationProvider" class="org.springframework.security.extensions.kerberos.KerberosServiceAuthenticationProvider">
    <property name="ticketValidator">
        <bean class="org.springframework.security.extensions.kerberos.SunJaasKerberosTicketValidator">
            <property name="servicePrincipal" value="HTTP/tomcat@EXAMPLE.COM" />
            <property name="keyTabLocation" value="classpath:krb5.keytab" />
            <property name="debug" value="true"/>
        </bean>
    </property>
    <property name="userDetailsService" ref="dummyUserDetailsService" />
</bean>

<bean id="dummyUserDetailsService" class="org.wmmnpr.mykrb.config.Krb5DumyUserService"/>



</beans>

And started the Maven based project using the maven-tomcat-plugin as follows:
>mvn tomcat7:run

When I tried to access a protected URL, I got the following error:

org.apache.catalina.core.StandardWrapperValve invoke
Schwerwiegend: Servlet.service() for servlet [jsp] in context with path [/mykrb5webapp] threw exception [Filter execution threw an exception] with root cause
java.lang.NoSuchMethodError: org.springframework.security.authentication.AbstractAuthenticationToken.<init>(Ljava/util/List;)V
at org.springframework.security.extensions.kerberos.KerberosServiceRequestToken.<init>

Obviously, the SpnegoAuthenticationProcessingFilter was incompatible with the spring-security-core library. I had no option but to clone the spring-security-security repository and see if I could fix the problem.

Using the eclipse git plugin, I cloned the remote repository (https://github.com/spring-projects/spring-security-kerberos.git) and then ran the gradle buiild. To import the code into eclipse I added "apply plugin: 'eclipse'" to the sources build.gradle file and run the "eclipse" target. This generated the proper eclipse project files, which allowed me to import the project into eclipse (see below)



To build and deploy the library, I ran the gradle.gui by calling:

gradlew.bat --gui




This made it very easy to install the jar into my local maven repository. All I had to do was click on the install target shown above.

Next, I updated the eclipse project of my web application to reflect the new version and ran the project again in the debugger. 

The problem was that the source code was the changed but the executable code seemed to be different. When I tried to step through my changed code, the debugger didn't seem to know about the changes. 

I spent lots of time trying to clean and rebuild the gradle project, I even deleted the artifacts in my maven repository, but it didn't help either. 

Then started I started the jvisualvm using the same user, tomcat, under which the web application as running (see here)



I could not see which jar file the class was being loaded from. To do that, I needed to start the server using the -verbose:class JVM option, which I passed to maven by setting in the environment the MAVEN_OPTS variable.

This showed the source jar of the class very nicely.


[Loaded org.springframework.security.extensions.kerberos.web.SpnegoAuthenticationProcessingFilter from file:/C:/Users/wnpr/.m2/repository/org/springframework/security/extensions/spring-security-kerberos-core/1.0.0.CI-SNAPSHOT/spring-security-kerberos-core-1.0.0.CI-SNAPSHOT.jar]

Despite all my efforts, I was still getting the NoSuchMethodError. Eventually, I had the idea to look into the publish-maven.gradle file. There I found references to some external resources.

scm {
     url = 'https://github.com/SpringSource/spring-security-kerberos'
     connection = 'scm:git:git://github.com/SpringSource/spring-security-kerberos'
     developerConnection = 'scm:git:git://github.com/SpringSource/spring-security-kerberos'

}

which I changed to something meaningless:

scm {
     url = 'https://githubxxx.com/SpringSource/spring-security-kerberos'
    connection = 'scm:git:git://githubxxx.com/SpringSource/spring-security-kerberos'
     developerConnection = 'scm:git:git://githubxxx.com/SpringSource/spring-security-kerberos'



I ran the gradle clean, the maven install and started the web application. This time java code shown in the debugger agreed what was being executed and the NoSuchMethodError was gone. Obviously, the jars which I had picked up from the recommended setting (shown below) were wrong; however, now it didn't matter as I was building my own using gradle and deploying to my local maven repository.


<repositories>
    <repository>
        <id>spring-snapshots</id>
        <name>Spring Snapshots</name>
        <url>http://repo.spring.io/snapshot</url>
        <snapshots>
            <enabled>true</enabled>
        </snapshots>
    </repository>

   <repository>
<id>spring-milestone</id>
<name>Spring Portfolio Milestone Repository</name>
<url>http://maven.springframework.org/milestone</url>
   </repository>
</repositories>


All that was left to do, was to comment out the above in my pom.xml file and change my dependency declaration of the kerberos artifact.

<dependency>
   <groupId>org.springframework.security.kerberos</groupId>
   <artifactId>spring-security-kerberos-core</artifactId>
   <version>1.0.0.CI-SNAPSHOT</version>
</dependency>



Windows 7 home directory Administrator runas

While trying to start various applications on Windows 7 using a local account, I kept getting:

"Could not create directory" ...

In the case below, I tried to start jvisualvm as a user called tomcat, whom I created as a local user with admin rights; however,  instead of using tomcat's own home directory, the application kept trying to use the one from the Administrator user.




The application was being started with runas:

>runas /user:tomcat /profile jvisualvm

Fixing this required opening a cmd window as tomcat

>runas /user:tomcat cmd
Geben Sie das Kennwort für "tomcat" ein:

Es wird versucht, cmd als Benutzer "GFT6841\tomcat" zu starten...

then starting regedit and changing all references to "Administrator" to appropriate user, in my case tomcat, in the the following keys:

HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders\
Desktop
LocalAppData
AppData

There might be more but changing the above was sufficient for my purposes.


jvisualvm runas windows

On Windows 7, it may be necessary to profile a java application which is running under a user other than the one you're presently logged on as. 

To be able to see the process, it will also be necessary to run jvisualvm as that user. This can be done as follows:

>runas /user:tomcat jvisualm 

Where tomcat is the user name under which the program was running, namely Apache Tomcat, that I wanted to profile.

Make sure that the jvisualvm is in the path. 

If you encounter problems with the runas user not being able to access or create certain folders, like this:

"Could not create directory" ...

then see here.

Windows Maven runas

Rather than passing user.home to the JVM as shown below, a better fix, which requires a registry change, can be found here.

I created a new Windows' account called tomcat and wanted to start my web application using Maven's maven-tomcat-plugin for debugging purposes . 

From my "normal" Windows' account, I started a new command prompt window for the:
tomcat user as follows:

C:\Users\wnpr>runas /user:tomcat cmd
Geben Sie das Kennwort für "tomcat" ein:
Es wird versucht, cmd als Benutzer "GFT6841\tomcat" zu starten...

C:\Users\wnpr>

In the newly opened window, I tried to clean the project using Maven.

>mvn clean
Could not create local repository at C:\Users\Administrator\.m2\repository -> [Help 1]



My first attempt to solve this issue was taken from stackoverflow and involved creating a .m2 folder in the tomcat user's home directory and to copying a settings.xml file into the folder and, in the settings.xml file, setting the "localRepository" XML element to point to the actual repository's location, which existed in my normal user's directory; namely: "C:\Users\wnpr\.m2\repository"; however, this still didn't help.  

Then I tried to hard code it in the MAVEN_HOME\bin\m2.conf file, see below, but this didn't change anything either.

set maven.home default ${user.home}/.m2

Next, I tried to pass it to Maven as a run time parameter
>mvn  clean -Duser.home=c:\Users\wnpr 

This helped for clean and compiling but not exactly for running the application.

:Error creating bean with name org.springframework.security.extensions.kerberos.SunJaasKerberosTicketValidator defined in class path resource [applicationContext-security.xml]: Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: Cannot locate default realm

Which is a Kerberos error and not a tomcat one. For the solution see here.

  
  

Thursday, May 29, 2014

spring kerberos spnego Cannot locate default realm

While trying to start my kerberized application using the maven-tomcat-plugin as a different user under Windows 7, I got the following error : Cannot locate default realm.

Looking at the krb5.conf file, I realized that various things in the file were wrong so I changed them and the file look as follows:

[libdefaults]
default_realm = EXAMPLE.COM
# default_tgs_enctypes = des-cbc-md5,des3-cbc-sha1-kd
# defaualt_tkt_enctypes = des-cbc-md5,des3-cbc-sha1-kd
kdc_timeout = 5000
dns_lookup_realm = false
dns_lookup_kdc = false
allow_weak_crypto = yes
forwardable = true

[realms]
EXAMPLE.COM = {
kdc = 127.0.0.1:60088
}

[login]
krb4_convert = true
krb4_get_tickets = false


This didn't do much so I looked in the ApacheDS log file for more clues and noticed that the service principal's name (servicePrincipal) of the KerberosServiceAuthenticationProvider bean was incorrect. I corrected it and the server started properly. Below is the complete Spring Security configuration file, less the namespace related attributes, and below that the corresponding LDAP structure.

<beans ...>
    
<sec:http entry-point-ref="spnegoEntryPoint">
    <sec:intercept-url pattern="/**" access="IS_AUTHENTICATED_FULLY" />
    <sec:custom-filter ref="spnegoAuthenticationProcessingFilter" position="BASIC_AUTH_FILTER" />
</sec:http>
    
    
<bean id="spnegoEntryPoint" class="org.springframework.security.extensions.kerberos.web.SpnegoEntryPoint" />

<bean id="spnegoAuthenticationProcessingFilter" class="org.springframework.security.extensions.kerberos.web.SpnegoAuthenticationProcessingFilter">
    <property name="authenticationManager" ref="authenticationManager" />
</bean>

<sec:authentication-manager alias="authenticationManager">
    <sec:authentication-provider ref="kerberosServiceAuthenticationProvider" />
</sec:authentication-manager>

<bean id="kerberosServiceAuthenticationProvider" class="org.springframework.security.extensions.kerberos.KerberosServiceAuthenticationProvider">
    <property name="ticketValidator">
        <bean class="org.springframework.security.extensions.kerberos.SunJaasKerberosTicketValidator">
            <property name="servicePrincipal" value="HTTP/tomcat@EXAMPLE.COM" />
            <property name="keyTabLocation" value="classpath:krb5.keytab" />
            <property name="debug" value="true"/>
        </bean>
    </property>
    <property name="userDetailsService" ref="dummyUserDetailsService" />
</bean>

<!-- Just returns the User authenticated by Kerberos and gives him the ROLE_USER -->
<!--  
<bean id="dummyUserDetailsService1" class="org.springframework.security.extensions.kerberos.sample.DummyUserDetailsService"/>
-->

<bean id="dummyUserDetailsService" class="org.wmmnpr.mykrb.config.Krb5DumyUserService"/>



</beans>




The server produced the following output during its start.




keytab add principal access denied

While trying to add a new principal, I got an access denied error. The error is mostly likely related to the fact that I'm  trying to generate a keytab with a session, which was created using "runas". That is the cmd console is running as a user, which is different from the one I logged on as.

>ktab.exe" -a tomcat@EXAMPLE.COM secret
Failed to save new entry.
java.io.FileNotFoundException: C:\Users\Administrator\krb5.keytab (Zugriff verweigert)at java.io.FileOutputStream.open(Native Method)

Or "Access denied"

Because I was suspicious that the problem was somehow related to the tomcat user's environment, I decided to explicitly specified the keytab file with the "-k" parameter,

>ktab.exe" -a tomcat@EXAMPLE.COM secret -k c:\Users\tomcat\krb5.keytab
Done!
Service key for tomcat@EXAMPLE.COM is saved in c:\Users\tomcat\krb5.keytab

This wasn't that obvious because the program's help of the documentation are vague about the usage of the flag.

Wednesday, May 28, 2014

Spring Security XML Configuration maven project


As a starting point for my application, I used the maven-archetype-webapp project template. I created an instance of the archetype with help from eclipse's (kepler's) maven plugin (if you have troubles, then go here). Shown here is a walk-through of the wizard's steps:








With the web application in place, I then changed the default web.xml to the following:

<web-app>
<display-name>Archetype Created Web Application</display-name>

<!-- Configure ContextLoaderListener to use AnnotationConfigWebApplicationContext 
instead of the default XmlWebApplicationContext -->
<context-param>
<param-name>contextClass</param-name>
<param-value>
org.springframework.web.context.support.AnnotationConfigWebApplicationContext
</param-value>
</context-param>

<!-- Configuration locations must consist of one or more comma- or space-delimited 
fully-qualified @Configuration classes. Fully-qualified packages may also 
be specified for component-scanning -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>org.wmmnpr.mykrb.config</param-value>
</context-param>


<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

<!-- Bootstrap the root application context as usual using ContextLoaderListener -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>

I used the AnnotationConfigWebApplicationContext configurator because I'm only interested in configuring the security via XML and not the rest. 

In the org.wmmnpr.mykrb.config package(because the archetype doesn't include such a package it  had to be created as part of a new "src" folder), I created the following @Configuration class that will be scanned by the AnnotationConfigWebApplicationContext  configurator. 

@Configuration
@ImportResource("classpath:applicationContext-security.xml")
public class BootConfiguration {

}

As one can see, this class contains a reference the XML file, which configures the application's security. 

Before the project can be run, the maven-tomcat-plugin must be added to the project's pom and then started using the maven7:run goal. My first start produced the following error:

You cannot use a spring-security-2.0.xsd or spring-security-3.0.xsd or spring-security-3.1.xsd schema with Spring Security 3.2. Please update your schema declarations to the 3.2 schema.
Offending resource: class path resource [applicationContext-security.xml]

From the message, it's clear that the "schemaLocation" attribute's map entry for the XML namespace ("s") had to be changed: 
http://www.springframework.org/schema/security/spring-security-3.2.xsd

My complete security configuration is as follows:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:util="http://www.springframework.org/schema/util"
       xmlns:s="http://www.springframework.org/schema/security"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans                         
                 http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
          http://www.springframework.org/schema/util 
                 http://www.springframework.org/schema/util/spring-util-3.2.xsd 
          http://www.springframework.org/schema/security                 
                 http://www.springframework.org/schema/security/spring-security-3.2.xsd" >

            
    <s:http use-expressions="true" auto-config="true"  > 
        <s:intercept-url pattern="/**" access="isFullyAuthenticated()"/>                
    </s:http>
    
    <s:authentication-manager>
        <s:authentication-provider ref="ldapAuthenticationProvider"/>
    </s:authentication-manager>

    <bean id="contextSource" class="org.springframework.security.ldap.DefaultSpringSecurityContextSource">
        <constructor-arg value="ldap://192.168.48.30/dc=gft,dc=com" />
    </bean>

    <bean id="ldapAuthenticationProvider" class="org.springframework.security.ldap.authentication.LdapAuthenticationProvider">

        <constructor-arg>
            <bean class="org.springframework.security.ldap.authentication.BindAuthenticator">
                <constructor-arg ref="contextSource" />
                <property name="userSearch">
                    <bean id="userSearch" class="org.springframework.security.ldap.search.FilterBasedLdapUserSearch">
                      <constructor-arg index="0" value="ou=users,ou=frankfurt,ou=gftobjects"/>
                      <constructor-arg index="1" value="cn={0}"/>
                      <constructor-arg index="2" ref="contextSource" />
                    </bean>
                </property>
            </bean>
        </constructor-arg>
        <constructor-arg>
            <bean class="org.springframework.security.ldap.authentication.NullLdapAuthoritiesPopulator"/>
        </constructor-arg>

    </bean>

</beans>


The application now starts and the index.jsp is protected. When I log in, I get:

[ERROR] An internal error occurred while trying to authenticate the user.
org.springframework.security.authentication.InternalAuthenticationServiceException: Uncategorized exception occured during LDAP processing; nested exception is javax.naming.NamingException: [LDAP: error code 1 - 000004DC: LdapErr: DSID-0C0906E8, comment: In order to perform this operation a successful bind must be completed on the connection., data 0, v1db1 ]; remaining name 'ou=users,ou=frankfurt,ou=gftobjects'
        at org.springframework.security.ldap.authentication.LdapAuthenticationProvider.doAuthentication(LdapAuthenticationProvider.java:191)

I modified the XML configuration file because various things were wrong and now it works. The attributes of the parent element are the same as those above.

<beans ... >
           
    <s:http use-expressions="true" auto-config="true"  > 
        <s:intercept-url pattern="/**" access="isFullyAuthenticated()"/>                
    </s:http>
    
    <s:authentication-manager>
        <s:authentication-provider ref="ldapAuthenticationProvider"/>
    </s:authentication-manager>

    <bean id="contextSource" class="org.springframework.security.ldap.DefaultSpringSecurityContextSource">
        <constructor-arg value="ldap://192.168.48.30/dc=gft,dc=com" />
    </bean>

    <bean id="ldapAuthenticationProvider" class="org.springframework.security.ldap.authentication.LdapAuthenticationProvider">

        <constructor-arg>
            <bean class="org.springframework.security.ldap.authentication.BindAuthenticator">
                <constructor-arg ref="contextSource" />
                <property name="userDnPatterns">
                <array>
                <value>cn={0},ou=users,ou=frankfurt,ou=gftobjects</value>                            
                </array>                
                </property>
            </bean>
        </constructor-arg>
        <constructor-arg>
            <bean class="org.springframework.security.ldap.authentication.NullLdapAuthoritiesPopulator"/>
        </constructor-arg>

    </bean>
</beans>

If you still have problem, then you might want to look here.