Friday, December 27, 2013

Spring SAML with ADFS

Running Spring SAML based SP against ADFS might result in errors like the following captured in ADFS Event Log:
System.IdentityModel.SignatureVerificationFailedException: MSIS0038: SAML Message has wrong signature. Issuer: 'xxx'.
It looks like MS update KB2843638 is the cause so try removing the update.
PS: MS relesed another update that solved the issue according to http://social.technet.microsoft.com/Forums/en-US/4acc04b7-aac7-43e9-ba50-9570503045f9/msis0038-saml-message-has-wrong-signature

jBPM and H2 database

By default jBPM uses a demo H2 database. When doing a new deployment the old database is still used since it is stored in user profile directory. If you want to start with a new, fresh copy just delete from  users\<user-name>jbpm.*.db files

Logging Rest calls in JBoss

Trying to use HTTP request logging will be of no use since REST calls use different content type than regular HTTP cals.
A custom server-side REST interceptor can do our job.

  • Create a maven based project and include dependencies to resteasy-jaxrs (groupId org.jboss.resteasy),resteasy-jaxb-provider (groupId org.jboss.resteasy) and servlet-api (groupId javax.servlet)
  • Create a class that contains our interceptor:

@Provider
@ServerInterceptor
public class RestEasyLogger implements PreProcessInterceptor, MessageBodyWriterInterceptor {

    Logger logger = Logger.getLogger(RestEasyLogger.class);

    @Context
    HttpServletRequest servletRequest;

    public ServerResponse preProcess(HttpRequest request,
            ResourceMethod resourceMethod) throws Failure,
            WebApplicationException {

        logger.info("Receiving request : " + servletRequest.getRequestURL().toString());

        BufferedInputStream bis = new BufferedInputStream(request.getInputStream());
        ByteArrayOutputStream buf = new ByteArrayOutputStream();
        String content = "";
        int result;
        try {
            result = bis.read();
            while (result != -1) {
                byte b = (byte) result;
                buf.write(b);
                result = bis.read();
            }
        } catch (IOException ex) {
            java.util.logging.Logger.getLogger(RestEasyLogger.class.getName()).log(Level.SEVERE, null, ex);
        }
        try {
            content = buf.toString("UTF-8");
            ByteArrayInputStream bi = new ByteArrayInputStream(buf.toByteArray());
            request.setInputStream(bi);
        } catch (UnsupportedEncodingException ex) {
            java.util.logging.Logger.getLogger(RestEasyLogger.class.getName()).log(Level.SEVERE, null, ex);
        }

        logger.info("\t\t" + content);

        return null;
    }

    public void write(MessageBodyWriterContext mbwc) throws IOException, WebApplicationException {

        OutputStream oStream = mbwc.getOutputStream();
        ByteArrayOutputStream buf = new ByteArrayOutputStream();
        mbwc.setOutputStream(buf);
        mbwc.proceed();
        String content = buf.toString("UTF-8");
        oStream.write(buf.toByteArray());
        mbwc.setOutputStream(oStream);
        logger.info("\t\t" + content);

    }


  • Configure logging for our class in standalone-full.xml (or whatever configuration file you use for JBoss start-up)

         <subsystem xmlns="urn:jboss:domain:logging:1.1">
            <console-handler name="CONSOLE">
                <level name="INFO"/>
                <formatter>
                    <pattern-formatter pattern="%d{HH:mm:ss,SSS} %-5p [%c] (%t) %s%E%n"/>
                </formatter>
            </console-handler>
            <periodic-rotating-file-handler name="FILE">
                <formatter>
                    <pattern-formatter pattern="%d{HH:mm:ss,SSS} %-5p [%c] (%t) %s%E%n"/>
                </formatter>
                <file relative-to="jboss.server.log.dir" path="server.log"/>
                <suffix value=".yyyy-MM-dd"/>
                <append value="true"/>
            </periodic-rotating-file-handler>
            <periodic-rotating-file-handler name="FILE-RESTEASY">
                <formatter>
                    <pattern-formatter pattern="%d{HH:mm:ss,SSS} %-5p [%c] (%t) %s%E%n"/>
                </formatter>
                <file relative-to="jboss.server.log.dir" path="rs.log"/>
                <suffix value=".yyyy-MM-dd"/>
                <append value="true"/>
            </periodic-rotating-file-handler>
            <logger category="com.arjuna">
                <level name="WARN"/>
            </logger>
            <logger category="org.apache.tomcat.util.modeler">
                <level name="WARN"/>
            </logger>
            <logger category="sun.rmi">
                <level name="WARN"/>
            </logger>
            <logger category="jacorb">
                <level name="WARN"/>
            </logger>
            <logger category="jacorb.config">
                <level name="ERROR"/>
            </logger>
            <logger category="ro.mycompany.jbpm.resteasy.logging.RestEasyLogger" use-parent-handlers="false">
                <level name="DEBUG"/>
                <handlers>
                    <handler name="FILE-RESTEASY"/>
                </handlers>
            </logger>
            <root-logger>
                <level name="INFO"/>
                <handlers>
                    <handler name="CONSOLE"/>
                    <handler name="FILE"/>
                </handlers>
            </root-logger>
        </subsystem>



  • Deploy the resulted jar inside the targeted war (inside WEB_INF\lib)
  • Do some REST calls; you should be able to see detailed info in standalone\log\rs.log

Thursday, October 31, 2013

Install and configure OpenAM for Spring SAML (on Windows)


  • assign a FQDN to 127.0.0.1 in hosts (like localhost.domain.home)
  • Install Tomcat as a service (7 at the moment this post was written)
  • Change the default HTTP port from 8080 to whatever value is required to avoid conflicts (in ...\Tomcat 7.0\conf\server.xml)
  • Restart the service
  • Download OpenAM distribution (I have used OpenAM-12.0.0-SNAPSHOT_nightly_20131021since the stable 10 version had a known issue on signing SAML assertions due to a problem in JDK 7_u25)
  • Extract distribution content, rename OpenAM-12.0.0-SNAPSHOT.war to openam.war
  • Copy openam.war to ...Tomcat 7.0\webapps
  • Browse to http://<server>:<port>/openam
  • Run the custom configuration (the default has a problem with generating the default domain for cookies) 
    • Use .domain.home as cockie domain (notice the leading dot)
    • Use the embedded LDAP (OpenDJ)
  • When the configuration is finnished login with amadmin/<password>
  • Choose "Create a hosted Identity Provider"
    • Choose the test signing key (or another if previously installed) if your SP requires signed/encripted assertions
    • Choose a name for the circle of trust
    • Press Configure
  • Add a SP in the same circle of trust:
    • Choose "Create a remote Service Provider"
    • Use the option to upload the metadata from a file (or specify on URL)
    • Press Configure
  • Configure attributes to be retrieved from data store
    • Go to Access Control->Top Level Realm->Data Stores->Embedded
    • Use the New Value/Add to add required (missing) attributes to be retrieved from the data store (like isMemberOf); 
      • attribute list is more generic and notr specific to the actual data store
      • use a LDAP browser to see actual attributes
  • Configure the mapping between required  assertion attributes (to be sent in SAML Response) and data store attributes:
    • Go to Federation->target IdP->Assertion Processing
    • In Attribute Mapper->Attribute map section use New Value->Add to add mappings (like isMemberOf=isMemberOf)
    • Save
  • Restart OpenAM or Tomcat; without restarting previously configured attribute mapping will not take place
Adding users:
  • Access Control->Top Level Realm
    • Group:add new groups
    • User: add/configure users
      • Be sure to fill in the field that the SP has configured as a potential Name ID (like email) otherwise an exception will be thrown (and recorded in OpenAM log) and an error will be sent back as a SAML Response

On non expected behavior check logs in openam configuration folder (default c:\openam): 
  • C:\openam\openam\debug
  • C:\openam\openam\log





Browsing OpenAM default LDAP store (OpenDJ)

When you need to configures attributes that will be sent in SAML response back to Service Provider you might need to know what actually is available and what are the specific names/formats.

For that purpose you need a LDAP browser (like Softera free tool).

  • Be sure OpenAM is started
  • Connect to embeded LADP using:
    • Host: localhost
    • Port: 50389
    • User Principal): cn=Directory Manager
    • Password: the password you initially configured for amadmin
  • Be sure to select (on object's properties): Display operational attributes/Display all

@Autowired in web applications

"Marks a constructor, field, setter method or config method as to be autowired by Spring's dependency injection facilities." (http://docs.spring.io/spring/docs/3.0.x/api/org/springframework/beans/factory/annotation/Autowired.html)

Almost all samples on this topic use command line Java applications that programatically load the configuration file that maps the interface to its implementation class. In JSF based applications use the context param tag in web.xml to achieve the same result.

<context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            classpath:/security/samlUserDetailsService4Autowire.xml
            classpath:/security/securityContext.xml
        </param-value>
</context-param>
Please notice that the order of configuration files is important and is dictated by dependency injection 'dependencies' (at the moment of injection the required instances to be injected must already exist)

Wednesday, October 23, 2013

Decoding SAML Request/Response

You can use online decoder provided by ssocircle: https://idp.ssocircle.com/sso/toolbox/samlDecode.jsp
If sent as URL parameter the request/response must be decoded with the redirect option.
The actual algorithm for obtaing the encoded string is as follows:
  1. Gzip deflate the request/response if sent as query parameter (no change for POST parameters)
  2. Convert to Base64
  3. URL encode the result

Configure Galssfish (3.1.2.2) for HTTPS


  1. Prepare a certificate (issued for the FQDN of the server running Glassfish) in PKCS#12 format (p12 extension) containing the private/public key pair; OpenSSL can be used to generate a CA and generate afterwards test certificates
  2. In target domains/<domanin>/config/cacerts.jks add* CA public certificate; default keystore password is 'changeit'
  3. In target domains/<domanin>/config/keystore.jks add the p12 certificate; use the same password as the keystore ('default value 'changeit')
  4. Open adnin console, go to server configuration and expand it to Network Config->Network Listners->network-listner-2, SSL tab.
  5. Replace the default test certificate name (s1as) with the keystore name of the newly added certificate
  6. Apply changes and restart glassfish
*You can use Portecle GUI based tool to manage keystores

Run Spring SAML Extension sample app.

Prerequisites:

  1. NetBeans 7.4
  2. Glassfish 3.1.2.2
  3. A PKCS#12 format cerificate for SAML requests signing
  4. Spring SAML Extension sample app downloaded (http://docs.spring.io/spring-security/site/extensions/saml/)

  • Add the certificate in  samlKeystore.jks (in resources\security)
  • Open project in NetBeans
  • Change the default securityContext.xml (will be used later):
    • configure the newly added certificate in keyManagerBean as an entry in constructor parameters
                             <entry key="localhostsaml" value="nalle123"/>
    • configure the metadataGeneratorBean constructor parameter:

                            <bean class="org.springframework.security.saml.metadata.MetadataGenerator">
                               <property name="entityId" value="urn:myid:saml"/>
                               <property name="signMetadata" value="false"/>
                               <property name="bindingsSSO" >
                                 <list>
                                    <value>POST</value>
                                 </list>
                              </property>
                           </bean>
  • save the  and NetBeans will deploy the war
  • run the metadata generation page : https:// localhost.myserver.local:8181/spring-security-saml2-sample/saml/web/metadata; use the real FQDN so that metadata will be generated with actual values
  • Click on the link "Generate new service provider metadata)
  • Change the values of:
    • Entity ID and Entity alias to same urn as the entityId previously configured (eg urn:myid:saml)
    • SigningKey and EncriptionKey to the one added in samlKeystore.jks (localhostsaml in our sample)
    • Sign Metadata: No
    • Single sign-on bindings: unckeck Artifact and choose as default the POST one
    • Enable IDP Discovery profile:: No
  • Press Generate Metadata
  • Copy The content of Metadata texta area andcopy the content in a newly created sp.xml, placed in resources\security
  • In securityContext.xml add a constructor parameter for "metadata" bean:
                <bean class="org.springframework.security.saml.metadata.ExtendedMetadataDelegate">
                    <constructor-arg>
                        <bean class="org.opensaml.saml2.metadata.provider.FilesystemMetadataProvider">
                            <constructor-arg>
                                <value type="java.io.File">classpath:security/sp.xml</value>
                            </constructor-arg>
                            <property name="parserPool" ref="parserPool"/>
                        </bean>
                    </constructor-arg>
                    <constructor-arg>
                        <bean class="org.springframework.security.saml.metadata.ExtendedMetadata">
                            <property name="local" value="true"/>
                            <property name="alias" value="urn:myid:saml"/>
                            <property name="securityProfile" value="metaiop"/>
                            <property name="sslSecurityProfile" value="pkix"/>
                            <property name="signingKey" value="localhostsaml"/>
                            <property name="encryptionKey" value="localhostsaml"/>
                            <property name="requireArtifactResolveSigned" value="false"/>
                            <property name="requireLogoutRequestSigned" value="false"/>
                            <property name="requireLogoutResponseSigned" value="false"/>
                            <property name="idpDiscoveryEnabled" value="false"/>
                        </bean>
                    </constructor-arg>
                </bean> 
Please notice that the ExtendedMetadata   content is copy/pasted from metadata generation result (Configuration text area)  
  • Un comment and update the tag, values is actually the value of the alias (we have used the same value for Id and alias): 
             <property name="hostedSPName" value="urn:myid:saml"/>
  • Be sure that the only uncommented tag for defaultIDPis the following:
             <property name="defaultIDP " value="http://idp.ssocircle.com"/>
  • Go to http://www.ssocircle.com/en/ and create an account
  • After login go to Manage Metadata and create a new Service Provider:
    • Provide the FDQN as the entityId (urm:myid:saml in our sample)
    • Mark all attributes
    • Paste the content of sp.xml in the metadata information text area
  • Log out from ssocircle
  • Run the application; you should be redirected to ssocircle and after entering usernam/password redirected back to index.jsp showing information received in  SAML Response

Saturday, June 29, 2013

JSF caching of javascript, css, etc

In order to have static resources cached change in web.xml from
    <context-param>
        <param-name>javax.faces.PROJECT_STAGE</param-name>
        <param-value>Development</param-value>
    </context-param>
to
    <context-param>
        <param-name>javax.faces.PROJECT_STAGE</param-name>
        <param-value>Production</param-value>
    </context-param>
This will allow caching of static resources fro 10 minutes;in order to change the default value add another context parameter: com.sun.faces.defaultResourceMaxAge (the default value of this parameter is 604800 ~ 10 mins)

Wednesday, April 17, 2013

JasperReports

iReports

After installing add the JDBC DataSource jar in iReports Classpath through Tools->Options-Classpath (be sure to add the jar, not the folder); restart iReports
Create a DataSource (no menu option available, just click the plug-in socket icon); be sure you add the database name at the end of the URL
Now you can create a new report and modify the report query. You can define report default values for parameters as follows (please notice that parameters are strong typed so you must create an instance of the java class):

  • String: enclose the desired value in double quotes
  • Date:
  1. new Date()
  2. new Date(113,0,1)
The second expression will initialize the default value to 1st of January 2013 (!):year+1900,month+1

Formatting dates:

  • add an expression in the report
  • change the expression text with something like this:
  • "From "+(new SimpleDateFormat("yyyy/MM/dd")).format($P{DateStart})+" to "+(new SimpleDateFormat("yyyy/MM/dd")).format($P{DateEnd})

JSF and JasperReports

Add the required jars (found in jasper reports distribution in dist and lib sub-directories) as project references. Minimal (depending on used functionality):
  • jasperreports
  • all commons
  • iText
  • groovy-all
Deploy compiled report in resources/jsrep (for instance) project sub-directory.
In managed bean you can use the following code to show the report in PDF format (the sample code uses PrimeFaces file download component, see the showcase):

      InputStream stream = ((ServletContext)FacesContext.getCurrentInstance().getExternalContext().getContext()).getResourceAsStream("/resources/jsrep/Report.jasper"); 
       JasperReport report = (JasperReport) JRLoader.loadObject(stream);
       Map<String, Object> params = new HashMap<String, Object>();
       params.put("Param", 494);
       JasperPrint jasperPrint = JasperFillManager.fillReport(report, params, ds.getConnection());
       ByteArrayOutputStream baos=new ByteArrayOutputStream();
       JasperExportManager.exportReportToPdfStream(jasperPrint,baos );
       ByteArrayInputStream bais=new ByteArrayInputStream(baos.toByteArray());
       return new DefaultStreamedContent(bais, "application/pdf", "report.pdf"); 
The data source can be obtained through injection, like
@Resource(name="jdbc/MyDataSource")
DataSource ds;
In order to export to XLS add poi jar to solution and use the following code:

        InputStream stream = ((ServletContext) FacesContext.getCurrentInstance().getExternalContext().getContext()).getResourceAsStream("/resources/jsrep/Report.jasper");
        JasperReport report = (JasperReport) JRLoader.loadObject(stream);
        Map<String, Object> params = new HashMap<String, Object>();
        params.put("Param", 123);
        JasperPrint jasperPrint = JasperFillManager.fillReport(report, params, ds.getConnection());
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        JRXlsExporter exporterXLS = new JRXlsExporter();
        exporterXLS.setParameter(JRXlsExporterParameter.JASPER_PRINT, jasperPrint);
        exporterXLS.setParameter(JRXlsExporterParameter.OUTPUT_STREAM, baos);
        exporterXLS.setParameter(JRXlsExporterParameter.IS_ONE_PAGE_PER_SHEET, Boolean.FALSE);
        exporterXLS.setParameter(JRXlsExporterParameter.IS_DETECT_CELL_TYPE, Boolean.TRUE);
        exporterXLS.setParameter(JRXlsExporterParameter.IS_WHITE_PAGE_BACKGROUND, Boolean.FALSE);
        exporterXLS.setParameter(JRXlsExporterParameter.IS_REMOVE_EMPTY_SPACE_BETWEEN_ROWS, Boolean.TRUE);
        exporterXLS.setParameter(JRXlsExporterParameter.PASSWORD, null);
        exporterXLS.exportReport();
        ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
        return new DefaultStreamedContent(bais, "application/vnd.ms-excel", "report.xls");

Creating reports that will be exported to XLS can be tricky: be sure that fields are top aligned pixel-perfect otherwise they will not be shown or shown in an unexpected manner in resulting XLS



Monday, April 8, 2013

Configure MySQL jdbc resource


  1. Copy the MySQL jdbc driver (jar file) in Glassfish lib directory and restart Glassfish
  2. In the glassfish administration console (site) go to Resources\JDBC\JDBC ConnectionPools
  3. Create a new pool of type javax.sql.DataSource and choose MySQL as the database vendor
  4. Fill in: server, user, password, database and, very important, check the URL to contain the database name (for some reason or under some circumstances it is not correctly filled in)
  5. Try Ping, should be successfull
  6. Now create the jdbc Resource, etc

Friday, March 15, 2013

JEE 6, my first web application

IE10:

  • use <!DOCTYPE html>
  • include in head section:
        <f::facet name="first">
            <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        </f::facet>

Date format:
JSF dispalys by defaut UTC dates while Prime faces display date as stored. Add context param in web.config:

    <context-param>
        <param-name>
javax.faces.DATETIMECONVERTER_DEFAULT_TIMEZONE_IS_SYSTEM_TIMEZONE
</param-name>
        <param-value>true</param-value>
    </context-param>

Specific theme:
  • Add context param in web.config:
    <context-param>
        <param-name>primefaces.THEME</param-name>
        <param-value>bootstrap</param-value>
    </context-param>
  • Copy theme (expanded) in web\resources\primefaces-bootstrap
CSS load order
In order to load your theme after primefaces.css add a facet in <h:body> (yes, body!!!)section:

        <f:facet name="last">
            <h:outputStylesheet name="css/mycss.css"/> 
        </f:facet>
Adding it in <h:head> section does not work. 

You could also use (this time in head section!) :
        <f:facet name="last">
            <link rel="stylesheet" type="text/css" href="#{request.contextPath}/resources/css/mytheme.css" />
        </f:facet>
but this will prevent you from using #{} type constructs in mytheme.css


AD authentication:
From com.sun.enterprise.security.auth.realm.ldap.LDAPRealm source code:
Realm wrapper for supporting LDAP authentication.
See LDAPLoginModule documentation for more details on the operation of the LDAP realm and login module.
The ldap realm needs the following properties in its configuration:
  • directory - URL of LDAP directory to use
  • base-dn - The base DN to use for user searches.
  • jaas-ctx - JAAS context name used to access LoginModule for authentication.
Besides JDK Context properties start with java.naming, javax.security, one can also set connection pool related properties starting with com.sun.jndi.ldap.connect.pool. See http://java.sun.com/products/jndi/tutorial/ldap/connect/config.html for details. Also, the following optional attributes can also be specified:
  • search-filter - LDAP filter to use for searching for the user entry based on username given to iAS. The default value is uid=%s where %s is expanded to the username.
  • group-base-dn - The base DN to use for group searches. By default its value is the same as base-dn.
  • group-search-filter - The LDAP filter to use for searching group membership of a given user. The default value is uniquemember=%dwhere %d is expanded to the DN of the user found by the user search.
  • group-target - The attribute which value(s) are interpreted as group membership names of the user. Default value is cn.
  • search-bind-dn - The dn of ldap user. optional and no default value.
  • search-bind-password - The password of search-bind-dn.optional and no default value.
  • pool-size - The JNDI ldap connection pool size.

Add  security realm through Glassfish administration site (Configuration->server-config->Security->Realms):
  • JAAS Context: ldapRealm (check login.conf in config subdirectory of target domain)
  • Directory: URL of LDAP directory (like ldap://mycompany.local:389)
  • Base DN: Search sarting point in LDAP structure (like OU=Users,OU=mycompany,DC=totalsoft,DC=local )
  • Assign Groups: additional groups (apart from those retrieved from LDAP) the user will be assigned to

Add additional properties, as described by above mentioned source code. A sample of domain.xml below:

        <auth-realm name="MyRealm" classname="com.sun.enterprise.security.auth.realm.ldap.LDAPRealm">

          <property name="directory" value="ldap://mycompany.local:389"></property>

          <property name="assign-groups" value="users"></property>

          <property name="base-dn" value="OU=Users,OU=mycompany,DC=mycompany,DC=local"></property>

          <property name="group-base-dn" value="OU=Groups,OU=mycompany,DC=mycompany,DC=local"></property>

          <property name="jaas-context" value="ldapRealm"></property>

          <property name="search-filter" value="(&amp;(objectClass=user)(sAMAccountName=%s))"></property>

          <property name="group-search-filter" value="(&amp;(objectClass=group)(member=%d))"></property>

          <property name="search-bind-dn" value="mycompany\dummy"></property>

          <property name="search-bind-password" value="DummyPwd"></property>
        </auth-realm>


You might have also to add the following property in domain.xml: 
<jvm-options>-Djava.naming.referral=follow</jvm-options>


In order to find the right values for above mentioned properties you might use LDAP Browser from Softerra.
In order to check the actual search parameters sent to LDAP or the response received use WhireShark filtering by the LDAP port (389 usually):

Search DN:OU=Groups,OU=mycompany,DC=mycompany,DC=local
Filter: (&(objectClass=group)(member=CN=First Name LastName,OU=Users,OU=mycompany,DC=mycompany,DC=local))


Web service on HTTPS:
You will find plenty samples but all fail to say that if using EJB to expose a web service than the configuration must not be done in web.xml but in server specific ejb configuration file (with a different schema than web.xml); for instane for Glassfish use glassfish-ejb-jar.xml

Calling MS SQL stored procedures:
Sine there is no support fr calling stored procedures in JPA you can use the following approach.

Define a named query containing the stored procedure call

@NamedNativeQuery(name = "CallStoredProcedure", 
query = "DECLARE @P1 int;DECLARE @P2 int;
EXECUTE @P1 = usp_MyUSP @P2 OUTPUT;SELECT @P2")

In the apropriate EJB declare the method:


public List runNamedQuery(String namedQuery) {
        Query cq = getEntityManager().createNamedQuery(namedQuery);
        return cq.getResultList();
    }

And finally call it from the controller:


List rez = getFacade().runNamedQuery("CallStoredProcedure");Integer p2=(Integer) rez.get(0));


If you need parameters you can use the regular JPA syntax in the named query and pass the values to the Query object (using setParameter method)