Social Icons

Pages

Featured Posts

Monday, September 15, 2014

Spring Security and Active Directory

Recently I run into an issue to integrate an application with corporate LDAP. Typical LDAP requires a bind. If the server does not allow anonymous bind, then a manager/admin username and password must be supplied.

But when dealing with Active Directory, one can use the incoming user and password of an authentication request to do the binding. This is a non-standard way to integrate with a typical LDAP server. Spring Security has direct support for this type of configuration and setup.

<security:authentication-manager alias="authenticationManager">
  <security:authentication-provider ref="adAuthenticationProvider"/>
</security:authentication-manager>

<bean id="adAuthenticationProvider" class="org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider">
  <constructor-arg value="mycompany.com"/>
  <constructor-arg value="ldap://my-company-active-directory-url"/>
  <property name="useAuthenticationRequestCredentials" value="true"/>
  <property name="convertSubErrorCodesToExceptions" value="true"/>
</bean>

Accessing Http Session In JAX-RS 2.0

JAX-RS 2.0 defines Filters and Interceptors. One can access the Http session by implementing a filter. For example:
import java.io.IOException;
import javax.inject.Named;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.core.Context;
import javax.ws.rs.ext.Provider;

@Named
@Provider
public class UsernameInterceptor implements ContainerRequestFilter {
    @Context
    private HttpServletRequest servletRequest;

    public void filter(ContainerRequestContext requestContext) throws IOException {
        
        HttpSession session = servletRequest.getSession(false);

        if (session != null) {
            // do stuff
        }
    }
}

Friday, September 5, 2014

Gradle and Embedded Jetty access log

The standard Jetty plugin for Gradle has limited features. For example, there seems to have no option to configure access logs with this plugin. Luckily there is a much more feature rich jetty plugin called JettyEclipse for Gradle. This can be found here. Configuring this plugin is quite easy. Add the following into your build.graddle.
buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath (group: 'com.sahlbach.gradle', name: 'gradle-jetty-eclipse-plugin', version: '1.9.+')
    }
}
apply plugin: 'jettyEclipse'

jettyEclipse {
    System.setProperty("catalina.home", "${project.buildDir}")   
    requestLog = new File("${project.buildDir}/logs/access.log")
}

Then run gradle clean jettyEclipseRun

Handling Resources with Gradle

Gradle copies resources to a different directory than Maven. So when migrating from Maven, the source code needs to be changed to take care of the new resource locations. In order to keep the resources are copied to the correct location, add the following into your projects build.gradle file.

project.buildDir = 'target'

sourceSets {
    main {
        output.resourcesDir "${project.buildDir}/classes"
    }
    test {
        output.resourcesDir "${project.buildDir}/classes/test"
    }
}

Querydsl JPA Model Generation with Gradle

Querydsl is a very useful tool to write concise JPA queries. This requires a plugin to generate the meta model classes that will be referenced in the queries. There is a maven plugin to handle the code generation. But when using Gradle, this needs to be configured manually. Add this to the build.gradle file.
ext {
    generatedSourcesDir = file("${buildDir}/generated-sources")
    querydslVersion    = "3.4.2"
}
sourceSets {
    main {
        java {
            srcDir "src/main/java"
            srcDir generatedSourcesDir
        }
    }
}
configurations {
    querydslapt
}
task generateQueryDSL(type: JavaCompile, group: 'build', description: 'Generates the QueryDSL query types') {
    source = sourceSets.main.java
    classpath = configurations.compile + configurations.querydslapt
    options.compilerArgs = [
            "-proc:only",
            "-processor", "com.mysema.query.apt.jpa.JPAAnnotationProcessor"
    ]
    destinationDir = generatedSourcesDir
}
compileJava {
    doFirst {
        generatedSourcesDir.mkdirs();
    }
    options.compilerArgs += ['-s', generatedSourcesDir]

    dependsOn generateQueryDSL
}
dependencies {
      compile  "com.mysema.querydsl:querydsl-core:${querydslVersion}"
      compile  "com.mysema.querydsl:querydsl-jpa:${querydslVersion}"
      querydslapt  "com.mysema.querydsl:querydsl-apt:${querydslVersion}"
}

Wednesday, August 27, 2014

Spring, Spring Data, RestEasy, Apache Shiro and LDAP

Here are the basic configurations needed to integrate Spring, Spring Data, RestEasy, Apache Shiro and LDAP authentication.

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app
    xmlns="http://java.sun.com/xml/ns/j2ee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation=
    "http://java.sun.com/xml/ns/j2ee
     http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" version="2.4">

    <!-- LogBack Configuration File  -->
    <context-param>
      <param-name>logbackConfigLocation</param-name>
      <param-value>/WEB-INF/logback.xml</param-value>
    </context-param>

    <!-- Define the spring context locations -->
    <context-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>/WEB-INF/test-service-config.xml</param-value>
    </context-param>

    <!--
        Filters
    -->

    <filter>
      <filter-name>jpaFilter</filter-name>
      <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    <init-param>
      <param-name>targetBean</param-name>
      <param-value>jpaFilter</param-value>
    </init-param>
    </filter>

    <filter-mapping>
      <filter-name>jpaFilter</filter-name>
      <url-pattern>/*</url-pattern>
    </filter-mapping>

  <filter>
    <filter-name>shiroFilter</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    <init-param>
      <param-name>targetBean</param-name>
      <param-value>shiroFilter</param-value>
    </init-param>

    <init-param>
      <param-name>targetFilterLifecycle</param-name>
      <param-value>true</param-value>
    </init-param>
  </filter>

  <filter-mapping>
    <filter-name>shiroFilter</filter-name>
    <url-pattern>/*</url-pattern>
    <dispatcher>REQUEST</dispatcher> 
    <dispatcher>FORWARD</dispatcher> 
    <dispatcher>INCLUDE</dispatcher> 
    <dispatcher>ERROR</dispatcher>
  </filter-mapping>

    <!--
      Listeners
    -->
  <listener>
    <listener-class>
      org.springframework.web.context.ContextLoaderListener
    </listener-class>
  </listener>

 
  <listener>
    <listener-class>
      ch.qos.logback.ext.spring.web.LogbackConfigListener
    </listener-class>
  </listener>

  <!-- 
     Servlet
   -->

  <servlet>
    <servlet-name>testServlet</servlet-name>
    <display-name>Test Servlet</display-name>

    <description>
      A Test Servlet
    </description>

    <servlet-class>
      org.springframework.web.servlet.DispatcherServlet
    </servlet-class>
 
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>
        /WEB-INF/test-web-config.xml
      </param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>

  <servlet-mapping>
    <servlet-name>testServlet</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>

  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>

</web-app>

test-service-config.xml

<?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:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:jdbc="http://www.springframework.org/schema/jdbc"
       xmlns:jpa="http://www.springframework.org/schema/data/jpa"
       xmlns:repository="http://www.springframework.org/schema/data/repository"
       xmlns:security="http://www.springframework.org/schema/security"
       xsi:schemaLocation=
       "http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/jdbc
        http://www.springframework.org/schema/jdbc/spring-jdbc.xsd
        http://www.springframework.org/schema/data/jpa
        http://www.springframework.org/schema/data/jpa/spring-jpa.xsd
        http://www.springframework.org/schema/data/repository
        http://www.springframework.org/schema/data/repository/spring-repository.xsd
        http://www.springframework.org/schema/security
        http://www.springframework.org/schema/security/spring-security.xsd">

  <context:annotation-config/>
  <context:component-scan base-package="org.suresh.api.rest.v1"/>
  <context:component-scan base-package="org.suresh.service"/>
  <context:component-scan base-package="org.suresh.controllers"/>

  <jpa:repositories base-package="org.suresh.repo"/>
  <jdbc:embedded-database id="testDb" type="HSQL"/>

  <bean id="entityManagerFactory"
        class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">

    <property name="dataSource" ref="testDb"/>
    <property name="persistenceUnitName" value="test"/>
    <property name="packagesToScan">
      <list>
        <value>org.suresh.model</value>
      </list>
    </property>

    <property name="jpaVendorAdapter">
      <bean
          class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
        <property name="generateDdl" value="true"/>
        <property name="database" value="HSQL"/>
      </bean>
    </property>
  </bean>

  <bean id="transactionManager"
        class="org.springframework.orm.jpa.JpaTransactionManager">
    <property name="entityManagerFactory" ref="entityManagerFactory"/>
    <property name="dataSource" ref="testDb"/>
  </bean>

  <!-- Security -->
  <security:ldap-server
      root="dc=suresh,dc=org"
      ldif="classpath:resources/test-users.ldif" port="33389"/>

  <bean id="shiroSecurityManager"
        class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
    <property name="realm" ref="ldapRealm"/>
  </bean>

  <bean id="ldapRealm" class="org.apache.shiro.realm.ldap.JndiLdapRealm">
    <property name="userDnTemplate"
              value="uid={0},ou=people,dc=suresh,dc=org"/>
    <property name="contextFactory" ref="ldapContextFactory"/>
  </bean>

  <bean id="ldapContextFactory"
        class="org.apache.shiro.realm.ldap.JndiLdapContextFactory">
    <property name="url"
              value="ldap://localhost:33389/dc=suresh,dc=org"/>
  </bean>

  <bean id="shiroFilter"
        class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
    <property name="securityManager" ref="shiroSecurityManager"/>
    <property name="loginUrl"        value="/views/login.jsp"/>
    <property name="successUrl"      value="/views/home.jsp"/>

    <property name="filterChainDefinitions">
      <value>
        /api/**   = authc
        /views/** = authc
      </value>
    </property>

  </bean>

  <!-- enable shiro annotations -->
  <bean id="lifecycleBeanPostProcessor"
        class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>

  <bean
      class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor"/>
    <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
      <property name="securityManager" ref="shiroSecurityManager"/>
    </bean>

  <!-- Filters -->
  <bean id="jpaFilter"
        class="org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter"/>
</beans>

test-web-config.xml

<?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:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation=
       "http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd">

  <import resource="classpath:springmvc-resteasy.xml"/>

  <context:annotation-config/>
  <context:component-scan base-package="org.suresh.api.rest.v1"/>

  <mvc:annotation-driven/>


  <mvc:resources mapping="/images/**" location="/public/images/" order="-1"/>
  <mvc:resources mapping="/stylesheets/**"
                 location="/public/stylesheets/"    order="-1"/>
  <mvc:resources mapping="/javascripts/**"
                 location="/public/javascripts/"     order="-1"/>
  <mvc:default-servlet-handler/>


  <!-- RESTEasy prefix -->
  <bean id="resteasy.handlerMapping" parent="abstract.resteasy.handlerMapping">
    <property name="prefix" value="/api" />
  </bean>
</beans>

Wednesday, May 28, 2014

Executing Cypher Queries using Spring Data Neo4jTemplate


In addition to standard CRUD methods, org.springframework.data.neo4j.support.Neo4jTemplate provides the ability to execute Cypher queries. The following example shows how to use Neo4jTemplate:
@ContextConfiguration(locations = {"classpath:/test-config.xml"})
public class QueryTest extends AbstractTestNGSpringContextTests {
    @Test
    public void testQuery() {
        Object result = template.exec(new GraphCallback<Object>() {
                public Object doWithGraph(GraphDatabase graph) throws Exception {
                    QueryEngine engine = graph.queryEngine();
                    
                    Result<Map<String, Object>> result
                        = engine.query("match n return n", null);

                    for (Map<String, Object> m : result) {
                        for (Map.Entry<String, Object> e : m.entrySet()) {
                            Node node = (Node) e.getValue();
                            System.out.println("===>>" + node.getId());
                            System.out.println("===>>" + node.getLabels());

                        }
                    }
                    return null;
                }
            });
    }
    @Autowired
    private Neo4jTemplate template;
}


Saturday, May 24, 2014

Neo4j Labels Cannot be Deleted


In Neo4j version 2.1.0-RC2 labels once created cannot be deleted, even after all the nodes has been dropped. The only way around is to completely delete the database!
create (u:User {name: "John Doe", email: "johndoe@nowhere.com"})
create (b:Blog {name: "My Blog"})
match n delete n
Now if looking at the WebUI, you can see both labels are still present. In addition, by calling the REST API http://localhost:7474/db/data/labels returns both labels, even though there are no nodes referring to it. There is no current API or command to delete these orphaned labels.